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