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