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