From dbcd4481c8a2f38dc947dc728e30aac5e0a5761a Mon Sep 17 00:00:00 2001
From: Nathan Kinder <nkinder@redhat.com>
Date: Tue, 28 Feb 2012 09:42:23 -0800
Subject: [PATCH] ticket 181 - Allow PAM passthru plug-in to have multiple
 config entries

Previously, the PAM passthru plug-in only allowed a single configuration
to be in place.  The only config entry was the top-level PAM plug-in
entry in cn=config.

This patch allows multiple PAM passthru configuration entries to be
specified.  This gives the ability to have much more flexibility
when passing authentication to PAM.  You can  do things like use
different PAM server files for different portions of the DIT, or
even different mapping methods and security requirements.

To allow even more flexibility, I added support for a new pamFilter
configuration attribute.  This allows an LDAP filter to be used to
determine which entries a PAM passthru configuration should apply
to.  This allows a flat DIT to have different PAM passthru config
based off of the contents of the entries, such as using the objectclass
value.

Lastly, I added the ability to use an alternate plug-in configuration
area for PAM passthru config entries.  This allows one to store the
config entries in a replicated tree instead of cn=config.  When using
the alternate config area, only the child entries of the alternate
config container are considered to be PAM passthru config entries.
When the normal area in cn=config is used, both the top-level PAM
passthru plug-in config entry and it's children are considered to
be config entries.  This ensures that the existing config style is
backwards compatible.  Using an alternate config area meant getting
rid of the DSE style config callbacks and implementing normal pre-op
and post-op callback for dynamic config validation and loading.
---
 ldap/schema/60pam-plugin.ldif                    |    3 +-
 ldap/servers/plugins/pam_passthru/pam_passthru.h |   48 ++-
 ldap/servers/plugins/pam_passthru/pam_ptconfig.c |  715 +++++++++++++++-------
 ldap/servers/plugins/pam_passthru/pam_ptimpl.c   |   28 +-
 ldap/servers/plugins/pam_passthru/pam_ptpreop.c  |  580 +++++++++++++++---
 5 files changed, 1032 insertions(+), 342 deletions(-)

