]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/ppolicy.c
Fix void* pointer arithmetic from ber_bvchr()
[openldap] / servers / slapd / overlays / ppolicy.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 2004-2005 The OpenLDAP Foundation.
5  * Portions Copyright 2004-2005 Howard Chu, Symas Corporation.
6  * Portions Copyright 2004 Hewlett-Packard Company.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 /* ACKNOWLEDGEMENTS:
18  * This work was developed by Howard Chu for inclusion in
19  * OpenLDAP Software, based on prior work by Neil Dunbar (HP).
20  * This work was sponsored by the Hewlett-Packard Company.
21  */
22
23 #include "portable.h"
24
25 /* This file implements "Password Policy for LDAP Directories",
26  * based on draft behera-ldap-password-policy-09
27  */
28
29 #ifdef SLAPD_OVER_PPOLICY
30
31 #include <ldap.h>
32 #include "lutil.h"
33 #include "slap.h"
34 #if SLAPD_MODULES
35 #define LIBLTDL_DLL_IMPORT      /* Win32: don't re-export libltdl's symbols */
36 #include <ltdl.h>
37 #endif
38 #include <ac/errno.h>
39 #include <ac/time.h>
40 #include <ac/string.h>
41 #include <ac/ctype.h>
42
43 #ifndef MODULE_NAME_SZ
44 #define MODULE_NAME_SZ 256
45 #endif
46
47 /* Per-instance configuration information */
48 typedef struct pp_info {
49         struct berval def_policy;       /* DN of default policy subentry */
50         int use_lockout;                /* send AccountLocked result? */
51         int hash_passwords;             /* transparently hash cleartext pwds */
52 } pp_info;
53
54 /* Our per-connection info - note, it is not per-instance, it is 
55  * used by all instances
56  */
57 typedef struct pw_conn {
58         int restricted;         /* TRUE if connection is restricted */
59 } pw_conn;
60
61 static pw_conn *pwcons;
62 static int ppolicy_cid;
63
64 typedef struct pass_policy {
65         AttributeDescription *ad; /* attribute to which the policy applies */
66         int pwdMinAge; /* minimum time (seconds) until passwd can change */
67         int pwdMaxAge; /* time in seconds until pwd will expire after change */
68         int pwdInHistory; /* number of previous passwords kept */
69         int pwdCheckQuality; /* 0 = don't check quality, 1 = check if possible,
70                                                    2 = check mandatory; fail if not possible */
71         int pwdMinLength; /* minimum number of chars in password */
72         int pwdExpireWarning; /* number of seconds that warning controls are
73                                                         sent before a password expires */
74         int pwdGraceAuthNLimit; /* number of times you can log in with an
75                                                         expired password */
76         int pwdLockout; /* 0 = do not lockout passwords, 1 = lock them out */
77         int pwdLockoutDuration; /* time in seconds a password is locked out for */
78         int pwdMaxFailure; /* number of failed binds allowed before lockout */
79         int pwdFailureCountInterval; /* number of seconds before failure
80                                                                         counts are zeroed */
81         int pwdMustChange; /* 0 = users can use admin set password
82                                                         1 = users must change password after admin set */
83         int pwdAllowUserChange; /* 0 = users cannot change their passwords
84                                                                 1 = users can change them */
85         int pwdSafeModify; /* 0 = old password doesn't need to come
86                                                                 with password change request
87                                                         1 = password change must supply existing pwd */
88         char pwdCheckModule[MODULE_NAME_SZ]; /* name of module to dynamically
89                                                                                     load to check password */
90 } PassPolicy;
91
92 typedef struct pw_hist {
93         time_t t;       /* timestamp of history entry */
94         struct berval pw;       /* old password hash */
95         struct berval bv;       /* text of entire entry */
96         struct pw_hist *next;
97 } pw_hist;
98
99 /* Operational attributes */
100 static AttributeDescription *ad_pwdChangedTime, *ad_pwdAccountLockedTime,
101         *ad_pwdFailureTime, *ad_pwdHistory, *ad_pwdGraceUseTime, *ad_pwdReset,
102         *ad_pwdPolicySubentry;
103
104 static struct schema_info {
105         char *def;
106         AttributeDescription **ad;
107 } pwd_OpSchema[] = {
108         {       "( 1.3.6.1.4.1.42.2.27.8.1.16 "
109                 "NAME ( 'pwdChangedTime' ) "
110                 "DESC 'The time the password was last changed' "
111                 "EQUALITY generalizedTimeMatch "
112                 "ORDERING generalizedTimeOrderingMatch "
113                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
114                 "SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )",
115                 &ad_pwdChangedTime },
116         {       "( 1.3.6.1.4.1.42.2.27.8.1.17 "
117                 "NAME ( 'pwdAccountLockedTime' ) "
118                 "DESC 'The time an user account was locked' "
119                 "EQUALITY generalizedTimeMatch "
120                 "ORDERING generalizedTimeOrderingMatch "
121                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
122                 "SINGLE-VALUE "
123 #if 0
124                 /* Not until MANAGEDIT control is released */
125                 "NO-USER-MODIFICATION "
126 #endif
127                 "USAGE directoryOperation )",
128                 &ad_pwdAccountLockedTime },
129         {       "( 1.3.6.1.4.1.42.2.27.8.1.19 "
130                 "NAME ( 'pwdFailureTime' ) "
131                 "DESC 'The timestamps of the last consecutive authentication failures' "
132                 "EQUALITY generalizedTimeMatch "
133                 "ORDERING generalizedTimeOrderingMatch "
134                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
135                 "NO-USER-MODIFICATION USAGE directoryOperation )",
136                 &ad_pwdFailureTime },
137         {       "( 1.3.6.1.4.1.42.2.27.8.1.20 "
138                 "NAME ( 'pwdHistory' ) "
139                 "DESC 'The history of users passwords' "
140                 "EQUALITY octetStringMatch "
141                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 "
142                 "NO-USER-MODIFICATION USAGE directoryOperation )",
143                 &ad_pwdHistory },
144         {       "( 1.3.6.1.4.1.42.2.27.8.1.21 "
145                 "NAME ( 'pwdGraceUseTime' ) "
146                 "DESC 'The timestamps of the grace login once the password has expired' "
147                 "EQUALITY generalizedTimeMatch "
148                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
149                 "NO-USER-MODIFICATION USAGE directoryOperation )",
150                 &ad_pwdGraceUseTime }, 
151         {       "( 1.3.6.1.4.1.42.2.27.8.1.22 "
152                 "NAME ( 'pwdReset' ) "
153                 "DESC 'The indication that the password has been reset' "
154                 "EQUALITY booleanMatch "
155                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
156                 "SINGLE-VALUE USAGE directoryOperation )",
157                 &ad_pwdReset },
158         {       "( 1.3.6.1.4.1.42.2.27.8.1.23 "
159                 "NAME ( 'pwdPolicySubentry' ) "
160                 "DESC 'The pwdPolicy subentry in effect for this object' "
161                 "EQUALITY distinguishedNameMatch "
162                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 "
163                 "SINGLE-VALUE "
164 #if 0
165                 /* Not until MANAGEDIT control is released */
166                 "NO-USER-MODIFICATION "
167 #endif
168                 "USAGE directoryOperation )",
169                 &ad_pwdPolicySubentry },
170         { NULL, NULL }
171 };
172
173 /* User attributes */
174 static AttributeDescription *ad_pwdMinAge, *ad_pwdMaxAge, *ad_pwdInHistory,
175         *ad_pwdCheckQuality, *ad_pwdMinLength, *ad_pwdMaxFailure, 
176         *ad_pwdGraceAuthNLimit, *ad_pwdExpireWarning, *ad_pwdLockoutDuration,
177         *ad_pwdFailureCountInterval, *ad_pwdCheckModule, *ad_pwdLockout,
178         *ad_pwdMustChange, *ad_pwdAllowUserChange, *ad_pwdSafeModify,
179         *ad_pwdAttribute;
180
181 #define TAB(name)       { #name, &ad_##name }
182
183 static struct schema_info pwd_UsSchema[] = {
184         TAB(pwdAttribute),
185         TAB(pwdMinAge),
186         TAB(pwdMaxAge),
187         TAB(pwdInHistory),
188         TAB(pwdCheckQuality),
189         TAB(pwdMinLength),
190         TAB(pwdMaxFailure),
191         TAB(pwdGraceAuthNLimit),
192         TAB(pwdExpireWarning),
193         TAB(pwdLockout),
194         TAB(pwdLockoutDuration),
195         TAB(pwdFailureCountInterval),
196         TAB(pwdCheckModule),
197         TAB(pwdMustChange),
198         TAB(pwdAllowUserChange),
199         TAB(pwdSafeModify),
200         { NULL, NULL }
201 };
202
203 static ldap_pvt_thread_mutex_t chk_syntax_mutex;
204
205 static time_t
206 parse_time( char *atm )
207 {
208         struct lutil_tm tm;
209         struct lutil_timet tt;
210         time_t ret = (time_t)-1;
211
212         if ( lutil_parsetime( atm, &tm ) == 0) {
213                 lutil_tm2time( &tm, &tt );
214                 ret = tt.tt_sec;
215         }
216         return ret;
217 }
218
219 static int
220 account_locked( Operation *op, Entry *e,
221                 PassPolicy *pp, Modifications **mod ) 
222 {
223         Attribute       *la;
224
225         assert(mod != NULL);
226
227         if ( (la = attr_find( e->e_attrs, ad_pwdAccountLockedTime )) != NULL ) {
228                 BerVarray vals = la->a_nvals;
229
230                 /*
231                  * there is a lockout stamp - we now need to know if it's
232                  * a valid one.
233                  */
234                 if (vals[0].bv_val != NULL) {
235                         time_t then, now;
236                         Modifications *m;
237
238                         if (!pp->pwdLockoutDuration)
239                                 return 1;
240
241                         if ((then = parse_time( vals[0].bv_val )) == (time_t)0)
242                                 return 1;
243
244                         now = slap_get_time();
245
246                         if (now < then + pp->pwdLockoutDuration)
247                                 return 1;
248
249                         m = ch_calloc( sizeof(Modifications), 1 );
250                         m->sml_op = LDAP_MOD_DELETE;
251                         m->sml_flags = 0;
252                         m->sml_type = ad_pwdAccountLockedTime->ad_cname;
253                         m->sml_desc = ad_pwdAccountLockedTime;
254                         m->sml_next = *mod;
255                         *mod = m;
256                 }
257         }
258
259         return 0;
260 }
261
262 #define PPOLICY_WARNING 0xa0L
263 #define PPOLICY_ERROR 0xa1L
264  
265 #define PPOLICY_EXPIRE 0xa0L
266 #define PPOLICY_GRACE  0xa1L
267
268 static LDAPControl *
269 create_passcontrol( int exptime, int grace, LDAPPasswordPolicyError err )
270 {
271         char berbuf[LBER_ELEMENT_SIZEOF], bb2[LBER_ELEMENT_SIZEOF];
272         BerElement *ber = (BerElement *)berbuf, *b2 = (BerElement *)bb2;
273         LDAPControl *c;
274         struct berval bv;
275
276         if ((c = ch_calloc( sizeof( LDAPControl ), 1 )) == NULL) return NULL;
277         c->ldctl_oid = LDAP_CONTROL_PASSWORDPOLICYRESPONSE;
278         c->ldctl_iscritical = 0;
279         c->ldctl_value.bv_val = NULL;
280         c->ldctl_value.bv_len = 0;
281
282         ber_init2( ber, NULL, LBER_USE_DER );
283         ber_printf(ber, "{" /*}*/ );
284
285         if (exptime >= 0) {
286                 ber_init2( b2, NULL, LBER_USE_DER );
287                 ber_printf( b2, "ti", PPOLICY_EXPIRE, exptime );
288                 ber_flatten2( b2, &bv, 1 );
289                 ber_printf( ber, "tO", PPOLICY_WARNING, &bv );
290                 ch_free( bv.bv_val );
291         } else if (grace > 0) {
292                 ber_init2( b2, NULL, LBER_USE_DER );
293                 ber_printf( b2, "ti", PPOLICY_GRACE, grace );
294                 ber_flatten2( b2, &bv, 1 );
295                 ber_printf( ber, "tO", PPOLICY_WARNING, &bv );
296                 ch_free( bv.bv_val );
297         }
298
299         if (err != PP_noError ) {
300                 ber_printf( ber, "te", PPOLICY_ERROR, err );
301         }
302         ber_printf( ber, /*{*/ "N}" );
303
304         if (ber_flatten2( ber, &(c->ldctl_value), 1 ) == LBER_DEFAULT) {
305                 ch_free(c);
306                 (void)ber_free_buf(ber);
307                 return NULL;
308         }
309         (void)ber_free_buf(ber);
310         return c;
311 }
312
313 static void
314 ppolicy_get( Operation *op, Entry *e, PassPolicy *pp )
315 {
316         slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
317         pp_info *pi = on->on_bi.bi_private;
318         Attribute *a;
319         BerVarray vals;
320         int rc;
321         Entry *pe = NULL;
322 #if 0
323         const char *text;
324 #endif
325
326         memset( pp, 0, sizeof(PassPolicy) );
327
328         /* Users can change their own password by default */
329         pp->pwdAllowUserChange = 1;
330
331         if ((a = attr_find( e->e_attrs, ad_pwdPolicySubentry )) == NULL) {
332                 /*
333                  * entry has no password policy assigned - use default
334                  */
335                 vals = &pi->def_policy;
336                 if ( !vals->bv_val )
337                         goto defaultpol;
338         } else {
339                 vals = a->a_nvals;
340                 if (vals[0].bv_val == NULL) {
341                         Debug( LDAP_DEBUG_ANY,
342                                 "ppolicy_get: NULL value for policySubEntry\n", 0, 0, 0 );
343                         goto defaultpol;
344                 }
345         }
346
347         op->o_bd->bd_info = (BackendInfo *)on->on_info;
348         rc = be_entry_get_rw( op, vals, NULL, NULL, 0, &pe );
349         op->o_bd->bd_info = (BackendInfo *)on;
350
351         if ( rc ) goto defaultpol;
352
353 #if 0   /* Only worry about userPassword for now */
354         if ((a = attr_find( pe->e_attrs, ad_pwdAttribute )))
355                 slap_bv2ad( &a->a_vals[0], &pp->ad, &text );
356 #else
357         pp->ad = slap_schema.si_ad_userPassword;
358 #endif
359
360         if ((a = attr_find( pe->e_attrs, ad_pwdMinAge )))
361                 pp->pwdMinAge = atoi(a->a_vals[0].bv_val );
362         if ((a = attr_find( pe->e_attrs, ad_pwdMaxAge )))
363                 pp->pwdMaxAge = atoi(a->a_vals[0].bv_val );
364         if ((a = attr_find( pe->e_attrs, ad_pwdInHistory )))
365                 pp->pwdInHistory = atoi(a->a_vals[0].bv_val );
366         if ((a = attr_find( pe->e_attrs, ad_pwdCheckQuality )))
367                 pp->pwdCheckQuality = atoi(a->a_vals[0].bv_val );
368         if ((a = attr_find( pe->e_attrs, ad_pwdMinLength )))
369                 pp->pwdMinLength = atoi(a->a_vals[0].bv_val );
370         if ((a = attr_find( pe->e_attrs, ad_pwdMaxFailure )))
371                 pp->pwdMaxFailure = atoi(a->a_vals[0].bv_val );
372         if ((a = attr_find( pe->e_attrs, ad_pwdGraceAuthNLimit )))
373                 pp->pwdGraceAuthNLimit = atoi(a->a_vals[0].bv_val );
374         if ((a = attr_find( pe->e_attrs, ad_pwdExpireWarning )))
375                 pp->pwdExpireWarning = atoi(a->a_vals[0].bv_val );
376         if ((a = attr_find( pe->e_attrs, ad_pwdFailureCountInterval )))
377                 pp->pwdFailureCountInterval = atoi(a->a_vals[0].bv_val );
378         if ((a = attr_find( pe->e_attrs, ad_pwdLockoutDuration )))
379                 pp->pwdLockoutDuration = atoi(a->a_vals[0].bv_val );
380
381         if ((a = attr_find( pe->e_attrs, ad_pwdCheckModule ))) {
382                 strncpy(pp->pwdCheckModule, a->a_vals[0].bv_val,
383                         sizeof(pp->pwdCheckModule));
384                 pp->pwdCheckModule[sizeof(pp->pwdCheckModule)-1] = '\0';
385         }
386
387         if ((a = attr_find( pe->e_attrs, ad_pwdLockout )))
388         pp->pwdLockout = !strcmp( a->a_nvals[0].bv_val, "TRUE" );
389         if ((a = attr_find( pe->e_attrs, ad_pwdMustChange )))
390         pp->pwdMustChange = !strcmp( a->a_nvals[0].bv_val, "TRUE" );
391         if ((a = attr_find( pe->e_attrs, ad_pwdAllowUserChange )))
392         pp->pwdAllowUserChange = !strcmp( a->a_nvals[0].bv_val, "TRUE" );
393         if ((a = attr_find( pe->e_attrs, ad_pwdSafeModify )))
394         pp->pwdSafeModify = !strcmp( a->a_nvals[0].bv_val, "TRUE" );
395     
396         op->o_bd->bd_info = (BackendInfo *)on->on_info;
397         be_entry_release_r( op, pe );
398         op->o_bd->bd_info = (BackendInfo *)on;
399
400         return;
401
402 defaultpol:
403         Debug( LDAP_DEBUG_ANY,
404                 "ppolicy_get: using default policy\n", 0, 0, 0 );
405         return;
406 }
407
408 static int
409 password_scheme( struct berval *cred, struct berval *sch )
410 {
411         int e;
412     
413         assert( cred != NULL );
414
415         if (sch) {
416                 sch->bv_val = NULL;
417                 sch->bv_len = 0;
418         }
419     
420         if ((cred->bv_len == 0) || (cred->bv_val == NULL) ||
421                 (cred->bv_val[0] != '{')) return LDAP_OTHER;
422
423         for(e = 1; cred->bv_val[e] && cred->bv_val[e] != '}'; e++);
424         if (cred->bv_val[e]) {
425                 int rc;
426                 rc = lutil_passwd_scheme( cred->bv_val );
427                 if (rc && sch) {
428                         sch->bv_val = cred->bv_val;
429                         sch->bv_len = e;
430                         return LDAP_SUCCESS;
431                 }
432         }
433         return LDAP_OTHER;
434 }
435
436 static int
437 check_password_quality( struct berval *cred, PassPolicy *pp, LDAPPasswordPolicyError *err, Entry *e )
438 {
439         int rc = LDAP_SUCCESS, ok = LDAP_SUCCESS;
440         char *ptr = cred->bv_val;
441         struct berval sch;
442
443         assert( cred != NULL );
444         assert( pp != NULL );
445
446         if ((cred->bv_len == 0) || (pp->pwdMinLength > cred->bv_len)) {
447                 rc = LDAP_CONSTRAINT_VIOLATION;
448                 if ( err ) *err = PP_passwordTooShort;
449                 return rc;
450         }
451
452         /*
453          * We need to know if the password is already hashed - if so
454          * what scheme is it. The reason being that the "hash" of
455          * {cleartext} still allows us to check the password.
456          */
457         rc = password_scheme( cred, &sch );
458         if (rc == LDAP_SUCCESS) {
459                 if ((sch.bv_val) && (strncasecmp( sch.bv_val, "{cleartext}",
460                         sch.bv_len ) == 0)) {
461                         /*
462                          * We can check the cleartext "hash"
463                          */
464                         ptr = cred->bv_val + sch.bv_len;
465                 } else {
466                         /* everything else, we can't check */
467                         if (pp->pwdCheckQuality == 2) {
468                                 rc = LDAP_CONSTRAINT_VIOLATION;
469                                 if (err) *err = PP_insufficientPasswordQuality;
470                                 return rc;
471                         }
472                         /*
473                          * We can't check the syntax of the password, but it's not
474                          * mandatory (according to the policy), so we return success.
475                          */
476                     
477                         return LDAP_SUCCESS;
478                 }
479         }
480
481         rc = LDAP_SUCCESS;
482
483         if (pp->pwdCheckModule[0]) {
484 #if SLAPD_MODULES
485                 lt_dlhandle mod;
486                 const char *err;
487                 
488                 if ((mod = lt_dlopen( pp->pwdCheckModule )) == NULL) {
489                         err = lt_dlerror();
490
491                         Debug(LDAP_DEBUG_ANY,
492                         "check_password_quality: lt_dlopen failed: (%s) %s.\n",
493                                 pp->pwdCheckModule, err, 0 );
494                         ok = LDAP_OTHER; /* internal error */
495                 } else {
496                         int (*prog)( char *passwd, char **text, Entry *ent );
497
498                         if ((prog = lt_dlsym( mod, "check_password" )) == NULL) {
499                                 err = lt_dlerror();
500                             
501                                 Debug(LDAP_DEBUG_ANY,
502                                         "check_password_quality: lt_dlsym failed: (%s) %s.\n",
503                                         pp->pwdCheckModule, err, 0 );
504                                 ok = LDAP_OTHER;
505                         } else {
506                                 char *txt = NULL;
507
508                                 ldap_pvt_thread_mutex_lock( &chk_syntax_mutex );
509                                 ok = prog( cred->bv_val, &txt, e );
510                                 ldap_pvt_thread_mutex_unlock( &chk_syntax_mutex );
511                                 if (txt) {
512                                         Debug(LDAP_DEBUG_ANY,
513                                                 "check_password_quality: module error: (%s) %s.[%d]\n",
514                                                 pp->pwdCheckModule, txt, ok );
515                                         free(txt);
516                                 } else
517                                         ok = LDAP_SUCCESS;
518                         }
519                             
520                         lt_dlclose( mod );
521                 }
522 #else
523         Debug(LDAP_DEBUG_ANY, "check_password_quality: external modules not "
524                 "supported. pwdCheckModule ignored.\n", 0, 0, 0);
525 #endif /* SLAPD_MODULES */
526         }
527                 
528                     
529         if (ok != LDAP_SUCCESS) {
530                 rc = LDAP_CONSTRAINT_VIOLATION;
531                 if (err) *err = PP_insufficientPasswordQuality;
532         }
533         
534         return rc;
535 }
536
537 static int
538 parse_pwdhistory( struct berval *bv, char **oid, time_t *oldtime, struct berval *oldpw )
539 {
540         char *ptr;
541         struct berval nv, npw;
542         int i, j;
543         
544         assert (bv && (bv->bv_len > 0) && (bv->bv_val) && oldtime && oldpw );
545
546         if ( oid ) *oid = 0;
547         *oldtime = (time_t)-1;
548         oldpw->bv_val = NULL;
549         oldpw->bv_len = 0;
550         
551         ber_dupbv( &nv, bv );
552
553         /* first get the time field */
554         for(i=0; (i < nv.bv_len) && (nv.bv_val[i] != '#'); i++);
555         if ( i == nv.bv_len) goto exit_failure; /* couldn't locate the '#' separator */
556         nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */
557         ptr = nv.bv_val;
558         *oldtime = parse_time( ptr );
559         if (*oldtime == (time_t)-1) goto exit_failure;
560
561         /* get the OID field */
562         for(ptr = &(nv.bv_val[i]);(i < nv.bv_len) && (nv.bv_val[i] != '#'); i++);
563         if ( i == nv.bv_len) goto exit_failure; /* couldn't locate the '#' separator */
564         nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */
565         if ( oid ) *oid = ber_strdup( ptr );
566         
567         /* get the length field */
568         for(ptr = &(nv.bv_val[i]);(i < nv.bv_len) && (nv.bv_val[i] != '#'); i++);
569         if ( i == nv.bv_len) goto exit_failure; /* couldn't locate the '#' separator */
570         nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */
571         oldpw->bv_len = strtol( ptr, NULL, 10 );
572         if (errno == ERANGE) goto exit_failure;
573
574         /* lastly, get the octets of the string */
575         for(j=i, ptr = &(nv.bv_val[i]);i < nv.bv_len; i++);
576         if (i-j != oldpw->bv_len) goto exit_failure; /* length is wrong */
577
578         npw.bv_val = ptr;
579         npw.bv_len = oldpw->bv_len;
580         ber_dupbv( oldpw, &npw );
581         
582         return LDAP_SUCCESS;
583 exit_failure:
584         if (oid && *oid) { ber_memfree(*oid); *oid = NULL; }
585         if (oldpw->bv_val) {
586                 ber_memfree( oldpw->bv_val); oldpw->bv_val = NULL;
587                 oldpw->bv_len = 0;
588         }
589         ber_memfree(nv.bv_val);
590         return LDAP_OTHER;
591 }
592
593 static void
594 add_to_pwd_history( pw_hist **l, time_t t,
595                     struct berval *oldpw, struct berval *bv )
596 {
597         pw_hist *p, *p1, *p2;
598     
599         if (!l) return;
600
601         p = ch_malloc( sizeof( pw_hist ));
602         p->pw = *oldpw;
603         ber_dupbv( &p->bv, bv );
604         p->t = t;
605         p->next = NULL;
606         
607         if (*l == NULL) {
608                 /* degenerate case */
609                 *l = p;
610                 return;
611         }
612         /*
613          * advance p1 and p2 such that p1 is the node before the
614          * new one, and p2 is the node after it
615          */
616         for (p1 = NULL, p2 = *l; p2 && p2->t <= t; p1 = p2, p2=p2->next );
617         p->next = p2;
618         if (p1 == NULL) { *l = p; return; }
619         p1->next = p;
620 }
621
622 #ifndef MAX_PWD_HISTORY_SZ
623 #define MAX_PWD_HISTORY_SZ 1024
624 #endif /* MAX_PWD_HISTORY_SZ */
625
626 static void
627 make_pwd_history_value( char *timebuf, struct berval *bv, Attribute *pa )
628 {
629         char str[ MAX_PWD_HISTORY_SZ ];
630         int nlen;
631
632         snprintf( str, MAX_PWD_HISTORY_SZ,
633                   "%s#%s#%lu#", timebuf,
634                   pa->a_desc->ad_type->sat_syntax->ssyn_oid,
635                   (unsigned long) pa->a_nvals[0].bv_len );
636         str[MAX_PWD_HISTORY_SZ-1] = 0;
637         nlen = strlen(str);
638
639         /*
640          * We have to assume that the string is a string of octets,
641          * not readable characters. In reality, yes, it probably is
642          * a readable (ie, base64) string, but we can't count on that
643          * Hence, while the first 3 fields of the password history
644          * are definitely readable (a timestamp, an OID and an integer
645          * length), the remaining octets of the actual password
646          * are deemed to be binary data.
647          */
648         AC_MEMCPY( str + nlen, pa->a_nvals[0].bv_val, pa->a_nvals[0].bv_len );
649         nlen += pa->a_nvals[0].bv_len;
650         bv->bv_val = ch_malloc( nlen + 1 );
651         AC_MEMCPY( bv->bv_val, str, nlen );
652         bv->bv_val[nlen] = '\0';
653         bv->bv_len = nlen;
654 }
655
656 static void
657 free_pwd_history_list( pw_hist **l )
658 {
659         pw_hist *p;
660     
661         if (!l) return;
662         p = *l;
663         while (p) {
664                 pw_hist *pp = p->next;
665
666                 free(p->pw.bv_val);
667                 free(p->bv.bv_val);
668                 free(p);
669                 p = pp;
670         }
671         *l = NULL;
672 }
673
674 typedef struct ppbind {
675         slap_overinst *on;
676         int send_ctrl;
677         Modifications *mod;
678         LDAPPasswordPolicyError pErr;
679         PassPolicy pp;
680 } ppbind;
681
682 static int
683 ppolicy_bind_resp( Operation *op, SlapReply *rs )
684 {
685         ppbind *ppb = op->o_callback->sc_private;
686         slap_overinst *on = ppb->on;
687         Modifications *mod = ppb->mod, *m;
688         int pwExpired = 0;
689         int ngut = -1, warn = -1, age, rc;
690         Attribute *a;
691         time_t now, pwtime = (time_t)-1;
692         char nowstr[ LDAP_LUTIL_GENTIME_BUFSIZE ];
693         struct berval timestamp;
694         BackendInfo *bi = op->o_bd->bd_info;
695         Entry *e;
696
697         /* If we already know it's locked, just get on with it */
698         if ( ppb->pErr != PP_noError ) {
699                 goto locked;
700         }
701
702         op->o_bd->bd_info = (BackendInfo *)on->on_info;
703         rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
704         op->o_bd->bd_info = bi;
705
706         if ( rc != LDAP_SUCCESS ) {
707                 return SLAP_CB_CONTINUE;
708         }
709
710         now = slap_get_time(); /* stored for later consideration */
711         timestamp.bv_val = nowstr;
712         timestamp.bv_len = sizeof(nowstr);
713         slap_timestamp( &now, &timestamp );
714
715         if ( rs->sr_err == LDAP_INVALID_CREDENTIALS ) {
716                 int i = 0, fc = 0;
717
718                 m = ch_calloc( sizeof(Modifications), 1 );
719                 m->sml_op = LDAP_MOD_ADD;
720                 m->sml_flags = 0;
721                 m->sml_type = ad_pwdFailureTime->ad_cname;
722                 m->sml_desc = ad_pwdFailureTime;
723                 m->sml_values = ch_calloc( sizeof(struct berval), 2 );
724
725                 ber_dupbv( &m->sml_values[0], &timestamp );
726                 m->sml_next = mod;
727                 mod = m;
728
729                 /*
730                  * Count the pwdFailureTimes - if it's
731                  * greater than the policy pwdMaxFailure,
732                  * then lock the account.
733                  */
734                 if ((a = attr_find( e->e_attrs, ad_pwdFailureTime )) != NULL) {
735                         for(i=0; a->a_nvals[i].bv_val; i++) {
736
737                                 /*
738                                  * If the interval is 0, then failures
739                                  * stay on the record until explicitly
740                                  * reset by successful authentication.
741                                  */
742                                 if (ppb->pp.pwdFailureCountInterval == 0) {
743                                         fc++;
744                                 } else if (now <=
745                                                         parse_time(a->a_nvals[i].bv_val) +
746                                                         ppb->pp.pwdFailureCountInterval) {
747
748                                         fc++;
749                                 }
750                                 /*
751                                  * We only count those failures
752                                  * which are not due to expire.
753                                  */
754                         }
755                 }
756                 
757                 if ((ppb->pp.pwdMaxFailure > 0) &&
758                         (fc >= ppb->pp.pwdMaxFailure - 1)) {
759
760                         /*
761                          * We subtract 1 from the failure max
762                          * because the new failure entry hasn't
763                          * made it to the entry yet.
764                          */
765                         m = ch_calloc( sizeof(Modifications), 1 );
766                         m->sml_op = LDAP_MOD_REPLACE;
767                         m->sml_flags = 0;
768                         m->sml_type = ad_pwdAccountLockedTime->ad_cname;
769                         m->sml_desc = ad_pwdAccountLockedTime;
770                         m->sml_values = ch_calloc( sizeof(struct berval), 2 );
771                         ber_dupbv( &m->sml_values[0], &timestamp );
772                         m->sml_next = mod;
773                         mod = m;
774                 }
775         } else if ( rs->sr_err == LDAP_SUCCESS ) {
776                 if ((a = attr_find( e->e_attrs, ad_pwdChangedTime )) != NULL)
777                         pwtime = parse_time( a->a_nvals[0].bv_val );
778
779                 /* delete all pwdFailureTimes */
780                 if ( attr_find( e->e_attrs, ad_pwdFailureTime )) {
781                         m = ch_calloc( sizeof(Modifications), 1 );
782                         m->sml_op = LDAP_MOD_DELETE;
783                         m->sml_flags = 0;
784                         m->sml_type = ad_pwdFailureTime->ad_cname;
785                         m->sml_desc = ad_pwdFailureTime;
786                         m->sml_next = mod;
787                         mod = m;
788                 }
789
790                 /*
791                  * check to see if the password must be changed
792                  */
793                 if ( ppb->pp.pwdMustChange &&
794                         (a = attr_find( e->e_attrs, ad_pwdReset )) &&
795                         !strcmp( a->a_nvals[0].bv_val, "TRUE" ) ) {
796                         /*
797                          * need to inject client controls here to give
798                          * more information. For the moment, we ensure
799                          * that we are disallowed from doing anything
800                          * other than change password.
801                          */
802                         pwcons[op->o_conn->c_conn_idx].restricted = 1;
803
804                         ppb->pErr = PP_changeAfterReset;
805
806                 } else {
807                         /*
808                          * the password does not need to be changed, so
809                          * we now check whether the password has expired.
810                          *
811                          * We can skip this bit if passwords don't age in
812                          * the policy.
813                          */
814                         if (ppb->pp.pwdMaxAge == 0) goto grace;
815
816                         if (pwtime == (time_t)-1) {
817                                 /*
818                                  * Hmm. No password changed time on the
819                                  * entry. This is odd - it should have
820                                  * been provided when the attribute was added.
821                                  *
822                                  * However, it's possible that it could be
823                                  * missing if the DIT was established via
824                                  * an import process.
825                                  */
826                                 Debug( LDAP_DEBUG_ANY,
827                                         "ppolicy_bind: Entry %s does not have valid pwdChangedTime attribute - assuming password expired\n",
828                                         e->e_name.bv_val, 0, 0);
829                                 
830                                 pwExpired = 1;
831                         } else {
832                                 /*
833                                  * Check: was the last change time of
834                                  * the password older than the maximum age
835                                  * allowed. (Ignore case 2 from I-D, it's just silly.)
836                                  */
837                                 if (now - pwtime > ppb->pp.pwdMaxAge ) pwExpired = 1;
838                         }
839                 }
840
841 grace:
842                 if (!pwExpired) goto check_expiring_password;
843                 
844                 if ((a = attr_find( e->e_attrs, ad_pwdGraceUseTime )) == NULL)
845                         ngut = ppb->pp.pwdGraceAuthNLimit;
846                 else {
847                         for(ngut=0; a->a_nvals[ngut].bv_val; ngut++);
848                         ngut = ppb->pp.pwdGraceAuthNLimit - ngut;
849                 }
850
851                 /*
852                  * ngut is the number of remaining grace logins
853                  */
854                 Debug( LDAP_DEBUG_ANY,
855                         "ppolicy_bind: Entry %s has an expired password: %d grace logins\n",
856                         e->e_name.bv_val, ngut, 0);
857                 
858                 if (ngut < 1) {
859                         ppb->pErr = PP_passwordExpired;
860                         rs->sr_err = LDAP_INVALID_CREDENTIALS;
861                         goto done;
862                 }
863
864                 /*
865                  * Add a grace user time to the entry
866                  */
867                 m = ch_calloc( sizeof(Modifications), 1 );
868                 m->sml_op = LDAP_MOD_ADD;
869                 m->sml_flags = 0;
870                 m->sml_type = ad_pwdGraceUseTime->ad_cname;
871                 m->sml_desc = ad_pwdGraceUseTime;
872                 m->sml_values = ch_calloc( sizeof(struct berval), 2 );
873                 ber_dupbv( &m->sml_values[0], &timestamp );
874                 m->sml_next = mod;
875                 mod = m;
876
877 check_expiring_password:
878                 /*
879                  * Now we need to check to see
880                  * if it is about to expire, and if so, should the user
881                  * be warned about it in the password policy control.
882                  *
883                  * If the password has expired, and we're in the grace period, then
884                  * we don't need to do this bit. Similarly, if we don't have password
885                  * aging, then there's no need to do this bit either.
886                  */
887                 if ((ppb->pp.pwdMaxAge < 1) || (pwExpired) || (ppb->pp.pwdExpireWarning < 1))
888                         goto done;
889
890                 age = (int)(now - pwtime);
891                 
892                 /*
893                  * We know that there is a password Change Time attribute - if
894                  * there wasn't, then the pwdExpired value would be true, unless
895                  * there is no password aging - and if there is no password aging,
896                  * then this section isn't called anyway - you can't have an
897                  * expiring password if there's no limit to expire.
898                  */
899                 if (ppb->pp.pwdMaxAge - age < ppb->pp.pwdExpireWarning ) {
900                         /*
901                          * Set the warning value.
902                          */
903                         warn = ppb->pp.pwdMaxAge - age; /* seconds left until expiry */
904                         if (warn < 0) warn = 0; /* something weird here - why is pwExpired not set? */
905                         
906                         Debug( LDAP_DEBUG_ANY,
907                                 "ppolicy_bind: Setting warning for password expiry for %s = %d seconds\n",
908                                 op->o_req_dn.bv_val, warn, 0 );
909                 }
910         }
911
912 done:
913         op->o_bd->bd_info = (BackendInfo *)on->on_info;
914         be_entry_release_r( op, e );
915
916 locked:
917         if ( mod ) {
918                 Operation op2 = *op;
919                 SlapReply r2 = { REP_RESULT };
920                 slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
921
922                 /* FIXME: Need to handle replication of some (but not all)
923                  * of the operational attributes...
924                  */
925                 op2.o_tag = LDAP_REQ_MODIFY;
926                 op2.o_callback = &cb;
927                 op2.orm_modlist = mod;
928                 op2.o_dn = op->o_bd->be_rootdn;
929                 op2.o_ndn = op->o_bd->be_rootndn;
930                 op2.o_bd->bd_info = (BackendInfo *)on->on_info;
931                 rc = op->o_bd->be_modify( &op2, &r2 );
932                 slap_mods_free( mod, 1 );
933         }
934
935         if ( ppb->send_ctrl ) {
936                 LDAPControl **ctrls = NULL;
937                 pp_info *pi = on->on_bi.bi_private;
938
939                 /* Do we really want to tell that the account is locked? */
940                 if ( ppb->pErr == PP_accountLocked && !pi->use_lockout ) {
941                         ppb->pErr = PP_noError;
942                 }
943                 ctrls = ch_calloc( sizeof( LDAPControl *) , 2 );
944                 ctrls[0] = create_passcontrol( warn, ngut, ppb->pErr );
945                 ctrls[1] = NULL;
946                 rs->sr_ctrls = ctrls;
947         }
948         op->o_bd->bd_info = bi;
949         return SLAP_CB_CONTINUE;
950 }
951
952 static int
953 ppolicy_bind( Operation *op, SlapReply *rs )
954 {
955         slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
956
957         /* Reset lockout status on all Bind requests */
958         pwcons[op->o_conn->c_conn_idx].restricted = 0;
959
960         /* Root bypasses policy */
961         if ( !be_isroot_dn( op->o_bd, &op->o_req_ndn )) {
962                 Entry *e;
963                 int rc;
964                 ppbind *ppb;
965                 slap_callback *cb;
966
967                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
968                 rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
969
970                 if ( rc != LDAP_SUCCESS ) {
971                         return SLAP_CB_CONTINUE;
972                 }
973
974                 cb = op->o_tmpcalloc( sizeof(ppbind)+sizeof(slap_callback),
975                         1, op->o_tmpmemctx );
976                 ppb = (ppbind *)(cb+1);
977                 ppb->on = on;
978                 ppb->pErr = PP_noError;
979
980                 /* Setup a callback so we can munge the result */
981
982                 cb->sc_response = ppolicy_bind_resp;
983                 cb->sc_next = op->o_callback->sc_next;
984                 cb->sc_private = ppb;
985                 op->o_callback->sc_next = cb;
986
987                 /* Did we receive a password policy request control? */
988                 if ( op->o_ctrlflag[ppolicy_cid] ) {
989                         ppb->send_ctrl = 1;
990                 }
991
992                 op->o_bd->bd_info = (BackendInfo *)on;
993                 ppolicy_get( op, e, &ppb->pp );
994
995                 rc = account_locked( op, e, &ppb->pp, &ppb->mod );
996
997                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
998                 be_entry_release_r( op, e );
999
1000                 if ( rc ) {
1001                         /* This will be the Draft 8 response, Unwilling is bogus */
1002                         ppb->pErr = PP_accountLocked;
1003                         send_ldap_error( op, rs, LDAP_INVALID_CREDENTIALS, NULL );
1004                         return rs->sr_err;
1005                 }
1006
1007         }
1008
1009         return SLAP_CB_CONTINUE;
1010 }
1011
1012 /* Reset the restricted flag for the next session on this connection */
1013 static int
1014 ppolicy_connection_destroy( BackendDB *bd, Connection *conn )
1015 {
1016         pwcons[conn->c_conn_idx].restricted = 0;
1017         return SLAP_CB_CONTINUE;
1018 }
1019
1020 /* Check if this connection is restricted */
1021 static int
1022 ppolicy_restrict(
1023         Operation *op,
1024         SlapReply *rs )
1025 {
1026         slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1027         int send_ctrl = 0;
1028
1029         /* Did we receive a password policy request control? */
1030         if ( op->o_ctrlflag[ppolicy_cid] ) {
1031                 send_ctrl = 1;
1032         }
1033
1034         if ( op->o_conn && pwcons[op->o_conn->c_conn_idx].restricted ) {
1035                 Debug( LDAP_DEBUG_TRACE,
1036                         "connection restricted to password changing only\n", 0, 0, 0);
1037                 if ( send_ctrl ) {
1038                         LDAPControl **ctrls = NULL;
1039
1040                         ctrls = ch_calloc( sizeof( LDAPControl *) , 2 );
1041                         ctrls[0] = create_passcontrol( -1, -1, PP_changeAfterReset );
1042                         ctrls[1] = NULL;
1043                         rs->sr_ctrls = ctrls;
1044                 }
1045                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1046                 send_ldap_error( op, rs, LDAP_INSUFFICIENT_ACCESS, 
1047                         "Operations are restricted to bind/unbind/abandon/StartTLS/modify password" );
1048                 return rs->sr_err;
1049         }
1050
1051         return SLAP_CB_CONTINUE;
1052 }
1053
1054 static int
1055 ppolicy_add(
1056         Operation *op,
1057         SlapReply *rs )
1058 {
1059         slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1060         pp_info *pi = on->on_bi.bi_private;
1061         PassPolicy pp;
1062         Attribute *pa;
1063         const char *txt;
1064
1065         if ( ppolicy_restrict( op, rs ) != SLAP_CB_CONTINUE )
1066                 return rs->sr_err;
1067
1068         /* If this is a replica, assume the master checked everything */
1069         if ( be_shadow_update( op ))
1070                 return SLAP_CB_CONTINUE;
1071
1072         /* Check for password in entry */
1073         if ((pa = attr_find( op->oq_add.rs_e->e_attrs,
1074                 slap_schema.si_ad_userPassword )))
1075         {
1076                 /*
1077                  * new entry contains a password - if we're not the root user
1078                  * then we need to check that the password fits in with the
1079                  * security policy for the new entry.
1080                  */
1081                 ppolicy_get( op, op->ora_e, &pp );
1082                 if (pp.pwdCheckQuality > 0 && !be_isroot( op )) {
1083                         struct berval *bv = &(pa->a_vals[0]);
1084                         int rc, send_ctrl = 0;
1085                         LDAPPasswordPolicyError pErr = PP_noError;
1086
1087                         /* Did we receive a password policy request control? */
1088                         if ( op->o_ctrlflag[ppolicy_cid] ) {
1089                                 send_ctrl = 1;
1090                         }
1091                         rc = check_password_quality( bv, &pp, &pErr, op->ora_e );
1092                         if (rc != LDAP_SUCCESS) {
1093                                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1094                                 if ( send_ctrl ) {
1095                                         LDAPControl **ctrls = NULL;
1096
1097                                         ctrls = ch_calloc( sizeof( LDAPControl *) , 2 );
1098                                         ctrls[0] = create_passcontrol( -1, -1, pErr );
1099                                         ctrls[1] = NULL;
1100                                         rs->sr_ctrls = ctrls;
1101                                 }
1102                                 send_ldap_error( op, rs, rc, "Password fails quality checking policy" );
1103                                 return rs->sr_err;
1104                         }
1105                             /*
1106                              * A controversial bit. We hash cleartext
1107                              * passwords provided via add and modify operations
1108                              * You're not really supposed to do this, since
1109                              * the X.500 model says "store attributes" as they
1110                              * get provided. By default, this is what we do
1111                              *
1112                              * But if the hash_passwords flag is set, we hash
1113                              * any cleartext password attribute values via the
1114                              * default password hashing scheme.
1115                              */
1116                         if ((pi->hash_passwords) &&
1117                                 (password_scheme( &(pa->a_vals[0]), NULL ) != LDAP_SUCCESS)) {
1118                                 struct berval hpw;
1119
1120                                 slap_passwd_hash( &(pa->a_vals[0]), &hpw, &txt );
1121                                 if (hpw.bv_val == NULL) {
1122                                     /*
1123                                      * hashing didn't work. Emit an error.
1124                                      */
1125                                         rs->sr_err = LDAP_OTHER;
1126                                         rs->sr_text = txt;
1127                                         send_ldap_error( op, rs, LDAP_OTHER, "Password hashing failed" );
1128                                         return rs->sr_err;
1129                                 }
1130
1131                                 memset( pa->a_vals[0].bv_val, 0, pa->a_vals[0].bv_len);
1132                                 ber_memfree( pa->a_vals[0].bv_val );
1133                                 pa->a_vals[0].bv_val = hpw.bv_val;
1134                                 pa->a_vals[0].bv_len = hpw.bv_len;
1135                         }
1136                 }
1137                 /* If password aging is in effect, set the pwdChangedTime */
1138                 if ( pp.pwdMaxAge || pp.pwdMinAge ) {
1139                         struct berval timestamp;
1140                         char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
1141                         time_t now = slap_get_time();
1142
1143                         timestamp.bv_val = timebuf;
1144                         timestamp.bv_len = sizeof(timebuf);
1145                         slap_timestamp( &now, &timestamp );
1146
1147                         attr_merge_one( op->ora_e, ad_pwdChangedTime, &timestamp, NULL );
1148                 }
1149         }
1150         return SLAP_CB_CONTINUE;
1151 }
1152
1153 static int
1154 ppolicy_modify( Operation *op, SlapReply *rs )
1155 {
1156         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
1157         pp_info                 *pi = on->on_bi.bi_private;
1158         int                     i, rc, mod_pw_only, pwmod, pwmop = -1, deladd,
1159                                 hsize = 0;
1160         PassPolicy              pp;
1161         Modifications           *mods = NULL, *modtail = NULL,
1162                                 *ml, *delmod, *addmod;
1163         Attribute               *pa, *ha, at;
1164         const char              *txt;
1165         pw_hist                 *tl = NULL, *p;
1166         int                     zapReset, send_ctrl = 0;
1167         Entry                   *e;
1168         struct berval           newpw = BER_BVNULL, oldpw = BER_BVNULL,
1169                                 *bv, cr[2];
1170         LDAPPasswordPolicyError pErr = PP_noError;
1171
1172         op->o_bd->bd_info = (BackendInfo *)on->on_info;
1173         rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
1174         op->o_bd->bd_info = (BackendInfo *)on;
1175
1176         if ( rc != LDAP_SUCCESS ) return SLAP_CB_CONTINUE;
1177
1178         /* If this is a replica, we may need to tweak some of the
1179          * master's modifications. Otherwise, just pass it through.
1180          */
1181         if ( be_shadow_update( op )) {
1182                 Modifications **prev;
1183                 int got_del_grace = 0, got_del_lock = 0, got_pw = 0, got_del_fail = 0;
1184                 Attribute *a_grace, *a_lock, *a_fail;
1185
1186                 a_grace = attr_find( e->e_attrs, ad_pwdGraceUseTime );
1187                 a_lock = attr_find( e->e_attrs, ad_pwdAccountLockedTime );
1188                 a_fail = attr_find( e->e_attrs, ad_pwdFailureTime );
1189
1190                 for( prev = &op->oq_modify.rs_modlist, ml = *prev; ml; ml = *prev ) {
1191
1192                         if ( ml->sml_desc == slap_schema.si_ad_userPassword )
1193                                 got_pw = 1;
1194
1195                         /* If we're deleting an attr that didn't exist,
1196                          * drop this delete op
1197                          */
1198                         if ( ml->sml_op == LDAP_MOD_DELETE ) {
1199                                 int drop = 0;
1200
1201                                 if ( ml->sml_desc == ad_pwdGraceUseTime ) {
1202                                         got_del_grace = 1;
1203                                         if ( !a_grace )
1204                                                 drop = 1;
1205                                 } else
1206                                 if ( ml->sml_desc == ad_pwdAccountLockedTime ) {
1207                                         got_del_lock = 1;
1208                                         if ( !a_lock )
1209                                                 drop = 1;
1210                                 } else
1211                                 if ( ml->sml_desc == ad_pwdFailureTime ) {
1212                                         got_del_fail = 1;
1213                                         if ( !a_fail )
1214                                                 drop = 1;
1215                                 }
1216                                 if ( drop ) {
1217                                         *prev = ml->sml_next;
1218                                         ml->sml_next = NULL;
1219                                         slap_mods_free( ml, 1 );
1220                                         continue;
1221                                 }
1222                         }
1223                         prev = &ml->sml_next;
1224                 }
1225
1226                 /* If we're resetting the password, make sure grace, accountlock,
1227                  * and failure also get removed.
1228                  */
1229                 if ( got_pw ) {
1230                         if ( a_grace && !got_del_grace ) {
1231                                 ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
1232                                 ml->sml_op = LDAP_MOD_DELETE;
1233                                 ml->sml_flags = SLAP_MOD_INTERNAL;
1234                                 ml->sml_type.bv_val = NULL;
1235                                 ml->sml_desc = ad_pwdGraceUseTime;
1236                                 ml->sml_values = NULL;
1237                                 ml->sml_nvalues = NULL;
1238                                 ml->sml_next = NULL;
1239                                 *prev = ml;
1240                                 prev = &ml->sml_next;
1241                         }
1242                         if ( a_lock && !got_del_lock ) {
1243                                 ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
1244                                 ml->sml_op = LDAP_MOD_DELETE;
1245                                 ml->sml_flags = SLAP_MOD_INTERNAL;
1246                                 ml->sml_type.bv_val = NULL;
1247                                 ml->sml_desc = ad_pwdAccountLockedTime;
1248                                 ml->sml_values = NULL;
1249                                 ml->sml_nvalues = NULL;
1250                                 ml->sml_next = NULL;
1251                                 *prev = ml;
1252                         }
1253                         if ( a_fail && !got_del_fail ) {
1254                                 ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
1255                                 ml->sml_op = LDAP_MOD_DELETE;
1256                                 ml->sml_flags = SLAP_MOD_INTERNAL;
1257                                 ml->sml_type.bv_val = NULL;
1258                                 ml->sml_desc = ad_pwdFailureTime;
1259                                 ml->sml_values = NULL;
1260                                 ml->sml_nvalues = NULL;
1261                                 ml->sml_next = NULL;
1262                                 *prev = ml;
1263                         }
1264                 }
1265                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1266                 be_entry_release_r( op, e );
1267                 return SLAP_CB_CONTINUE;
1268         }
1269
1270         /* Did we receive a password policy request control? */
1271         if ( op->o_ctrlflag[ppolicy_cid] ) {
1272                 send_ctrl = 1;
1273         }
1274
1275         /* See if this is a pwdModify exop. If so, we can
1276          * access the plaintext passwords from that request.
1277          */
1278         {
1279                 slap_callback *sc;
1280
1281                 for ( sc = op->o_callback; sc; sc=sc->sc_next ) {
1282                         if ( sc->sc_response == slap_replog_cb &&
1283                                 sc->sc_private ) {
1284                                 req_pwdexop_s *qpw = sc->sc_private;
1285                                 newpw = qpw->rs_new;
1286                                 oldpw = qpw->rs_old;
1287                                 break;
1288                         }
1289                 }
1290         }
1291
1292         ppolicy_get( op, e, &pp );
1293
1294         for ( ml = op->oq_modify.rs_modlist,
1295                         pwmod = 0, mod_pw_only = 1,
1296                         deladd = 0, delmod = NULL,
1297                         addmod = NULL,
1298                         zapReset = 1;
1299                 ml != NULL; modtail = ml, ml = ml->sml_next )
1300         {
1301                 if ( ml->sml_desc == pp.ad ) {
1302                         pwmod = 1;
1303                         pwmop = ml->sml_op;
1304                         if ((deladd == 0) && (ml->sml_op == LDAP_MOD_DELETE) &&
1305                                 (ml->sml_values) && (ml->sml_values[0].bv_val != NULL)) {
1306                                 deladd = 1;
1307                                 delmod = ml;
1308                         }
1309
1310                         if ((deladd == 1) && ((ml->sml_op == LDAP_MOD_ADD) ||
1311                                                                   (ml->sml_op == LDAP_MOD_REPLACE)))
1312                                 deladd = 2;
1313
1314                         if ((ml->sml_op == LDAP_MOD_ADD) ||
1315                                 (ml->sml_op == LDAP_MOD_REPLACE))
1316                                 addmod = ml;
1317                 } else if (! is_at_operational( ml->sml_desc->ad_type )) {
1318                         mod_pw_only = 0;
1319                         /* modifying something other than password */
1320                 }
1321
1322                 /*
1323                  * If there is a request to explicitly add a pwdReset
1324                  * attribute, then we suppress the normal behaviour on
1325                  * password change, which is to remove the pwdReset
1326                  * attribute.
1327                  *
1328                  * This enables an administrator to assign a new password
1329                  * and place a "must reset" flag on the entry, which will
1330                  * stay until the user explicitly changes his/her password.
1331                  */
1332                 if (ml->sml_desc == ad_pwdReset ) {
1333                         if ((ml->sml_op == LDAP_MOD_ADD) ||
1334                                 (ml->sml_op == LDAP_MOD_REPLACE))
1335                                 zapReset = 0;
1336                 }
1337         }
1338         
1339         if (pwcons[op->o_conn->c_conn_idx].restricted && !mod_pw_only) {
1340                 Debug( LDAP_DEBUG_TRACE,
1341                         "connection restricted to password changing only\n", 0, 0, 0 );
1342                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 
1343                 rs->sr_text = "Operations are restricted to bind/unbind/abandon/StartTLS/modify password";
1344                 pErr = PP_changeAfterReset;
1345                 goto return_results;
1346         }
1347
1348         /*
1349          * if we have a "safe password modify policy", then we need to check if we're doing
1350          * a delete (with the old password), followed by an add (with the new password).
1351          *
1352          * If we don't have this, then we fail with an error. We also skip all the checks if
1353          * the root user is bound. Root can do anything, including avoid the policies.
1354          */
1355
1356         if (!pwmod) goto do_modify;
1357
1358         /*
1359          * Did we get a valid add mod?
1360          */
1361
1362         if (!addmod) {
1363                 rs->sr_err = LDAP_OTHER;
1364                 rs->sr_text = "Internal Error";
1365                 Debug( LDAP_DEBUG_TRACE,
1366                         "cannot locate modification supplying new password\n", 0, 0, 0 );
1367                 goto return_results;
1368         }
1369
1370         /*
1371          * Build the password history list in ascending time order
1372          * We need this, even if the user is root, in order to maintain
1373          * the pwdHistory operational attributes properly.
1374          */
1375         if (pp.pwdInHistory > 0 && (ha = attr_find( e->e_attrs, ad_pwdHistory ))) {
1376                 struct berval oldpw;
1377                 time_t oldtime;
1378
1379                 for(i=0; ha->a_nvals[i].bv_val; i++) {
1380                         rc = parse_pwdhistory( &(ha->a_nvals[i]), NULL,
1381                                 &oldtime, &oldpw );
1382
1383                         if (rc != LDAP_SUCCESS) continue; /* invalid history entry */
1384
1385                         if (oldpw.bv_val) {
1386                                 add_to_pwd_history( &tl, oldtime, &oldpw,
1387                                         &(ha->a_nvals[i]) );
1388                                 oldpw.bv_val = NULL;
1389                                 oldpw.bv_len = 0;
1390                         }
1391                 }
1392                 for(p=tl; p; p=p->next, hsize++); /* count history size */
1393         }
1394
1395         if (be_isroot( op )) goto do_modify;
1396
1397         /* This is a pwdModify exop that provided the old pw.
1398          * We need to create a Delete mod for this old pw and 
1399          * let the matching value get found later
1400          */
1401         if (pp.pwdSafeModify && oldpw.bv_val ) {
1402                 ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
1403                 ml->sml_op = LDAP_MOD_DELETE;
1404                 ml->sml_flags = SLAP_MOD_INTERNAL;
1405                 ml->sml_desc = pp.ad;
1406                 ml->sml_type = pp.ad->ad_cname;
1407                 ml->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
1408                 ber_dupbv( &ml->sml_values[0], &oldpw );
1409                 ml->sml_values[1].bv_len = 0;
1410                 ml->sml_values[1].bv_val = NULL;
1411                 ml->sml_nvalues = NULL;
1412                 ml->sml_next = op->orm_modlist;
1413                 op->orm_modlist = ml;
1414                 delmod = ml;
1415                 deladd = 2;
1416         }
1417
1418         if (pp.pwdSafeModify && deladd != 2) {
1419                 Debug( LDAP_DEBUG_TRACE,
1420                         "change password must use DELETE followed by ADD/REPLACE\n",
1421                         0, 0, 0 );
1422                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1423                 rs->sr_text = "Must supply old password to be changed as well as new one";
1424                 pErr = PP_mustSupplyOldPassword;
1425                 goto return_results;
1426         }
1427
1428         if (!pp.pwdAllowUserChange) {
1429                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1430                 rs->sr_text = "User alteration of password is not allowed";
1431                 pErr = PP_passwordModNotAllowed;
1432                 goto return_results;
1433         }
1434
1435         if (pp.pwdMinAge > 0) {
1436                 time_t pwtime = (time_t)-1, now;
1437                 int age;
1438
1439                 if ((pa = attr_find( e->e_attrs, ad_pwdChangedTime )) != NULL)
1440                         pwtime = parse_time( pa->a_nvals[0].bv_val );
1441                 now = slap_get_time();
1442                 age = (int)(now - pwtime);
1443                 if ((pwtime != (time_t)-1) && (age < pp.pwdMinAge)) {
1444                         rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
1445                         rs->sr_text = "Password is too young to change";
1446                         pErr = PP_passwordTooYoung;
1447                         goto return_results;
1448                 }
1449         }
1450
1451         /* pa is used in password history check below, be sure it's set */
1452         if ((pa = attr_find( e->e_attrs, pp.ad )) != NULL && delmod) {
1453                 /*
1454                  * we have a password to check
1455                  */
1456                 const char *txt;
1457                 
1458                 bv = oldpw.bv_val ? &oldpw : delmod->sml_values;
1459                 /* FIXME: no access checking? */
1460                 rc = slap_passwd_check( op, NULL, pa, bv, &txt );
1461                 if (rc != LDAP_SUCCESS) {
1462                         Debug( LDAP_DEBUG_TRACE,
1463                                 "old password check failed: %s\n", txt, 0, 0 );
1464                         
1465                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1466                         rs->sr_text = "Must supply correct old password to change to new one";
1467                         pErr = PP_mustSupplyOldPassword;
1468                         goto return_results;
1469
1470                 } else {
1471                         int i;
1472                         
1473                         /*
1474                          * replace the delete value with the (possibly hashed)
1475                          * value which is currently in the password.
1476                          */
1477                         for(i=0; delmod->sml_values[i].bv_val; i++) {
1478                                 free(delmod->sml_values[i].bv_val);
1479                                 delmod->sml_values[i].bv_len = 0;
1480                         }
1481                         free(delmod->sml_values);
1482                         delmod->sml_values = ch_calloc( sizeof(struct berval), 2 );
1483                         delmod->sml_values[1].bv_len = 0;
1484                         delmod->sml_values[1].bv_val = NULL;
1485                         ber_dupbv(&(delmod->sml_values[0]),  &(pa->a_nvals[0]));
1486                 }
1487         }
1488
1489         bv = newpw.bv_val ? &newpw : addmod->sml_values;
1490         if (pp.pwdCheckQuality > 0) {
1491
1492                 rc = check_password_quality( bv, &pp, &pErr, e );
1493                 if (rc != LDAP_SUCCESS) {
1494                         rs->sr_err = rc;
1495                         rs->sr_text = "Password fails quality checking policy";
1496                         goto return_results;
1497                 }
1498         }
1499
1500         if (pa) {
1501                 /*
1502                  * Last check - the password history.
1503                  */
1504                 /* FIXME: no access checking? */
1505                 if (slap_passwd_check( op, NULL, pa, bv, &txt ) == LDAP_SUCCESS) {
1506                         /*
1507                          * This is bad - it means that the user is attempting
1508                          * to set the password to the same as the old one.
1509                          */
1510                         rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
1511                         rs->sr_text = "Password is not being changed from existing value";
1512                         pErr = PP_passwordInHistory;
1513                         goto return_results;
1514                 }
1515         
1516                 if (pp.pwdInHistory < 1) goto do_modify;
1517         
1518                 /*
1519                  * Iterate through the password history, and fail on any
1520                  * password matches.
1521                  */
1522                 at = *pa;
1523                 at.a_vals = cr;
1524                 cr[1].bv_val = NULL;
1525                 for(p=tl; p; p=p->next) {
1526                         cr[0] = p->pw;
1527                         /* FIXME: no access checking? */
1528                         rc = slap_passwd_check( op, NULL, &at, bv, &txt );
1529                         
1530                         if (rc != LDAP_SUCCESS) continue;
1531                         
1532                         rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
1533                         rs->sr_text = "Password is in history of old passwords";
1534                         pErr = PP_passwordInHistory;
1535                         goto return_results;
1536                 }
1537         }
1538
1539 do_modify:
1540         if (pwmod) {
1541                 struct berval timestamp;
1542                 char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
1543                 time_t now = slap_get_time();
1544                 
1545                 /*
1546                  * keep the necessary pwd.. operational attributes
1547                  * up to date.
1548                  */
1549
1550                 timestamp.bv_val = timebuf;
1551                 timestamp.bv_len = sizeof(timebuf);
1552                 slap_timestamp( &now, &timestamp );
1553
1554                 mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1555                 mods->sml_type.bv_val = NULL;
1556                 mods->sml_desc = ad_pwdChangedTime;
1557                 if (pwmop != LDAP_MOD_DELETE) {
1558                         mods->sml_op = LDAP_MOD_REPLACE;
1559                         mods->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
1560                         ber_dupbv( &mods->sml_values[0], &timestamp );
1561                         mods->sml_values[1].bv_len = 0;
1562                         mods->sml_values[1].bv_val = NULL;
1563                         assert( mods->sml_values[0].bv_val != NULL );
1564                 } else {
1565                         mods->sml_op = LDAP_MOD_DELETE;
1566                         mods->sml_values = NULL;
1567                 }
1568                 mods->sml_flags = SLAP_MOD_INTERNAL;
1569                 mods->sml_nvalues = NULL;
1570                 mods->sml_next = NULL;
1571                 modtail->sml_next = mods;
1572                 modtail = mods;
1573
1574                 if (attr_find(e->e_attrs, ad_pwdGraceUseTime )) {
1575                         mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1576                         mods->sml_op = LDAP_MOD_DELETE;
1577                         mods->sml_flags = SLAP_MOD_INTERNAL;
1578                         mods->sml_type.bv_val = NULL;
1579                         mods->sml_desc = ad_pwdGraceUseTime;
1580                         mods->sml_values = NULL;
1581                         mods->sml_nvalues = NULL;
1582                         mods->sml_next = NULL;
1583                         modtail->sml_next = mods;
1584                         modtail = mods;
1585                 }
1586
1587                 if (attr_find(e->e_attrs, ad_pwdAccountLockedTime )) {
1588                         mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1589                         mods->sml_op = LDAP_MOD_DELETE;
1590                         mods->sml_flags = SLAP_MOD_INTERNAL;
1591                         mods->sml_type.bv_val = NULL;
1592                         mods->sml_desc = ad_pwdAccountLockedTime;
1593                         mods->sml_values = NULL;
1594                         mods->sml_nvalues = NULL;
1595                         mods->sml_next = NULL;
1596                         modtail->sml_next = mods;
1597                         modtail = mods;
1598                 }
1599
1600                 if (attr_find(e->e_attrs, ad_pwdFailureTime )) {
1601                         mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1602                         mods->sml_op = LDAP_MOD_DELETE;
1603                         mods->sml_flags = SLAP_MOD_INTERNAL;
1604                         mods->sml_type.bv_val = NULL;
1605                         mods->sml_desc = ad_pwdFailureTime;
1606                         mods->sml_values = NULL;
1607                         mods->sml_nvalues = NULL;
1608                         mods->sml_next = NULL;
1609                         modtail->sml_next = mods;
1610                         modtail = mods;
1611                 }
1612
1613                 /* Delete the pwdReset attribute, since it's being reset */
1614                 if ((zapReset) && (attr_find(e->e_attrs, ad_pwdReset ))) {
1615                         mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1616                         mods->sml_op = LDAP_MOD_DELETE;
1617                         mods->sml_flags = SLAP_MOD_INTERNAL;
1618                         mods->sml_type.bv_val = NULL;
1619                         mods->sml_desc = ad_pwdReset;
1620                         mods->sml_values = NULL;
1621                         mods->sml_nvalues = NULL;
1622                         mods->sml_next = NULL;
1623                         modtail->sml_next = mods;
1624                         modtail = mods;
1625                 }
1626
1627                 if (pp.pwdInHistory > 0) {
1628                         if (hsize >= pp.pwdInHistory) {
1629                                 /*
1630                                  * We use the >= operator, since we are going to add
1631                                  * the existing password attribute value into the
1632                                  * history - thus the cardinality of history values is
1633                                  * about to rise by one.
1634                                  *
1635                                  * If this would push it over the limit of history
1636                                  * values (remembering - the password policy could have
1637                                  * changed since the password was last altered), we must
1638                                  * delete at least 1 value from the pwdHistory list.
1639                                  *
1640                                  * In fact, we delete '(#pwdHistory attrs - max pwd
1641                                  * history length) + 1' values, starting with the oldest.
1642                                  * This is easily evaluated, since the linked list is
1643                                  * created in ascending time order.
1644                                  */
1645                                 mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1646                                 mods->sml_op = LDAP_MOD_DELETE;
1647                                 mods->sml_flags = SLAP_MOD_INTERNAL;
1648                                 mods->sml_type.bv_val = NULL;
1649                                 mods->sml_desc = ad_pwdHistory;
1650                                 mods->sml_nvalues = NULL;
1651                                 mods->sml_values = ch_calloc( sizeof( struct berval ),
1652                                                                                            hsize - pp.pwdInHistory + 2 );
1653                                 mods->sml_values[ hsize - pp.pwdInHistory + 1 ].bv_val = NULL;
1654                                 mods->sml_values[ hsize - pp.pwdInHistory + 1 ].bv_len = 0;
1655                                 for(i=0,p=tl; i < (hsize - pp.pwdInHistory + 1); i++, p=p->next) {
1656                                         mods->sml_values[i].bv_val = NULL;
1657                                         mods->sml_values[i].bv_len = 0;
1658                                         ber_dupbv( &(mods->sml_values[i]), &p->bv );
1659                                 }
1660                                 mods->sml_next = NULL;
1661                                 modtail->sml_next = mods;
1662                                 modtail = mods;
1663                         }
1664                         free_pwd_history_list( &tl );
1665
1666                         /*
1667                          * Now add the existing password into the history list.
1668                          * This will be executed even if the operation is to delete
1669                          * the password entirely.
1670                          *
1671                          * This isn't in the spec explicitly, but it seems to make
1672                          * sense that the password history list is the list of all
1673                          * previous passwords - even if they were deleted. Thus, if
1674                          * someone tries to add a historical password at some future
1675                          * point, it will fail.
1676                          */
1677                         if ((pa = attr_find( e->e_attrs, pp.ad )) != NULL) {
1678                                 mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1679                                 mods->sml_op = LDAP_MOD_ADD;
1680                                 mods->sml_flags = SLAP_MOD_INTERNAL;
1681                                 mods->sml_type.bv_val = NULL;
1682                                 mods->sml_desc = ad_pwdHistory;
1683                                 mods->sml_nvalues = NULL;
1684                                 mods->sml_values = ch_calloc( sizeof( struct berval ), 2 );
1685                                 mods->sml_values[ 1 ].bv_val = NULL;
1686                                 mods->sml_values[ 1 ].bv_len = 0;
1687                                 make_pwd_history_value( timebuf, &mods->sml_values[0], pa );
1688                                 mods->sml_next = NULL;
1689                                 modtail->sml_next = mods;
1690                                 modtail = mods;
1691                         } else {
1692                                 Debug( LDAP_DEBUG_TRACE,
1693                                 "ppolicy_modify: password attr lookup failed\n", 0, 0, 0 );
1694                         }
1695                 }
1696
1697                 /*
1698                  * Controversial bit here. If the new password isn't hashed
1699                  * (ie, is cleartext), we probably should hash it according
1700                  * to the default hash. The reason for this is that we want
1701                  * to use the policy if possible, but if we hash the password
1702                  * before, then we're going to run into trouble when it
1703                  * comes time to check the password.
1704                  *
1705                  * Now, the right thing to do is to use the extended password
1706                  * modify operation, but not all software can do this,
1707                  * therefore it makes sense to hash the new password, now
1708                  * we know it passes the policy requirements.
1709                  *
1710                  * Of course, if the password is already hashed, then we
1711                  * leave it alone.
1712                  */
1713
1714                 if ((pi->hash_passwords) && (addmod) && !newpw.bv_val && 
1715                         (password_scheme( &(addmod->sml_values[0]), NULL ) != LDAP_SUCCESS)) {
1716                         struct berval hpw, bv;
1717                         
1718                         slap_passwd_hash( &(addmod->sml_values[0]), &hpw, &txt );
1719                         if (hpw.bv_val == NULL) {
1720                                         /*
1721                                          * hashing didn't work. Emit an error.
1722                                          */
1723                                 rs->sr_err = LDAP_OTHER;
1724                                 rs->sr_text = txt;
1725                                 goto return_results;
1726                         }
1727                         bv.bv_val = addmod->sml_values[0].bv_val;
1728                         bv.bv_len = addmod->sml_values[0].bv_len;
1729                                 /* clear and discard the clear password */
1730                         memset(bv.bv_val, 0, bv.bv_len);
1731                         ber_memfree(bv.bv_val);
1732                         addmod->sml_values[0].bv_val = hpw.bv_val;
1733                         addmod->sml_values[0].bv_len = hpw.bv_len;
1734                 }
1735         }
1736         op->o_bd->bd_info = (BackendInfo *)on->on_info;
1737         be_entry_release_r( op, e );
1738         return SLAP_CB_CONTINUE;
1739
1740 return_results:
1741         free_pwd_history_list( &tl );
1742         op->o_bd->bd_info = (BackendInfo *)on->on_info;
1743         be_entry_release_r( op, e );
1744         if ( send_ctrl ) {
1745                 LDAPControl **ctrls = NULL;
1746
1747                 ctrls = ch_calloc( sizeof( LDAPControl *) , 2 );
1748                 ctrls[0] = create_passcontrol( -1, -1, pErr );
1749                 ctrls[1] = NULL;
1750                 rs->sr_ctrls = ctrls;
1751         }
1752         send_ldap_result( op, rs );
1753         return rs->sr_err;
1754 }
1755
1756 static int
1757 ppolicy_parseCtrl(
1758         Operation *op,
1759         SlapReply *rs,
1760         LDAPControl *ctrl )
1761 {
1762         if ( ctrl->ldctl_value.bv_len ) {
1763                 rs->sr_text = "passwordPolicyRequest control value not empty";
1764                 return LDAP_PROTOCOL_ERROR;
1765         }
1766         if ( ctrl->ldctl_iscritical ) {
1767                 rs->sr_text = "passwordPolicyRequest control invalid criticality";
1768                 return LDAP_PROTOCOL_ERROR;
1769         }
1770         op->o_ctrlflag[ppolicy_cid] = SLAP_CONTROL_NONCRITICAL;
1771
1772         return LDAP_SUCCESS;
1773 }
1774
1775 static int
1776 attrPretty(
1777         Syntax *syntax,
1778         struct berval *val,
1779         struct berval *out,
1780         void *ctx )
1781 {
1782         AttributeDescription *ad = NULL;
1783         const char *err;
1784         int code;
1785
1786         code = slap_bv2ad( val, &ad, &err );
1787         if ( !code ) {
1788                 ber_dupbv_x( out, &ad->ad_type->sat_cname, ctx );
1789         }
1790         return code;
1791 }
1792
1793 static int
1794 attrNormalize(
1795         slap_mask_t use,
1796         Syntax *syntax,
1797         MatchingRule *mr,
1798         struct berval *val,
1799         struct berval *out,
1800         void *ctx )
1801 {
1802         AttributeDescription *ad = NULL;
1803         const char *err;
1804         int code;
1805
1806         code = slap_bv2ad( val, &ad, &err );
1807         if ( !code ) {
1808                 ber_str2bv_x( ad->ad_type->sat_oid, 0, 1, out, ctx );
1809         }
1810         return code;
1811 }
1812
1813 static int
1814 ppolicy_db_init(
1815         BackendDB *be
1816 )
1817 {
1818         slap_overinst *on = (slap_overinst *) be->bd_info;
1819
1820         /* Has User Schema been initialized yet? */
1821         if ( !pwd_UsSchema[0].ad[0] ) {
1822                 const char *err;
1823                 int i, code;
1824
1825                 for (i=0; pwd_UsSchema[i].def; i++) {
1826                         code = slap_str2ad( pwd_UsSchema[i].def, pwd_UsSchema[i].ad, &err );
1827                         if ( code ) {
1828                                 fprintf( stderr, "User Schema Load failed %d: %s\n", code, err );
1829                                 return code;
1830                         }
1831                 }
1832                 {
1833                         Syntax *syn;
1834                         MatchingRule *mr;
1835
1836                         syn = ch_malloc( sizeof( Syntax ));
1837                         *syn = *ad_pwdAttribute->ad_type->sat_syntax;
1838                         syn->ssyn_pretty = attrPretty;
1839                         ad_pwdAttribute->ad_type->sat_syntax = syn;
1840
1841                         mr = ch_malloc( sizeof( MatchingRule ));
1842                         *mr = *ad_pwdAttribute->ad_type->sat_equality;
1843                         mr->smr_normalize = attrNormalize;
1844                         ad_pwdAttribute->ad_type->sat_equality = mr;
1845                 }
1846         }
1847
1848         on->on_bi.bi_private = ch_calloc( sizeof(pp_info), 1 );
1849
1850         if ( dtblsize && !pwcons )
1851                 pwcons = ch_calloc(sizeof(pw_conn), dtblsize );
1852
1853         return 0;
1854 }
1855
1856 static int
1857 ppolicy_db_open(
1858     BackendDB *be
1859 )
1860 {
1861         return overlay_register_control( be, LDAP_CONTROL_PASSWORDPOLICYREQUEST );
1862 }
1863
1864 static int
1865 ppolicy_close(
1866         BackendDB *be
1867 )
1868 {
1869         slap_overinst *on = (slap_overinst *) be->bd_info;
1870         pp_info *pi = on->on_bi.bi_private;
1871         
1872         free( pwcons );
1873         free( pi->def_policy.bv_val );
1874         free( pi );
1875
1876         return 0;
1877 }
1878
1879 static int
1880 ppolicy_config(
1881     BackendDB   *be,
1882     const char  *fname,
1883     int         lineno,
1884     int         argc,
1885     char        **argv
1886 )
1887 {
1888         slap_overinst *on = (slap_overinst *) be->bd_info;
1889         pp_info *pi = on->on_bi.bi_private;
1890         struct berval dn;
1891         
1892
1893         if ( strcasecmp( argv[0], "ppolicy_default" ) == 0 ) {
1894                 if ( argc != 2 ) {
1895                         fprintf( stderr, "%s: line %d: invalid arguments in \"ppolicy_default"
1896                                 " <policyDN>\n", fname, lineno );
1897                         return ( 1 );
1898                 }
1899                 ber_str2bv( argv[1], 0, 0, &dn );
1900                 if ( dnNormalize( 0, NULL, NULL, &dn, &pi->def_policy, NULL ) ) {
1901                         fprintf( stderr, "%s: line %d: policyDN is invalid\n",
1902                                 fname, lineno );
1903                         return 1;
1904                 }
1905                 return 0;
1906
1907         } else if ( strcasecmp( argv[0], "ppolicy_use_lockout" ) == 0 ) {
1908                 if ( argc != 1 ) {
1909                         fprintf( stderr, "%s: line %d: ppolicy_use_lockout "
1910                                 "takes no arguments\n", fname, lineno );
1911                         return ( 1 );
1912                 }
1913                 pi->use_lockout = 1;
1914                 return 0;
1915         } else if ( strcasecmp( argv[0], "ppolicy_hash_cleartext" ) == 0 ) {
1916                 if ( argc != 1 ) {
1917                         fprintf( stderr, "%s: line %d: ppolicy_hash_cleartext "
1918                                 "takes no arguments\n", fname, lineno );
1919                         return ( 1 );
1920                 }
1921                 pi->hash_passwords = 1;
1922                 return 0;
1923         }
1924         return SLAP_CONF_UNKNOWN;
1925 }
1926
1927 static char *extops[] = {
1928         LDAP_EXOP_MODIFY_PASSWD,
1929         NULL
1930 };
1931
1932 static slap_overinst ppolicy;
1933
1934 int ppolicy_init()
1935 {
1936         LDAPAttributeType *at;
1937         const char *err;
1938         int i, code;
1939
1940         for (i=0; pwd_OpSchema[i].def; i++) {
1941                 at = ldap_str2attributetype( pwd_OpSchema[i].def, &code, &err,
1942                         LDAP_SCHEMA_ALLOW_ALL );
1943                 if ( !at ) {
1944                         fprintf( stderr, "AttributeType Load failed %s %s\n",
1945                                 ldap_scherr2str(code), err );
1946                         return code;
1947                 }
1948                 code = at_add( at, 0, NULL, &err );
1949                 if ( !code ) {
1950                         slap_str2ad( at->at_names[0], pwd_OpSchema[i].ad, &err );
1951                 }
1952                 ldap_memfree( at );
1953                 if ( code ) {
1954                         fprintf( stderr, "AttributeType Load failed %s %s\n",
1955                                 scherr2str(code), err );
1956                         return code;
1957                 }
1958                 /* Allow Manager to set these as needed */
1959                 if ( is_at_no_user_mod( (*pwd_OpSchema[i].ad)->ad_type )) {
1960                         (*pwd_OpSchema[i].ad)->ad_type->sat_flags |=
1961                                 SLAP_AT_MANAGEABLE;
1962                 }
1963         }
1964
1965         code = register_supported_control( LDAP_CONTROL_PASSWORDPOLICYREQUEST,
1966                 SLAP_CTRL_ADD|SLAP_CTRL_BIND|SLAP_CTRL_MODIFY|SLAP_CTRL_HIDE, extops,
1967                 ppolicy_parseCtrl, &ppolicy_cid );
1968         if ( code != LDAP_SUCCESS ) {
1969                 fprintf( stderr, "Failed to register control %d\n", code );
1970                 return code;
1971         }
1972
1973         ldap_pvt_thread_mutex_init( &chk_syntax_mutex );
1974
1975         ppolicy.on_bi.bi_type = "ppolicy";
1976         ppolicy.on_bi.bi_db_init = ppolicy_db_init;
1977         ppolicy.on_bi.bi_db_open = ppolicy_db_open;
1978         ppolicy.on_bi.bi_db_config = ppolicy_config;
1979         ppolicy.on_bi.bi_db_close = ppolicy_close;
1980
1981         ppolicy.on_bi.bi_op_add = ppolicy_add;
1982         ppolicy.on_bi.bi_op_bind = ppolicy_bind;
1983         ppolicy.on_bi.bi_op_compare = ppolicy_restrict;
1984         ppolicy.on_bi.bi_op_delete = ppolicy_restrict;
1985         ppolicy.on_bi.bi_op_modify = ppolicy_modify;
1986         ppolicy.on_bi.bi_op_search = ppolicy_restrict;
1987         ppolicy.on_bi.bi_connection_destroy = ppolicy_connection_destroy;
1988
1989         return overlay_register( &ppolicy );
1990 }
1991
1992 #if SLAPD_OVER_PPOLICY == SLAPD_MOD_DYNAMIC
1993 int init_module(int argc, char *argv[]) {
1994         return ppolicy_init();
1995 }
1996 #endif
1997
1998 #endif  /* defined(SLAPD_OVER_PPOLICY) */