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