diff --git a/ldap/schema/60pam-plugin.ldif b/ldap/schema/60pam-plugin.ldif
index 97157a2..ddf91d9 100644
--- a/ldap/schema/60pam-plugin.ldif
+++ b/ldap/schema/60pam-plugin.ldif
@@ -48,4 +48,5 @@ attributeTypes: ( 2.16.840.1.113730.3.1.2071 NAME 'pamIDAttr' DESC 'Name of attr
 attributeTypes: ( 2.16.840.1.113730.3.1.2072 NAME 'pamFallback' DESC 'Fallback to regular LDAP BIND if PAM auth fails' SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE X-ORIGIN 'Red Hat Directory Server' )
 attributeTypes: ( 2.16.840.1.113730.3.1.2073 NAME 'pamSecure' DESC 'Require secure (TLS/SSL) connection for PAM auth' SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE X-ORIGIN 'Red Hat Directory Server' )
 attributeTypes: ( 2.16.840.1.113730.3.1.2074 NAME 'pamService' DESC 'Service name to pass to pam_start' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'Red Hat Directory Server' )
-objectClasses: ( 2.16.840.1.113730.3.2.318 NAME 'pamConfig' DESC 'PAM plugin configuration' SUP top AUXILIARY MAY ( pamMissingSuffix $ pamExcludeSuffix $ pamIncludeSuffix $ pamIDAttr $ pamIDMapMethod $ pamFallback $ pamSecure $ pamService ) X-ORIGIN 'Red Hat Directory Server' )
+attributeTypes: ( 2.16.840.1.113730.3.1.2131 NAME 'pamFilter' DESC 'Filter to match entries that should use PAM authentication' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'Red Hat Directory Server' )
+objectClasses: ( 2.16.840.1.113730.3.2.318 NAME 'pamConfig' DESC 'PAM plugin configuration' SUP top AUXILIARY MAY ( cn $ pamMissingSuffix $ pamExcludeSuffix $ pamIncludeSuffix $ pamIDAttr $ pamIDMapMethod $ pamFallback $ pamSecure $ pamService $ pamFilter ) X-ORIGIN 'Red Hat Directory Server' )
diff --git a/ldap/servers/plugins/pam_passthru/pam_passthru.h b/ldap/servers/plugins/pam_passthru/pam_passthru.h
index 748c2f1..c565fcb 100644
--- a/ldap/servers/plugins/pam_passthru/pam_passthru.h
+++ b/ldap/servers/plugins/pam_passthru/pam_passthru.h
@@ -61,16 +61,26 @@
 /*
  * macros
  */
-#define PAM_PASSTHRU_PLUGIN_SUBSYSTEM	"pam_passthru-plugin"   /* for logging */
+#define PAM_PASSTHRU_PLUGIN_SUBSYSTEM   "pam_passthru-plugin"   /* for logging */
+#define PAM_PASSTHRU_INT_POSTOP_DESC    "PAM Passthru internal postop plugin"
+#define PAM_PASSTHRU_POSTOP_DESC        "PAM Passthru postop plugin"
 
 #define PAM_PASSTHRU_ASSERT( expr )		PR_ASSERT( expr )
 
 #define PAM_PASSTHRU_OP_NOT_HANDLED		0
 #define PAM_PASSTHRU_OP_HANDLED		1
+#define PAM_PASSTHRU_SUCCESS 0
+#define PAM_PASSTHRU_FAILURE -1
 
 /* #define	PAM_PASSTHRU_VERBOSE_LOGGING	*/
 
 /*
+ * Plug-in globals
+ */
+extern int g_plugin_started;
+extern PRCList *pam_passthru_global_config;
+
+/*
  * structs
  */
 typedef struct pam_passthrusuffix {
@@ -87,21 +97,24 @@ typedef struct pam_passthrusuffix {
 #define PAMPT_MISSING_SUFFIX_IGNORE_STRING "IGNORE"
 
 typedef struct pam_passthruconfig {
-	Slapi_Mutex *lock; /* for config access */
+    PRCList list;
+    char *dn;
     Pam_PassthruSuffix *pamptconfig_includes; /* list of suffixes to include in this op */
     Pam_PassthruSuffix *pamptconfig_excludes; /* list of suffixes to exclude in this op */
-	PRBool pamptconfig_fallback; /* if false, failure here fails entire bind */
-	                             /* if true, failure here falls through to regular bind */
+    char *filter_str; /* search filter used to identify bind entries to include in this op */
+    Slapi_Filter *slapi_filter; /* a Slapi_Filter version of the above filter */
+    PRBool pamptconfig_fallback; /* if false, failure here fails entire bind */
+                                 /* if true, failure here falls through to regular bind */
     PRBool pamptconfig_secure; /* if true, plugin only operates on secure connections */
-	char *pamptconfig_pam_ident_attr; /* name of attribute in user entry for ENTRY map method */
-	int pamptconfig_map_method1; /* how to map the BIND DN to the PAM identity */
-	int pamptconfig_map_method2; /* how to map the BIND DN to the PAM identity */
-	int pamptconfig_map_method3; /* how to map the BIND DN to the PAM identity */
+    char *pamptconfig_pam_ident_attr; /* name of attribute in user entry for ENTRY map method */
+    int pamptconfig_map_method1; /* how to map the BIND DN to the PAM identity */
+    int pamptconfig_map_method2; /* how to map the BIND DN to the PAM identity */
+    int pamptconfig_map_method3; /* how to map the BIND DN to the PAM identity */
 #define PAMPT_MAP_METHOD_NONE -1 /* do not map */
 #define PAMPT_MAP_METHOD_DN 0 /* use the full DN as the PAM identity */
 #define PAMPT_MAP_METHOD_RDN 1 /* use the leftmost RDN value as the PAM identity */
 #define PAMPT_MAP_METHOD_ENTRY 2 /* use the PAM identity attribute in the entry */
-	char *pamptconfig_service; /* the PAM service name for pam_start() */
+    char *pamptconfig_service; /* the PAM service name for pam_start() */
 } Pam_PassthruConfig;
 
 #define PAMPT_MAP_METHOD_DN_STRING "DN"
@@ -116,6 +129,7 @@ typedef struct pam_passthruconfig {
 #define PAMPT_FALLBACK_ATTR "pamFallback" /* single */
 #define PAMPT_SECURE_ATTR "pamSecure" /* single */
 #define PAMPT_SERVICE_ATTR "pamService" /* single */
+#define PAMPT_FILTER_ATTR "pamFilter" /* single */
 
 /*
  * public functions
@@ -123,13 +137,23 @@ typedef struct pam_passthruconfig {
 
 void pam_passthruauth_set_plugin_identity(void * identity);
 void * pam_passthruauth_get_plugin_identity();
+void pam_passthruauth_set_plugin_sdn(const Slapi_DN *plugin_sdn);
+const Slapi_DN *pam_passthruauth_get_plugin_sdn();
+const char *pam_passthruauth_get_plugin_dn();
+void pam_passthru_read_lock();
+void pam_passthru_write_lock();
+void pam_passthru_unlock();
 
 /*
  * pam_ptconfig.c:
  */
-int pam_passthru_config( Slapi_Entry *config_e );
-Pam_PassthruConfig *pam_passthru_get_config( void );
-int pam_passthru_check_suffix(Pam_PassthruConfig *cfg, const char *binddn);
+int pam_passthru_load_config(int skip_validate);
+void pam_passthru_delete_config();
+Pam_PassthruConfig *pam_passthru_get_config( Slapi_DN *bind_sdn );
+int pam_passthru_validate_config (Slapi_Entry* e, char *returntext);
+int pam_passthru_dn_is_config(Slapi_DN *sdn);
+void pam_passthru_set_config_area(Slapi_DN *sdn);
+Slapi_DN* pam_passthru_get_config_area();
 
 /*
  * pam_ptimpl.c
diff --git a/ldap/servers/plugins/pam_passthru/pam_ptconfig.c b/ldap/servers/plugins/pam_passthru/pam_ptconfig.c
index bde2ef6..fce8000 100644
--- a/ldap/servers/plugins/pam_passthru/pam_ptconfig.c
+++ b/ldap/servers/plugins/pam_passthru/pam_ptconfig.c
@@ -52,102 +52,170 @@
 
 /*
  * The configuration attributes are contained in the plugin entry e.g.
- * cn=PAM Pass Through,cn=plugins,cn=config
+ * cn=PAM Pass Through,cn=plugins,cn=config, or an alternate config area.
  *
  * Configuration is a two step process.  The first pass is a validation step which
  * occurs pre-op - check inputs and error out if bad.  The second pass actually
  * applies the changes to the run time config.
  */
 
+static Slapi_DN *_ConfigArea = NULL;
 
 /*
  * function prototypes
  */ 
-static int pam_passthru_validate_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, 
-										 int *returncode, char *returntext, void *arg);
-static int pam_passthru_apply_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, 
-										 int *returncode, char *returntext, void *arg);
-static int pam_passthru_search (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, 
-								int *returncode, char *returntext, void *arg)
-{
-	return SLAPI_DSE_CALLBACK_OK;
-}
+static int pam_passthru_apply_config (Slapi_Entry* e);
 
 /*
- * static variables
+ * Read and load configuration.  Validation will also
+ * be performed unless skip_validate is set to non-0.
+ * Returns PAM_PASSTHRU_SUCCESS if all is well.
  */
-/* for now, there is only one configuration and it is global to the plugin  */
-static Pam_PassthruConfig	theConfig;
-static int		inited = 0;
+int
+pam_passthru_load_config(int skip_validate)
+{
+    int status = PAM_PASSTHRU_SUCCESS;
+    int result;
+    int i;
+    int alternate = 0;
+    Slapi_PBlock *search_pb;
+    Slapi_Entry **entries = NULL;
+
+    slapi_log_error( SLAPI_LOG_TRACE, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+                     "=> pam_passthru_load_config\n");
+
+    pam_passthru_write_lock();
+    pam_passthru_delete_config();
+
+    search_pb = slapi_pblock_new();
+
+    /* Find all entries in the active config area. */
+    slapi_search_internal_set_pb(search_pb, slapi_sdn_get_ndn(pam_passthru_get_config_area()),
+                                 LDAP_SCOPE_SUBTREE, "objectclass=*",
+                                 NULL, 0, NULL, NULL,
+                                 pam_passthruauth_get_plugin_identity(), 0);
+    slapi_search_internal_pb(search_pb);
+    slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
+
+    if (LDAP_SUCCESS != result) {
+        status = PAM_PASSTHRU_FAILURE;
+        goto cleanup;
+    }
+
+    slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES,
+                     &entries);
+    if (NULL == entries || NULL == entries[0]) {
+        status = PAM_PASSTHRU_FAILURE;
+        goto cleanup;
+    }
+
+    /* Check if we are using an alternate config area.  We do this here
+     * so we don't have to check each every time in the loop below. */
+    if (slapi_sdn_compare(pam_passthru_get_config_area(),
+            pam_passthruauth_get_plugin_sdn()) != 0) {
+        alternate = 1;
+    }
 
+    /* Validate and apply config if valid.  If skip_validate is set, we skip
+     * validation and just apply the config.  This should only be done if the
+     * configuration has already been validated. */
+    for (i = 0; (entries[i] != NULL); i++) {
+        /* If this is the alternate config container, skip it since
+         * we don't consider it to be an actual config entry. */
+        if (alternate && (slapi_sdn_compare(pam_passthru_get_config_area(),
+                slapi_entry_get_sdn(entries[i])) == 0)) {
+            continue;
+        }
+
+        if (skip_validate || (PAM_PASSTHRU_SUCCESS == pam_passthru_validate_config(entries[i], NULL))) {
+            if (PAM_PASSTHRU_FAILURE == pam_passthru_apply_config(entries[i])) {
+                slapi_log_error( SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+                                 "pam_passthru_load_config: unable to apply config "
+                                 "for entry \"%s\"\n", slapi_entry_get_ndn(entries[i]));
+            }
+        } else {
+            slapi_log_error( SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+                             "pam_passthru_load_config: skipping invalid config "
+                             "entry \"%s\"\n", slapi_entry_get_ndn(entries[i]));
+        }
+    }
+
+  cleanup:
+    slapi_free_search_results_internal(search_pb);
+    slapi_pblock_destroy(search_pb);
+    pam_passthru_unlock();
+    slapi_log_error(SLAPI_LOG_TRACE, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+                    "<= pam_passthru_load_config\n");
+
+    return status;
+}
+
+static void
+Delete_Pam_PassthruSuffix(Pam_PassthruSuffix *one)
+{
+    if (one) {
+        slapi_sdn_free(&one->pamptsuffix_dn);
+        slapi_ch_free((void **)&one);
+    }
+}
 
-static int dont_allow_that(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, 
-						   int *returncode, char *returntext, void *arg)
+static void
+pam_ptconfig_free_suffixes(Pam_PassthruSuffix *list)
 {
-	*returncode = LDAP_UNWILLING_TO_PERFORM;
-    return SLAPI_DSE_CALLBACK_ERROR;
+    while (list) {
+        Pam_PassthruSuffix *next = list->pamptsuffix_next;
+        Delete_Pam_PassthruSuffix(list);
+        list = next;
+    }
 }
 
 /*
- * Read configuration and create a configuration data structure.
- * This is called after the server has configured itself so we can check
- *   for things like collisions between our suffixes and backend's suffixes.
- * Returns an LDAP error code (LDAP_SUCCESS if all goes well).
+ * Free a config struct.
  */
-int
-pam_passthru_config(Slapi_Entry *config_e)
+static void
+pam_passthru_free_config_entry(Pam_PassthruConfig **entry)
 {
-	int returncode = LDAP_SUCCESS;
-	char returntext[SLAPI_DSE_RETURNTEXT_SIZE];
+    Pam_PassthruConfig *e = *entry;
 
-    if ( inited ) {
-		slapi_log_error( SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
-						 "only one PAM pass through plugin instance can be used\n" );
-		return( LDAP_PARAM_ERROR );
+    if (e == NULL) {
+        return;
     }
 
-	/* initialize fields */
-	if ((theConfig.lock = slapi_new_mutex()) == NULL) {
-	    return( LDAP_LOCAL_ERROR );
-	}
-	/* do not fallback to regular bind */
-	theConfig.pamptconfig_fallback = PR_FALSE;
-	/* require TLS/SSL security */
-	theConfig.pamptconfig_secure = PR_TRUE;
-	/* use the RDN method to derive the PAM identity */
-	theConfig.pamptconfig_map_method1 = PAMPT_MAP_METHOD_RDN;
-	theConfig.pamptconfig_map_method2 = PAMPT_MAP_METHOD_NONE;
-	theConfig.pamptconfig_map_method3 = PAMPT_MAP_METHOD_NONE;
-
-	if (SLAPI_DSE_CALLBACK_OK == pam_passthru_validate_config(NULL, NULL, config_e,
-															  &returncode, returntext, NULL)) {
-		pam_passthru_apply_config(NULL, NULL, config_e,
-								  &returncode, returntext, NULL);
-	}
+    slapi_ch_free_string(&e->dn);
+    pam_ptconfig_free_suffixes(e->pamptconfig_includes);
+    pam_ptconfig_free_suffixes(e->pamptconfig_excludes);
+    slapi_ch_free_string(&e->pamptconfig_pam_ident_attr);
+    slapi_ch_free_string(&e->pamptconfig_service);
+    slapi_ch_free_string(&e->filter_str);
+    slapi_filter_free(e->slapi_filter, 1);
 
-	/* config DSE must be initialized before we get here */
-	if (returncode == LDAP_SUCCESS) {
-		const char *config_dn = slapi_entry_get_dn_const(config_e);
-		slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, config_dn, LDAP_SCOPE_BASE,
-									   PAM_PT_CONFIG_FILTER, pam_passthru_validate_config,NULL);
-		slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_POSTOP, config_dn, LDAP_SCOPE_BASE,
-									   PAM_PT_CONFIG_FILTER, pam_passthru_apply_config,NULL);
-		slapi_config_register_callback(SLAPI_OPERATION_MODRDN, DSE_FLAG_PREOP, config_dn, LDAP_SCOPE_BASE,
-									   PAM_PT_CONFIG_FILTER, dont_allow_that, NULL);
-		slapi_config_register_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP, config_dn, LDAP_SCOPE_BASE,
-									   PAM_PT_CONFIG_FILTER, dont_allow_that, NULL);
-		slapi_config_register_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, config_dn, LDAP_SCOPE_BASE,
-									   PAM_PT_CONFIG_FILTER, pam_passthru_search,NULL);
-	}
+    slapi_ch_free((void **) entry);
+}
 
-    inited = 1;
+/*
+ * Free and remove a single config item from the list.
+ */
+static void
+pam_passthru_delete_configEntry(PRCList *entry)
+{
+    PR_REMOVE_LINK(entry);
+    pam_passthru_free_config_entry((Pam_PassthruConfig **) &entry);
+}
 
-	if (returncode != LDAP_SUCCESS) {
-		slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
-						"Error %d: %s\n", returncode, returntext);
-	}
+/*
+ * Delete the entire config list contents.
+ */
+void
+pam_passthru_delete_config()
+{
+    PRCList *list;
 
-    return returncode;
+    while (!PR_CLIST_IS_EMPTY(pam_passthru_global_config)) {
+        list = PR_LIST_HEAD(pam_passthru_global_config);
+        pam_passthru_delete_configEntry(list);
+    }
+
+    return;
 }
 
 static int
