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