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