@@ -230,39 +298,67 @@ meth_to_int(char **map_method, int *err)
 static int
 parse_map_method(char *map_method, int *one, int *two, int *three, char *returntext)
 {
-	int err = LDAP_SUCCESS;
+	int err = PAM_PASSTHRU_SUCCESS;
 	char **ptr = &map_method;
 
 	*one = *two = *three = PAMPT_MAP_METHOD_NONE;
 	*one = meth_to_int(ptr, &err);
 	if (err) {
-		PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
-					"The map method in the string [%s] is invalid: must be "
-					"one of %s", map_method, get_map_method_values());
-		return LDAP_UNWILLING_TO_PERFORM;
+		if (returntext) {
+			PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+						"The map method in the string [%s] is invalid: must be "
+						"one of %s", map_method, get_map_method_values());
+		} else {
+			slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+						"The map method in the string [%s] is invalid: must be "
+						"one of %s\n", map_method, get_map_method_values());
+		}
+		err = PAM_PASSTHRU_FAILURE;
+		goto bail;
 	}
 	*two = meth_to_int(ptr, &err);
 	if (err) {
-		PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
-					"The map method in the string [%s] is invalid: must be "
-					"one of %s", map_method, get_map_method_values());
-		return LDAP_UNWILLING_TO_PERFORM;
+		if (returntext) {
+			PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+						"The map method in the string [%s] is invalid: must be "
+						"one of %s", map_method, get_map_method_values());
+		} else {
+			slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+						"The map method in the string [%s] is invalid: must be "
+						"one of %s\n", map_method, get_map_method_values());
+		}
+		err = PAM_PASSTHRU_FAILURE;
+                goto bail;
 	}
 	*three = meth_to_int(ptr, &err);
 	if (err) {
-		PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
-					"The map method in the string [%s] is invalid: must be "
-					"one of %s", map_method, get_map_method_values());
-		return LDAP_UNWILLING_TO_PERFORM;
+		if (returntext) {
+			PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+						"The map method in the string [%s] is invalid: must be "
+						"one of %s", map_method, get_map_method_values());
+		} else {
+			slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+						"The map method in the string [%s] is invalid: must be "
+						"one of %s\n", map_method, get_map_method_values());
+		}
+		err = PAM_PASSTHRU_FAILURE;
+                goto bail;
 	}
-	if ((meth_to_int(ptr, &err) != PAMPT_MAP_METHOD_NONE) ||
-		err) {
-		PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
-					"Invalid extra text [%s] after last map method",
-					((ptr && *ptr) ? *ptr : "(null)"));
-		return LDAP_UNWILLING_TO_PERFORM;		
+	if ((meth_to_int(ptr, &err) != PAMPT_MAP_METHOD_NONE) || err) {
+		if (returntext) {
+			PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+						"Invalid extra text [%s] after last map method",
+						((ptr && *ptr) ? *ptr : "(null)"));
+		} else {
+			slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+						"Invalid extra text [%s] after last map method\n",
+						((ptr && *ptr) ? *ptr : "(null)"));
+		}
+		err = PAM_PASSTHRU_FAILURE;
+                goto bail;
 	}
 
+  bail:
 	return err;
 }
 
@@ -283,12 +379,14 @@ print_suffixes()
 }
 
 /*
-  Validate the pending changes in the e entry.
-*/
-static int 
-pam_passthru_validate_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, 
-	int *returncode, char *returntext, void *arg)
+ * Validate the pending changes in the e entry.
+ * If returntext is NULL, we log messages about invalid config
+ * to the errors log.
+ */
+int 
+pam_passthru_validate_config (Slapi_Entry* e, char *returntext)
 {
+	int rc = PAM_PASSTHRU_FAILURE;
 	char *missing_suffix_str = NULL;
 	int missing_suffix;
 	int ii;
@@ -296,15 +394,22 @@ pam_passthru_validate_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_
 	char **includes = NULL;
 	char *pam_ident_attr = NULL;
 	char *map_method = NULL;
+	char *pam_filter_str = NULL;
+	Slapi_Filter *pam_filter = NULL;
 
-	*returncode = LDAP_UNWILLING_TO_PERFORM; /* be pessimistic */
 	/* first, get the missing_suffix flag and validate it */
 	missing_suffix_str = slapi_entry_attr_get_charptr(e, PAMPT_MISSING_SUFFIX_ATTR);
 	if ((missing_suffix = missing_suffix_to_int(missing_suffix_str)) < 0 ||
 		!check_missing_suffix_flag(missing_suffix)) {
-		PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+		if (returntext) {
+			PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
 					"Error: valid values for %s are %s",
 					PAMPT_MISSING_SUFFIX_ATTR, get_missing_suffix_values());
+		} else {
+			slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+					"Error: valid values for %s are %s\n",
+					PAMPT_MISSING_SUFFIX_ATTR, get_missing_suffix_values());
+		}
 		goto done;
 	}
 
@@ -314,7 +419,8 @@ pam_passthru_validate_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_
 		/* get the list of excluded suffixes */
 		excludes = slapi_entry_attr_get_charray(e, PAMPT_EXCLUDES_ATTR);
 		for (ii = 0; excludes && excludes[ii]; ++ii) {
-			Slapi_DN *comp_dn = slapi_sdn_new_dn_byref(excludes[ii]);
+			/* The excludes DNs are already normalized. */
+			Slapi_DN *comp_dn = slapi_sdn_new_normdn_byref(excludes[ii]);
 			if (!slapi_be_exist(comp_dn)) {
 				charray_add(&missing_list, slapi_ch_strdup(excludes[ii]));
 			}
@@ -324,7 +430,8 @@ pam_passthru_validate_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_
 		/* get the list of included suffixes */
 		includes = slapi_entry_attr_get_charray(e, PAMPT_INCLUDES_ATTR);
 		for (ii = 0; includes && includes[ii]; ++ii) {
-			Slapi_DN *comp_dn = slapi_sdn_new_dn_byref(includes[ii]);
+			/* The includes DNs are already normalized. */
+			Slapi_DN *comp_dn = slapi_sdn_new_normdn_byref(includes[ii]);
 			if (!slapi_be_exist(comp_dn)) {
 				charray_add(&missing_list, slapi_ch_strdup(includes[ii]));
 			}
@@ -332,24 +439,33 @@ pam_passthru_validate_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_
 		}
 
 		if (missing_list) {
-			PRUint32 size =
-				PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
-							"The following suffixes listed in %s or %s are not present in this "
-							"server: ", PAMPT_EXCLUDES_ATTR, PAMPT_INCLUDES_ATTR);
-			for (ii = 0; missing_list[ii]; ++ii) {
-				if (size < SLAPI_DSE_RETURNTEXT_SIZE) {
-					size += PR_snprintf(returntext+size, SLAPI_DSE_RETURNTEXT_SIZE-size,
-										"%s%s", (ii > 0) ? "; " : "",
-										missing_list[ii]);
+			if (returntext) {
+				PRUint32 size =
+					PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+								"The following suffixes listed in %s or %s are not present in this "
+								"server: ", PAMPT_EXCLUDES_ATTR, PAMPT_INCLUDES_ATTR);
+				for (ii = 0; missing_list[ii]; ++ii) {
+					if (size < SLAPI_DSE_RETURNTEXT_SIZE) {
+						size += PR_snprintf(returntext+size, SLAPI_DSE_RETURNTEXT_SIZE-size,
+											"%s%s", (ii > 0) ? "; " : "",
+											missing_list[ii]);
+					}
 				}
+			} else {
+				slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+							"The suffixes listed in %s or %s are not present in "
+							"this server\n", PAMPT_EXCLUDES_ATTR, PAMPT_INCLUDES_ATTR);
 			}
+
 			slapi_ch_array_free(missing_list);
 			missing_list = NULL;
 			print_suffixes();
 			if (missing_suffix != PAMPT_MISSING_SUFFIX_ERROR) {
-				slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
-								"Warning: %s\n", returntext);
-				*returntext = 0; /* log error, don't report back to user */
+				if (returntext) {
+					slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+									"Warning: %s\n", returntext);
+					*returntext = 0; /* log error, don't report back to user */
+				}
 			} else {
 				goto done;
 			}
@@ -360,31 +476,64 @@ pam_passthru_validate_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_
 	map_method = slapi_entry_attr_get_charptr(e, PAMPT_MAP_METHOD_ATTR);
 	if (map_method) {
 		int one, two, three;
-		if (LDAP_SUCCESS !=
-			(*returncode = parse_map_method(map_method, &one, &two, &three, returntext))) {
-			goto done; /* returntext set already */
+		if (PAM_PASSTHRU_SUCCESS !=
+			(rc = parse_map_method(map_method, &one, &two, &three, returntext))) {
+			goto done; /* returntext set already (or error logged) */
 		}
 		if (!pam_ident_attr &&
 			((one == PAMPT_MAP_METHOD_ENTRY) || (two == PAMPT_MAP_METHOD_ENTRY) ||
 			 (three == PAMPT_MAP_METHOD_ENTRY))) {
-			PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, "Error: the %s method"
-						" was specified, but no %s was given",
-						PAMPT_MAP_METHOD_ENTRY_STRING, PAMPT_PAM_IDENT_ATTR);
-			*returncode = LDAP_UNWILLING_TO_PERFORM;
+			if (returntext) {
+				PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, "Error: the %s method"
+							" was specified, but no %s was given",
+							PAMPT_MAP_METHOD_ENTRY_STRING, PAMPT_PAM_IDENT_ATTR);
+			} else {
+				slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+							"Error: the %s method was specified, but no %s was given\n",
+							PAMPT_MAP_METHOD_ENTRY_STRING, PAMPT_PAM_IDENT_ATTR);
+			}
+			rc = PAM_PASSTHRU_FAILURE;
 			goto done;
 		}
 		if ((one == PAMPT_MAP_METHOD_NONE) && (two == PAMPT_MAP_METHOD_NONE) &&
 			(three == PAMPT_MAP_METHOD_NONE)) {
-			PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, "Error: no method(s)"
-						" specified for %s, should be one or more of %s",
-						PAMPT_MAP_METHOD_ATTR, get_map_method_values());
-			*returncode = LDAP_UNWILLING_TO_PERFORM;
+			if (returntext) {
+				PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, "Error: no method(s)"
+							" specified for %s, should be one or more of %s",
+							PAMPT_MAP_METHOD_ATTR, get_map_method_values());
+			} else {
+				slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+							"Error: no method(s) specified for %s, should be "
+							"one or more of %s\n", PAMPT_MAP_METHOD_ATTR,
+							get_map_method_values());
+			}
+			rc = PAM_PASSTHRU_FAILURE;
+			goto done;
+		}
+	}
+
+	/* Validate filter by converting to Slapi_Filter */
+	pam_filter_str = slapi_entry_attr_get_charptr(e, PAMPT_FILTER_ATTR);
+	if (pam_filter_str) {
+		pam_filter = slapi_str2filter(pam_filter_str);
+		if (pam_filter == NULL) {
+			if (returntext) {
+				PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, "Error: invalid "
+							"filter specified for %s (filter: \"%s\")",
+							PAMPT_FILTER_ATTR, pam_filter_str);
+			} else {
+				slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+							"Error: invalid filter specified for %s "
+							"(filter: \"%s\")\n", PAMPT_FILTER_ATTR,
+							pam_filter_str);
+			}
+			rc = PAM_PASSTHRU_FAILURE;
 			goto done;
 		}
 	}
 
 	/* success */
