INSTALLATION
============
-Build dependencies
+Dependencies
------------------
-OpenLDAP sources must be available. For an easier build, copy all ppm module
-into contrib/slapd-modules OpenLDAP source directory.
+ppm is provided along with OpenLDAP sources. By default, it is available into contrib/slapd-modules.
+ - make sure both OpenLDAP sources and ppm are available for building.
+ - install cracklib development files if you want to test passwords against cracklib
+
Build
-----
-Be sure to have copied ppm module into contrib/slapd-modules OpenLDAP source
-directory.
-
-Adapt the Makefile command to indicate:
-OLDAP_SOURCES : should point to OpenLDAP source directory
-CONFIG: where the ppm.conf example configuration file will finally stand
+Enter contrib/slapd-modules/ppm directory
+
+You can optionally customize some variables if you don't want the default ones:
+- prefix: prefix of the path where ppm is to be installed (default to /usr/local)
+- libdir: where the ppm library is to be deployed (default to /lib under prefix)
+- etcdir: where the ppm example policy is to be deployed (default to /etc/openldap under prefix)
+- LDAP_SRC: path to OpenLDAP source directory
+- Options in OPTS variable:
+ CONFIG_FILE: (DEPRECATED) path to a ppm configuration file (see PPM_READ_FILE in ppm.h)
note: ppm configuration now lies into pwdCheckModuleArg password policy attribute
- provided config file is only helpful as an example or for testing
-LIBDIR: where the library will be installed
-DEBUG: If defined, ppm logs its actions with syslog
+ provided example file is only helpful as an example or for testing
+ CRACKLIB: if defined, link against cracklib
+ DEBUG: If defined, ppm logs its actions with syslog
-If necessary, you can also adapt some OpenLDAP source directories (if changed):
-LDAP_INC : OpenLDAP headers directory
-LDAP_LIBS : OpenLDAP built libraries directory
-then type:
+To build ppm, simply run these commands:
+(based upon the default prefix /usr/local of OpenLDAP)
```
make clean
-make CONFIG=/etc/openldap/ppm.conf OLDAP_SOURCES=../../..
+make
make test
-make install CONFIG=/etc/openldap/ppm.conf LIBDIR=/usr/lib/openldap
+make install
```
-
-For LTB build, use rather:
+Here is an illustrative example showing how to overload some options:
```
make clean
-make "CONFIG=/usr/local/openldap/etc/openldap/ppm.conf" "OLDAP_SOURCES=.."
-make test
-make install CONFIG=/usr/local/openldap/etc/openldap/ppm.conf LIBDIR=/usr/local/openldap/lib64
+make LDAP_SRC=../../.. prefix=/usr/local libdir=/usr/local/lib
+make test LDAP_SRC=../../..
+make install prefix=/usr/local libdir=/usr/local/lib
```
# Copyright 2014 David Coutadeur, Paris. All Rights Reserved.
#
-CC=gcc
+LDAP_SRC=../../..
+LDAP_BUILD=$(LDAP_SRC)
+LDAP_INC=-I$(LDAP_SRC)/include \
+ -I$(LDAP_SRC)/servers/slapd
+LDAP_LIBS=-L$(LDAP_BUILD)/libraries/liblber/.libs \
+ -L$(LDAP_BUILD)/libraries/libldap/.libs
+LDAP_LIB=-lldap -llber
+CRACK_LIB=-lcrack
-# Path of OpenLDAP sources
-OLDAP_SOURCES=../../..
-# Where the ppm configuration file should be installed
-CONFIG=/etc/openldap/ppm.conf
-# Path of OpenLDAP installed libs, where the ppm library should be installed
-LIBDIR=/usr/lib/openldap
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+etcdir = $(exec_prefix)/etc$(ldap_subdir)
+
+CC=gcc
+INSTALL = /usr/bin/install
+PROGRAMS=ppm.so
+TEST=ppm_test
+EXAMPLE=ppm.example
+TESTS=./unit_tests.sh
OPT=-g -O2 -Wall -fpic \
- -DCONFIG_FILE="\"$(CONFIG)\"" \
+ -DCONFIG_FILE="\"$(etcdir)/$(EXAMPLE)\"" \
+ -DCRACKLIB \
-DDEBUG
-# Where to find the OpenLDAP headers.
-
-LDAP_INC=-I$(OLDAP_SOURCES)/include \
- -I$(OLDAP_SOURCES)/servers/slapd
-
-# Where to find the OpenLDAP libraries.
-
-LDAP_LIBS=-L$(OLDAP_SOURCES)/libraries/liblber/.libs \
- -L$(OLDAP_SOURCES)/libraries/libldap/.libs
+# don't link against cracklib if option -DCRACKLIB is not defined in OPT
+ifeq (,$(findstring CRACKLIB,$(OPT)))
+ CRACK_LIB=
+endif
-CRACK_INC=-DCRACKLIB
-
-INCS=$(LDAP_INC) $(CRACK_INC)
-
-LDAP_LIB=-lldap -llber
-
-CRACK_LIB=-lcrack
-
-LIBS=$(LDAP_LIB) $(CRACK_LIB)
-
-TESTS=./unit_tests.sh
-all: ppm ppm_test
+all: ppm $(TEST)
-ppm_test:
- $(CC) -g $(LDAP_INC) $(LDAP_LIBS) -Wl,-rpath=. -o ppm_test ppm_test.c ppm.so $(LIBS)
+$(TEST):
+ $(CC) -g $(LDAP_INC) $(LDAP_LIBS) -Wl,-rpath=. -o $(TEST) ppm_test.c $(PROGRAMS) $(LDAP_LIB) $(CRACK_LIB)
ppm.o:
- $(CC) $(OPT) -c $(INCS) ppm.c
+ $(CC) $(OPT) -c $(LDAP_INC) ppm.c
ppm: ppm.o
- $(CC) $(LDAP_INC) -shared -o ppm.so ppm.o $(CRACK_LIB)
+ $(CC) $(LDAP_INC) -shared -o $(PROGRAMS) ppm.o $(CRACK_LIB)
install: ppm
- cp -f ppm.so $(LIBDIR)
- cp -f ppm_test $(LIBDIR)
- cp -f ppm.conf $(CONFIG)
+ $(INSTALL) -m 644 $(PROGRAMS) $(libdir)
+ $(INSTALL) -m 755 $(TEST) $(libdir)
+ $(INSTALL) -m 644 $(EXAMPLE) $(etcdir)/
.PHONY: clean
clean:
- $(RM) -f ppm.o ppm.so ppm.lo ppm_test
+ $(RM) -f ppm.o $(PROGRAMS) ppm.lo $(TEST)
$(RM) -rf .libs
-test: ppm ppm_test
- $(TESTS)
+test: ppm $(TEST)
+ LDAP_SRC=$(LDAP_SRC) $(TESTS)
USAGE
-----
-Create a password policy entry and indicate the fresh compiled
-library ppm.so:
+Create a password policy entry and indicate the path of the ppm.so library
+and the content of the desired policy.
+Use a base64 tool to code / decode the content of the policy stored into
+pwdCheckModuleArg. Here is an example:
+```
dn: cn=default,ou=policies,dc=my-domain,dc=com
objectClass: pwdPolicy
+objectClass: top
objectClass: pwdPolicyChecker
objectClass: person
-objectClass: top
-cn: default
-sn: default
-pwdAttribute: userPassword
pwdCheckQuality: 2
-...
-pwdCheckModule: /path/to/new/ppm.so
-pwdCheckModuleArg: [see configuration section]
+pwdAttribute: userPassword
+sn: default
+cn: default
+pwdMinLength: 6
+pwdCheckModule: /usr/local/lib/ppm.so
+pwdCheckModuleArg:: bWluUXVhbGl0eSAzCmNoZWNrUkROIDAKZm9yYmlkZGVuQ2hhcnMKbWF4Q29uc2VjdXRpdmVQZXJDbGFzcyAwCnVzZUNyYWNrbGliIDAKY3JhY2tsaWJEaWN0IC92YXIvY2FjaGUvY3JhY2tsaWIvY3JhY2tsaWJfZGljdApjbGFzcy11cHBlckNhc2UgQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVogMCAxCmNsYXNzLWxvd2VyQ2FzZSBhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5eiAwIDEKY2xhc3MtZGlnaXQgMDEyMzQ1Njc4OSAwIDEKY2xhc3Mtc3BlY2lhbCA8Piw/Oy46LyHCp8O5JSrCtV7CqCTCo8KyJsOpfiIjJ3soWy18w6hgX1zDp17DoEApXcKwPX0rIDAgMQ==
+```
See slapo-ppolicy for more information, but to sum up:
Since OpenLDAP 2.5 version, ppm configuration is held in a binary
attribute of the password policy: pwdCheckModuleArg
-The configuration file (/etc/openldap/ppm.conf by default) is to be
+The example file (/etc/openldap/ppm.example by default) is to be
considered as an example configuration, to import in the pwdCheckModuleArg
attribute. It is also used for testing passwords with the test program
provided.
Server log:
```
-Jul 27 20:09:14 machine slapd[20270]: ppm: Opening file /etc/openldap/ppm.conf
-Jul 27 20:09:14 machine slapd[20270]: ppm: Param = minQuality, value = 3, min = (null), minForPoint= (null)
-Jul 27 20:09:14 machine slapd[20270]: ppm: Accepted replaced value: 3
-Jul 27 20:09:14 machine slapd[20270]: ppm: Param = forbiddenChars, value = , min = (null), minForPoint= (null)
-Jul 27 20:09:14 machine slapd[20270]: ppm: Accepted replaced value:
-Jul 27 20:09:14 machine slapd[20270]: ppm: Param = class-upperCase, value = ABCDEFGHIJKLMNOPQRSTUVWXYZ, min = 0, minForPoint= 5
-Jul 27 20:09:14 machine slapd[20270]: ppm: Accepted replaced value: ABCDEFGHIJKLMNOPQRSTUVWXYZ
-Jul 27 20:09:14 machine slapd[20270]: ppm: Param = class-lowerCase, value = abcdefghijklmnopqrstuvwxyz, min = 0, minForPoint= 12
-Jul 27 20:09:14 machine slapd[20270]: ppm: Accepted replaced value: abcdefghijklmnopqrstuvwxyz
-Jul 27 20:09:14 machine slapd[20270]: ppm: Param = class-digit, value = 0123456789, min = 0, minForPoint= 1
-Jul 27 20:09:14 machine slapd[20270]: ppm: Accepted replaced value: 0123456789
-Jul 27 20:09:14 machine slapd[20270]: ppm: Param = class-special, value = <>,?;.:/!§ù%*µ^¨$£²&é~"#'{([-|è`_\ç^à @)]°=}+, min = 0, minForPoint= 1
-Jul 27 20:09:14 machine slapd[20270]: ppm: Accepted replaced value: <>,?;.:/!§ù%*µ^¨$£²&é~"#'{([-|è`_\ç^à @)]°=}+
-Jul 27 20:09:14 machine slapd[20270]: ppm: Param = class-myClass, value = :), min = 1, minForPoint= 1
-Jul 27 20:09:14 machine slapd[20270]: ppm: Accepted new value:
-Jul 27 20:09:14 machine slapd[20270]: ppm: 1 point granted for class class-upperCase
-Jul 27 20:09:14 machine slapd[20270]: ppm: 1 point granted for class class-lowerCase
-Jul 27 20:09:14 machine slapd[20270]: ppm: 1 point granted for class class-digit
+Feb 26 14:46:10 debian-10-64 slapd[1981]: conn=1000 op=16 MOD dn="uid=user,ou=persons,dc=my-domain,dc=com"
+Feb 26 14:46:10 debian-10-64 slapd[1981]: conn=1000 op=16 MOD attr=userPassword
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: entry uid=user,ou=persons,dc=my-domain,dc=com
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: Reading pwdCheckModuleArg attribute
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: RAW configuration: # minQuality parameter#012# Format:#012# minQuality [NUMBER]#012# Description:#012# One point is granted for each class for which MIN_FOR_POINT criteria is fulfilled.#012# defines the minimum point numbers for the password to be accepted.#012minQuality 3#012#012# checkRDN parameter#012# Format:#012# checkRDN [0 | 1]#012# Description:#012# If set to 1, password must not contain a token from the RDN.#012# Tokens are separated by the following delimiters : space tabulation _ - , ; £#012checkRDN 0#012#012# forbiddenChars parameter#012# Format:#012# forbiddenChars [CHARACTERS_FORBIDDEN]#012# Description:#012# Defines the forbidden characters list (no separator).#012# If one of them is found in the password, then it is rejected.#012forbiddenChars#012#012# maxConsecutivePerClass parameter#012# Format:#012# maxConsecutivePerClass [NUMBER]#012# Description:#012# Defines the maximum number of consecutive character allowed for any class#012maxConsecutivePerClass 0#012#012# useCracklib parameter#012# Format:#012# useCracklib [0 | 1]#012# Description:#012# If set to 1, the password must pass the cracklib check#012useCracklib 0#012#012# cracklibDict parameter#012# Format:#012# cracklibDict [path_to_cracklib_dictionnary]#012# Description:#012# directory+filename-prefix that your version of CrackLib will go hunting for#012# For example, /var/pw_dict resolves as /var/pw_dict.pwd,#012# /var/pw_dict.pwi and /var/pw_dict.hwm dictionnary files#012cracklibDict /var/cache/cracklib/cracklib_dict#012#012# classes parameter#012# Format:#012# class-[CLASS_NAME] [CHARACTERS_DEFINING_CLASS] [MIN] [MIN_FOR_POINT]#012# Description:#012# [CHARACTERS_DEFINING_CLASS]: characters defining the class (no separator)#012# [MIN]: If at least [MIN] characters of this class is not found in the password, then it is rejected#012# [MIN_FOR_POINT]: one point is granted if password contains at least [MIN_FOR_POINT] character numbers of this class#012class-upperCase ABCDEFGHIJKLMNOPQRSTUVWXYZ 0 1#012class-lowerCase abcdefghijklmnopqrstuvwxyz 0 1#012class-digit 0123456789 0 1#012class-special <>,?;.:/!§ù%*µ^¨$£²&é~"#'{([-|è`_\ç^à@)]°=}+ 0 1
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: Parsing pwdCheckModuleArg attribute
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # minQuality parameter
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # minQuality [NUMBER]
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # One point is granted for each class for which MIN_FOR_POINT criteria is fulfilled.
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # defines the minimum point numbers for the password to be accepted.
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: minQuality 3
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: Param = minQuality, value = 3, min = (null), minForPoint= (null)
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: Accepted replaced value: 3
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # checkRDN parameter
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # checkRDN [0 | 1]
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # If set to 1, password must not contain a token from the RDN.
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Tokens are separated by the following delimiters : space tabulation _ - , ; £
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: checkRDN 0
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: Param = checkRDN, value = 0, min = (null), minForPoint= (null)
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: Accepted replaced value: 0
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # forbiddenChars parameter
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # forbiddenChars [CHARACTERS_FORBIDDEN]
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Defines the forbidden characters list (no separator).
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # If one of them is found in the password, then it is rejected.
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: forbiddenChars
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: No value, goto next parameter
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # maxConsecutivePerClass parameter
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # maxConsecutivePerClass [NUMBER]
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Defines the maximum number of consecutive character allowed for any class
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: maxConsecutivePerClass 0
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: Param = maxConsecutivePerClass, value = 0, min = (null), minForPoint= (null)
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: Accepted replaced value: 0
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # useCracklib parameter
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # useCracklib [0 | 1]
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # If set to 1, the password must pass the cracklib check
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: useCracklib 0
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: Param = useCracklib, value = 0, min = (null), minForPoint= (null)
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: Accepted replaced value: 0
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # cracklibDict parameter
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # cracklibDict [path_to_cracklib_dictionnary]
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # directory+filename-prefix that your version of CrackLib will go hunting for
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # For example, /var/pw_dict resolves as /var/pw_dict.pwd,
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # /var/pw_dict.pwi and /var/pw_dict.hwm dictionnary files
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: cracklibDict /var/cache/cracklib/cracklib_dict
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: Param = cracklibDict, value = /var/cache/cracklib/cracklib_dict, min = (null), minForPoint= (null)
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: Accepted replaced value: /var/cache/cracklib/cracklib_dict
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # classes parameter
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # class-[CLASS_NAME] [CHARACTERS_DEFINING_CLASS] [MIN] [MIN_FOR_POINT]
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # [CHARACTERS_DEFINING_CLASS]: characters defining the class (no separator)
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # [MIN]: If at least [MIN] characters of this class is not found in the password, then it is rejected
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # [MIN_FOR_POINT]: one point is granted if password contains at least [MIN_FOR_POINT] character numbers of this class
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: class-upperCase ABCDEFGHIJKLMNOPQRSTUVWXYZ 0 1
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: Param = class-upperCase, value = ABCDEFGHIJKLMNOPQRSTUVWXYZ, min = 0, minForPoint= 1
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: Accepted replaced value: ABCDEFGHIJKLMNOPQRSTUVWXYZ
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: class-lowerCase abcdefghijklmnopqrstuvwxyz 0 1
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: Param = class-lowerCase, value = abcdefghijklmnopqrstuvwxyz, min = 0, minForPoint= 1
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: Accepted replaced value: abcdefghijklmnopqrstuvwxyz
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: class-digit 0123456789 0 1
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: Param = class-digit, value = 0123456789, min = 0, minForPoint= 1
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: Accepted replaced value: 0123456789
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: class-special <>,?;.:/!§ù%*µ^¨$£²&é~"#'{([-|è`_\ç^à@)]°=}+ 0 1
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: Param = class-special, value = <>,?;.:/!§ù%*µ^¨$£²&é~"#'{([-|è`_\ç^à@)]°=}+, min = 0, minForPoint= 1
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: Accepted replaced value: <>,?;.:/!§ù%*µ^¨$£²&é~"#'{([-|è`_\ç^à@)]°=}+
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: 1 point granted for class class-lowerCase
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: 1 point granted for class class-digit
+Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: Reallocating szErrStr from 64 to 173
+Feb 26 14:46:10 debian-10-64 slapd[1981]: check_password_quality: module error: (/usr/local/lib/ppm.so) Password for dn="uid=user,ou=persons,dc=my-domain,dc=com" does not pass required number of strength checks (2 of 3).[1]
+Feb 26 14:46:10 debian-10-64 slapd[1981]: conn=1000 op=16 RESULT tag=103 err=19 qtime=0.000020 etime=0.001496 text=Password for dn="uid=user,ou=persons,dc=my-domain,dc=com" does not pass required number of strength checks (2 of 3)
```
It is possible to test one particular password using directly the test program:
```
-cd /usr/local/openldap/lib64
-LD_LIBRARY_PATH=. ./ppm_test "uid=test,ou=users,dc=my-domain,dc=com" "my_password" "/usr/local/openldap/etc/openldap/ppm.conf" && echo OK
+cd /usr/local/lib
+LD_LIBRARY_PATH=. ./ppm_test "uid=test,ou=users,dc=my-domain,dc=com" "my_password" "/usr/local/etc/openldap/ppm.example" && echo OK
```