]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/ppolicy.c
fix wrong response tags (ITS4182; consequence of fixing ITS#4173)
[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                         /*
1107                          * A controversial bit. We hash cleartext
1108                          * passwords provided via add and modify operations
1109                          * You're not really supposed to do this, since
1110                          * the X.500 model says "store attributes" as they
1111                          * get provided. By default, this is what we do
1112                          *
1113                          * But if the hash_passwords flag is set, we hash
1114                          * any cleartext password attribute values via the
1115                          * default password hashing scheme.
1116                          */
1117                 if ((pi->hash_passwords) &&
1118                         (password_scheme( &(pa->a_vals[0]), NULL ) != LDAP_SUCCESS)) {
1119                         struct berval hpw;
1120
1121                         slap_passwd_hash( &(pa->a_vals[0]), &hpw, &txt );
1122                         if (hpw.bv_val == NULL) {
1123                                 /*
1124                                  * hashing didn't work. Emit an error.
1125                                  */
1126                                 rs->sr_err = LDAP_OTHER;
1127                                 rs->sr_text = txt;
1128                                 send_ldap_error( op, rs, LDAP_OTHER, "Password hashing failed" );
1129                                 return rs->sr_err;
1130                         }
1131
1132                         memset( pa->a_vals[0].bv_val, 0, pa->a_vals[0].bv_len);
1133                         ber_memfree( pa->a_vals[0].bv_val );
1134                         pa->a_vals[0].bv_val = hpw.bv_val;
1135                         pa->a_vals[0].bv_len = hpw.bv_len;
1136                 }
1137
1138                 /* If password aging is in effect, set the pwdChangedTime */
1139                 if ( pp.pwdMaxAge || pp.pwdMinAge ) {
1140                         struct berval timestamp;
1141                         char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
1142                         time_t now = slap_get_time();
1143
1144                         timestamp.bv_val = timebuf;
1145                         timestamp.bv_len = sizeof(timebuf);
1146                         slap_timestamp( &now, &timestamp );
1147
1148                         attr_merge_one( op->ora_e, ad_pwdChangedTime, &timestamp, NULL );
1149                 }
1150         }
1151         return SLAP_CB_CONTINUE;
1152 }
1153
1154 static int
1155 ppolicy_modify( Operation *op, SlapReply *rs )
1156 {
1157         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
1158         pp_info                 *pi = on->on_bi.bi_private;
1159         int                     i, rc, mod_pw_only, pwmod, pwmop = -1, deladd,
1160                                 hsize = 0;
1161         PassPolicy              pp;
1162         Modifications           *mods = NULL, *modtail = NULL,
1163                                 *ml, *delmod, *addmod;
1164         Attribute               *pa, *ha, at;
1165         const char              *txt;
1166         pw_hist                 *tl = NULL, *p;
1167         int                     zapReset, send_ctrl = 0;
1168         Entry                   *e;
1169         struct berval           newpw = BER_BVNULL, oldpw = BER_BVNULL,
1170                                 *bv, cr[2];
1171         LDAPPasswordPolicyError pErr = PP_noError;
1172
1173         op->o_bd->bd_info = (BackendInfo *)on->on_info;
1174         rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
1175         op->o_bd->bd_info = (BackendInfo *)on;
1176
1177         if ( rc != LDAP_SUCCESS ) return SLAP_CB_CONTINUE;
1178
1179         /* If this is a replica, we may need to tweak some of the
1180          * master's modifications. Otherwise, just pass it through.
1181          */
1182         if ( be_shadow_update( op )) {
1183                 Modifications **prev;
1184                 int got_del_grace = 0, got_del_lock = 0, got_pw = 0, got_del_fail = 0;
1185                 Attribute *a_grace, *a_lock, *a_fail;
1186
1187                 a_grace = attr_find( e->e_attrs, ad_pwdGraceUseTime );
1188                 a_lock = attr_find( e->e_attrs, ad_pwdAccountLockedTime );
1189                 a_fail = attr_find( e->e_attrs, ad_pwdFailureTime );
1190
1191                 for( prev = &op->oq_modify.rs_modlist, ml = *prev; ml; ml = *prev ) {
1192
1193                         if ( ml->sml_desc == slap_schema.si_ad_userPassword )
1194                                 got_pw = 1;
1195
1196                         /* If we're deleting an attr that didn't exist,
1197                          * drop this delete op
1198                          */
1199                         if ( ml->sml_op == LDAP_MOD_DELETE ) {
1200                                 int drop = 0;
1201
1202                                 if ( ml->sml_desc == ad_pwdGraceUseTime ) {
1203                                         got_del_grace = 1;
1204                                         if ( !a_grace )
1205                                                 drop = 1;
1206                                 } else
1207                                 if ( ml->sml_desc == ad_pwdAccountLockedTime ) {
1208                                         got_del_lock = 1;
1209                                         if ( !a_lock )
1210                                                 drop = 1;
1211                                 } else
1212                                 if ( ml->sml_desc == ad_pwdFailureTime ) {
1213                                         got_del_fail = 1;
1214                                         if ( !a_fail )
1215                                                 drop = 1;
1216                                 }
1217                                 if ( drop ) {
1218                                         *prev = ml->sml_next;
1219                                         ml->sml_next = NULL;
1220                                         slap_mods_free( ml, 1 );
1221                                         continue;
1222                                 }
1223                         }
1224                         prev = &ml->sml_next;
1225                 }
1226
1227                 /* If we're resetting the password, make sure grace, accountlock,
1228                  * and failure also get removed.
1229                  */
1230                 if ( got_pw ) {
1231                         if ( a_grace && !got_del_grace ) {
1232                                 ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
1233                                 ml->sml_op = LDAP_MOD_DELETE;
1234                                 ml->sml_flags = SLAP_MOD_INTERNAL;
1235                                 ml->sml_type.bv_val = NULL;
1236                                 ml->sml_desc = ad_pwdGraceUseTime;
1237                                 ml->sml_values = NULL;
1238                                 ml->sml_nvalues = NULL;
1239                                 ml->sml_next = NULL;
1240                                 *prev = ml;
1241                                 prev = &ml->sml_next;
1242                         }
1243                         if ( a_lock && !got_del_lock ) {
1244                                 ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
1245                                 ml->sml_op = LDAP_MOD_DELETE;
1246                                 ml->sml_flags = SLAP_MOD_INTERNAL;
1247                                 ml->sml_type.bv_val = NULL;
1248                                 ml->sml_desc = ad_pwdAccountLockedTime;
1249                                 ml->sml_values = NULL;
1250                                 ml->sml_nvalues = NULL;
1251                                 ml->sml_next = NULL;
1252                                 *prev = ml;
1253                         }
1254                         if ( a_fail && !got_del_fail ) {
1255                                 ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
1256                                 ml->sml_op = LDAP_MOD_DELETE;
1257                                 ml->sml_flags = SLAP_MOD_INTERNAL;
1258                                 ml->sml_type.bv_val = NULL;
1259                                 ml->sml_desc = ad_pwdFailureTime;
1260                                 ml->sml_values = NULL;
1261                                 ml->sml_nvalues = NULL;
1262                                 ml->sml_next = NULL;
1263                                 *prev = ml;
1264                         }
1265                 }
1266                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1267                 be_entry_release_r( op, e );
1268                 return SLAP_CB_CONTINUE;
1269         }
1270
1271         /* Did we receive a password policy request control? */
1272         if ( op->o_ctrlflag[ppolicy_cid] ) {
1273                 send_ctrl = 1;
1274         }
1275
1276         /* See if this is a pwdModify exop. If so, we can
1277          * access the plaintext passwords from that request.
1278          */
1279         {
1280                 slap_callback *sc;
1281
1282                 for ( sc = op->o_callback; sc; sc=sc->sc_next ) {
1283                         if ( sc->sc_response == slap_replog_cb &&
1284                                 sc->sc_private ) {
1285                                 req_pwdexop_s *qpw = sc->sc_private;
1286                                 newpw = qpw->rs_new;
1287                                 oldpw = qpw->rs_old;
1288                                 break;
1289                         }
1290                 }
1291         }
1292
1293         ppolicy_get( op, e, &pp );
1294
1295         for ( ml = op->oq_modify.rs_modlist,
1296                         pwmod = 0, mod_pw_only = 1,
1297                         deladd = 0, delmod = NULL,
1298                         addmod = NULL,
1299                         zapReset = 1;
1300                 ml != NULL; modtail = ml, ml = ml->sml_next )
1301         {
1302                 if ( ml->sml_desc == pp.ad ) {
1303                         pwmod = 1;
1304                         pwmop = ml->sml_op;
1305                         if ((deladd == 0) && (ml->sml_op == LDAP_MOD_DELETE) &&
1306                                 (ml->sml_values) && (ml->sml_values[0].bv_val != NULL)) {
1307                                 deladd = 1;
1308                                 delmod = ml;
1309                         }
1310
1311                         if ((deladd == 1) && ((ml->sml_op == LDAP_MOD_ADD) ||
1312                                                                   (ml->sml_op == LDAP_MOD_REPLACE)))
1313                                 deladd = 2;
1314
1315                         if ((ml->sml_op == LDAP_MOD_ADD) ||
1316                                 (ml->sml_op == LDAP_MOD_REPLACE))
1317                                 addmod = ml;
1318                 } else if (! is_at_operational( ml->sml_desc->ad_type )) {
1319                         mod_pw_only = 0;
1320                         /* modifying something other than password */
1321                 }
1322
1323                 /*
1324                  * If there is a request to explicitly add a pwdReset
1325                  * attribute, then we suppress the normal behaviour on
1326                  * password change, which is to remove the pwdReset
1327                  * attribute.
1328                  *
1329                  * This enables an administrator to assign a new password
1330                  * and place a "must reset" flag on the entry, which will
1331                  * stay until the user explicitly changes his/her password.
1332                  */
1333                 if (ml->sml_desc == ad_pwdReset ) {
1334                         if ((ml->sml_op == LDAP_MOD_ADD) ||
1335                                 (ml->sml_op == LDAP_MOD_REPLACE))
1336                                 zapReset = 0;
1337                 }
1338         }
1339         
1340         if (pwcons[op->o_conn->c_conn_idx].restricted && !mod_pw_only) {
1341                 Debug( LDAP_DEBUG_TRACE,
1342                         "connection restricted to password changing only\n", 0, 0, 0 );
1343                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 
1344                 rs->sr_text = "Operations are restricted to bind/unbind/abandon/StartTLS/modify password";
1345                 pErr = PP_changeAfterReset;
1346                 goto return_results;
1347         }
1348
1349         /*
1350          * if we have a "safe password modify policy", then we need to check if we're doing
1351          * a delete (with the old password), followed by an add (with the new password).
1352          *
1353          * If we don't have this, then we fail with an error. We also skip all the checks if
1354          * the root user is bound. Root can do anything, including avoid the policies.
1355          */
1356
1357         if (!pwmod) goto do_modify;
1358
1359         /*
1360          * Did we get a valid add mod?
1361          */
1362
1363         if (!addmod) {
1364                 rs->sr_err = LDAP_OTHER;
1365                 rs->sr_text = "Internal Error";
1366                 Debug( LDAP_DEBUG_TRACE,
1367                         "cannot locate modification supplying new password\n", 0, 0, 0 );
1368                 goto return_results;
1369         }
1370
1371         /*
1372          * Build the password history list in ascending time order
1373          * We need this, even if the user is root, in order to maintain
1374          * the pwdHistory operational attributes properly.
1375          */
1376         if (pp.pwdInHistory > 0 && (ha = attr_find( e->e_attrs, ad_pwdHistory ))) {
1377                 struct berval oldpw;
1378                 time_t oldtime;
1379
1380                 for(i=0; ha->a_nvals[i].bv_val; i++) {
1381                         rc = parse_pwdhistory( &(ha->a_nvals[i]), NULL,
1382                                 &oldtime, &oldpw );
1383
1384                         if (rc != LDAP_SUCCESS) continue; /* invalid history entry */
1385
1386                         if (oldpw.bv_val) {
1387                                 add_to_pwd_history( &tl, oldtime, &oldpw,
1388                                         &(ha->a_nvals[i]) );
1389                                 oldpw.bv_val = NULL;
1390                                 oldpw.bv_len = 0;
1391                         }
1392                 }
1393                 for(p=tl; p; p=p->next, hsize++); /* count history size */
1394         }
1395
1396         if (be_isroot( op )) goto do_modify;
1397
1398         /* This is a pwdModify exop that provided the old pw.
1399          * We need to create a Delete mod for this old pw and 
1400          * let the matching value get found later
1401          */
1402         if (pp.pwdSafeModify && oldpw.bv_val ) {
1403                 ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
1404                 ml->sml_op = LDAP_MOD_DELETE;
1405                 ml->sml_flags = SLAP_MOD_INTERNAL;
1406                 ml->sml_desc = pp.ad;
1407                 ml->sml_type = pp.ad->ad_cname;
1408                 ml->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
1409                 ber_dupbv( &ml->sml_values[0], &oldpw );
1410                 ml->sml_values[1].bv_len = 0;
1411                 ml->sml_values[1].bv_val = NULL;
1412                 ml->sml_nvalues = NULL;
1413                 ml->sml_next = op->orm_modlist;
1414                 op->orm_modlist = ml;
1415                 delmod = ml;
1416                 deladd = 2;
1417         }
1418
1419         if (pp.pwdSafeModify && deladd != 2) {
1420                 Debug( LDAP_DEBUG_TRACE,
1421                         "change password must use DELETE followed by ADD/REPLACE\n",
1422                         0, 0, 0 );
1423                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1424                 rs->sr_text = "Must supply old password to be changed as well as new one";
1425                 pErr = PP_mustSupplyOldPassword;
1426                 goto return_results;
1427         }
1428
1429         if (!pp.pwdAllowUserChange) {
1430                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1431                 rs->sr_text = "User alteration of password is not allowed";
1432                 pErr = PP_passwordModNotAllowed;
1433                 goto return_results;
1434         }
1435
1436         if (pp.pwdMinAge > 0) {
1437                 time_t pwtime = (time_t)-1, now;
1438                 int age;
1439
1440                 if ((pa = attr_find( e->e_attrs, ad_pwdChangedTime )) != NULL)
1441                         pwtime = parse_time( pa->a_nvals[0].bv_val );
1442                 now = slap_get_time();
1443                 age = (int)(now - pwtime);
1444                 if ((pwtime != (time_t)-1) && (age < pp.pwdMinAge)) {
1445                         rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
1446                         rs->sr_text = "Password is too young to change";
1447                         pErr = PP_passwordTooYoung;
1448                         goto return_results;
1449                 }
1450         }
1451
1452         /* pa is used in password history check below, be sure it's set */
1453         if ((pa = attr_find( e->e_attrs, pp.ad )) != NULL && delmod) {
1454                 /*
1455                  * we have a password to check
1456                  */
1457                 const char *txt;
1458                 
1459                 bv = oldpw.bv_val ? &oldpw : delmod->sml_values;
1460                 /* FIXME: no access checking? */
1461                 rc = slap_passwd_check( op, NULL, pa, bv, &txt );
1462                 if (rc != LDAP_SUCCESS) {
1463                         Debug( LDAP_DEBUG_TRACE,
1464                                 "old password check failed: %s\n", txt, 0, 0 );
1465                         
1466                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1467                         rs->sr_text = "Must supply correct old password to change to new one";
1468                         pErr = PP_mustSupplyOldPassword;
1469                         goto return_results;
1470
1471                 } else {
1472                         int i;
1473                         
1474                         /*
1475                          * replace the delete value with the (possibly hashed)
1476                          * value which is currently in the password.
1477                          */
1478                         for(i=0; delmod->sml_values[i].bv_val; i++) {
1479                                 free(delmod->sml_values[i].bv_val);
1480                                 delmod->sml_values[i].bv_len = 0;
1481                         }
1482                         free(delmod->sml_values);
1483                         delmod->sml_values = ch_calloc( sizeof(struct berval), 2 );
1484                         delmod->sml_values[1].bv_len = 0;
1485                         delmod->sml_values[1].bv_val = NULL;
1486                         ber_dupbv(&(delmod->sml_values[0]),  &(pa->a_nvals[0]));
1487                 }
1488         }
1489
1490         bv = newpw.bv_val ? &newpw : addmod->sml_values;
1491         if (pp.pwdCheckQuality > 0) {
1492
1493                 rc = check_password_quality( bv, &pp, &pErr, e );
1494                 if (rc != LDAP_SUCCESS) {
1495                         rs->sr_err = rc;
1496                         rs->sr_text = "Password fails quality checking policy";
1497                         goto return_results;
1498                 }
1499         }
1500
1501         if (pa) {
1502                 /*
1503                  * Last check - the password history.
1504                  */
1505                 /* FIXME: no access checking? */
1506                 if (slap_passwd_check( op, NULL, pa, bv, &txt ) == LDAP_SUCCESS) {
1507                         /*
1508                          * This is bad - it means that the user is attempting
1509                          * to set the password to the same as the old one.
1510                          */
1511                         rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
1512                         rs->sr_text = "Password is not being changed from existing value";
1513                         pErr = PP_passwordInHistory;
1514                         goto return_results;
1515                 }
1516         
1517                 if (pp.pwdInHistory < 1) goto do_modify;
1518         
1519                 /*
1520                  * Iterate through the password history, and fail on any
1521                  * password matches.
1522                  */
1523                 at = *pa;
1524                 at.a_vals = cr;
1525                 cr[1].bv_val = NULL;
1526                 for(p=tl; p; p=p->next) {
1527                         cr[0] = p->pw;
1528                         /* FIXME: no access checking? */
1529                         rc = slap_passwd_check( op, NULL, &at, bv, &txt );
1530                         
1531                         if (rc != LDAP_SUCCESS) continue;
1532                         
1533                         rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
1534                         rs->sr_text = "Password is in history of old passwords";
1535                         pErr = PP_passwordInHistory;
1536                         goto return_results;
1537                 }
1538         }
1539
1540 do_modify:
1541         if (pwmod) {
1542                 struct berval timestamp;
1543                 char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
1544                 time_t now = slap_get_time();
1545                 
1546                 /*
1547                  * keep the necessary pwd.. operational attributes
1548                  * up to date.
1549                  */
1550
1551                 timestamp.bv_val = timebuf;
1552                 timestamp.bv_len = sizeof(timebuf);
1553                 slap_timestamp( &now, &timestamp );
1554
1555                 mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1556                 mods->sml_type.bv_val = NULL;
1557                 mods->sml_desc = ad_pwdChangedTime;
1558                 if (pwmop != LDAP_MOD_DELETE) {
1559                         mods->sml_op = LDAP_MOD_REPLACE;
1560                         mods->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
1561                         ber_dupbv( &mods->sml_values[0], &timestamp );
1562                         mods->sml_values[1].bv_len = 0;
1563                         mods->sml_values[1].bv_val = NULL;
1564                         assert( mods->sml_values[0].bv_val != NULL );
1565                 } else {
1566                         mods->sml_op = LDAP_MOD_DELETE;
1567                         mods->sml_values = NULL;
1568                 }
1569                 mods->sml_flags = SLAP_MOD_INTERNAL;
1570                 mods->sml_nvalues = NULL;
1571                 mods->sml_next = NULL;
1572                 modtail->sml_next = mods;
1573                 modtail = mods;
1574
1575                 if (attr_find(e->e_attrs, ad_pwdGraceUseTime )) {
1576                         mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1577                         mods->sml_op = LDAP_MOD_DELETE;
1578                         mods->sml_flags = SLAP_MOD_INTERNAL;
1579                         mods->sml_type.bv_val = NULL;
1580                         mods->sml_desc = ad_pwdGraceUseTime;
1581                         mods->sml_values = NULL;
1582                         mods->sml_nvalues = NULL;
1583                         mods->sml_next = NULL;
1584                         modtail->sml_next = mods;
1585                         modtail = mods;
1586                 }
1587
1588                 if (attr_find(e->e_attrs, ad_pwdAccountLockedTime )) {
1589                         mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1590                         mods->sml_op = LDAP_MOD_DELETE;
1591                         mods->sml_flags = SLAP_MOD_INTERNAL;
1592                         mods->sml_type.bv_val = NULL;
1593                         mods->sml_desc = ad_pwdAccountLockedTime;
1594                         mods->sml_values = NULL;
1595                         mods->sml_nvalues = NULL;
1596                         mods->sml_next = NULL;
1597                         modtail->sml_next = mods;
1598                         modtail = mods;
1599                 }
1600
1601                 if (attr_find(e->e_attrs, ad_pwdFailureTime )) {
1602                         mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1603                         mods->sml_op = LDAP_MOD_DELETE;
1604                         mods->sml_flags = SLAP_MOD_INTERNAL;
1605                         mods->sml_type.bv_val = NULL;
1606                         mods->sml_desc = ad_pwdFailureTime;
1607                         mods->sml_values = NULL;
1608                         mods->sml_nvalues = NULL;
1609                         mods->sml_next = NULL;
1610                         modtail->sml_next = mods;
1611                         modtail = mods;
1612                 }
1613
1614                 /* Delete the pwdReset attribute, since it's being reset */
1615                 if ((zapReset) && (attr_find(e->e_attrs, ad_pwdReset ))) {
1616                         mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1617                         mods->sml_op = LDAP_MOD_DELETE;
1618                         mods->sml_flags = SLAP_MOD_INTERNAL;
1619                         mods->sml_type.bv_val = NULL;
1620                         mods->sml_desc = ad_pwdReset;
1621                         mods->sml_values = NULL;
1622                         mods->sml_nvalues = NULL;
1623                         mods->sml_next = NULL;
1624                         modtail->sml_next = mods;
1625                         modtail = mods;
1626                 }
1627
1628                 if (pp.pwdInHistory > 0) {
1629                         if (hsize >= pp.pwdInHistory) {
1630                                 /*
1631                                  * We use the >= operator, since we are going to add
1632                                  * the existing password attribute value into the
1633                                  * history - thus the cardinality of history values is
1634                                  * about to rise by one.
1635                                  *
1636                                  * If this would push it over the limit of history
1637                                  * values (remembering - the password policy could have
1638                                  * changed since the password was last altered), we must
1639                                  * delete at least 1 value from the pwdHistory list.
1640                                  *
1641                                  * In fact, we delete '(#pwdHistory attrs - max pwd
1642                                  * history length) + 1' values, starting with the oldest.
1643                                  * This is easily evaluated, since the linked list is
1644                                  * created in ascending time order.
1645                                  */
1646                                 mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1647                                 mods->sml_op = LDAP_MOD_DELETE;
1648                                 mods->sml_flags = SLAP_MOD_INTERNAL;
1649                                 mods->sml_type.bv_val = NULL;
1650                                 mods->sml_desc = ad_pwdHistory;
1651                                 mods->sml_nvalues = NULL;
1652                                 mods->sml_values = ch_calloc( sizeof( struct berval ),
1653                                                                                            hsize - pp.pwdInHistory + 2 );
1654                                 mods->sml_values[ hsize - pp.pwdInHistory + 1 ].bv_val = NULL;
1655                                 mods->sml_values[ hsize - pp.pwdInHistory + 1 ].bv_len = 0;
1656                                 for(i=0,p=tl; i < (hsize - pp.pwdInHistory + 1); i++, p=p->next) {
1657                                         mods->sml_values[i].bv_val = NULL;
1658                                         mods->sml_values[i].bv_len = 0;
1659                                         ber_dupbv( &(mods->sml_values[i]), &p->bv );
1660                                 }
1661                                 mods->sml_next = NULL;
1662                                 modtail->sml_next = mods;
1663                                 modtail = mods;
1664                         }
1665                         free_pwd_history_list( &tl );
1666
1667                         /*
1668                          * Now add the existing password into the history list.
1669                          * This will be executed even if the operation is to delete
1670                          * the password entirely.
1671                          *
1672                          * This isn't in the spec explicitly, but it seems to make
1673                          * sense that the password history list is the list of all
1674                          * previous passwords - even if they were deleted. Thus, if
1675                          * someone tries to add a historical password at some future
1676                          * point, it will fail.
1677                          */
1678                         if ((pa = attr_find( e->e_attrs, pp.ad )) != NULL) {
1679                                 mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1680                                 mods->sml_op = LDAP_MOD_ADD;
1681                                 mods->sml_flags = SLAP_MOD_INTERNAL;
1682                                 mods->sml_type.bv_val = NULL;
1683                                 mods->sml_desc = ad_pwdHistory;
1684                                 mods->sml_nvalues = NULL;
1685                                 mods->sml_values = ch_calloc( sizeof( struct berval ), 2 );
1686                                 mods->sml_values[ 1 ].bv_val = NULL;
1687                                 mods->sml_values[ 1 ].bv_len = 0;
1688                                 make_pwd_history_value( timebuf, &mods->sml_values[0], pa );
1689                                 mods->sml_next = NULL;
1690                                 modtail->sml_next = mods;
1691                                 modtail = mods;
1692                         } else {
1693                                 Debug( LDAP_DEBUG_TRACE,
1694                                 "ppolicy_modify: password attr lookup failed\n", 0, 0, 0 );
1695                         }
1696                 }
1697
1698                 /*
1699                  * Controversial bit here. If the new password isn't hashed
1700                  * (ie, is cleartext), we probably should hash it according
1701                  * to the default hash. The reason for this is that we want
1702                  * to use the policy if possible, but if we hash the password
1703                  * before, then we're going to run into trouble when it
1704                  * comes time to check the password.
1705                  *
1706                  * Now, the right thing to do is to use the extended password
1707                  * modify operation, but not all software can do this,
1708                  * therefore it makes sense to hash the new password, now
1709                  * we know it passes the policy requirements.
1710                  *
1711                  * Of course, if the password is already hashed, then we
1712                  * leave it alone.
1713                  */
1714
1715                 if ((pi->hash_passwords) && (addmod) && !newpw.bv_val && 
1716                         (password_scheme( &(addmod->sml_values[0]), NULL ) != LDAP_SUCCESS)) {
1717                         struct berval hpw, bv;
1718                         
1719                         slap_passwd_hash( &(addmod->sml_values[0]), &hpw, &txt );
1720                         if (hpw.bv_val == NULL) {
1721                                         /*
1722                                          * hashing didn't work. Emit an error.
1723                                          */
1724                                 rs->sr_err = LDAP_OTHER;
1725                                 rs->sr_text = txt;
1726                                 goto return_results;
1727                         }
1728                         bv.bv_val = addmod->sml_values[0].bv_val;
1729                         bv.bv_len = addmod->sml_values[0].bv_len;
1730                                 /* clear and discard the clear password */
1731                         memset(bv.bv_val, 0, bv.bv_len);
1732                         ber_memfree(bv.bv_val);
1733                         addmod->sml_values[0].bv_val = hpw.bv_val;
1734                         addmod->sml_values[0].bv_len = hpw.bv_len;
1735                 }
1736         }
1737         op->o_bd->bd_info = (BackendInfo *)on->on_info;
1738         be_entry_release_r( op, e );
1739         return SLAP_CB_CONTINUE;
1740
1741 return_results:
1742         free_pwd_history_list( &tl );
1743         op->o_bd->bd_info = (BackendInfo *)on->on_info;
1744         be_entry_release_r( op, e );
1745         if ( send_ctrl ) {
1746                 LDAPControl **ctrls = NULL;
1747
1748                 ctrls = ch_calloc( sizeof( LDAPControl *) , 2 );
1749                 ctrls[0] = create_passcontrol( -1, -1, pErr );
1750                 ctrls[1] = NULL;
1751                 rs->sr_ctrls = ctrls;
1752         }
1753         send_ldap_result( op, rs );
1754         return rs->sr_err;
1755 }
1756
1757 static int
1758 ppolicy_parseCtrl(
1759         Operation *op,
1760         SlapReply *rs,
1761         LDAPControl *ctrl )
1762 {
1763         if ( ctrl->ldctl_value.bv_len ) {
1764                 rs->sr_text = "passwordPolicyRequest control value not empty";
1765                 return LDAP_PROTOCOL_ERROR;
1766         }
1767         if ( ctrl->ldctl_iscritical ) {
1768                 rs->sr_text = "passwordPolicyRequest control invalid criticality";
1769                 return LDAP_PROTOCOL_ERROR;
1770         }
1771         op->o_ctrlflag[ppolicy_cid] = SLAP_CONTROL_NONCRITICAL;
1772
1773         return LDAP_SUCCESS;
1774 }
1775
1776 static int
1777 attrPretty(
1778         Syntax *syntax,
1779         struct berval *val,
1780         struct berval *out,
1781         void *ctx )
1782 {
1783         AttributeDescription *ad = NULL;
1784         const char *err;
1785         int code;
1786
1787         code = slap_bv2ad( val, &ad, &err );
1788         if ( !code ) {
1789                 ber_dupbv_x( out, &ad->ad_type->sat_cname, ctx );
1790         }
1791         return code;
1792 }
1793
1794 static int
1795 attrNormalize(
1796         slap_mask_t use,
1797         Syntax *syntax,
1798         MatchingRule *mr,
1799         struct berval *val,
1800         struct berval *out,
1801         void *ctx )
1802 {
1803         AttributeDescription *ad = NULL;
1804         const char *err;
1805         int code;
1806
1807         code = slap_bv2ad( val, &ad, &err );
1808         if ( !code ) {
1809                 ber_str2bv_x( ad->ad_type->sat_oid, 0, 1, out, ctx );
1810         }
1811         return code;
1812 }
1813
1814 static int
1815 ppolicy_db_init(
1816         BackendDB *be
1817 )
1818 {
1819         slap_overinst *on = (slap_overinst *) be->bd_info;
1820
1821         /* Has User Schema been initialized yet? */
1822         if ( !pwd_UsSchema[0].ad[0] ) {
1823                 const char *err;
1824                 int i, code;
1825
1826                 for (i=0; pwd_UsSchema[i].def; i++) {
1827                         code = slap_str2ad( pwd_UsSchema[i].def, pwd_UsSchema[i].ad, &err );
1828                         if ( code ) {
1829                                 fprintf( stderr, "User Schema Load failed %d: %s\n", code, err );
1830                                 return code;
1831                         }
1832                 }
1833                 {
1834                         Syntax *syn;
1835                         MatchingRule *mr;
1836
1837                         syn = ch_malloc( sizeof( Syntax ));
1838                         *syn = *ad_pwdAttribute->ad_type->sat_syntax;
1839                         syn->ssyn_pretty = attrPretty;
1840                         ad_pwdAttribute->ad_type->sat_syntax = syn;
1841
1842                         mr = ch_malloc( sizeof( MatchingRule ));
1843                         *mr = *ad_pwdAttribute->ad_type->sat_equality;
1844                         mr->smr_normalize = attrNormalize;
1845                         ad_pwdAttribute->ad_type->sat_equality = mr;
1846                 }
1847         }
1848
1849         on->on_bi.bi_private = ch_calloc( sizeof(pp_info), 1 );
1850
1851         if ( dtblsize && !pwcons )
1852                 pwcons = ch_calloc(sizeof(pw_conn), dtblsize );
1853
1854         return 0;
1855 }
1856
1857 static int
1858 ppolicy_db_open(
1859     BackendDB *be
1860 )
1861 {
1862         return overlay_register_control( be, LDAP_CONTROL_PASSWORDPOLICYREQUEST );
1863 }
1864
1865 static int
1866 ppolicy_close(
1867         BackendDB *be
1868 )
1869 {
1870         slap_overinst *on = (slap_overinst *) be->bd_info;
1871         pp_info *pi = on->on_bi.bi_private;
1872         
1873         free( pwcons );
1874         free( pi->def_policy.bv_val );
1875         free( pi );
1876
1877         return 0;
1878 }
1879
1880 static int
1881 ppolicy_config(
1882     BackendDB   *be,
1883     const char  *fname,
1884     int         lineno,
1885     int         argc,
1886     char        **argv
1887 )
1888 {
1889         slap_overinst *on = (slap_overinst *) be->bd_info;
1890         pp_info *pi = on->on_bi.bi_private;
1891         struct berval dn;
1892         
1893
1894         if ( strcasecmp( argv[0], "ppolicy_default" ) == 0 ) {
1895                 if ( argc != 2 ) {
1896                         fprintf( stderr, "%s: line %d: invalid arguments in \"ppolicy_default"
1897                                 " <policyDN>\n", fname, lineno );
1898                         return ( 1 );
1899                 }
1900                 ber_str2bv( argv[1], 0, 0, &dn );
1901                 if ( dnNormalize( 0, NULL, NULL, &dn, &pi->def_policy, NULL ) ) {
1902                         fprintf( stderr, "%s: line %d: policyDN is invalid\n",
1903                                 fname, lineno );
1904                         return 1;
1905                 }
1906                 return 0;
1907
1908         } else if ( strcasecmp( argv[0], "ppolicy_use_lockout" ) == 0 ) {
1909                 if ( argc != 1 ) {
1910                         fprintf( stderr, "%s: line %d: ppolicy_use_lockout "
1911                                 "takes no arguments\n", fname, lineno );
1912                         return ( 1 );
1913                 }
1914                 pi->use_lockout = 1;
1915                 return 0;
1916         } else if ( strcasecmp( argv[0], "ppolicy_hash_cleartext" ) == 0 ) {
1917                 if ( argc != 1 ) {
1918                         fprintf( stderr, "%s: line %d: ppolicy_hash_cleartext "
1919                                 "takes no arguments\n", fname, lineno );
1920                         return ( 1 );
1921                 }
1922                 pi->hash_passwords = 1;
1923                 return 0;
1924         }
1925         return SLAP_CONF_UNKNOWN;
1926 }
1927
1928 static char *extops[] = {
1929         LDAP_EXOP_MODIFY_PASSWD,
1930         NULL
1931 };
1932
1933 static slap_overinst ppolicy;
1934
1935 int ppolicy_init()
1936 {
1937         LDAPAttributeType *at;
1938         const char *err;
1939         int i, code;
1940
1941         for (i=0; pwd_OpSchema[i].def; i++) {
1942                 at = ldap_str2attributetype( pwd_OpSchema[i].def, &code, &err,
1943                         LDAP_SCHEMA_ALLOW_ALL );
1944                 if ( !at ) {
1945                         fprintf( stderr, "AttributeType Load failed %s %s\n",
1946                                 ldap_scherr2str(code), err );
1947                         return code;
1948                 }
1949                 code = at_add( at, 0, NULL, &err );
1950                 if ( !code ) {
1951                         slap_str2ad( at->at_names[0], pwd_OpSchema[i].ad, &err );
1952                 }
1953                 ldap_memfree( at );
1954                 if ( code ) {
1955                         fprintf( stderr, "AttributeType Load failed %s %s\n",
1956                                 scherr2str(code), err );
1957                         return code;
1958                 }
1959                 /* Allow Manager to set these as needed */
1960                 if ( is_at_no_user_mod( (*pwd_OpSchema[i].ad)->ad_type )) {
1961                         (*pwd_OpSchema[i].ad)->ad_type->sat_flags |=
1962                                 SLAP_AT_MANAGEABLE;
1963                 }
1964         }
1965
1966         code = register_supported_control( LDAP_CONTROL_PASSWORDPOLICYREQUEST,
1967                 SLAP_CTRL_ADD|SLAP_CTRL_BIND|SLAP_CTRL_MODIFY|SLAP_CTRL_HIDE, extops,
1968                 ppolicy_parseCtrl, &ppolicy_cid );
1969         if ( code != LDAP_SUCCESS ) {
1970                 fprintf( stderr, "Failed to register control %d\n", code );
1971                 return code;
1972         }
1973
1974         ldap_pvt_thread_mutex_init( &chk_syntax_mutex );
1975
1976         ppolicy.on_bi.bi_type = "ppolicy";
1977         ppolicy.on_bi.bi_db_init = ppolicy_db_init;
1978         ppolicy.on_bi.bi_db_open = ppolicy_db_open;
1979         ppolicy.on_bi.bi_db_config = ppolicy_config;
1980         ppolicy.on_bi.bi_db_close = ppolicy_close;
1981
1982         ppolicy.on_bi.bi_op_add = ppolicy_add;
1983         ppolicy.on_bi.bi_op_bind = ppolicy_bind;
1984         ppolicy.on_bi.bi_op_compare = ppolicy_restrict;
1985         ppolicy.on_bi.bi_op_delete = ppolicy_restrict;
1986         ppolicy.on_bi.bi_op_modify = ppolicy_modify;
1987         ppolicy.on_bi.bi_op_search = ppolicy_restrict;
1988         ppolicy.on_bi.bi_connection_destroy = ppolicy_connection_destroy;
1989
1990         return overlay_register( &ppolicy );
1991 }
1992
1993 #if SLAPD_OVER_PPOLICY == SLAPD_MOD_DYNAMIC
1994 int init_module(int argc, char *argv[]) {
1995         return ppolicy_init();
1996 }
1997 #endif
1998
1999 #endif  /* defined(SLAPD_OVER_PPOLICY) */