-	*returncode = LDAP_SUCCESS;
+	rc = PAM_PASSTHRU_SUCCESS;
 
 done:
 	slapi_ch_free_string(&map_method);
@@ -394,15 +543,10 @@ done:
 	slapi_ch_array_free(includes);
 	includes = NULL;
 	slapi_ch_free_string(&missing_suffix_str);
+	slapi_ch_free_string(&pam_filter_str);
+	slapi_filter_free(pam_filter, 1);
 
-	if (*returncode != LDAP_SUCCESS)
-	{
-		return SLAPI_DSE_CALLBACK_ERROR;
-	}
-	else
-    {
-	    return SLAPI_DSE_CALLBACK_OK;
-    }
+	return rc;
 }
 
 static Pam_PassthruSuffix *
@@ -411,7 +555,8 @@ New_Pam_PassthruSuffix(char *suffix)
 	Pam_PassthruSuffix *newone = NULL;
 	if (suffix) {
 		newone = (Pam_PassthruSuffix *)slapi_ch_malloc(sizeof(Pam_PassthruSuffix));
-		newone->pamptsuffix_dn = slapi_sdn_new_dn_byval(suffix);
+		/* The passed in suffix should already be normalized. */
+		newone->pamptsuffix_dn = slapi_sdn_new_normdn_byval(suffix);
 		newone->pamptsuffix_next = NULL;
 	}
 	return newone;
@@ -438,115 +583,139 @@ pam_ptconfig_add_suffixes(char **str_list)
 	return head;
 }
 
-static void
-Delete_Pam_PassthruSuffix(Pam_PassthruSuffix *one)
-{
-	if (one) {
-		slapi_sdn_free(&one->pamptsuffix_dn);
-		slapi_ch_free((void **)&one);
-	}
-}
-
-static void
-pam_ptconfig_free_suffixes(Pam_PassthruSuffix *list)
-{
-	while (list) {
-		Pam_PassthruSuffix *next = list->pamptsuffix_next;
-		Delete_Pam_PassthruSuffix(list);
-		list = next;
-	}
-}
-
 /*
   Apply the pending changes in the e entry to our config struct.
   validate must have already been called
 */
 static int 
