]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/ppolicy.c
ITS#4355 timestamps must be normalized
[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                 m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
735
736                 ber_dupbv( &m->sml_values[0], &timestamp );
737                 ber_dupbv( &m->sml_nvalues[0], &timestamp );
738                 m->sml_next = mod;
739                 mod = m;
740
741                 /*
742                  * Count the pwdFailureTimes - if it's
743                  * greater than the policy pwdMaxFailure,
744                  * then lock the account.
745                  */
746                 if ((a = attr_find( e->e_attrs, ad_pwdFailureTime )) != NULL) {
747                         for(i=0; a->a_nvals[i].bv_val; i++) {
748
749                                 /*
750                                  * If the interval is 0, then failures
751                                  * stay on the record until explicitly
752                                  * reset by successful authentication.
753                                  */
754                                 if (ppb->pp.pwdFailureCountInterval == 0) {
755                                         fc++;
756                                 } else if (now <=
757                                                         parse_time(a->a_nvals[i].bv_val) +
758                                                         ppb->pp.pwdFailureCountInterval) {
759
760                                         fc++;
761                                 }
762                                 /*
763                                  * We only count those failures
764                                  * which are not due to expire.
765                                  */
766                         }
767                 }
768                 
769                 if ((ppb->pp.pwdMaxFailure > 0) &&
770                         (fc >= ppb->pp.pwdMaxFailure - 1)) {
771
772                         /*
773                          * We subtract 1 from the failure max
774                          * because the new failure entry hasn't
775                          * made it to the entry yet.
776                          */
777                         m = ch_calloc( sizeof(Modifications), 1 );
778                         m->sml_op = LDAP_MOD_REPLACE;
779                         m->sml_flags = 0;
780                         m->sml_type = ad_pwdAccountLockedTime->ad_cname;
781                         m->sml_desc = ad_pwdAccountLockedTime;
782                         m->sml_values = ch_calloc( sizeof(struct berval), 2 );
783                         m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
784                         ber_dupbv( &m->sml_values[0], &timestamp );
785                         ber_dupbv( &m->sml_nvalues[0], &timestamp );
786                         m->sml_next = mod;
787                         mod = m;
788                 }
789         } else if ( rs->sr_err == LDAP_SUCCESS ) {
790                 if ((a = attr_find( e->e_attrs, ad_pwdChangedTime )) != NULL)
791                         pwtime = parse_time( a->a_nvals[0].bv_val );
792
793                 /* delete all pwdFailureTimes */
794                 if ( attr_find( e->e_attrs, ad_pwdFailureTime )) {
795                         m = ch_calloc( sizeof(Modifications), 1 );
796                         m->sml_op = LDAP_MOD_DELETE;
797                         m->sml_flags = 0;
798                         m->sml_type = ad_pwdFailureTime->ad_cname;
799                         m->sml_desc = ad_pwdFailureTime;
800                         m->sml_next = mod;
801                         mod = m;
802                 }
803
804                 /*
805                  * check to see if the password must be changed
806                  */
807                 if ( ppb->pp.pwdMustChange &&
808                         (a = attr_find( e->e_attrs, ad_pwdReset )) &&
809                         !strcmp( a->a_nvals[0].bv_val, "TRUE" ) ) {
810                         /*
811                          * need to inject client controls here to give
812                          * more information. For the moment, we ensure
813                          * that we are disallowed from doing anything
814                          * other than change password.
815                          */
816                         pwcons[op->o_conn->c_conn_idx].restricted = 1;
817
818                         ppb->pErr = PP_changeAfterReset;
819
820                 } else {
821                         /*
822                          * the password does not need to be changed, so
823                          * we now check whether the password has expired.
824                          *
825                          * We can skip this bit if passwords don't age in
826                          * the policy.
827                          */
828                         if (ppb->pp.pwdMaxAge == 0) goto grace;
829
830                         if (pwtime == (time_t)-1) {
831                                 /*
832                                  * Hmm. No password changed time on the
833                                  * entry. This is odd - it should have
834                                  * been provided when the attribute was added.
835                                  *
836                                  * However, it's possible that it could be
837                                  * missing if the DIT was established via
838                                  * an import process.
839                                  */
840                                 Debug( LDAP_DEBUG_ANY,
841                                         "ppolicy_bind: Entry %s does not have valid pwdChangedTime attribute - assuming password expired\n",
842                                         e->e_name.bv_val, 0, 0);
843                                 
844                                 pwExpired = 1;
845                         } else {
846                                 /*
847                                  * Check: was the last change time of
848                                  * the password older than the maximum age
849                                  * allowed. (Ignore case 2 from I-D, it's just silly.)
850                                  */
851                                 if (now - pwtime > ppb->pp.pwdMaxAge ) pwExpired = 1;
852                         }
853                 }
854
855 grace:
856                 if (!pwExpired) goto check_expiring_password;
857                 
858                 if ((a = attr_find( e->e_attrs, ad_pwdGraceUseTime )) == NULL)
859                         ngut = ppb->pp.pwdGraceAuthNLimit;
860                 else {
861                         for(ngut=0; a->a_nvals[ngut].bv_val; ngut++);
862                         ngut = ppb->pp.pwdGraceAuthNLimit - ngut;
863                 }
864
865                 /*
866                  * ngut is the number of remaining grace logins
867                  */
868                 Debug( LDAP_DEBUG_ANY,
869                         "ppolicy_bind: Entry %s has an expired password: %d grace logins\n",
870                         e->e_name.bv_val, ngut, 0);
871                 
872                 if (ngut < 1) {
873                         ppb->pErr = PP_passwordExpired;
874                         rs->sr_err = LDAP_INVALID_CREDENTIALS;
875                         goto done;
876                 }
877
878                 /*
879                  * Add a grace user time to the entry
880                  */
881                 m = ch_calloc( sizeof(Modifications), 1 );
882                 m->sml_op = LDAP_MOD_ADD;
883                 m->sml_flags = 0;
884                 m->sml_type = ad_pwdGraceUseTime->ad_cname;
885                 m->sml_desc = ad_pwdGraceUseTime;
886                 m->sml_values = ch_calloc( sizeof(struct berval), 2 );
887                 m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
888                 ber_dupbv( &m->sml_values[0], &timestamp );
889                 ber_dupbv( &m->sml_nvalues[0], &timestamp );
890                 m->sml_next = mod;
891                 mod = m;
892
893 check_expiring_password:
894                 /*
895                  * Now we need to check to see
896                  * if it is about to expire, and if so, should the user
897                  * be warned about it in the password policy control.
898                  *
899                  * If the password has expired, and we're in the grace period, then
900                  * we don't need to do this bit. Similarly, if we don't have password
901                  * aging, then there's no need to do this bit either.
902                  */
903                 if ((ppb->pp.pwdMaxAge < 1) || (pwExpired) || (ppb->pp.pwdExpireWarning < 1))
904                         goto done;
905
906                 age = (int)(now - pwtime);
907                 
908                 /*
909                  * We know that there is a password Change Time attribute - if
910                  * there wasn't, then the pwdExpired value would be true, unless
911                  * there is no password aging - and if there is no password aging,
912                  * then this section isn't called anyway - you can't have an
913                  * expiring password if there's no limit to expire.
914                  */
915                 if (ppb->pp.pwdMaxAge - age < ppb->pp.pwdExpireWarning ) {
916                         /*
917                          * Set the warning value.
918                          */
919                         warn = ppb->pp.pwdMaxAge - age; /* seconds left until expiry */
920                         if (warn < 0) warn = 0; /* something weird here - why is pwExpired not set? */
921                         
922                         Debug( LDAP_DEBUG_ANY,
923                                 "ppolicy_bind: Setting warning for password expiry for %s = %d seconds\n",
924                                 op->o_req_dn.bv_val, warn, 0 );
925                 }
926         }
927
928 done:
929         op->o_bd->bd_info = (BackendInfo *)on->on_info;
930         be_entry_release_r( op, e );
931
932 locked:
933         if ( mod ) {
934                 Operation op2 = *op;
935                 SlapReply r2 = { REP_RESULT };
936                 slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
937
938                 /* FIXME: Need to handle replication of some (but not all)
939                  * of the operational attributes...
940                  */
941                 op2.o_tag = LDAP_REQ_MODIFY;
942                 op2.o_callback = &cb;
943                 op2.orm_modlist = mod;
944                 op2.o_dn = op->o_bd->be_rootdn;
945                 op2.o_ndn = op->o_bd->be_rootndn;
946                 op2.o_bd->bd_info = (BackendInfo *)on->on_info;
947                 rc = op->o_bd->be_modify( &op2, &r2 );
948                 slap_mods_free( mod, 1 );
949         }
950
951         if ( ppb->send_ctrl ) {
952                 LDAPControl **ctrls = NULL;
953                 pp_info *pi = on->on_bi.bi_private;
954
955                 /* Do we really want to tell that the account is locked? */
956                 if ( ppb->pErr == PP_accountLocked && !pi->use_lockout ) {
957                         ppb->pErr = PP_noError;
958                 }
959                 ctrls = ch_calloc( sizeof( LDAPControl *) , 2 );
960                 ctrls[0] = create_passcontrol( warn, ngut, ppb->pErr );
961                 ctrls[1] = NULL;
962                 rs->sr_ctrls = ctrls;
963         }
964         op->o_bd->bd_info = bi;
965         return SLAP_CB_CONTINUE;
966 }
967
968 static int
969 ppolicy_bind( Operation *op, SlapReply *rs )
970 {
971         slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
972
973         /* Reset lockout status on all Bind requests */
974         pwcons[op->o_conn->c_conn_idx].restricted = 0;
975
976         /* Root bypasses policy */
977         if ( !be_isroot_dn( op->o_bd, &op->o_req_ndn )) {
978                 Entry *e;
979                 int rc;
980                 ppbind *ppb;
981                 slap_callback *cb;
982
983                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
984                 rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
985
986                 if ( rc != LDAP_SUCCESS ) {
987                         return SLAP_CB_CONTINUE;
988                 }
989
990                 cb = op->o_tmpcalloc( sizeof(ppbind)+sizeof(slap_callback),
991                         1, op->o_tmpmemctx );
992                 ppb = (ppbind *)(cb+1);
993                 ppb->on = on;
994                 ppb->pErr = PP_noError;
995
996                 /* Setup a callback so we can munge the result */
997
998                 cb->sc_response = ppolicy_bind_resp;
999                 cb->sc_next = op->o_callback->sc_next;
1000                 cb->sc_private = ppb;
1001                 op->o_callback->sc_next = cb;
1002
1003                 /* Did we receive a password policy request control? */
1004                 if ( op->o_ctrlflag[ppolicy_cid] ) {
1005                         ppb->send_ctrl = 1;
1006                 }
1007
1008                 op->o_bd->bd_info = (BackendInfo *)on;
1009                 ppolicy_get( op, e, &ppb->pp );
1010
1011                 rc = account_locked( op, e, &ppb->pp, &ppb->mod );
1012
1013                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1014                 be_entry_release_r( op, e );
1015
1016                 if ( rc ) {
1017                         /* This will be the Draft 8 response, Unwilling is bogus */
1018                         ppb->pErr = PP_accountLocked;
1019                         send_ldap_error( op, rs, LDAP_INVALID_CREDENTIALS, NULL );
1020                         return rs->sr_err;
1021                 }
1022
1023         }
1024
1025         return SLAP_CB_CONTINUE;
1026 }
1027
1028 /* Reset the restricted flag for the next session on this connection */
1029 static int
1030 ppolicy_connection_destroy( BackendDB *bd, Connection *conn )
1031 {
1032         pwcons[conn->c_conn_idx].restricted = 0;
1033         return SLAP_CB_CONTINUE;
1034 }
1035
1036 /* Check if this connection is restricted */
1037 static int
1038 ppolicy_restrict(
1039         Operation *op,
1040         SlapReply *rs )
1041 {
1042         slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1043         int send_ctrl = 0;
1044
1045         /* Did we receive a password policy request control? */
1046         if ( op->o_ctrlflag[ppolicy_cid] ) {
1047                 send_ctrl = 1;
1048         }
1049
1050         if ( op->o_conn && pwcons[op->o_conn->c_conn_idx].restricted ) {
1051                 Debug( LDAP_DEBUG_TRACE,
1052                         "connection restricted to password changing only\n", 0, 0, 0);
1053                 if ( send_ctrl ) {
1054                         LDAPControl **ctrls = NULL;
1055
1056                         ctrls = ch_calloc( sizeof( LDAPControl *) , 2 );
1057                         ctrls[0] = create_passcontrol( -1, -1, PP_changeAfterReset );
1058                         ctrls[1] = NULL;
1059                         rs->sr_ctrls = ctrls;
1060                 }
1061                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1062                 send_ldap_error( op, rs, LDAP_INSUFFICIENT_ACCESS, 
1063                         "Operations are restricted to bind/unbind/abandon/StartTLS/modify password" );
1064                 return rs->sr_err;
1065         }
1066
1067         return SLAP_CB_CONTINUE;
1068 }
1069
1070 static int
1071 ppolicy_add(
1072         Operation *op,
1073         SlapReply *rs )
1074 {
1075         slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1076         pp_info *pi = on->on_bi.bi_private;
1077         PassPolicy pp;
1078         Attribute *pa;
1079         const char *txt;
1080
1081         if ( ppolicy_restrict( op, rs ) != SLAP_CB_CONTINUE )
1082                 return rs->sr_err;
1083
1084         /* If this is a replica, assume the master checked everything */
1085         if ( be_shadow_update( op ))
1086                 return SLAP_CB_CONTINUE;
1087
1088         /* Check for password in entry */
1089         if ((pa = attr_find( op->oq_add.rs_e->e_attrs,
1090                 slap_schema.si_ad_userPassword )))
1091         {
1092                 /*
1093                  * new entry contains a password - if we're not the root user
1094                  * then we need to check that the password fits in with the
1095                  * security policy for the new entry.
1096                  */
1097                 ppolicy_get( op, op->ora_e, &pp );
1098                 if (pp.pwdCheckQuality > 0 && !be_isroot( op )) {
1099                         struct berval *bv = &(pa->a_vals[0]);
1100                         int rc, send_ctrl = 0;
1101                         LDAPPasswordPolicyError pErr = PP_noError;
1102
1103                         /* Did we receive a password policy request control? */
1104                         if ( op->o_ctrlflag[ppolicy_cid] ) {
1105                                 send_ctrl = 1;
1106                         }
1107                         rc = check_password_quality( bv, &pp, &pErr, op->ora_e );
1108                         if (rc != LDAP_SUCCESS) {
1109                                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1110                                 if ( send_ctrl ) {
1111                                         LDAPControl **ctrls = NULL;
1112
1113                                         ctrls = ch_calloc( sizeof( LDAPControl *) , 2 );
1114                                         ctrls[0] = create_passcontrol( -1, -1, pErr );
1115                                         ctrls[1] = NULL;
1116                                         rs->sr_ctrls = ctrls;
1117                                 }
1118                                 send_ldap_error( op, rs, rc, "Password fails quality checking policy" );
1119                                 return rs->sr_err;
1120                         }
1121                 }
1122                         /*
1123                          * A controversial bit. We hash cleartext
1124                          * passwords provided via add and modify operations
1125                          * You're not really supposed to do this, since
1126                          * the X.500 model says "store attributes" as they
1127                          * get provided. By default, this is what we do
1128                          *
1129                          * But if the hash_passwords flag is set, we hash
1130                          * any cleartext password attribute values via the
1131                          * default password hashing scheme.
1132                          */
1133                 if ((pi->hash_passwords) &&
1134                         (password_scheme( &(pa->a_vals[0]), NULL ) != LDAP_SUCCESS)) {
1135                         struct berval hpw;
1136
1137                         slap_passwd_hash( &(pa->a_vals[0]), &hpw, &txt );
1138                         if (hpw.bv_val == NULL) {
1139                                 /*
1140                                  * hashing didn't work. Emit an error.
1141                                  */
1142                                 rs->sr_err = LDAP_OTHER;
1143                                 rs->sr_text = txt;
1144                                 send_ldap_error( op, rs, LDAP_OTHER, "Password hashing failed" );
1145                                 return rs->sr_err;
1146                         }
1147
1148                         memset( pa->a_vals[0].bv_val, 0, pa->a_vals[0].bv_len);
1149                         ber_memfree( pa->a_vals[0].bv_val );
1150                         pa->a_vals[0].bv_val = hpw.bv_val;
1151                         pa->a_vals[0].bv_len = hpw.bv_len;
1152                 }
1153
1154                 /* If password aging is in effect, set the pwdChangedTime */
1155                 if ( pp.pwdMaxAge || pp.pwdMinAge ) {
1156                         struct berval timestamp;
1157                         char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
1158                         time_t now = slap_get_time();
1159
1160                         timestamp.bv_val = timebuf;
1161                         timestamp.bv_len = sizeof(timebuf);
1162                         slap_timestamp( &now, &timestamp );
1163
1164                         attr_merge_one( op->ora_e, ad_pwdChangedTime, &timestamp, &timestamp );
1165                 }
1166         }
1167         return SLAP_CB_CONTINUE;
1168 }
1169
1170 static int
1171 ppolicy_modify( Operation *op, SlapReply *rs )
1172 {
1173         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
1174         pp_info                 *pi = on->on_bi.bi_private;
1175         int                     i, rc, mod_pw_only, pwmod, pwmop = -1, deladd,
1176                                 hsize = 0;
1177         PassPolicy              pp;
1178         Modifications           *mods = NULL, *modtail = NULL,
1179                                 *ml, *delmod, *addmod;
1180         Attribute               *pa, *ha, at;
1181         const char              *txt;
1182         pw_hist                 *tl = NULL, *p;
1183         int                     zapReset, send_ctrl = 0;
1184         Entry                   *e;
1185         struct berval           newpw = BER_BVNULL, oldpw = BER_BVNULL,
1186                                 *bv, cr[2];
1187         LDAPPasswordPolicyError pErr = PP_noError;
1188
1189         op->o_bd->bd_info = (BackendInfo *)on->on_info;
1190         rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
1191         op->o_bd->bd_info = (BackendInfo *)on;
1192
1193         if ( rc != LDAP_SUCCESS ) return SLAP_CB_CONTINUE;
1194
1195         /* If this is a replica, we may need to tweak some of the
1196          * master's modifications. Otherwise, just pass it through.
1197          */
1198         if ( be_shadow_update( op )) {
1199                 Modifications **prev;
1200                 int got_del_grace = 0, got_del_lock = 0, got_pw = 0, got_del_fail = 0;
1201                 Attribute *a_grace, *a_lock, *a_fail;
1202
1203                 a_grace = attr_find( e->e_attrs, ad_pwdGraceUseTime );
1204                 a_lock = attr_find( e->e_attrs, ad_pwdAccountLockedTime );
1205                 a_fail = attr_find( e->e_attrs, ad_pwdFailureTime );
1206
1207                 for( prev = &op->oq_modify.rs_modlist, ml = *prev; ml; ml = *prev ) {
1208
1209                         if ( ml->sml_desc == slap_schema.si_ad_userPassword )
1210                                 got_pw = 1;
1211
1212                         /* If we're deleting an attr that didn't exist,
1213                          * drop this delete op
1214                          */
1215                         if ( ml->sml_op == LDAP_MOD_DELETE ) {
1216                                 int drop = 0;
1217
1218                                 if ( ml->sml_desc == ad_pwdGraceUseTime ) {
1219                                         got_del_grace = 1;
1220                                         if ( !a_grace )
1221                                                 drop = 1;
1222                                 } else
1223                                 if ( ml->sml_desc == ad_pwdAccountLockedTime ) {
1224                                         got_del_lock = 1;
1225                                         if ( !a_lock )
1226                                                 drop = 1;
1227                                 } else
1228                                 if ( ml->sml_desc == ad_pwdFailureTime ) {
1229                                         got_del_fail = 1;
1230                                         if ( !a_fail )
1231                                                 drop = 1;
1232                                 }
1233                                 if ( drop ) {
1234                                         *prev = ml->sml_next;
1235                                         ml->sml_next = NULL;
1236                                         slap_mods_free( ml, 1 );
1237                                         continue;
1238                                 }
1239                         }
1240                         prev = &ml->sml_next;
1241                 }
1242
1243                 /* If we're resetting the password, make sure grace, accountlock,
1244                  * and failure also get removed.
1245                  */
1246                 if ( got_pw ) {
1247                         if ( a_grace && !got_del_grace ) {
1248                                 ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
1249                                 ml->sml_op = LDAP_MOD_DELETE;
1250                                 ml->sml_flags = SLAP_MOD_INTERNAL;
1251                                 ml->sml_type.bv_val = NULL;
1252                                 ml->sml_desc = ad_pwdGraceUseTime;
1253                                 ml->sml_values = NULL;
1254                                 ml->sml_nvalues = NULL;
1255                                 ml->sml_next = NULL;
1256                                 *prev = ml;
1257                                 prev = &ml->sml_next;
1258                         }
1259                         if ( a_lock && !got_del_lock ) {
1260                                 ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
1261                                 ml->sml_op = LDAP_MOD_DELETE;
1262                                 ml->sml_flags = SLAP_MOD_INTERNAL;
1263                                 ml->sml_type.bv_val = NULL;
1264                                 ml->sml_desc = ad_pwdAccountLockedTime;
1265                                 ml->sml_values = NULL;
1266                                 ml->sml_nvalues = NULL;
1267                                 ml->sml_next = NULL;
1268                                 *prev = ml;
1269                         }
1270                         if ( a_fail && !got_del_fail ) {
1271                                 ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
1272                                 ml->sml_op = LDAP_MOD_DELETE;
1273                                 ml->sml_flags = SLAP_MOD_INTERNAL;
1274                                 ml->sml_type.bv_val = NULL;
1275                                 ml->sml_desc = ad_pwdFailureTime;
1276                                 ml->sml_values = NULL;
1277                                 ml->sml_nvalues = NULL;
1278                                 ml->sml_next = NULL;
1279                                 *prev = ml;
1280                         }
1281                 }
1282                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1283                 be_entry_release_r( op, e );
1284                 return SLAP_CB_CONTINUE;
1285         }
1286
1287         /* Did we receive a password policy request control? */
1288         if ( op->o_ctrlflag[ppolicy_cid] ) {
1289                 send_ctrl = 1;
1290         }
1291
1292         /* See if this is a pwdModify exop. If so, we can
1293          * access the plaintext passwords from that request.
1294          */
1295         {
1296                 slap_callback *sc;
1297
1298                 for ( sc = op->o_callback; sc; sc=sc->sc_next ) {
1299                         if ( sc->sc_response == slap_replog_cb &&
1300                                 sc->sc_private ) {
1301                                 req_pwdexop_s *qpw = sc->sc_private;
1302                                 newpw = qpw->rs_new;
1303                                 oldpw = qpw->rs_old;
1304                                 break;
1305                         }
1306                 }
1307         }
1308
1309         ppolicy_get( op, e, &pp );
1310
1311         for ( ml = op->oq_modify.rs_modlist,
1312                         pwmod = 0, mod_pw_only = 1,
1313                         deladd = 0, delmod = NULL,
1314                         addmod = NULL,
1315                         zapReset = 1;
1316                 ml != NULL; modtail = ml, ml = ml->sml_next )
1317         {
1318                 if ( ml->sml_desc == pp.ad ) {
1319                         pwmod = 1;
1320                         pwmop = ml->sml_op;
1321                         if ((deladd == 0) && (ml->sml_op == LDAP_MOD_DELETE) &&
1322                                 (ml->sml_values) && (ml->sml_values[0].bv_val != NULL)) {
1323                                 deladd = 1;
1324                                 delmod = ml;
1325                         }
1326
1327                         if ((deladd == 1) && ((ml->sml_op == LDAP_MOD_ADD) ||
1328                                                                   (ml->sml_op == LDAP_MOD_REPLACE)))
1329                                 deladd = 2;
1330
1331                         if ((ml->sml_op == LDAP_MOD_ADD) ||
1332                                 (ml->sml_op == LDAP_MOD_REPLACE))
1333                                 addmod = ml;
1334                 } else if (! is_at_operational( ml->sml_desc->ad_type )) {
1335                         mod_pw_only = 0;
1336                         /* modifying something other than password */
1337                 }
1338
1339                 /*
1340                  * If there is a request to explicitly add a pwdReset
1341                  * attribute, then we suppress the normal behaviour on
1342                  * password change, which is to remove the pwdReset
1343                  * attribute.
1344                  *
1345                  * This enables an administrator to assign a new password
1346                  * and place a "must reset" flag on the entry, which will
1347                  * stay until the user explicitly changes his/her password.
1348                  */
1349                 if (ml->sml_desc == ad_pwdReset ) {
1350                         if ((ml->sml_op == LDAP_MOD_ADD) ||
1351                                 (ml->sml_op == LDAP_MOD_REPLACE))
1352                                 zapReset = 0;
1353                 }
1354         }
1355         
1356         if (pwcons[op->o_conn->c_conn_idx].restricted && !mod_pw_only) {
1357                 Debug( LDAP_DEBUG_TRACE,
1358                         "connection restricted to password changing only\n", 0, 0, 0 );
1359                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 
1360                 rs->sr_text = "Operations are restricted to bind/unbind/abandon/StartTLS/modify password";
1361                 pErr = PP_changeAfterReset;
1362                 goto return_results;
1363         }
1364
1365         /*
1366          * if we have a "safe password modify policy", then we need to check if we're doing
1367          * a delete (with the old password), followed by an add (with the new password).
1368          *
1369          * If we don't have this, then we fail with an error. We also skip all the checks if
1370          * the root user is bound. Root can do anything, including avoid the policies.
1371          */
1372
1373         if (!pwmod) goto do_modify;
1374
1375         /*
1376          * Did we get a valid add mod?
1377          */
1378
1379         if (!addmod) {
1380                 rs->sr_err = LDAP_OTHER;
1381                 rs->sr_text = "Internal Error";
1382                 Debug( LDAP_DEBUG_TRACE,
1383                         "cannot locate modification supplying new password\n", 0, 0, 0 );
1384                 goto return_results;
1385         }
1386
1387         /*
1388          * Build the password history list in ascending time order
1389          * We need this, even if the user is root, in order to maintain
1390          * the pwdHistory operational attributes properly.
1391          */
1392         if (pp.pwdInHistory > 0 && (ha = attr_find( e->e_attrs, ad_pwdHistory ))) {
1393                 struct berval oldpw;
1394                 time_t oldtime;
1395
1396                 for(i=0; ha->a_nvals[i].bv_val; i++) {
1397                         rc = parse_pwdhistory( &(ha->a_nvals[i]), NULL,
1398                                 &oldtime, &oldpw );
1399
1400                         if (rc != LDAP_SUCCESS) continue; /* invalid history entry */
1401
1402                         if (oldpw.bv_val) {
1403                                 add_to_pwd_history( &tl, oldtime, &oldpw,
1404                                         &(ha->a_nvals[i]) );
1405                                 oldpw.bv_val = NULL;
1406                                 oldpw.bv_len = 0;
1407                         }
1408                 }
1409                 for(p=tl; p; p=p->next, hsize++); /* count history size */
1410         }
1411
1412         if (be_isroot( op )) goto do_modify;
1413
1414         /* This is a pwdModify exop that provided the old pw.
1415          * We need to create a Delete mod for this old pw and 
1416          * let the matching value get found later
1417          */
1418         if (pp.pwdSafeModify && oldpw.bv_val ) {
1419                 ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
1420                 ml->sml_op = LDAP_MOD_DELETE;
1421                 ml->sml_flags = SLAP_MOD_INTERNAL;
1422                 ml->sml_desc = pp.ad;
1423                 ml->sml_type = pp.ad->ad_cname;
1424                 ml->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
1425                 ber_dupbv( &ml->sml_values[0], &oldpw );
1426                 ml->sml_values[1].bv_len = 0;
1427                 ml->sml_values[1].bv_val = NULL;
1428                 ml->sml_nvalues = NULL;
1429                 ml->sml_next = op->orm_modlist;
1430                 op->orm_modlist = ml;
1431                 delmod = ml;
1432                 deladd = 2;
1433         }
1434
1435         if (pp.pwdSafeModify && deladd != 2) {
1436                 Debug( LDAP_DEBUG_TRACE,
1437                         "change password must use DELETE followed by ADD/REPLACE\n",
1438                         0, 0, 0 );
1439                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1440                 rs->sr_text = "Must supply old password to be changed as well as new one";
1441                 pErr = PP_mustSupplyOldPassword;
1442                 goto return_results;
1443         }
1444
1445         if (!pp.pwdAllowUserChange) {
1446                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1447                 rs->sr_text = "User alteration of password is not allowed";
1448                 pErr = PP_passwordModNotAllowed;
1449                 goto return_results;
1450         }
1451
1452         if (pp.pwdMinAge > 0) {
1453                 time_t pwtime = (time_t)-1, now;
1454                 int age;
1455
1456                 if ((pa = attr_find( e->e_attrs, ad_pwdChangedTime )) != NULL)
1457                         pwtime = parse_time( pa->a_nvals[0].bv_val );
1458                 now = slap_get_time();
1459                 age = (int)(now - pwtime);
1460                 if ((pwtime != (time_t)-1) && (age < pp.pwdMinAge)) {
1461                         rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
1462                         rs->sr_text = "Password is too young to change";
1463                         pErr = PP_passwordTooYoung;
1464                         goto return_results;
1465                 }
1466         }
1467
1468         /* pa is used in password history check below, be sure it's set */
1469         if ((pa = attr_find( e->e_attrs, pp.ad )) != NULL && delmod) {
1470                 /*
1471                  * we have a password to check
1472                  */
1473                 const char *txt;
1474                 
1475                 bv = oldpw.bv_val ? &oldpw : delmod->sml_values;
1476                 /* FIXME: no access checking? */
1477                 rc = slap_passwd_check( op, NULL, pa, bv, &txt );
1478                 if (rc != LDAP_SUCCESS) {
1479                         Debug( LDAP_DEBUG_TRACE,
1480                                 "old password check failed: %s\n", txt, 0, 0 );
1481                         
1482                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1483                         rs->sr_text = "Must supply correct old password to change to new one";
1484                         pErr = PP_mustSupplyOldPassword;
1485                         goto return_results;
1486
1487                 } else {
1488                         int i;
1489                         
1490                         /*
1491                          * replace the delete value with the (possibly hashed)
1492                          * value which is currently in the password.
1493                          */
1494                         for(i=0; delmod->sml_values[i].bv_val; i++) {
1495                                 free(delmod->sml_values[i].bv_val);
1496                                 delmod->sml_values[i].bv_len = 0;
1497                         }
1498                         free(delmod->sml_values);
1499                         delmod->sml_values = ch_calloc( sizeof(struct berval), 2 );
1500                         delmod->sml_values[1].bv_len = 0;
1501                         delmod->sml_values[1].bv_val = NULL;
1502                         ber_dupbv(&(delmod->sml_values[0]),  &(pa->a_nvals[0]));
1503                 }
1504         }
1505
1506         bv = newpw.bv_val ? &newpw : addmod->sml_values;
1507         if (pp.pwdCheckQuality > 0) {
1508
1509                 rc = check_password_quality( bv, &pp, &pErr, e );
1510                 if (rc != LDAP_SUCCESS) {
1511                         rs->sr_err = rc;
1512                         rs->sr_text = "Password fails quality checking policy";
1513                         goto return_results;
1514                 }
1515         }
1516
1517         if (pa) {
1518                 /*
1519                  * Last check - the password history.
1520                  */
1521                 /* FIXME: no access checking? */
1522                 if (slap_passwd_check( op, NULL, pa, bv, &txt ) == LDAP_SUCCESS) {
1523                         /*
1524                          * This is bad - it means that the user is attempting
1525                          * to set the password to the same as the old one.
1526                          */
1527                         rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
1528                         rs->sr_text = "Password is not being changed from existing value";
1529                         pErr = PP_passwordInHistory;
1530                         goto return_results;
1531                 }
1532         
1533                 if (pp.pwdInHistory < 1) goto do_modify;
1534         
1535                 /*
1536                  * Iterate through the password history, and fail on any
1537                  * password matches.
1538                  */
1539                 at = *pa;
1540                 at.a_vals = cr;
1541                 cr[1].bv_val = NULL;
1542                 for(p=tl; p; p=p->next) {
1543                         cr[0] = p->pw;
1544                         /* FIXME: no access checking? */
1545                         rc = slap_passwd_check( op, NULL, &at, bv, &txt );
1546                         
1547                         if (rc != LDAP_SUCCESS) continue;
1548                         
1549                         rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
1550                         rs->sr_text = "Password is in history of old passwords";
1551                         pErr = PP_passwordInHistory;
1552                         goto return_results;
1553                 }
1554         }
1555
1556 do_modify:
1557         if (pwmod) {
1558                 struct berval timestamp;
1559                 char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
1560                 time_t now = slap_get_time();
1561                 
1562                 /*
1563                  * keep the necessary pwd.. operational attributes
1564                  * up to date.
1565                  */
1566
1567                 timestamp.bv_val = timebuf;
1568                 timestamp.bv_len = sizeof(timebuf);
1569                 slap_timestamp( &now, &timestamp );
1570
1571                 mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1572                 mods->sml_type.bv_val = NULL;
1573                 mods->sml_desc = ad_pwdChangedTime;
1574                 if (pwmop != LDAP_MOD_DELETE) {
1575                         mods->sml_op = LDAP_MOD_REPLACE;
1576                         mods->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
1577                         mods->sml_nvalues = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
1578                         ber_dupbv( &mods->sml_values[0], &timestamp );
1579                         ber_dupbv( &mods->sml_nvalues[0], &timestamp );
1580                         mods->sml_values[1].bv_len = 0;
1581                         mods->sml_values[1].bv_val = NULL;
1582                         assert( mods->sml_values[0].bv_val != NULL );
1583                 } else {
1584                         mods->sml_op = LDAP_MOD_DELETE;
1585                         mods->sml_values = NULL;
1586                 }
1587                 mods->sml_flags = SLAP_MOD_INTERNAL;
1588                 mods->sml_nvalues = NULL;
1589                 mods->sml_next = NULL;
1590                 modtail->sml_next = mods;
1591                 modtail = mods;
1592
1593                 if (attr_find(e->e_attrs, ad_pwdGraceUseTime )) {
1594                         mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1595                         mods->sml_op = LDAP_MOD_DELETE;
1596                         mods->sml_flags = SLAP_MOD_INTERNAL;
1597                         mods->sml_type.bv_val = NULL;
1598                         mods->sml_desc = ad_pwdGraceUseTime;
1599                         mods->sml_values = NULL;
1600                         mods->sml_nvalues = NULL;
1601                         mods->sml_next = NULL;
1602                         modtail->sml_next = mods;
1603                         modtail = mods;
1604                 }
1605
1606                 if (attr_find(e->e_attrs, ad_pwdAccountLockedTime )) {
1607                         mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1608                         mods->sml_op = LDAP_MOD_DELETE;
1609                         mods->sml_flags = SLAP_MOD_INTERNAL;
1610                         mods->sml_type.bv_val = NULL;
1611                         mods->sml_desc = ad_pwdAccountLockedTime;
1612                         mods->sml_values = NULL;
1613                         mods->sml_nvalues = NULL;
1614                         mods->sml_next = NULL;
1615                         modtail->sml_next = mods;
1616                         modtail = mods;
1617                 }
1618
1619                 if (attr_find(e->e_attrs, ad_pwdFailureTime )) {
1620                         mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1621                         mods->sml_op = LDAP_MOD_DELETE;
1622                         mods->sml_flags = SLAP_MOD_INTERNAL;
1623                         mods->sml_type.bv_val = NULL;
1624                         mods->sml_desc = ad_pwdFailureTime;
1625                         mods->sml_values = NULL;
1626                         mods->sml_nvalues = NULL;
1627                         mods->sml_next = NULL;
1628                         modtail->sml_next = mods;
1629                         modtail = mods;
1630                 }
1631
1632                 /* Delete the pwdReset attribute, since it's being reset */
1633                 if ((zapReset) && (attr_find(e->e_attrs, ad_pwdReset ))) {
1634                         mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1635                         mods->sml_op = LDAP_MOD_DELETE;
1636                         mods->sml_flags = SLAP_MOD_INTERNAL;
1637                         mods->sml_type.bv_val = NULL;
1638                         mods->sml_desc = ad_pwdReset;
1639                         mods->sml_values = NULL;
1640                         mods->sml_nvalues = NULL;
1641                         mods->sml_next = NULL;
1642                         modtail->sml_next = mods;
1643                         modtail = mods;
1644                 }
1645
1646                 if (pp.pwdInHistory > 0) {
1647                         if (hsize >= pp.pwdInHistory) {
1648                                 /*
1649                                  * We use the >= operator, since we are going to add
1650                                  * the existing password attribute value into the
1651                                  * history - thus the cardinality of history values is
1652                                  * about to rise by one.
1653                                  *
1654                                  * If this would push it over the limit of history
1655                                  * values (remembering - the password policy could have
1656                                  * changed since the password was last altered), we must
1657                                  * delete at least 1 value from the pwdHistory list.
1658                                  *
1659                                  * In fact, we delete '(#pwdHistory attrs - max pwd
1660                                  * history length) + 1' values, starting with the oldest.
1661                                  * This is easily evaluated, since the linked list is
1662                                  * created in ascending time order.
1663                                  */
1664                                 mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1665                                 mods->sml_op = LDAP_MOD_DELETE;
1666                                 mods->sml_flags = SLAP_MOD_INTERNAL;
1667                                 mods->sml_type.bv_val = NULL;
1668                                 mods->sml_desc = ad_pwdHistory;
1669                                 mods->sml_nvalues = NULL;
1670                                 mods->sml_values = ch_calloc( sizeof( struct berval ),
1671                                                                                            hsize - pp.pwdInHistory + 2 );
1672                                 mods->sml_values[ hsize - pp.pwdInHistory + 1 ].bv_val = NULL;
1673                                 mods->sml_values[ hsize - pp.pwdInHistory + 1 ].bv_len = 0;
1674                                 for(i=0,p=tl; i < (hsize - pp.pwdInHistory + 1); i++, p=p->next) {
1675                                         mods->sml_values[i].bv_val = NULL;
1676                                         mods->sml_values[i].bv_len = 0;
1677                                         ber_dupbv( &(mods->sml_values[i]), &p->bv );
1678                                 }
1679                                 mods->sml_next = NULL;
1680                                 modtail->sml_next = mods;
1681                                 modtail = mods;
1682                         }
1683                         free_pwd_history_list( &tl );
1684
1685                         /*
1686                          * Now add the existing password into the history list.
1687                          * This will be executed even if the operation is to delete
1688                          * the password entirely.
1689                          *
1690                          * This isn't in the spec explicitly, but it seems to make
1691                          * sense that the password history list is the list of all
1692                          * previous passwords - even if they were deleted. Thus, if
1693                          * someone tries to add a historical password at some future
1694                          * point, it will fail.
1695                          */
1696                         if ((pa = attr_find( e->e_attrs, pp.ad )) != NULL) {
1697                                 mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
1698                                 mods->sml_op = LDAP_MOD_ADD;
1699                                 mods->sml_flags = SLAP_MOD_INTERNAL;
1700                                 mods->sml_type.bv_val = NULL;
1701                                 mods->sml_desc = ad_pwdHistory;
1702                                 mods->sml_nvalues = NULL;
1703                                 mods->sml_values = ch_calloc( sizeof( struct berval ), 2 );
1704                                 mods->sml_values[ 1 ].bv_val = NULL;
1705                                 mods->sml_values[ 1 ].bv_len = 0;
1706                                 make_pwd_history_value( timebuf, &mods->sml_values[0], pa );
1707                                 mods->sml_next = NULL;
1708                                 modtail->sml_next = mods;
1709                                 modtail = mods;
1710                         } else {
1711                                 Debug( LDAP_DEBUG_TRACE,
1712                                 "ppolicy_modify: password attr lookup failed\n", 0, 0, 0 );
1713                         }
1714                 }
1715
1716                 /*
1717                  * Controversial bit here. If the new password isn't hashed
1718                  * (ie, is cleartext), we probably should hash it according
1719                  * to the default hash. The reason for this is that we want
1720                  * to use the policy if possible, but if we hash the password
1721                  * before, then we're going to run into trouble when it
1722                  * comes time to check the password.
1723                  *
1724                  * Now, the right thing to do is to use the extended password
1725                  * modify operation, but not all software can do this,
1726                  * therefore it makes sense to hash the new password, now
1727                  * we know it passes the policy requirements.
1728                  *
1729                  * Of course, if the password is already hashed, then we
1730                  * leave it alone.
1731                  */
1732
1733                 if ((pi->hash_passwords) && (addmod) && !newpw.bv_val && 
1734                         (password_scheme( &(addmod->sml_values[0]), NULL ) != LDAP_SUCCESS)) {
1735                         struct berval hpw, bv;
1736                         
1737                         slap_passwd_hash( &(addmod->sml_values[0]), &hpw, &txt );
1738                         if (hpw.bv_val == NULL) {
1739                                         /*
1740                                          * hashing didn't work. Emit an error.
1741                                          */
1742                                 rs->sr_err = LDAP_OTHER;
1743                                 rs->sr_text = txt;
1744                                 goto return_results;
1745                         }
1746                         bv.bv_val = addmod->sml_values[0].bv_val;
1747                         bv.bv_len = addmod->sml_values[0].bv_len;
1748                                 /* clear and discard the clear password */
1749                         memset(bv.bv_val, 0, bv.bv_len);
1750                         ber_memfree(bv.bv_val);
1751                         addmod->sml_values[0].bv_val = hpw.bv_val;
1752                         addmod->sml_values[0].bv_len = hpw.bv_len;
1753                 }
1754         }
1755         op->o_bd->bd_info = (BackendInfo *)on->on_info;
1756         be_entry_release_r( op, e );
1757         return SLAP_CB_CONTINUE;
1758
1759 return_results:
1760         free_pwd_history_list( &tl );
1761         op->o_bd->bd_info = (BackendInfo *)on->on_info;
1762         be_entry_release_r( op, e );
1763         if ( send_ctrl ) {
1764                 LDAPControl **ctrls = NULL;
1765
1766                 ctrls = ch_calloc( sizeof( LDAPControl *) , 2 );
1767                 ctrls[0] = create_passcontrol( -1, -1, pErr );
1768                 ctrls[1] = NULL;
1769                 rs->sr_ctrls = ctrls;
1770         }
1771         send_ldap_result( op, rs );
1772         return rs->sr_err;
1773 }
1774
1775 static int
1776 ppolicy_parseCtrl(
1777         Operation *op,
1778         SlapReply *rs,
1779         LDAPControl *ctrl )
1780 {
1781         if ( ctrl->ldctl_value.bv_len ) {
1782                 rs->sr_text = "passwordPolicyRequest control value not empty";
1783                 return LDAP_PROTOCOL_ERROR;
1784         }
1785         if ( ctrl->ldctl_iscritical ) {
1786                 rs->sr_text = "passwordPolicyRequest control invalid criticality";
1787                 return LDAP_PROTOCOL_ERROR;
1788         }
1789         op->o_ctrlflag[ppolicy_cid] = SLAP_CONTROL_NONCRITICAL;
1790
1791         return LDAP_SUCCESS;
1792 }
1793
1794 static int
1795 attrPretty(
1796         Syntax *syntax,
1797         struct berval *val,
1798         struct berval *out,
1799         void *ctx )
1800 {
1801         AttributeDescription *ad = NULL;
1802         const char *err;
1803         int code;
1804
1805         code = slap_bv2ad( val, &ad, &err );
1806         if ( !code ) {
1807                 ber_dupbv_x( out, &ad->ad_type->sat_cname, ctx );
1808         }
1809         return code;
1810 }
1811
1812 static int
1813 attrNormalize(
1814         slap_mask_t use,
1815         Syntax *syntax,
1816         MatchingRule *mr,
1817         struct berval *val,
1818         struct berval *out,
1819         void *ctx )
1820 {
1821         AttributeDescription *ad = NULL;
1822         const char *err;
1823         int code;
1824
1825         code = slap_bv2ad( val, &ad, &err );
1826         if ( !code ) {
1827                 ber_str2bv_x( ad->ad_type->sat_oid, 0, 1, out, ctx );
1828         }
1829         return code;
1830 }
1831
1832 static int
1833 ppolicy_db_init(
1834         BackendDB *be
1835 )
1836 {
1837         slap_overinst *on = (slap_overinst *) be->bd_info;
1838
1839         /* Has User Schema been initialized yet? */
1840         if ( !pwd_UsSchema[0].ad[0] ) {
1841                 const char *err;
1842                 int i, code;
1843
1844                 for (i=0; pwd_UsSchema[i].def; i++) {
1845                         code = slap_str2ad( pwd_UsSchema[i].def, pwd_UsSchema[i].ad, &err );
1846                         if ( code ) {
1847                                 fprintf( stderr, "User Schema Load failed %d: %s\n", code, err );
1848                                 return code;
1849                         }
1850                 }
1851                 {
1852                         Syntax *syn;
1853                         MatchingRule *mr;
1854
1855                         syn = ch_malloc( sizeof( Syntax ));
1856                         *syn = *ad_pwdAttribute->ad_type->sat_syntax;
1857                         syn->ssyn_pretty = attrPretty;
1858                         ad_pwdAttribute->ad_type->sat_syntax = syn;
1859
1860                         mr = ch_malloc( sizeof( MatchingRule ));
1861                         *mr = *ad_pwdAttribute->ad_type->sat_equality;
1862                         mr->smr_normalize = attrNormalize;
1863                         ad_pwdAttribute->ad_type->sat_equality = mr;
1864                 }
1865         }
1866
1867         on->on_bi.bi_private = ch_calloc( sizeof(pp_info), 1 );
1868
1869         if ( dtblsize && !pwcons )
1870                 pwcons = ch_calloc(sizeof(pw_conn), dtblsize );
1871
1872         return 0;
1873 }
1874
1875 static int
1876 ppolicy_db_open(
1877     BackendDB *be
1878 )
1879 {
1880         return overlay_register_control( be, LDAP_CONTROL_PASSWORDPOLICYREQUEST );
1881 }
1882
1883 static int
1884 ppolicy_close(
1885         BackendDB *be
1886 )
1887 {
1888         slap_overinst *on = (slap_overinst *) be->bd_info;
1889         pp_info *pi = on->on_bi.bi_private;
1890         
1891         free( pwcons );
1892         free( pi->def_policy.bv_val );
1893         free( pi );
1894
1895         return 0;
1896 }
1897
1898 static int
1899 ppolicy_config(
1900     BackendDB   *be,
1901     const char  *fname,
1902     int         lineno,
1903     int         argc,
1904     char        **argv
1905 )
1906 {
1907         slap_overinst *on = (slap_overinst *) be->bd_info;
1908         pp_info *pi = on->on_bi.bi_private;
1909         struct berval dn;
1910         
1911
1912         if ( strcasecmp( argv[0], "ppolicy_default" ) == 0 ) {
1913                 if ( argc != 2 ) {
1914                         fprintf( stderr, "%s: line %d: invalid arguments in \"ppolicy_default"
1915                                 " <policyDN>\n", fname, lineno );
1916                         return ( 1 );
1917                 }
1918                 ber_str2bv( argv[1], 0, 0, &dn );
1919                 if ( dnNormalize( 0, NULL, NULL, &dn, &pi->def_policy, NULL ) ) {
1920                         fprintf( stderr, "%s: line %d: policyDN is invalid\n",
1921                                 fname, lineno );
1922                         return 1;
1923                 }
1924                 return 0;
1925
1926         } else if ( strcasecmp( argv[0], "ppolicy_use_lockout" ) == 0 ) {
1927                 if ( argc != 1 ) {
1928                         fprintf( stderr, "%s: line %d: ppolicy_use_lockout "
1929                                 "takes no arguments\n", fname, lineno );
1930                         return ( 1 );
1931                 }
1932                 pi->use_lockout = 1;
1933                 return 0;
1934         } else if ( strcasecmp( argv[0], "ppolicy_hash_cleartext" ) == 0 ) {
1935                 if ( argc != 1 ) {
1936                         fprintf( stderr, "%s: line %d: ppolicy_hash_cleartext "
1937                                 "takes no arguments\n", fname, lineno );
1938                         return ( 1 );
1939                 }
1940                 pi->hash_passwords = 1;
1941                 return 0;
1942         }
1943         return SLAP_CONF_UNKNOWN;
1944 }
1945
1946 static char *extops[] = {
1947         LDAP_EXOP_MODIFY_PASSWD,
1948         NULL
1949 };
1950
1951 static slap_overinst ppolicy;
1952
1953 int ppolicy_initialize()
1954 {
1955         LDAPAttributeType *at;
1956         const char *err;
1957         int i, code;
1958
1959         for (i=0; pwd_OpSchema[i].def; i++) {
1960                 at = ldap_str2attributetype( pwd_OpSchema[i].def, &code, &err,
1961                         LDAP_SCHEMA_ALLOW_ALL );
1962                 if ( !at ) {
1963                         fprintf( stderr, "AttributeType Load failed %s %s\n",
1964                                 ldap_scherr2str(code), err );
1965                         return code;
1966                 }
1967                 code = at_add( at, 0, NULL, &err );
1968                 if ( !code ) {
1969                         slap_str2ad( at->at_names[0], pwd_OpSchema[i].ad, &err );
1970                 }
1971                 ldap_memfree( at );
1972                 if ( code ) {
1973                         fprintf( stderr, "AttributeType Load failed %s %s\n",
1974                                 scherr2str(code), err );
1975                         return code;
1976                 }
1977                 /* Allow Manager to set these as needed */
1978                 if ( is_at_no_user_mod( (*pwd_OpSchema[i].ad)->ad_type )) {
1979                         (*pwd_OpSchema[i].ad)->ad_type->sat_flags |=
1980                                 SLAP_AT_MANAGEABLE;
1981                 }
1982         }
1983
1984         code = register_supported_control( LDAP_CONTROL_PASSWORDPOLICYREQUEST,
1985                 SLAP_CTRL_ADD|SLAP_CTRL_BIND|SLAP_CTRL_MODIFY|SLAP_CTRL_HIDE, extops,
1986                 ppolicy_parseCtrl, &ppolicy_cid );
1987         if ( code != LDAP_SUCCESS ) {
1988                 fprintf( stderr, "Failed to register control %d\n", code );
1989                 return code;
1990         }
1991
1992         ldap_pvt_thread_mutex_init( &chk_syntax_mutex );
1993
1994         ppolicy.on_bi.bi_type = "ppolicy";
1995         ppolicy.on_bi.bi_db_init = ppolicy_db_init;
1996         ppolicy.on_bi.bi_db_open = ppolicy_db_open;
1997         ppolicy.on_bi.bi_db_config = ppolicy_config;
1998         ppolicy.on_bi.bi_db_close = ppolicy_close;
1999
2000         ppolicy.on_bi.bi_op_add = ppolicy_add;
2001         ppolicy.on_bi.bi_op_bind = ppolicy_bind;
2002         ppolicy.on_bi.bi_op_compare = ppolicy_restrict;
2003         ppolicy.on_bi.bi_op_delete = ppolicy_restrict;
2004         ppolicy.on_bi.bi_op_modify = ppolicy_modify;
2005         ppolicy.on_bi.bi_op_search = ppolicy_restrict;
2006         ppolicy.on_bi.bi_connection_destroy = ppolicy_connection_destroy;
2007
2008         return overlay_register( &ppolicy );
2009 }
2010
2011 #if SLAPD_OVER_PPOLICY == SLAPD_MOD_DYNAMIC
2012 int init_module(int argc, char *argv[]) {
2013         return ppolicy_initialize();
2014 }
2015 #endif
2016
2017 #endif  /* defined(SLAPD_OVER_PPOLICY) */