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