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