-pam_passthru_apply_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, 
-	int *returncode, char *returntext, void *arg)
+pam_passthru_apply_config (Slapi_Entry* e)
 {
-	char **excludes = NULL;
-	char **includes = NULL;
-	char *new_service = NULL;
-	char *pam_ident_attr = NULL;
-	char *map_method = NULL;
-	PRBool fallback;
-	PRBool secure;
+    int rc = PAM_PASSTHRU_SUCCESS;
+    char **excludes = NULL;
+    char **includes = NULL;
+    char *new_service = NULL;
+    char *pam_ident_attr = NULL;
+    char *map_method = NULL;
+    char *dn = NULL;
+    PRBool fallback;
+    PRBool secure;
+    Pam_PassthruConfig *entry = NULL;
+    PRCList *list;
+    Slapi_Attr *a = NULL;
+    char *filter_str = NULL;
+
+    pam_ident_attr = slapi_entry_attr_get_charptr(e, PAMPT_PAM_IDENT_ATTR);
+    map_method = slapi_entry_attr_get_charptr(e, PAMPT_MAP_METHOD_ATTR);
+    new_service = slapi_entry_attr_get_charptr(e, PAMPT_SERVICE_ATTR);
+    excludes = slapi_entry_attr_get_charray(e, PAMPT_EXCLUDES_ATTR);
+    includes = slapi_entry_attr_get_charray(e, PAMPT_INCLUDES_ATTR);
+    fallback = slapi_entry_attr_get_bool(e, PAMPT_FALLBACK_ATTR);
+    filter_str = slapi_entry_attr_get_charptr(e, PAMPT_FILTER_ATTR);
+    /* Require SSL/TLS if the secure attr is not specified.  We
+     * need to check if the attribute is present to make this
+     * determiniation. */
+    if (slapi_entry_attr_find(e, PAMPT_SECURE_ATTR, &a) == 0) {
+        secure = slapi_entry_attr_get_bool(e, PAMPT_SECURE_ATTR);
+    } else {
+        secure = PR_TRUE;
+    }
 
-	*returncode = LDAP_SUCCESS;
+    /* Allocate a config struct. */
+    entry = (Pam_PassthruConfig *)
+        slapi_ch_calloc(1, sizeof(Pam_PassthruConfig));
+    if (NULL == entry) {
+        rc = PAM_PASSTHRU_FAILURE;
+        goto bail;
+    }
 
-	pam_ident_attr = slapi_entry_attr_get_charptr(e, PAMPT_PAM_IDENT_ATTR);
-	map_method = slapi_entry_attr_get_charptr(e, PAMPT_MAP_METHOD_ATTR);
-	new_service = slapi_entry_attr_get_charptr(e, PAMPT_SERVICE_ATTR);
-	excludes = slapi_entry_attr_get_charray(e, PAMPT_EXCLUDES_ATTR);
-	includes = slapi_entry_attr_get_charray(e, PAMPT_INCLUDES_ATTR);
-	fallback = slapi_entry_attr_get_bool(e, PAMPT_FALLBACK_ATTR);
-	secure = slapi_entry_attr_get_bool(e, PAMPT_SECURE_ATTR);
-
-	/* lock config here */
-	slapi_lock_mutex(theConfig.lock);
-
-	theConfig.pamptconfig_fallback = fallback;
-	theConfig.pamptconfig_secure = secure;
-	if (!theConfig.pamptconfig_service ||
-		(new_service && PL_strcmp(theConfig.pamptconfig_service, new_service))) {
-		slapi_ch_free_string(&theConfig.pamptconfig_service);
-		theConfig.pamptconfig_service = new_service;
-		new_service = NULL; /* config now owns memory */
-	}
+    /* use the RDN method to derive the PAM identity by default*/
+    entry->pamptconfig_map_method1 = PAMPT_MAP_METHOD_RDN;
+    entry->pamptconfig_map_method2 = PAMPT_MAP_METHOD_NONE;
+    entry->pamptconfig_map_method3 = PAMPT_MAP_METHOD_NONE;
 
-	/* get the list of excluded suffixes */
-	pam_ptconfig_free_suffixes(theConfig.pamptconfig_excludes);
-	theConfig.pamptconfig_excludes = pam_ptconfig_add_suffixes(excludes);
+    /* Fill in the struct. */
+    dn = slapi_entry_get_ndn(e);
+    if (dn) {
+        entry->dn = slapi_ch_strdup(dn);
+    }
 
-	/* get the list of included suffixes */
-	pam_ptconfig_free_suffixes(theConfig.pamptconfig_includes);
-	theConfig.pamptconfig_includes = pam_ptconfig_add_suffixes(includes);
+    entry->pamptconfig_fallback = fallback;
+    entry->pamptconfig_secure = secure;
 
-	if (!theConfig.pamptconfig_pam_ident_attr ||
-		(pam_ident_attr && PL_strcmp(theConfig.pamptconfig_pam_ident_attr, pam_ident_attr))) {
-		slapi_ch_free_string(&theConfig.pamptconfig_pam_ident_attr);
-		theConfig.pamptconfig_pam_ident_attr = pam_ident_attr;
-		pam_ident_attr = NULL; /* config now owns memory */
-	}
+    if (!entry->pamptconfig_service ||
+        (new_service && PL_strcmp(entry->pamptconfig_service, new_service))) {
+        slapi_ch_free_string(&entry->pamptconfig_service);
+        entry->pamptconfig_service = new_service;
+        new_service = NULL; /* config now owns memory */
+    }
 
-	if (map_method) {
-		parse_map_method(map_method,
-						 &theConfig.pamptconfig_map_method1,
-						 &theConfig.pamptconfig_map_method2,
-						 &theConfig.pamptconfig_map_method3,
-						 NULL);
-	}
+    /* get the list of excluded suffixes */
+    pam_ptconfig_free_suffixes(entry->pamptconfig_excludes);
+    entry->pamptconfig_excludes = pam_ptconfig_add_suffixes(excludes);
 
-	/* unlock config here */
-	slapi_unlock_mutex(theConfig.lock);
+    /* get the list of included suffixes */
+    pam_ptconfig_free_suffixes(entry->pamptconfig_includes);
+    entry->pamptconfig_includes = pam_ptconfig_add_suffixes(includes);
 
-	slapi_ch_free_string(&new_service);
-	slapi_ch_free_string(&map_method);
-	slapi_ch_free_string(&pam_ident_attr);
-	slapi_ch_array_free(excludes);
-	slapi_ch_array_free(includes);
+    if (!entry->pamptconfig_pam_ident_attr ||
+        (pam_ident_attr && PL_strcmp(entry->pamptconfig_pam_ident_attr, pam_ident_attr))) {
+        slapi_ch_free_string(&entry->pamptconfig_pam_ident_attr);
+        entry->pamptconfig_pam_ident_attr = pam_ident_attr;
+        pam_ident_attr = NULL; /* config now owns memory */
+    }
 
-	if (*returncode != LDAP_SUCCESS)
-	{
-		return SLAPI_DSE_CALLBACK_ERROR;
-	}
-	else
-    {
-	    return SLAPI_DSE_CALLBACK_OK;
+    if (map_method) {
+        parse_map_method(map_method,
+        &entry->pamptconfig_map_method1,
+        &entry->pamptconfig_map_method2,
+        &entry->pamptconfig_map_method3,
+        NULL);
     }
+
+    if (filter_str) {
+        entry->filter_str = filter_str;
+        filter_str = NULL; /* config now owns memory */
+        entry->slapi_filter = slapi_str2filter(entry->filter_str);
+    }
+
+    /* Add config to list.  We just store at the tail. */
+    if (!PR_CLIST_IS_EMPTY(pam_passthru_global_config)) {
+        list = PR_LIST_HEAD(pam_passthru_global_config);
+        while (list != pam_passthru_global_config) {
+            list = PR_NEXT_LINK(list);
+
+            if (pam_passthru_global_config == list) {
+                /* add to tail */
+                PR_INSERT_BEFORE(&(entry->list), list);
+                slapi_log_error(SLAPI_LOG_CONFIG, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+                                "store [%s] at tail\n", entry->dn);
+                break;
+            }
+        }
+    } else {
+        /* first entry */
+        PR_INSERT_LINK(&(entry->list), pam_passthru_global_config);
+        slapi_log_error(SLAPI_LOG_CONFIG, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+                        "store [%s] at head \n", entry->dn);
+    }
+
+  bail:
+    slapi_ch_free_string(&new_service);
+    slapi_ch_free_string(&map_method);
+    slapi_ch_free_string(&pam_ident_attr);
+    slapi_ch_free_string(&filter_str);
+    slapi_ch_array_free(excludes);
+    slapi_ch_array_free(includes);
+
+    return rc;
 }
 
-int
-pam_passthru_check_suffix(Pam_PassthruConfig *cfg, const char *binddn)
+static int
+pam_passthru_check_suffix(Pam_PassthruConfig *cfg, const Slapi_DN *bindsdn)
 {
-	Slapi_DN *comp_dn;
 	Pam_PassthruSuffix *try;
 	int ret = LDAP_SUCCESS;
 
-	comp_dn = slapi_sdn_new_dn_byref(binddn);
-
-	slapi_lock_mutex(cfg->lock);
 	if (!cfg->pamptconfig_includes && !cfg->pamptconfig_excludes) {
 		goto done; /* NULL means allow */
 	}
@@ -554,7 +723,7 @@ pam_passthru_check_suffix(Pam_PassthruConfig *cfg, const char *binddn)
 	/* exclude trumps include - if suffix is on exclude list, then
 	   deny */
 	for (try = cfg->pamptconfig_excludes; try; try = try->pamptsuffix_next) {
-		if (slapi_sdn_issuffix(comp_dn, try->pamptsuffix_dn)) {
+		if (slapi_sdn_issuffix(bindsdn, try->pamptsuffix_dn)) {
 			ret = LDAP_UNWILLING_TO_PERFORM; /* suffix is excluded */
 			goto done;
 		}
@@ -564,7 +733,7 @@ pam_passthru_check_suffix(Pam_PassthruConfig *cfg, const char *binddn)
 	if (cfg->pamptconfig_includes) {
 		ret = LDAP_UNWILLING_TO_PERFORM; /* suffix is excluded */
 		for (try = cfg->pamptconfig_includes; try; try = try->pamptsuffix_next) {
-			if (slapi_sdn_issuffix(comp_dn, try->pamptsuffix_dn)) {
+			if (slapi_sdn_issuffix(bindsdn, try->pamptsuffix_dn)) {
 				ret = LDAP_SUCCESS; /* suffix is included */
 				goto done;
 			}
@@ -572,18 +741,116 @@ pam_passthru_check_suffix(Pam_PassthruConfig *cfg, const char *binddn)
 	}
 		
 done:
-	slapi_unlock_mutex(cfg->lock);
-	slapi_sdn_free(&comp_dn);
 
 	return ret;
 }
 
+
 /*
- * Get the pass though configuration data.  For now, there is only one
- * configuration and it is global to the plugin.
+ * Find the config entry that matches the passed in bind DN
  */
 Pam_PassthruConfig *
-pam_passthru_get_config( void )
+pam_passthru_get_config( Slapi_DN *bind_sdn )
 {
-    return( &theConfig );
+    PRCList *list = NULL;
+    Pam_PassthruConfig *cfg = NULL;
+
+    /* Loop through config list to see if there is a match. */
+    if (!PR_CLIST_IS_EMPTY(pam_passthru_global_config)) {
+        list = PR_LIST_HEAD(pam_passthru_global_config);
+        while (list != pam_passthru_global_config) {
+            cfg = (Pam_PassthruConfig *)list;
+            if (pam_passthru_check_suffix( cfg, bind_sdn ) == LDAP_SUCCESS) {
+                if (cfg->slapi_filter) {
+                    /* A filter is configured, so see if the bind entry is a match. */
+                    Slapi_Entry *test_e = NULL;
+
+                    /* Fetch the bind entry */
+                    slapi_search_internal_get_entry(bind_sdn, NULL, &test_e,
+                                                    pam_passthruauth_get_plugin_identity());
+
+                    /* If the entry doesn't exist, just fall through to the main server code */
+                    if (test_e) {
+                        /* Evaluate the filter. */
+                        if (LDAP_SUCCESS == slapi_filter_test_simple(test_e, cfg-> slapi_filter)) {
+                            /* This is a match. */
+                            slapi_entry_free(test_e);
+                            goto done;
+                        }
+
+                        slapi_entry_free(test_e);
+                    }
+                } else {
+                    /* There is no filter to check, so this is a match. */
+                    goto done;
+                }
+            }
+
+            cfg = NULL;
+            list = PR_NEXT_LINK(list);
+        }
+    }
+
+  done:
+    return(cfg);
 }
+
+/*
+ * Check if the DN is considered to be a config entry.
+ *
+ * If the config is stored in cn=config, the top-level plug-in
+ * entry and it's children are considered to be config.  If an
+ * alternate plug-in config area is being used, only the children
+ * of the alternate config container are considered to be config.
+ *
+ * Returns 1 if DN is a config entry.
+ */
+int
+pam_passthru_dn_is_config(Slapi_DN *sdn)
+{
+    int rc = 0;
+
+    if (sdn == NULL) {
+        goto bail;
+    }
+
+    /* Check if we're using the standard config area. */
+    if (slapi_sdn_compare(pam_passthru_get_config_area(),
+            pam_passthruauth_get_plugin_sdn()) == 0) {
+        /* We're using the standard config area, so both
+         * the container and the children are considered
+         * to be config entries. */
+        if (slapi_sdn_issuffix(sdn, pam_passthru_get_config_area())) {
+            rc = 1;
+        }
+    } else {
+        /* We're using an alternative config area, so only
+         * the children are considered to be config entries. */
+        if (slapi_sdn_issuffix(sdn, pam_passthru_get_config_area()) &&
+                slapi_sdn_compare(sdn, pam_passthru_get_config_area())) {
+            rc = 1;
+        }
+    }
+
+  bail:
+    return rc;
+}
+
+/*
+ * Set the active config area.
+ */
+void
+pam_passthru_set_config_area(Slapi_DN *sdn)
+{
+    _ConfigArea = sdn;
+}
+
+/*
+ * Return the active config area.
+ */
+Slapi_DN *
+pam_passthru_get_config_area()
+{
+    return _ConfigArea;
+}
+
diff --git a/ldap/servers/plugins/pam_passthru/pam_ptimpl.c b/ldap/servers/plugins/pam_passthru/pam_ptimpl.c
index c0f03be..91dcc10 100644
--- a/ldap/servers/plugins/pam_passthru/pam_ptimpl.c
+++ b/ldap/servers/plugins/pam_passthru/pam_ptimpl.c
@@ -91,13 +91,13 @@ struct my_pam_conv_str {
  * Get the PAM identity from the value of the leftmost RDN in the BIND DN.
  */
 static char *
-derive_from_bind_dn(Slapi_PBlock *pb, const char *binddn, MyStrBuf *pam_id)
+derive_from_bind_dn(Slapi_PBlock *pb, const Slapi_DN *bindsdn, MyStrBuf *pam_id)
 {
 	Slapi_RDN *rdn;
 	char *type = NULL;
 	char *value = NULL;
 
-	rdn = slapi_rdn_new_dn(binddn);
+	rdn = slapi_rdn_new_sdn(bindsdn);
 	slapi_rdn_get_first(rdn, &type, &value);
 	init_my_str_buf(pam_id, value);
 	slapi_rdn_free(&rdn);
@@ -106,33 +106,30 @@ derive_from_bind_dn(Slapi_PBlock *pb, const char *binddn, MyStrBuf *pam_id)
 }
 
 static char *
-derive_from_bind_entry(Slapi_PBlock *pb, const char *binddn, 
+derive_from_bind_entry(Slapi_PBlock *pb, const Slapi_DN *bindsdn, 
                        MyStrBuf *pam_id, char *map_ident_attr, int *locked)
 {
 	char buf[BUFSIZ];
 	Slapi_Entry *entry = NULL;
-	Slapi_DN *sdn = slapi_sdn_new_dn_byref(binddn);
 	char *attrs[] = { NULL, NULL };
 	attrs[0] = map_ident_attr;
-	int rc = slapi_search_internal_get_entry(sdn, attrs, &entry,
+	int rc = slapi_search_internal_get_entry(bindsdn, attrs, &entry,
 											 pam_passthruauth_get_plugin_identity());
 
-	slapi_sdn_free(&sdn);
-
 	if (rc != LDAP_SUCCESS) {
 		slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
 						"Could not find BIND dn %s (error %d - %s)\n",
-						escape_string(binddn, buf), rc, ldap_err2string(rc));
+						escape_string(slapi_sdn_get_ndn(bindsdn), buf), rc, ldap_err2string(rc));
 		init_my_str_buf(pam_id, NULL);
    	} else if (NULL == entry) {
 		slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
 						"Could not find entry for BIND dn %s\n",
-						escape_string(binddn, buf));
+						escape_string(slapi_sdn_get_ndn(bindsdn), buf));
 		init_my_str_buf(pam_id, NULL);
 	} else if (slapi_check_account_lock( pb, entry, 0, 0, 0 ) == 1) {
 		slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
 						"Account %s inactivated.\n",
-						escape_string(binddn, buf));
+						escape_string(slapi_sdn_get_ndn(bindsdn), buf));
 		init_my_str_buf(pam_id, NULL);
 		*locked = 1;
 	} else {
@@ -286,9 +283,9 @@ do_one_pam_auth(
 	binddn = slapi_sdn_get_dn(bindsdn);
 
 	if (method == PAMPT_MAP_METHOD_RDN) {
-		derive_from_bind_dn(pb, binddn, &pam_id);
+		derive_from_bind_dn(pb, bindsdn, &pam_id);
 	} else if (method == PAMPT_MAP_METHOD_ENTRY) {
-		derive_from_bind_entry(pb, binddn, &pam_id, map_ident_attr, &locked);
+		derive_from_bind_entry(pb, bindsdn, &pam_id, map_ident_attr, &locked);
 	} else {
 		init_my_str_buf(&pam_id, binddn);
 	}
@@ -451,10 +448,7 @@ pam_passthru_do_pam_auth(Slapi_PBlock *pb, Pam_PassthruConfig *cfg)
 	int pw_response_requested;
 	LDAPControl **reqctrls = NULL;
 
-	/* first lock and get the methods and other info */
-	/* we do this so we can acquire and release the lock quickly to
-	   avoid potential deadlocks */
-	slapi_lock_mutex(cfg->lock);
+	/* get the methods and other info */
 	method1 = cfg->pamptconfig_map_method1;
 	method2 = cfg->pamptconfig_map_method2;
 	method3 = cfg->pamptconfig_map_method3;
@@ -464,8 +458,6 @@ pam_passthru_do_pam_auth(Slapi_PBlock *pb, Pam_PassthruConfig *cfg)
 
 	fallback = cfg->pamptconfig_fallback;
 
-	slapi_unlock_mutex(cfg->lock);
-
 	slapi_pblock_get (pb, SLAPI_REQCONTROLS, &reqctrls);
 	slapi_pblock_get (pb, SLAPI_PWPOLICY, &pw_response_requested);
 
diff --git a/ldap/servers/plugins/pam_passthru/pam_ptpreop.c b/ldap/servers/plugins/pam_passthru/pam_ptpreop.c
index d726017..687d24c 100644
--- a/ldap/servers/plugins/pam_passthru/pam_ptpreop.c
+++ b/ldap/servers/plugins/pam_passthru/pam_ptpreop.c
@@ -49,7 +49,15 @@
 static Slapi_PluginDesc pdesc = { "pam_passthruauth",  VENDOR, DS_PACKAGE_VERSION,
 	"PAM pass through authentication plugin" };
 
-static void * pam_passthruauth_plugin_identity = NULL;
+static void *pam_passthruauth_plugin_identity = NULL;
+static const Slapi_DN *pam_passthruauth_plugin_sdn = NULL;
+static Slapi_RWLock *g_pam_config_lock = NULL;
+
+/*
+ * Plug-in globals
+ */
+int g_pam_plugin_started = 0;
+PRCList *pam_passthru_global_config = NULL;
 
 /*
  * function prototypes
@@ -57,7 +65,14 @@ static void * pam_passthruauth_plugin_identity = NULL;
 static int pam_passthru_bindpreop( Slapi_PBlock *pb );
 static int pam_passthru_bindpreop_start( Slapi_PBlock *pb );
 static int pam_passthru_bindpreop_close( Slapi_PBlock *pb );
-
+static int pam_passthru_preop(Slapi_PBlock *pb, int modtype);
+static int pam_passthru_add_preop(Slapi_PBlock *pb);
+static int pam_passthru_mod_preop(Slapi_PBlock *pb);
+static int pam_passthru_del_preop(Slapi_PBlock *pb);
+static int pam_passthru_modrdn_preop(Slapi_PBlock *pb);
+static int pam_passthru_postop(Slapi_PBlock *pb);
+static int pam_passthru_internal_postop_init(Slapi_PBlock *pb);
+static int pam_passthru_postop_init(Slapi_PBlock *pb);
 
 /*
 ** Plugin identity mgmt
@@ -65,12 +80,27 @@ static int pam_passthru_bindpreop_close( Slapi_PBlock *pb );
 
 void pam_passthruauth_set_plugin_identity(void * identity) 
 {
-	pam_passthruauth_plugin_identity=identity;
+    pam_passthruauth_plugin_identity=identity;
 }
 
 void * pam_passthruauth_get_plugin_identity()
 {
-	return pam_passthruauth_plugin_identity;
+    return pam_passthruauth_plugin_identity;
+}
+
+void pam_passthruauth_set_plugin_sdn(const Slapi_DN *plugin_sdn)
+{
+    pam_passthruauth_plugin_sdn = plugin_sdn;
+}
+
+const Slapi_DN *pam_passthruauth_get_plugin_sdn()
+{
+    return pam_passthruauth_plugin_sdn;
+}
+
+const char *pam_passthruauth_get_plugin_dn()
+{
+    return slapi_sdn_get_ndn(pam_passthruauth_plugin_sdn);
 }
 
 /*
@@ -80,33 +110,125 @@ void * pam_passthruauth_get_plugin_identity()
 int
 pam_passthruauth_init( Slapi_PBlock *pb )
 {
+    int status = 0;
+
     PAM_PASSTHRU_ASSERT( pb != NULL );
 
-    slapi_log_error( SLAPI_LOG_PLUGIN, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
-	    "=> pam_passthruauth_init\n" );
+    slapi_log_error( SLAPI_LOG_TRACE, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+                     "=> pam_passthruauth_init\n" );
 
     slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &pam_passthruauth_plugin_identity);
     PR_ASSERT (pam_passthruauth_plugin_identity);
 
     if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
-		    (void *)SLAPI_PLUGIN_VERSION_01 ) != 0
-	    || slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
-		    (void *)&pdesc ) != 0
+            (void *)SLAPI_PLUGIN_VERSION_01 ) != 0
+            || slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+                    (void *)&pdesc ) != 0
 	    || slapi_pblock_set( pb, SLAPI_PLUGIN_START_FN,
-		    (void *)pam_passthru_bindpreop_start ) != 0
+                    (void *)pam_passthru_bindpreop_start ) != 0
 	    || slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_BIND_FN,
-		    (void *)pam_passthru_bindpreop ) != 0
+                    (void *)pam_passthru_bindpreop ) != 0
+            || slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_ADD_FN,
+                    (void *)pam_passthru_add_preop ) != 0
+            || slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_MODIFY_FN,
+                    (void *)pam_passthru_mod_preop ) != 0
+            || slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_DELETE_FN,
+                    (void *)pam_passthru_del_preop ) != 0
+            || slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_MODRDN_FN,
+                    (void *)pam_passthru_modrdn_preop ) != 0
 	    || slapi_pblock_set( pb, SLAPI_PLUGIN_CLOSE_FN,
-		    (void *)pam_passthru_bindpreop_close ) != 0  ) {
-	slapi_log_error( SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
-		"pam_passthruauth_init failed\n" );
-	return( -1 );
+                    (void *)pam_passthru_bindpreop_close ) != 0  ) {
+        slapi_log_error( SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+                         "pam_passthruauth_init failed\n" );
+        status = -1;
+        goto bail;
     }
 
-    slapi_log_error( SLAPI_LOG_PLUGIN, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
-	"<= pam_passthruauth_init succeeded\n" );
+    /* Register internal postop functions. */
+    if (slapi_register_plugin("internalpostoperation",  /* op type */
+                              1,        /* Enabled */
+                              "pam_passthruauth_init",   /* this function desc */
+                              pam_passthru_internal_postop_init,  /* init func */
+                              PAM_PASSTHRU_INT_POSTOP_DESC,      /* plugin desc */
+                              NULL,     /* ? */
+                              pam_passthruauth_plugin_identity   /* access control */
+    )) {
+        slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+                        "pam_passthruauth_init: failed to register plugin\n");
+        status = -1;
+        goto bail;
+    }
 
-    return( 0 );
+    /* Register postop functions */
+    if (slapi_register_plugin("postoperation",  /* op type */
+                              1,        /* Enabled */
+                              "pam_passthruauth_init",   /* this function desc */
+                              pam_passthru_postop_init,  /* init func for post op */
+                              PAM_PASSTHRU_POSTOP_DESC,      /* plugin desc */
+                              NULL,     /* ? */
+                              pam_passthruauth_plugin_identity   /* access control */
+    )) {
+        slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+                        "pam_passthruauth_init: failed to register plugin\n");
+        status = -1;
+        goto bail;
+    }
+
+    slapi_log_error( SLAPI_LOG_TRACE, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+                     "<= pam_passthruauth_init\n" );
+
+  bail:
+    return status;
+}
+
+static int
+pam_passthru_internal_postop_init(Slapi_PBlock *pb)
+{
+    int status = 0;
+
+    if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION,
+                         SLAPI_PLUGIN_VERSION_01) != 0 ||
+        slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION,
+                         (void *) &pdesc) != 0 ||
+        slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_ADD_FN,
+                         (void *) pam_passthru_postop) != 0 ||
+        slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_DELETE_FN,
+                         (void *) pam_passthru_postop) != 0 ||
+        slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_MODIFY_FN,
+                         (void *) pam_passthru_postop) != 0 ||
+        slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_MODRDN_FN,
+                         (void *) pam_passthru_postop) != 0) {
+        slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+                        "pam_passthru_internal_postop_init: failed to register plugin\n");
+        status = -1;
+    }
+
+    return status;
+}
+
+static int
+pam_passthru_postop_init(Slapi_PBlock *pb)
+{
+    int status = 0;
+
+    if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION,
+                         SLAPI_PLUGIN_VERSION_01) != 0 ||
+        slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION,
+                         (void *) &pdesc) != 0 ||
+        slapi_pblock_set(pb, SLAPI_PLUGIN_POST_ADD_FN,
+                         (void *) pam_passthru_postop) != 0 ||
+        slapi_pblock_set(pb, SLAPI_PLUGIN_POST_DELETE_FN,
+                         (void *) pam_passthru_postop) != 0 ||
+        slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODIFY_FN,
+                         (void *) pam_passthru_postop) != 0 ||
+        slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODRDN_FN,
+                         (void *) pam_passthru_postop) != 0) {
+        slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+                        "pam_passthru_postop_init: failed to register plugin\n");
+        status = -1;
+    }
+
+    return status;
 }
 
 /*
@@ -116,33 +238,85 @@ pam_passthruauth_init( Slapi_PBlock *pb )
 static int
 pam_passthru_bindpreop_start( Slapi_PBlock *pb )
 {
-	int rc;
-	Slapi_Entry *config_e = NULL; /* entry containing plugin config */
+    int rc = PAM_PASSTHRU_SUCCESS;
+    Slapi_DN *pluginsdn = NULL;
+    char *config_area = NULL;
 
     PAM_PASSTHRU_ASSERT( pb != NULL );
 
