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