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