-    slapi_log_error( SLAPI_LOG_PLUGIN, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
-	    "=> pam_passthru_bindpreop_start\n" );
+    slapi_log_error( SLAPI_LOG_TRACE, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+                     "=> pam_passthru_bindpreop_start\n" );
 
-    if ( slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &config_e ) != 0 ) {
-		slapi_log_error( SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
-						 "missing config entry\n" );
-		return( -1 );
+    /* Check if we're already started */
+    if (g_pam_plugin_started) {
+        goto done;
     }
 
-    if (( rc = pam_passthru_config( config_e )) != LDAP_SUCCESS ) {
-		slapi_log_error( SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
-						 "configuration failed (%s)\n", ldap_err2string( rc ));
-		return( -1 );
+    /* Get the plug-in configuration DN and store it for later use. */
+    slapi_pblock_get(pb, SLAPI_TARGET_SDN, &pluginsdn);
+    if (NULL == pluginsdn || 0 == slapi_sdn_get_ndn_len(pluginsdn)) {
+        slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+                        "pam_passthru_bindpreop_start: unable to determine plug-in config dn\n");
+        rc = PAM_PASSTHRU_FAILURE;
+        goto done;
     }
 
+    /* Dup the plugin SDN to save it. */
+    pam_passthruauth_set_plugin_sdn(slapi_sdn_dup(pluginsdn));
+
+    /* Set the alternate config area if one is defined. */
+    slapi_pblock_get(pb, SLAPI_PLUGIN_CONFIG_AREA, &config_area);
+    if (config_area) {
+        pam_passthru_set_config_area(slapi_sdn_new_normdn_byval(config_area));
+    } else {
+        pam_passthru_set_config_area(slapi_sdn_dup(pluginsdn));
+    }
+
+    slapi_log_error(SLAPI_LOG_PLUGIN, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+                    "pam_passthru_bindpreop_start: config at %s\n",
+                    slapi_sdn_get_ndn(pam_passthru_get_config_area()));
+
+    /* Create the lock that protects the config . */
+    g_pam_config_lock = slapi_new_rwlock();
+
+    if (!g_pam_config_lock) {
+        slapi_log_error( SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+                         "pam_passthru_bindpreop_start: lock creation failed\n");
+        rc = PAM_PASSTHRU_FAILURE;
+        goto done;
+    }
+
+    /* Allocate the config list. */
+    pam_passthru_global_config = (PRCList *)
+        slapi_ch_calloc(1, sizeof(Pam_PassthruConfig));
+    PR_INIT_CLIST(pam_passthru_global_config);
+
+    /* Load config. */
+    pam_passthru_load_config(0 /* don't skip validation */);
+
     if (( rc = pam_passthru_pam_init()) != LDAP_SUCCESS ) {
-		slapi_log_error( SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
-						 "could not initialize PAM subsystem (%d)\n", rc);
-		return( -1 );
+        slapi_log_error( SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+                         "could not initialize PAM subsystem (%d)\n", rc);
+        rc =  PAM_PASSTHRU_FAILURE;
+        goto done;
     }
 
-    return( 0 );
+done:
+    if ( rc != PAM_PASSTHRU_SUCCESS ) {
+        pam_passthru_delete_config();
+        slapi_destroy_rwlock(g_pam_config_lock);
+        g_pam_config_lock = NULL;
+        slapi_ch_free((void **)&pam_passthru_global_config);
+    } else {
+        g_pam_plugin_started = 1;
+        slapi_log_error( SLAPI_LOG_PLUGIN, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+                         "pam_passthru: ready for service\n" );
+    }
+
+    slapi_log_error( SLAPI_LOG_TRACE, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+                     "<= pam_passthru_bindpreop_start\n" );
+
+    return( rc );
 }
 
 
