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