]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/ppolicy.c
Sync with HEAD (ready for 2.3 "release"?)
[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                 char *sc = ch_calloc( sizeof(char), e + 2);
417                 sc[e + 1] = '\0'; /* terminate string */
418                 strncpy( sc, cred->bv_val, e + 1);
419                 e = lutil_passwd_scheme( sc );
420                 free( sc );
421                 if (e && sch) {
422                         sch->bv_val = cred->bv_val;
423                         sch->bv_len = e;
424                         return LDAP_SUCCESS;
425                 }
426         }
427         return LDAP_OTHER;
428 }
429
430 static int
431 check_password_quality( struct berval *cred, PassPolicy *pp, LDAPPasswordPolicyError *err, Entry *e )
432 {
433         int rc = LDAP_SUCCESS, ok = LDAP_SUCCESS;
434         char *ptr = cred->bv_val;
435         char *modpath;
436         struct berval sch;
437
438         assert( cred != NULL );
439         assert( pp != NULL );
440
441         if ((cred->bv_len == 0) || (pp->pwdMinLength > cred->bv_len)) {
442                 rc = LDAP_CONSTRAINT_VIOLATION;
443                 if ( err ) *err = PP_passwordTooShort;
444                 return rc;
445         }
446
447         /*
448          * We need to know if the password is already hashed - if so
449          * what scheme is it. The reason being that the "hash" of
450          * {cleartext} still allows us to check the password.
451          */
452         rc = password_scheme( cred, &sch );
453         if (rc == LDAP_SUCCESS) {
454                 if ((sch.bv_val) && (strncasecmp( sch.bv_val, "{cleartext}",
455                         sch.bv_len ) == 0)) {
456                         /*
457                          * We can check the cleartext "hash"
458                          */
459                         ptr = cred->bv_val + sch.bv_len;
460                 } else {
461                         /* everything else, we can't check */
462                         if (pp->pwdCheckQuality == 2) {
463                                 rc = LDAP_CONSTRAINT_VIOLATION;
464                                 if (err) *err = PP_insufficientPasswordQuality;
465                                 return rc;
466                         }
467                         /*
468                          * We can't check the syntax of the password, but it's not
469                          * mandatory (according to the policy), so we return success.
470                          */
471                     
472                         return LDAP_SUCCESS;
473                 }
474         }
475
476         rc = LDAP_SUCCESS;
477
478         if (pp->pwdCheckModule[0]) {
479 #if SLAPD_MODULES
480                 lt_dlhandle mod;
481                 const char *err;
482                 
483                 if ((mod = lt_dlopen( pp->pwdCheckModule )) == NULL) {
484                         err = lt_dlerror();
485
486                         Debug(LDAP_DEBUG_ANY,
487                         "check_password_quality: lt_dlopen failed: (%s) %s.\n",
488                                 pp->pwdCheckModule, err, 0 );
489                         ok = LDAP_OTHER; /* internal error */
490                 } else {
491                         int (*prog)( char *passwd, char **text, Entry *ent );
492
493                         if ((prog = lt_dlsym( mod, "check_password" )) == NULL) {
494                                 err = lt_dlerror();
495                             
496                                 Debug(LDAP_DEBUG_ANY,
497                                         "check_password_quality: lt_dlsym failed: (%s) %s.\n",
498                                         pp->pwdCheckModule, err, 0 );
499                                 ok = LDAP_OTHER;
500                         } else {
501                                 char *txt = NULL;
502
503                                 ldap_pvt_thread_mutex_lock( &chk_syntax_mutex );
504                                 ok = prog( cred->bv_val, &txt, e );
505                                 ldap_pvt_thread_mutex_unlock( &chk_syntax_mutex );
506                                 if (txt) {
507                                         Debug(LDAP_DEBUG_ANY,
508                                                 "check_password_quality: module error: (%s) %s.[%d]\n",
509                                                 pp->pwdCheckModule, txt, ok );
510                                         free(txt);
511                                 } else
512                                         ok = LDAP_SUCCESS;
513                         }
514                             
515                         lt_dlclose( mod );
516                 }
517 #else
518         Debug(LDAP_DEBUG_ANY, "check_password_quality: external modules not "
519                 "supported. pwdCheckModule ignored.\n", 0, 0, 0);
520 #endif /* SLAPD_MODULES */
521         }
522                 
523                     
524         if (ok != LDAP_SUCCESS) {
525                 rc = LDAP_CONSTRAINT_VIOLATION;
526                 if (err) *err = PP_insufficientPasswordQuality;
527         }
528         
529         return rc;
530 }
531
532 static int
533 parse_pwdhistory( struct berval *bv, char **oid, time_t *oldtime, struct berval *oldpw )
534 {
535         char *ptr;
536         struct berval nv, npw;
537         int i, j;
538         
539         assert (bv && (bv->bv_len > 0) && (bv->bv_val) && oldtime && oldpw );
540
541         if ( oid ) *oid = 0;
542         *oldtime = (time_t)-1;
543         oldpw->bv_val = NULL;
544         oldpw->bv_len = 0;
545         
546         ber_dupbv( &nv, bv );
547
548         /* first get the time field */
549         for(i=0; (i < nv.bv_len) && (nv.bv_val[i] != '#'); i++);
550         if ( i == nv.bv_len) goto exit_failure; /* couldn't locate the '#' separator */
551         nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */
552         ptr = nv.bv_val;
553         *oldtime = parse_time( ptr );
554         if (*oldtime == (time_t)-1) goto exit_failure;
555
556         /* get the OID field */
557         for(ptr = &(nv.bv_val[i]);(i < nv.bv_len) && (nv.bv_val[i] != '#'); i++);
558         if ( i == nv.bv_len) goto exit_failure; /* couldn't locate the '#' separator */
559         nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */
560         if ( oid ) *oid = ber_strdup( ptr );
561         
562         /* get the length field */
563         for(ptr = &(nv.bv_val[i]);(i < nv.bv_len) && (nv.bv_val[i] != '#'); i++);
564         if ( i == nv.bv_len) goto exit_failure; /* couldn't locate the '#' separator */
565         nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */
566         oldpw->bv_len = strtol( ptr, NULL, 10 );
567         if (errno == ERANGE) goto exit_failure;
568
569         /* lastly, get the octets of the string */
570         for(j=i, ptr = &(nv.bv_val[i]);i < nv.bv_len; i++);
571         if (i-j != oldpw->bv_len) goto exit_failure; /* length is wrong */
572
573         npw.bv_val = ptr;
574         npw.bv_len = oldpw->bv_len;
575         ber_dupbv( oldpw, &npw );
576         
577         return LDAP_SUCCESS;
578 exit_failure:
579         if (oid && *oid) { ber_memfree(*oid); *oid = NULL; }
580         if (oldpw->bv_val) {
581                 ber_memfree( oldpw->bv_val); oldpw->bv_val = NULL;
582                 oldpw->bv_len = 0;
583         }
584         ber_memfree(nv.bv_val);
585         return LDAP_OTHER;
586 }
587
588 static void
589 add_to_pwd_history( pw_hist **l, time_t t,
590                     struct berval *oldpw, struct berval *bv )
591 {
592         pw_hist *p, *p1, *p2;
593     
594         if (!l) return;
595
596         p = ch_malloc( sizeof( pw_hist ));
597         p->pw = *oldpw;
598         ber_dupbv( &p->bv, bv );
599         p->t = t;
600         p->next = NULL;
601         
602         if (*l == NULL) {
603                 /* degenerate case */
604                 *l = p;
605                 return;
606         }
607         /*
608          * advance p1 and p2 such that p1 is the node before the
609          * new one, and p2 is the node after it
610          */
611         for (p1 = NULL, p2 = *l; p2 && p2->t <= t; p1 = p2, p2=p2->next );
612         p->next = p2;
613         if (p1 == NULL) { *l = p; return; }
614         p1->next = p;
615 }
616
617 #ifndef MAX_PWD_HISTORY_SZ
618 #define MAX_PWD_HISTORY_SZ 1024
619 #endif /* MAX_PWD_HISTORY_SZ */
620
621 static void
622 make_pwd_history_value( char *timebuf, struct berval *bv, Attribute *pa )
623 {
624         char str[ MAX_PWD_HISTORY_SZ ];
625         int nlen;
626
627         snprintf( str, MAX_PWD_HISTORY_SZ,
628                   "%s#%s#%d#", timebuf,
629                   pa->a_desc->ad_type->sat_syntax->ssyn_oid,
630                   pa->a_nvals[0].bv_len );
631         str[MAX_PWD_HISTORY_SZ-1] = 0;
632         nlen = strlen(str);
633
634         /*
635          * We have to assume that the string is a string of octets,
636          * not readable characters. In reality, yes, it probably is
637          * a readable (ie, base64) string, but we can't count on that
638          * Hence, while the first 3 fields of the password history
639          * are definitely readable (a timestamp, an OID and an integer
640          * length), the remaining octets of the actual password
641          * are deemed to be binary data.
642          */
643         AC_MEMCPY( str + nlen, pa->a_nvals[0].bv_val, pa->a_nvals[0].bv_len );
644         nlen += pa->a_nvals[0].bv_len;
645         bv->bv_val = ch_malloc( nlen + 1 );
646         AC_MEMCPY( bv->bv_val, str, nlen );
647         bv->bv_val[nlen] = '\0';
648         bv->bv_len = nlen;
649 }
650
651 static void
652 free_pwd_history_list( pw_hist **l )
653 {
654         pw_hist *p;
655     
656         if (!l) return;
657         p = *l;
658         while (p) {
659                 pw_hist *pp = p->next;
660
661                 free(p->pw.bv_val);
662                 free(p->bv.bv_val);
663                 free(p);
664                 p = pp;
665         }
666         *l = NULL;
667 }
668
669 typedef struct ppbind {
670         slap_overinst *on;
671         int send_ctrl;
672         Modifications *mod;
673         LDAPPasswordPolicyError pErr;
674         PassPolicy pp;
675 } ppbind;
676
677 static int
678 ppolicy_bind_resp( Operation *op, SlapReply *rs )
679 {
680         ppbind *ppb = op->o_callback->sc_private;
681         slap_overinst *on = ppb->on;
682         Modifications *mod = ppb->mod, *m;
683         int pwExpired = 0;
684         int ngut = -1, warn = -1, age, rc, i;
685         Attribute *a;
686         time_t now, then, pwtime = (time_t)-1;
687         const char *txt;
688         char nowstr[ LDAP_LUTIL_GENTIME_BUFSIZE ];
689         struct berval timestamp;
690         BackendInfo *bi = op->o_bd->bd_info;
691         Entry *e;
692
693         /* If we already know it's locked, just get on with it */
694         if ( ppb->pErr != PP_noError ) {
695                 goto locked;
696         }
697
698         op->o_bd->bd_info = (BackendInfo *)on->on_info;
699         rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
700         op->o_bd->bd_info = bi;
701
702         if ( rc != LDAP_SUCCESS ) {
703                 return SLAP_CB_CONTINUE;
704         }
705
706         now = slap_get_time(); /* stored for later consideration */
707         timestamp.bv_val = nowstr;
708         timestamp.bv_len = sizeof(nowstr);
709         slap_timestamp( &now, &timestamp );
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_flags = 0;
717                 m->sml_type = ad_pwdFailureTime->ad_cname;
718                 m->sml_desc = ad_pwdFailureTime;
719                 m->sml_values = ch_calloc( sizeof(struct berval), 2 );
720
721                 ber_dupbv( &m->sml_values[0], &timestamp );
722                 m->sml_next = mod;
723                 mod = m;
724
725                 /*
726                  * Count the pwdFailureTimes - if it's
727                  * greater than the policy pwdMaxFailure,
728                  * then lock the account.
729                  */
730                 if ((a = attr_find( e->e_attrs, ad_pwdFailureTime )) != NULL) {
731                         for(i=0; a->a_nvals[i].bv_val; i++) {
732
733                                 /*
734                                  * If the interval is 0, then failures
735                                  * stay on the record until explicitly
736                                  * reset by successful authentication.
737                                  */
738                                 if (ppb->pp.pwdFailureCountInterval == 0) {
739                                         fc++;
740                                 } else if (now <=
741                                                         parse_time(a->a_nvals[i].bv_val) +
742                                                         ppb->pp.pwdFailureCountInterval) {
743
744                                         fc++;
745                                 }
746                                 /*
747                                  * We only count those failures
748                                  * which are not due to expire.
749                                  */
750                         }
751                 }
752                 
753                 if ((ppb->pp.pwdMaxFailure > 0) &&
754                         (fc >= ppb->pp.pwdMaxFailure - 1)) {
755
756                         /*
757                          * We subtract 1 from the failure max
758                          * because the new failure entry hasn't
759                          * made it to the entry yet.
760                          */
761                         m = ch_calloc( sizeof(Modifications), 1 );
762                         m->sml_op = LDAP_MOD_REPLACE;
763                         m->sml_flags = 0;
764                         m->sml_type = ad_pwdAccountLockedTime->ad_cname;
765                         m->sml_desc = ad_pwdAccountLockedTime;
766                         m->sml_values = ch_calloc( sizeof(struct berval), 2 );
767                         ber_dupbv( &m->sml_values[0], &timestamp );
768                         m->sml_next = mod;
769                         mod = m;
770                 }
771         } else if ( rs->sr_err == LDAP_SUCCESS ) {
772                 if ((a = attr_find( e->e_attrs, ad_pwdChangedTime )) != NULL)
773                         pwtime = parse_time( a->a_nvals[0].bv_val );
774
775                 /* delete all pwdFailureTimes */
776                 if ( attr_find( e->e_attrs, ad_pwdFailureTime )) {
777                         m = ch_calloc( sizeof(Modifications), 1 );
778                         m->sml_op = LDAP_MOD_DELETE;
779                         m->sml_flags = 0;
780                         m->sml_type = ad_pwdFailureTime->ad_cname;
781                         m->sml_desc = ad_pwdFailureTime;
782                         m->sml_next = mod;
783                         mod = m;
784                 }
785
786                 /*
787                  * check to see if the password must be changed
788                  */
789                 if ( ppb->pp.pwdMustChange &&
790                         (a = attr_find( e->e_attrs, ad_pwdReset )) &&
791                         !strcmp( a->a_nvals[0].bv_val, "TRUE" ) ) {
792                         /*
793                          * need to inject client controls here to give
794                          * more information. For the moment, we ensure
795                          * that we are disallowed from doing anything
796                          * other than change password.
797                          */
798                         pwcons[op->o_conn->c_conn_idx].restricted = 1;
799
800                         ppb->pErr = PP_changeAfterReset;
801
802                 } else {
803                         /*
804                          * the password does not need to be changed, so
805                          * we now check whether the password has expired.
806                          *
807                          * We can skip this bit if passwords don't age in
808                          * the policy.
809                          */
810                         if (ppb->pp.pwdMaxAge == 0) goto grace;
811
812                         if (pwtime == (time_t)-1) {
813                                 /*
814                                  * Hmm. No password changed time on the
815                                  * entry. This is odd - it should have
816                                  * been provided when the attribute was added.
817                                  *
818                                  * However, it's possible that it could be
819                                  * missing if the DIT was established via
820                                  * an import process.
821                                  */
822                                 Debug( LDAP_DEBUG_ANY,
823                                         "ppolicy_bind: Entry %s does not have valid pwdChangedTime attribute - assuming password expired\n",
824                                         e->e_name.bv_val, 0, 0);
825                                 
826                                 pwExpired = 1;
827                         } else {
828                                 /*
829                                  * Check: was the last change time of
830                                  * the password older than the maximum age
831                                  * allowed. (Ignore case 2 from I-D, it's just silly.)
832                                  */
833                                 if (now - pwtime > ppb->pp.pwdMaxAge ) pwExpired = 1;
834                         }
835                 }
836
837 grace:
838                 if (!pwExpired) goto check_expiring_password;
839                 
840                 if ((a = attr_find( e->e_attrs, ad_pwdGraceUseTime )) == NULL)
841                         ngut = ppb->pp.pwdGraceAuthNLimit;
842                 else {
843                         for(ngut=0; a->a_nvals[ngut].bv_val; ngut++);
844                         ngut = ppb->pp.pwdGraceAuthNLimit - ngut;
845                 }
846
847                 /*
848                  * ngut is the number of remaining grace logins
849                  */
850                 Debug( LDAP_DEBUG_ANY,
851                         "ppolicy_bind: Entry %s has an expired password: %d grace logins\n",
852                         e->e_name.bv_val, ngut, 0);
853                 
854                 if (ngut < 1) {
855                         ppb->pErr = PP_passwordExpired;
856                         rs->sr_err = LDAP_INVALID_CREDENTIALS;
857                         goto done;
858                 }
859
860                 /*
861                  * Add a grace user time to the entry
862                  */
863                 m = ch_calloc( sizeof(Modifications), 1 );
864                 m->sml_op = LDAP_MOD_ADD;
865                 m->sml_flags = 0;
866                 m->sml_type = ad_pwdGraceUseTime->ad_cname;
867                 m->sml_desc = ad_pwdGraceUseTime;
868                 m->sml_values = ch_calloc( sizeof(struct berval), 2 );
869                 ber_dupbv( &m->sml_values[0], &timestamp );
870                 m->sml_next = mod;
871                 mod = m;
872
873 check_expiring_password:
874                 /*
875                  * Now we need to check to see
876                  * if it is about to expire, and if so, should the user
877                  * be warned about it in the password policy control.
878                  *
879                  * If the password has expired, and we're in the grace period, then
880                  * we don't need to do this bit. Similarly, if we don't have password
881                  * aging, then there's no need to do this bit either.
882                  */
883                 if ((ppb->pp.pwdMaxAge < 1) || (pwExpired) || (ppb->pp.pwdExpireWarning < 1))
884                         goto done;
885
886                 age = (int)(now - pwtime);
887                 
888                 /*
889                  * We know that there is a password Change Time attribute - if
890                  * there wasn't, then the pwdExpired value would be true, unless
891                  * there is no password aging - and if there is no password aging,
892                  * then this section isn't called anyway - you can't have an
893                  * expiring password if there's no limit to expire.
894                  */
895                 if (ppb->pp.pwdMaxAge - age < ppb->pp.pwdExpireWarning ) {
896                         /*
897                          * Set the warning value.
898                          */
899                         warn = ppb->pp.pwdMaxAge - age; /* seconds left until expiry */
900                         if (warn < 0) warn = 0; /* something weird here - why is pwExpired not set? */
901                         
902                         Debug( LDAP_DEBUG_ANY,
903                                 "ppolicy_bind: Setting warning for password expiry for %s = %d seconds\n",
904                                 op->o_req_dn.bv_val, warn, 0 );
905                 }
906         }
907
908 done:
909         op->o_bd->bd_info = (BackendInfo *)on->on_info;
910         be_entry_release_r( op, e );
911
912 locked:
913         if ( mod ) {
914                 Operation op2 = *op;
915                 SlapReply r2 = { REP_RESULT };
916                 slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
917
918                 /* FIXME: Need to handle replication of some (but not all)
919                  * of the operational attributes...
920                  */
921                 op2.o_tag = LDAP_REQ_MODIFY;
922                 op2.o_callback = &cb;
923                 op2.orm_modlist = mod;
924                 op2.o_dn = op->o_bd->be_rootdn;
925                 op2.o_ndn = op->o_bd->be_rootndn;
926                 op2.o_bd->bd_info = (BackendInfo *)on->on_info;
927                 rc = op->o_bd->be_modify( &op2, &r2 );
928                 slap_mods_free( mod );
929         }
930
931         if ( ppb->send_ctrl ) {
932                 LDAPControl **ctrls = NULL;
933                 pp_info *pi = on->on_bi.bi_private;
934
935                 /* Do we really want to tell that the account is locked? */
936                 if ( ppb->pErr == PP_accountLocked && !pi->use_lockout ) {
937                         ppb->pErr = PP_noError;
938                 }
939                 ctrls = ch_calloc( sizeof( LDAPControl *) , 2 );
940                 ctrls[0] = create_passcontrol( warn, ngut, ppb->pErr );
941                 ctrls[1] = NULL;
942                 rs->sr_ctrls = ctrls;
943         }
944         op->o_bd->bd_info = bi;
945         return SLAP_CB_CONTINUE;
946 }
947
948 static int
949 ppolicy_bind( Operation *op, SlapReply *rs )
950 {
951         slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
952
953         /* Root bypasses policy */
954         if ( !be_isroot_dn( op->o_bd, &op->o_req_ndn )) {
955                 Entry *e;
956                 int i, rc;
957                 ppbind *ppb;
958                 slap_callback *cb;
959
960                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
961                 rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
962
963                 if ( rc != LDAP_SUCCESS ) {
964                         return SLAP_CB_CONTINUE;
965                 }
966
967                 cb = op->o_tmpcalloc( sizeof(ppbind)+sizeof(slap_callback),
968                         1, op->o_tmpmemctx );
969                 ppb = (ppbind *)(cb+1);
970                 ppb->on = on;
971                 ppb->pErr = PP_noError;
972
973                 /* Setup a callback so we can munge the result */
974
975                 cb->sc_response = ppolicy_bind_resp;
976                 cb->sc_next = op->o_callback->sc_next;
977                 cb->sc_private = ppb;
978                 op->o_callback->sc_next = cb;
979
980                 /* Did we receive a password policy request control? */
981                 if ( op->o_ctrlflag[ppolicy_cid] ) {
982                         ppb->send_ctrl = 1;
983                 }
984
985                 op->o_bd->bd_info = (BackendInfo *)on;
986                 ppolicy_get( op, e, &ppb->pp );
987
988                 rc = account_locked( op, e, &ppb->pp, &ppb->mod );
989
990                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
991                 be_entry_release_r( op, e );
992
993                 if ( rc ) {
994                         /* This will be the Draft 8 response, Unwilling is bogus */
995                         ppb->pErr = PP_accountLocked;
996                         send_ldap_error( op, rs, LDAP_INVALID_CREDENTIALS, NULL );
997                         return rs->sr_err;
998                 }
999
1000         }
1001
1002         return SLAP_CB_CONTINUE;
1003 }
1004
1005 /* Reset the restricted flag for the next session on this connection */
1006 static int
1007 ppolicy_unbind( Operation *op, SlapReply *rs )
1008 {
1009         pwcons[op->o_conn->c_conn_idx].restricted = 0;
1010         return SLAP_CB_CONTINUE;
1011 }
1012
1013 /* Check if this connection is restricted */
1014 static int
1015 ppolicy_restrict(
1016         Operation *op,
1017         SlapReply *rs )
1018 {
1019         slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1020         int i, send_ctrl = 0;
1021
1022         /* Did we receive a password policy request control? */
1023         if ( op->o_ctrlflag[ppolicy_cid] ) {
1024                 send_ctrl = 1;
1025         }
1026
1027         if ( op->o_conn && pwcons[op->o_conn->c_conn_idx].restricted ) {
1028                 Debug( LDAP_DEBUG_TRACE,
1029                         "connection restricted to password changing only\n", 0, 0, 0);
1030                 if ( send_ctrl ) {
1031                         LDAPControl **ctrls = NULL;
1032
1033                         ctrls = ch_calloc( sizeof( LDAPControl *) , 2 );
1034                         ctrls[0] = create_passcontrol( -1, -1, PP_changeAfterReset );
1035                         ctrls[1] = NULL;
1036                         rs->sr_ctrls = ctrls;
1037                 }
1038                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1039                 send_ldap_error( op, rs, LDAP_INSUFFICIENT_ACCESS, 
1040                         "Operations are restricted to bind/unbind/abandon/StartTLS/modify password" );
1041                 return rs->sr_err;
1042         }
1043
1044         return SLAP_CB_CONTINUE;
1045 }
1046
1047 static int
1048 ppolicy_add(
1049         Operation *op,
1050         SlapReply *rs )
1051 {
1052         slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1053         pp_info *pi = on->on_bi.bi_private;
1054         PassPolicy pp;
1055         int pw;
1056         Attribute *pa;
1057         const char *txt;
1058
1059         if ( ppolicy_restrict( op, rs ) != SLAP_CB_CONTINUE )
1060                 return rs->sr_err;
1061
1062         /* Check for password in entry */
1063         if ((pa = attr_find( op->oq_add.rs_e->e_attrs,
1064                 slap_schema.si_ad_userPassword )))
1065         {
1066                 /*
1067                  * new entry contains a password - if we're not the root user
1068                  * then we need to check that the password fits in with the
1069                  * security policy for the new entry.
1070                  */
1071                 ppolicy_get( op, op->ora_e, &pp );
1072                 if (pp.pwdCheckQuality > 0 && !be_isroot( op )) {
1073                         struct berval *bv = &(pa->a_vals[0]);
1074                         int rc, i, send_ctrl = 0; 
1075                         LDAPPasswordPolicyError pErr = PP_noError;
1076
1077                         /* Did we receive a password policy request control? */
1078                         if ( op->o_ctrlflag[ppolicy_cid] ) {
1079                                 send_ctrl = 1;
1080                         }
1081                         rc = check_password_quality( bv, &pp, &pErr, op->ora_e );
1082                         if (rc != LDAP_SUCCESS) {
1083                                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1084                                 if ( send_ctrl ) {
1085                                         LDAPControl **ctrls = NULL;
1086
1087                                         ctrls = ch_calloc( sizeof( LDAPControl *) , 2 );
1088                                         ctrls[0] = create_passcontrol( -1, -1, pErr );
1089                                         ctrls[1] = NULL;
1090                                         rs->sr_ctrls = ctrls;
1091                                 }
1092                                 send_ldap_error( op, rs, rc, "Password fails quality checking policy" );
1093                                 return rs->sr_err;
1094                         }
1095                             /*
1096                              * A controversial bit. We hash cleartext
1097                              * passwords provided via add and modify operations
1098                              * You're not really supposed to do this, since
1099                              * the X.500 model says "store attributes" as they
1100                              * get provided. By default, this is what we do
1101                              *
1102                              * But if the hash_passwords flag is set, we hash
1103                              * any cleartext password attribute values via the
1104                              * default password hashing scheme.
1105                              */
1106                         if ((pi->hash_passwords) &&
1107                                 (password_scheme( &(pa->a_vals[0]), NULL ) != LDAP_SUCCESS)) {
1108                                 struct berval hpw;
1109
1110                                 slap_passwd_hash( &(pa->a_vals[0]), &hpw, &txt );
1111                                 if (hpw.bv_val == NULL) {
1112                                     /*
1113                                      * hashing didn't work. Emit an error.
1114                                      */
1115                                         rs->sr_err = LDAP_OTHER;
1116                                         rs->sr_text = txt;
1117                                         send_ldap_error( op, rs, LDAP_OTHER, "Password hashing failed" );
1118                                         return rs->sr_err;
1119                                 }
1120
1121                                 memset( pa->a_vals[0].bv_val, 0, pa->a_vals[0].bv_len);
1122                                 ber_memfree( pa->a_vals[0].bv_val );
1123                                 pa->a_vals[0].bv_val = hpw.bv_val;
1124                                 pa->a_vals[0].bv_len = hpw.bv_len;
1125                         }
1126                 }
1127                 /* If password aging is in effect, set the pwdChangedTime */
1128                 if (( pp.pwdMaxAge || pp.pwdMinAge ) && !be_shadow_update( op )) {
1129                         struct berval timestamp;
1130                         char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
1131                         time_t now = slap_get_time();
1132
1133                         timestamp.bv_val = timebuf;
1134                         timestamp.bv_len = sizeof(timebuf);
1135                         slap_timestamp( &now, &timestamp );
1136
1137                         attr_merge_one( op->ora_e, ad_pwdChangedTime, &timestamp, NULL );
1138                 }
1139         }
1140         return SLAP_CB_CONTINUE;
1141 }
1142
1143 static int
1144 ppolicy_modify( Operation *op, SlapReply *rs )
1145 {
1146         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
1147         pp_info                 *pi = on->on_bi.bi_private;
1148         int                     i, rc, mod_pw_only, pwmod, pwmop, deladd,
1149                                 hsize = 0;
1150         PassPolicy              pp;
1151         Modifications           *mods = NULL, *modtail, *ml, *delmod, *addmod;
1152         Attribute               *pa, *ha, *ra, at;
1153         const char              *txt;
1154         pw_hist                 *tl = NULL, *p;
1155         int                     zapReset, send_ctrl = 0;
1156         Entry                   *e;
1157         struct berval           newpw = BER_BVNULL, oldpw = BER_BVNULL,
1158                                 *bv, cr[2];
1159         LDAPPasswordPolicyError pErr = PP_noError;
1160
1161         op->o_bd->bd_info = (BackendInfo *)on->on_info;
1162         rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
1163         op->o_bd->bd_info = (BackendInfo *)on;
1164
1165         if ( rc != LDAP_SUCCESS ) return SLAP_CB_CONTINUE;
1166
1167         /* Did we receive a password policy request control? */
1168         if ( op->o_ctrlflag[ppolicy_cid] ) {
1169                 send_ctrl = 1;
1170         }
1171
1172         /* See if this is a pwdModify exop. If so, we can
1173          * access the plaintext passwords from that request.
1174          */
1175         {
1176                 slap_callback *sc;
1177
1178                 for ( sc = op->o_callback; sc; sc=sc->sc_next ) {
1179                         if ( sc->sc_response == slap_replog_cb &&
1180                                 sc->sc_private ) {
1181                                 req_pwdexop_s *qpw = sc->sc_private;
1182                                 newpw = qpw->rs_new;
1183                                 oldpw = qpw->rs_old;
1184                                 break;
1185                         }
1186                 }
1187         }
1188
1189         ppolicy_get( op, e, &pp );
1190
1191         for(ml = op->oq_modify.rs_modlist,
1192                         pwmod = 0, mod_pw_only = 1,
1193                         deladd = 0, delmod = NULL,
1194                         addmod = NULL,
1195                         zapReset = 1;
1196                 ml != NULL; modtail = ml, ml = ml->sml_next ) {
1197                 if ( ml->sml_desc == pp.ad ) {
1198                         pwmod = 1;
1199                         pwmop = ml->sml_op;
1200                         if ((deladd == 0) && (ml->sml_op == LDAP_MOD_DELETE) &&
1201                                 (ml->sml_values) && (ml->sml_values[0].bv_val != NULL)) {
1202                                 deladd = 1;
1203                                 delmod = ml;
1204                         }
1205
1206                         if ((deladd == 1) && ((ml->sml_op == LDAP_MOD_ADD) ||
1207                                                                   (ml->sml_op == LDAP_MOD_REPLACE)))
1208                                 deladd = 2;
1209
1210                         if ((ml->sml_op == LDAP_MOD_ADD) ||
1211                                 (ml->sml_op == LDAP_MOD_REPLACE))
1212                                 addmod = ml;
1213                 } else if (! is_at_operational( ml->sml_desc->ad_type )) {
1214                         mod_pw_only = 0;
1215                         /* modifying something other than password */
1216                 }
1217
1218                 /*
1219                  * If there is a request to explicitly add a pwdReset
1220                  * attribute, then we suppress the normal behaviour on
1221                  * password change, which is to remove the pwdReset
1222                  * attribute.
1223                  *
1224                  * This enables an administrator to assign a new password
1225                  * and place a "must reset" flag on the entry, which will
1226                  * stay until the user explicitly changes his/her password.
1227                  */
1228                 if (ml->sml_desc == ad_pwdReset ) {
1229                         if ((ml->sml_op == LDAP_MOD_ADD) ||
1230                                 (ml->sml_op == LDAP_MOD_REPLACE))
1231                                 zapReset = 0;
1232                 }
1233         }
1234         
1235         if (pwcons[op->o_conn->c_conn_idx].restricted && !mod_pw_only) {
1236                 Debug( LDAP_DEBUG_TRACE,
1237                         "connection restricted to password changing only\n", 0, 0, 0 );
1238                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 
1239                 rs->sr_text = "Operations are restricted to bind/unbind/abandon/StartTLS/modify password";
1240                 pErr = PP_changeAfterReset;
1241                 goto return_results;
1242         }
1243
1244         /*
1245          * if we have a "safe password modify policy", then we need to check if we're doing
1246          * a delete (with the old password), followed by an add (with the new password).
1247          *
1248          * If we don't have this, then we fail with an error. We also skip all the checks if
1249          * the root user is bound. Root can do anything, including avoid the policies.
1250          */
1251
1252         if (!pwmod) goto do_modify;
1253
1254         /*
1255          * Did we get a valid add mod?
1256          */
1257
1258         if (!addmod) {
1259                 rs->sr_err = LDAP_OTHER;
1260                 rs->sr_text = "Internal Error";
1261                 Debug( LDAP_DEBUG_TRACE,
1262                         "cannot locate modification supplying new password\n", 0, 0, 0 );
1263                 goto return_results;
1264         }
1265
1266         /*
1267          * Build the password history list in ascending time order
1268          * We need this, even if the user is root, in order to maintain
1269          * the pwdHistory operational attributes properly.
1270          */
1271         if (pp.pwdInHistory > 0 && (ha = attr_find( e->e_attrs, ad_pwdHistory ))) {
1272                 struct berval oldpw;
1273                 time_t oldtime;
1274                 char *oid;
1275
1276                 for(i=0; ha->a_nvals[i].bv_val; i++) {
1277                         rc = parse_pwdhistory( &(ha->a_nvals[i]), NULL,
1278                                 &oldtime, &oldpw );
1279
1280                         if (rc != LDAP_SUCCESS) continue; /* invalid history entry */
1281
1282                         if (oldpw.bv_val) {
1283                                 add_to_pwd_history( &tl, oldtime, &oldpw,
1284                                         &(ha->a_nvals[i]) );
1285                                 oldpw.bv_val = NULL;
1286                                 oldpw.bv_len = 0;
1287                         }
1288                 }
1289                 for(p=tl; p; p=p->next, hsize++); /* count history size */
1290         }
1291
1292         if (be_isroot( op )) goto do_modify;
1293
1294         /* This is a pwdModify exop that provided the old pw.
1295          * We need to create a Delete mod for this old pw and 
1296          * let the matching value get found later
1297          */
1298         if (pp.pwdSafeModify && oldpw.bv_val ) {
1299                 ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
1300                 ml->sml_op = LDAP_MOD_DELETE;
1301                 ml->sml_flags = SLAP_MOD_INTERNAL;
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                 time_t now = slap_get_time();
1441                 Attribute *ga;
1442                 
1443                 /*
1444                  * keep the necessary pwd.. operational attributes
1445                  * up to date.
1446                  */
1447
1448                 timestamp.bv_val = timebuf;
1449                 timestamp.bv_len = sizeof(timebuf);
1450                 slap_timestamp( &now, &timestamp );
1451
1452                 mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1453                 mods->sml_type.bv_val = NULL;
1454                 mods->sml_desc = ad_pwdChangedTime;
1455                 if (pwmop != LDAP_MOD_DELETE) {
1456                         mods->sml_op = LDAP_MOD_REPLACE;
1457                         mods->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
1458                         ber_dupbv( &mods->sml_values[0], &timestamp );
1459                         mods->sml_values[1].bv_len = 0;
1460                         mods->sml_values[1].bv_val = NULL;
1461                         assert( mods->sml_values[0].bv_val );
1462                 } else {
1463                         mods->sml_op = LDAP_MOD_DELETE;
1464                         mods->sml_values = NULL;
1465                 }
1466                 mods->sml_flags = SLAP_MOD_INTERNAL;
1467                 mods->sml_nvalues = NULL;
1468                 mods->sml_next = NULL;
1469                 modtail->sml_next = mods;
1470                 modtail = mods;
1471
1472                 if (attr_find(e->e_attrs, ad_pwdGraceUseTime )) {
1473                         mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1474                         mods->sml_op = LDAP_MOD_DELETE;
1475                         mods->sml_flags = SLAP_MOD_INTERNAL;
1476                         mods->sml_type.bv_val = NULL;
1477                         mods->sml_desc = ad_pwdGraceUseTime;
1478                         mods->sml_values = NULL;
1479                         mods->sml_nvalues = NULL;
1480                         mods->sml_next = NULL;
1481                         modtail->sml_next = mods;
1482                         modtail = mods;
1483                 }
1484
1485                 /* Delete the pwdReset attribute, since it's being reset */
1486                 if ((zapReset) && (attr_find(e->e_attrs, ad_pwdReset ))) {
1487                         mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1488                         mods->sml_op = LDAP_MOD_DELETE;
1489                         mods->sml_flags = SLAP_MOD_INTERNAL;
1490                         mods->sml_type.bv_val = NULL;
1491                         mods->sml_desc = ad_pwdReset;
1492                         mods->sml_values = NULL;
1493                         mods->sml_nvalues = NULL;
1494                         mods->sml_next = NULL;
1495                         modtail->sml_next = mods;
1496                         modtail = mods;
1497                 }
1498
1499                 if (pp.pwdInHistory > 0) {
1500                         if (hsize >= pp.pwdInHistory) {
1501                                 /*
1502                                  * We use the >= operator, since we are going to add
1503                                  * the existing password attribute value into the
1504                                  * history - thus the cardinality of history values is
1505                                  * about to rise by one.
1506                                  *
1507                                  * If this would push it over the limit of history
1508                                  * values (remembering - the password policy could have
1509                                  * changed since the password was last altered), we must
1510                                  * delete at least 1 value from the pwdHistory list.
1511                                  *
1512                                  * In fact, we delete '(#pwdHistory attrs - max pwd
1513                                  * history length) + 1' values, starting with the oldest.
1514                                  * This is easily evaluated, since the linked list is
1515                                  * created in ascending time order.
1516                                  */
1517                                 mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1518                                 mods->sml_op = LDAP_MOD_DELETE;
1519                                 mods->sml_flags = SLAP_MOD_INTERNAL;
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_flags = SLAP_MOD_INTERNAL;
1553                                 mods->sml_type.bv_val = NULL;
1554                                 mods->sml_desc = ad_pwdHistory;
1555                                 mods->sml_nvalues = NULL;
1556                                 mods->sml_values = ch_calloc( sizeof( struct berval ), 2 );
1557                                 mods->sml_values[ 1 ].bv_val = NULL;
1558                                 mods->sml_values[ 1 ].bv_len = 0;
1559                                 make_pwd_history_value( timebuf, &mods->sml_values[0], pa );
1560                                 mods->sml_next = NULL;
1561                                 modtail->sml_next = mods;
1562                                 modtail = mods;
1563                         } else {
1564                                 Debug( LDAP_DEBUG_TRACE,
1565                                 "ppolicy_modify: password attr lookup failed\n", 0, 0, 0 );
1566                         }
1567                 }
1568
1569                 /*
1570                  * Controversial bit here. If the new password isn't hashed
1571                  * (ie, is cleartext), we probably should hash it according
1572                  * to the default hash. The reason for this is that we want
1573                  * to use the policy if possible, but if we hash the password
1574                  * before, then we're going to run into trouble when it
1575                  * comes time to check the password.
1576                  *
1577                  * Now, the right thing to do is to use the extended password
1578                  * modify operation, but not all software can do this,
1579                  * therefore it makes sense to hash the new password, now
1580                  * we know it passes the policy requirements.
1581                  *
1582                  * Of course, if the password is already hashed, then we
1583                  * leave it alone.
1584                  */
1585
1586                 if ((pi->hash_passwords) && (addmod) && !newpw.bv_val && 
1587                         (password_scheme( &(addmod->sml_values[0]), NULL ) != LDAP_SUCCESS)) {
1588                         struct berval hpw, bv;
1589                         
1590                         slap_passwd_hash( &(addmod->sml_values[0]), &hpw, &txt );
1591                         if (hpw.bv_val == NULL) {
1592                                         /*
1593                                          * hashing didn't work. Emit an error.
1594                                          */
1595                                 rs->sr_err = LDAP_OTHER;
1596                                 rs->sr_text = txt;
1597                                 goto return_results;
1598                         }
1599                         bv.bv_val = addmod->sml_values[0].bv_val;
1600                         bv.bv_len = addmod->sml_values[0].bv_len;
1601                                 /* clear and discard the clear password */
1602                         memset(bv.bv_val, 0, bv.bv_len);
1603                         ber_memfree(bv.bv_val);
1604                         addmod->sml_values[0].bv_val = hpw.bv_val;
1605                         addmod->sml_values[0].bv_len = hpw.bv_len;
1606                 }
1607         }
1608         op->o_bd->bd_info = (BackendInfo *)on->on_info;
1609         be_entry_release_r( op, e );
1610         return SLAP_CB_CONTINUE;
1611
1612 return_results:
1613         free_pwd_history_list( &tl );
1614         op->o_bd->bd_info = (BackendInfo *)on->on_info;
1615         be_entry_release_r( op, e );
1616         if ( send_ctrl ) {
1617                 LDAPControl **ctrls = NULL;
1618
1619                 ctrls = ch_calloc( sizeof( LDAPControl *) , 2 );
1620                 ctrls[0] = create_passcontrol( -1, -1, pErr );
1621                 ctrls[1] = NULL;
1622                 rs->sr_ctrls = ctrls;
1623         }
1624         send_ldap_result( op, rs );
1625         return rs->sr_err;
1626 }
1627
1628 static int
1629 ppolicy_parseCtrl(
1630         Operation *op,
1631         SlapReply *rs,
1632         LDAPControl *ctrl )
1633 {
1634         if ( ctrl->ldctl_value.bv_len ) {
1635                 rs->sr_text = "passwordPolicyRequest control value not empty";
1636                 return LDAP_PROTOCOL_ERROR;
1637         }
1638         if ( ctrl->ldctl_iscritical ) {
1639                 rs->sr_text = "passwordPolicyRequest control invalid criticality";
1640                 return LDAP_PROTOCOL_ERROR;
1641         }
1642         op->o_ctrlflag[ppolicy_cid] = SLAP_CONTROL_NONCRITICAL;
1643
1644         return LDAP_SUCCESS;
1645 }
1646
1647 static int
1648 ppolicy_db_init(
1649         BackendDB *be
1650 )
1651 {
1652         slap_overinst *on = (slap_overinst *) be->bd_info;
1653
1654         /* Has User Schema been initialized yet? */
1655         if ( !pwd_UsSchema[0].ad[0] ) {
1656                 const char *err;
1657                 int i, code;
1658
1659                 for (i=0; pwd_UsSchema[i].def; i++) {
1660                         code = slap_str2ad( pwd_UsSchema[i].def, pwd_UsSchema[i].ad, &err );
1661                         if ( code ) {
1662                                 fprintf( stderr, "User Schema Load failed %d: %s\n", code, err );
1663                                 return code;
1664                         }
1665                 }
1666         }
1667
1668         on->on_bi.bi_private = ch_calloc( sizeof(pp_info), 1 );
1669
1670         if ( dtblsize && !pwcons )
1671                 pwcons = ch_calloc(sizeof(pw_conn), dtblsize );
1672
1673         return 0;
1674 }
1675
1676 static int
1677 ppolicy_db_open(
1678     BackendDB *be
1679 )
1680 {
1681         return overlay_register_control( be, LDAP_CONTROL_PASSWORDPOLICYREQUEST );
1682 }
1683
1684 static int
1685 ppolicy_close(
1686         BackendDB *be
1687 )
1688 {
1689         slap_overinst *on = (slap_overinst *) be->bd_info;
1690         pp_info *pi = on->on_bi.bi_private;
1691         
1692         free( pwcons );
1693         free( pi->def_policy.bv_val );
1694         free( pi );
1695
1696         return 0;
1697 }
1698
1699 static int
1700 ppolicy_config(
1701     BackendDB   *be,
1702     const char  *fname,
1703     int         lineno,
1704     int         argc,
1705     char        **argv
1706 )
1707 {
1708         slap_overinst *on = (slap_overinst *) be->bd_info;
1709         pp_info *pi = on->on_bi.bi_private;
1710         struct berval dn;
1711         
1712
1713         if ( strcasecmp( argv[0], "ppolicy_default" ) == 0 ) {
1714                 if ( argc != 2 ) {
1715                         fprintf( stderr, "%s: line %d: invalid arguments in \"ppolicy_default"
1716                                 " <policyDN>\n", fname, lineno );
1717                         return ( 1 );
1718                 }
1719                 ber_str2bv( argv[1], 0, 0, &dn );
1720                 if ( dnNormalize( 0, NULL, NULL, &dn, &pi->def_policy, NULL ) ) {
1721                         fprintf( stderr, "%s: line %d: policyDN is invalid\n",
1722                                 fname, lineno );
1723                         return 1;
1724                 }
1725                 return 0;
1726
1727         } else if ( strcasecmp( argv[0], "ppolicy_use_lockout" ) == 0 ) {
1728                 if ( argc != 1 ) {
1729                         fprintf( stderr, "%s: line %d: ppolicy_use_lockout "
1730                                 "takes no arguments\n", fname, lineno );
1731                         return ( 1 );
1732                 }
1733                 pi->use_lockout = 1;
1734                 return 0;
1735         } else if ( strcasecmp( argv[0], "ppolicy_hash_cleartext" ) == 0 ) {
1736                 if ( argc != 1 ) {
1737                         fprintf( stderr, "%s: line %d: ppolicy_hash_cleartext "
1738                                 "takes no arguments\n", fname, lineno );
1739                         return ( 1 );
1740                 }
1741                 pi->hash_passwords = 1;
1742         }
1743         return SLAP_CONF_UNKNOWN;
1744 }
1745
1746 static char *extops[] = {
1747         LDAP_EXOP_MODIFY_PASSWD,
1748         NULL
1749 };
1750
1751 static slap_overinst ppolicy;
1752
1753 int ppolicy_init()
1754 {
1755         LDAPAttributeType *at;
1756         const char *err;
1757         int i, code;
1758
1759         for (i=0; pwd_OpSchema[i].def; i++) {
1760                 at = ldap_str2attributetype( pwd_OpSchema[i].def, &code, &err,
1761                         LDAP_SCHEMA_ALLOW_ALL );
1762                 if ( !at ) {
1763                         fprintf( stderr, "AttributeType Load failed %s %s\n",
1764                                 ldap_scherr2str(code), err );
1765                         return code;
1766                 }
1767                 code = at_add( at, 0, NULL, &err );
1768                 if ( !code ) {
1769                         slap_str2ad( at->at_names[0], pwd_OpSchema[i].ad, &err );
1770                 }
1771                 ldap_memfree( at );
1772                 if ( code ) {
1773                         fprintf( stderr, "AttributeType Load failed %s %s\n",
1774                                 scherr2str(code), err );
1775                         return code;
1776                 }
1777         }
1778
1779         code = register_supported_control( LDAP_CONTROL_PASSWORDPOLICYREQUEST,
1780                 SLAP_CTRL_ADD|SLAP_CTRL_BIND|SLAP_CTRL_MODIFY|SLAP_CTRL_HIDE, extops,
1781                 ppolicy_parseCtrl, &ppolicy_cid );
1782         if ( code != LDAP_SUCCESS ) {
1783                 fprintf( stderr, "Failed to register control %d\n", code );
1784                 return code;
1785         }
1786
1787         ldap_pvt_thread_mutex_init( &chk_syntax_mutex );
1788
1789         ppolicy.on_bi.bi_type = "ppolicy";
1790         ppolicy.on_bi.bi_db_init = ppolicy_db_init;
1791         ppolicy.on_bi.bi_db_open = ppolicy_db_open;
1792         ppolicy.on_bi.bi_db_config = ppolicy_config;
1793         ppolicy.on_bi.bi_db_close = ppolicy_close;
1794
1795         ppolicy.on_bi.bi_op_add = ppolicy_add;
1796         ppolicy.on_bi.bi_op_bind = ppolicy_bind;
1797         ppolicy.on_bi.bi_op_unbind = ppolicy_unbind;
1798         ppolicy.on_bi.bi_op_compare = ppolicy_restrict;
1799         ppolicy.on_bi.bi_op_delete = ppolicy_restrict;
1800         ppolicy.on_bi.bi_op_modify = ppolicy_modify;
1801         ppolicy.on_bi.bi_op_search = ppolicy_restrict;
1802
1803         return overlay_register( &ppolicy );
1804 }
1805
1806 #if SLAPD_OVER_PPOLICY == SLAPD_MOD_DYNAMIC
1807 int init_module(int argc, char *argv[]) {
1808         return ppolicy_init();
1809 }
1810 #endif
1811
1812 #endif  /* defined(SLAPD_OVER_PPOLICY) */