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