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