]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/ppolicy.c
fix ITS#3788: don't free slapo-rwm callback, rather reuse it
[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 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-08
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 USAGE directoryOperation NO-USER-MODIFICATION )",
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 USAGE directoryOperation )",
123                 &ad_pwdAccountLockedTime },
124         {       "( 1.3.6.1.4.1.42.2.27.8.1.19 "
125                 "NAME ( 'pwdFailureTime' ) "
126                 "DESC 'The timestamps of the last consecutive authentication failures' "
127                 "EQUALITY generalizedTimeMatch "
128                 "ORDERING generalizedTimeOrderingMatch "
129                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
130                 "USAGE directoryOperation )",
131                 &ad_pwdFailureTime },
132         {       "( 1.3.6.1.4.1.42.2.27.8.1.20 "
133                 "NAME ( 'pwdHistory' ) "
134                 "DESC 'The history of users passwords' "
135                 "EQUALITY octetStringMatch "
136                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 "
137                 "USAGE directoryOperation NO-USER-MODIFICATION )",
138                 &ad_pwdHistory },
139         {       "( 1.3.6.1.4.1.42.2.27.8.1.21 "
140                 "NAME ( 'pwdGraceUseTime' ) "
141                 "DESC 'The timestamps of the grace login once the password has expired' "
142                 "EQUALITY generalizedTimeMatch "
143                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
144                 "USAGE directoryOperation NO-USER-MODIFICATION )",
145                 &ad_pwdGraceUseTime }, 
146         {       "( 1.3.6.1.4.1.42.2.27.8.1.22 "
147                 "NAME ( 'pwdReset' ) "
148                 "DESC 'The indication that the password has been reset' "
149                 "EQUALITY booleanMatch "
150                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
151                 "SINGLE-VALUE USAGE directoryOperation )",
152                 &ad_pwdReset },
153         {       "( 1.3.6.1.4.1.42.2.27.8.1.23 "
154                 "NAME ( 'pwdPolicySubentry' ) "
155                 "DESC 'The pwdPolicy subentry in effect for this object' "
156                 "EQUALITY distinguishedNameMatch "
157                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 "
158                 "SINGLE-VALUE USAGE directoryOperation )",
159                 &ad_pwdPolicySubentry },
160         { NULL, NULL }
161 };
162
163 /* User attributes */
164 static AttributeDescription *ad_pwdMinAge, *ad_pwdMaxAge, *ad_pwdInHistory,
165         *ad_pwdCheckQuality, *ad_pwdMinLength, *ad_pwdMaxFailure, 
166         *ad_pwdGraceAuthNLimit, *ad_pwdExpireWarning, *ad_pwdLockoutDuration,
167         *ad_pwdFailureCountInterval, *ad_pwdCheckModule, *ad_pwdLockout,
168         *ad_pwdMustChange, *ad_pwdAllowUserChange, *ad_pwdSafeModify,
169         *ad_pwdAttribute;
170
171 #define TAB(name)       { #name, &ad_##name }
172
173 static struct schema_info pwd_UsSchema[] = {
174         TAB(pwdAttribute),
175         TAB(pwdMinAge),
176         TAB(pwdMaxAge),
177         TAB(pwdInHistory),
178         TAB(pwdCheckQuality),
179         TAB(pwdMinLength),
180         TAB(pwdMaxFailure),
181         TAB(pwdGraceAuthNLimit),
182         TAB(pwdExpireWarning),
183         TAB(pwdLockout),
184         TAB(pwdLockoutDuration),
185         TAB(pwdFailureCountInterval),
186         TAB(pwdCheckModule),
187         TAB(pwdMustChange),
188         TAB(pwdAllowUserChange),
189         TAB(pwdSafeModify),
190         { NULL, NULL }
191 };
192
193 static ldap_pvt_thread_mutex_t chk_syntax_mutex;
194
195 static time_t
196 parse_time( char *atm )
197 {
198         struct lutil_tm tm;
199         struct lutil_timet tt;
200         time_t ret = (time_t)-1;
201
202         if ( lutil_parsetime( atm, &tm ) == 0) {
203                 lutil_tm2time( &tm, &tt );
204                 ret = tt.tt_sec;
205         }
206         return ret;
207 }
208
209 static int
210 account_locked( Operation *op, Entry *e,
211                 PassPolicy *pp, Modifications **mod ) 
212 {
213         Attribute       *la;
214         int rc;
215         Entry *de;
216
217         assert(mod);
218
219         if ( (la = attr_find( e->e_attrs, ad_pwdAccountLockedTime )) != NULL ) {
220                 BerVarray vals = la->a_nvals;
221
222                 /*
223                  * there is a lockout stamp - we now need to know if it's
224                  * a valid one.
225                  */
226                 if (vals[0].bv_val != NULL) {
227                         time_t then, now;
228                         struct berval bv;
229                         Modifications *m;
230
231                         if (!pp->pwdLockoutDuration)
232                                 return 1;
233
234                         if ((then = parse_time( vals[0].bv_val )) == (time_t)0)
235                                 return 1;
236
237                         now = slap_get_time();
238
239                         if (now < then + pp->pwdLockoutDuration)
240                                 return 1;
241
242                         m = ch_calloc( sizeof(Modifications), 1 );
243                         m->sml_op = LDAP_MOD_DELETE;
244                         m->sml_flags = 0;
245                         m->sml_type = ad_pwdAccountLockedTime->ad_cname;
246                         m->sml_desc = ad_pwdAccountLockedTime;
247                         m->sml_next = *mod;
248                         *mod = m;
249                 }
250         }
251
252         return 0;
253 }
254
255 #define PPOLICY_WARNING 0xa0L
256 #define PPOLICY_ERROR 0xa1L
257  
258 #define PPOLICY_EXPIRE 0xa0L
259 #define PPOLICY_GRACE  0xa1L
260
261 static LDAPControl *
262 create_passcontrol( int exptime, int grace, LDAPPasswordPolicyError err )
263 {
264         char berbuf[LBER_ELEMENT_SIZEOF], bb2[LBER_ELEMENT_SIZEOF];
265         BerElement *ber = (BerElement *)berbuf, *b2 = (BerElement *)bb2;
266         LDAPControl *c;
267         struct berval bv;
268
269         if ((c = ch_calloc( sizeof( LDAPControl ), 1 )) == NULL) return NULL;
270         c->ldctl_oid = LDAP_CONTROL_PASSWORDPOLICYRESPONSE;
271         c->ldctl_iscritical = 0;
272         c->ldctl_value.bv_val = NULL;
273         c->ldctl_value.bv_len = 0;
274
275         ber_init2( ber, NULL, LBER_USE_DER );
276         ber_printf(ber, "{" /*}*/ );
277
278         if (exptime >= 0) {
279                 ber_init2( b2, NULL, LBER_USE_DER );
280                 ber_printf( b2, "ti", PPOLICY_EXPIRE, exptime );
281                 ber_flatten2( b2, &bv, 1 );
282                 ber_printf( ber, "tO", PPOLICY_WARNING, &bv );
283                 ch_free( bv.bv_val );
284         } else if (grace > 0) {
285                 ber_init2( b2, NULL, LBER_USE_DER );
286                 ber_printf( b2, "ti", PPOLICY_GRACE, grace );
287                 ber_flatten2( b2, &bv, 1 );
288                 ber_printf( ber, "tO", PPOLICY_WARNING, &bv );
289                 ch_free( bv.bv_val );
290         }
291
292         if (err != PP_noError ) {
293                 ber_printf( ber, "te", PPOLICY_ERROR, err );
294         }
295         ber_printf( ber, /*{*/ "N}" );
296
297         if (ber_flatten2( ber, &(c->ldctl_value), 1 ) == LBER_DEFAULT) {
298                 ch_free(c);
299                 (void)ber_free_buf(ber);
300                 return NULL;
301         }
302         (void)ber_free_buf(ber);
303         return c;
304 }
305
306 static void
307 ppolicy_get( Operation *op, Entry *e, PassPolicy *pp )
308 {
309         slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
310         pp_info *pi = on->on_bi.bi_private;
311         Attribute *a;
312         BerVarray vals;
313         int i, rc, nent;
314         Entry *pe = NULL;
315         AttributeDescription *oca = slap_schema.si_ad_objectClass;
316         const char *text;
317         AttributeDescription *ad;
318         struct berval bv;
319
320         memset( pp, 0, sizeof(PassPolicy) );
321
322         /* Users can change their own password by default */
323         pp->pwdAllowUserChange = 1;
324
325         if ((a = attr_find( e->e_attrs, ad_pwdPolicySubentry )) == NULL) {
326                 /*
327                  * entry has no password policy assigned - use default
328                  */
329                 vals = &pi->def_policy;
330                 if ( !vals->bv_val )
331                         goto defaultpol;
332         } else {
333                 vals = a->a_nvals;
334                 if (vals[0].bv_val == NULL) {
335                         Debug( LDAP_DEBUG_ANY,
336                                 "ppolicy_get: NULL value for policySubEntry\n", 0, 0, 0 );
337                         goto defaultpol;
338                 }
339         }
340
341         op->o_bd->bd_info = (BackendInfo *)on->on_info;
342         rc = be_entry_get_rw( op, vals, NULL, NULL, 0, &pe );
343         op->o_bd->bd_info = (BackendInfo *)on;
344
345         if ( rc ) goto defaultpol;
346
347 #if 0   /* Only worry about userPassword for now */
348         if ((a = attr_find( pe->e_attrs, ad_pwdAttribute )))
349                 slap_bv2ad( &a->a_vals[0], &pp->ad, &text );
350 #else
351         pp->ad = slap_schema.si_ad_userPassword;
352 #endif
353
354         if ((a = attr_find( pe->e_attrs, ad_pwdMinAge )))
355                 pp->pwdMinAge = atoi(a->a_vals[0].bv_val );
356         if ((a = attr_find( pe->e_attrs, ad_pwdMaxAge )))
357                 pp->pwdMaxAge = atoi(a->a_vals[0].bv_val );
358         if ((a = attr_find( pe->e_attrs, ad_pwdInHistory )))
359                 pp->pwdInHistory = atoi(a->a_vals[0].bv_val );
360         if ((a = attr_find( pe->e_attrs, ad_pwdCheckQuality )))
361                 pp->pwdCheckQuality = atoi(a->a_vals[0].bv_val );
362         if ((a = attr_find( pe->e_attrs, ad_pwdMinLength )))
363                 pp->pwdMinLength = atoi(a->a_vals[0].bv_val );
364         if ((a = attr_find( pe->e_attrs, ad_pwdMaxFailure )))
365                 pp->pwdMaxFailure = atoi(a->a_vals[0].bv_val );
366         if ((a = attr_find( pe->e_attrs, ad_pwdGraceAuthNLimit )))
367                 pp->pwdGraceAuthNLimit = atoi(a->a_vals[0].bv_val );
368         if ((a = attr_find( pe->e_attrs, ad_pwdExpireWarning )))
369                 pp->pwdExpireWarning = atoi(a->a_vals[0].bv_val );
370         if ((a = attr_find( pe->e_attrs, ad_pwdFailureCountInterval )))
371                 pp->pwdFailureCountInterval = atoi(a->a_vals[0].bv_val );
372         if ((a = attr_find( pe->e_attrs, ad_pwdLockoutDuration )))
373                 pp->pwdLockoutDuration = atoi(a->a_vals[0].bv_val );
374
375         if ((a = attr_find( pe->e_attrs, ad_pwdCheckModule ))) {
376                 strncpy(pp->pwdCheckModule, a->a_vals[0].bv_val,
377                         sizeof(pp->pwdCheckModule));
378                 pp->pwdCheckModule[sizeof(pp->pwdCheckModule)-1] = '\0';
379         }
380
381         if ((a = attr_find( pe->e_attrs, ad_pwdLockout )))
382         pp->pwdLockout = !strcmp( a->a_nvals[0].bv_val, "TRUE" );
383         if ((a = attr_find( pe->e_attrs, ad_pwdMustChange )))
384         pp->pwdMustChange = !strcmp( a->a_nvals[0].bv_val, "TRUE" );
385         if ((a = attr_find( pe->e_attrs, ad_pwdAllowUserChange )))
386         pp->pwdAllowUserChange = !strcmp( a->a_nvals[0].bv_val, "TRUE" );
387         if ((a = attr_find( pe->e_attrs, ad_pwdSafeModify )))
388         pp->pwdSafeModify = !strcmp( a->a_nvals[0].bv_val, "TRUE" );
389     
390         op->o_bd->bd_info = (BackendInfo *)on->on_info;
391         be_entry_release_r( op, pe );
392         op->o_bd->bd_info = (BackendInfo *)on;
393
394         return;
395
396 defaultpol:
397         Debug( LDAP_DEBUG_ANY,
398                 "ppolicy_get: using default policy\n", 0, 0, 0 );
399         return;
400 }
401
402 static int
403 password_scheme( struct berval *cred, struct berval *sch )
404 {
405         int e;
406     
407         assert( cred != NULL );
408
409         if (sch) {
410                 sch->bv_val = NULL;
411                 sch->bv_len = 0;
412         }
413     
414         if ((cred->bv_len == 0) || (cred->bv_val == NULL) ||
415                 (cred->bv_val[0] != '{')) return LDAP_OTHER;
416
417         for(e = 1; cred->bv_val[e] && cred->bv_val[e] != '}'; e++);
418         if (cred->bv_val[e]) {
419                 int rc;
420                 char *sc = ch_calloc( sizeof(char), e + 2);
421                 sc[e + 1] = '\0'; /* terminate string */
422                 strncpy( sc, cred->bv_val, e + 1);
423                 rc = lutil_passwd_scheme( sc );
424                 free( sc );
425                 if (rc && sch) {
426                         sch->bv_val = cred->bv_val;
427                         sch->bv_len = e;
428                         return LDAP_SUCCESS;
429                 }
430         }
431         return LDAP_OTHER;
432 }
433
434 static int
435 check_password_quality( struct berval *cred, PassPolicy *pp, LDAPPasswordPolicyError *err, Entry *e )
436 {
437         int rc = LDAP_SUCCESS, ok = LDAP_SUCCESS;
438         char *ptr = cred->bv_val;
439         char *modpath;
440         struct berval sch;
441
442         assert( cred != NULL );
443         assert( pp != NULL );
444
445         if ((cred->bv_len == 0) || (pp->pwdMinLength > cred->bv_len)) {
446                 rc = LDAP_CONSTRAINT_VIOLATION;
447                 if ( err ) *err = PP_passwordTooShort;
448                 return rc;
449         }
450
451         /*
452          * We need to know if the password is already hashed - if so
453          * what scheme is it. The reason being that the "hash" of
454          * {cleartext} still allows us to check the password.
455          */
456         rc = password_scheme( cred, &sch );
457         if (rc == LDAP_SUCCESS) {
458                 if ((sch.bv_val) && (strncasecmp( sch.bv_val, "{cleartext}",
459                         sch.bv_len ) == 0)) {
460                         /*
461                          * We can check the cleartext "hash"
462                          */
463                         ptr = cred->bv_val + sch.bv_len;
464                 } else {
465                         /* everything else, we can't check */
466                         if (pp->pwdCheckQuality == 2) {
467                                 rc = LDAP_CONSTRAINT_VIOLATION;
468                                 if (err) *err = PP_insufficientPasswordQuality;
469                                 return rc;
470                         }
471                         /*
472                          * We can't check the syntax of the password, but it's not
473                          * mandatory (according to the policy), so we return success.
474                          */
475                     
476                         return LDAP_SUCCESS;
477                 }
478         }
479
480         rc = LDAP_SUCCESS;
481
482         if (pp->pwdCheckModule[0]) {
483 #if SLAPD_MODULES
484                 lt_dlhandle mod;
485                 const char *err;
486                 
487                 if ((mod = lt_dlopen( pp->pwdCheckModule )) == NULL) {
488                         err = lt_dlerror();
489
490                         Debug(LDAP_DEBUG_ANY,
491                         "check_password_quality: lt_dlopen failed: (%s) %s.\n",
492                                 pp->pwdCheckModule, err, 0 );
493                         ok = LDAP_OTHER; /* internal error */
494                 } else {
495                         int (*prog)( char *passwd, char **text, Entry *ent );
496
497                         if ((prog = lt_dlsym( mod, "check_password" )) == NULL) {
498                                 err = lt_dlerror();
499                             
500                                 Debug(LDAP_DEBUG_ANY,
501                                         "check_password_quality: lt_dlsym failed: (%s) %s.\n",
502                                         pp->pwdCheckModule, err, 0 );
503                                 ok = LDAP_OTHER;
504                         } else {
505                                 char *txt = NULL;
506
507                                 ldap_pvt_thread_mutex_lock( &chk_syntax_mutex );
508                                 ok = prog( cred->bv_val, &txt, e );
509                                 ldap_pvt_thread_mutex_unlock( &chk_syntax_mutex );
510                                 if (txt) {
511                                         Debug(LDAP_DEBUG_ANY,
512                                                 "check_password_quality: module error: (%s) %s.[%d]\n",
513                                                 pp->pwdCheckModule, txt, ok );
514                                         free(txt);
515                                 } else
516                                         ok = LDAP_SUCCESS;
517                         }
518                             
519                         lt_dlclose( mod );
520                 }
521 #else
522         Debug(LDAP_DEBUG_ANY, "check_password_quality: external modules not "
523                 "supported. pwdCheckModule ignored.\n", 0, 0, 0);
524 #endif /* SLAPD_MODULES */
525         }
526                 
527                     
528         if (ok != LDAP_SUCCESS) {
529                 rc = LDAP_CONSTRAINT_VIOLATION;
530                 if (err) *err = PP_insufficientPasswordQuality;
531         }
532         
533         return rc;
534 }
535
536 static int
537 parse_pwdhistory( struct berval *bv, char **oid, time_t *oldtime, struct berval *oldpw )
538 {
539         char *ptr;
540         struct berval nv, npw;
541         int i, j;
542         
543         assert (bv && (bv->bv_len > 0) && (bv->bv_val) && oldtime && oldpw );
544
545         if ( oid ) *oid = 0;
546         *oldtime = (time_t)-1;
547         oldpw->bv_val = NULL;
548         oldpw->bv_len = 0;
549         
550         ber_dupbv( &nv, bv );
551
552         /* first get the time field */
553         for(i=0; (i < nv.bv_len) && (nv.bv_val[i] != '#'); i++);
554         if ( i == nv.bv_len) goto exit_failure; /* couldn't locate the '#' separator */
555         nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */
556         ptr = nv.bv_val;
557         *oldtime = parse_time( ptr );
558         if (*oldtime == (time_t)-1) goto exit_failure;
559
560         /* get the OID field */
561         for(ptr = &(nv.bv_val[i]);(i < nv.bv_len) && (nv.bv_val[i] != '#'); i++);
562         if ( i == nv.bv_len) goto exit_failure; /* couldn't locate the '#' separator */
563         nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */
564         if ( oid ) *oid = ber_strdup( ptr );
565         
566         /* get the length field */
567         for(ptr = &(nv.bv_val[i]);(i < nv.bv_len) && (nv.bv_val[i] != '#'); i++);
568         if ( i == nv.bv_len) goto exit_failure; /* couldn't locate the '#' separator */
569         nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */
570         oldpw->bv_len = strtol( ptr, NULL, 10 );
571         if (errno == ERANGE) goto exit_failure;
572
573         /* lastly, get the octets of the string */
574         for(j=i, ptr = &(nv.bv_val[i]);i < nv.bv_len; i++);
575         if (i-j != oldpw->bv_len) goto exit_failure; /* length is wrong */
576
577         npw.bv_val = ptr;
578         npw.bv_len = oldpw->bv_len;
579         ber_dupbv( oldpw, &npw );
580         
581         return LDAP_SUCCESS;
582 exit_failure:
583         if (oid && *oid) { ber_memfree(*oid); *oid = NULL; }
584         if (oldpw->bv_val) {
585                 ber_memfree( oldpw->bv_val); oldpw->bv_val = NULL;
586                 oldpw->bv_len = 0;
587         }
588         ber_memfree(nv.bv_val);
589         return LDAP_OTHER;
590 }
591
592 static void
593 add_to_pwd_history( pw_hist **l, time_t t,
594                     struct berval *oldpw, struct berval *bv )
595 {
596         pw_hist *p, *p1, *p2;
597     
598         if (!l) return;
599
600         p = ch_malloc( sizeof( pw_hist ));
601         p->pw = *oldpw;
602         ber_dupbv( &p->bv, bv );
603         p->t = t;
604         p->next = NULL;
605         
606         if (*l == NULL) {
607                 /* degenerate case */
608                 *l = p;
609                 return;
610         }
611         /*
612          * advance p1 and p2 such that p1 is the node before the
613          * new one, and p2 is the node after it
614          */
615         for (p1 = NULL, p2 = *l; p2 && p2->t <= t; p1 = p2, p2=p2->next );
616         p->next = p2;
617         if (p1 == NULL) { *l = p; return; }
618         p1->next = p;
619 }
620
621 #ifndef MAX_PWD_HISTORY_SZ
622 #define MAX_PWD_HISTORY_SZ 1024
623 #endif /* MAX_PWD_HISTORY_SZ */
624
625 static void
626 make_pwd_history_value( char *timebuf, struct berval *bv, Attribute *pa )
627 {
628         char str[ MAX_PWD_HISTORY_SZ ];
629         int nlen;
630
631         snprintf( str, MAX_PWD_HISTORY_SZ,
632                   "%s#%s#%d#", timebuf,
633                   pa->a_desc->ad_type->sat_syntax->ssyn_oid,
634                   pa->a_nvals[0].bv_len );
635         str[MAX_PWD_HISTORY_SZ-1] = 0;
636         nlen = strlen(str);
637
638         /*
639          * We have to assume that the string is a string of octets,
640          * not readable characters. In reality, yes, it probably is
641          * a readable (ie, base64) string, but we can't count on that
642          * Hence, while the first 3 fields of the password history
643          * are definitely readable (a timestamp, an OID and an integer
644          * length), the remaining octets of the actual password
645          * are deemed to be binary data.
646          */
647         AC_MEMCPY( str + nlen, pa->a_nvals[0].bv_val, pa->a_nvals[0].bv_len );
648         nlen += pa->a_nvals[0].bv_len;
649         bv->bv_val = ch_malloc( nlen + 1 );
650         AC_MEMCPY( bv->bv_val, str, nlen );
651         bv->bv_val[nlen] = '\0';
652         bv->bv_len = nlen;
653 }
654
655 static void
656 free_pwd_history_list( pw_hist **l )
657 {
658         pw_hist *p;
659     
660         if (!l) return;
661         p = *l;
662         while (p) {
663                 pw_hist *pp = p->next;
664
665                 free(p->pw.bv_val);
666                 free(p->bv.bv_val);
667                 free(p);
668                 p = pp;
669         }
670         *l = NULL;
671 }
672
673 typedef struct ppbind {
674         slap_overinst *on;
675         int send_ctrl;
676         Modifications *mod;
677         LDAPPasswordPolicyError pErr;
678         PassPolicy pp;
679 } ppbind;
680
681 static int
682 ppolicy_bind_resp( Operation *op, SlapReply *rs )
683 {
684         ppbind *ppb = op->o_callback->sc_private;
685         slap_overinst *on = ppb->on;
686         Modifications *mod = ppb->mod, *m;
687         int pwExpired = 0;
688         int ngut = -1, warn = -1, age, rc, i;
689         Attribute *a;
690         time_t now, then, pwtime = (time_t)-1;
691         const char *txt;
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 );
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         /* Root bypasses policy */
958         if ( !be_isroot_dn( op->o_bd, &op->o_req_ndn )) {
959                 Entry *e;
960                 int i, rc;
961                 ppbind *ppb;
962                 slap_callback *cb;
963
964                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
965                 rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
966
967                 if ( rc != LDAP_SUCCESS ) {
968                         return SLAP_CB_CONTINUE;
969                 }
970
971                 cb = op->o_tmpcalloc( sizeof(ppbind)+sizeof(slap_callback),
972                         1, op->o_tmpmemctx );
973                 ppb = (ppbind *)(cb+1);
974                 ppb->on = on;
975                 ppb->pErr = PP_noError;
976
977                 /* Setup a callback so we can munge the result */
978
979                 cb->sc_response = ppolicy_bind_resp;
980                 cb->sc_next = op->o_callback->sc_next;
981                 cb->sc_private = ppb;
982                 op->o_callback->sc_next = cb;
983
984                 /* Did we receive a password policy request control? */
985                 if ( op->o_ctrlflag[ppolicy_cid] ) {
986                         ppb->send_ctrl = 1;
987                 }
988
989                 op->o_bd->bd_info = (BackendInfo *)on;
990                 ppolicy_get( op, e, &ppb->pp );
991
992                 rc = account_locked( op, e, &ppb->pp, &ppb->mod );
993
994                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
995                 be_entry_release_r( op, e );
996
997                 if ( rc ) {
998                         /* This will be the Draft 8 response, Unwilling is bogus */
999                         ppb->pErr = PP_accountLocked;
1000                         send_ldap_error( op, rs, LDAP_INVALID_CREDENTIALS, NULL );
1001                         return rs->sr_err;
1002                 }
1003
1004         }
1005
1006         return SLAP_CB_CONTINUE;
1007 }
1008
1009 /* Reset the restricted flag for the next session on this connection */
1010 static int
1011 ppolicy_unbind( Operation *op, SlapReply *rs )
1012 {
1013         pwcons[op->o_conn->c_conn_idx].restricted = 0;
1014         return SLAP_CB_CONTINUE;
1015 }
1016
1017 /* Check if this connection is restricted */
1018 static int
1019 ppolicy_restrict(
1020         Operation *op,
1021         SlapReply *rs )
1022 {
1023         slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1024         int i, send_ctrl = 0;
1025
1026         /* Did we receive a password policy request control? */
1027         if ( op->o_ctrlflag[ppolicy_cid] ) {
1028                 send_ctrl = 1;
1029         }
1030
1031         if ( op->o_conn && pwcons[op->o_conn->c_conn_idx].restricted ) {
1032                 Debug( LDAP_DEBUG_TRACE,
1033                         "connection restricted to password changing only\n", 0, 0, 0);
1034                 if ( send_ctrl ) {
1035                         LDAPControl **ctrls = NULL;
1036
1037                         ctrls = ch_calloc( sizeof( LDAPControl *) , 2 );
1038                         ctrls[0] = create_passcontrol( -1, -1, PP_changeAfterReset );
1039                         ctrls[1] = NULL;
1040                         rs->sr_ctrls = ctrls;
1041                 }
1042                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1043                 send_ldap_error( op, rs, LDAP_INSUFFICIENT_ACCESS, 
1044                         "Operations are restricted to bind/unbind/abandon/StartTLS/modify password" );
1045                 return rs->sr_err;
1046         }
1047
1048         return SLAP_CB_CONTINUE;
1049 }
1050
1051 static int
1052 ppolicy_add(
1053         Operation *op,
1054         SlapReply *rs )
1055 {
1056         slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1057         pp_info *pi = on->on_bi.bi_private;
1058         PassPolicy pp;
1059         int pw;
1060         Attribute *pa;
1061         const char *txt;
1062
1063         if ( ppolicy_restrict( op, rs ) != SLAP_CB_CONTINUE )
1064                 return rs->sr_err;
1065
1066         /* Check for password in entry */
1067         if ((pa = attr_find( op->oq_add.rs_e->e_attrs,
1068                 slap_schema.si_ad_userPassword )))
1069         {
1070                 /*
1071                  * new entry contains a password - if we're not the root user
1072                  * then we need to check that the password fits in with the
1073                  * security policy for the new entry.
1074                  */
1075                 ppolicy_get( op, op->ora_e, &pp );
1076                 if (pp.pwdCheckQuality > 0 && !be_isroot( op )) {
1077                         struct berval *bv = &(pa->a_vals[0]);
1078                         int rc, i, send_ctrl = 0; 
1079                         LDAPPasswordPolicyError pErr = PP_noError;
1080
1081                         /* Did we receive a password policy request control? */
1082                         if ( op->o_ctrlflag[ppolicy_cid] ) {
1083                                 send_ctrl = 1;
1084                         }
1085                         rc = check_password_quality( bv, &pp, &pErr, op->ora_e );
1086                         if (rc != LDAP_SUCCESS) {
1087                                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1088                                 if ( send_ctrl ) {
1089                                         LDAPControl **ctrls = NULL;
1090
1091                                         ctrls = ch_calloc( sizeof( LDAPControl *) , 2 );
1092                                         ctrls[0] = create_passcontrol( -1, -1, pErr );
1093                                         ctrls[1] = NULL;
1094                                         rs->sr_ctrls = ctrls;
1095                                 }
1096                                 send_ldap_error( op, rs, rc, "Password fails quality checking policy" );
1097                                 return rs->sr_err;
1098                         }
1099                             /*
1100                              * A controversial bit. We hash cleartext
1101                              * passwords provided via add and modify operations
1102                              * You're not really supposed to do this, since
1103                              * the X.500 model says "store attributes" as they
1104                              * get provided. By default, this is what we do
1105                              *
1106                              * But if the hash_passwords flag is set, we hash
1107                              * any cleartext password attribute values via the
1108                              * default password hashing scheme.
1109                              */
1110                         if ((pi->hash_passwords) &&
1111                                 (password_scheme( &(pa->a_vals[0]), NULL ) != LDAP_SUCCESS)) {
1112                                 struct berval hpw;
1113
1114                                 slap_passwd_hash( &(pa->a_vals[0]), &hpw, &txt );
1115                                 if (hpw.bv_val == NULL) {
1116                                     /*
1117                                      * hashing didn't work. Emit an error.
1118                                      */
1119                                         rs->sr_err = LDAP_OTHER;
1120                                         rs->sr_text = txt;
1121                                         send_ldap_error( op, rs, LDAP_OTHER, "Password hashing failed" );
1122                                         return rs->sr_err;
1123                                 }
1124
1125                                 memset( pa->a_vals[0].bv_val, 0, pa->a_vals[0].bv_len);
1126                                 ber_memfree( pa->a_vals[0].bv_val );
1127                                 pa->a_vals[0].bv_val = hpw.bv_val;
1128                                 pa->a_vals[0].bv_len = hpw.bv_len;
1129                         }
1130                 }
1131                 /* If password aging is in effect, set the pwdChangedTime */
1132                 if (( pp.pwdMaxAge || pp.pwdMinAge ) && !be_shadow_update( op )) {
1133                         struct berval timestamp;
1134                         char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
1135                         time_t now = slap_get_time();
1136
1137                         timestamp.bv_val = timebuf;
1138                         timestamp.bv_len = sizeof(timebuf);
1139                         slap_timestamp( &now, &timestamp );
1140
1141                         attr_merge_one( op->ora_e, ad_pwdChangedTime, &timestamp, NULL );
1142                 }
1143         }
1144         return SLAP_CB_CONTINUE;
1145 }
1146
1147 static int
1148 ppolicy_modify( Operation *op, SlapReply *rs )
1149 {
1150         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
1151         pp_info                 *pi = on->on_bi.bi_private;
1152         int                     i, rc, mod_pw_only, pwmod, pwmop, deladd,
1153                                 hsize = 0;
1154         PassPolicy              pp;
1155         Modifications           *mods = NULL, *modtail, *ml, *delmod, *addmod;
1156         Attribute               *pa, *ha, *ra, at;
1157         const char              *txt;
1158         pw_hist                 *tl = NULL, *p;
1159         int                     zapReset, send_ctrl = 0;
1160         Entry                   *e;
1161         struct berval           newpw = BER_BVNULL, oldpw = BER_BVNULL,
1162                                 *bv, cr[2];
1163         LDAPPasswordPolicyError pErr = PP_noError;
1164
1165         op->o_bd->bd_info = (BackendInfo *)on->on_info;
1166         rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
1167         op->o_bd->bd_info = (BackendInfo *)on;
1168
1169         if ( rc != LDAP_SUCCESS ) return SLAP_CB_CONTINUE;
1170
1171         /* Did we receive a password policy request control? */
1172         if ( op->o_ctrlflag[ppolicy_cid] ) {
1173                 send_ctrl = 1;
1174         }
1175
1176         /* See if this is a pwdModify exop. If so, we can
1177          * access the plaintext passwords from that request.
1178          */
1179         {
1180                 slap_callback *sc;
1181
1182                 for ( sc = op->o_callback; sc; sc=sc->sc_next ) {
1183                         if ( sc->sc_response == slap_replog_cb &&
1184                                 sc->sc_private ) {
1185                                 req_pwdexop_s *qpw = sc->sc_private;
1186                                 newpw = qpw->rs_new;
1187                                 oldpw = qpw->rs_old;
1188                                 break;
1189                         }
1190                 }
1191         }
1192
1193         ppolicy_get( op, e, &pp );
1194
1195         for(ml = op->oq_modify.rs_modlist,
1196                         pwmod = 0, mod_pw_only = 1,
1197                         deladd = 0, delmod = NULL,
1198                         addmod = NULL,
1199                         zapReset = 1;
1200                 ml != NULL; modtail = ml, ml = ml->sml_next ) {
1201                 if ( ml->sml_desc == pp.ad ) {
1202                         pwmod = 1;
1203                         pwmop = ml->sml_op;
1204                         if ((deladd == 0) && (ml->sml_op == LDAP_MOD_DELETE) &&
1205                                 (ml->sml_values) && (ml->sml_values[0].bv_val != NULL)) {
1206                                 deladd = 1;
1207                                 delmod = ml;
1208                         }
1209
1210                         if ((deladd == 1) && ((ml->sml_op == LDAP_MOD_ADD) ||
1211                                                                   (ml->sml_op == LDAP_MOD_REPLACE)))
1212                                 deladd = 2;
1213
1214                         if ((ml->sml_op == LDAP_MOD_ADD) ||
1215                                 (ml->sml_op == LDAP_MOD_REPLACE))
1216                                 addmod = ml;
1217                 } else if (! is_at_operational( ml->sml_desc->ad_type )) {
1218                         mod_pw_only = 0;
1219                         /* modifying something other than password */
1220                 }
1221
1222                 /*
1223                  * If there is a request to explicitly add a pwdReset
1224                  * attribute, then we suppress the normal behaviour on
1225                  * password change, which is to remove the pwdReset
1226                  * attribute.
1227                  *
1228                  * This enables an administrator to assign a new password
1229                  * and place a "must reset" flag on the entry, which will
1230                  * stay until the user explicitly changes his/her password.
1231                  */
1232                 if (ml->sml_desc == ad_pwdReset ) {
1233                         if ((ml->sml_op == LDAP_MOD_ADD) ||
1234                                 (ml->sml_op == LDAP_MOD_REPLACE))
1235                                 zapReset = 0;
1236                 }
1237         }
1238         
1239         if (pwcons[op->o_conn->c_conn_idx].restricted && !mod_pw_only) {
1240                 Debug( LDAP_DEBUG_TRACE,
1241                         "connection restricted to password changing only\n", 0, 0, 0 );
1242                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 
1243                 rs->sr_text = "Operations are restricted to bind/unbind/abandon/StartTLS/modify password";
1244                 pErr = PP_changeAfterReset;
1245                 goto return_results;
1246         }
1247
1248         /*
1249          * if we have a "safe password modify policy", then we need to check if we're doing
1250          * a delete (with the old password), followed by an add (with the new password).
1251          *
1252          * If we don't have this, then we fail with an error. We also skip all the checks if
1253          * the root user is bound. Root can do anything, including avoid the policies.
1254          */
1255
1256         if (!pwmod) goto do_modify;
1257
1258         /*
1259          * Did we get a valid add mod?
1260          */
1261
1262         if (!addmod) {
1263                 rs->sr_err = LDAP_OTHER;
1264                 rs->sr_text = "Internal Error";
1265                 Debug( LDAP_DEBUG_TRACE,
1266                         "cannot locate modification supplying new password\n", 0, 0, 0 );
1267                 goto return_results;
1268         }
1269
1270         /*
1271          * Build the password history list in ascending time order
1272          * We need this, even if the user is root, in order to maintain
1273          * the pwdHistory operational attributes properly.
1274          */
1275         if (pp.pwdInHistory > 0 && (ha = attr_find( e->e_attrs, ad_pwdHistory ))) {
1276                 struct berval oldpw;
1277                 time_t oldtime;
1278                 char *oid;
1279
1280                 for(i=0; ha->a_nvals[i].bv_val; i++) {
1281                         rc = parse_pwdhistory( &(ha->a_nvals[i]), NULL,
1282                                 &oldtime, &oldpw );
1283
1284                         if (rc != LDAP_SUCCESS) continue; /* invalid history entry */
1285
1286                         if (oldpw.bv_val) {
1287                                 add_to_pwd_history( &tl, oldtime, &oldpw,
1288                                         &(ha->a_nvals[i]) );
1289                                 oldpw.bv_val = NULL;
1290                                 oldpw.bv_len = 0;
1291                         }
1292                 }
1293                 for(p=tl; p; p=p->next, hsize++); /* count history size */
1294         }
1295
1296         if (be_isroot( op )) goto do_modify;
1297
1298         /* This is a pwdModify exop that provided the old pw.
1299          * We need to create a Delete mod for this old pw and 
1300          * let the matching value get found later
1301          */
1302         if (pp.pwdSafeModify && oldpw.bv_val ) {
1303                 ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
1304                 ml->sml_op = LDAP_MOD_DELETE;
1305                 ml->sml_flags = SLAP_MOD_INTERNAL;
1306                 ml->sml_desc = pp.ad;
1307                 ml->sml_type = pp.ad->ad_cname;
1308                 ml->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
1309                 ber_dupbv( &ml->sml_values[0], &oldpw );
1310                 ml->sml_values[1].bv_len = 0;
1311                 ml->sml_values[1].bv_val = NULL;
1312                 ml->sml_nvalues = NULL;
1313                 ml->sml_next = op->orm_modlist;
1314                 op->orm_modlist = ml;
1315                 delmod = ml;
1316                 deladd = 2;
1317         }
1318
1319         if (pp.pwdSafeModify && deladd != 2) {
1320                 Debug( LDAP_DEBUG_TRACE,
1321                         "change password must use DELETE followed by ADD/REPLACE\n",
1322                         0, 0, 0 );
1323                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1324                 rs->sr_text = "Must supply old password to be changed as well as new one";
1325                 pErr = PP_mustSupplyOldPassword;
1326                 goto return_results;
1327         }
1328
1329         if (!pp.pwdAllowUserChange) {
1330                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1331                 rs->sr_text = "User alteration of password is not allowed";
1332                 pErr = PP_passwordModNotAllowed;
1333                 goto return_results;
1334         }
1335
1336         if (pp.pwdMinAge > 0) {
1337                 time_t pwtime = (time_t)-1, now;
1338                 int age;
1339
1340                 if ((pa = attr_find( e->e_attrs, ad_pwdChangedTime )) != NULL)
1341                         pwtime = parse_time( pa->a_nvals[0].bv_val );
1342                 now = slap_get_time();
1343                 age = (int)(now - pwtime);
1344                 if ((pwtime != (time_t)-1) && (age < pp.pwdMinAge)) {
1345                         rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
1346                         rs->sr_text = "Password is too young to change";
1347                         pErr = PP_passwordTooYoung;
1348                         goto return_results;
1349                 }
1350         }
1351
1352         /* pa is used in password history check below, be sure it's set */
1353         if ((pa = attr_find( e->e_attrs, pp.ad )) != NULL && delmod) {
1354                 /*
1355                  * we have a password to check
1356                  */
1357                 const char *txt;
1358                 
1359                 bv = oldpw.bv_val ? &oldpw : delmod->sml_values;
1360                 /* FIXME: no access checking? */
1361                 rc = slap_passwd_check( op, NULL, pa, bv, &txt );
1362                 if (rc != LDAP_SUCCESS) {
1363                         Debug( LDAP_DEBUG_TRACE,
1364                                 "old password check failed: %s\n", txt, 0, 0 );
1365                         
1366                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1367                         rs->sr_text = "Must supply correct old password to change to new one";
1368                         pErr = PP_mustSupplyOldPassword;
1369                         goto return_results;
1370
1371                 } else {
1372                         int i;
1373                         
1374                         /*
1375                          * replace the delete value with the (possibly hashed)
1376                          * value which is currently in the password.
1377                          */
1378                         for(i=0; delmod->sml_values[i].bv_val; i++) {
1379                                 free(delmod->sml_values[i].bv_val);
1380                                 delmod->sml_values[i].bv_len = 0;
1381                         }
1382                         free(delmod->sml_values);
1383                         delmod->sml_values = ch_calloc( sizeof(struct berval), 2 );
1384                         delmod->sml_values[1].bv_len = 0;
1385                         delmod->sml_values[1].bv_val = NULL;
1386                         ber_dupbv(&(delmod->sml_values[0]),  &(pa->a_nvals[0]));
1387                 }
1388         }
1389
1390         bv = newpw.bv_val ? &newpw : addmod->sml_values;
1391         if (pp.pwdCheckQuality > 0) {
1392
1393                 rc = check_password_quality( bv, &pp, &pErr, e );
1394                 if (rc != LDAP_SUCCESS) {
1395                         rs->sr_err = rc;
1396                         rs->sr_text = "Password fails quality checking policy";
1397                         goto return_results;
1398                 }
1399         }
1400
1401         if (pa) {
1402                 /*
1403                  * Last check - the password history.
1404                  */
1405                 /* FIXME: no access checking? */
1406                 if (slap_passwd_check( op, NULL, pa, bv, &txt ) == LDAP_SUCCESS) {
1407                         /*
1408                          * This is bad - it means that the user is attempting
1409                          * to set the password to the same as the old one.
1410                          */
1411                         rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
1412                         rs->sr_text = "Password is not being changed from existing value";
1413                         pErr = PP_passwordInHistory;
1414                         goto return_results;
1415                 }
1416         
1417                 if (pp.pwdInHistory < 1) goto do_modify;
1418         
1419                 /*
1420                  * Iterate through the password history, and fail on any
1421                  * password matches.
1422                  */
1423                 at = *pa;
1424                 at.a_vals = cr;
1425                 cr[1].bv_val = NULL;
1426                 for(p=tl; p; p=p->next) {
1427                         cr[0] = p->pw;
1428                         /* FIXME: no access checking? */
1429                         rc = slap_passwd_check( op, NULL, &at, bv, &txt );
1430                         
1431                         if (rc != LDAP_SUCCESS) continue;
1432                         
1433                         rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
1434                         rs->sr_text = "Password is in history of old passwords";
1435                         pErr = PP_passwordInHistory;
1436                         goto return_results;
1437                 }
1438         }
1439
1440 do_modify:
1441         if ((pwmod) && (!be_shadow_update( op ))) {
1442                 struct berval timestamp;
1443                 char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
1444                 time_t now = slap_get_time();
1445                 Attribute *ga;
1446                 
1447                 /*
1448                  * keep the necessary pwd.. operational attributes
1449                  * up to date.
1450                  */
1451
1452                 timestamp.bv_val = timebuf;
1453                 timestamp.bv_len = sizeof(timebuf);
1454                 slap_timestamp( &now, &timestamp );
1455
1456                 mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1457                 mods->sml_type.bv_val = NULL;
1458                 mods->sml_desc = ad_pwdChangedTime;
1459                 if (pwmop != LDAP_MOD_DELETE) {
1460                         mods->sml_op = LDAP_MOD_REPLACE;
1461                         mods->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
1462                         ber_dupbv( &mods->sml_values[0], &timestamp );
1463                         mods->sml_values[1].bv_len = 0;
1464                         mods->sml_values[1].bv_val = NULL;
1465                         assert( mods->sml_values[0].bv_val );
1466                 } else {
1467                         mods->sml_op = LDAP_MOD_DELETE;
1468                         mods->sml_values = NULL;
1469                 }
1470                 mods->sml_flags = SLAP_MOD_INTERNAL;
1471                 mods->sml_nvalues = NULL;
1472                 mods->sml_next = NULL;
1473                 modtail->sml_next = mods;
1474                 modtail = mods;
1475
1476                 if (attr_find(e->e_attrs, ad_pwdGraceUseTime )) {
1477                         mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1478                         mods->sml_op = LDAP_MOD_DELETE;
1479                         mods->sml_flags = SLAP_MOD_INTERNAL;
1480                         mods->sml_type.bv_val = NULL;
1481                         mods->sml_desc = ad_pwdGraceUseTime;
1482                         mods->sml_values = NULL;
1483                         mods->sml_nvalues = NULL;
1484                         mods->sml_next = NULL;
1485                         modtail->sml_next = mods;
1486                         modtail = mods;
1487                 }
1488
1489                 /* Delete the pwdReset attribute, since it's being reset */
1490                 if ((zapReset) && (attr_find(e->e_attrs, ad_pwdReset ))) {
1491                         mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1492                         mods->sml_op = LDAP_MOD_DELETE;
1493                         mods->sml_flags = SLAP_MOD_INTERNAL;
1494                         mods->sml_type.bv_val = NULL;
1495                         mods->sml_desc = ad_pwdReset;
1496                         mods->sml_values = NULL;
1497                         mods->sml_nvalues = NULL;
1498                         mods->sml_next = NULL;
1499                         modtail->sml_next = mods;
1500                         modtail = mods;
1501                 }
1502
1503                 if (pp.pwdInHistory > 0) {
1504                         if (hsize >= pp.pwdInHistory) {
1505                                 /*
1506                                  * We use the >= operator, since we are going to add
1507                                  * the existing password attribute value into the
1508                                  * history - thus the cardinality of history values is
1509                                  * about to rise by one.
1510                                  *
1511                                  * If this would push it over the limit of history
1512                                  * values (remembering - the password policy could have
1513                                  * changed since the password was last altered), we must
1514                                  * delete at least 1 value from the pwdHistory list.
1515                                  *
1516                                  * In fact, we delete '(#pwdHistory attrs - max pwd
1517                                  * history length) + 1' values, starting with the oldest.
1518                                  * This is easily evaluated, since the linked list is
1519                                  * created in ascending time order.
1520                                  */
1521                                 mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1522                                 mods->sml_op = LDAP_MOD_DELETE;
1523                                 mods->sml_flags = SLAP_MOD_INTERNAL;
1524                                 mods->sml_type.bv_val = NULL;
1525                                 mods->sml_desc = ad_pwdHistory;
1526                                 mods->sml_nvalues = NULL;
1527                                 mods->sml_values = ch_calloc( sizeof( struct berval ),
1528                                                                                            hsize - pp.pwdInHistory + 2 );
1529                                 mods->sml_values[ hsize - pp.pwdInHistory + 1 ].bv_val = NULL;
1530                                 mods->sml_values[ hsize - pp.pwdInHistory + 1 ].bv_len = 0;
1531                                 for(i=0,p=tl; i < (hsize - pp.pwdInHistory + 1); i++, p=p->next) {
1532                                         mods->sml_values[i].bv_val = NULL;
1533                                         mods->sml_values[i].bv_len = 0;
1534                                         ber_dupbv( &(mods->sml_values[i]), &p->bv );
1535                                 }
1536                                 mods->sml_next = NULL;
1537                                 modtail->sml_next = mods;
1538                                 modtail = mods;
1539                         }
1540                         free_pwd_history_list( &tl );
1541
1542                         /*
1543                          * Now add the existing password into the history list.
1544                          * This will be executed even if the operation is to delete
1545                          * the password entirely.
1546                          *
1547                          * This isn't in the spec explicitly, but it seems to make
1548                          * sense that the password history list is the list of all
1549                          * previous passwords - even if they were deleted. Thus, if
1550                          * someone tries to add a historical password at some future
1551                          * point, it will fail.
1552                          */
1553                         if ((pa = attr_find( e->e_attrs, pp.ad )) != NULL) {
1554                                 mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1555                                 mods->sml_op = LDAP_MOD_ADD;
1556                                 mods->sml_flags = SLAP_MOD_INTERNAL;
1557                                 mods->sml_type.bv_val = NULL;
1558                                 mods->sml_desc = ad_pwdHistory;
1559                                 mods->sml_nvalues = NULL;
1560                                 mods->sml_values = ch_calloc( sizeof( struct berval ), 2 );
1561                                 mods->sml_values[ 1 ].bv_val = NULL;
1562                                 mods->sml_values[ 1 ].bv_len = 0;
1563                                 make_pwd_history_value( timebuf, &mods->sml_values[0], pa );
1564                                 mods->sml_next = NULL;
1565                                 modtail->sml_next = mods;
1566                                 modtail = mods;
1567                         } else {
1568                                 Debug( LDAP_DEBUG_TRACE,
1569                                 "ppolicy_modify: password attr lookup failed\n", 0, 0, 0 );
1570                         }
1571                 }
1572
1573                 /*
1574                  * Controversial bit here. If the new password isn't hashed
1575                  * (ie, is cleartext), we probably should hash it according
1576                  * to the default hash. The reason for this is that we want
1577                  * to use the policy if possible, but if we hash the password
1578                  * before, then we're going to run into trouble when it
1579                  * comes time to check the password.
1580                  *
1581                  * Now, the right thing to do is to use the extended password
1582                  * modify operation, but not all software can do this,
1583                  * therefore it makes sense to hash the new password, now
1584                  * we know it passes the policy requirements.
1585                  *
1586                  * Of course, if the password is already hashed, then we
1587                  * leave it alone.
1588                  */
1589
1590                 if ((pi->hash_passwords) && (addmod) && !newpw.bv_val && 
1591                         (password_scheme( &(addmod->sml_values[0]), NULL ) != LDAP_SUCCESS)) {
1592                         struct berval hpw, bv;
1593                         
1594                         slap_passwd_hash( &(addmod->sml_values[0]), &hpw, &txt );
1595                         if (hpw.bv_val == NULL) {
1596                                         /*
1597                                          * hashing didn't work. Emit an error.
1598                                          */
1599                                 rs->sr_err = LDAP_OTHER;
1600                                 rs->sr_text = txt;
1601                                 goto return_results;
1602                         }
1603                         bv.bv_val = addmod->sml_values[0].bv_val;
1604                         bv.bv_len = addmod->sml_values[0].bv_len;
1605                                 /* clear and discard the clear password */
1606                         memset(bv.bv_val, 0, bv.bv_len);
1607                         ber_memfree(bv.bv_val);
1608                         addmod->sml_values[0].bv_val = hpw.bv_val;
1609                         addmod->sml_values[0].bv_len = hpw.bv_len;
1610                 }
1611         }
1612         op->o_bd->bd_info = (BackendInfo *)on->on_info;
1613         be_entry_release_r( op, e );
1614         return SLAP_CB_CONTINUE;
1615
1616 return_results:
1617         free_pwd_history_list( &tl );
1618         op->o_bd->bd_info = (BackendInfo *)on->on_info;
1619         be_entry_release_r( op, e );
1620         if ( send_ctrl ) {
1621                 LDAPControl **ctrls = NULL;
1622
1623                 ctrls = ch_calloc( sizeof( LDAPControl *) , 2 );
1624                 ctrls[0] = create_passcontrol( -1, -1, pErr );
1625                 ctrls[1] = NULL;
1626                 rs->sr_ctrls = ctrls;
1627         }
1628         send_ldap_result( op, rs );
1629         return rs->sr_err;
1630 }
1631
1632 static int
1633 ppolicy_parseCtrl(
1634         Operation *op,
1635         SlapReply *rs,
1636         LDAPControl *ctrl )
1637 {
1638         if ( ctrl->ldctl_value.bv_len ) {
1639                 rs->sr_text = "passwordPolicyRequest control value not empty";
1640                 return LDAP_PROTOCOL_ERROR;
1641         }
1642         if ( ctrl->ldctl_iscritical ) {
1643                 rs->sr_text = "passwordPolicyRequest control invalid criticality";
1644                 return LDAP_PROTOCOL_ERROR;
1645         }
1646         op->o_ctrlflag[ppolicy_cid] = SLAP_CONTROL_NONCRITICAL;
1647
1648         return LDAP_SUCCESS;
1649 }
1650
1651 static int
1652 ppolicy_db_init(
1653         BackendDB *be
1654 )
1655 {
1656         slap_overinst *on = (slap_overinst *) be->bd_info;
1657
1658         /* Has User Schema been initialized yet? */
1659         if ( !pwd_UsSchema[0].ad[0] ) {
1660                 const char *err;
1661                 int i, code;
1662
1663                 for (i=0; pwd_UsSchema[i].def; i++) {
1664                         code = slap_str2ad( pwd_UsSchema[i].def, pwd_UsSchema[i].ad, &err );
1665                         if ( code ) {
1666                                 fprintf( stderr, "User Schema Load failed %d: %s\n", code, err );
1667                                 return code;
1668                         }
1669                 }
1670         }
1671
1672         on->on_bi.bi_private = ch_calloc( sizeof(pp_info), 1 );
1673
1674         if ( dtblsize && !pwcons )
1675                 pwcons = ch_calloc(sizeof(pw_conn), dtblsize );
1676
1677         return 0;
1678 }
1679
1680 static int
1681 ppolicy_db_open(
1682     BackendDB *be
1683 )
1684 {
1685         return overlay_register_control( be, LDAP_CONTROL_PASSWORDPOLICYREQUEST );
1686 }
1687
1688 static int
1689 ppolicy_close(
1690         BackendDB *be
1691 )
1692 {
1693         slap_overinst *on = (slap_overinst *) be->bd_info;
1694         pp_info *pi = on->on_bi.bi_private;
1695         
1696         free( pwcons );
1697         free( pi->def_policy.bv_val );
1698         free( pi );
1699
1700         return 0;
1701 }
1702
1703 static int
1704 ppolicy_config(
1705     BackendDB   *be,
1706     const char  *fname,
1707     int         lineno,
1708     int         argc,
1709     char        **argv
1710 )
1711 {
1712         slap_overinst *on = (slap_overinst *) be->bd_info;
1713         pp_info *pi = on->on_bi.bi_private;
1714         struct berval dn;
1715         
1716
1717         if ( strcasecmp( argv[0], "ppolicy_default" ) == 0 ) {
1718                 if ( argc != 2 ) {
1719                         fprintf( stderr, "%s: line %d: invalid arguments in \"ppolicy_default"
1720                                 " <policyDN>\n", fname, lineno );
1721                         return ( 1 );
1722                 }
1723                 ber_str2bv( argv[1], 0, 0, &dn );
1724                 if ( dnNormalize( 0, NULL, NULL, &dn, &pi->def_policy, NULL ) ) {
1725                         fprintf( stderr, "%s: line %d: policyDN is invalid\n",
1726                                 fname, lineno );
1727                         return 1;
1728                 }
1729                 return 0;
1730
1731         } else if ( strcasecmp( argv[0], "ppolicy_use_lockout" ) == 0 ) {
1732                 if ( argc != 1 ) {
1733                         fprintf( stderr, "%s: line %d: ppolicy_use_lockout "
1734                                 "takes no arguments\n", fname, lineno );
1735                         return ( 1 );
1736                 }
1737                 pi->use_lockout = 1;
1738                 return 0;
1739         } else if ( strcasecmp( argv[0], "ppolicy_hash_cleartext" ) == 0 ) {
1740                 if ( argc != 1 ) {
1741                         fprintf( stderr, "%s: line %d: ppolicy_hash_cleartext "
1742                                 "takes no arguments\n", fname, lineno );
1743                         return ( 1 );
1744                 }
1745                 pi->hash_passwords = 1;
1746         }
1747         return SLAP_CONF_UNKNOWN;
1748 }
1749
1750 static char *extops[] = {
1751         LDAP_EXOP_MODIFY_PASSWD,
1752         NULL
1753 };
1754
1755 static slap_overinst ppolicy;
1756
1757 int ppolicy_init()
1758 {
1759         LDAPAttributeType *at;
1760         const char *err;
1761         int i, code;
1762
1763         for (i=0; pwd_OpSchema[i].def; i++) {
1764                 at = ldap_str2attributetype( pwd_OpSchema[i].def, &code, &err,
1765                         LDAP_SCHEMA_ALLOW_ALL );
1766                 if ( !at ) {
1767                         fprintf( stderr, "AttributeType Load failed %s %s\n",
1768                                 ldap_scherr2str(code), err );
1769                         return code;
1770                 }
1771                 code = at_add( at, 0, NULL, &err );
1772                 if ( !code ) {
1773                         slap_str2ad( at->at_names[0], pwd_OpSchema[i].ad, &err );
1774                 }
1775                 ldap_memfree( at );
1776                 if ( code ) {
1777                         fprintf( stderr, "AttributeType Load failed %s %s\n",
1778                                 scherr2str(code), err );
1779                         return code;
1780                 }
1781         }
1782
1783         code = register_supported_control( LDAP_CONTROL_PASSWORDPOLICYREQUEST,
1784                 SLAP_CTRL_ADD|SLAP_CTRL_BIND|SLAP_CTRL_MODIFY|SLAP_CTRL_HIDE, extops,
1785                 ppolicy_parseCtrl, &ppolicy_cid );
1786         if ( code != LDAP_SUCCESS ) {
1787                 fprintf( stderr, "Failed to register control %d\n", code );
1788                 return code;
1789         }
1790
1791         ldap_pvt_thread_mutex_init( &chk_syntax_mutex );
1792
1793         ppolicy.on_bi.bi_type = "ppolicy";
1794         ppolicy.on_bi.bi_db_init = ppolicy_db_init;
1795         ppolicy.on_bi.bi_db_open = ppolicy_db_open;
1796         ppolicy.on_bi.bi_db_config = ppolicy_config;
1797         ppolicy.on_bi.bi_db_close = ppolicy_close;
1798
1799         ppolicy.on_bi.bi_op_add = ppolicy_add;
1800         ppolicy.on_bi.bi_op_bind = ppolicy_bind;
1801         ppolicy.on_bi.bi_op_unbind = ppolicy_unbind;
1802         ppolicy.on_bi.bi_op_compare = ppolicy_restrict;
1803         ppolicy.on_bi.bi_op_delete = ppolicy_restrict;
1804         ppolicy.on_bi.bi_op_modify = ppolicy_modify;
1805         ppolicy.on_bi.bi_op_search = ppolicy_restrict;
1806
1807         return overlay_register( &ppolicy );
1808 }
1809
1810 #if SLAPD_OVER_PPOLICY == SLAPD_MOD_DYNAMIC
1811 int init_module(int argc, char *argv[]) {
1812         return ppolicy_init();
1813 }
1814 #endif
1815
1816 #endif  /* defined(SLAPD_OVER_PPOLICY) */