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