Name Date Size #Lines LOC

..08-May-2022-

INSTALL.mdH A D08-May-20221.7 KiB4938

LICENSEH A D08-May-20222.2 KiB5139

MakefileH A D08-May-20222.2 KiB9153

README.mdH A D08-May-202218.7 KiB361295

ppm.cH A D08-May-202221.1 KiB682537

ppm.exampleH A D08-May-20223 KiB8674

ppm.hH A D08-May-20223.8 KiB12887

ppm_test.cH A D08-May-20221.3 KiB6945

unit_tests.shH A D08-May-20223.5 KiB11976

README.md

1
2ppm.c - OpenLDAP password policy module
3
4version 2.0
5
6ppm.c is an OpenLDAP module for checking password quality when they are modified.
7Passwords are checked against the presence or absence of certain character classes.
8
9This module is used as an extension of the OpenLDAP password policy controls,
10see slapo-ppolicy(5) section pwdCheckModule.
11
12contributions
13-------------
14
15* 2014 - 2021 - David Coutadeur <david.coutadeur@gmail.com> - maintainer
16* 2015 - Daly Chikhaoui - Janua <dchikhaoui@janua.fr> - contribution on RDN checks
17* 2017 - tdb - Tim Bishop - contribution on some compilation improvements
18
19
20INSTALLATION
21------------
22
23See INSTALL file
24
25
26USAGE
27-----
28
29Create a password policy entry and indicate the path of the ppm.so library
30and the content of the desired policy.
31Use a base64 tool to code / decode the content of the policy stored into
32pwdCheckModuleArg. Here is an example:
33
34```
35dn: cn=default,ou=policies,dc=my-domain,dc=com
36objectClass: pwdPolicy
37objectClass: top
38objectClass: pwdPolicyChecker
39objectClass: person
40pwdCheckQuality: 2
41pwdAttribute: userPassword
42sn: default
43cn: default
44pwdMinLength: 6
45pwdCheckModule: /usr/local/lib/ppm.so
46pwdCheckModuleArg:: bWluUXVhbGl0eSAzCmNoZWNrUkROIDAKZm9yYmlkZGVuQ2hhcnMKbWF4Q29uc2VjdXRpdmVQZXJDbGFzcyAwCnVzZUNyYWNrbGliIDAKY3JhY2tsaWJEaWN0IC92YXIvY2FjaGUvY3JhY2tsaWIvY3JhY2tsaWJfZGljdApjbGFzcy11cHBlckNhc2UgQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVogMCAxCmNsYXNzLWxvd2VyQ2FzZSBhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5eiAwIDEKY2xhc3MtZGlnaXQgMDEyMzQ1Njc4OSAwIDEKY2xhc3Mtc3BlY2lhbCA8Piw/Oy46LyHCp8O5JSrCtV7CqCTCo8KyJsOpfiIjJ3soWy18w6hgX1zDp17DoEApXcKwPX0rIDAgMQ==
47```
48
49
50See slapo-ppolicy for more information, but to sum up:
51- enable ppolicy overlay in your database.
52This example show the activation for a slapd.conf file
53(see slapd-config and slapo-ppolicy for more information for
54 cn=config configuration)
55
56```
57overlay ppolicy
58ppolicy_default "cn=default,ou=policies,dc=my-domain,dc=com"
59#ppolicy_use_lockout   # for having more infos about the lockout
60```
61
62- define a default password policy in OpenLDAP configuration or
63use pwdPolicySubentry attribute to point to the given policy.
64
65
66
67
68Password checks
69---------------
70
71- 4 character classes are defined by default:
72upper case, lower case, digits and special characters.
73
74- more character classes can be defined, just write your own.
75
76- passwords must match the amount of quality points.
77A point is validated when at least m characters of the corresponding
78character class are present in the password.
79
80- passwords must have at least n of the corresponding character class
81present, else they are rejected.
82
83- the two previous criteria are checked against any specific character class
84defined.
85
86- if a password contains any of the forbidden characters, then it is
87rejected.
88
89- if a password contains tokens from the RDN, then it is rejected.
90
91- if a password is too long, it can be rejected.
92
93- if a password does not pass cracklib check, it can be rejected.
94
95
96Configuration
97-------------
98
99Since OpenLDAP 2.5 version, ppm configuration is held in a binary
100attribute of the password policy: pwdCheckModuleArg
101The example file (/etc/openldap/ppm.example by default) is to be
102considered as an example configuration, to import in the pwdCheckModuleArg
103attribute. It is also used for testing passwords with the test program
104provided.
105If for some reasons, any parameter is not found, it will be given its
106default value.
107
108Note: you can still compile ppm to use the configuration file, by enabling
109PPM_READ_FILE in ppm.h (but this is deprecated now). If you decide to do so,
110you can use the PPM_CONFIG_FILE environment variable for overloading the
111configuration file path.
112
113The syntax of a configuration line is:
114parameter value [min] [minForPoint]
115
116with spaces being delimiters and Line Feed (LF) ending the line.
117Parameter names ARE case sensitive.
118
119The default configuration is the following:
120
121```
122# minQuality parameter
123# Format:
124# minQuality [NUMBER]
125# Description:
126# One point is granted for each class for which MIN_FOR_POINT criteria is fulfilled.
127# defines the minimum point numbers for the password to be accepted.
128minQuality 3
129
130# checkRDN parameter
131# Format:
132# checkRDN [0 | 1]
133# Description:
134# If set to 1, password must not contain a token from the RDN.
135# Tokens are separated by the following delimiters : space tabulation _ - , ; £
136checkRDN 0
137
138# forbiddenChars parameter
139# Format:
140# forbiddenChars [CHARACTERS_FORBIDDEN]
141# Description:
142# Defines the forbidden characters list (no separator).
143# If one of them is found in the password, then it is rejected.
144forbiddenChars
145
146# maxConsecutivePerClass parameter
147# Format:
148# maxConsecutivePerClass [NUMBER]
149# Description:
150# Defines the maximum number of consecutive character allowed for any class
151maxConsecutivePerClass 0
152
153# useCracklib parameter
154# Format:
155# useCracklib [0 | 1]
156# Description:
157# If set to 1, the password must pass the cracklib check
158useCracklib 0
159
160# cracklibDict parameter
161# Format:
162# cracklibDict [path_to_cracklib_dictionary]
163# Description:
164# directory+filename-prefix that your version of CrackLib will go hunting for
165# For example, /var/pw_dict resolves as /var/pw_dict.pwd,
166# /var/pw_dict.pwi and /var/pw_dict.hwm dictionary files
167cracklibDict /var/cache/cracklib/cracklib_dict
168
169# classes parameter
170# Format:
171# class-[CLASS_NAME] [CHARACTERS_DEFINING_CLASS] [MIN] [MIN_FOR_POINT]
172# Description:
173# [CHARACTERS_DEFINING_CLASS]: characters defining the class (no separator)
174# [MIN]: If at least [MIN] characters of this class is not found in the password, then it is rejected
175# [MIN_FOR_POINT]: one point is granted if password contains at least [MIN_FOR_POINT] character numbers of this class
176class-upperCase ABCDEFGHIJKLMNOPQRSTUVWXYZ 0 1
177class-lowerCase abcdefghijklmnopqrstuvwxyz 0 1
178class-digit 0123456789 0 1
179class-special <>,?;.:/!§ù%*µ^¨$£²&é~"#'{([-|è`_\ç^à@)]°=}+ 0 1
180```
181
182Example
183-------
184
185With this policy:
186```
187minQuality 4
188forbiddenChars .?,
189checkRDN 1
190class-upperCase ABCDEFGHIJKLMNOPQRSTUVWXYZ 0 5
191class-lowerCase abcdefghijklmnopqrstuvwxyz 0 12
192class-digit 0123456789 0 1
193class-special <>,?;.:/!§ù%*µ^¨$£²&é~"#'{([-|è`_\ç^à@)]°=}+ 0 1
194class-myClass :) 1 1``
195```
196
197the password
198
199ThereIsNoCowLevel)
200
201is working, because,
202- it has 4 character classes validated : upper, lower, special, and myClass
203- it has no character among .?,
204- it has at least one character among : or )
205
206but it won't work for the user uid=John Cowlevel,ou=people,cn=example,cn=com,
207because the token "Cowlevel" from his RDN exists in the password (case insensitive).
208
209
210Logs
211----
212If a user password is rejected by ppm, the user will get this type of message:
213
214Typical user message from ldappasswd(5):
215  Result: Constraint violation (19)
216  Additional info: Password for dn=\"%s\" does not pass required number of strength checks (2 of 3)
217
218A more detailed message is written to the server log.
219
220Server log:
221
222```
223Feb 26 14:46:10 debian-10-64 slapd[1981]: conn=1000 op=16 MOD dn="uid=user,ou=persons,dc=my-domain,dc=com"
224Feb 26 14:46:10 debian-10-64 slapd[1981]: conn=1000 op=16 MOD attr=userPassword
225Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: entry uid=user,ou=persons,dc=my-domain,dc=com
226Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: Reading pwdCheckModuleArg attribute
227Feb 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_dictionary]#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 dictionary 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
228Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: Parsing pwdCheckModuleArg attribute
229Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # minQuality parameter
230Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Format:
231Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # minQuality [NUMBER]
232Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Description:
233Feb 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.
234Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # defines the minimum point numbers for the password to be accepted.
235Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: minQuality 3
236Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: Param = minQuality, value = 3, min = (null), minForPoint= (null)
237Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm:  Accepted replaced value: 3
238Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # checkRDN parameter
239Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Format:
240Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # checkRDN [0 | 1]
241Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Description:
242Feb 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.
243Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Tokens are separated by the following delimiters : space tabulation _ - , ; £
244Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: checkRDN 0
245Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: Param = checkRDN, value = 0, min = (null), minForPoint= (null)
246Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm:  Accepted replaced value: 0
247Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # forbiddenChars parameter
248Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Format:
249Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # forbiddenChars [CHARACTERS_FORBIDDEN]
250Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Description:
251Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Defines the forbidden characters list (no separator).
252Feb 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.
253Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: forbiddenChars
254Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: No value, goto next parameter
255Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # maxConsecutivePerClass parameter
256Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Format:
257Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # maxConsecutivePerClass [NUMBER]
258Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Description:
259Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Defines the maximum number of consecutive character allowed for any class
260Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: maxConsecutivePerClass 0
261Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: Param = maxConsecutivePerClass, value = 0, min = (null), minForPoint= (null)
262Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm:  Accepted replaced value: 0
263Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # useCracklib parameter
264Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Format:
265Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # useCracklib [0 | 1]
266Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Description:
267Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # If set to 1, the password must pass the cracklib check
268Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: useCracklib 0
269Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: Param = useCracklib, value = 0, min = (null), minForPoint= (null)
270Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm:  Accepted replaced value: 0
271Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # cracklibDict parameter
272Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Format:
273Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # cracklibDict [path_to_cracklib_dictionary]
274Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Description:
275Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # directory+filename-prefix that your version of CrackLib will go hunting for
276Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # For example, /var/pw_dict resolves as /var/pw_dict.pwd,
277Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # /var/pw_dict.pwi and /var/pw_dict.hwm dictionary files
278Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: cracklibDict /var/cache/cracklib/cracklib_dict
279Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: Param = cracklibDict, value = /var/cache/cracklib/cracklib_dict, min = (null), minForPoint= (null)
280Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm:  Accepted replaced value: /var/cache/cracklib/cracklib_dict
281Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # classes parameter
282Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Format:
283Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # class-[CLASS_NAME] [CHARACTERS_DEFINING_CLASS] [MIN] [MIN_FOR_POINT]
284Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # Description:
285Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: # [CHARACTERS_DEFINING_CLASS]: characters defining the class (no separator)
286Feb 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
287Feb 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
288Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: class-upperCase ABCDEFGHIJKLMNOPQRSTUVWXYZ 0 1
289Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: Param = class-upperCase, value = ABCDEFGHIJKLMNOPQRSTUVWXYZ, min = 0, minForPoint= 1
290Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm:  Accepted replaced value: ABCDEFGHIJKLMNOPQRSTUVWXYZ
291Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: class-lowerCase abcdefghijklmnopqrstuvwxyz 0 1
292Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: Param = class-lowerCase, value = abcdefghijklmnopqrstuvwxyz, min = 0, minForPoint= 1
293Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm:  Accepted replaced value: abcdefghijklmnopqrstuvwxyz
294Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: class-digit 0123456789 0 1
295Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: Param = class-digit, value = 0123456789, min = 0, minForPoint= 1
296Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm:  Accepted replaced value: 0123456789
297Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: get line: class-special <>,?;.:/!§ù%*µ^¨$£²&é~"#'{([-|è`_\ç^à@)]°=}+ 0 1
298Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: Param = class-special, value = <>,?;.:/!§ù%*µ^¨$£²&é~"#'{([-|è`_\ç^à@)]°=}+, min = 0, minForPoint= 1
299Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm:  Accepted replaced value: <>,?;.:/!§ù%*µ^¨$£²&é~"#'{([-|è`_\ç^à@)]°=}+
300Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: 1 point granted for class class-lowerCase
301Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: 1 point granted for class class-digit
302Feb 26 14:46:10 debian-10-64 slapd[1981]: ppm: Reallocating szErrStr from 64 to 173
303Feb 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]
304Feb 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)
305```
306
307
308Tests
309-----
310
311There is a unit test script: "unit_tests.sh" that illustrates checking some passwords.
312It is possible to test one particular password using directly the test program:
313
314```
315cd /usr/local/lib
316LD_LIBRARY_PATH=. ./ppm_test "uid=test,ou=users,dc=my-domain,dc=com" "my_password" "/usr/local/etc/openldap/ppm.example" && echo OK
317```
318
319
320
321HISTORY
322-------
323
324* 2021-02-23 David Coutadeur <david.coutadeur@gmail.com>
325  remove maxLength attribute (#21)
326  adapt the readme and documentation of ppm (#22)
327  prepare ppolicy10 in OpenLDAP 2.5 (#20, #23 and #24)
328  add pwdCheckModuleArg feature
329  Version 2.0
330* 2019-08-20 David Coutadeur <david.coutadeur@gmail.com>
331  adding debug symbols for ppm_test,
332  improve tests with the possibility to add username,
333  fix openldap crash when checkRDN=1 and username contains too short parts
334  Version 1.8
335* 2018-03-30 David Coutadeur <david.coutadeur@gmail.com>
336  various minor improvements provided by Tim Bishop (tdb) (compilation, test program,
337  imprvts in Makefile: new OLDAP_SOURCES variable pointing to OLDAP install. directory
338  Version 1.7
339* 2017-05-19 David Coutadeur <david.coutadeur@gmail.com>
340  Adds cracklib support
341  Readme adaptations and cleaning
342  Version 1.6
343* 2017-02-07 David Coutadeur <david.coutadeur@gmail.com>
344  Adds maxConsecutivePerClass (idea from Trevor Vaughan / tvaughan@onyxpoint.com)
345  Version 1.5
346* 2016-08-22 David Coutadeur <david.coutadeur@gmail.com>
347  Get config file from environment variable
348  Version 1.4
349* 2014-12-20 Daly Chikhaoui <dchikhaoui@janua.fr>
350  Adding checkRDN parameter
351  Version 1.3
352* 2014-10-28 David Coutadeur <david.coutadeur@gmail.com>
353  Adding maxLength parameter
354  Version 1.2
355* 2014-07-27 David Coutadeur <david.coutadeur@gmail.com>
356  Changing the configuration file and the configuration data structure
357  Version 1.1
358* 2014-04-04 David Coutadeur <david.coutadeur@gmail.com>
359  Version 1.0
360
361