]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/ppolicy.c
fix ITS#3753
[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 ((then = parse_time( vals[0].bv_val )) == (time_t)0)
232                                 return 1;
233
234                         now = slap_get_time();
235
236                         if (now < then + pp->pwdLockoutDuration)
237                                 return 1;
238
239                         m = ch_calloc( sizeof(Modifications), 1 );
240                         m->sml_op = LDAP_MOD_DELETE;
241                         m->sml_type = ad_pwdAccountLockedTime->ad_cname;
242                         m->sml_desc = ad_pwdAccountLockedTime;
243                         m->sml_next = *mod;
244                         *mod = m;
245                 }
246         }
247
248         return 0;
249 }
250
251 #define PPOLICY_WARNING 0xa0L
252 #define PPOLICY_ERROR 0xa1L
253  
254 #define PPOLICY_EXPIRE 0xa0L
255 #define PPOLICY_GRACE  0xa1L
256
257 static LDAPControl *
258 create_passcontrol( int exptime, int grace, LDAPPasswordPolicyError err )
259 {
260         char berbuf[LBER_ELEMENT_SIZEOF], bb2[LBER_ELEMENT_SIZEOF];
261         BerElement *ber = (BerElement *)berbuf, *b2 = (BerElement *)bb2;
262         LDAPControl *c;
263         struct berval bv;
264
265         if ((c = ch_calloc( sizeof( LDAPControl ), 1 )) == NULL) return NULL;
266         c->ldctl_oid = LDAP_CONTROL_PASSWORDPOLICYRESPONSE;
267         c->ldctl_iscritical = 0;
268         c->ldctl_value.bv_val = NULL;
269         c->ldctl_value.bv_len = 0;
270
271         ber_init2( ber, NULL, LBER_USE_DER );
272         ber_printf(ber, "{" /*}*/ );
273
274         if (exptime >= 0) {
275                 ber_init2( b2, NULL, LBER_USE_DER );
276                 ber_printf( b2, "ti", PPOLICY_EXPIRE, exptime );
277                 ber_flatten2( b2, &bv, 1 );
278                 ber_printf( ber, "tO", PPOLICY_WARNING, &bv );
279                 ch_free( bv.bv_val );
280         } else if (grace > 0) {
281                 ber_init2( b2, NULL, LBER_USE_DER );
282                 ber_printf( b2, "ti", PPOLICY_GRACE, grace );
283                 ber_flatten2( b2, &bv, 1 );
284                 ber_printf( ber, "tO", PPOLICY_WARNING, &bv );
285                 ch_free( bv.bv_val );
286         }
287
288         if (err != PP_noError ) {
289                 ber_printf( ber, "te", PPOLICY_ERROR, err );
290         }
291         ber_printf( ber, /*{*/ "N}" );
292
293         if (ber_flatten2( ber, &(c->ldctl_value), 1 ) == LBER_DEFAULT) {
294                 ch_free(c);
295                 (void)ber_free_buf(ber);
296                 return NULL;
297         }
298         (void)ber_free_buf(ber);
299         return c;
300 }
301
302 static void
303 ppolicy_get( Operation *op, Entry *e, PassPolicy *pp )
304 {
305         slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
306         pp_info *pi = on->on_bi.bi_private;
307         Attribute *a;
308         BerVarray vals;
309         int i, rc, nent;
310         Entry *pe = NULL;
311         AttributeDescription *oca = slap_schema.si_ad_objectClass;
312         const char *text;
313         AttributeDescription *ad;
314         struct berval bv;
315
316         memset( pp, 0, sizeof(PassPolicy) );
317
318         /* Users can change their own password by default */
319         pp->pwdAllowUserChange = 1;
320
321         if ((a = attr_find( e->e_attrs, ad_pwdPolicySubentry )) == NULL) {
322                 /*
323                  * entry has no password policy assigned - use default
324                  */
325                 vals = &pi->def_policy;
326                 if ( !vals->bv_val )
327                         goto defaultpol;
328         } else {
329                 vals = a->a_nvals;
330                 if (vals[0].bv_val == NULL) {
331                         Debug( LDAP_DEBUG_ANY,
332                                 "ppolicy_get: NULL value for policySubEntry\n", 0, 0, 0 );
333                         goto defaultpol;
334                 }
335         }
336
337         op->o_bd->bd_info = (BackendInfo *)on->on_info;
338         rc = be_entry_get_rw( op, vals, NULL, NULL, 0, &pe );
339         op->o_bd->bd_info = (BackendInfo *)on;
340
341         if ( rc ) goto defaultpol;
342
343 #if 0   /* Only worry about userPassword for now */
344         if ((a = attr_find( pe->e_attrs, ad_pwdAttribute )))
345                 slap_bv2ad( &a->a_vals[0], &pp->ad, &text );
346 #else
347         pp->ad = slap_schema.si_ad_userPassword;
348 #endif
349
350         if ((a = attr_find( pe->e_attrs, ad_pwdMinAge )))
351                 pp->pwdMinAge = atoi(a->a_vals[0].bv_val );
352         if ((a = attr_find( pe->e_attrs, ad_pwdMaxAge )))
353                 pp->pwdMaxAge = atoi(a->a_vals[0].bv_val );
354         if ((a = attr_find( pe->e_attrs, ad_pwdInHistory )))
355                 pp->pwdInHistory = atoi(a->a_vals[0].bv_val );
356         if ((a = attr_find( pe->e_attrs, ad_pwdCheckQuality )))
357                 pp->pwdCheckQuality = atoi(a->a_vals[0].bv_val );
358         if ((a = attr_find( pe->e_attrs, ad_pwdMinLength )))
359                 pp->pwdMinLength = atoi(a->a_vals[0].bv_val );
360         if ((a = attr_find( pe->e_attrs, ad_pwdMaxFailure )))
361                 pp->pwdMaxFailure = atoi(a->a_vals[0].bv_val );
362         if ((a = attr_find( pe->e_attrs, ad_pwdGraceAuthNLimit )))
363                 pp->pwdGraceAuthNLimit = atoi(a->a_vals[0].bv_val );
364         if ((a = attr_find( pe->e_attrs, ad_pwdExpireWarning )))
365                 pp->pwdExpireWarning = atoi(a->a_vals[0].bv_val );
366         if ((a = attr_find( pe->e_attrs, ad_pwdFailureCountInterval )))
367                 pp->pwdFailureCountInterval = atoi(a->a_vals[0].bv_val );
368         if ((a = attr_find( pe->e_attrs, ad_pwdLockoutDuration )))
369                 pp->pwdLockoutDuration = atoi(a->a_vals[0].bv_val );
370
371         if ((a = attr_find( pe->e_attrs, ad_pwdCheckModule ))) {
372                 strncpy(pp->pwdCheckModule, a->a_vals[0].bv_val,
373                         sizeof(pp->pwdCheckModule));
374                 pp->pwdCheckModule[sizeof(pp->pwdCheckModule)-1] = '\0';
375         }
376
377         if ((a = attr_find( pe->e_attrs, ad_pwdLockout )))
378         pp->pwdLockout = !strcmp( a->a_nvals[0].bv_val, "TRUE" );
379         if ((a = attr_find( pe->e_attrs, ad_pwdMustChange )))
380         pp->pwdMustChange = !strcmp( a->a_nvals[0].bv_val, "TRUE" );
381         if ((a = attr_find( pe->e_attrs, ad_pwdAllowUserChange )))
382         pp->pwdAllowUserChange = !strcmp( a->a_nvals[0].bv_val, "TRUE" );
383         if ((a = attr_find( pe->e_attrs, ad_pwdSafeModify )))
384         pp->pwdSafeModify = !strcmp( a->a_nvals[0].bv_val, "TRUE" );
385     
386         op->o_bd->bd_info = (BackendInfo *)on->on_info;
387         be_entry_release_r( op, pe );
388         op->o_bd->bd_info = (BackendInfo *)on;
389
390         return;
391
392 defaultpol:
393         Debug( LDAP_DEBUG_ANY,
394                 "ppolicy_get: using default policy\n", 0, 0, 0 );
395         return;
396 }
397
398 static int
399 password_scheme( struct berval *cred, struct berval *sch )
400 {
401         int e;
402     
403         assert( cred != NULL );
404
405         if (sch) {
406                 sch->bv_val = NULL;
407                 sch->bv_len = 0;
408         }
409     
410         if ((cred->bv_len == 0) || (cred->bv_val == NULL) ||
411                 (cred->bv_val[0] != '{')) return LDAP_OTHER;
412
413         for(e = 1; cred->bv_val[e] && cred->bv_val[e] != '}'; e++);
414         if (cred->bv_val[e]) {
415                 char *sc = ch_calloc( sizeof(char), e + 2);
416                 sc[e + 1] = '\0'; /* terminate string */
417                 strncpy( sc, cred->bv_val, e + 1);
418                 e = lutil_passwd_scheme( sc );
419                 free( sc );
420                 if (e && sch) {
421                         sch->bv_val = cred->bv_val;
422                         sch->bv_len = e;
423                         return LDAP_SUCCESS;
424                 }
425         }
426         return LDAP_OTHER;
427 }
428
429 static int
430 check_password_quality( struct berval *cred, PassPolicy *pp, LDAPPasswordPolicyError *err, Entry *e )
431 {
432         int rc = LDAP_SUCCESS, ok = LDAP_SUCCESS;
433         char *ptr = cred->bv_val;
434         char *modpath;
435         struct berval sch;
436
437         assert( cred != NULL );
438         assert( pp != NULL );
439
440         if ((cred->bv_len == 0) || (pp->pwdMinLength > cred->bv_len)) {
441                 rc = LDAP_CONSTRAINT_VIOLATION;
442                 if ( err ) *err = PP_passwordTooShort;
443                 return rc;
444         }
445
446         /*
447          * We need to know if the password is already hashed - if so
448          * what scheme is it. The reason being that the "hash" of
449          * {cleartext} still allows us to check the password.
450          */
451         rc = password_scheme( cred, &sch );
452         if (rc == LDAP_SUCCESS) {
453                 if ((sch.bv_val) && (strncasecmp( sch.bv_val, "{cleartext}",
454                         sch.bv_len ) == 0)) {
455                         /*
456                          * We can check the cleartext "hash"
457                          */
458                         ptr = cred->bv_val + sch.bv_len;
459                 } else {
460                         /* everything else, we can't check */
461                         if (pp->pwdCheckQuality == 2) {
462                                 rc = LDAP_CONSTRAINT_VIOLATION;
463                                 if (err) *err = PP_insufficientPasswordQuality;
464                                 return rc;
465                         }
466                         /*
467                          * We can't check the syntax of the password, but it's not
468                          * mandatory (according to the policy), so we return success.
469                          */
470                     
471                         return LDAP_SUCCESS;
472                 }
473         }
474
475         rc = LDAP_SUCCESS;
476
477         if (pp->pwdCheckModule[0]) {
478 #if SLAPD_MODULES
479                 lt_dlhandle mod;
480                 const char *err;
481                 
482                 if ((mod = lt_dlopen( pp->pwdCheckModule )) == NULL) {
483                         err = lt_dlerror();
484
485                         Debug(LDAP_DEBUG_ANY,
486                         "check_password_quality: lt_dlopen failed: (%s) %s.\n",
487                                 pp->pwdCheckModule, err, 0 );
488                         ok = LDAP_OTHER; /* internal error */
489                 } else {
490                         int (*prog)( char *passwd, char **text, Entry *ent );
491
492                         if ((prog = lt_dlsym( mod, "check_password" )) == NULL) {
493                                 err = lt_dlerror();
494                             
495                                 Debug(LDAP_DEBUG_ANY,
496                                         "check_password_quality: lt_dlsym failed: (%s) %s.\n",
497                                         pp->pwdCheckModule, err, 0 );
498                                 ok = LDAP_OTHER;
499                         } else {
500                                 char *txt = NULL;
501
502                                 ldap_pvt_thread_mutex_lock( &chk_syntax_mutex );
503                                 ok = prog( cred->bv_val, &txt, e );
504                                 ldap_pvt_thread_mutex_unlock( &chk_syntax_mutex );
505                                 if (txt) {
506                                         Debug(LDAP_DEBUG_ANY,
507                                                 "check_password_quality: module error: (%s) %s.[%d]\n",
508                                                 pp->pwdCheckModule, txt, ok );
509                                         free(txt);
510                                 } else
511                                         ok = LDAP_SUCCESS;
512                         }
513                             
514                         lt_dlclose( mod );
515                 }
516 #else
517         Debug(LDAP_DEBUG_ANY, "check_password_quality: external modules not "
518                 "supported. pwdCheckModule ignored.\n", 0, 0, 0);
519 #endif /* SLAPD_MODULES */
520         }
521                 
522                     
523         if (ok != LDAP_SUCCESS) {
524                 rc = LDAP_CONSTRAINT_VIOLATION;
525                 if (err) *err = PP_insufficientPasswordQuality;
526         }
527         
528         return rc;
529 }
530
531 static int
532 parse_pwdhistory( struct berval *bv, char **oid, time_t *oldtime, struct berval *oldpw )
533 {
534         char *ptr;
535         struct berval nv, npw;
536         int i, j;
537         
538         assert (bv && (bv->bv_len > 0) && (bv->bv_val) && oldtime && oldpw );
539
540         if ( oid ) *oid = 0;
541         *oldtime = (time_t)-1;
542         oldpw->bv_val = NULL;
543         oldpw->bv_len = 0;
544         
545         ber_dupbv( &nv, bv );
546
547         /* first get the time field */
548         for(i=0; (i < nv.bv_len) && (nv.bv_val[i] != '#'); i++);
549         if ( i == nv.bv_len) goto exit_failure; /* couldn't locate the '#' separator */
550         nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */
551         ptr = nv.bv_val;
552         *oldtime = parse_time( ptr );
553         if (*oldtime == (time_t)-1) goto exit_failure;
554
555         /* get the OID field */
556         for(ptr = &(nv.bv_val[i]);(i < nv.bv_len) && (nv.bv_val[i] != '#'); i++);
557         if ( i == nv.bv_len) goto exit_failure; /* couldn't locate the '#' separator */
558         nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */
559         if ( oid ) *oid = ber_strdup( ptr );
560         
561         /* get the length 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         oldpw->bv_len = strtol( ptr, NULL, 10 );
566         if (errno == ERANGE) goto exit_failure;
567
568         /* lastly, get the octets of the string */
569         for(j=i, ptr = &(nv.bv_val[i]);i < nv.bv_len; i++);
570         if (i-j != oldpw->bv_len) goto exit_failure; /* length is wrong */
571
572         npw.bv_val = ptr;
573         npw.bv_len = oldpw->bv_len;
574         ber_dupbv( oldpw, &npw );
575         
576         return LDAP_SUCCESS;
577 exit_failure:
578         if (oid && *oid) { ber_memfree(*oid); *oid = NULL; }
579         if (oldpw->bv_val) {
580                 ber_memfree( oldpw->bv_val); oldpw->bv_val = NULL;
581                 oldpw->bv_len = 0;
582         }
583         ber_memfree(nv.bv_val);
584         return LDAP_OTHER;
585 }
586
587 static void
588 add_to_pwd_history( pw_hist **l, time_t t,
589                     struct berval *oldpw, struct berval *bv )
590 {
591         pw_hist *p, *p1, *p2;
592     
593         if (!l) return;
594
595         p = ch_malloc( sizeof( pw_hist ));
596         p->pw = *oldpw;
597         ber_dupbv( &p->bv, bv );
598         p->t = t;
599         p->next = NULL;
600         
601         if (*l == NULL) {
602                 /* degenerate case */
603                 *l = p;
604                 return;
605         }
606         /*
607          * advance p1 and p2 such that p1 is the node before the
608          * new one, and p2 is the node after it
609          */
610         for (p1 = NULL, p2 = *l; p2 && p2->t <= t; p1 = p2, p2=p2->next );
611         p->next = p2;
612         if (p1 == NULL) { *l = p; return; }
613         p1->next = p;
614 }
615
616 #ifndef MAX_PWD_HISTORY_SZ
617 #define MAX_PWD_HISTORY_SZ 1024
618 #endif /* MAX_PWD_HISTORY_SZ */
619
620 static void
621 make_pwd_history_value( char *timebuf, struct berval *bv, Attribute *pa )
622 {
623         char str[ MAX_PWD_HISTORY_SZ ];
624         int nlen;
625
626         snprintf( str, MAX_PWD_HISTORY_SZ,
627                   "%s#%s#%d#", timebuf,
628                   pa->a_desc->ad_type->sat_syntax->ssyn_oid,
629                   pa->a_nvals[0].bv_len );
630         str[MAX_PWD_HISTORY_SZ-1] = 0;
631         nlen = strlen(str);
632
633         /*
634          * We have to assume that the string is a string of octets,
635          * not readable characters. In reality, yes, it probably is
636          * a readable (ie, base64) string, but we can't count on that
637          * Hence, while the first 3 fields of the password history
638          * are definitely readable (a timestamp, an OID and an integer
639          * length), the remaining octets of the actual password
640          * are deemed to be binary data.
641          */
642         AC_MEMCPY( str + nlen, pa->a_nvals[0].bv_val, pa->a_nvals[0].bv_len );
643         nlen += pa->a_nvals[0].bv_len;
644         bv->bv_val = ch_malloc( nlen + 1 );
645         AC_MEMCPY( bv->bv_val, str, nlen );
646         bv->bv_val[nlen] = '\0';
647         bv->bv_len = nlen;
648 }
649
650 static void
651 free_pwd_history_list( pw_hist **l )
652 {
653         pw_hist *p;
654     
655         if (!l) return;
656         p = *l;
657         while (p) {
658                 pw_hist *pp = p->next;
659
660                 free(p->pw.bv_val);
661                 free(p->bv.bv_val);
662                 free(p);
663                 p = pp;
664         }
665         *l = NULL;
666 }
667
668 typedef struct ppbind {
669         slap_overinst *on;
670         int send_ctrl;
671         Modifications *mod;
672         LDAPPasswordPolicyError pErr;
673         PassPolicy pp;
674 } ppbind;
675
676 static int
677 ppolicy_bind_resp( Operation *op, SlapReply *rs )
678 {
679         ppbind *ppb = op->o_callback->sc_private;
680         slap_overinst *on = ppb->on;
681         Modifications *mod = ppb->mod, *m;
682         int pwExpired = 0;
683         int ngut = -1, warn = -1, age, rc, i;
684         Attribute *a;
685         struct tm *tm;
686         time_t now, then, pwtime = (time_t)-1;
687         const char *txt;
688         char nowstr[ LDAP_LUTIL_GENTIME_BUFSIZE ];
689         BackendInfo *bi = op->o_bd->bd_info;
690         Entry *e;
691
692         /* If we already know it's locked, just get on with it */
693         if ( ppb->pErr != PP_noError ) {
694                 goto locked;
695         }
696
697         op->o_bd->bd_info = (BackendInfo *)on->on_info;
698         rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
699         op->o_bd->bd_info = bi;
700
701         if ( rc != LDAP_SUCCESS ) {
702                 return SLAP_CB_CONTINUE;
703         }
704
705         now = slap_get_time(); /* stored for later consideration */
706         ldap_pvt_thread_mutex_lock( &gmtime_mutex );
707         tm = gmtime(&now);
708         lutil_gentime( nowstr, sizeof(nowstr), tm );
709         ldap_pvt_thread_mutex_unlock( &gmtime_mutex );
710
711         if ( rs->sr_err == LDAP_INVALID_CREDENTIALS ) {
712                 int i = 0, fc = 0;
713
714                 m = ch_calloc( sizeof(Modifications), 1 );
715                 m->sml_op = LDAP_MOD_ADD;
716                 m->sml_type = ad_pwdFailureTime->ad_cname;
717                 m->sml_desc = ad_pwdFailureTime;
718                 m->sml_values = ch_calloc( sizeof(struct berval), 2 );
719
720                 ber_str2bv( nowstr, 0, 1, &m->sml_values[0] );
721                 m->sml_next = mod;
722                 mod = m;
723
724                 /*
725                  * Count the pwdFailureTimes - if it's
726                  * greater than the policy pwdMaxFailure,
727                  * then lock the account.
728                  */
729                 if ((a = attr_find( e->e_attrs, ad_pwdFailureTime )) != NULL) {
730                         for(i=0; a->a_nvals[i].bv_val; i++) {
731
732                                 /*
733                                  * If the interval is 0, then failures
734                                  * stay on the record until explicitly
735                                  * reset by successful authentication.
736                                  */
737                                 if (ppb->pp.pwdFailureCountInterval == 0) {
738                                         fc++;
739                                 } else if (now <=
740                                                         parse_time(a->a_nvals[i].bv_val) +
741                                                         ppb->pp.pwdFailureCountInterval) {
742
743                                         fc++;
744                                 }
745                                 /*
746                                  * We only count those failures
747                                  * which are not due to expire.
748                                  */
749                         }
750                 }
751                 
752                 if ((ppb->pp.pwdMaxFailure > 0) &&
753                         (fc >= ppb->pp.pwdMaxFailure - 1)) {
754
755                         /*
756                          * We subtract 1 from the failure max
757                          * because the new failure entry hasn't
758                          * made it to the entry yet.
759                          */
760                         m = ch_calloc( sizeof(Modifications), 1 );
761                         m->sml_op = LDAP_MOD_REPLACE;
762                         m->sml_type = ad_pwdAccountLockedTime->ad_cname;
763                         m->sml_desc = ad_pwdAccountLockedTime;
764                         m->sml_values = ch_calloc( sizeof(struct berval), 2 );
765                         ber_str2bv( nowstr, 0, 1, &m->sml_values[0] );
766                         m->sml_next = mod;
767                         mod = m;
768                 }
769         } else if ( rs->sr_err == LDAP_SUCCESS ) {
770                 if ((a = attr_find( e->e_attrs, ad_pwdChangedTime )) != NULL)
771                         pwtime = parse_time( a->a_nvals[0].bv_val );
772
773                 /* delete all pwdFailureTimes */
774                 if ( attr_find( e->e_attrs, ad_pwdFailureTime )) {
775                         m = ch_calloc( sizeof(Modifications), 1 );
776                         m->sml_op = LDAP_MOD_DELETE;
777                         m->sml_type = ad_pwdFailureTime->ad_cname;
778                         m->sml_desc = ad_pwdFailureTime;
779                         m->sml_next = mod;
780                         mod = m;
781                 }
782
783                 /*
784                  * check to see if the password must be changed
785                  */
786                 if ( ppb->pp.pwdMustChange &&
787                         (a = attr_find( e->e_attrs, ad_pwdReset )) &&
788                         !strcmp( a->a_nvals[0].bv_val, "TRUE" ) ) {
789                         /*
790                          * need to inject client controls here to give
791                          * more information. For the moment, we ensure
792                          * that we are disallowed from doing anything
793                          * other than change password.
794                          */
795                         pwcons[op->o_conn->c_conn_idx].restricted = 1;
796
797                         ppb->pErr = PP_changeAfterReset;
798
799                 } else {
800                         /*
801                          * the password does not need to be changed, so
802                          * we now check whether the password has expired.
803                          *
804                          * We can skip this bit if passwords don't age in
805                          * the policy.
806                          */
807                         if (ppb->pp.pwdMaxAge == 0) goto grace;
808
809                         if (pwtime == (time_t)-1) {
810                                 /*
811                                  * Hmm. No password changed time on the
812                                  * entry. This is odd - it should have
813                                  * been provided when the attribute was added.
814                                  *
815                                  * However, it's possible that it could be
816                                  * missing if the DIT was established via
817                                  * an import process.
818                                  */
819                                 Debug( LDAP_DEBUG_ANY,
820                                         "ppolicy_bind: Entry %s does not have valid pwdChangedTime attribute - assuming password expired\n",
821                                         e->e_name.bv_val, 0, 0);
822                                 
823                                 pwExpired = 1;
824                         } else {
825                                 /*
826                                  * Check: was the last change time of
827                                  * the password older than the maximum age
828                                  * allowed. (Ignore case 2 from I-D, it's just silly.)
829                                  */
830                                 if (now - pwtime > ppb->pp.pwdMaxAge ) pwExpired = 1;
831                         }
832                 }
833
834 grace:
835                 if (!pwExpired) goto check_expiring_password;
836                 
837                 if ((a = attr_find( e->e_attrs, ad_pwdGraceUseTime )) == NULL)
838                         ngut = ppb->pp.pwdGraceAuthNLimit;
839                 else {
840                         for(ngut=0; a->a_nvals[ngut].bv_val; ngut++);
841                         ngut = ppb->pp.pwdGraceAuthNLimit - ngut;
842                 }
843
844                 /*
845                  * ngut is the number of remaining grace logins
846                  */
847                 Debug( LDAP_DEBUG_ANY,
848                         "ppolicy_bind: Entry %s has an expired password: %d grace logins\n",
849                         e->e_name.bv_val, ngut, 0);
850                 
851                 if (ngut < 1) {
852                         ppb->pErr = PP_passwordExpired;
853                         rs->sr_err = LDAP_INVALID_CREDENTIALS;
854                         goto done;
855                 }
856
857                 /*
858                  * Add a grace user time to the entry
859                  */
860                 m = ch_calloc( sizeof(Modifications), 1 );
861                 m->sml_op = LDAP_MOD_ADD;
862                 m->sml_type = ad_pwdGraceUseTime->ad_cname;
863                 m->sml_desc = ad_pwdGraceUseTime;
864                 m->sml_values = ch_calloc( sizeof(struct berval), 2 );
865                 ber_str2bv( nowstr, 0, 1, &m->sml_values[0] );
866                 m->sml_next = mod;
867                 mod = m;
868
869 check_expiring_password:
870                 /*
871                  * Now we need to check to see
872                  * if it is about to expire, and if so, should the user
873                  * be warned about it in the password policy control.
874                  *
875                  * If the password has expired, and we're in the grace period, then
876                  * we don't need to do this bit. Similarly, if we don't have password
877                  * aging, then there's no need to do this bit either.
878                  */
879                 if ((ppb->pp.pwdMaxAge < 1) || (pwExpired) || (ppb->pp.pwdExpireWarning < 1))
880                         goto done;
881
882                 age = (int)(now - pwtime);
883                 
884                 /*
885                  * We know that there is a password Change Time attribute - if
886                  * there wasn't, then the pwdExpired value would be true, unless
887                  * there is no password aging - and if there is no password aging,
888                  * then this section isn't called anyway - you can't have an
889                  * expiring password if there's no limit to expire.
890                  */
891                 if (ppb->pp.pwdMaxAge - age < ppb->pp.pwdExpireWarning ) {
892                         /*
893                          * Set the warning value.
894                          */
895                         warn = ppb->pp.pwdMaxAge - age; /* seconds left until expiry */
896                         if (warn < 0) warn = 0; /* something weird here - why is pwExpired not set? */
897                         
898                         Debug( LDAP_DEBUG_ANY,
899                                 "ppolicy_bind: Setting warning for password expiry for %s = %d seconds\n",
900                                 op->o_req_dn.bv_val, warn, 0 );
901                 }
902         }
903
904 done:
905         op->o_bd->bd_info = (BackendInfo *)on->on_info;
906         be_entry_release_r( op, e );
907
908 locked:
909         if ( mod ) {
910                 Operation op2 = *op;
911                 SlapReply r2 = { REP_RESULT };
912                 slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
913
914                 /* FIXME: Need to handle replication of some (but not all)
915                  * of the operational attributes...
916                  */
917                 op2.o_tag = LDAP_REQ_MODIFY;
918                 op2.o_callback = &cb;
919                 op2.orm_modlist = mod;
920                 op2.o_dn = op->o_bd->be_rootdn;
921                 op2.o_ndn = op->o_bd->be_rootndn;
922                 op2.o_bd->bd_info = (BackendInfo *)on->on_info;
923                 rc = op->o_bd->be_modify( &op2, &r2 );
924                 slap_mods_free( mod );
925         }
926
927         if ( ppb->send_ctrl ) {
928                 LDAPControl **ctrls = NULL;
929                 pp_info *pi = on->on_bi.bi_private;
930
931                 /* Do we really want to tell that the account is locked? */
932                 if ( ppb->pErr == PP_accountLocked && !pi->use_lockout ) {
933                         ppb->pErr = PP_noError;
934                 }
935                 ctrls = ch_calloc( sizeof( LDAPControl *) , 2 );
936                 ctrls[0] = create_passcontrol( warn, ngut, ppb->pErr );
937                 ctrls[1] = NULL;
938                 rs->sr_ctrls = ctrls;
939         }
940         op->o_bd->bd_info = bi;
941         return SLAP_CB_CONTINUE;
942 }
943
944 static int
945 ppolicy_bind( Operation *op, SlapReply *rs )
946 {
947         slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
948
949         /* Root bypasses policy */
950         if ( !be_isroot_dn( op->o_bd, &op->o_req_ndn )) {
951                 Entry *e;
952                 int i, rc;
953                 ppbind *ppb;
954                 slap_callback *cb;
955
956                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
957                 rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
958
959                 if ( rc != LDAP_SUCCESS ) {
960                         return SLAP_CB_CONTINUE;
961                 }
962
963                 cb = op->o_tmpcalloc( sizeof(ppbind)+sizeof(slap_callback),
964                         1, op->o_tmpmemctx );
965                 ppb = (ppbind *)(cb+1);
966                 ppb->on = on;
967                 ppb->pErr = PP_noError;
968
969                 /* Setup a callback so we can munge the result */
970
971                 cb->sc_response = ppolicy_bind_resp;
972                 cb->sc_next = op->o_callback->sc_next;
973                 cb->sc_private = ppb;
974                 op->o_callback->sc_next = cb;
975
976                 /* Did we receive a password policy request control? */
977                 if ( op->o_ctrlflag[ppolicy_cid] ) {
978                         ppb->send_ctrl = 1;
979                 }
980
981                 op->o_bd->bd_info = (BackendInfo *)on;
982                 ppolicy_get( op, e, &ppb->pp );
983
984                 rc = account_locked( op, e, &ppb->pp, &ppb->mod );
985
986                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
987                 be_entry_release_r( op, e );
988
989                 if ( rc ) {
990                         /* This will be the Draft 8 response, Unwilling is bogus */
991                         ppb->pErr = PP_accountLocked;
992                         send_ldap_error( op, rs, LDAP_INVALID_CREDENTIALS, NULL );
993                         return rs->sr_err;
994                 }
995
996         }
997
998         return SLAP_CB_CONTINUE;
999 }
1000
1001 /* Reset the restricted flag for the next session on this connection */
1002 static int
1003 ppolicy_unbind( Operation *op, SlapReply *rs )
1004 {
1005         pwcons[op->o_conn->c_conn_idx].restricted = 0;
1006         return SLAP_CB_CONTINUE;
1007 }
1008
1009 /* Check if this connection is restricted */
1010 static int
1011 ppolicy_restrict(
1012         Operation *op,
1013         SlapReply *rs )
1014 {
1015         slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1016         int i, send_ctrl = 0;
1017
1018         /* Did we receive a password policy request control? */
1019         if ( op->o_ctrlflag[ppolicy_cid] ) {
1020                 send_ctrl = 1;
1021         }
1022
1023         if ( op->o_conn && pwcons[op->o_conn->c_conn_idx].restricted ) {
1024                 Debug( LDAP_DEBUG_TRACE,
1025                         "connection restricted to password changing only\n", 0, 0, 0);
1026                 if ( send_ctrl ) {
1027                         LDAPControl **ctrls = NULL;
1028
1029                         ctrls = ch_calloc( sizeof( LDAPControl *) , 2 );
1030                         ctrls[0] = create_passcontrol( -1, -1, PP_changeAfterReset );
1031                         ctrls[1] = NULL;
1032                         rs->sr_ctrls = ctrls;
1033                 }
1034                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1035                 send_ldap_error( op, rs, LDAP_INSUFFICIENT_ACCESS, 
1036                         "Operations are restricted to bind/unbind/abandon/StartTLS/modify password" );
1037                 return rs->sr_err;
1038         }
1039
1040         return SLAP_CB_CONTINUE;
1041 }
1042
1043 static int
1044 ppolicy_add(
1045         Operation *op,
1046         SlapReply *rs )
1047 {
1048         slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1049         pp_info *pi = on->on_bi.bi_private;
1050         PassPolicy pp;
1051         int pw;
1052         Attribute *pa;
1053         const char *txt;
1054
1055         if ( ppolicy_restrict( op, rs ) != SLAP_CB_CONTINUE )
1056                 return rs->sr_err;
1057
1058         /* Check for password in entry */
1059         if ((pa = attr_find( op->oq_add.rs_e->e_attrs,
1060                 slap_schema.si_ad_userPassword )))
1061         {
1062                 /*
1063                  * new entry contains a password - if we're not the root user
1064                  * then we need to check that the password fits in with the
1065                  * security policy for the new entry.
1066                  */
1067                 ppolicy_get( op, op->ora_e, &pp );
1068                 if (pp.pwdCheckQuality > 0 && !be_isroot( op )) {
1069                         struct berval *bv = &(pa->a_vals[0]);
1070                         int rc, i, send_ctrl = 0; 
1071                         LDAPPasswordPolicyError pErr = PP_noError;
1072
1073                         /* Did we receive a password policy request control? */
1074                         if ( op->o_ctrlflag[ppolicy_cid] ) {
1075                                 send_ctrl = 1;
1076                         }
1077                         rc = check_password_quality( bv, &pp, &pErr, op->ora_e );
1078                         if (rc != LDAP_SUCCESS) {
1079                                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1080                                 if ( send_ctrl ) {
1081                                         LDAPControl **ctrls = NULL;
1082
1083                                         ctrls = ch_calloc( sizeof( LDAPControl *) , 2 );
1084                                         ctrls[0] = create_passcontrol( -1, -1, pErr );
1085                                         ctrls[1] = NULL;
1086                                         rs->sr_ctrls = ctrls;
1087                                 }
1088                                 send_ldap_error( op, rs, rc, "Password fails quality checking policy" );
1089                                 return rs->sr_err;
1090                         }
1091                             /*
1092                              * A controversial bit. We hash cleartext
1093                              * passwords provided via add and modify operations
1094                              * You're not really supposed to do this, since
1095                              * the X.500 model says "store attributes" as they
1096                              * get provided. By default, this is what we do
1097                              *
1098                              * But if the hash_passwords flag is set, we hash
1099                              * any cleartext password attribute values via the
1100                              * default password hashing scheme.
1101                              */
1102                         if ((pi->hash_passwords) &&
1103                                 (password_scheme( &(pa->a_vals[0]), NULL ) != LDAP_SUCCESS)) {
1104                                 struct berval hpw;
1105
1106                                 slap_passwd_hash( &(pa->a_vals[0]), &hpw, &txt );
1107                                 if (hpw.bv_val == NULL) {
1108                                     /*
1109                                      * hashing didn't work. Emit an error.
1110                                      */
1111                                         rs->sr_err = LDAP_OTHER;
1112                                         rs->sr_text = txt;
1113                                         send_ldap_error( op, rs, LDAP_OTHER, "Password hashing failed" );
1114                                         return rs->sr_err;
1115                                 }
1116
1117                                 memset( pa->a_vals[0].bv_val, 0, pa->a_vals[0].bv_len);
1118                                 ber_memfree( pa->a_vals[0].bv_val );
1119                                 pa->a_vals[0].bv_val = hpw.bv_val;
1120                                 pa->a_vals[0].bv_len = hpw.bv_len;
1121                         }
1122                 }
1123                 /* If password aging is in effect, set the pwdChangedTime */
1124                 if (( pp.pwdMaxAge || pp.pwdMinAge ) && !be_shadow_update( op )) {
1125                         struct berval timestamp;
1126                         char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
1127                         struct tm *ltm;
1128                         time_t now = slap_get_time();
1129
1130                         ldap_pvt_thread_mutex_lock( &gmtime_mutex );
1131                         ltm = gmtime( &now );
1132                         lutil_gentime( timebuf, sizeof(timebuf), ltm );
1133                         ldap_pvt_thread_mutex_unlock( &gmtime_mutex );
1134
1135                         timestamp.bv_val = timebuf;
1136                         timestamp.bv_len = strlen(timebuf);
1137
1138                         attr_merge_one( op->ora_e, ad_pwdChangedTime, &timestamp, NULL );
1139                 }
1140         }
1141         return SLAP_CB_CONTINUE;
1142 }
1143
1144 static int
1145 ppolicy_modify( Operation *op, SlapReply *rs )
1146 {
1147         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
1148         pp_info                 *pi = on->on_bi.bi_private;
1149         int                     i, rc, mod_pw_only, pwmod, pwmop, deladd,
1150                                 hsize = 0;
1151         PassPolicy              pp;
1152         Modifications           *mods = NULL, *modtail, *ml, *delmod, *addmod;
1153         Attribute               *pa, *ha, *ra, at;
1154         const char              *txt;
1155         pw_hist                 *tl = NULL, *p;
1156         int                     zapReset, send_ctrl = 0;
1157         Entry                   *e;
1158         struct berval           newpw = BER_BVNULL, oldpw = BER_BVNULL,
1159                                 *bv, cr[2];
1160         LDAPPasswordPolicyError pErr = PP_noError;
1161
1162         op->o_bd->bd_info = (BackendInfo *)on->on_info;
1163         rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
1164         op->o_bd->bd_info = (BackendInfo *)on;
1165
1166         if ( rc != LDAP_SUCCESS ) return SLAP_CB_CONTINUE;
1167
1168         /* Did we receive a password policy request control? */
1169         if ( op->o_ctrlflag[ppolicy_cid] ) {
1170                 send_ctrl = 1;
1171         }
1172
1173         /* See if this is a pwdModify exop. If so, we can
1174          * access the plaintext passwords from that request.
1175          */
1176         {
1177                 slap_callback *sc;
1178
1179                 for ( sc = op->o_callback; sc; sc=sc->sc_next ) {
1180                         if ( sc->sc_response == slap_replog_cb &&
1181                                 sc->sc_private ) {
1182                                 req_pwdexop_s *qpw = sc->sc_private;
1183                                 newpw = qpw->rs_new;
1184                                 oldpw = qpw->rs_old;
1185                                 break;
1186                         }
1187                 }
1188         }
1189
1190         ppolicy_get( op, e, &pp );
1191
1192         for(ml = op->oq_modify.rs_modlist,
1193                         pwmod = 0, mod_pw_only = 1,
1194                         deladd = 0, delmod = NULL,
1195                         addmod = NULL,
1196                         zapReset = 1;
1197                 ml != NULL; modtail = ml, ml = ml->sml_next ) {
1198                 if ( ml->sml_desc == pp.ad ) {
1199                         pwmod = 1;
1200                         pwmop = ml->sml_op;
1201                         if ((deladd == 0) && (ml->sml_op == LDAP_MOD_DELETE) &&
1202                                 (ml->sml_values) && (ml->sml_values[0].bv_val != NULL)) {
1203                                 deladd = 1;
1204                                 delmod = ml;
1205                         }
1206
1207                         if ((deladd == 1) && ((ml->sml_op == LDAP_MOD_ADD) ||
1208                                                                   (ml->sml_op == LDAP_MOD_REPLACE)))
1209                                 deladd = 2;
1210
1211                         if ((ml->sml_op == LDAP_MOD_ADD) ||
1212                                 (ml->sml_op == LDAP_MOD_REPLACE))
1213                                 addmod = ml;
1214                 } else if (! is_at_operational( ml->sml_desc->ad_type )) {
1215                         mod_pw_only = 0;
1216                         /* modifying something other than password */
1217                 }
1218
1219                 /*
1220                  * If there is a request to explicitly add a pwdReset
1221                  * attribute, then we suppress the normal behaviour on
1222                  * password change, which is to remove the pwdReset
1223                  * attribute.
1224                  *
1225                  * This enables an administrator to assign a new password
1226                  * and place a "must reset" flag on the entry, which will
1227                  * stay until the user explicitly changes his/her password.
1228                  */
1229                 if (ml->sml_desc == ad_pwdReset ) {
1230                         if ((ml->sml_op == LDAP_MOD_ADD) ||
1231                                 (ml->sml_op == LDAP_MOD_REPLACE))
1232                                 zapReset = 0;
1233                 }
1234         }
1235         
1236         if (pwcons[op->o_conn->c_conn_idx].restricted && !mod_pw_only) {
1237                 Debug( LDAP_DEBUG_TRACE,
1238                         "connection restricted to password changing only\n", 0, 0, 0 );
1239                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 
1240                 rs->sr_text = "Operations are restricted to bind/unbind/abandon/StartTLS/modify password";
1241                 pErr = PP_changeAfterReset;
1242                 goto return_results;
1243         }
1244
1245         /*
1246          * if we have a "safe password modify policy", then we need to check if we're doing
1247          * a delete (with the old password), followed by an add (with the new password).
1248          *
1249          * If we don't have this, then we fail with an error. We also skip all the checks if
1250          * the root user is bound. Root can do anything, including avoid the policies.
1251          */
1252
1253         if (!pwmod) goto do_modify;
1254
1255         /*
1256          * Did we get a valid add mod?
1257          */
1258
1259         if (!addmod) {
1260                 rs->sr_err = LDAP_OTHER;
1261                 rs->sr_text = "Internal Error";
1262                 Debug( LDAP_DEBUG_TRACE,
1263                         "cannot locate modification supplying new password\n", 0, 0, 0 );
1264                 goto return_results;
1265         }
1266
1267         /*
1268          * Build the password history list in ascending time order
1269          * We need this, even if the user is root, in order to maintain
1270          * the pwdHistory operational attributes properly.
1271          */
1272         if (pp.pwdInHistory > 0 && (ha = attr_find( e->e_attrs, ad_pwdHistory ))) {
1273                 struct berval oldpw;
1274                 time_t oldtime;
1275                 char *oid;
1276
1277                 for(i=0; ha->a_nvals[i].bv_val; i++) {
1278                         rc = parse_pwdhistory( &(ha->a_nvals[i]), NULL,
1279                                 &oldtime, &oldpw );
1280
1281                         if (rc != LDAP_SUCCESS) continue; /* invalid history entry */
1282
1283                         if (oldpw.bv_val) {
1284                                 add_to_pwd_history( &tl, oldtime, &oldpw,
1285                                         &(ha->a_nvals[i]) );
1286                                 oldpw.bv_val = NULL;
1287                                 oldpw.bv_len = 0;
1288                         }
1289                 }
1290                 for(p=tl; p; p=p->next, hsize++); /* count history size */
1291         }
1292
1293         if (be_isroot( op )) goto do_modify;
1294
1295         /* This is a pwdModify exop that provided the old pw.
1296          * We need to create a Delete mod for this old pw and 
1297          * let the matching value get found later
1298          */
1299         if (pp.pwdSafeModify && oldpw.bv_val ) {
1300                 ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
1301                 ml->sml_op = LDAP_MOD_DELETE;
1302                 ml->sml_desc = pp.ad;
1303                 ml->sml_type = pp.ad->ad_cname;
1304                 ml->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
1305                 ber_dupbv( &ml->sml_values[0], &oldpw );
1306                 ml->sml_values[1].bv_len = 0;
1307                 ml->sml_values[1].bv_val = NULL;
1308                 ml->sml_nvalues = NULL;
1309                 ml->sml_next = op->orm_modlist;
1310                 op->orm_modlist = ml;
1311                 delmod = ml;
1312                 deladd = 2;
1313         }
1314
1315         if (pp.pwdSafeModify && deladd != 2) {
1316                 Debug( LDAP_DEBUG_TRACE,
1317                         "change password must use DELETE followed by ADD/REPLACE\n",
1318                         0, 0, 0 );
1319                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1320                 rs->sr_text = "Must supply old password to be changed as well as new one";
1321                 pErr = PP_mustSupplyOldPassword;
1322                 goto return_results;
1323         }
1324
1325         if (!pp.pwdAllowUserChange) {
1326                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1327                 rs->sr_text = "User alteration of password is not allowed";
1328                 pErr = PP_passwordModNotAllowed;
1329                 goto return_results;
1330         }
1331
1332         if (pp.pwdMinAge > 0) {
1333                 time_t pwtime = (time_t)-1, now;
1334                 int age;
1335
1336                 if ((pa = attr_find( e->e_attrs, ad_pwdChangedTime )) != NULL)
1337                         pwtime = parse_time( pa->a_nvals[0].bv_val );
1338                 now = slap_get_time();
1339                 age = (int)(now - pwtime);
1340                 if ((pwtime != (time_t)-1) && (age < pp.pwdMinAge)) {
1341                         rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
1342                         rs->sr_text = "Password is too young to change";
1343                         pErr = PP_passwordTooYoung;
1344                         goto return_results;
1345                 }
1346         }
1347
1348         /* pa is used in password history check below, be sure it's set */
1349         if ((pa = attr_find( e->e_attrs, pp.ad )) != NULL && delmod) {
1350                 /*
1351                  * we have a password to check
1352                  */
1353                 const char *txt;
1354                 
1355                 bv = oldpw.bv_val ? &oldpw : delmod->sml_values;
1356                 /* FIXME: no access checking? */
1357                 rc = slap_passwd_check( op, NULL, pa, bv, &txt );
1358                 if (rc != LDAP_SUCCESS) {
1359                         Debug( LDAP_DEBUG_TRACE,
1360                                 "old password check failed: %s\n", txt, 0, 0 );
1361                         
1362                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1363                         rs->sr_text = "Must supply correct old password to change to new one";
1364                         pErr = PP_mustSupplyOldPassword;
1365                         goto return_results;
1366
1367                 } else {
1368                         int i;
1369                         
1370                         /*
1371                          * replace the delete value with the (possibly hashed)
1372                          * value which is currently in the password.
1373                          */
1374                         for(i=0; delmod->sml_values[i].bv_val; i++) {
1375                                 free(delmod->sml_values[i].bv_val);
1376                                 delmod->sml_values[i].bv_len = 0;
1377                         }
1378                         free(delmod->sml_values);
1379                         delmod->sml_values = ch_calloc( sizeof(struct berval), 2 );
1380                         delmod->sml_values[1].bv_len = 0;
1381                         delmod->sml_values[1].bv_val = NULL;
1382                         ber_dupbv(&(delmod->sml_values[0]),  &(pa->a_nvals[0]));
1383                 }
1384         }
1385
1386         bv = newpw.bv_val ? &newpw : addmod->sml_values;
1387         if (pp.pwdCheckQuality > 0) {
1388
1389                 rc = check_password_quality( bv, &pp, &pErr, e );
1390                 if (rc != LDAP_SUCCESS) {
1391                         rs->sr_err = rc;
1392                         rs->sr_text = "Password fails quality checking policy";
1393                         goto return_results;
1394                 }
1395         }
1396
1397         if (pa) {
1398                 /*
1399                  * Last check - the password history.
1400                  */
1401                 /* FIXME: no access checking? */
1402                 if (slap_passwd_check( op, NULL, pa, bv, &txt ) == LDAP_SUCCESS) {
1403                         /*
1404                          * This is bad - it means that the user is attempting
1405                          * to set the password to the same as the old one.
1406                          */
1407                         rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
1408                         rs->sr_text = "Password is not being changed from existing value";
1409                         pErr = PP_passwordInHistory;
1410                         goto return_results;
1411                 }
1412         
1413                 if (pp.pwdInHistory < 1) goto do_modify;
1414         
1415                 /*
1416                  * Iterate through the password history, and fail on any
1417                  * password matches.
1418                  */
1419                 at = *pa;
1420                 at.a_vals = cr;
1421                 cr[1].bv_val = NULL;
1422                 for(p=tl; p; p=p->next) {
1423                         cr[0] = p->pw;
1424                         /* FIXME: no access checking? */
1425                         rc = slap_passwd_check( op, NULL, &at, bv, &txt );
1426                         
1427                         if (rc != LDAP_SUCCESS) continue;
1428                         
1429                         rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
1430                         rs->sr_text = "Password is in history of old passwords";
1431                         pErr = PP_passwordInHistory;
1432                         goto return_results;
1433                 }
1434         }
1435
1436 do_modify:
1437         if ((pwmod) && (!be_shadow_update( op ))) {
1438                 struct berval timestamp;
1439                 char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
1440                 struct tm *ltm;
1441                 time_t now = slap_get_time();
1442                 Attribute *ga;
1443                 
1444                 /*
1445                  * keep the necessary pwd.. operational attributes
1446                  * up to date.
1447                  */
1448
1449                 ldap_pvt_thread_mutex_lock( &gmtime_mutex );
1450                 ltm = gmtime( &now );
1451                 lutil_gentime( timebuf, sizeof(timebuf), ltm );
1452                 ldap_pvt_thread_mutex_unlock( &gmtime_mutex );
1453
1454                 timestamp.bv_val = timebuf;
1455                 timestamp.bv_len = strlen(timebuf);
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_nvalues = NULL;
1471                 mods->sml_next = NULL;
1472                 modtail->sml_next = mods;
1473                 modtail = mods;
1474
1475                 if (attr_find(e->e_attrs, ad_pwdGraceUseTime )) {
1476                         mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1477                         mods->sml_op = LDAP_MOD_DELETE;
1478                         mods->sml_type.bv_val = NULL;
1479                         mods->sml_desc = ad_pwdGraceUseTime;
1480                         mods->sml_values = NULL;
1481                         mods->sml_nvalues = NULL;
1482                         mods->sml_next = NULL;
1483                         modtail->sml_next = mods;
1484                         modtail = mods;
1485                 }
1486
1487                 /* Delete the pwdReset attribute, since it's being reset */
1488                 if ((zapReset) && (attr_find(e->e_attrs, ad_pwdReset ))) {
1489                         mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1490                         mods->sml_op = LDAP_MOD_DELETE;
1491                         mods->sml_type.bv_val = NULL;
1492                         mods->sml_desc = ad_pwdReset;
1493                         mods->sml_values = NULL;
1494                         mods->sml_nvalues = NULL;
1495                         mods->sml_next = NULL;
1496                         modtail->sml_next = mods;
1497                         modtail = mods;
1498                 }
1499
1500                 if (pp.pwdInHistory > 0) {
1501                         if (hsize >= pp.pwdInHistory) {
1502                                 /*
1503                                  * We use the >= operator, since we are going to add
1504                                  * the existing password attribute value into the
1505                                  * history - thus the cardinality of history values is
1506                                  * about to rise by one.
1507                                  *
1508                                  * If this would push it over the limit of history
1509                                  * values (remembering - the password policy could have
1510                                  * changed since the password was last altered), we must
1511                                  * delete at least 1 value from the pwdHistory list.
1512                                  *
1513                                  * In fact, we delete '(#pwdHistory attrs - max pwd
1514                                  * history length) + 1' values, starting with the oldest.
1515                                  * This is easily evaluated, since the linked list is
1516                                  * created in ascending time order.
1517                                  */
1518                                 mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1519                                 mods->sml_op = LDAP_MOD_DELETE;
1520                                 mods->sml_type.bv_val = NULL;
1521                                 mods->sml_desc = ad_pwdHistory;
1522                                 mods->sml_nvalues = NULL;
1523                                 mods->sml_values = ch_calloc( sizeof( struct berval ),
1524                                                                                            hsize - pp.pwdInHistory + 2 );
1525                                 mods->sml_values[ hsize - pp.pwdInHistory + 1 ].bv_val = NULL;
1526                                 mods->sml_values[ hsize - pp.pwdInHistory + 1 ].bv_len = 0;
1527                                 for(i=0,p=tl; i < (hsize - pp.pwdInHistory + 1); i++, p=p->next) {
1528                                         mods->sml_values[i].bv_val = NULL;
1529                                         mods->sml_values[i].bv_len = 0;
1530                                         ber_dupbv( &(mods->sml_values[i]), &p->bv );
1531                                 }
1532                                 mods->sml_next = NULL;
1533                                 modtail->sml_next = mods;
1534                                 modtail = mods;
1535                         }
1536                         free_pwd_history_list( &tl );
1537
1538                         /*
1539                          * Now add the existing password into the history list.
1540                          * This will be executed even if the operation is to delete
1541                          * the password entirely.
1542                          *
1543                          * This isn't in the spec explicitly, but it seems to make
1544                          * sense that the password history list is the list of all
1545                          * previous passwords - even if they were deleted. Thus, if
1546                          * someone tries to add a historical password at some future
1547                          * point, it will fail.
1548                          */
1549                         if ((pa = attr_find( e->e_attrs, pp.ad )) != NULL) {
1550                                 mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1551                                 mods->sml_op = LDAP_MOD_ADD;
1552                                 mods->sml_type.bv_val = NULL;
1553                                 mods->sml_desc = ad_pwdHistory;
1554                                 mods->sml_nvalues = NULL;
1555                                 mods->sml_values = ch_calloc( sizeof( struct berval ), 2 );
1556                                 mods->sml_values[ 1 ].bv_val = NULL;
1557                                 mods->sml_values[ 1 ].bv_len = 0;
1558                                 make_pwd_history_value( timebuf, &mods->sml_values[0], pa );
1559                                 mods->sml_next = NULL;
1560                                 modtail->sml_next = mods;
1561                                 modtail = mods;
1562                         } else {
1563                                 Debug( LDAP_DEBUG_TRACE,
1564                                 "ppolicy_modify: password attr lookup failed\n", 0, 0, 0 );
1565                         }
1566                 }
1567
1568                 /*
1569                  * Controversial bit here. If the new password isn't hashed
1570                  * (ie, is cleartext), we probably should hash it according
1571                  * to the default hash. The reason for this is that we want
1572                  * to use the policy if possible, but if we hash the password
1573                  * before, then we're going to run into trouble when it
1574                  * comes time to check the password.
1575                  *
1576                  * Now, the right thing to do is to use the extended password
1577                  * modify operation, but not all software can do this,
1578                  * therefore it makes sense to hash the new password, now
1579                  * we know it passes the policy requirements.
1580                  *
1581                  * Of course, if the password is already hashed, then we
1582                  * leave it alone.
1583                  */
1584
1585                 if ((pi->hash_passwords) && (addmod) && !newpw.bv_val && 
1586                         (password_scheme( &(addmod->sml_values[0]), NULL ) != LDAP_SUCCESS)) {
1587                         struct berval hpw, bv;
1588                         
1589                         slap_passwd_hash( &(addmod->sml_values[0]), &hpw, &txt );
1590                         if (hpw.bv_val == NULL) {
1591                                         /*
1592                                          * hashing didn't work. Emit an error.
1593                                          */
1594                                 rs->sr_err = LDAP_OTHER;
1595                                 rs->sr_text = txt;
1596                                 goto return_results;
1597                         }
1598                         bv.bv_val = addmod->sml_values[0].bv_val;
1599                         bv.bv_len = addmod->sml_values[0].bv_len;
1600                                 /* clear and discard the clear password */
1601                         memset(bv.bv_val, 0, bv.bv_len);
1602                         ber_memfree(bv.bv_val);
1603                         addmod->sml_values[0].bv_val = hpw.bv_val;
1604                         addmod->sml_values[0].bv_len = hpw.bv_len;
1605                 }
1606         }
1607         op->o_bd->bd_info = (BackendInfo *)on->on_info;
1608         be_entry_release_r( op, e );
1609         return SLAP_CB_CONTINUE;
1610
1611 return_results:
1612         free_pwd_history_list( &tl );
1613         op->o_bd->bd_info = (BackendInfo *)on->on_info;
1614         be_entry_release_r( op, e );
1615         if ( send_ctrl ) {
1616                 LDAPControl **ctrls = NULL;
1617
1618                 ctrls = ch_calloc( sizeof( LDAPControl *) , 2 );
1619                 ctrls[0] = create_passcontrol( -1, -1, pErr );
1620                 ctrls[1] = NULL;
1621                 rs->sr_ctrls = ctrls;
1622         }
1623         send_ldap_result( op, rs );
1624         return rs->sr_err;
1625 }
1626
1627 static int
1628 ppolicy_parseCtrl(
1629         Operation *op,
1630         SlapReply *rs,
1631         LDAPControl *ctrl )
1632 {
1633         if ( ctrl->ldctl_value.bv_len ) {
1634                 rs->sr_text = "passwordPolicyRequest control value not empty";
1635                 return LDAP_PROTOCOL_ERROR;
1636         }
1637         if ( ctrl->ldctl_iscritical ) {
1638                 rs->sr_text = "passwordPolicyRequest control invalid criticality";
1639                 return LDAP_PROTOCOL_ERROR;
1640         }
1641         op->o_ctrlflag[ppolicy_cid] = SLAP_CONTROL_NONCRITICAL;
1642
1643         return LDAP_SUCCESS;
1644 }
1645
1646 static int
1647 ppolicy_db_init(
1648         BackendDB *be
1649 )
1650 {
1651         slap_overinst *on = (slap_overinst *) be->bd_info;
1652
1653         /* Has User Schema been initialized yet? */
1654         if ( !pwd_UsSchema[0].ad[0] ) {
1655                 const char *err;
1656                 int i, code;
1657
1658                 for (i=0; pwd_UsSchema[i].def; i++) {
1659                         code = slap_str2ad( pwd_UsSchema[i].def, pwd_UsSchema[i].ad, &err );
1660                         if ( code ) {
1661                                 fprintf( stderr, "User Schema Load failed %d: %s\n", code, err );
1662                                 return code;
1663                         }
1664                 }
1665         }
1666
1667         on->on_bi.bi_private = ch_calloc( sizeof(pp_info), 1 );
1668
1669         if ( dtblsize && !pwcons )
1670                 pwcons = ch_calloc(sizeof(pw_conn), dtblsize );
1671
1672         return 0;
1673 }
1674
1675 static int
1676 ppolicy_db_open(
1677     BackendDB *be
1678 )
1679 {
1680         return overlay_register_control( be, LDAP_CONTROL_PASSWORDPOLICYREQUEST );
1681 }
1682
1683 static int
1684 ppolicy_close(
1685         BackendDB *be
1686 )
1687 {
1688         slap_overinst *on = (slap_overinst *) be->bd_info;
1689         pp_info *pi = on->on_bi.bi_private;
1690         
1691         free( pwcons );
1692         free( pi->def_policy.bv_val );
1693         free( pi );
1694
1695         return 0;
1696 }
1697
1698 static int
1699 ppolicy_config(
1700     BackendDB   *be,
1701     const char  *fname,
1702     int         lineno,
1703     int         argc,
1704     char        **argv
1705 )
1706 {
1707         slap_overinst *on = (slap_overinst *) be->bd_info;
1708         pp_info *pi = on->on_bi.bi_private;
1709         struct berval dn;
1710         
1711
1712         if ( strcasecmp( argv[0], "ppolicy_default" ) == 0 ) {
1713                 if ( argc != 2 ) {
1714                         fprintf( stderr, "%s: line %d: invalid arguments in \"ppolicy_default"
1715                                 " <policyDN>\n", fname, lineno );
1716                         return ( 1 );
1717                 }
1718                 ber_str2bv( argv[1], 0, 0, &dn );
1719                 if ( dnNormalize( 0, NULL, NULL, &dn, &pi->def_policy, NULL ) ) {
1720                         fprintf( stderr, "%s: line %d: policyDN is invalid\n",
1721                                 fname, lineno );
1722                         return 1;
1723                 }
1724                 return 0;
1725
1726         } else if ( strcasecmp( argv[0], "ppolicy_use_lockout" ) == 0 ) {
1727                 if ( argc != 1 ) {
1728                         fprintf( stderr, "%s: line %d: ppolicy_use_lockout "
1729                                 "takes no arguments\n", fname, lineno );
1730                         return ( 1 );
1731                 }
1732                 pi->use_lockout = 1;
1733                 return 0;
1734         } else if ( strcasecmp( argv[0], "ppolicy_hash_cleartext" ) == 0 ) {
1735                 if ( argc != 1 ) {
1736                         fprintf( stderr, "%s: line %d: ppolicy_hash_cleartext "
1737                                 "takes no arguments\n", fname, lineno );
1738                         return ( 1 );
1739                 }
1740                 pi->hash_passwords = 1;
1741         }
1742         return SLAP_CONF_UNKNOWN;
1743 }
1744
1745 static char *extops[] = {
1746         LDAP_EXOP_MODIFY_PASSWD,
1747         NULL
1748 };
1749
1750 static slap_overinst ppolicy;
1751
1752 int ppolicy_init()
1753 {
1754         LDAPAttributeType *at;
1755         const char *err;
1756         int i, code;
1757
1758         for (i=0; pwd_OpSchema[i].def; i++) {
1759                 at = ldap_str2attributetype( pwd_OpSchema[i].def, &code, &err,
1760                         LDAP_SCHEMA_ALLOW_ALL );
1761                 if ( !at ) {
1762                         fprintf( stderr, "AttributeType Load failed %s %s\n",
1763                                 ldap_scherr2str(code), err );
1764                         return code;
1765                 }
1766                 code = at_add( at, 0, NULL, &err );
1767                 if ( !code ) {
1768                         slap_str2ad( at->at_names[0], pwd_OpSchema[i].ad, &err );
1769                 }
1770                 ldap_memfree( at );
1771                 if ( code ) {
1772                         fprintf( stderr, "AttributeType Load failed %s %s\n",
1773                                 scherr2str(code), err );
1774                         return code;
1775                 }
1776         }
1777
1778         code = register_supported_control( LDAP_CONTROL_PASSWORDPOLICYREQUEST,
1779                 SLAP_CTRL_ADD|SLAP_CTRL_BIND|SLAP_CTRL_MODIFY|SLAP_CTRL_HIDE, extops,
1780                 ppolicy_parseCtrl, &ppolicy_cid );
1781         if ( code != LDAP_SUCCESS ) {
1782                 fprintf( stderr, "Failed to register control %d\n", code );
1783                 return code;
1784         }
1785
1786         ldap_pvt_thread_mutex_init( &chk_syntax_mutex );
1787
1788         ppolicy.on_bi.bi_type = "ppolicy";
1789         ppolicy.on_bi.bi_db_init = ppolicy_db_init;
1790         ppolicy.on_bi.bi_db_open = ppolicy_db_open;
1791         ppolicy.on_bi.bi_db_config = ppolicy_config;
1792         ppolicy.on_bi.bi_db_close = ppolicy_close;
1793
1794         ppolicy.on_bi.bi_op_add = ppolicy_add;
1795         ppolicy.on_bi.bi_op_bind = ppolicy_bind;
1796         ppolicy.on_bi.bi_op_unbind = ppolicy_unbind;
1797         ppolicy.on_bi.bi_op_compare = ppolicy_restrict;
1798         ppolicy.on_bi.bi_op_delete = ppolicy_restrict;
1799         ppolicy.on_bi.bi_op_modify = ppolicy_modify;
1800         ppolicy.on_bi.bi_op_search = ppolicy_restrict;
1801
1802         return overlay_register( &ppolicy );
1803 }
1804
1805 #if SLAPD_OVER_PPOLICY == SLAPD_MOD_DYNAMIC
1806 int init_module(int argc, char *argv[]) {
1807         return ppolicy_init();
1808 }
1809 #endif
1810
1811 #endif  /* defined(SLAPD_OVER_PPOLICY) */