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