@@ -154,9 +328,32 @@ pam_passthru_bindpreop_close( Slapi_PBlock *pb )
 {
     PAM_PASSTHRU_ASSERT( pb != NULL );
 
-    slapi_log_error( SLAPI_LOG_PLUGIN, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+    slapi_log_error( SLAPI_LOG_TRACE, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
 	    "=> pam_passthru_bindpreop_close\n" );
 
+    if (!g_pam_plugin_started) {
+        goto done;
+    }
+
+    pam_passthru_write_lock();
+    g_pam_plugin_started = 0;
+    pam_passthru_delete_config();
+    pam_passthru_unlock();
+
+    slapi_ch_free((void **)&pam_passthru_global_config);
+
+    /* We explicitly don't destroy the config lock here.  If we did,
+     * there is the slight possibility that another thread that just
+     * passed the g_pam_plugin_started check is about to try to obtain
+     * a reader lock.  We leave the lock around so these threads
+     * don't crash the process.  If we always check the started
+     * flag again after obtaining a reader lock, no free'd resources
+     * will be used. */
+
+done:
+    slapi_log_error( SLAPI_LOG_TRACE, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+                     "<= pam_passthru_bindpreop_close\n" );
+
     return( 0 );
 }
 
@@ -164,7 +361,8 @@ pam_passthru_bindpreop_close( Slapi_PBlock *pb )
 static int
 pam_passthru_bindpreop( Slapi_PBlock *pb )
 {
-    int rc, method;
+    int rc = LDAP_SUCCESS;
+    int method;
     const char *normbinddn;
     char *errmsg = NULL;
     Slapi_DN *bindsdn = NULL;
@@ -174,18 +372,18 @@ pam_passthru_bindpreop( Slapi_PBlock *pb )
 
     PAM_PASSTHRU_ASSERT( pb != NULL );
 
-    slapi_log_error( SLAPI_LOG_PLUGIN, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
-	    "=> pam_passthru_bindpreop\n" );
+    slapi_log_error( SLAPI_LOG_TRACE, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+                     "=> pam_passthru_bindpreop\n" );
 
     /*
      * retrieve parameters for bind operation
      */
     if ( slapi_pblock_get( pb, SLAPI_BIND_METHOD, &method ) != 0 ||
-		 slapi_pblock_get( pb, SLAPI_BIND_TARGET_SDN, &bindsdn ) != 0 ||
-		 slapi_pblock_get( pb, SLAPI_BIND_CREDENTIALS, &creds ) != 0 ) {
-		slapi_log_error( SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
-						 "<= not handled (unable to retrieve bind parameters)\n" );
-		return retcode;
+            slapi_pblock_get( pb, SLAPI_BIND_TARGET_SDN, &bindsdn ) != 0 ||
+            slapi_pblock_get( pb, SLAPI_BIND_CREDENTIALS, &creds ) != 0 ) {
+        slapi_log_error( SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+                         "<= not handled (unable to retrieve bind parameters)\n" );
+        return retcode;
     }
     normbinddn = slapi_sdn_get_dn(bindsdn);
 
@@ -194,41 +392,45 @@ pam_passthru_bindpreop( Slapi_PBlock *pb )
      * credentials.  Let the Directory Server itself handle everything else.
      */
     if ( method != LDAP_AUTH_SIMPLE || *normbinddn == '\0' ||
-		 creds->bv_len == 0 ) {
-		slapi_log_error( SLAPI_LOG_PLUGIN, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
-						 "<= not handled (not simple bind or NULL dn/credentials)\n" );
-		return retcode;
+            creds->bv_len == 0 ) {
+        slapi_log_error( SLAPI_LOG_PLUGIN, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+                         "<= not handled (not simple bind or NULL dn/credentials)\n" );
+        return retcode;
     }
 
-	/* get the config */
-	cfg = pam_passthru_get_config();
+    /* Get the config lock.  From this point on, we must go to done
+     * to be sure we unlock. */
+    pam_passthru_read_lock();
+
+    /* Bail out if the plug-in close function was just called. */
+    if (!g_pam_plugin_started) {
+        goto done;
+    }
 
-	/* don't lock mutex here - simple integer access - assume atomic */
-	if (cfg->pamptconfig_secure) { /* is a secure connection required? */
-		int is_ssl = 0;
-		slapi_pblock_get(pb, SLAPI_CONN_IS_SSL_SESSION, &is_ssl);
-		if (!is_ssl) {
-			slapi_log_error( SLAPI_LOG_PLUGIN, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
-							 "<= connection not secure (secure connection required; check config)");
-			return retcode;
-		}
-	}
+    /* See if any of our config entries apply to this user */
+    cfg = pam_passthru_get_config(bindsdn);
 
-    /*
-     * Check to see if the target DN is one we should "pass through" to
-     * PAM
-     */
-    if ( pam_passthru_check_suffix( cfg, normbinddn ) != LDAP_SUCCESS ) {
-		slapi_log_error( SLAPI_LOG_PLUGIN, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
-						 "<= not handled (not one of our suffixes)\n" );
-		return retcode;
+    if (!cfg) {
+        slapi_log_error( SLAPI_LOG_PLUGIN, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+                         "<= \"%s\" not handled (doesn't meet configuration criteria)\n", normbinddn );
+        goto done;
+    }
+
+    if (cfg->pamptconfig_secure) { /* is a secure connection required? */
+        int is_ssl = 0;
+        slapi_pblock_get(pb, SLAPI_CONN_IS_SSL_SESSION, &is_ssl);
+        if (!is_ssl) {
+            slapi_log_error( SLAPI_LOG_PLUGIN, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+                             "<= connection not secure (secure connection required; check config)");
+            goto done;
+        }
     }
 
     /*
      * We are now committed to handling this bind request.
      * Chain it off to PAM
      */
-	rc = pam_passthru_do_pam_auth(pb, cfg);
+    rc = pam_passthru_do_pam_auth(pb, cfg);
 
     /*
      * If bind succeeded, change authentication information associated
@@ -237,34 +439,238 @@ pam_passthru_bindpreop( Slapi_PBlock *pb )
     if (rc == LDAP_SUCCESS) {
         char *ndn = slapi_ch_strdup(normbinddn);
         if ((slapi_pblock_set(pb, SLAPI_CONN_DN, ndn) != 0) ||
-			(slapi_pblock_set(pb, SLAPI_CONN_AUTHMETHOD,
-							  SLAPD_AUTH_SIMPLE) != 0)) {
+                (slapi_pblock_set(pb, SLAPI_CONN_AUTHMETHOD,
+                SLAPD_AUTH_SIMPLE) != 0)) {
             slapi_ch_free_string(&ndn);
             rc = LDAP_OPERATIONS_ERROR;
             errmsg = "unable to set connection DN or AUTHTYPE";
             slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
-							"%s\n", errmsg);
+                            "%s\n", errmsg);
         } else {
-			LDAPControl **reqctrls = NULL;
-			slapi_pblock_get(pb, SLAPI_REQCONTROLS, &reqctrls);
-			if (slapi_control_present(reqctrls, LDAP_CONTROL_AUTH_REQUEST, NULL, NULL)) {
-				slapi_add_auth_response_control(pb, ndn);
-			}
-		}
+            LDAPControl **reqctrls = NULL;
+            slapi_pblock_get(pb, SLAPI_REQCONTROLS, &reqctrls);
+            if (slapi_control_present(reqctrls, LDAP_CONTROL_AUTH_REQUEST, NULL, NULL)) {
+                slapi_add_auth_response_control(pb, ndn);
+            }
+        }
+    }
+
+    if (rc == LDAP_SUCCESS) {
+        /* we are handling the result */
+        slapi_send_ldap_result(pb, rc, NULL, errmsg, 0, NULL);
+        /* tell bind code we handled the result */
+        retcode = PAM_PASSTHRU_OP_HANDLED;
+    } else if (!cfg->pamptconfig_fallback) {
+        /* tell bind code we already sent back the error result in pam_ptimpl.c */
+        retcode = PAM_PASSTHRU_OP_HANDLED;
     }
 
-	if (rc == LDAP_SUCCESS) {
-		/* we are handling the result */
-		slapi_send_ldap_result(pb, rc, NULL, errmsg, 0, NULL);
-		/* tell bind code we handled the result */
-		retcode = PAM_PASSTHRU_OP_HANDLED;
-	} else if (!cfg->pamptconfig_fallback) {
-		/* tell bind code we already sent back the error result in pam_ptimpl.c */
-		retcode = PAM_PASSTHRU_OP_HANDLED;
-	}
+done:
+    pam_passthru_unlock();
 
     slapi_log_error(SLAPI_LOG_PLUGIN, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
-					"<= handled (error %d - %s)\n", rc, ldap_err2string(rc));
+                    "<= handled (error %d - %s)\n", rc, ldap_err2string(rc));
+
+    slapi_log_error( SLAPI_LOG_TRACE, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+                     "<= pam_passthru_bindpreop\n" );
 
     return retcode;
 }
