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