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