1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3 *
4 * Copyright 2004-2021 The OpenLDAP Foundation.
5 * Portions Copyright 2004-2005 Howard Chu, Symas Corporation.
6 * Portions Copyright 2004 Hewlett-Packard Company.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted only as authorized by the OpenLDAP
11 * Public License.
12 *
13 * A copy of this license is available in the file LICENSE in the
14 * top-level directory of the distribution or, alternatively, at
15 * <http://www.OpenLDAP.org/license.html>.
16 */
17 /* ACKNOWLEDGEMENTS:
18 * This work was developed by Howard Chu for inclusion in
19 * OpenLDAP Software, based on prior work by Neil Dunbar (HP).
20 * This work was sponsored by the Hewlett-Packard Company.
21 */
22
23 #include "portable.h"
24
25 /* This file implements "Password Policy for LDAP Directories",
26 * based on draft behera-ldap-password-policy-09
27 */
28
29 #ifdef SLAPD_OVER_PPOLICY
30
31 #include <ldap.h>
32 #include "lutil.h"
33 #include "slap.h"
34 #ifdef SLAPD_MODULES
35 #define LIBLTDL_DLL_IMPORT /* Win32: don't re-export libltdl's symbols */
36 #include <ltdl.h>
37 #endif
38 #include <ac/errno.h>
39 #include <ac/time.h>
40 #include <ac/string.h>
41 #include <ac/ctype.h>
42 #include "slap-config.h"
43
44 #ifndef PPOLICY_DEFAULT_MAXRECORDED_FAILURE
45 #define PPOLICY_DEFAULT_MAXRECORDED_FAILURE 5
46 #endif
47
48 /* External password quality checking function.
49 * The error message must have a preallocated buffer and size
50 * passed in. Module can still allocate a buffer for
51 * it if the provided one is too small.
52 */
53 typedef int (check_func)( char *passwd, struct berval *errmsg, Entry *ent, struct berval *arg );
54 #define ERRBUFSIZ 256
55
56 /* Per-instance configuration information */
57 typedef struct pp_info {
58 struct berval def_policy; /* DN of default policy subentry */
59 int use_lockout; /* send AccountLocked result? */
60 int hash_passwords; /* transparently hash cleartext pwds */
61 int forward_updates; /* use frontend for policy state updates */
62 int disable_write;
63 int send_netscape_controls; /* send netscape password controls */
64 char *pwdCheckModule; /* name of module to dynamically
65 load to check password */
66 lt_dlhandle pwdCheckHandle; /* handle from lt_dlopen */
67 check_func *pwdCheckFunc;
68 ldap_pvt_thread_mutex_t pwdFailureTime_mutex;
69 } pp_info;
70
71 /* Our per-connection info - note, it is not per-instance, it is
72 * used by all instances
73 */
74 typedef struct pw_conn {
75 struct berval dn; /* DN of restricted user */
76 } pw_conn;
77
78 static pw_conn *pwcons;
79 static int ppolicy_cid;
80 static int account_usability_cid;
81 static int ov_count;
82
83 typedef struct pass_policy {
84 AttributeDescription *ad; /* attribute to which the policy applies */
85 int pwdMinAge; /* minimum time (seconds) until passwd can change */
86 int pwdMaxAge; /* time in seconds until pwd will expire after change */
87 int pwdMaxIdle; /* number of seconds since last successful bind before
88 passwd gets locked out */
89 int pwdInHistory; /* number of previous passwords kept */
90 int pwdCheckQuality; /* 0 = don't check quality, 1 = check if possible,
91 2 = check mandatory; fail if not possible */
92 int pwdMinLength; /* minimum number of chars in password */
93 int pwdMaxLength; /* maximum number of chars in password */
94 int pwdExpireWarning; /* number of seconds that warning controls are
95 sent before a password expires */
96 int pwdGraceExpiry; /* number of seconds after expiry grace logins are
97 valid */
98 int pwdGraceAuthNLimit; /* number of times you can log in with an
99 expired password */
100 int pwdLockout; /* 0 = do not lockout passwords, 1 = lock them out */
101 int pwdLockoutDuration; /* time in seconds a password is locked out for */
102 int pwdMinDelay; /* base bind delay in seconds on failure */
103 int pwdMaxDelay; /* maximum bind delay in seconds */
104 int pwdMaxFailure; /* number of failed binds allowed before lockout */
105 int pwdMaxRecordedFailure; /* number of failed binds to store */
106 int pwdFailureCountInterval; /* number of seconds before failure
107 counts are zeroed */
108 int pwdMustChange; /* 0 = users can use admin set password
109 1 = users must change password after admin set */
110 int pwdAllowUserChange; /* 0 = users cannot change their passwords
111 1 = users can change them */
112 int pwdSafeModify; /* 0 = old password doesn't need to come
113 with password change request
114 1 = password change must supply existing pwd */
115 int pwdUseCheckModule; /* 0 = do not use password check module, 1 = use */
116 struct berval pwdCheckModuleArg; /* Optional argument to the password check
117 module */
118 } PassPolicy;
119
120 typedef struct pw_hist {
121 time_t t; /* timestamp of history entry */
122 struct berval pw; /* old password hash */
123 struct berval bv; /* text of entire entry */
124 struct pw_hist *next;
125 } pw_hist;
126
127 /* Operational attributes */
128 static AttributeDescription *ad_pwdChangedTime, *ad_pwdAccountLockedTime,
129 *ad_pwdFailureTime, *ad_pwdHistory, *ad_pwdGraceUseTime, *ad_pwdReset,
130 *ad_pwdPolicySubentry, *ad_pwdStartTime, *ad_pwdEndTime,
131 *ad_pwdLastSuccess, *ad_pwdAccountTmpLockoutEnd;
132
133 /* Policy attributes */
134 static AttributeDescription *ad_pwdMinAge, *ad_pwdMaxAge, *ad_pwdMaxIdle,
135 *ad_pwdInHistory, *ad_pwdCheckQuality, *ad_pwdMinLength, *ad_pwdMaxLength,
136 *ad_pwdMaxFailure, *ad_pwdGraceExpiry, *ad_pwdGraceAuthNLimit,
137 *ad_pwdExpireWarning, *ad_pwdMinDelay, *ad_pwdMaxDelay,
138 *ad_pwdLockoutDuration, *ad_pwdFailureCountInterval,
139 *ad_pwdCheckModule, *ad_pwdCheckModuleArg, *ad_pwdUseCheckModule, *ad_pwdLockout,
140 *ad_pwdMustChange, *ad_pwdAllowUserChange, *ad_pwdSafeModify,
141 *ad_pwdAttribute, *ad_pwdMaxRecordedFailure;
142
143 static struct schema_info {
144 char *def;
145 AttributeDescription **ad;
146 } pwd_OpSchema[] = {
147 { "( 1.3.6.1.4.1.42.2.27.8.1.16 "
148 "NAME ( 'pwdChangedTime' ) "
149 "DESC 'The time the password was last changed' "
150 "EQUALITY generalizedTimeMatch "
151 "ORDERING generalizedTimeOrderingMatch "
152 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
153 "SINGLE-VALUE "
154 "NO-USER-MODIFICATION "
155 "USAGE directoryOperation )",
156 &ad_pwdChangedTime },
157 { "( 1.3.6.1.4.1.42.2.27.8.1.17 "
158 "NAME ( 'pwdAccountLockedTime' ) "
159 "DESC 'The time an user account was locked' "
160 "EQUALITY generalizedTimeMatch "
161 "ORDERING generalizedTimeOrderingMatch "
162 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
163 "SINGLE-VALUE "
164 #if 0 /* FIXME: ITS#9671 until we introduce a separate lockout flag? */
165 "NO-USER-MODIFICATION "
166 #endif
167 "USAGE directoryOperation )",
168 &ad_pwdAccountLockedTime },
169 { "( 1.3.6.1.4.1.42.2.27.8.1.19 "
170 "NAME ( 'pwdFailureTime' ) "
171 "DESC 'The timestamps of the last consecutive authentication failures' "
172 "EQUALITY generalizedTimeMatch "
173 "ORDERING generalizedTimeOrderingMatch "
174 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
175 "NO-USER-MODIFICATION "
176 "USAGE directoryOperation )",
177 &ad_pwdFailureTime },
178 { "( 1.3.6.1.4.1.42.2.27.8.1.20 "
179 "NAME ( 'pwdHistory' ) "
180 "DESC 'The history of users passwords' "
181 "EQUALITY octetStringMatch "
182 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 "
183 "NO-USER-MODIFICATION "
184 "USAGE directoryOperation )",
185 &ad_pwdHistory },
186 { "( 1.3.6.1.4.1.42.2.27.8.1.21 "
187 "NAME ( 'pwdGraceUseTime' ) "
188 "DESC 'The timestamps of the grace login once the password has expired' "
189 "EQUALITY generalizedTimeMatch "
190 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
191 "NO-USER-MODIFICATION "
192 "USAGE directoryOperation )",
193 &ad_pwdGraceUseTime },
194 { "( 1.3.6.1.4.1.42.2.27.8.1.22 "
195 "NAME ( 'pwdReset' ) "
196 "DESC 'The indication that the password has been reset' "
197 "EQUALITY booleanMatch "
198 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
199 "SINGLE-VALUE "
200 "USAGE directoryOperation )",
201 &ad_pwdReset },
202 { "( 1.3.6.1.4.1.42.2.27.8.1.23 "
203 "NAME ( 'pwdPolicySubentry' ) "
204 "DESC 'The pwdPolicy subentry in effect for this object' "
205 "EQUALITY distinguishedNameMatch "
206 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 "
207 "SINGLE-VALUE "
208 #if 0 /* ITS#9671: until we implement ITS#9343 or similar */
209 "NO-USER-MODIFICATION "
210 #endif
211 "USAGE directoryOperation )",
212 &ad_pwdPolicySubentry },
213 { "( 1.3.6.1.4.1.42.2.27.8.1.27 "
214 "NAME ( 'pwdStartTime' ) "
215 "DESC 'The time the password becomes enabled' "
216 "EQUALITY generalizedTimeMatch "
217 "ORDERING generalizedTimeOrderingMatch "
218 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
219 "SINGLE-VALUE "
220 "USAGE directoryOperation )",
221 &ad_pwdStartTime },
222 { "( 1.3.6.1.4.1.42.2.27.8.1.28 "
223 "NAME ( 'pwdEndTime' ) "
224 "DESC 'The time the password becomes disabled' "
225 "EQUALITY generalizedTimeMatch "
226 "ORDERING generalizedTimeOrderingMatch "
227 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
228 "SINGLE-VALUE "
229 "USAGE directoryOperation )",
230 &ad_pwdEndTime },
231 /* Defined in schema_prep.c now
232 { "( 1.3.6.1.4.1.42.2.27.8.1.29 "
233 "NAME ( 'pwdLastSuccess' ) "
234 "DESC 'The timestamp of the last successful authentication' "
235 "EQUALITY generalizedTimeMatch "
236 "ORDERING generalizedTimeOrderingMatch "
237 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
238 "SINGLE-VALUE "
239 "NO-USER-MODIFICATION "
240 "USAGE directoryOperation )",
241 &ad_pwdLastSuccess },
242 */
243 { "( 1.3.6.1.4.1.42.2.27.8.1.33 "
244 "NAME ( 'pwdAccountTmpLockoutEnd' ) "
245 "DESC 'Temporary lockout end' "
246 "EQUALITY generalizedTimeMatch "
247 "ORDERING generalizedTimeOrderingMatch "
248 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
249 "SINGLE-VALUE "
250 "NO-USER-MODIFICATION "
251 "USAGE directoryOperation )",
252 &ad_pwdAccountTmpLockoutEnd },
253
254 { "( 1.3.6.1.4.1.42.2.27.8.1.1 "
255 "NAME ( 'pwdAttribute' ) "
256 "EQUALITY objectIdentifierMatch "
257 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )",
258 &ad_pwdAttribute },
259 { "( 1.3.6.1.4.1.42.2.27.8.1.2 "
260 "NAME ( 'pwdMinAge' ) "
261 "EQUALITY integerMatch "
262 "ORDERING integerOrderingMatch "
263 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
264 "SINGLE-VALUE )",
265 &ad_pwdMinAge },
266 { "( 1.3.6.1.4.1.42.2.27.8.1.3 "
267 "NAME ( 'pwdMaxAge' ) "
268 "EQUALITY integerMatch "
269 "ORDERING integerOrderingMatch "
270 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
271 "SINGLE-VALUE )",
272 &ad_pwdMaxAge },
273 { "( 1.3.6.1.4.1.42.2.27.8.1.4 "
274 "NAME ( 'pwdInHistory' ) "
275 "EQUALITY integerMatch "
276 "ORDERING integerOrderingMatch "
277 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
278 "SINGLE-VALUE )",
279 &ad_pwdInHistory },
280 { "( 1.3.6.1.4.1.42.2.27.8.1.5 "
281 "NAME ( 'pwdCheckQuality' ) "
282 "EQUALITY integerMatch "
283 "ORDERING integerOrderingMatch "
284 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
285 "SINGLE-VALUE )",
286 &ad_pwdCheckQuality },
287 { "( 1.3.6.1.4.1.42.2.27.8.1.6 "
288 "NAME ( 'pwdMinLength' ) "
289 "EQUALITY integerMatch "
290 "ORDERING integerOrderingMatch "
291 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
292 "SINGLE-VALUE )",
293 &ad_pwdMinLength },
294 { "( 1.3.6.1.4.1.42.2.27.8.1.31 "
295 "NAME ( 'pwdMaxLength' ) "
296 "EQUALITY integerMatch "
297 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
298 "SINGLE-VALUE )",
299 &ad_pwdMaxLength },
300 { "( 1.3.6.1.4.1.42.2.27.8.1.7 "
301 "NAME ( 'pwdExpireWarning' ) "
302 "EQUALITY integerMatch "
303 "ORDERING integerOrderingMatch "
304 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
305 "SINGLE-VALUE )",
306 &ad_pwdExpireWarning },
307 { "( 1.3.6.1.4.1.42.2.27.8.1.8 "
308 "NAME ( 'pwdGraceAuthNLimit' ) "
309 "EQUALITY integerMatch "
310 "ORDERING integerOrderingMatch "
311 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
312 "SINGLE-VALUE )",
313 &ad_pwdGraceAuthNLimit },
314 { "( 1.3.6.1.4.1.42.2.27.8.1.30 "
315 "NAME ( 'pwdGraceExpiry' ) "
316 "EQUALITY integerMatch "
317 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
318 "SINGLE-VALUE )",
319 &ad_pwdGraceExpiry },
320 { "( 1.3.6.1.4.1.42.2.27.8.1.9 "
321 "NAME ( 'pwdLockout' ) "
322 "EQUALITY booleanMatch "
323 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
324 "SINGLE-VALUE )",
325 &ad_pwdLockout },
326 { "( 1.3.6.1.4.1.42.2.27.8.1.10 "
327 "NAME ( 'pwdLockoutDuration' ) "
328 "EQUALITY integerMatch "
329 "ORDERING integerOrderingMatch "
330 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
331 "SINGLE-VALUE )",
332 &ad_pwdLockoutDuration },
333 { "( 1.3.6.1.4.1.42.2.27.8.1.11 "
334 "NAME ( 'pwdMaxFailure' ) "
335 "EQUALITY integerMatch "
336 "ORDERING integerOrderingMatch "
337 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
338 "SINGLE-VALUE )",
339 &ad_pwdMaxFailure },
340 { "( 1.3.6.1.4.1.42.2.27.8.1.12 "
341 "NAME ( 'pwdFailureCountInterval' ) "
342 "EQUALITY integerMatch "
343 "ORDERING integerOrderingMatch "
344 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
345 "SINGLE-VALUE )",
346 &ad_pwdFailureCountInterval },
347 { "( 1.3.6.1.4.1.42.2.27.8.1.13 "
348 "NAME ( 'pwdMustChange' ) "
349 "EQUALITY booleanMatch "
350 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
351 "SINGLE-VALUE )",
352 &ad_pwdMustChange },
353 { "( 1.3.6.1.4.1.42.2.27.8.1.14 "
354 "NAME ( 'pwdAllowUserChange' ) "
355 "EQUALITY booleanMatch "
356 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
357 "SINGLE-VALUE )",
358 &ad_pwdAllowUserChange },
359 { "( 1.3.6.1.4.1.42.2.27.8.1.15 "
360 "NAME ( 'pwdSafeModify' ) "
361 "EQUALITY booleanMatch "
362 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
363 "SINGLE-VALUE )",
364 &ad_pwdSafeModify },
365 { "( 1.3.6.1.4.1.42.2.27.8.1.24 "
366 "NAME ( 'pwdMinDelay' ) "
367 "EQUALITY integerMatch "
368 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
369 "SINGLE-VALUE )",
370 &ad_pwdMinDelay },
371 { "( 1.3.6.1.4.1.42.2.27.8.1.25 "
372 "NAME ( 'pwdMaxDelay' ) "
373 "EQUALITY integerMatch "
374 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
375 "SINGLE-VALUE )",
376 &ad_pwdMaxDelay },
377 { "( 1.3.6.1.4.1.42.2.27.8.1.26 "
378 "NAME ( 'pwdMaxIdle' ) "
379 "EQUALITY integerMatch "
380 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
381 "SINGLE-VALUE )",
382 &ad_pwdMaxIdle },
383 { "( 1.3.6.1.4.1.42.2.27.8.1.32 "
384 "NAME ( 'pwdMaxRecordedFailure' ) "
385 "EQUALITY integerMatch "
386 "ORDERING integerOrderingMatch "
387 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
388 "SINGLE-VALUE )",
389 &ad_pwdMaxRecordedFailure },
390 { "( 1.3.6.1.4.1.4754.1.99.1 "
391 "NAME ( 'pwdCheckModule' ) "
392 "EQUALITY caseExactIA5Match "
393 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 "
394 "DESC 'Obsolete, no longer used' "
395 "OBSOLETE "
396 "SINGLE-VALUE )",
397 &ad_pwdCheckModule },
398 { "( 1.3.6.1.4.1.4754.1.99.2 "
399 "NAME ( 'pwdCheckModuleArg' ) "
400 "EQUALITY octetStringMatch "
401 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 "
402 "DESC 'Argument to pass to check_password() function' "
403 "SINGLE-VALUE )",
404 &ad_pwdCheckModuleArg },
405 { "( 1.3.6.1.4.1.4754.1.99.3 "
406 "NAME ( 'pwdUseCheckModule' ) "
407 "EQUALITY booleanMatch "
408 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
409 "DESC 'Toggle use of the loaded pwdCheckModule' "
410 "SINGLE-VALUE )",
411 &ad_pwdUseCheckModule },
412
413 { NULL, NULL }
414 };
415
416 static char *pwd_ocs[] = {
417 "( 1.3.6.1.4.1.4754.2.99.1 "
418 "NAME 'pwdPolicyChecker' "
419 "SUP top "
420 "AUXILIARY "
421 "MAY ( pwdCheckModule $ pwdCheckModuleArg $ pwdUseCheckModule ) )" ,
422 "( 1.3.6.1.4.1.42.2.27.8.2.1 "
423 "NAME 'pwdPolicy' "
424 "SUP top "
425 "AUXILIARY "
426 "MUST ( pwdAttribute ) "
427 "MAY ( pwdMinAge $ pwdMaxAge $ pwdInHistory $ pwdCheckQuality $ "
428 "pwdMinLength $ pwdMaxLength $ pwdExpireWarning $ "
429 "pwdGraceAuthNLimit $ pwdGraceExpiry $ pwdLockout $ "
430 "pwdLockoutDuration $ pwdMaxFailure $ pwdFailureCountInterval $ "
431 "pwdMustChange $ pwdAllowUserChange $ pwdSafeModify $ "
432 "pwdMinDelay $ pwdMaxDelay $ pwdMaxIdle $ "
433 "pwdMaxRecordedFailure ) )",
434 NULL
435 };
436
437 static ldap_pvt_thread_mutex_t chk_syntax_mutex;
438
439 enum {
440 PPOLICY_DEFAULT = 1,
441 PPOLICY_HASH_CLEARTEXT,
442 PPOLICY_USE_LOCKOUT,
443 PPOLICY_DISABLE_WRITE,
444 PPOLICY_CHECK_MODULE,
445 };
446
447 static ConfigDriver ppolicy_cf_default, ppolicy_cf_checkmod;
448
449 static ConfigTable ppolicycfg[] = {
450 { "ppolicy_default", "policyDN", 2, 2, 0,
451 ARG_DN|ARG_QUOTE|ARG_MAGIC|PPOLICY_DEFAULT, ppolicy_cf_default,
452 "( OLcfgOvAt:12.1 NAME 'olcPPolicyDefault' "
453 "DESC 'DN of a pwdPolicy object for uncustomized objects' "
454 "EQUALITY distinguishedNameMatch "
455 "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
456 { "ppolicy_hash_cleartext", "on|off", 1, 2, 0,
457 ARG_ON_OFF|ARG_OFFSET|PPOLICY_HASH_CLEARTEXT,
458 (void *)offsetof(pp_info,hash_passwords),
459 "( OLcfgOvAt:12.2 NAME 'olcPPolicyHashCleartext' "
460 "DESC 'Hash passwords on add or modify' "
461 "EQUALITY booleanMatch "
462 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
463 { "ppolicy_forward_updates", "on|off", 1, 2, 0,
464 ARG_ON_OFF|ARG_OFFSET,
465 (void *)offsetof(pp_info,forward_updates),
466 "( OLcfgOvAt:12.4 NAME 'olcPPolicyForwardUpdates' "
467 "DESC 'Allow policy state updates to be forwarded via updateref' "
468 "EQUALITY booleanMatch "
469 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
470 { "ppolicy_use_lockout", "on|off", 1, 2, 0,
471 ARG_ON_OFF|ARG_OFFSET|PPOLICY_USE_LOCKOUT,
472 (void *)offsetof(pp_info,use_lockout),
473 "( OLcfgOvAt:12.3 NAME 'olcPPolicyUseLockout' "
474 "DESC 'Warn clients with AccountLocked' "
475 "EQUALITY booleanMatch "
476 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
477 { "ppolicy_disable_write", "on|off", 1, 2, 0,
478 ARG_ON_OFF|ARG_OFFSET|PPOLICY_DISABLE_WRITE,
479 (void *)offsetof(pp_info,disable_write),
480 "( OLcfgOvAt:12.5 NAME 'olcPPolicyDisableWrite' "
481 "DESC 'Prevent all policy overlay writes' "
482 "EQUALITY booleanMatch "
483 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
484 { "ppolicy_send_netscape_controls", "on|off", 1, 2, 0,
485 ARG_ON_OFF|ARG_OFFSET,
486 (void *)offsetof(pp_info,send_netscape_controls),
487 "( OLcfgOvAt:12.6 NAME 'olcPPolicySendNetscapeControls' "
488 "DESC 'Send Netscape policy controls' "
489 "EQUALITY booleanMatch "
490 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
491 { "ppolicy_check_module", "path", 2, 2, 0,
492 ARG_STRING|ARG_MAGIC|PPOLICY_CHECK_MODULE, ppolicy_cf_checkmod,
493 "( OLcfgOvAt:12.7 NAME 'olcPPolicyCheckModule' "
494 "DESC 'Loadable module that instantiates check_password() function' "
495 "EQUALITY caseExactIA5Match "
496 "SYNTAX OMsIA5String "
497 "SINGLE-VALUE )", NULL, NULL },
498 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
499 };
500
501 static ConfigOCs ppolicyocs[] = {
502 { "( OLcfgOvOc:12.1 "
503 "NAME 'olcPPolicyConfig' "
504 "DESC 'Password Policy configuration' "
505 "SUP olcOverlayConfig "
506 "MAY ( olcPPolicyDefault $ olcPPolicyHashCleartext $ "
507 "olcPPolicyUseLockout $ olcPPolicyForwardUpdates $ "
508 "olcPPolicyDisableWrite $ olcPPolicySendNetscapeControls $ "
509 "olcPPolicyCheckModule ) )",
510 Cft_Overlay, ppolicycfg },
511 { NULL, 0, NULL }
512 };
513
514 static int
ppolicy_cf_default(ConfigArgs * c)515 ppolicy_cf_default( ConfigArgs *c )
516 {
517 slap_overinst *on = (slap_overinst *)c->bi;
518 pp_info *pi = (pp_info *)on->on_bi.bi_private;
519 int rc = ARG_BAD_CONF;
520
521 assert ( c->type == PPOLICY_DEFAULT );
522 Debug(LDAP_DEBUG_TRACE, "==> ppolicy_cf_default\n" );
523
524 switch ( c->op ) {
525 case SLAP_CONFIG_EMIT:
526 Debug(LDAP_DEBUG_TRACE, "==> ppolicy_cf_default emit\n" );
527 rc = 0;
528 if ( !BER_BVISEMPTY( &pi->def_policy )) {
529 rc = value_add_one( &c->rvalue_vals,
530 &pi->def_policy );
531 if ( rc ) return rc;
532 rc = value_add_one( &c->rvalue_nvals,
533 &pi->def_policy );
534 }
535 break;
536 case LDAP_MOD_DELETE:
537 Debug(LDAP_DEBUG_TRACE, "==> ppolicy_cf_default delete\n" );
538 if ( pi->def_policy.bv_val ) {
539 ber_memfree ( pi->def_policy.bv_val );
540 pi->def_policy.bv_val = NULL;
541 }
542 pi->def_policy.bv_len = 0;
543 rc = 0;
544 break;
545 case SLAP_CONFIG_ADD:
546 /* fallthru to LDAP_MOD_ADD */
547 case LDAP_MOD_ADD:
548 Debug(LDAP_DEBUG_TRACE, "==> ppolicy_cf_default add\n" );
549 if ( pi->def_policy.bv_val ) {
550 ber_memfree ( pi->def_policy.bv_val );
551 }
552 pi->def_policy = c->value_ndn;
553 ber_memfree( c->value_dn.bv_val );
554 BER_BVZERO( &c->value_dn );
555 BER_BVZERO( &c->value_ndn );
556 rc = 0;
557 break;
558 default:
559 abort ();
560 }
561
562 return rc;
563 }
564
565 static int
ppolicy_cf_checkmod(ConfigArgs * c)566 ppolicy_cf_checkmod( ConfigArgs *c )
567 {
568 slap_overinst *on = (slap_overinst *)c->bi;
569 pp_info *pi = (pp_info *)on->on_bi.bi_private;
570 int rc = ARG_BAD_CONF;
571
572 assert ( c->type == PPOLICY_CHECK_MODULE );
573 Debug(LDAP_DEBUG_TRACE, "==> ppolicy_cf_checkmod\n" );
574
575 switch ( c->op ) {
576 case SLAP_CONFIG_EMIT:
577 if ( pi->pwdCheckModule ) {
578 c->value_string = ch_strdup( pi->pwdCheckModule );
579 rc = 0;
580 }
581 break;
582 case LDAP_MOD_DELETE:
583 if ( pi->pwdCheckHandle ) {
584 lt_dlclose( pi->pwdCheckHandle );
585 pi->pwdCheckHandle = NULL;
586 pi->pwdCheckFunc = NULL;
587 }
588 ch_free( pi->pwdCheckModule );
589 pi->pwdCheckModule = NULL;
590 rc = 0;
591 break;
592 case SLAP_CONFIG_ADD:
593 /* fallthru to LDAP_MOD_ADD */
594 case LDAP_MOD_ADD:
595 pi->pwdCheckHandle = lt_dlopen( c->value_string );
596 if ( pi->pwdCheckHandle == NULL ) {
597 const char *dlerr = lt_dlerror();
598 snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> lt_dlopen(%s) failed: %s",
599 c->argv[0], c->value_string, dlerr );
600 Debug(LDAP_DEBUG_ANY, "%s: %s\n", c->log, c->cr_msg );
601 } else {
602 if (( pi->pwdCheckFunc = lt_dlsym( pi->pwdCheckHandle, "check_password" )) == NULL) {
603 const char *dlerr = lt_dlerror();
604 snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> lt_dlsym(%s) failed: %s",
605 c->argv[0], c->value_string, dlerr );
606 Debug(LDAP_DEBUG_ANY, "%s: %s\n", c->log, c->cr_msg );
607 } else {
608 pi->pwdCheckModule = c->value_string;
609 rc = 0;
610 }
611 }
612 break;
613 default:
614 abort ();
615 }
616
617 return rc;
618 }
619
620 static time_t
parse_time(char * atm)621 parse_time( char *atm )
622 {
623 struct lutil_tm tm;
624 struct lutil_timet tt;
625 time_t ret = (time_t)-1;
626
627 if ( lutil_parsetime( atm, &tm ) == 0) {
628 lutil_tm2time( &tm, &tt );
629 ret = tt.tt_sec;
630 }
631 return ret;
632 }
633
634 static int
account_locked(Operation * op,Entry * e,PassPolicy * pp,Modifications ** mod)635 account_locked( Operation *op, Entry *e,
636 PassPolicy *pp, Modifications **mod )
637 {
638 Attribute *la;
639
640 if ( (la = attr_find( e->e_attrs, ad_pwdStartTime )) != NULL ) {
641 BerVarray vals = la->a_nvals;
642 time_t then, now = op->o_time;
643
644 /*
645 * Password has a defined start of validity
646 */
647 if ( vals[0].bv_val != NULL ) {
648 if ( (then = parse_time( vals[0].bv_val )) == (time_t)-1 ) {
649 return 1;
650 }
651 if ( now < then ) {
652 return 1;
653 }
654 }
655 }
656
657 if ( (la = attr_find( e->e_attrs, ad_pwdEndTime )) != NULL ) {
658 BerVarray vals = la->a_nvals;
659 time_t then, now = op->o_time;
660
661 /*
662 * Password has a defined end of validity
663 */
664 if ( vals[0].bv_val != NULL ) {
665 if ( (then = parse_time( vals[0].bv_val )) == (time_t)-1 ) {
666 return 1;
667 }
668 if ( then <= now ) {
669 return 1;
670 }
671 }
672 }
673
674 if ( !pp->pwdLockout )
675 return 0;
676
677 if ( (la = attr_find( e->e_attrs, ad_pwdAccountTmpLockoutEnd )) != NULL ) {
678 BerVarray vals = la->a_nvals;
679 time_t then, now = op->o_time;
680
681 /*
682 * We have temporarily locked the account after a failure
683 */
684 if ( vals[0].bv_val != NULL ) {
685 if ( (then = parse_time( vals[0].bv_val )) == (time_t)-1 ) {
686 return 1;
687 }
688 if ( now < then ) {
689 return 1;
690 }
691 }
692 }
693
694 /* Only check if database maintains lastbind */
695 if ( pp->pwdMaxIdle && SLAP_LASTBIND( op->o_bd ) ) {
696 time_t lastbindtime = (time_t)-1;
697
698 la = attr_find( e->e_attrs, ad_pwdLastSuccess );
699 if ( la == NULL ) {
700 la = attr_find( e->e_attrs, ad_pwdChangedTime );
701 }
702 if ( la != NULL ) {
703 lastbindtime = parse_time( la->a_nvals[0].bv_val );
704 }
705
706 if ( lastbindtime != (time_t)-1 &&
707 op->o_time > lastbindtime + pp->pwdMaxIdle ) {
708 return 1;
709 }
710 }
711
712 if ( (la = attr_find( e->e_attrs, ad_pwdAccountLockedTime )) != NULL ) {
713 BerVarray vals = la->a_nvals;
714
715 /*
716 * there is a lockout stamp - we now need to know if it's
717 * a valid one.
718 */
719 if (vals[0].bv_val != NULL) {
720 time_t then, now;
721 Modifications *m;
722
723 if ((then = parse_time( vals[0].bv_val )) == (time_t)0)
724 return 1;
725
726 now = slap_get_time();
727
728 /* Still in the future? not yet in effect */
729 if (now < then)
730 return 0;
731
732 if (!pp->pwdLockoutDuration)
733 return 1;
734
735 if (now < then + pp->pwdLockoutDuration)
736 return 1;
737
738 if ( mod != NULL ) {
739 m = ch_calloc( sizeof(Modifications), 1 );
740 m->sml_op = LDAP_MOD_DELETE;
741 m->sml_flags = 0;
742 m->sml_type = ad_pwdAccountLockedTime->ad_cname;
743 m->sml_desc = ad_pwdAccountLockedTime;
744 m->sml_next = *mod;
745 *mod = m;
746 }
747 }
748 }
749
750 return 0;
751 }
752
753 /* IMPLICIT TAGS, all context-specific */
754 #define PPOLICY_WARNING 0xa0L /* constructed + 0 */
755 #define PPOLICY_ERROR 0x81L /* primitive + 1 */
756
757 #define PPOLICY_EXPIRE 0x80L /* primitive + 0 */
758 #define PPOLICY_GRACE 0x81L /* primitive + 1 */
759
760 static const char ppolicy_ctrl_oid[] = LDAP_CONTROL_PASSWORDPOLICYRESPONSE;
761 static const char ppolicy_account_ctrl_oid[] = LDAP_CONTROL_X_ACCOUNT_USABILITY;
762 static const char ppolicy_pwd_expired_oid[] = LDAP_CONTROL_X_PASSWORD_EXPIRED;
763 static const char ppolicy_pwd_expiring_oid[] = LDAP_CONTROL_X_PASSWORD_EXPIRING;
764
765 static LDAPControl *
create_passcontrol(Operation * op,int exptime,int grace,LDAPPasswordPolicyError err)766 create_passcontrol( Operation *op, int exptime, int grace, LDAPPasswordPolicyError err )
767 {
768 BerElementBuffer berbuf, bb2;
769 BerElement *ber = (BerElement *) &berbuf, *b2 = (BerElement *) &bb2;
770 LDAPControl c = { 0 }, *cp;
771 struct berval bv;
772 int rc;
773
774 BER_BVZERO( &c.ldctl_value );
775
776 ber_init2( ber, NULL, LBER_USE_DER );
777 ber_printf( ber, "{" /*}*/ );
778
779 if ( exptime >= 0 ) {
780 ber_init2( b2, NULL, LBER_USE_DER );
781 ber_printf( b2, "ti", PPOLICY_EXPIRE, exptime );
782 rc = ber_flatten2( b2, &bv, 1 );
783 (void)ber_free_buf(b2);
784 if (rc == -1) {
785 cp = NULL;
786 goto fail;
787 }
788 ber_printf( ber, "tO", PPOLICY_WARNING, &bv );
789 ch_free( bv.bv_val );
790 } else if ( grace >= 0 ) {
791 ber_init2( b2, NULL, LBER_USE_DER );
792 ber_printf( b2, "ti", PPOLICY_GRACE, grace );
793 rc = ber_flatten2( b2, &bv, 1 );
794 (void)ber_free_buf(b2);
795 if (rc == -1) {
796 cp = NULL;
797 goto fail;
798 }
799 ber_printf( ber, "tO", PPOLICY_WARNING, &bv );
800 ch_free( bv.bv_val );
801 }
802
803 if (err != PP_noError ) {
804 ber_printf( ber, "te", PPOLICY_ERROR, err );
805 }
806 ber_printf( ber, /*{*/ "N}" );
807
808 if (ber_flatten2( ber, &c.ldctl_value, 0 ) == -1) {
809 return NULL;
810 }
811 cp = op->o_tmpalloc( sizeof( LDAPControl ) + c.ldctl_value.bv_len, op->o_tmpmemctx );
812 cp->ldctl_oid = (char *)ppolicy_ctrl_oid;
813 cp->ldctl_iscritical = 0;
814 cp->ldctl_value.bv_val = (char *)&cp[1];
815 cp->ldctl_value.bv_len = c.ldctl_value.bv_len;
816 AC_MEMCPY( cp->ldctl_value.bv_val, c.ldctl_value.bv_val, c.ldctl_value.bv_len );
817 fail:
818 (void)ber_free_buf(ber);
819
820 return cp;
821 }
822
823 static LDAPControl *
create_passexpiry(Operation * op,int expired,int warn)824 create_passexpiry( Operation *op, int expired, int warn )
825 {
826 LDAPControl *cp;
827 char buf[sizeof("-2147483648")];
828 struct berval bv = { .bv_val = buf, .bv_len = sizeof(buf) };
829
830 bv.bv_len = snprintf( bv.bv_val, bv.bv_len, "%d", warn );
831
832 cp = op->o_tmpalloc( sizeof( LDAPControl ) + bv.bv_len, op->o_tmpmemctx );
833 if ( expired ) {
834 cp->ldctl_oid = (char *)ppolicy_pwd_expired_oid;
835 } else {
836 cp->ldctl_oid = (char *)ppolicy_pwd_expiring_oid;
837 }
838 cp->ldctl_iscritical = 0;
839 cp->ldctl_value.bv_val = (char *)&cp[1];
840 cp->ldctl_value.bv_len = bv.bv_len;
841 AC_MEMCPY( cp->ldctl_value.bv_val, bv.bv_val, bv.bv_len );
842 return cp;
843 }
844
845 static LDAPControl **
add_passcontrol(Operation * op,SlapReply * rs,LDAPControl * ctrl)846 add_passcontrol( Operation *op, SlapReply *rs, LDAPControl *ctrl )
847 {
848 LDAPControl **ctrls, **oldctrls = rs->sr_ctrls;
849 int n;
850
851 n = 0;
852 if ( oldctrls ) {
853 for ( ; oldctrls[n]; n++ )
854 ;
855 }
856 n += 2;
857
858 ctrls = op->o_tmpcalloc( sizeof( LDAPControl * ), n, op->o_tmpmemctx );
859
860 n = 0;
861 if ( oldctrls ) {
862 for ( ; oldctrls[n]; n++ ) {
863 ctrls[n] = oldctrls[n];
864 }
865 }
866 ctrls[n] = ctrl;
867 ctrls[n+1] = NULL;
868
869 rs->sr_ctrls = ctrls;
870
871 return oldctrls;
872 }
873
874 static void
add_account_control(Operation * op,SlapReply * rs,int available,int remaining,LDAPAccountUsabilityMoreInfo * more_info)875 add_account_control(
876 Operation *op,
877 SlapReply *rs,
878 int available,
879 int remaining,
880 LDAPAccountUsabilityMoreInfo *more_info )
881 {
882 BerElementBuffer berbuf;
883 BerElement *ber = (BerElement *) &berbuf;
884 LDAPControl c = { 0 }, *cp = NULL, **ctrls;
885 int i = 0;
886
887 BER_BVZERO( &c.ldctl_value );
888
889 ber_init2( ber, NULL, LBER_USE_DER );
890
891 if ( available ) {
892 ber_put_int( ber, remaining, LDAP_TAG_X_ACCOUNT_USABILITY_AVAILABLE );
893 } else {
894 assert( more_info != NULL );
895
896 ber_start_seq( ber, LDAP_TAG_X_ACCOUNT_USABILITY_NOT_AVAILABLE );
897 ber_put_boolean( ber, more_info->inactive, LDAP_TAG_X_ACCOUNT_USABILITY_INACTIVE );
898 ber_put_boolean( ber, more_info->reset, LDAP_TAG_X_ACCOUNT_USABILITY_RESET );
899 ber_put_boolean( ber, more_info->expired, LDAP_TAG_X_ACCOUNT_USABILITY_EXPIRED );
900 ber_put_int( ber, more_info->remaining_grace, LDAP_TAG_X_ACCOUNT_USABILITY_REMAINING_GRACE );
901 ber_put_int( ber, more_info->seconds_before_unlock, LDAP_TAG_X_ACCOUNT_USABILITY_UNTIL_UNLOCK );
902 ber_put_seq( ber );
903 }
904
905 if (ber_flatten2( ber, &c.ldctl_value, 0 ) == -1) {
906 goto fail;
907 }
908
909 if ( rs->sr_ctrls != NULL ) {
910 for ( ; rs->sr_ctrls[ i ] != NULL; i++ ) /* Count */;
911 }
912
913 ctrls = op->o_tmprealloc( rs->sr_ctrls, sizeof(LDAPControl *)*( i + 2 ), op->o_tmpmemctx );
914 if ( ctrls == NULL ) {
915 goto fail;
916 }
917
918 cp = op->o_tmpalloc( sizeof( LDAPControl ) + c.ldctl_value.bv_len, op->o_tmpmemctx );
919 cp->ldctl_oid = (char *)ppolicy_account_ctrl_oid;
920 cp->ldctl_iscritical = 0;
921 cp->ldctl_value.bv_val = (char *)&cp[1];
922 cp->ldctl_value.bv_len = c.ldctl_value.bv_len;
923 AC_MEMCPY( cp->ldctl_value.bv_val, c.ldctl_value.bv_val, c.ldctl_value.bv_len );
924
925 ctrls[ i ] = cp;
926 ctrls[ i + 1 ] = NULL;
927 rs->sr_ctrls = ctrls;
928
929 fail:
930 (void)ber_free_buf(ber);
931 }
932
933 static void
ppolicy_get_default(PassPolicy * pp)934 ppolicy_get_default( PassPolicy *pp )
935 {
936 memset( pp, 0, sizeof(PassPolicy) );
937
938 pp->ad = slap_schema.si_ad_userPassword;
939
940 /* Users can change their own password by default */
941 pp->pwdAllowUserChange = 1;
942 }
943
944
945 static int
ppolicy_get(Operation * op,Entry * e,PassPolicy * pp)946 ppolicy_get( Operation *op, Entry *e, PassPolicy *pp )
947 {
948 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
949 pp_info *pi = on->on_bi.bi_private;
950 BackendDB *bd, *bd_orig = op->o_bd;
951 AttributeDescription *ad = NULL;
952 Attribute *a;
953 BerVarray vals;
954 int rc = LDAP_SUCCESS;
955 Entry *pe = NULL;
956 #if 0
957 const char *text;
958 #endif
959
960 ppolicy_get_default( pp );
961
962 ad = ad_pwdPolicySubentry;
963 if ( (a = attr_find( e->e_attrs, ad )) == NULL ) {
964 /*
965 * entry has no password policy assigned - use default
966 */
967 vals = &pi->def_policy;
968 if ( !vals->bv_val )
969 goto defaultpol;
970 } else {
971 vals = a->a_nvals;
972 if (vals[0].bv_val == NULL) {
973 Debug( LDAP_DEBUG_ANY,
974 "ppolicy_get: NULL value for policySubEntry\n" );
975 goto defaultpol;
976 }
977 }
978
979 op->o_bd = bd = select_backend( vals, 0 );
980 if ( op->o_bd == NULL ) {
981 op->o_bd = bd_orig;
982 goto defaultpol;
983 }
984
985 rc = be_entry_get_rw( op, vals, NULL, NULL, 0, &pe );
986 op->o_bd = bd_orig;
987
988 if ( rc ) goto defaultpol;
989
990 #if 0 /* Only worry about userPassword for now */
991 if ((a = attr_find( pe->e_attrs, ad_pwdAttribute )))
992 slap_bv2ad( &a->a_vals[0], &pp->ad, &text );
993 #endif
994
995 ad = ad_pwdMinAge;
996 if ( (a = attr_find( pe->e_attrs, ad ))
997 && lutil_atoi( &pp->pwdMinAge, a->a_vals[0].bv_val ) != 0 ) {
998 rc = LDAP_CONSTRAINT_VIOLATION;
999 goto defaultpol;
1000 }
1001
1002 ad = ad_pwdMaxAge;
1003 if ( (a = attr_find( pe->e_attrs, ad ))
1004 && lutil_atoi( &pp->pwdMaxAge, a->a_vals[0].bv_val ) != 0 ) {
1005 rc = LDAP_CONSTRAINT_VIOLATION;
1006 goto defaultpol;
1007 }
1008
1009 ad = ad_pwdMaxIdle;
1010 if ( (a = attr_find( pe->e_attrs, ad ))
1011 && lutil_atoi( &pp->pwdMaxIdle, a->a_vals[0].bv_val ) != 0 ) {
1012 rc = LDAP_CONSTRAINT_VIOLATION;
1013 goto defaultpol;
1014 }
1015
1016 ad = ad_pwdInHistory;
1017 if ( (a = attr_find( pe->e_attrs, ad ))
1018 && lutil_atoi( &pp->pwdInHistory, a->a_vals[0].bv_val ) != 0 ) {
1019 rc = LDAP_CONSTRAINT_VIOLATION;
1020 goto defaultpol;
1021 }
1022
1023 ad = ad_pwdCheckQuality;
1024 if ( (a = attr_find( pe->e_attrs, ad ))
1025 && lutil_atoi( &pp->pwdCheckQuality, a->a_vals[0].bv_val ) != 0 ) {
1026 rc = LDAP_CONSTRAINT_VIOLATION;
1027 goto defaultpol;
1028 }
1029
1030 ad = ad_pwdMinLength;
1031 if ( (a = attr_find( pe->e_attrs, ad ))
1032 && lutil_atoi( &pp->pwdMinLength, a->a_vals[0].bv_val ) != 0 ) {
1033 rc = LDAP_CONSTRAINT_VIOLATION;
1034 goto defaultpol;
1035 }
1036
1037 ad = ad_pwdMaxLength;
1038 if ( (a = attr_find( pe->e_attrs, ad ))
1039 && lutil_atoi( &pp->pwdMaxLength, a->a_vals[0].bv_val ) != 0 ) {
1040 rc = LDAP_CONSTRAINT_VIOLATION;
1041 goto defaultpol;
1042 }
1043
1044 ad = ad_pwdMaxFailure;
1045 if ( (a = attr_find( pe->e_attrs, ad ))
1046 && lutil_atoi( &pp->pwdMaxFailure, a->a_vals[0].bv_val ) != 0 ) {
1047 rc = LDAP_CONSTRAINT_VIOLATION;
1048 goto defaultpol;
1049 }
1050
1051 ad = ad_pwdMaxRecordedFailure;
1052 if ( (a = attr_find( pe->e_attrs, ad ))
1053 && lutil_atoi( &pp->pwdMaxRecordedFailure, a->a_vals[0].bv_val ) != 0 ) {
1054 rc = LDAP_CONSTRAINT_VIOLATION;
1055 goto defaultpol;
1056 }
1057
1058 ad = ad_pwdGraceExpiry;
1059 if ( (a = attr_find( pe->e_attrs, ad ))
1060 && lutil_atoi( &pp->pwdGraceExpiry, a->a_vals[0].bv_val ) != 0 ) {
1061 rc = LDAP_CONSTRAINT_VIOLATION;
1062 goto defaultpol;
1063 }
1064
1065 ad = ad_pwdGraceAuthNLimit;
1066 if ( (a = attr_find( pe->e_attrs, ad ))
1067 && lutil_atoi( &pp->pwdGraceAuthNLimit, a->a_vals[0].bv_val ) != 0 ) {
1068 rc = LDAP_CONSTRAINT_VIOLATION;
1069 goto defaultpol;
1070 }
1071
1072 ad = ad_pwdExpireWarning;
1073 if ( (a = attr_find( pe->e_attrs, ad ))
1074 && lutil_atoi( &pp->pwdExpireWarning, a->a_vals[0].bv_val ) != 0 ) {
1075 rc = LDAP_CONSTRAINT_VIOLATION;
1076 goto defaultpol;
1077 }
1078
1079 ad = ad_pwdFailureCountInterval;
1080 if ( (a = attr_find( pe->e_attrs, ad ))
1081 && lutil_atoi( &pp->pwdFailureCountInterval, a->a_vals[0].bv_val ) != 0 ) {
1082 rc = LDAP_CONSTRAINT_VIOLATION;
1083 goto defaultpol;
1084 }
1085
1086 ad = ad_pwdLockoutDuration;
1087 if ( (a = attr_find( pe->e_attrs, ad ))
1088 && lutil_atoi( &pp->pwdLockoutDuration, a->a_vals[0].bv_val ) != 0 ) {
1089 rc = LDAP_CONSTRAINT_VIOLATION;
1090 goto defaultpol;
1091 }
1092
1093 ad = ad_pwdMinDelay;
1094 if ( (a = attr_find( pe->e_attrs, ad ))
1095 && lutil_atoi( &pp->pwdMinDelay, a->a_vals[0].bv_val ) != 0 ) {
1096 rc = LDAP_CONSTRAINT_VIOLATION;
1097 goto defaultpol;
1098 }
1099
1100 ad = ad_pwdMaxDelay;
1101 if ( (a = attr_find( pe->e_attrs, ad ))
1102 && lutil_atoi( &pp->pwdMaxDelay, a->a_vals[0].bv_val ) != 0 ) {
1103 rc = LDAP_CONSTRAINT_VIOLATION;
1104 goto defaultpol;
1105 }
1106
1107 ad = ad_pwdCheckModule;
1108 if ( attr_find( pe->e_attrs, ad )) {
1109 Debug( LDAP_DEBUG_ANY, "ppolicy_get: "
1110 "WARNING: Ignoring OBSOLETE attribute %s in policy %s.\n",
1111 ad->ad_cname.bv_val, pe->e_name.bv_val );
1112 }
1113
1114 ad = ad_pwdUseCheckModule;
1115 if ( (a = attr_find( pe->e_attrs, ad )) )
1116 pp->pwdUseCheckModule = bvmatch( &a->a_nvals[0], &slap_true_bv );
1117
1118 ad = ad_pwdCheckModuleArg;
1119 if ( (a = attr_find( pe->e_attrs, ad )) ) {
1120 ber_dupbv_x( &pp->pwdCheckModuleArg, &a->a_vals[0], op->o_tmpmemctx );
1121 }
1122
1123 ad = ad_pwdLockout;
1124 if ( (a = attr_find( pe->e_attrs, ad )) )
1125 pp->pwdLockout = bvmatch( &a->a_nvals[0], &slap_true_bv );
1126
1127 ad = ad_pwdMustChange;
1128 if ( (a = attr_find( pe->e_attrs, ad )) )
1129 pp->pwdMustChange = bvmatch( &a->a_nvals[0], &slap_true_bv );
1130
1131 ad = ad_pwdAllowUserChange;
1132 if ( (a = attr_find( pe->e_attrs, ad )) )
1133 pp->pwdAllowUserChange = bvmatch( &a->a_nvals[0], &slap_true_bv );
1134
1135 ad = ad_pwdSafeModify;
1136 if ( (a = attr_find( pe->e_attrs, ad )) )
1137 pp->pwdSafeModify = bvmatch( &a->a_nvals[0], &slap_true_bv );
1138
1139 if ( pp->pwdMaxRecordedFailure < pp->pwdMaxFailure )
1140 pp->pwdMaxRecordedFailure = pp->pwdMaxFailure;
1141
1142 if ( !pp->pwdMaxRecordedFailure && pp->pwdMinDelay )
1143 pp->pwdMaxRecordedFailure = PPOLICY_DEFAULT_MAXRECORDED_FAILURE;
1144
1145 if ( pp->pwdMinDelay && !pp->pwdMaxDelay ) {
1146 Debug( LDAP_DEBUG_ANY, "ppolicy_get: "
1147 "pwdMinDelay was set but pwdMaxDelay wasn't, assuming they "
1148 "are equal\n" );
1149 pp->pwdMaxDelay = pp->pwdMinDelay;
1150 }
1151
1152 op->o_bd = bd;
1153 be_entry_release_r( op, pe );
1154 op->o_bd = bd_orig;
1155
1156 return LDAP_SUCCESS;
1157
1158 defaultpol:
1159 if ( pe ) {
1160 op->o_bd = bd;
1161 be_entry_release_r( op, pe );
1162 op->o_bd = bd_orig;
1163 }
1164
1165 if ( rc && !BER_BVISNULL( vals ) ) {
1166 Debug( LDAP_DEBUG_ANY, "ppolicy_get: "
1167 "policy subentry %s missing or invalid at '%s', "
1168 "no policy will be applied!\n",
1169 vals->bv_val, ad ? ad->ad_cname.bv_val : "" );
1170 } else {
1171 Debug( LDAP_DEBUG_TRACE,
1172 "ppolicy_get: using default policy\n" );
1173 }
1174
1175 ppolicy_get_default( pp );
1176
1177 return -1;
1178 }
1179
1180 static int
password_scheme(struct berval * cred,struct berval * sch)1181 password_scheme( struct berval *cred, struct berval *sch )
1182 {
1183 int e;
1184
1185 assert( cred != NULL );
1186
1187 if (sch) {
1188 sch->bv_val = NULL;
1189 sch->bv_len = 0;
1190 }
1191
1192 if ((cred->bv_len == 0) || (cred->bv_val == NULL) ||
1193 (cred->bv_val[0] != '{')) return LDAP_OTHER;
1194
1195 for(e = 1; cred->bv_val[e] && cred->bv_val[e] != '}'; e++);
1196 if (cred->bv_val[e]) {
1197 int rc;
1198 rc = lutil_passwd_scheme( cred->bv_val );
1199 if (rc) {
1200 if (sch) {
1201 sch->bv_val = cred->bv_val;
1202 sch->bv_len = e;
1203 }
1204 return LDAP_SUCCESS;
1205 }
1206 }
1207 return LDAP_OTHER;
1208 }
1209
1210 static int
check_password_quality(struct berval * cred,pp_info * pi,PassPolicy * pp,LDAPPasswordPolicyError * err,Entry * e,struct berval * errmsg)1211 check_password_quality( struct berval *cred, pp_info *pi, PassPolicy *pp, LDAPPasswordPolicyError *err,
1212 Entry *e, struct berval *errmsg )
1213 {
1214 int rc = LDAP_SUCCESS, ok = LDAP_SUCCESS;
1215 char *ptr;
1216 struct berval sch;
1217
1218 assert( cred != NULL );
1219 assert( pp != NULL );
1220 assert( errmsg != NULL );
1221
1222 ptr = errmsg->bv_val;
1223 *ptr = '\0';
1224
1225 ptr = cred->bv_val;
1226
1227 if ((cred->bv_len == 0) || (pp->pwdMinLength > cred->bv_len)) {
1228 rc = LDAP_CONSTRAINT_VIOLATION;
1229 if ( err ) *err = PP_passwordTooShort;
1230 return rc;
1231 }
1232
1233 if ( pp->pwdMaxLength && cred->bv_len > pp->pwdMaxLength ) {
1234 rc = LDAP_CONSTRAINT_VIOLATION;
1235 if ( err ) *err = PP_passwordTooLong;
1236 return rc;
1237 }
1238
1239 /*
1240 * We need to know if the password is already hashed - if so
1241 * what scheme is it. The reason being that the "hash" of
1242 * {cleartext} still allows us to check the password.
1243 */
1244 rc = password_scheme( cred, &sch );
1245 if (rc == LDAP_SUCCESS) {
1246 if ((sch.bv_val) && (strncasecmp( sch.bv_val, "{cleartext}",
1247 sch.bv_len ) == 0)) {
1248 /*
1249 * We can check the cleartext "hash"
1250 */
1251 ptr = cred->bv_val + sch.bv_len;
1252 } else {
1253 /* everything else, we can't check */
1254 if (pp->pwdCheckQuality == 2) {
1255 rc = LDAP_CONSTRAINT_VIOLATION;
1256 if (err) *err = PP_insufficientPasswordQuality;
1257 return rc;
1258 }
1259 /*
1260 * We can't check the syntax of the password, but it's not
1261 * mandatory (according to the policy), so we return success.
1262 */
1263
1264 return LDAP_SUCCESS;
1265 }
1266 }
1267
1268 rc = LDAP_SUCCESS;
1269
1270 if (pp->pwdUseCheckModule) {
1271 #ifdef SLAPD_MODULES
1272 check_func *prog;
1273
1274 if ( !pi->pwdCheckFunc ) {
1275 Debug(LDAP_DEBUG_ANY,
1276 "check_password_quality: no CheckModule loaded\n" );
1277 ok = LDAP_OTHER;
1278 } else {
1279 struct berval *arg = NULL;
1280 if ( !BER_BVISNULL( &pp->pwdCheckModuleArg ) ) {
1281 arg = &pp->pwdCheckModuleArg;
1282 }
1283
1284 ldap_pvt_thread_mutex_lock( &chk_syntax_mutex );
1285 ok = pi->pwdCheckFunc( ptr, errmsg, e, arg );
1286 ldap_pvt_thread_mutex_unlock( &chk_syntax_mutex );
1287 if (ok != LDAP_SUCCESS) {
1288 Debug(LDAP_DEBUG_ANY,
1289 "check_password_quality: module error: (%s) %s.[%d]\n",
1290 pi->pwdCheckModule, errmsg->bv_val ? errmsg->bv_val : "", ok );
1291 }
1292 }
1293 #else
1294 Debug(LDAP_DEBUG_ANY, "check_password_quality: external modules not "
1295 "supported. pwdCheckModule ignored.\n" );
1296 #endif /* SLAPD_MODULES */
1297 }
1298
1299 if (ok != LDAP_SUCCESS) {
1300 rc = LDAP_CONSTRAINT_VIOLATION;
1301 if (err) *err = PP_insufficientPasswordQuality;
1302 }
1303
1304 return rc;
1305 }
1306
1307 static int
parse_pwdhistory(struct berval * bv,char ** oid,time_t * oldtime,struct berval * oldpw)1308 parse_pwdhistory( struct berval *bv, char **oid, time_t *oldtime, struct berval *oldpw )
1309 {
1310 char *ptr;
1311 struct berval nv, npw;
1312 ber_len_t i, j;
1313
1314 assert (bv && (bv->bv_len > 0) && (bv->bv_val) && oldtime && oldpw );
1315
1316 if ( oid ) {
1317 *oid = 0;
1318 }
1319 *oldtime = (time_t)-1;
1320 BER_BVZERO( oldpw );
1321
1322 ber_dupbv( &nv, bv );
1323
1324 /* first get the time field */
1325 for ( i = 0; (i < nv.bv_len) && (nv.bv_val[i] != '#'); i++ )
1326 ;
1327 if ( i == nv.bv_len ) {
1328 goto exit_failure; /* couldn't locate the '#' separator */
1329 }
1330 nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */
1331 ptr = nv.bv_val;
1332 *oldtime = parse_time( ptr );
1333 if (*oldtime == (time_t)-1) {
1334 goto exit_failure;
1335 }
1336
1337 /* get the OID field */
1338 for (ptr = &(nv.bv_val[i]); (i < nv.bv_len) && (nv.bv_val[i] != '#'); i++ )
1339 ;
1340 if ( i == nv.bv_len ) {
1341 goto exit_failure; /* couldn't locate the '#' separator */
1342 }
1343 nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */
1344 if ( oid ) {
1345 *oid = ber_strdup( ptr );
1346 }
1347
1348 /* get the length field */
1349 for ( ptr = &(nv.bv_val[i]); (i < nv.bv_len) && (nv.bv_val[i] != '#'); i++ )
1350 ;
1351 if ( i == nv.bv_len ) {
1352 goto exit_failure; /* couldn't locate the '#' separator */
1353 }
1354 nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */
1355 oldpw->bv_len = strtol( ptr, NULL, 10 );
1356 if (errno == ERANGE) {
1357 goto exit_failure;
1358 }
1359
1360 /* lastly, get the octets of the string */
1361 for ( j = i, ptr = &(nv.bv_val[i]); i < nv.bv_len; i++ )
1362 ;
1363 if ( i - j != oldpw->bv_len) {
1364 goto exit_failure; /* length is wrong */
1365 }
1366
1367 npw.bv_val = ptr;
1368 npw.bv_len = oldpw->bv_len;
1369 ber_dupbv( oldpw, &npw );
1370 ber_memfree( nv.bv_val );
1371
1372 return LDAP_SUCCESS;
1373
1374 exit_failure:;
1375 if ( oid && *oid ) {
1376 ber_memfree(*oid);
1377 *oid = NULL;
1378 }
1379 if ( oldpw->bv_val ) {
1380 ber_memfree( oldpw->bv_val);
1381 BER_BVZERO( oldpw );
1382 }
1383 ber_memfree( nv.bv_val );
1384
1385 return LDAP_OTHER;
1386 }
1387
1388 static void
add_to_pwd_history(pw_hist ** l,time_t t,struct berval * oldpw,struct berval * bv)1389 add_to_pwd_history( pw_hist **l, time_t t,
1390 struct berval *oldpw, struct berval *bv )
1391 {
1392 pw_hist *p, *p1, *p2;
1393
1394 if (!l) return;
1395
1396 p = ch_malloc( sizeof( pw_hist ));
1397 p->pw = *oldpw;
1398 ber_dupbv( &p->bv, bv );
1399 p->t = t;
1400 p->next = NULL;
1401
1402 if (*l == NULL) {
1403 /* degenerate case */
1404 *l = p;
1405 return;
1406 }
1407 /*
1408 * advance p1 and p2 such that p1 is the node before the
1409 * new one, and p2 is the node after it
1410 */
1411 for (p1 = NULL, p2 = *l; p2 && p2->t <= t; p1 = p2, p2=p2->next );
1412 p->next = p2;
1413 if (p1 == NULL) { *l = p; return; }
1414 p1->next = p;
1415 }
1416
1417 #ifndef MAX_PWD_HISTORY_SZ
1418 #define MAX_PWD_HISTORY_SZ 1024
1419 #endif /* MAX_PWD_HISTORY_SZ */
1420
1421 static void
make_pwd_history_value(char * timebuf,struct berval * bv,Attribute * pa)1422 make_pwd_history_value( char *timebuf, struct berval *bv, Attribute *pa )
1423 {
1424 char str[ MAX_PWD_HISTORY_SZ ];
1425 int nlen;
1426
1427 snprintf( str, MAX_PWD_HISTORY_SZ,
1428 "%s#%s#%lu#", timebuf,
1429 pa->a_desc->ad_type->sat_syntax->ssyn_oid,
1430 (unsigned long) pa->a_nvals[0].bv_len );
1431 str[MAX_PWD_HISTORY_SZ-1] = 0;
1432 nlen = strlen(str);
1433
1434 /*
1435 * We have to assume that the string is a string of octets,
1436 * not readable characters. In reality, yes, it probably is
1437 * a readable (ie, base64) string, but we can't count on that
1438 * Hence, while the first 3 fields of the password history
1439 * are definitely readable (a timestamp, an OID and an integer
1440 * length), the remaining octets of the actual password
1441 * are deemed to be binary data.
1442 */
1443 AC_MEMCPY( str + nlen, pa->a_nvals[0].bv_val, pa->a_nvals[0].bv_len );
1444 nlen += pa->a_nvals[0].bv_len;
1445 bv->bv_val = ch_malloc( nlen + 1 );
1446 AC_MEMCPY( bv->bv_val, str, nlen );
1447 bv->bv_val[nlen] = '\0';
1448 bv->bv_len = nlen;
1449 }
1450
1451 static void
free_pwd_history_list(pw_hist ** l)1452 free_pwd_history_list( pw_hist **l )
1453 {
1454 pw_hist *p;
1455
1456 if (!l) return;
1457 p = *l;
1458 while (p) {
1459 pw_hist *pp = p->next;
1460
1461 free(p->pw.bv_val);
1462 free(p->bv.bv_val);
1463 free(p);
1464 p = pp;
1465 }
1466 *l = NULL;
1467 }
1468
1469 typedef struct ppbind {
1470 slap_overinst *on;
1471 int send_ctrl;
1472 int set_restrict;
1473 LDAPControl **oldctrls;
1474 Modifications *mod;
1475 LDAPPasswordPolicyError pErr;
1476 PassPolicy pp;
1477 } ppbind;
1478
1479 static void
ctrls_cleanup(Operation * op,SlapReply * rs,LDAPControl ** oldctrls)1480 ctrls_cleanup( Operation *op, SlapReply *rs, LDAPControl **oldctrls )
1481 {
1482 int n;
1483
1484 assert( rs->sr_ctrls != NULL );
1485 assert( rs->sr_ctrls[0] != NULL );
1486
1487 for ( n = 0; rs->sr_ctrls[n]; n++ ) {
1488 if ( rs->sr_ctrls[n]->ldctl_oid == ppolicy_ctrl_oid ||
1489 rs->sr_ctrls[n]->ldctl_oid == ppolicy_pwd_expired_oid ||
1490 rs->sr_ctrls[n]->ldctl_oid == ppolicy_pwd_expiring_oid ) {
1491 op->o_tmpfree( rs->sr_ctrls[n], op->o_tmpmemctx );
1492 rs->sr_ctrls[n] = (LDAPControl *)(-1);
1493 break;
1494 }
1495 }
1496
1497 if ( rs->sr_ctrls[n] == NULL ) {
1498 /* missed? */
1499 }
1500
1501 op->o_tmpfree( rs->sr_ctrls, op->o_tmpmemctx );
1502
1503 rs->sr_ctrls = oldctrls;
1504 }
1505
1506 static int
ppolicy_ctrls_cleanup(Operation * op,SlapReply * rs)1507 ppolicy_ctrls_cleanup( Operation *op, SlapReply *rs )
1508 {
1509 ppbind *ppb = op->o_callback->sc_private;
1510 if ( ppb->send_ctrl ) {
1511 ctrls_cleanup( op, rs, ppb->oldctrls );
1512 }
1513 return SLAP_CB_CONTINUE;
1514 }
1515
1516 static int
ppolicy_bind_response(Operation * op,SlapReply * rs)1517 ppolicy_bind_response( Operation *op, SlapReply *rs )
1518 {
1519 ppbind *ppb = op->o_callback->sc_private;
1520 slap_overinst *on = ppb->on;
1521 pp_info *pi = on->on_bi.bi_private;
1522 Modifications *mod = ppb->mod, *m;
1523 int pwExpired = 0;
1524 int ngut = -1, warn = -1, fc = 0, age, rc;
1525 Attribute *a;
1526 time_t now, pwtime = (time_t)-1;
1527 struct lutil_tm now_tm;
1528 struct lutil_timet now_usec;
1529 char nowstr[ LDAP_LUTIL_GENTIME_BUFSIZE ];
1530 char nowstr_usec[ LDAP_LUTIL_GENTIME_BUFSIZE+8 ];
1531 struct berval timestamp, timestamp_usec;
1532 BackendInfo *bi = op->o_bd->bd_info;
1533 LDAPControl *ctrl = NULL;
1534 Entry *e;
1535
1536 ldap_pvt_thread_mutex_lock( &pi->pwdFailureTime_mutex );
1537 /* If we already know it's locked, just get on with it */
1538 if ( ppb->pErr != PP_noError ) {
1539 goto locked;
1540 }
1541
1542 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1543 rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
1544 op->o_bd->bd_info = bi;
1545
1546 if ( rc != LDAP_SUCCESS ) {
1547 ldap_pvt_thread_mutex_unlock( &pi->pwdFailureTime_mutex );
1548 return SLAP_CB_CONTINUE;
1549 }
1550
1551 /* ITS#7089 Skip lockout checks/modifications if password attribute missing */
1552 if ( attr_find( e->e_attrs, ppb->pp.ad ) == NULL ) {
1553 goto done;
1554 }
1555
1556 ldap_pvt_gettime(&now_tm); /* stored for later consideration */
1557 lutil_tm2time(&now_tm, &now_usec);
1558 now = now_usec.tt_sec;
1559 timestamp.bv_val = nowstr;
1560 timestamp.bv_len = sizeof(nowstr);
1561 slap_timestamp( &now, ×tamp );
1562
1563 /* Separate timestamp for pwdFailureTime with microsecond granularity */
1564 strcpy(nowstr_usec, nowstr);
1565 timestamp_usec.bv_val = nowstr_usec;
1566 timestamp_usec.bv_len = timestamp.bv_len;
1567 snprintf( timestamp_usec.bv_val + timestamp_usec.bv_len-1, sizeof(".123456Z"), ".%06dZ", now_usec.tt_nsec / 1000 );
1568 timestamp_usec.bv_len += STRLENOF(".123456");
1569
1570 if ( rs->sr_err == LDAP_INVALID_CREDENTIALS && ppb->pp.pwdMaxRecordedFailure ) {
1571 int i = 0;
1572
1573 m = ch_calloc( sizeof(Modifications), 1 );
1574 m->sml_op = LDAP_MOD_ADD;
1575 m->sml_flags = 0;
1576 m->sml_type = ad_pwdFailureTime->ad_cname;
1577 m->sml_desc = ad_pwdFailureTime;
1578 m->sml_numvals = 1;
1579 m->sml_values = ch_calloc( sizeof(struct berval), 2 );
1580 m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
1581
1582 ber_dupbv( &m->sml_values[0], ×tamp_usec );
1583 ber_dupbv( &m->sml_nvalues[0], ×tamp_usec );
1584 m->sml_next = mod;
1585 mod = m;
1586
1587 /*
1588 * Count the pwdFailureTimes - if it's
1589 * greater than the policy pwdMaxFailure,
1590 * then lock the account.
1591 */
1592 if ((a = attr_find( e->e_attrs, ad_pwdFailureTime )) != NULL) {
1593 for(i=0; a->a_nvals[i].bv_val; i++) {
1594
1595 /*
1596 * If the interval is 0, then failures
1597 * stay on the record until explicitly
1598 * reset by successful authentication.
1599 */
1600 if (ppb->pp.pwdFailureCountInterval == 0) {
1601 fc++;
1602 } else if (now <=
1603 parse_time(a->a_nvals[i].bv_val) +
1604 ppb->pp.pwdFailureCountInterval) {
1605
1606 fc++;
1607 }
1608 /*
1609 * We only count those failures
1610 * which are not due to expire.
1611 */
1612 }
1613 /* Do we have too many timestamps? If so, delete some values.
1614 * We don't bother to sort the values here. OpenLDAP keeps the
1615 * values in order by default. Fundamentally, relying on the
1616 * information here is wrong anyway; monitoring systems should
1617 * be tracking Bind failures in syslog, not here.
1618 */
1619 if (a->a_numvals >= ppb->pp.pwdMaxRecordedFailure) {
1620 int j = ppb->pp.pwdMaxRecordedFailure-1;
1621 /* If more than 2x, cheaper to perform a Replace */
1622 if (a->a_numvals >= 2 * ppb->pp.pwdMaxRecordedFailure) {
1623 struct berval v, nv;
1624
1625 /* Change the mod we constructed above */
1626 m->sml_op = LDAP_MOD_REPLACE;
1627 m->sml_numvals = ppb->pp.pwdMaxRecordedFailure;
1628 v = m->sml_values[0];
1629 nv = m->sml_nvalues[0];
1630 ch_free(m->sml_values);
1631 ch_free(m->sml_nvalues);
1632 m->sml_values = ch_calloc( sizeof(struct berval), ppb->pp.pwdMaxRecordedFailure+1 );
1633 m->sml_nvalues = ch_calloc( sizeof(struct berval), ppb->pp.pwdMaxRecordedFailure+1 );
1634 for (i=0; i<j; i++) {
1635 ber_dupbv(&m->sml_values[i], &a->a_vals[a->a_numvals-j+i]);
1636 ber_dupbv(&m->sml_nvalues[i], &a->a_nvals[a->a_numvals-j+i]);
1637 }
1638 m->sml_values[i] = v;
1639 m->sml_nvalues[i] = nv;
1640 } else {
1641 /* else just delete some */
1642 m = ch_calloc( sizeof(Modifications), 1 );
1643 m->sml_op = LDAP_MOD_DELETE;
1644 m->sml_type = ad_pwdFailureTime->ad_cname;
1645 m->sml_desc = ad_pwdFailureTime;
1646 m->sml_numvals = a->a_numvals - j;
1647 m->sml_values = ch_calloc( sizeof(struct berval), m->sml_numvals+1 );
1648 m->sml_nvalues = ch_calloc( sizeof(struct berval), m->sml_numvals+1 );
1649 for (i=0; i<m->sml_numvals; i++) {
1650 ber_dupbv(&m->sml_values[i], &a->a_vals[i]);
1651 ber_dupbv(&m->sml_nvalues[i], &a->a_nvals[i]);
1652 }
1653 m->sml_next = mod;
1654 mod = m;
1655 }
1656 }
1657 }
1658
1659 if ((ppb->pp.pwdMaxFailure > 0) &&
1660 (fc >= ppb->pp.pwdMaxFailure - 1)) {
1661
1662 /*
1663 * We subtract 1 from the failure max
1664 * because the new failure entry hasn't
1665 * made it to the entry yet.
1666 */
1667 m = ch_calloc( sizeof(Modifications), 1 );
1668 m->sml_op = LDAP_MOD_REPLACE;
1669 m->sml_flags = 0;
1670 m->sml_type = ad_pwdAccountLockedTime->ad_cname;
1671 m->sml_desc = ad_pwdAccountLockedTime;
1672 m->sml_numvals = 1;
1673 m->sml_values = ch_calloc( sizeof(struct berval), 2 );
1674 m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
1675 ber_dupbv( &m->sml_values[0], ×tamp );
1676 ber_dupbv( &m->sml_nvalues[0], ×tamp );
1677 m->sml_next = mod;
1678 mod = m;
1679 } else if ( ppb->pp.pwdMinDelay ) {
1680 int waittime = ppb->pp.pwdMinDelay << fc;
1681 time_t wait_end;
1682 struct berval lockout_stamp;
1683
1684 if ( waittime > ppb->pp.pwdMaxDelay ) {
1685 waittime = ppb->pp.pwdMaxDelay;
1686 }
1687 wait_end = now + waittime;
1688
1689 slap_timestamp( &wait_end, &lockout_stamp );
1690
1691 m = ch_calloc( sizeof(Modifications), 1 );
1692 m->sml_op = LDAP_MOD_REPLACE;
1693 m->sml_flags = 0;
1694 m->sml_type = ad_pwdAccountTmpLockoutEnd->ad_cname;
1695 m->sml_desc = ad_pwdAccountTmpLockoutEnd;
1696 m->sml_numvals = 1;
1697 m->sml_values = ch_calloc( sizeof(struct berval), 2 );
1698 m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
1699 ber_dupbv( &m->sml_values[0], &lockout_stamp );
1700 ber_dupbv( &m->sml_nvalues[0], &lockout_stamp );
1701 m->sml_next = mod;
1702 mod = m;
1703 }
1704 } else if ( rs->sr_err == LDAP_SUCCESS ) {
1705 if ((a = attr_find( e->e_attrs, ad_pwdChangedTime )) != NULL)
1706 pwtime = parse_time( a->a_nvals[0].bv_val );
1707
1708 /* delete all pwdFailureTimes */
1709 if ( attr_find( e->e_attrs, ad_pwdFailureTime )) {
1710 m = ch_calloc( sizeof(Modifications), 1 );
1711 m->sml_op = LDAP_MOD_DELETE;
1712 m->sml_flags = 0;
1713 m->sml_type = ad_pwdFailureTime->ad_cname;
1714 m->sml_desc = ad_pwdFailureTime;
1715 m->sml_next = mod;
1716 mod = m;
1717 }
1718
1719 /*
1720 * check to see if the password must be changed
1721 */
1722 if ( ppb->pp.pwdMustChange &&
1723 (a = attr_find( e->e_attrs, ad_pwdReset )) &&
1724 bvmatch( &a->a_nvals[0], &slap_true_bv ) )
1725 {
1726 /*
1727 * need to inject client controls here to give
1728 * more information. For the moment, we ensure
1729 * that we are disallowed from doing anything
1730 * other than change password.
1731 */
1732 if ( ppb->set_restrict ) {
1733 ber_dupbv( &pwcons[op->o_conn->c_conn_idx].dn,
1734 &op->o_conn->c_ndn );
1735 }
1736
1737 ppb->pErr = PP_changeAfterReset;
1738
1739 } else {
1740 /*
1741 * the password does not need to be changed, so
1742 * we now check whether the password has expired.
1743 *
1744 * We can skip this bit if passwords don't age in
1745 * the policy. Also, if there was no pwdChangedTime
1746 * attribute in the entry, the password never expires.
1747 */
1748 if (ppb->pp.pwdMaxAge == 0) goto grace;
1749
1750 if (pwtime != (time_t)-1) {
1751 /*
1752 * Check: was the last change time of
1753 * the password older than the maximum age
1754 * allowed. (Ignore case 2 from I-D, it's just silly.)
1755 */
1756 if (now - pwtime > ppb->pp.pwdMaxAge ) pwExpired = 1;
1757 }
1758 }
1759
1760 grace:
1761 if (!pwExpired) goto check_expiring_password;
1762
1763 if ( ppb->pp.pwdGraceExpiry &&
1764 now - pwtime > ppb->pp.pwdMaxAge + ppb->pp.pwdGraceExpiry ) {
1765 /* Grace logins have expired now */
1766 ngut = 0;
1767 } else if ((a = attr_find( e->e_attrs, ad_pwdGraceUseTime )) == NULL) {
1768 ngut = ppb->pp.pwdGraceAuthNLimit;
1769 } else {
1770 for(ngut=0; a->a_nvals[ngut].bv_val; ngut++);
1771 ngut = ppb->pp.pwdGraceAuthNLimit - ngut;
1772 }
1773
1774 /*
1775 * ngut is the number of remaining grace logins
1776 */
1777 Debug( LDAP_DEBUG_ANY,
1778 "ppolicy_bind: Entry %s has an expired password: %d grace logins\n",
1779 e->e_name.bv_val, ngut );
1780
1781 ngut--;
1782
1783 if (ngut < 0) {
1784 ppb->pErr = PP_passwordExpired;
1785 rs->sr_err = LDAP_INVALID_CREDENTIALS;
1786 goto done;
1787 }
1788
1789 /*
1790 * Add a grace user time to the entry
1791 */
1792 m = ch_calloc( sizeof(Modifications), 1 );
1793 m->sml_op = LDAP_MOD_ADD;
1794 m->sml_flags = 0;
1795 m->sml_type = ad_pwdGraceUseTime->ad_cname;
1796 m->sml_desc = ad_pwdGraceUseTime;
1797 m->sml_numvals = 1;
1798 m->sml_values = ch_calloc( sizeof(struct berval), 2 );
1799 m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
1800 ber_dupbv( &m->sml_values[0], ×tamp_usec );
1801 ber_dupbv( &m->sml_nvalues[0], ×tamp_usec );
1802 m->sml_next = mod;
1803 mod = m;
1804
1805 check_expiring_password:
1806 /*
1807 * Now we need to check to see
1808 * if it is about to expire, and if so, should the user
1809 * be warned about it in the password policy control.
1810 *
1811 * If the password has expired, and we're in the grace period, then
1812 * we don't need to do this bit. Similarly, if we don't have password
1813 * aging, then there's no need to do this bit either.
1814 *
1815 * If pwdtime is -1 there is no password Change Time attribute on the
1816 * entry so we skip the expiry check.
1817 *
1818 */
1819 if ((ppb->pp.pwdMaxAge < 1) || (pwExpired) || (ppb->pp.pwdExpireWarning < 1) ||
1820 (pwtime == -1))
1821 goto done;
1822
1823 age = (int)(now - pwtime);
1824
1825 /*
1826 * We know that there is a password Change Time attribute - if
1827 * there wasn't, then the pwdExpired value would be true, unless
1828 * there is no password aging - and if there is no password aging,
1829 * then this section isn't called anyway - you can't have an
1830 * expiring password if there's no limit to expire.
1831 */
1832 if (ppb->pp.pwdMaxAge - age < ppb->pp.pwdExpireWarning ) {
1833 /*
1834 * Set the warning value.
1835 */
1836 warn = ppb->pp.pwdMaxAge - age; /* seconds left until expiry */
1837 if (warn < 0) warn = 0; /* something weird here - why is pwExpired not set? */
1838
1839 Debug( LDAP_DEBUG_TRACE,
1840 "ppolicy_bind: Setting warning for password expiry for %s = %d seconds\n",
1841 op->o_req_dn.bv_val, warn );
1842 }
1843 }
1844
1845 done:
1846 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1847 be_entry_release_r( op, e );
1848
1849 locked:
1850 if ( mod && !pi->disable_write ) {
1851 Operation op2 = *op;
1852 SlapReply r2 = { REP_RESULT };
1853 slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
1854 LDAPControl c, *ca[2];
1855
1856 op2.o_tag = LDAP_REQ_MODIFY;
1857 op2.o_callback = &cb;
1858 op2.orm_modlist = mod;
1859 op2.orm_no_opattrs = 0;
1860 op2.o_dn = op->o_bd->be_rootdn;
1861 op2.o_ndn = op->o_bd->be_rootndn;
1862
1863 /* If this server is a shadow and forward_updates is true,
1864 * use the frontend to perform this modify. That will trigger
1865 * the update referral, which can then be forwarded by the
1866 * chain overlay. Obviously the updateref and chain overlay
1867 * must be configured appropriately for this to be useful.
1868 */
1869 if ( SLAP_SHADOW( op->o_bd ) && pi->forward_updates ) {
1870 op2.o_bd = frontendDB;
1871
1872 /* Must use Relax control since these are no-user-mod */
1873 op2.o_relax = SLAP_CONTROL_CRITICAL;
1874 op2.o_ctrls = ca;
1875 ca[0] = &c;
1876 ca[1] = NULL;
1877 BER_BVZERO( &c.ldctl_value );
1878 c.ldctl_iscritical = 1;
1879 c.ldctl_oid = LDAP_CONTROL_RELAX;
1880 } else {
1881 /* If not forwarding, don't update opattrs and don't replicate */
1882 if ( SLAP_SINGLE_SHADOW( op->o_bd )) {
1883 op2.orm_no_opattrs = 1;
1884 op2.o_dont_replicate = 1;
1885 }
1886 op2.o_bd->bd_info = (BackendInfo *)on->on_info;
1887 }
1888 rc = op2.o_bd->be_modify( &op2, &r2 );
1889 if ( rc != LDAP_SUCCESS ) {
1890 Debug( LDAP_DEBUG_ANY, "%s ppolicy_bind_response: "
1891 "ppolicy state change failed with rc=%d text=%s\n",
1892 op->o_log_prefix, rc, r2.sr_text );
1893 }
1894 }
1895 if ( mod ) {
1896 slap_mods_free( mod, 1 );
1897 }
1898
1899 if ( ppb->send_ctrl ) {
1900
1901 /* Do we really want to tell that the account is locked? */
1902 if ( ppb->pErr == PP_accountLocked && !pi->use_lockout ) {
1903 ppb->pErr = PP_noError;
1904 }
1905 ctrl = create_passcontrol( op, warn, ngut, ppb->pErr );
1906 } else if ( pi->send_netscape_controls ) {
1907 if ( ppb->pErr != PP_noError || pwExpired ) {
1908 ctrl = create_passexpiry( op, 1, 0 );
1909 } else if ( warn > 0 ) {
1910 ctrl = create_passexpiry( op, 0, warn );
1911 }
1912 }
1913 if ( ctrl ) {
1914 ppb->oldctrls = add_passcontrol( op, rs, ctrl );
1915 op->o_callback->sc_cleanup = ppolicy_ctrls_cleanup;
1916 }
1917 op->o_bd->bd_info = bi;
1918 ldap_pvt_thread_mutex_unlock( &pi->pwdFailureTime_mutex );
1919 return SLAP_CB_CONTINUE;
1920 }
1921
1922 static int
ppolicy_bind(Operation * op,SlapReply * rs)1923 ppolicy_bind( Operation *op, SlapReply *rs )
1924 {
1925 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1926
1927 /* Reset lockout status on all Bind requests */
1928 if ( !BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn )) {
1929 ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val );
1930 BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn );
1931 }
1932
1933 /* Root bypasses policy */
1934 if ( !be_isroot_dn( op->o_bd, &op->o_req_ndn )) {
1935 Entry *e;
1936 int rc;
1937 ppbind *ppb;
1938 slap_callback *cb;
1939
1940 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1941 rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
1942
1943 if ( rc != LDAP_SUCCESS ) {
1944 return SLAP_CB_CONTINUE;
1945 }
1946
1947 cb = op->o_tmpcalloc( sizeof(ppbind)+sizeof(slap_callback),
1948 1, op->o_tmpmemctx );
1949 ppb = (ppbind *)(cb+1);
1950 ppb->on = on;
1951 ppb->pErr = PP_noError;
1952 ppb->set_restrict = 1;
1953
1954 /* Setup a callback so we can munge the result */
1955
1956 cb->sc_response = ppolicy_bind_response;
1957 cb->sc_private = ppb;
1958 overlay_callback_after_backover( op, cb, 1 );
1959
1960 /* Did we receive a password policy request control? */
1961 if ( op->o_ctrlflag[ppolicy_cid] ) {
1962 ppb->send_ctrl = 1;
1963 }
1964
1965 op->o_bd->bd_info = (BackendInfo *)on;
1966
1967 if ( ppolicy_get( op, e, &ppb->pp ) == LDAP_SUCCESS ) {
1968 rc = account_locked( op, e, &ppb->pp, &ppb->mod );
1969 }
1970
1971 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1972 be_entry_release_r( op, e );
1973
1974 if ( rc ) {
1975 ppb->pErr = PP_accountLocked;
1976 send_ldap_error( op, rs, LDAP_INVALID_CREDENTIALS, NULL );
1977 return rs->sr_err;
1978 }
1979
1980 }
1981
1982 return SLAP_CB_CONTINUE;
1983 }
1984
1985 /* Reset the restricted info for the next session on this connection */
1986 static int
ppolicy_connection_destroy(BackendDB * bd,Connection * conn)1987 ppolicy_connection_destroy( BackendDB *bd, Connection *conn )
1988 {
1989 if ( pwcons && !BER_BVISEMPTY( &pwcons[conn->c_conn_idx].dn )) {
1990 ch_free( pwcons[conn->c_conn_idx].dn.bv_val );
1991 BER_BVZERO( &pwcons[conn->c_conn_idx].dn );
1992 }
1993 return SLAP_CB_CONTINUE;
1994 }
1995
1996 /* Check if this connection is restricted */
1997 static int
ppolicy_restrict(Operation * op,SlapReply * rs)1998 ppolicy_restrict(
1999 Operation *op,
2000 SlapReply *rs )
2001 {
2002 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
2003 int send_ctrl = 0;
2004
2005 /* Did we receive a password policy request control? */
2006 if ( op->o_ctrlflag[ppolicy_cid] ) {
2007 send_ctrl = 1;
2008 }
2009
2010 if ( op->o_conn && !BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn )) {
2011 LDAPControl **oldctrls;
2012 /* if the current authcDN doesn't match the one we recorded,
2013 * then an intervening Bind has succeeded and the restriction
2014 * no longer applies. (ITS#4516)
2015 */
2016 if ( !dn_match( &op->o_conn->c_ndn,
2017 &pwcons[op->o_conn->c_conn_idx].dn )) {
2018 ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val );
2019 BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn );
2020 return SLAP_CB_CONTINUE;
2021 }
2022
2023 Debug( LDAP_DEBUG_TRACE,
2024 "connection restricted to password changing only\n" );
2025 if ( send_ctrl ) {
2026 LDAPControl *ctrl = NULL;
2027 ctrl = create_passcontrol( op, -1, -1, PP_changeAfterReset );
2028 oldctrls = add_passcontrol( op, rs, ctrl );
2029 }
2030 op->o_bd->bd_info = (BackendInfo *)on->on_info;
2031 send_ldap_error( op, rs, LDAP_INSUFFICIENT_ACCESS,
2032 "Operations are restricted to bind/unbind/abandon/StartTLS/modify password" );
2033 if ( send_ctrl ) {
2034 ctrls_cleanup( op, rs, oldctrls );
2035 }
2036 return rs->sr_err;
2037 }
2038
2039 return SLAP_CB_CONTINUE;
2040 }
2041
2042 static int
ppolicy_account_usability_entry_cb(Operation * op,SlapReply * rs)2043 ppolicy_account_usability_entry_cb( Operation *op, SlapReply *rs )
2044 {
2045 slap_overinst *on = op->o_callback->sc_private;
2046 BackendInfo *bi = op->o_bd->bd_info;
2047 LDAPControl *ctrl = NULL;
2048 PassPolicy pp;
2049 Attribute *a;
2050 Entry *e = NULL;
2051 time_t pwtime = 0, seconds_until_expiry = -1, now = op->o_time;
2052 int isExpired = 0, grace = -1;
2053
2054 if ( rs->sr_type != REP_SEARCH ) {
2055 return SLAP_CB_CONTINUE;
2056 }
2057
2058 if ( be_entry_get_rw( op, &rs->sr_entry->e_nname, NULL, NULL, 0, &e ) != LDAP_SUCCESS ) {
2059 goto done;
2060 }
2061
2062 op->o_bd->bd_info = (BackendInfo *)on;
2063
2064 if ( ppolicy_get( op, e, &pp ) != LDAP_SUCCESS ) {
2065 /* TODO: If there is no policy, should we check if */
2066 goto done;
2067 }
2068
2069 if ( !access_allowed( op, e, pp.ad, NULL, ACL_COMPARE, NULL ) ) {
2070 goto done;
2071 }
2072
2073 if ( attr_find( e->e_attrs, pp.ad ) == NULL ) {
2074 goto done;
2075 }
2076
2077 if ((a = attr_find( e->e_attrs, ad_pwdChangedTime )) != NULL) {
2078 pwtime = parse_time( a->a_nvals[0].bv_val );
2079 }
2080
2081 if ( pp.pwdMaxAge && pwtime ) {
2082 seconds_until_expiry = pwtime + pp.pwdMaxAge - now;
2083 if ( seconds_until_expiry <= 0 ) isExpired = 1;
2084 if ( pp.pwdGraceAuthNLimit ) {
2085 if ( !pp.pwdGraceExpiry || seconds_until_expiry + pp.pwdGraceExpiry > 0 ) {
2086 grace = pp.pwdGraceAuthNLimit;
2087 if ( attr_find( e->e_attrs, ad_pwdGraceUseTime ) ) {
2088 grace -= a->a_numvals;
2089 }
2090 }
2091 }
2092 }
2093 if ( !isExpired && pp.pwdMaxIdle && (a = attr_find( e->e_attrs, ad_pwdLastSuccess )) ) {
2094 time_t lastbindtime = pwtime;
2095
2096 if ( (a = attr_find( e->e_attrs, ad_pwdLastSuccess )) != NULL ) {
2097 lastbindtime = parse_time( a->a_nvals[0].bv_val );
2098 }
2099
2100 if ( lastbindtime ) {
2101 int remaining_idle = lastbindtime + pp.pwdMaxIdle - now;
2102 if ( remaining_idle <= 0 ) {
2103 isExpired = 1;
2104 } else if ( seconds_until_expiry == -1 || remaining_idle < seconds_until_expiry ) {
2105 seconds_until_expiry = remaining_idle;
2106 }
2107 }
2108 }
2109
2110 if ( isExpired || account_locked( op, e, &pp, NULL ) ) {
2111 LDAPAccountUsabilityMoreInfo more_info = { 0, 0, 0, -1, -1 };
2112 time_t then, lockoutEnd = 0;
2113
2114 if ( isExpired ) more_info.remaining_grace = grace;
2115
2116 if ( (a = attr_find( e->e_attrs, ad_pwdAccountLockedTime )) != NULL ) {
2117 then = parse_time( a->a_vals[0].bv_val );
2118 if ( then == 0 )
2119 lockoutEnd = -1;
2120
2121 /* Still in the future? not yet in effect */
2122 if ( now < then )
2123 then = 0;
2124
2125 if ( !pp.pwdLockoutDuration )
2126 lockoutEnd = -1;
2127
2128 if ( now < then + pp.pwdLockoutDuration )
2129 lockoutEnd = then + pp.pwdLockoutDuration;
2130 }
2131
2132 if ( (a = attr_find( e->e_attrs, ad_pwdAccountTmpLockoutEnd )) != NULL ) {
2133 then = parse_time( a->a_vals[0].bv_val );
2134 if ( lockoutEnd != -1 && then > lockoutEnd )
2135 lockoutEnd = then;
2136 }
2137
2138 if ( lockoutEnd > now ) {
2139 more_info.inactive = 1;
2140 more_info.seconds_before_unlock = lockoutEnd - now;
2141 }
2142
2143 if ( pp.pwdMustChange &&
2144 (a = attr_find( e->e_attrs, ad_pwdReset )) &&
2145 bvmatch( &a->a_nvals[0], &slap_true_bv ) )
2146 {
2147 more_info.reset = 1;
2148 }
2149
2150 add_account_control( op, rs, 0, -1, &more_info );
2151 } else {
2152 add_account_control( op, rs, 1, seconds_until_expiry, NULL );
2153 }
2154
2155 done:
2156 op->o_bd->bd_info = bi;
2157 if ( e ) {
2158 be_entry_release_r( op, e );
2159 }
2160 return SLAP_CB_CONTINUE;
2161 }
2162
2163 static int
ppolicy_search(Operation * op,SlapReply * rs)2164 ppolicy_search(
2165 Operation *op,
2166 SlapReply *rs )
2167 {
2168 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
2169 int rc = ppolicy_restrict( op, rs );
2170
2171 if ( rc != SLAP_CB_CONTINUE ) {
2172 return rc;
2173 }
2174
2175 if ( op->o_ctrlflag[account_usability_cid] ) {
2176 slap_callback *cb;
2177
2178 cb = op->o_tmpcalloc( sizeof(slap_callback), 1, op->o_tmpmemctx );
2179
2180 cb->sc_response = ppolicy_account_usability_entry_cb;
2181 cb->sc_private = on;
2182 overlay_callback_after_backover( op, cb, 1 );
2183 }
2184
2185 return SLAP_CB_CONTINUE;
2186 }
2187
2188 static int
ppolicy_compare_response(Operation * op,SlapReply * rs)2189 ppolicy_compare_response(
2190 Operation *op,
2191 SlapReply *rs )
2192 {
2193 /* map compare responses to bind responses */
2194 if ( rs->sr_err == LDAP_COMPARE_TRUE )
2195 rs->sr_err = LDAP_SUCCESS;
2196 else if ( rs->sr_err == LDAP_COMPARE_FALSE )
2197 rs->sr_err = LDAP_INVALID_CREDENTIALS;
2198
2199 ppolicy_bind_response( op, rs );
2200
2201 /* map back to compare */
2202 if ( rs->sr_err == LDAP_SUCCESS )
2203 rs->sr_err = LDAP_COMPARE_TRUE;
2204 else if ( rs->sr_err == LDAP_INVALID_CREDENTIALS )
2205 rs->sr_err = LDAP_COMPARE_FALSE;
2206
2207 return SLAP_CB_CONTINUE;
2208 }
2209
2210 static int
ppolicy_compare(Operation * op,SlapReply * rs)2211 ppolicy_compare(
2212 Operation *op,
2213 SlapReply *rs )
2214 {
2215 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
2216
2217 if ( ppolicy_restrict( op, rs ) != SLAP_CB_CONTINUE )
2218 return rs->sr_err;
2219
2220 /* Did we receive a password policy request control?
2221 * Are we testing the userPassword?
2222 */
2223 if ( op->o_ctrlflag[ppolicy_cid] &&
2224 op->orc_ava->aa_desc == slap_schema.si_ad_userPassword ) {
2225 Entry *e;
2226 int rc;
2227 ppbind *ppb;
2228 slap_callback *cb;
2229
2230 op->o_bd->bd_info = (BackendInfo *)on->on_info;
2231 rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
2232
2233 if ( rc != LDAP_SUCCESS ) {
2234 return SLAP_CB_CONTINUE;
2235 }
2236
2237 cb = op->o_tmpcalloc( sizeof(ppbind)+sizeof(slap_callback),
2238 1, op->o_tmpmemctx );
2239 ppb = (ppbind *)(cb+1);
2240 ppb->on = on;
2241 ppb->pErr = PP_noError;
2242 ppb->send_ctrl = 1;
2243 /* failures here don't lockout the connection */
2244 ppb->set_restrict = 0;
2245
2246 /* Setup a callback so we can munge the result */
2247
2248 cb->sc_response = ppolicy_compare_response;
2249 cb->sc_private = ppb;
2250 overlay_callback_after_backover( op, cb, 1 );
2251
2252 op->o_bd->bd_info = (BackendInfo *)on;
2253
2254 if ( ppolicy_get( op, e, &ppb->pp ) == LDAP_SUCCESS ) {
2255 rc = account_locked( op, e, &ppb->pp, &ppb->mod );
2256 }
2257
2258 op->o_bd->bd_info = (BackendInfo *)on->on_info;
2259 be_entry_release_r( op, e );
2260
2261 if ( rc ) {
2262 ppb->pErr = PP_accountLocked;
2263 send_ldap_error( op, rs, LDAP_COMPARE_FALSE, NULL );
2264 return rs->sr_err;
2265 }
2266 }
2267 return SLAP_CB_CONTINUE;
2268 }
2269
2270 static int
ppolicy_add(Operation * op,SlapReply * rs)2271 ppolicy_add(
2272 Operation *op,
2273 SlapReply *rs )
2274 {
2275 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
2276 pp_info *pi = on->on_bi.bi_private;
2277 PassPolicy pp;
2278 Attribute *pa;
2279 const char *txt;
2280
2281 if ( ppolicy_restrict( op, rs ) != SLAP_CB_CONTINUE )
2282 return rs->sr_err;
2283
2284 /* If this is a replica, assume the provider checked everything */
2285 if ( SLAPD_SYNC_IS_SYNCCONN( op->o_connid ) )
2286 return SLAP_CB_CONTINUE;
2287
2288 /* Check for password in entry */
2289 if ((pa = attr_find( op->oq_add.rs_e->e_attrs,
2290 slap_schema.si_ad_userPassword )))
2291 {
2292 assert( pa->a_vals != NULL );
2293 assert( !BER_BVISNULL( &pa->a_vals[ 0 ] ) );
2294
2295 if ( !BER_BVISNULL( &pa->a_vals[ 1 ] ) ) {
2296 send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION, "Password policy only allows one password value" );
2297 return rs->sr_err;
2298 }
2299
2300 ppolicy_get( op, op->ora_e, &pp );
2301
2302 /*
2303 * new entry contains a password - if we're not the root user
2304 * then we need to check that the password fits in with the
2305 * security policy for the new entry.
2306 */
2307
2308 if (pp.pwdCheckQuality > 0 && !be_isroot( op )) {
2309 struct berval *bv = &(pa->a_vals[0]);
2310 int rc, send_ctrl = 0;
2311 LDAPPasswordPolicyError pErr = PP_noError;
2312 char errbuf[ERRBUFSIZ];
2313 struct berval errmsg = BER_BVC( errbuf );
2314
2315 /* Did we receive a password policy request control? */
2316 if ( op->o_ctrlflag[ppolicy_cid] ) {
2317 send_ctrl = 1;
2318 }
2319 rc = check_password_quality( bv, pi, &pp, &pErr, op->ora_e, &errmsg );
2320 if (rc != LDAP_SUCCESS) {
2321 char *txt = errmsg.bv_val;
2322 LDAPControl **oldctrls = NULL;
2323 op->o_bd->bd_info = (BackendInfo *)on->on_info;
2324 if ( send_ctrl ) {
2325 LDAPControl *ctrl = NULL;
2326 ctrl = create_passcontrol( op, -1, -1, pErr );
2327 oldctrls = add_passcontrol( op, rs, ctrl );
2328 }
2329 send_ldap_error( op, rs, rc, txt && txt[0] ? txt : "Password fails quality checking policy" );
2330 if ( txt != errbuf ) {
2331 free( txt );
2332 }
2333 if ( send_ctrl ) {
2334 ctrls_cleanup( op, rs, oldctrls );
2335 }
2336 return rs->sr_err;
2337 }
2338 }
2339 /*
2340 * A controversial bit. We hash cleartext
2341 * passwords provided via add and modify operations
2342 * You're not really supposed to do this, since
2343 * the X.500 model says "store attributes" as they
2344 * get provided. By default, this is what we do
2345 *
2346 * But if the hash_passwords flag is set, we hash
2347 * any cleartext password attribute values via the
2348 * default password hashing scheme.
2349 */
2350 if ((pi->hash_passwords) &&
2351 (password_scheme( &(pa->a_vals[0]), NULL ) != LDAP_SUCCESS)) {
2352 struct berval hpw;
2353
2354 slap_passwd_hash( &(pa->a_vals[0]), &hpw, &txt );
2355 if (hpw.bv_val == NULL) {
2356 /*
2357 * hashing didn't work. Emit an error.
2358 */
2359 rs->sr_err = LDAP_OTHER;
2360 rs->sr_text = txt;
2361 send_ldap_error( op, rs, LDAP_OTHER, "Password hashing failed" );
2362 return rs->sr_err;
2363 }
2364
2365 memset( pa->a_vals[0].bv_val, 0, pa->a_vals[0].bv_len);
2366 ber_memfree( pa->a_vals[0].bv_val );
2367 pa->a_vals[0].bv_val = hpw.bv_val;
2368 pa->a_vals[0].bv_len = hpw.bv_len;
2369 }
2370
2371 /* If password aging is in effect, set the pwdChangedTime */
2372 if ( pp.pwdMaxAge || pp.pwdMinAge ) {
2373 struct berval timestamp;
2374 char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
2375 time_t now = slap_get_time();
2376
2377 timestamp.bv_val = timebuf;
2378 timestamp.bv_len = sizeof(timebuf);
2379 slap_timestamp( &now, ×tamp );
2380
2381 attr_merge_one( op->ora_e, ad_pwdChangedTime, ×tamp, ×tamp );
2382 }
2383 }
2384 return SLAP_CB_CONTINUE;
2385 }
2386
2387 static int
ppolicy_mod_cb(Operation * op,SlapReply * rs)2388 ppolicy_mod_cb( Operation *op, SlapReply *rs )
2389 {
2390 slap_callback *sc = op->o_callback;
2391 op->o_callback = sc->sc_next;
2392 if ( rs->sr_err == LDAP_SUCCESS ) {
2393 ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val );
2394 BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn );
2395 }
2396 op->o_tmpfree( sc, op->o_tmpmemctx );
2397 return SLAP_CB_CONTINUE;
2398 }
2399
2400 static int
ppolicy_text_cleanup(Operation * op,SlapReply * rs)2401 ppolicy_text_cleanup( Operation *op, SlapReply *rs )
2402 {
2403 slap_callback *sc = op->o_callback;
2404
2405 if ( rs->sr_text == sc->sc_private ) {
2406 rs->sr_text = NULL;
2407 }
2408 free( sc->sc_private );
2409
2410 op->o_callback = sc->sc_next;
2411 op->o_tmpfree( sc, op->o_tmpmemctx );
2412 return SLAP_CB_CONTINUE;
2413 }
2414
2415 static int
ppolicy_modify(Operation * op,SlapReply * rs)2416 ppolicy_modify( Operation *op, SlapReply *rs )
2417 {
2418 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
2419 pp_info *pi = on->on_bi.bi_private;
2420 int i, rc, mod_pw_only, pwmod = 0, pwmop = -1, deladd,
2421 hsize = 0, hskip;
2422 PassPolicy pp;
2423 Modifications *mods = NULL, *modtail = NULL,
2424 *ml, *delmod, *addmod;
2425 Attribute *pa, *ha, at;
2426 const char *txt;
2427 char errbuf[ERRBUFSIZ];
2428 pw_hist *tl = NULL, *p;
2429 int zapReset, send_ctrl = 0, free_txt = 0;
2430 Entry *e;
2431 struct berval newpw = BER_BVNULL, oldpw = BER_BVNULL,
2432 *bv, cr[2];
2433 LDAPPasswordPolicyError pErr = PP_noError;
2434 LDAPControl *ctrl = NULL;
2435 LDAPControl **oldctrls = NULL;
2436 int is_pwdexop = 0, is_pwdadmin = 0;
2437 int got_del_grace = 0, got_del_lock = 0, got_pw = 0, got_del_fail = 0,
2438 got_del_success = 0;
2439 int got_changed = 0, got_history = 0;
2440 int have_policy = 0;
2441
2442 op->o_bd->bd_info = (BackendInfo *)on->on_info;
2443 rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
2444 op->o_bd->bd_info = (BackendInfo *)on;
2445
2446 if ( rc != LDAP_SUCCESS ) return SLAP_CB_CONTINUE;
2447 if ( pi->disable_write ) return SLAP_CB_CONTINUE;
2448
2449 /* If this is a replica, we may need to tweak some of the
2450 * provider's modifications. Otherwise, just pass it through.
2451 */
2452 if ( SLAPD_SYNC_IS_SYNCCONN( op->o_connid ) ) {
2453 Modifications **prev;
2454 Attribute *a_grace, *a_lock, *a_fail, *a_success;
2455
2456 a_grace = attr_find( e->e_attrs, ad_pwdGraceUseTime );
2457 a_lock = attr_find( e->e_attrs, ad_pwdAccountLockedTime );
2458 a_fail = attr_find( e->e_attrs, ad_pwdFailureTime );
2459 a_success = attr_find( e->e_attrs, ad_pwdLastSuccess );
2460
2461 for( prev = &op->orm_modlist, ml = *prev; ml; ml = *prev ) {
2462
2463 if ( ml->sml_desc == slap_schema.si_ad_userPassword )
2464 got_pw = 1;
2465
2466 /* If we're deleting an attr that didn't exist,
2467 * drop this delete op
2468 */
2469 if ( ml->sml_op == LDAP_MOD_DELETE ||
2470 ml->sml_op == SLAP_MOD_SOFTDEL ) {
2471 int drop = 0;
2472
2473 if ( ml->sml_desc == ad_pwdGraceUseTime ) {
2474 if ( !a_grace || got_del_grace ) {
2475 drop = ml->sml_op == LDAP_MOD_DELETE;
2476 } else {
2477 got_del_grace = 1;
2478 }
2479 } else
2480 if ( ml->sml_desc == ad_pwdAccountLockedTime ) {
2481 if ( !a_lock || got_del_lock ) {
2482 drop = ml->sml_op == LDAP_MOD_DELETE;
2483 } else {
2484 got_del_lock = 1;
2485 }
2486 } else
2487 if ( ml->sml_desc == ad_pwdFailureTime ) {
2488 if ( !a_fail || got_del_fail ) {
2489 drop = ml->sml_op == LDAP_MOD_DELETE;
2490 } else {
2491 got_del_fail = 1;
2492 }
2493 }
2494 if ( ml->sml_desc == ad_pwdLastSuccess ) {
2495 if ( !a_success || got_del_success ) {
2496 drop = ml->sml_op == LDAP_MOD_DELETE;
2497 } else {
2498 got_del_success = 1;
2499 }
2500 }
2501 if ( drop ) {
2502 *prev = ml->sml_next;
2503 ml->sml_next = NULL;
2504 slap_mods_free( ml, 1 );
2505 continue;
2506 }
2507 }
2508 prev = &ml->sml_next;
2509 }
2510
2511 /* If we're resetting the password, make sure grace, accountlock,
2512 * success, and failure also get removed.
2513 */
2514 if ( got_pw ) {
2515 if ( a_grace && !got_del_grace ) {
2516 ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
2517 ml->sml_op = LDAP_MOD_DELETE;
2518 ml->sml_flags = SLAP_MOD_INTERNAL;
2519 ml->sml_type.bv_val = NULL;
2520 ml->sml_desc = ad_pwdGraceUseTime;
2521 ml->sml_numvals = 0;
2522 ml->sml_values = NULL;
2523 ml->sml_nvalues = NULL;
2524 ml->sml_next = NULL;
2525 *prev = ml;
2526 prev = &ml->sml_next;
2527 }
2528 if ( a_lock && !got_del_lock ) {
2529 ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
2530 ml->sml_op = LDAP_MOD_DELETE;
2531 ml->sml_flags = SLAP_MOD_INTERNAL;
2532 ml->sml_type.bv_val = NULL;
2533 ml->sml_desc = ad_pwdAccountLockedTime;
2534 ml->sml_numvals = 0;
2535 ml->sml_values = NULL;
2536 ml->sml_nvalues = NULL;
2537 ml->sml_next = NULL;
2538 *prev = ml;
2539 }
2540 if ( a_fail && !got_del_fail ) {
2541 ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
2542 ml->sml_op = LDAP_MOD_DELETE;
2543 ml->sml_flags = SLAP_MOD_INTERNAL;
2544 ml->sml_type.bv_val = NULL;
2545 ml->sml_desc = ad_pwdFailureTime;
2546 ml->sml_numvals = 0;
2547 ml->sml_values = NULL;
2548 ml->sml_nvalues = NULL;
2549 ml->sml_next = NULL;
2550 *prev = ml;
2551 }
2552 if ( a_success && !got_del_success ) {
2553 ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
2554 ml->sml_op = LDAP_MOD_DELETE;
2555 ml->sml_flags = SLAP_MOD_INTERNAL;
2556 ml->sml_type.bv_val = NULL;
2557 ml->sml_desc = ad_pwdLastSuccess;
2558 ml->sml_numvals = 0;
2559 ml->sml_values = NULL;
2560 ml->sml_nvalues = NULL;
2561 ml->sml_next = NULL;
2562 *prev = ml;
2563 }
2564 }
2565 op->o_bd->bd_info = (BackendInfo *)on->on_info;
2566 be_entry_release_r( op, e );
2567 return SLAP_CB_CONTINUE;
2568 }
2569
2570 /* Did we receive a password policy request control? */
2571 if ( op->o_ctrlflag[ppolicy_cid] ) {
2572 send_ctrl = 1;
2573 }
2574
2575 /* See if this is a pwdModify exop. If so, we can
2576 * access the plaintext passwords from that request.
2577 */
2578 {
2579 slap_callback *sc;
2580
2581 for ( sc = op->o_callback; sc; sc=sc->sc_next ) {
2582 if ( sc->sc_response == slap_null_cb &&
2583 sc->sc_private ) {
2584 req_pwdexop_s *qpw = sc->sc_private;
2585 newpw = qpw->rs_new;
2586 oldpw = qpw->rs_old;
2587 is_pwdexop = 1;
2588 break;
2589 }
2590 }
2591 }
2592
2593 /* ppolicy_hash_cleartext depends on pwmod being determined first */
2594 if ( ppolicy_get( op, e, &pp ) == LDAP_SUCCESS ) {
2595 have_policy = 1;
2596 }
2597
2598 if ( access_allowed( op, e, pp.ad, NULL, ACL_MANAGE, NULL ) ) {
2599 is_pwdadmin = 1;
2600 }
2601
2602 for ( ml = op->orm_modlist,
2603 pwmod = 0, mod_pw_only = 1,
2604 deladd = 0, delmod = NULL,
2605 addmod = NULL,
2606 zapReset = 1;
2607 ml != NULL; modtail = ml, ml = ml->sml_next )
2608 {
2609 if ( ml->sml_desc == pp.ad ) {
2610 pwmod = 1;
2611 pwmop = ml->sml_op;
2612 if ((deladd == 0) && (ml->sml_op == LDAP_MOD_DELETE) &&
2613 (ml->sml_values) && !BER_BVISNULL( &ml->sml_values[0] ))
2614 {
2615 deladd = 1;
2616 delmod = ml;
2617 }
2618
2619 if ((ml->sml_op == LDAP_MOD_ADD) ||
2620 (ml->sml_op == LDAP_MOD_REPLACE))
2621 {
2622 if ( ml->sml_values && !BER_BVISNULL( &ml->sml_values[0] )) {
2623 if ( deladd == 1 )
2624 deladd = 2;
2625
2626 /* FIXME: there's no easy way to ensure
2627 * that add does not cause multiple
2628 * userPassword values; one way (that
2629 * would be consistent with the single
2630 * password constraint) would be to turn
2631 * add into replace); another would be
2632 * to disallow add.
2633 *
2634 * Let's check at least that a single value
2635 * is being added
2636 */
2637 if ( addmod || !BER_BVISNULL( &ml->sml_values[ 1 ] ) ) {
2638 rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
2639 rs->sr_text = "Password policy only allows one password value";
2640 goto return_results;
2641 }
2642
2643 addmod = ml;
2644 } else {
2645 /* replace can have no values, add cannot */
2646 assert( ml->sml_op == LDAP_MOD_REPLACE );
2647 }
2648 }
2649
2650 } else if ( !(ml->sml_flags & SLAP_MOD_INTERNAL) && !is_at_operational( ml->sml_desc->ad_type ) ) {
2651 mod_pw_only = 0;
2652 /* modifying something other than password */
2653 }
2654
2655 /*
2656 * If there is a request to explicitly add a pwdReset
2657 * attribute, then we suppress the normal behaviour on
2658 * password change, which is to remove the pwdReset
2659 * attribute.
2660 *
2661 * This enables an administrator to assign a new password
2662 * and place a "must reset" flag on the entry, which will
2663 * stay until the user explicitly changes his/her password.
2664 */
2665 if (ml->sml_desc == ad_pwdReset ) {
2666 if ((ml->sml_op == LDAP_MOD_ADD) ||
2667 (ml->sml_op == LDAP_MOD_REPLACE))
2668 zapReset = 0;
2669 }
2670 if ( ml->sml_op == LDAP_MOD_DELETE ) {
2671 if ( ml->sml_desc == ad_pwdGraceUseTime ) {
2672 got_del_grace = 1;
2673 } else if ( ml->sml_desc == ad_pwdAccountLockedTime ) {
2674 got_del_lock = 1;
2675 } else if ( ml->sml_desc == ad_pwdFailureTime ) {
2676 got_del_fail = 1;
2677 } else if ( ml->sml_desc == ad_pwdLastSuccess ) {
2678 got_del_success = 1;
2679 }
2680 }
2681 if ( ml->sml_desc == ad_pwdChangedTime ) {
2682 got_changed = 1;
2683 } else if (ml->sml_desc == ad_pwdHistory ) {
2684 got_history = 1;
2685 }
2686 }
2687
2688 if (!BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn ) && !mod_pw_only ) {
2689 if ( dn_match( &op->o_conn->c_ndn,
2690 &pwcons[op->o_conn->c_conn_idx].dn )) {
2691 Debug( LDAP_DEBUG_TRACE,
2692 "connection restricted to password changing only\n" );
2693 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
2694 rs->sr_text = "Operations are restricted to bind/unbind/abandon/StartTLS/modify password";
2695 pErr = PP_changeAfterReset;
2696 goto return_results;
2697 } else {
2698 ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val );
2699 BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn );
2700 }
2701 }
2702
2703 /*
2704 * if we have a "safe password modify policy", then we need to check if we're doing
2705 * a delete (with the old password), followed by an add (with the new password).
2706 *
2707 * If we got just a delete with nothing else, just let it go. We also skip all the checks if
2708 * the root user is bound. Root can do anything, including avoid the policies.
2709 */
2710
2711 if (!have_policy || !pwmod) goto do_modify;
2712
2713 /*
2714 * Build the password history list in ascending time order
2715 * We need this, even if the user is root, in order to maintain
2716 * the pwdHistory operational attributes properly.
2717 */
2718 if (addmod && pp.pwdInHistory > 0 && (ha = attr_find( e->e_attrs, ad_pwdHistory ))) {
2719 struct berval oldpw;
2720 time_t oldtime;
2721
2722 for(i=0; ha->a_nvals[i].bv_val; i++) {
2723 rc = parse_pwdhistory( &(ha->a_nvals[i]), NULL,
2724 &oldtime, &oldpw );
2725
2726 if (rc != LDAP_SUCCESS) continue; /* invalid history entry */
2727
2728 if (oldpw.bv_val) {
2729 add_to_pwd_history( &tl, oldtime, &oldpw,
2730 &(ha->a_nvals[i]) );
2731 oldpw.bv_val = NULL;
2732 oldpw.bv_len = 0;
2733 }
2734 }
2735 for(p=tl; p; p=p->next, hsize++); /* count history size */
2736 }
2737
2738 if (is_pwdadmin) goto do_modify;
2739
2740 /* NOTE: according to draft-behera-ldap-password-policy
2741 * pwdAllowUserChange == FALSE must only prevent pwd changes
2742 * by the user the pwd belongs to (ITS#7021) */
2743 if (!pp.pwdAllowUserChange && dn_match(&op->o_req_ndn, &op->o_ndn)) {
2744 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
2745 rs->sr_text = "User alteration of password is not allowed";
2746 pErr = PP_passwordModNotAllowed;
2747 goto return_results;
2748 }
2749
2750 /* Just deleting? */
2751 if (!addmod) {
2752 /* skip everything else */
2753 pwmod = 0;
2754 goto do_modify;
2755 }
2756
2757 /* This is a pwdModify exop that provided the old pw.
2758 * We need to create a Delete mod for this old pw and
2759 * let the matching value get found later
2760 */
2761 if (pp.pwdSafeModify && oldpw.bv_val ) {
2762 ml = (Modifications *)ch_calloc( sizeof( Modifications ), 1 );
2763 ml->sml_op = LDAP_MOD_DELETE;
2764 ml->sml_flags = SLAP_MOD_INTERNAL;
2765 ml->sml_desc = pp.ad;
2766 ml->sml_type = pp.ad->ad_cname;
2767 ml->sml_numvals = 1;
2768 ml->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
2769 ber_dupbv( &ml->sml_values[0], &oldpw );
2770 BER_BVZERO( &ml->sml_values[1] );
2771 ml->sml_next = op->orm_modlist;
2772 op->orm_modlist = ml;
2773 delmod = ml;
2774 deladd = 2;
2775 }
2776
2777 if (pp.pwdSafeModify && deladd != 2) {
2778 Debug( LDAP_DEBUG_TRACE,
2779 "change password must use DELETE followed by ADD/REPLACE\n" );
2780 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
2781 rs->sr_text = "Must supply old password to be changed as well as new one";
2782 pErr = PP_mustSupplyOldPassword;
2783 goto return_results;
2784 }
2785
2786 /* Check age, but only if pwdReset is not TRUE */
2787 pa = attr_find( e->e_attrs, ad_pwdReset );
2788 if ((!pa || !bvmatch( &pa->a_nvals[0], &slap_true_bv )) &&
2789 pp.pwdMinAge > 0) {
2790 time_t pwtime = (time_t)-1, now;
2791 int age;
2792
2793 if ((pa = attr_find( e->e_attrs, ad_pwdChangedTime )) != NULL)
2794 pwtime = parse_time( pa->a_nvals[0].bv_val );
2795 now = slap_get_time();
2796 age = (int)(now - pwtime);
2797 if ((pwtime != (time_t)-1) && (age < pp.pwdMinAge)) {
2798 rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
2799 rs->sr_text = "Password is too young to change";
2800 pErr = PP_passwordTooYoung;
2801 goto return_results;
2802 }
2803 }
2804
2805 /* pa is used in password history check below, be sure it's set */
2806 if ((pa = attr_find( e->e_attrs, pp.ad )) != NULL && delmod) {
2807 /*
2808 * we have a password to check
2809 */
2810 bv = oldpw.bv_val ? &oldpw : delmod->sml_values;
2811 /* FIXME: no access checking? */
2812 rc = slap_passwd_check( op, NULL, pa, bv, &txt );
2813 if (rc != LDAP_SUCCESS) {
2814 Debug( LDAP_DEBUG_TRACE,
2815 "old password check failed: %s\n", txt );
2816
2817 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
2818 rs->sr_text = "Must supply correct old password to change to new one";
2819 pErr = PP_mustSupplyOldPassword;
2820 goto return_results;
2821
2822 } else {
2823 int i;
2824
2825 /*
2826 * replace the delete value with the (possibly hashed)
2827 * value which is currently in the password.
2828 */
2829 for ( i = 0; !BER_BVISNULL( &delmod->sml_values[i] ); i++ ) {
2830 free( delmod->sml_values[i].bv_val );
2831 BER_BVZERO( &delmod->sml_values[i] );
2832 }
2833 free( delmod->sml_values );
2834 delmod->sml_values = ch_calloc( sizeof(struct berval), 2 );
2835 BER_BVZERO( &delmod->sml_values[1] );
2836 ber_dupbv( &(delmod->sml_values[0]), &(pa->a_nvals[0]) );
2837 }
2838 }
2839
2840 bv = newpw.bv_val ? &newpw : &addmod->sml_values[0];
2841 if (pp.pwdCheckQuality > 0) {
2842 struct berval errmsg = BER_BVC( errbuf );
2843
2844 rc = check_password_quality( bv, pi, &pp, &pErr, e, &errmsg );
2845 if (rc != LDAP_SUCCESS) {
2846 rs->sr_err = rc;
2847 txt = errmsg.bv_val;
2848 if ( txt && txt[0] ) {
2849 rs->sr_text = txt;
2850 if ( txt != errbuf )
2851 free_txt = 1;
2852 } else {
2853 rs->sr_text = "Password fails quality checking policy";
2854 }
2855 goto return_results;
2856 }
2857 }
2858
2859 /* If pwdInHistory is zero, passwords may be reused */
2860 if (pa && pp.pwdInHistory > 0) {
2861 /*
2862 * Last check - the password history.
2863 */
2864 /* FIXME: no access checking? */
2865 if (slap_passwd_check( op, NULL, pa, bv, &txt ) == LDAP_SUCCESS) {
2866 /*
2867 * This is bad - it means that the user is attempting
2868 * to set the password to the same as the old one.
2869 */
2870 rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
2871 rs->sr_text = "Password is not being changed from existing value";
2872 pErr = PP_passwordInHistory;
2873 goto return_results;
2874 }
2875
2876 /* We need this when reduce pwdInHistory */
2877 hskip = hsize - pp.pwdInHistory;
2878
2879 /*
2880 * Iterate through the password history, and fail on any
2881 * password matches.
2882 */
2883 at = *pa;
2884 at.a_vals = cr;
2885 cr[1].bv_val = NULL;
2886 for(p=tl; p; p=p->next) {
2887 if(hskip > 0){
2888 hskip--;
2889 continue;
2890 }
2891 cr[0] = p->pw;
2892 /* FIXME: no access checking? */
2893 rc = slap_passwd_check( op, NULL, &at, bv, &txt );
2894
2895 if (rc != LDAP_SUCCESS) continue;
2896
2897 rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
2898 rs->sr_text = "Password is in history of old passwords";
2899 pErr = PP_passwordInHistory;
2900 goto return_results;
2901 }
2902 }
2903
2904 do_modify:
2905 if (pwmod) {
2906 struct berval timestamp;
2907 char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
2908 time_t now = slap_get_time();
2909
2910 /* If the conn is restricted, set a callback to clear it
2911 * if the pwmod succeeds
2912 */
2913 if (!BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn )) {
2914 slap_callback *sc = op->o_tmpcalloc( 1, sizeof( slap_callback ),
2915 op->o_tmpmemctx );
2916 sc->sc_next = op->o_callback;
2917 /* Must use sc_response to insure we reset on success, before
2918 * the client sees the response. Must use sc_cleanup to insure
2919 * that it gets cleaned up if sc_response is not called.
2920 */
2921 sc->sc_response = ppolicy_mod_cb;
2922 sc->sc_cleanup = ppolicy_mod_cb;
2923 op->o_callback = sc;
2924 }
2925
2926 /*
2927 * keep the necessary pwd.. operational attributes
2928 * up to date.
2929 */
2930
2931 if (!got_changed) {
2932 timestamp.bv_val = timebuf;
2933 timestamp.bv_len = sizeof(timebuf);
2934 slap_timestamp( &now, ×tamp );
2935
2936 mods = NULL;
2937 if (pwmop != LDAP_MOD_DELETE) {
2938 mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
2939 mods->sml_op = LDAP_MOD_REPLACE;
2940 mods->sml_numvals = 1;
2941 mods->sml_values = (BerVarray) ch_calloc( sizeof( struct berval ), 2 );
2942 mods->sml_nvalues = (BerVarray) ch_calloc( sizeof( struct berval ), 2 );
2943
2944 ber_dupbv( &mods->sml_values[0], ×tamp );
2945 ber_dupbv( &mods->sml_nvalues[0], ×tamp );
2946 } else if (attr_find(e->e_attrs, ad_pwdChangedTime )) {
2947 mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
2948 mods->sml_op = LDAP_MOD_DELETE;
2949 }
2950 if (mods) {
2951 mods->sml_desc = ad_pwdChangedTime;
2952 mods->sml_flags = SLAP_MOD_INTERNAL;
2953 mods->sml_next = NULL;
2954 modtail->sml_next = mods;
2955 modtail = mods;
2956 }
2957 }
2958
2959 if (!got_del_grace && attr_find(e->e_attrs, ad_pwdGraceUseTime )) {
2960 mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
2961 mods->sml_op = LDAP_MOD_DELETE;
2962 mods->sml_desc = ad_pwdGraceUseTime;
2963 mods->sml_flags = SLAP_MOD_INTERNAL;
2964 mods->sml_next = NULL;
2965 modtail->sml_next = mods;
2966 modtail = mods;
2967 }
2968
2969 if (!got_del_lock && attr_find(e->e_attrs, ad_pwdAccountLockedTime )) {
2970 mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
2971 mods->sml_op = LDAP_MOD_DELETE;
2972 mods->sml_desc = ad_pwdAccountLockedTime;
2973 mods->sml_flags = SLAP_MOD_INTERNAL;
2974 mods->sml_next = NULL;
2975 modtail->sml_next = mods;
2976 modtail = mods;
2977 }
2978
2979 if (!got_del_fail && attr_find(e->e_attrs, ad_pwdFailureTime )) {
2980 mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
2981 mods->sml_op = LDAP_MOD_DELETE;
2982 mods->sml_desc = ad_pwdFailureTime;
2983 mods->sml_flags = SLAP_MOD_INTERNAL;
2984 mods->sml_next = NULL;
2985 modtail->sml_next = mods;
2986 modtail = mods;
2987 }
2988
2989 if ( zapReset ) {
2990 /*
2991 * ITS#7084 Is this a modification by the password
2992 * administrator? Then force a reset if configured.
2993 * Otherwise clear it.
2994 */
2995 if ( pp.pwdMustChange && is_pwdadmin ) {
2996 mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
2997 mods->sml_op = LDAP_MOD_REPLACE;
2998 mods->sml_desc = ad_pwdReset;
2999 mods->sml_flags = SLAP_MOD_INTERNAL;
3000 mods->sml_numvals = 1;
3001 mods->sml_values = (BerVarray) ch_calloc( sizeof( struct berval ), 2 );
3002 mods->sml_nvalues = (BerVarray) ch_calloc( sizeof( struct berval ), 2 );
3003
3004 ber_dupbv( &mods->sml_values[0], (struct berval *)&slap_true_bv );
3005 ber_dupbv( &mods->sml_nvalues[0], (struct berval *)&slap_true_bv );
3006
3007 mods->sml_next = NULL;
3008 modtail->sml_next = mods;
3009 modtail = mods;
3010 } else if ( attr_find( e->e_attrs, ad_pwdReset ) ) {
3011 mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
3012 mods->sml_op = LDAP_MOD_DELETE;
3013 mods->sml_desc = ad_pwdReset;
3014 mods->sml_flags = SLAP_MOD_INTERNAL;
3015 mods->sml_next = NULL;
3016 modtail->sml_next = mods;
3017 modtail = mods;
3018 }
3019 }
3020
3021 /* TODO: do we remove pwdLastSuccess or set it to 'now'? */
3022 if (!got_del_success && attr_find(e->e_attrs, ad_pwdLastSuccess )){
3023 mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
3024 mods->sml_op = LDAP_MOD_DELETE;
3025 mods->sml_flags = SLAP_MOD_INTERNAL;
3026 mods->sml_desc = ad_pwdLastSuccess;
3027 mods->sml_next = NULL;
3028 modtail->sml_next = mods;
3029 modtail = mods;
3030 }
3031
3032 /* Delete all pwdInHistory attribute */
3033 if (!got_history && pp.pwdInHistory == 0 &&
3034 attr_find(e->e_attrs, ad_pwdHistory )){
3035 mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
3036 mods->sml_op = LDAP_MOD_DELETE;
3037 mods->sml_flags = SLAP_MOD_INTERNAL;
3038 mods->sml_desc = ad_pwdHistory;
3039 mods->sml_next = NULL;
3040 modtail->sml_next = mods;
3041 modtail = mods;
3042 }
3043
3044 if (!got_history && pp.pwdInHistory > 0){
3045 if (hsize >= pp.pwdInHistory) {
3046 /*
3047 * We use the >= operator, since we are going to add
3048 * the existing password attribute value into the
3049 * history - thus the cardinality of history values is
3050 * about to rise by one.
3051 *
3052 * If this would push it over the limit of history
3053 * values (remembering - the password policy could have
3054 * changed since the password was last altered), we must
3055 * delete at least 1 value from the pwdHistory list.
3056 *
3057 * In fact, we delete '(#pwdHistory attrs - max pwd
3058 * history length) + 1' values, starting with the oldest.
3059 * This is easily evaluated, since the linked list is
3060 * created in ascending time order.
3061 */
3062 mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
3063 mods->sml_op = LDAP_MOD_DELETE;
3064 mods->sml_flags = SLAP_MOD_INTERNAL;
3065 mods->sml_desc = ad_pwdHistory;
3066 mods->sml_numvals = hsize - pp.pwdInHistory + 1;
3067 mods->sml_values = ch_calloc( sizeof( struct berval ),
3068 hsize - pp.pwdInHistory + 2 );
3069 BER_BVZERO( &mods->sml_values[ hsize - pp.pwdInHistory + 1 ] );
3070 for(i=0,p=tl; i < (hsize - pp.pwdInHistory + 1); i++, p=p->next) {
3071 BER_BVZERO( &mods->sml_values[i] );
3072 ber_dupbv( &(mods->sml_values[i]), &p->bv );
3073 }
3074 mods->sml_next = NULL;
3075 modtail->sml_next = mods;
3076 modtail = mods;
3077 }
3078 free_pwd_history_list( &tl );
3079
3080 /*
3081 * Now add the existing password into the history list.
3082 * This will be executed even if the operation is to delete
3083 * the password entirely.
3084 *
3085 * This isn't in the spec explicitly, but it seems to make
3086 * sense that the password history list is the list of all
3087 * previous passwords - even if they were deleted. Thus, if
3088 * someone tries to add a historical password at some future
3089 * point, it will fail.
3090 */
3091 if ((pa = attr_find( e->e_attrs, pp.ad )) != NULL) {
3092 mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
3093 mods->sml_op = LDAP_MOD_ADD;
3094 mods->sml_flags = SLAP_MOD_INTERNAL;
3095 mods->sml_type.bv_val = NULL;
3096 mods->sml_desc = ad_pwdHistory;
3097 mods->sml_nvalues = NULL;
3098 mods->sml_numvals = 1;
3099 mods->sml_values = ch_calloc( sizeof( struct berval ), 2 );
3100 mods->sml_values[ 1 ].bv_val = NULL;
3101 mods->sml_values[ 1 ].bv_len = 0;
3102 make_pwd_history_value( timebuf, &mods->sml_values[0], pa );
3103 mods->sml_next = NULL;
3104 modtail->sml_next = mods;
3105 modtail = mods;
3106
3107 } else {
3108 Debug( LDAP_DEBUG_TRACE,
3109 "ppolicy_modify: password attr lookup failed\n" );
3110 }
3111 }
3112
3113 /*
3114 * Controversial bit here. If the new password isn't hashed
3115 * (ie, is cleartext), we probably should hash it according
3116 * to the default hash. The reason for this is that we want
3117 * to use the policy if possible, but if we hash the password
3118 * before, then we're going to run into trouble when it
3119 * comes time to check the password.
3120 *
3121 * Now, the right thing to do is to use the extended password
3122 * modify operation, but not all software can do this,
3123 * therefore it makes sense to hash the new password, now
3124 * we know it passes the policy requirements.
3125 *
3126 * Of course, if the password is already hashed, then we
3127 * leave it alone.
3128 */
3129
3130 if ((pi->hash_passwords) && (addmod) && !newpw.bv_val &&
3131 (password_scheme( &(addmod->sml_values[0]), NULL ) != LDAP_SUCCESS))
3132 {
3133 struct berval hpw, bv;
3134
3135 slap_passwd_hash( &(addmod->sml_values[0]), &hpw, &txt );
3136 if (hpw.bv_val == NULL) {
3137 /*
3138 * hashing didn't work. Emit an error.
3139 */
3140 rs->sr_err = LDAP_OTHER;
3141 rs->sr_text = txt;
3142 goto return_results;
3143 }
3144 bv = addmod->sml_values[0];
3145 /* clear and discard the clear password */
3146 memset(bv.bv_val, 0, bv.bv_len);
3147 ber_memfree(bv.bv_val);
3148 addmod->sml_values[0] = hpw;
3149 }
3150 } else {
3151 /* ITS#8762 Make sure we drop pwdFailureTime if unlocking */
3152 if (got_del_lock && !got_del_fail && attr_find(e->e_attrs, ad_pwdFailureTime )) {
3153 mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
3154 mods->sml_op = LDAP_MOD_DELETE;
3155 mods->sml_desc = ad_pwdFailureTime;
3156 mods->sml_flags = SLAP_MOD_INTERNAL;
3157 mods->sml_next = NULL;
3158 modtail->sml_next = mods;
3159 modtail = mods;
3160 }
3161 }
3162 op->o_bd->bd_info = (BackendInfo *)on->on_info;
3163 be_entry_release_r( op, e );
3164 return SLAP_CB_CONTINUE;
3165
3166 return_results:
3167 free_pwd_history_list( &tl );
3168 op->o_bd->bd_info = (BackendInfo *)on->on_info;
3169 be_entry_release_r( op, e );
3170 if ( send_ctrl ) {
3171 ctrl = create_passcontrol( op, -1, -1, pErr );
3172 oldctrls = add_passcontrol( op, rs, ctrl );
3173 }
3174 send_ldap_result( op, rs );
3175 if ( free_txt ) {
3176 if ( is_pwdexop ) {
3177 slap_callback *cb;
3178 cb = op->o_tmpcalloc( sizeof(ppbind)+sizeof(slap_callback),
3179 1, op->o_tmpmemctx );
3180
3181 /* Setup a callback so we can free the text when sent */
3182 cb->sc_cleanup = ppolicy_text_cleanup;
3183 cb->sc_private = (void *)txt;
3184 overlay_callback_after_backover( op, cb, 1 );
3185 } else {
3186 if ( rs->sr_text == txt ) {
3187 rs->sr_text = NULL;
3188 }
3189 free( (char *)txt );
3190 }
3191 }
3192 if ( send_ctrl ) {
3193 if ( is_pwdexop ) {
3194 if ( rs->sr_flags & REP_CTRLS_MUSTBEFREED ) {
3195 op->o_tmpfree( oldctrls, op->o_tmpmemctx );
3196 }
3197 oldctrls = NULL;
3198 rs->sr_flags |= REP_CTRLS_MUSTBEFREED;
3199
3200 } else {
3201 ctrls_cleanup( op, rs, oldctrls );
3202 }
3203 }
3204 return rs->sr_err;
3205 }
3206
3207 static int
ppolicy_parseCtrl(Operation * op,SlapReply * rs,LDAPControl * ctrl)3208 ppolicy_parseCtrl(
3209 Operation *op,
3210 SlapReply *rs,
3211 LDAPControl *ctrl )
3212 {
3213 if ( !BER_BVISNULL( &ctrl->ldctl_value ) ) {
3214 rs->sr_text = "passwordPolicyRequest control value not absent";
3215 return LDAP_PROTOCOL_ERROR;
3216 }
3217 op->o_ctrlflag[ppolicy_cid] = ctrl->ldctl_iscritical
3218 ? SLAP_CONTROL_CRITICAL
3219 : SLAP_CONTROL_NONCRITICAL;
3220
3221 return LDAP_SUCCESS;
3222 }
3223
3224 static int
ppolicy_au_parseCtrl(Operation * op,SlapReply * rs,LDAPControl * ctrl)3225 ppolicy_au_parseCtrl(
3226 Operation *op,
3227 SlapReply *rs,
3228 LDAPControl *ctrl )
3229 {
3230 if ( !BER_BVISNULL( &ctrl->ldctl_value ) ) {
3231 rs->sr_text = "account usability control value not absent";
3232 return LDAP_PROTOCOL_ERROR;
3233 }
3234 op->o_ctrlflag[account_usability_cid] = ctrl->ldctl_iscritical
3235 ? SLAP_CONTROL_CRITICAL
3236 : SLAP_CONTROL_NONCRITICAL;
3237
3238 return LDAP_SUCCESS;
3239 }
3240
3241 static int
attrPretty(Syntax * syntax,struct berval * val,struct berval * out,void * ctx)3242 attrPretty(
3243 Syntax *syntax,
3244 struct berval *val,
3245 struct berval *out,
3246 void *ctx )
3247 {
3248 AttributeDescription *ad = NULL;
3249 const char *err;
3250 int code;
3251
3252 code = slap_bv2ad( val, &ad, &err );
3253 if ( !code ) {
3254 ber_dupbv_x( out, &ad->ad_type->sat_cname, ctx );
3255 }
3256 return code;
3257 }
3258
3259 static int
attrNormalize(slap_mask_t use,Syntax * syntax,MatchingRule * mr,struct berval * val,struct berval * out,void * ctx)3260 attrNormalize(
3261 slap_mask_t use,
3262 Syntax *syntax,
3263 MatchingRule *mr,
3264 struct berval *val,
3265 struct berval *out,
3266 void *ctx )
3267 {
3268 AttributeDescription *ad = NULL;
3269 const char *err;
3270 int code;
3271
3272 code = slap_bv2ad( val, &ad, &err );
3273 if ( !code ) {
3274 ber_str2bv_x( ad->ad_type->sat_oid, 0, 1, out, ctx );
3275 }
3276 return code;
3277 }
3278
3279 static int
ppolicy_db_init(BackendDB * be,ConfigReply * cr)3280 ppolicy_db_init(
3281 BackendDB *be,
3282 ConfigReply *cr
3283 )
3284 {
3285 slap_overinst *on = (slap_overinst *) be->bd_info;
3286 pp_info *pi;
3287
3288 if ( SLAP_ISGLOBALOVERLAY( be ) ) {
3289 /* do not allow slapo-ppolicy to be global by now (ITS#5858) */
3290 if ( cr ){
3291 snprintf( cr->msg, sizeof(cr->msg),
3292 "slapo-ppolicy cannot be global" );
3293 Debug( LDAP_DEBUG_ANY, "%s\n", cr->msg );
3294 }
3295 return 1;
3296 }
3297
3298 pi = on->on_bi.bi_private = ch_calloc( sizeof(pp_info), 1 );
3299
3300 if ( !pwcons ) {
3301 /* accommodate for c_conn_idx == -1 */
3302 pwcons = ch_calloc( sizeof(pw_conn), dtblsize + 1 );
3303 pwcons++;
3304 }
3305
3306 ov_count++;
3307
3308 ldap_pvt_thread_mutex_init( &pi->pwdFailureTime_mutex );
3309
3310 return 0;
3311 }
3312
3313 static int
ppolicy_db_open(BackendDB * be,ConfigReply * cr)3314 ppolicy_db_open(
3315 BackendDB *be,
3316 ConfigReply *cr
3317 )
3318 {
3319 int rc;
3320
3321 if ( (rc = overlay_register_control( be, LDAP_CONTROL_X_ACCOUNT_USABILITY )) != LDAP_SUCCESS ) {
3322 return rc;
3323 }
3324 return overlay_register_control( be, LDAP_CONTROL_PASSWORDPOLICYREQUEST );
3325 }
3326
3327 static int
ppolicy_db_close(BackendDB * be,ConfigReply * cr)3328 ppolicy_db_close(
3329 BackendDB *be,
3330 ConfigReply *cr
3331 )
3332 {
3333 #ifdef SLAP_CONFIG_DELETE
3334 overlay_unregister_control( be, LDAP_CONTROL_PASSWORDPOLICYREQUEST );
3335 overlay_unregister_control( be, LDAP_CONTROL_X_ACCOUNT_USABILITY );
3336 #endif /* SLAP_CONFIG_DELETE */
3337
3338 return 0;
3339 }
3340
3341 static int
ppolicy_db_destroy(BackendDB * be,ConfigReply * cr)3342 ppolicy_db_destroy(
3343 BackendDB *be,
3344 ConfigReply *cr
3345 )
3346 {
3347 slap_overinst *on = (slap_overinst *) be->bd_info;
3348 pp_info *pi = on->on_bi.bi_private;
3349
3350 on->on_bi.bi_private = NULL;
3351 ldap_pvt_thread_mutex_destroy( &pi->pwdFailureTime_mutex );
3352 free( pi->def_policy.bv_val );
3353 free( pi );
3354
3355 ov_count--;
3356 if ( ov_count <=0 && pwcons ) {
3357 pw_conn *pwc = pwcons;
3358 pwcons = NULL;
3359 pwc--;
3360 ch_free( pwc );
3361 }
3362 return 0;
3363 }
3364
3365 static char *extops[] = {
3366 LDAP_EXOP_MODIFY_PASSWD,
3367 NULL
3368 };
3369
3370 static slap_overinst ppolicy;
3371
ppolicy_initialize()3372 int ppolicy_initialize()
3373 {
3374 int i, code;
3375
3376 for (i=0; pwd_OpSchema[i].def; i++) {
3377 code = register_at( pwd_OpSchema[i].def, pwd_OpSchema[i].ad, 0 );
3378 if ( code ) {
3379 Debug( LDAP_DEBUG_ANY,
3380 "ppolicy_initialize: register_at failed\n" );
3381 return code;
3382 }
3383 /* Allow Manager to set these as needed */
3384 if ( is_at_no_user_mod( (*pwd_OpSchema[i].ad)->ad_type )) {
3385 (*pwd_OpSchema[i].ad)->ad_type->sat_flags |=
3386 SLAP_AT_MANAGEABLE;
3387 }
3388 }
3389 ad_pwdLastSuccess = slap_schema.si_ad_pwdLastSuccess;
3390 {
3391 Syntax *syn;
3392 MatchingRule *mr;
3393
3394 syn = ch_malloc( sizeof( Syntax ));
3395 *syn = *ad_pwdAttribute->ad_type->sat_syntax;
3396 syn->ssyn_pretty = attrPretty;
3397 ad_pwdAttribute->ad_type->sat_syntax = syn;
3398
3399 mr = ch_malloc( sizeof( MatchingRule ));
3400 *mr = *ad_pwdAttribute->ad_type->sat_equality;
3401 mr->smr_normalize = attrNormalize;
3402 ad_pwdAttribute->ad_type->sat_equality = mr;
3403 }
3404
3405 for (i=0; pwd_ocs[i]; i++) {
3406 code = register_oc( pwd_ocs[i], NULL, 0 );
3407 if ( code ) {
3408 Debug( LDAP_DEBUG_ANY, "ppolicy_initialize: "
3409 "register_oc failed\n" );
3410 return code;
3411 }
3412 }
3413
3414 code = register_supported_control( LDAP_CONTROL_PASSWORDPOLICYREQUEST,
3415 SLAP_CTRL_ADD|SLAP_CTRL_BIND|SLAP_CTRL_MODIFY, extops,
3416 ppolicy_parseCtrl, &ppolicy_cid );
3417 if ( code != LDAP_SUCCESS ) {
3418 Debug( LDAP_DEBUG_ANY, "Failed to register control %d\n", code );
3419 return code;
3420 }
3421
3422 code = register_supported_control( LDAP_CONTROL_X_ACCOUNT_USABILITY,
3423 SLAP_CTRL_SEARCH, NULL,
3424 ppolicy_au_parseCtrl, &account_usability_cid );
3425 if ( code != LDAP_SUCCESS ) {
3426 Debug( LDAP_DEBUG_ANY, "Failed to register control %d\n", code );
3427 return code;
3428 }
3429
3430 /* We don't expect to receive these controls, only send them */
3431 code = register_supported_control( LDAP_CONTROL_X_PASSWORD_EXPIRED,
3432 0, NULL, NULL, NULL );
3433 if ( code != LDAP_SUCCESS ) {
3434 Debug( LDAP_DEBUG_ANY, "Failed to register control %d\n", code );
3435 return code;
3436 }
3437
3438 code = register_supported_control( LDAP_CONTROL_X_PASSWORD_EXPIRING,
3439 0, NULL, NULL, NULL );
3440 if ( code != LDAP_SUCCESS ) {
3441 Debug( LDAP_DEBUG_ANY, "Failed to register control %d\n", code );
3442 return code;
3443 }
3444
3445 ldap_pvt_thread_mutex_init( &chk_syntax_mutex );
3446
3447 ppolicy.on_bi.bi_type = "ppolicy";
3448 ppolicy.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
3449 ppolicy.on_bi.bi_db_init = ppolicy_db_init;
3450 ppolicy.on_bi.bi_db_open = ppolicy_db_open;
3451 ppolicy.on_bi.bi_db_close = ppolicy_db_close;
3452 ppolicy.on_bi.bi_db_destroy = ppolicy_db_destroy;
3453
3454 ppolicy.on_bi.bi_op_add = ppolicy_add;
3455 ppolicy.on_bi.bi_op_bind = ppolicy_bind;
3456 ppolicy.on_bi.bi_op_compare = ppolicy_compare;
3457 ppolicy.on_bi.bi_op_delete = ppolicy_restrict;
3458 ppolicy.on_bi.bi_op_modify = ppolicy_modify;
3459 ppolicy.on_bi.bi_op_search = ppolicy_search;
3460 ppolicy.on_bi.bi_connection_destroy = ppolicy_connection_destroy;
3461
3462 ppolicy.on_bi.bi_cf_ocs = ppolicyocs;
3463 code = config_register_schema( ppolicycfg, ppolicyocs );
3464 if ( code ) return code;
3465
3466 return overlay_register( &ppolicy );
3467 }
3468
3469 #if SLAPD_OVER_PPOLICY == SLAPD_MOD_DYNAMIC
init_module(int argc,char * argv[])3470 int init_module(int argc, char *argv[]) {
3471 return ppolicy_initialize();
3472 }
3473 #endif
3474
3475 #endif /* defined(SLAPD_OVER_PPOLICY) */
3476