]> git.ipfire.org Git - thirdparty/openldap.git/commitdiff
ITS#9615 move CheckModule from policy to overlay config
authorHoward Chu <hyc@openldap.org>
Mon, 26 Jul 2021 15:04:24 +0000 (16:04 +0100)
committerQuanah Gibson-Mount <quanah@openldap.org>
Tue, 3 Aug 2021 16:31:49 +0000 (16:31 +0000)
And fix errmsg FIXME

contrib/slapd-modules/ppm/ppm.c
contrib/slapd-modules/ppm/ppm.h
contrib/slapd-modules/ppm/ppm_test.c
doc/man/man5/slapo-ppolicy.5
servers/slapd/overlays/ppolicy.c

index 7c4a54c227d6a8c0f12f9dca368c2efb246bf8c6..7c6a4068177b282cd815094fcca1e02c08d16ff1 100644 (file)
@@ -7,10 +7,10 @@
 
 /*
   password policy module is called with:
-  int check_password (char *pPasswd, char **ppErrStr, Entry *e, void *pArg)
+  int check_password (char *pPasswd, struct berval *ppErrmsg, Entry *e, void *pArg)
 
   *pPasswd: new password
-  **ppErrStr: pointer to the string containing the error message
+  *ppErrmsg: pointer to a struct berval containing space for an error message of length bv_len
   *e: pointer to the current user entry
   *pArg: pointer to a struct berval holding the value of pwdCheckModuleArg attr
 */
@@ -360,13 +360,14 @@ typeParam(char* param)
 #endif
 
 static int