+
+
+/*
+ * Pre-op callbacks for config change validation
+ */
+static int
+pam_passthru_preop(Slapi_PBlock *pb, int modtype)
+{
+    Slapi_DN *sdn = NULL;
+    Slapi_Entry *e = NULL;
+    LDAPMod **mods;
+    char returntext[SLAPI_DSE_RETURNTEXT_SIZE];
+    int ret = 0;
+
+    slapi_log_error(SLAPI_LOG_TRACE, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+                    "=> pam_passthru_preop\n");
+
+    /* Just bail if we aren't ready to service requests yet. */
+    if (!g_pam_plugin_started) {
+        goto bail;
+    }
+
+    /* Get the target SDN. */
+    slapi_pblock_get(pb, SLAPI_TARGET_SDN, &sdn);
+    if (!sdn) {
+        goto bail;
+    }
+
+    /* If this is a config entry, we need to validate it. */
+    if (pam_passthru_dn_is_config(sdn)) {
+        switch (modtype) {
+        case LDAP_CHANGETYPE_ADD:
+            slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &e);
+            /* Validate the entry being added. */
+            if (PAM_PASSTHRU_FAILURE == pam_passthru_validate_config(e, returntext)) {
+                ret = LDAP_UNWILLING_TO_PERFORM;
+                goto bail;
+            }
+            break;
+        case LDAP_CHANGETYPE_MODIFY:
+            /* Fetch the entry being modified so we can
+             * create the resulting entry for validation. */
+            slapi_search_internal_get_entry(sdn, 0, &e,
+                    pam_passthruauth_get_plugin_identity());
+
+            /* If the entry doesn't exist, just bail and
+             * let the server handle it. */
+            if (e == NULL) {
+                goto bail;
+            }
+
+            /* Grab the mods. */
+            slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods);
+
+            /* Apply the  mods to create the resulting entry.  If the mods
+             * don't apply cleanly, we just let the main server code handle it. */
+            if (mods && (slapi_entry_apply_mods(e, mods) == LDAP_SUCCESS)) {
+                /* Validate the resulting entry. */
+                if (PAM_PASSTHRU_FAILURE == pam_passthru_validate_config(e, returntext)) {
+                    ret = LDAP_UNWILLING_TO_PERFORM;
+                    /* Don't bail here, as we need to free the entry. */
+                }
+            }
+
+            /* Free the entry. */
+            slapi_entry_free(e);
+            break;
+        case LDAP_CHANGETYPE_DELETE:
+        case LDAP_CHANGETYPE_MODDN:
+            /* Don't allow the plug-in container in DSE to be deleted or renamed. */
+            if (slapi_sdn_compare(sdn, pam_passthruauth_get_plugin_sdn()) == 0) {
+                ret = LDAP_UNWILLING_TO_PERFORM;
+            }
+            break;
+        }
+
+    }
+
+  bail:
+    /* If we are refusing the operation, return the result to the client. */
+    if (ret) {
+        slapi_send_ldap_result(pb, ret, NULL, returntext, 0, NULL);
+        ret = -1;
+    }
+
+    slapi_log_error(SLAPI_LOG_TRACE, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+                    "<= pam_passthru_preop\n");
+
+    return ret;
+}
+
+static int
+pam_passthru_add_preop(Slapi_PBlock *pb)
+{
+    return pam_passthru_preop(pb, LDAP_CHANGETYPE_ADD);
+}
+
+static int
+pam_passthru_mod_preop(Slapi_PBlock *pb)
+{
+    return pam_passthru_preop(pb, LDAP_CHANGETYPE_MODIFY);
+}
+
+static int
+pam_passthru_del_preop(Slapi_PBlock *pb)
+{
+    return pam_passthru_preop(pb, LDAP_CHANGETYPE_DELETE);
+}
+
+static int
+pam_passthru_modrdn_preop(Slapi_PBlock *pb)
+{
+    return pam_passthru_preop(pb, LDAP_CHANGETYPE_MODDN);
+}
+
+/*
+ * Post-op callback for dynamic config loading.
+ */
+static int
+pam_passthru_postop(Slapi_PBlock *pb)
+{
+    int ret = 0;
+    Slapi_DN *sdn = NULL;
+    Slapi_DN *new_sdn = NULL;
+    Slapi_Entry *e = NULL;
+    int optype = SLAPI_OPERATION_NONE;
+    int oprc = -1;
+
+    slapi_log_error(SLAPI_LOG_TRACE, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+                    "=> pam_passthru_postop\n");
+
+    /* Just bail if we aren't ready to service requests yet. */
+    if (!g_pam_plugin_started) {
+        goto bail;
+    }
+
+    /* Make sure the operation succeeded and bail if it didn't. */
+    slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &oprc);
+    if (oprc != 0) {
+        goto bail;
+    }
+
+    /* Get the target SDN. */
+    slapi_pblock_get(pb, SLAPI_TARGET_SDN, &sdn);
+    if (!sdn) {
+        slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+                        "pam_passthru_postop: unale to fetch target SDN.\n");
+        goto bail;
+    }
+
+    /* Check if this is a rename operation.
+     * If so, we need to get the new DN. */
+    slapi_pblock_get(pb, SLAPI_OPERATION_TYPE, &optype);
+    if (optype == SLAPI_OPERATION_MODDN) {
+        slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &e);
+        if (e) {
+            new_sdn = slapi_entry_get_sdn(e);
+        } else {
+            slapi_log_error(SLAPI_LOG_FATAL, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+                            "pam_passthru_postop: unable to fetch post-op "
+                            "entry for rename operation.\n");
+            goto bail;
+        }
+    }
+
+    /* Check if the target is a config entry.  If so, reload all of the config.
+     * If this is a rename operation, we also need to see if the new DN is in
+     * the config scope. */
+    if (pam_passthru_dn_is_config(sdn) || (new_sdn && pam_passthru_dn_is_config(new_sdn))) {
+        pam_passthru_load_config(1); /* skip validation, as it was done at preop */
+    }
+
+    slapi_log_error(SLAPI_LOG_TRACE, PAM_PASSTHRU_PLUGIN_SUBSYSTEM,
+                    "<= pam_passthru_postop\n");
+
+  bail:
+    return ret;
+}
+
+/*
+ *
+ * Deal with config locking
+ *
+ */
+void pam_passthru_read_lock()
+{
+    slapi_rwlock_rdlock(g_pam_config_lock);
+}
+
+void pam_passthru_write_lock()
+{
+    slapi_rwlock_wrlock(g_pam_config_lock);
+}
+
+void pam_passthru_unlock()
+{
+    slapi_rwlock_unlock(g_pam_config_lock);
+}
-- 
1.7.7.6

