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