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