-realloc_error_message(char **target, int curlen, int nextlen)
+realloc_error_message(const char *orig, char **target, int curlen, int nextlen)
 {
     if (curlen < nextlen + MEMORY_MARGIN) {
         ppm_log(LOG_WARNING,
                "ppm: Reallocating szErrStr from %d to %d", curlen,
                nextlen + MEMORY_MARGIN);
-        ber_memfree(*target);
+        if (*target != orig)
+            ber_memfree(*target);
         curlen = nextlen + MEMORY_MARGIN;
         *target = (char *) ber_memalloc(curlen);
     }
@@ -428,7 +429,7 @@ containsRDN(char* passwd, char* DN)
 
 
 int
-check_password(char *pPasswd, char **ppErrStr, Entry *e, void *pArg)
+check_password(char *pPasswd, struct berval *ppErrmsg, Entry *e, void *pArg)
 {
 
     Entry *pEntry = e;
@@ -445,8 +446,9 @@ check_password(char *pPasswd, char **ppErrStr, Entry *e, void *pArg)
                           (*(struct berval*)pwdCheckModuleArg).bv_val);
     #endif
 
-    char *szErrStr = (char *) ber_memalloc(MEM_INIT_SZ);
-    int mem_len = MEM_INIT_SZ;
+    char *origmsg = ppErrmsg->bv_val;
+    char *szErrStr = origmsg;
+    int mem_len = ppErrmsg->bv_len;
     int numParam = 0; // Number of params in current configuration
 
     int useCracklib;
@@ -566,7 +568,7 @@ check_password(char *pPasswd, char **ppErrStr, Entry *e, void *pArg)
     }
 
     if (nQuality < minQuality) {
-        mem_len = realloc_error_message(&szErrStr, mem_len,
+        mem_len = realloc_error_message(origmsg, &szErrStr, mem_len,
                                         strlen(PASSWORD_QUALITY_SZ) +
                                         strlen(pEntry->e_nname.bv_val) + 4);
         sprintf(szErrStr, PASSWORD_QUALITY_SZ, pEntry->e_nname.bv_val,
@@ -579,7 +581,7 @@ check_password(char *pPasswd, char **ppErrStr, Entry *e, void *pArg)
             if ((nbInClass[i] < fileConf[i].min) &&
                  strlen(fileConf[i].value.sVal) != 0) {
                 // constraint is not satisfied... goto fail
-                mem_len = realloc_error_message(&szErrStr, mem_len,
+                mem_len = realloc_error_message(origmsg, &szErrStr, mem_len,
                                                 strlen(PASSWORD_CRITERIA) +
                                                 strlen(pEntry->e_nname.bv_val) + 
                                                 2 + PARAM_MAX_LEN);
@@ -592,7 +594,7 @@ check_password(char *pPasswd, char **ppErrStr, Entry *e, void *pArg)
 
     // Password checking done, now loocking for forbiddenChars criteria
     if (nForbiddenChars > 0) {  // at least 1 forbidden char... goto fail
-        mem_len = realloc_error_message(&szErrStr, mem_len,
+        mem_len = realloc_error_message(origmsg, &szErrStr, mem_len,
                                         strlen(PASSWORD_FORBIDDENCHARS) +
                                         strlen(pEntry->e_nname.bv_val) + 2 +
                                         VALUE_MAX_LEN);
@@ -610,7 +612,7 @@ check_password(char *pPasswd, char **ppErrStr, Entry *e, void *pArg)
                 // Too much consecutive characters of the same class
                 ppm_log(LOG_NOTICE, "ppm: Too much consecutive chars for class %s",
                        fileConf[i].param);
-                mem_len = realloc_error_message(&szErrStr, mem_len,
+                mem_len = realloc_error_message(origmsg, &szErrStr, mem_len,
                                         strlen(PASSWORD_MAXCONSECUTIVEPERCLASS) +
                                         strlen(pEntry->e_nname.bv_val) + 2 +
                                         PARAM_MAX_LEN);
@@ -630,7 +632,7 @@ check_password(char *pPasswd, char **ppErrStr, Entry *e, void *pArg)
             if (( fd = fopen ( cracklibDictFiles[j], "r")) == NULL ) {
                 ppm_log(LOG_NOTICE, "ppm: Error while reading %s file",
                        cracklibDictFiles[j]);
-                mem_len = realloc_error_message(&szErrStr, mem_len,
+                mem_len = realloc_error_message(origmsg, &szErrStr, mem_len,
                                 strlen(GENERIC_ERROR));
                 sprintf(szErrStr, GENERIC_ERROR);
                 goto fail;
@@ -644,7 +646,7 @@ check_password(char *pPasswd, char **ppErrStr, Entry *e, void *pArg)
         if ( res != NULL ) {
                 ppm_log(LOG_NOTICE, "ppm: cracklib does not validate password for entry %s",
                        pEntry->e_nname.bv_val);
-                mem_len = realloc_error_message(&szErrStr, mem_len,
+                mem_len = realloc_error_message(origmsg, &szErrStr, mem_len,
                                         strlen(PASSWORD_CRACKLIB) +
                                         strlen(pEntry->e_nname.bv_val));
                 sprintf(szErrStr, PASSWORD_CRACKLIB, pEntry->e_nname.bv_val);
@@ -659,7 +661,7 @@ check_password(char *pPasswd, char **ppErrStr, Entry *e, void *pArg)
     if (checkRDN == 1 && containsRDN(pPasswd, pEntry->e_nname.bv_val))
     // RDN check enabled and a token from RDN is found in password: goto fail
     {
-        mem_len = realloc_error_message(&szErrStr, mem_len,
+        mem_len = realloc_error_message(origmsg, &szErrStr, mem_len,
                                         strlen(RDN_TOKEN_FOUND) +
                                         strlen(pEntry->e_nname.bv_val));
         sprintf(szErrStr, RDN_TOKEN_FOUND, pEntry->e_nname.bv_val);
@@ -667,13 +669,12 @@ check_password(char *pPasswd, char **ppErrStr, Entry *e, void *pArg)
         goto fail;
     }
 
-    *ppErrStr = strdup("");
-    ber_memfree(szErrStr);
+    szErrStr[0] = '\0';
     return (LDAP_SUCCESS);
 
   fail:
-    *ppErrStr = strdup(szErrStr);
-    ber_memfree(szErrStr);
+    ppErrmsg->bv_val = szErrStr;
+    ppErrmsg->bv_len = mem_len;
     return (EXIT_FAILURE);
 
 }
index 25b360d974f250b9a754fc08c882c238e1b81cea..049623483f027805b2401a67ea4ca56993f76826 100644 (file)
@@ -31,7 +31,6 @@
 
 #define DEFAULT_QUALITY                   3
 #define MEMORY_MARGIN                     50
-#define MEM_INIT_SZ                       64
 #define DN_MAX_LEN                        512
 
 #define CONF_MAX_SIZE                      50
@@ -111,7 +110,7 @@ int min(char *str1, char *str2);
 #ifdef PPM_READ_FILE
   static void read_config_file(conf * fileConf, int *numParam, char *ppm_config_file);
 #endif
-int check_password(char *pPasswd, char **ppErrStr, Entry *e, void *pArg);
+int check_password(char *pPasswd, struct berval *ppErrmsg, Entry *e, void *pArg);
 int maxConsPerClass(char *password, char *charClass);
 void storeEntry(char *param, char *value, valueType valType, 
            char *min, char *minForPoint, conf * fileConf, int *numParam);
index 520aa0aff557223cb7dc88aabb00c0987057f711..3754149680f7bd5a22beb5bd5017cba5f85410b6 100644 (file)
@@ -19,7 +19,8 @@ int main(int argc, char *argv[])
           );
 
     /* format user entry */
-    char *errmsg = NULL;
+    char errbuf[256];
+    struct berval errmsg = { sizeof(errbuf)-1, errbuf };
     Entry pEntry;
     pEntry.e_nname.bv_val=argv[1];
     pEntry.e_name.bv_val=argv[1];
@@ -51,10 +52,11 @@ int main(int argc, char *argv[])
     }
     else
     {
-      printf("Password failed checks : %s\n", errmsg);
+      printf("Password failed checks : %s\n", errmsg.bv_val);
     }
 
-    ber_memfree(errmsg);
+    if (errmsg.bv_val != errbuf)
+        ber_memfree(errmsg.bv_val);
     return ret;
 
   }
index da768fec2534e065e37b328379708d9ef67612c0..8bd068b4f136371b7b009b65bf5e3a2945c96263 100644 (file)
@@ -100,6 +100,24 @@ If set, ppolicy will send the password policy expired (2.16.840.1.113730.3.4.4)
 and password policy expiring (2.16.840.1.113730.3.4.5) controls when
 appropriate. The controls are not sent for bind requests where the Password
 policy control has already been requested. Default is not to send the controls.
+.TP
+.B ppolicy_check_module <path>
+Specify the path of a loadable module containing a
+.B check_password()
+function for additional password quality checks. The use of this module
+is described further below in the description of the
+.B pwdPolicyChecker
+objectclass.
+
+Note: The user-defined loadable module must be in
+.B slapd's
+standard executable search PATH, or an absolute path must be provided.
+
+Note: Use of a
+.B ppolicy_check_module
+is a non-standard extension to the LDAP password
+policy proposal.
+
 
 .SH OBJECT CLASS
 The 
@@ -145,7 +163,7 @@ objectclass, used for password quality checking (see below).
     NAME 'pwdPolicyChecker'
     AUXILIARY
     SUP top
-    MAY ( pwdCheckModule $ pwdCheckModuleArg ) )
+    MAY ( pwdCheckModule $ pwdCheckModuleArg $ pwdUseCheckModule ) )
 .RE
 .P
 Every account that should be subject to password policy control should
@@ -279,8 +297,9 @@ without checking it (if
 is zero (0) or one (1)) or refuse it (if
 .B pwdCheckQuality
 is two (2)). If the number of characters should be enforced with regards
-to a particular encoding, the use of an appropriate pwdCheckModule is
-required.
+to a particular encoding, the use of an appropriate
+.B ppolicy_check_module
+is required.
 .LP
 .RS 4
 (  1.3.6.1.4.1.42.2.27.8.1.6
@@ -309,8 +328,9 @@ without checking it (if
 is zero (0) or one (1)) or refuse it (if
 .B pwdCheckQuality
 is two (2)). If the number of characters should be enforced with regards
-to a particular encoding, the use of an appropriate pwdCheckModule is
-required.
+to a particular encoding, the use of an appropriate
+.B ppolicy_check_module
+is required.
 .LP
 .RS 4
 (  1.3.6.1.4.1.42.2.27.8.1.31
@@ -622,9 +642,13 @@ is set to
    SINGLE\-VALUE )
 .RE
 
-.BR pwdCheckModule / pwdCheckModuleArg
+.BR pwdUseCheckModule / pwdCheckModuleArg
 .P
-This attribute names a user-defined loadable module that must
+The
+.B pwdUseCheckModule
+attribute enables use of a loadable module previously configured with
+.B ppolicy_check_module
+for the current policy. The module must
 instantiate the check_password() function.  This function
 will be called to further check a new password if
 .B pwdCheckQuality
@@ -635,14 +659,22 @@ function prototype:
 .RS 4
 int
 .I check_password
-(char *pPasswd, char **ppErrStr, Entry *pEntry, struct berval *pArg);
+(char *pPasswd, struct berval *pErrmsg, Entry *pEntry, struct berval *pArg);
 .RE
 The
 .B pPasswd
 parameter contains the clear-text user password, the
-.B ppErrStr
-parameter contains a double pointer that allows the function
+.B pErrmsg
+parameter points to a
+.B struct berval
+containing space
 to return human-readable details about any error it encounters.
+The
+.B bv_len
+field must contain the size of the space provided
+by the
+.B bv_val
+field.
 
 The
 .B pEntry
@@ -658,24 +690,31 @@ containing the value of
 in the effective password policy, if set, otherwise NULL.
 
 If
-.B ppErrStr
+.B pErrmsg
 is NULL, then 
 .I funcName
-must NOT attempt to use it/them.
+must NOT attempt to use it.
 A return value of LDAP_SUCCESS from the called
 function indicates that the password is ok, any other value
 indicates that the password is unacceptable.  If the password is
 unacceptable, the server will return an error to the client, and
-.B ppErrStr
+.B pErrmsg
 may be used to return a human-readable textual explanation of the
-error. The error string must be dynamically allocated as it will
+error. If the space passed in by the caller is too small, the function
+may replace it with a dynamically allocated buffer, which will
 be free()'d by slapd.
+
+The
+.B pwdCheckModule
+attribute is now obsolete and is ignored.
+
 .LP
 .RS 4
 (  1.3.6.1.4.1.4754.1.99.1
    NAME 'pwdCheckModule'
    EQUALITY caseExactIA5Match
    SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+   OBSOLETE
    SINGLE\-VALUE )
 
 ( 1.3.6.1.4.1.4754.1.99.2
@@ -684,19 +723,13 @@ be free()'d by slapd.
    SYNTAX 1.3.6.1.4.1.1466.115.121.1.40
    DESC 'Argument to pass to check_password() function'
    SINGLE\-VALUE )
+
+(  1.3.6.1.4.1.4754.1.99.3
+   NAME 'pwdUseCheckModule'
+   EQUALITY booleanMatch
+   SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
+   SINGLE\-VALUE )
 .RE
-.P
-Note: 
-The user-defined loadable module named by
-.B pwdCheckModule     
-must be in
-.B slapd's
-standard executable search PATH.
-.P
-Note:
-.B pwdCheckModule
-is a non-standard extension to the LDAP password
-policy proposal.
 
 .SH OPERATIONAL ATTRIBUTES
 .P
index 6f56752a59897e4c770839a17de80feb3b422761..e684ae921f3f2f36b1837791a585afd83ec6b002 100644 (file)
 #include <ac/ctype.h>
 #include "slap-config.h"
 
-#ifndef MODULE_NAME_SZ
-#define MODULE_NAME_SZ 256
-#endif
-
 #ifndef PPOLICY_DEFAULT_MAXRECORDED_FAILURE
 #define PPOLICY_DEFAULT_MAXRECORDED_FAILURE    5
 #endif
 
+               /* External password quality checking function.
+                * The error message must have a preallocated buffer and size
+                * passed in. Module can still allocate a buffer for
+                * it if the provided one is too small.
+                */
+typedef        int (check_func)( char *passwd, struct berval *errmsg, Entry *ent, struct berval *arg );
+#define ERRBUFSIZ      256
+
 /* Per-instance configuration information */
 typedef struct pp_info {
        struct berval def_policy;       /* DN of default policy subentry */
@@ -57,6 +61,10 @@ typedef struct pp_info {
        int forward_updates;    /* use frontend for policy state updates */
        int disable_write;
        int send_netscape_controls;     /* send netscape password controls */
+       char *pwdCheckModule; /* name of module to dynamically
+                                                                                   load to check password */
+       lt_dlhandle     pwdCheckHandle;         /* handle from lt_dlopen */
+       check_func *pwdCheckFunc;
        ldap_pvt_thread_mutex_t pwdFailureTime_mutex;
 } pp_info;
 
@@ -104,8 +112,7 @@ typedef struct pass_policy {
        int pwdSafeModify; /* 0 = old password doesn't need to come
                                                                with password change request
                                                        1 = password change must supply existing pwd */
-       char pwdCheckModule[MODULE_NAME_SZ]; /* name of module to dynamically
-                                                                                   load to check password */
+       int pwdUseCheckModule; /* 0 = do not use password check module, 1 = use */
        struct berval pwdCheckModuleArg; /* Optional argument to the password check
                                                                                module */
 } PassPolicy;
@@ -129,7 +136,7 @@ static AttributeDescription *ad_pwdMinAge, *ad_pwdMaxAge, *ad_pwdMaxIdle,
        *ad_pwdMaxFailure, *ad_pwdGraceExpiry, *ad_pwdGraceAuthNLimit,
        *ad_pwdExpireWarning, *ad_pwdMinDelay, *ad_pwdMaxDelay,
        *ad_pwdLockoutDuration, *ad_pwdFailureCountInterval,
-       *ad_pwdCheckModule, *ad_pwdCheckModuleArg, *ad_pwdLockout,
+       *ad_pwdCheckModule, *ad_pwdCheckModuleArg, *ad_pwdUseCheckModule, *ad_pwdLockout,
        *ad_pwdMustChange, *ad_pwdAllowUserChange, *ad_pwdSafeModify,
        *ad_pwdAttribute, *ad_pwdMaxRecordedFailure;
 
@@ -382,7 +389,8 @@ static struct schema_info {
                "NAME ( 'pwdCheckModule' ) "
                "EQUALITY caseExactIA5Match "
                "SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 "
-               "DESC 'Loadable module that instantiates check_password() function' "
+               "DESC 'Obsolete, no longer used' "
+               "OBSOLETE "
                "SINGLE-VALUE )",
                &ad_pwdCheckModule },
        {       "( 1.3.6.1.4.1.4754.1.99.2 "
@@ -392,6 +400,13 @@ static struct schema_info {
                "DESC 'Argument to pass to check_password() function' "
                "SINGLE-VALUE )",
                &ad_pwdCheckModuleArg },
+       {       "( 1.3.6.1.4.1.4754.1.99.3 "
+               "NAME ( 'pwdUseCheckModule' ) "
+               "EQUALITY booleanMatch "
+               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
+               "DESC 'Toggle use of the loaded pwdCheckModule' "
+               "SINGLE-VALUE )",
+               &ad_pwdUseCheckModule },
 
        { NULL, NULL }
 };
@@ -401,7 +416,7 @@ static char *pwd_ocs[] = {
                "NAME 'pwdPolicyChecker' "
                "SUP top "
                "AUXILIARY "
-               "MAY ( pwdCheckModule $ pwdCheckModuleArg ) )" ,
+               "MAY ( pwdCheckModule $ pwdCheckModuleArg $ pwdUseCheckModule ) )" ,
        "( 1.3.6.1.4.1.42.2.27.8.2.1 "
                "NAME 'pwdPolicy' "
                "SUP top "
@@ -424,9 +439,10 @@ enum {
        PPOLICY_HASH_CLEARTEXT,
        PPOLICY_USE_LOCKOUT,
        PPOLICY_DISABLE_WRITE,
+       PPOLICY_CHECK_MODULE,
 };
 
-static ConfigDriver ppolicy_cf_default;
+static ConfigDriver ppolicy_cf_default, ppolicy_cf_checkmod;
 
 static ConfigTable ppolicycfg[] = {
        { "ppolicy_default", "policyDN", 2, 2, 0,
@@ -470,6 +486,13 @@ static ConfigTable ppolicycfg[] = {
          "DESC 'Send Netscape policy controls' "
          "EQUALITY booleanMatch "
          "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+       { "ppolicy_check_module", "path", 2, 2, 0,
+         ARG_STRING|ARG_MAGIC|PPOLICY_CHECK_MODULE, ppolicy_cf_checkmod,
+         "( OLcfgOvAt:12.7 NAME 'olcPPolicyCheckModule' "
+         "DESC 'Loadable module that instantiates check_password() function' "
+         "EQUALITY caseExactIA5Match "
+         "SYNTAX OMsIA5String "
+         "SINGLE-VALUE )", NULL, NULL },
        { NULL, NULL, 0, 0, 0, ARG_IGNORED }
 };
 
@@ -480,7 +503,8 @@ static ConfigOCs ppolicyocs[] = {
          "SUP olcOverlayConfig "
          "MAY ( olcPPolicyDefault $ olcPPolicyHashCleartext $ "
          "olcPPolicyUseLockout $ olcPPolicyForwardUpdates $ "
-         "olcPPolicyDisableWrite $ olcPPolicySendNetscapeControls ) )",
+         "olcPPolicyDisableWrite $ olcPPolicySendNetscapeControls $ "
+         "olcPPolicyCheckModule ) )",
          Cft_Overlay, ppolicycfg },
        { NULL, 0, NULL }
 };
@@ -536,6 +560,61 @@ ppolicy_cf_default( ConfigArgs *c )
        return rc;
 }
 
+static int
+ppolicy_cf_checkmod( ConfigArgs *c )
+{
+       slap_overinst *on = (slap_overinst *)c->bi;
+       pp_info *pi = (pp_info *)on->on_bi.bi_private;
+       int rc = ARG_BAD_CONF;
+
+       assert ( c->type == PPOLICY_CHECK_MODULE );
+       Debug(LDAP_DEBUG_TRACE, "==> ppolicy_cf_checkmod\n" );
+
+       switch ( c->op ) {
+       case SLAP_CONFIG_EMIT:
+               if ( pi->pwdCheckModule ) {
+                       c->value_string = ch_strdup( pi->pwdCheckModule );
+                       rc = 0;
+               }
+               break;
+       case LDAP_MOD_DELETE:
+               if ( pi->pwdCheckHandle ) {
+                       lt_dlclose( pi->pwdCheckHandle );
+                       pi->pwdCheckHandle = NULL;
+                       pi->pwdCheckFunc = NULL;
+               }
+               ch_free( pi->pwdCheckModule );
+               pi->pwdCheckModule = NULL;
+               rc = 0;
+               break;
+       case SLAP_CONFIG_ADD:
+               /* fallthru to LDAP_MOD_ADD */
+       case LDAP_MOD_ADD:
+               pi->pwdCheckHandle = lt_dlopen( c->value_string );
+               if ( pi->pwdCheckHandle == NULL ) {
+                       const char *dlerr = lt_dlerror();
+                       snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> lt_dlopen(%s) failed: %s",
+                               c->argv[0], c->value_string, dlerr );
+                       Debug(LDAP_DEBUG_ANY, "%s: %s\n", c->log, c->cr_msg );
+               } else {
+                       if (( pi->pwdCheckFunc = lt_dlsym( pi->pwdCheckHandle, "check_password" )) == NULL) {
+                               const char *dlerr = lt_dlerror();
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> lt_dlsym(%s) failed: %s",
+                                       c->argv[0], c->value_string, dlerr );
+                               Debug(LDAP_DEBUG_ANY, "%s: %s\n", c->log, c->cr_msg );
+                       } else {
+                               pi->pwdCheckModule = c->value_string;
+                               rc = 0;
+                       }
+               }
+               break;
+       default:
+               abort ();
+       }
+
+       return rc;
+}
+
 static time_t
 parse_time( char *atm )
 {
@@ -1024,12 +1103,16 @@ ppolicy_get( Operation *op, Entry *e, PassPolicy *pp )
        }
 
        ad = ad_pwdCheckModule;
-       if ( (a = attr_find( pe->e_attrs, ad )) ) {
-               strncpy( pp->pwdCheckModule, a->a_vals[0].bv_val,
-                       sizeof(pp->pwdCheckModule) );
-               pp->pwdCheckModule[sizeof(pp->pwdCheckModule)-1] = '\0';
+       if ( attr_find( pe->e_attrs, ad )) {
+               Debug( LDAP_DEBUG_ANY, "ppolicy_get: "
+                               "WARNING: Ignoring OBSOLETE attribute %s in policy %s.\n",
+                               ad->ad_cname.bv_val, pe->e_name.bv_val );
        }
 
+       ad = ad_pwdUseCheckModule;
+       if ( (a = attr_find( pe->e_attrs, ad )) )
+               pp->pwdUseCheckModule = bvmatch( &a->a_nvals[0], &slap_true_bv );
+
        ad = ad_pwdCheckModuleArg;
        if ( (a = attr_find( pe->e_attrs, ad )) ) {
                ber_dupbv_x( &pp->pwdCheckModuleArg, &a->a_vals[0], op->o_tmpmemctx );
@@ -1123,7 +1206,8 @@ password_scheme( struct berval *cred, struct berval *sch )
 }
 
 static int
-check_password_quality( struct berval *cred, PassPolicy *pp, LDAPPasswordPolicyError *err, Entry *e, char **txt )
+check_password_quality( struct berval *cred, pp_info *pi, PassPolicy *pp, LDAPPasswordPolicyError *err,
+       Entry *e, struct berval *errmsg )
 {
        int rc = LDAP_SUCCESS, ok = LDAP_SUCCESS;
        char *ptr;
@@ -1131,11 +1215,12 @@ check_password_quality( struct berval *cred, PassPolicy *pp, LDAPPasswordPolicyE
 
        assert( cred != NULL );
        assert( pp != NULL );
-       assert( txt != NULL );
+       assert( errmsg != NULL );
 
-       ptr = cred->bv_val;
+       ptr = errmsg->bv_val;
+       *ptr = '\0';
 
-       *txt = NULL;
+       ptr = cred->bv_val;
 
        if ((cred->bv_len == 0) || (pp->pwdMinLength > cred->bv_len)) {
                rc = LDAP_CONSTRAINT_VIOLATION;
@@ -1180,63 +1265,40 @@ check_password_quality( struct berval *cred, PassPolicy *pp, LDAPPasswordPolicyE
 
        rc = LDAP_SUCCESS;
 
-       if (pp->pwdCheckModule[0]) {
+       if (pp->pwdUseCheckModule) {
 #ifdef SLAPD_MODULES
-               lt_dlhandle mod;
-               const char *err;
-               
-               if ((mod = lt_dlopen( pp->pwdCheckModule )) == NULL) {
-                       err = lt_dlerror();
+               check_func *prog;
 
+               if ( !pi->pwdCheckFunc ) {
                        Debug(LDAP_DEBUG_ANY,
-                       "check_password_quality: lt_dlopen failed: (%s) %s.\n",
-                               pp->pwdCheckModule, err );
-                       ok = LDAP_OTHER; /* internal error */
+                               "check_password_quality: no CheckModule loaded\n" );
+                       ok = LDAP_OTHER;
                } else {
-                       /* FIXME: the error message ought to be passed thru a
-                        * struct berval, with preallocated buffer and size
-                        * passed in. Module can still allocate a buffer for
-                        * it if the provided one is too small.
-                        */
-                       int (*prog)( char *passwd, char **text, Entry *ent, struct berval *arg );
-
-                       if ((prog = lt_dlsym( mod, "check_password" )) == NULL) {
-                               err = lt_dlerror();
+                       struct berval *arg = NULL;
+                       if ( !BER_BVISNULL( &pp->pwdCheckModuleArg ) ) {
+                               arg = &pp->pwdCheckModuleArg;
+                       }
 
+                       ldap_pvt_thread_mutex_lock( &chk_syntax_mutex );
+                       ok = pi->pwdCheckFunc( ptr, errmsg, e, arg );
+                       ldap_pvt_thread_mutex_unlock( &chk_syntax_mutex );
+                       if (ok != LDAP_SUCCESS) {
                                Debug(LDAP_DEBUG_ANY,
-                                       "check_password_quality: lt_dlsym failed: (%s) %s.\n",
-                                       pp->pwdCheckModule, err );
-                               ok = LDAP_OTHER;
-                       } else {
-                               struct berval *arg = NULL;
-                               if ( !BER_BVISNULL( &pp->pwdCheckModuleArg ) ) {
-                                       arg = &pp->pwdCheckModuleArg;
-                               }
-
-                               ldap_pvt_thread_mutex_lock( &chk_syntax_mutex );
-                               ok = prog( ptr, txt, e, arg );
-                               ldap_pvt_thread_mutex_unlock( &chk_syntax_mutex );
-                               if (ok != LDAP_SUCCESS) {
-                                       Debug(LDAP_DEBUG_ANY,
-                                               "check_password_quality: module error: (%s) %s.[%d]\n",
-                                               pp->pwdCheckModule, *txt ? *txt : "", ok );
-                               }
+                                       "check_password_quality: module error: (%s) %s.[%d]\n",
+                                       pi->pwdCheckModule, errmsg->bv_val ? errmsg->bv_val : "", ok );
                        }
-                           
-                       lt_dlclose( mod );
                }
 #else
-       Debug(LDAP_DEBUG_ANY, "check_password_quality: external modules not "
-               "supported. pwdCheckModule ignored.\n" );
+               Debug(LDAP_DEBUG_ANY, "check_password_quality: external modules not "
+                       "supported. pwdCheckModule ignored.\n" );
 #endif /* SLAPD_MODULES */
        }
-               
-                   
+
        if (ok != LDAP_SUCCESS) {
                rc = LDAP_CONSTRAINT_VIOLATION;
                if (err) *err = PP_insufficientPasswordQuality;
        }
-       
+
        return rc;
 }
 
@@ -2240,14 +2302,16 @@ ppolicy_add(
                        struct berval *bv = &(pa->a_vals[0]);
                        int rc, send_ctrl = 0;
                        LDAPPasswordPolicyError pErr = PP_noError;
-                       char *txt;
+                       char errbuf[ERRBUFSIZ];
+                       struct berval errmsg = BER_BVC( errbuf );
 
                        /* Did we receive a password policy request control? */
                        if ( op->o_ctrlflag[ppolicy_cid] ) {
                                send_ctrl = 1;
                        }
-                       rc = check_password_quality( bv, &pp, &pErr, op->ora_e, &txt );
+                       rc = check_password_quality( bv, pi, &pp, &pErr, op->ora_e, &errmsg );
                        if (rc != LDAP_SUCCESS) {
+                               char *txt = errmsg.bv_val;
                                LDAPControl **oldctrls = NULL;
                                op->o_bd->bd_info = (BackendInfo *)on->on_info;
                                if ( send_ctrl ) {
@@ -2255,8 +2319,8 @@ ppolicy_add(
                                        ctrl = create_passcontrol( op, -1, -1, pErr );
                                        oldctrls = add_passcontrol( op, rs, ctrl );
                                }
-                               send_ldap_error( op, rs, rc, txt ? txt : "Password fails quality checking policy" );
-                               if ( txt ) {
+                               send_ldap_error( op, rs, rc, txt && txt[0] ? txt : "Password fails quality checking policy" );
+                               if ( txt != errbuf ) {
                                        free( txt );
                                }
                                if ( send_ctrl ) {
@@ -2353,6 +2417,7 @@ ppolicy_modify( Operation *op, SlapReply *rs )
                                *ml, *delmod, *addmod;
        Attribute               *pa, *ha, at;
        const char              *txt;
+       char errbuf[ERRBUFSIZ];
        pw_hist                 *tl = NULL, *p;
        int                     zapReset, send_ctrl = 0, free_txt = 0;
        Entry                   *e;
@@ -2767,13 +2832,16 @@ ppolicy_modify( Operation *op, SlapReply *rs )
 
        bv = newpw.bv_val ? &newpw : &addmod->sml_values[0];
        if (pp.pwdCheckQuality > 0) {
+               struct berval errmsg = BER_BVC( errbuf );
 
-               rc = check_password_quality( bv, &pp, &pErr, e, (char **)&txt );
+               rc = check_password_quality( bv, pi, &pp, &pErr, e, &errmsg );
                if (rc != LDAP_SUCCESS) {
                        rs->sr_err = rc;
-                       if ( txt ) {
+                       txt = errmsg.bv_val;
+                       if ( txt && txt[0] ) {
                                rs->sr_text = txt;
-                               free_txt = 1;
+                               if ( txt != errbuf )
+                                       free_txt = 1;
                        } else {
                                rs->sr_text = "Password fails quality checking policy";
                        }