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