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