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