Back to index

openldap  2.4.31
ppolicy.c
Go to the documentation of this file.
00001 /* $OpenLDAP$ */
00002 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00003  *
00004  * Copyright 2004-2012 The OpenLDAP Foundation.
00005  * Portions Copyright 2004-2005 Howard Chu, Symas Corporation.
00006  * Portions Copyright 2004 Hewlett-Packard Company.
00007  * All rights reserved.
00008  *
00009  * Redistribution and use in source and binary forms, with or without
00010  * modification, are permitted only as authorized by the OpenLDAP
00011  * Public License.
00012  *
00013  * A copy of this license is available in the file LICENSE in the
00014  * top-level directory of the distribution or, alternatively, at
00015  * <http://www.OpenLDAP.org/license.html>.
00016  */
00017 /* ACKNOWLEDGEMENTS:
00018  * This work was developed by Howard Chu for inclusion in
00019  * OpenLDAP Software, based on prior work by Neil Dunbar (HP).
00020  * This work was sponsored by the Hewlett-Packard Company.
00021  */
00022 
00023 #include "portable.h"
00024 
00025 /* This file implements "Password Policy for LDAP Directories",
00026  * based on draft behera-ldap-password-policy-09
00027  */
00028 
00029 #ifdef SLAPD_OVER_PPOLICY
00030 
00031 #include <ldap.h>
00032 #include "lutil.h"
00033 #include "slap.h"
00034 #ifdef SLAPD_MODULES
00035 #define LIBLTDL_DLL_IMPORT  /* Win32: don't re-export libltdl's symbols */
00036 #include <ltdl.h>
00037 #endif
00038 #include <ac/errno.h>
00039 #include <ac/time.h>
00040 #include <ac/string.h>
00041 #include <ac/ctype.h>
00042 #include "config.h"
00043 
00044 #ifndef MODULE_NAME_SZ
00045 #define MODULE_NAME_SZ 256
00046 #endif
00047 
00048 /* Per-instance configuration information */
00049 typedef struct pp_info {
00050        struct berval def_policy;   /* DN of default policy subentry */
00051        int use_lockout;            /* send AccountLocked result? */
00052        int hash_passwords;         /* transparently hash cleartext pwds */
00053        int forward_updates; /* use frontend for policy state updates */
00054 } pp_info;
00055 
00056 /* Our per-connection info - note, it is not per-instance, it is 
00057  * used by all instances
00058  */
00059 typedef struct pw_conn {
00060        struct berval dn;    /* DN of restricted user */
00061 } pw_conn;
00062 
00063 static pw_conn *pwcons;
00064 static int ppolicy_cid;
00065 static int ov_count;
00066 
00067 typedef struct pass_policy {
00068        AttributeDescription *ad; /* attribute to which the policy applies */
00069        int pwdMinAge; /* minimum time (seconds) until passwd can change */
00070        int pwdMaxAge; /* time in seconds until pwd will expire after change */
00071        int pwdInHistory; /* number of previous passwords kept */
00072        int pwdCheckQuality; /* 0 = don't check quality, 1 = check if possible,
00073                                              2 = check mandatory; fail if not possible */
00074        int pwdMinLength; /* minimum number of chars in password */
00075        int pwdExpireWarning; /* number of seconds that warning controls are
00076                                                  sent before a password expires */
00077        int pwdGraceAuthNLimit; /* number of times you can log in with an
00078                                                  expired password */
00079        int pwdLockout; /* 0 = do not lockout passwords, 1 = lock them out */
00080        int pwdLockoutDuration; /* time in seconds a password is locked out for */
00081        int pwdMaxFailure; /* number of failed binds allowed before lockout */
00082        int pwdFailureCountInterval; /* number of seconds before failure
00083                                                                counts are zeroed */
00084        int pwdMustChange; /* 0 = users can use admin set password
00085                                                  1 = users must change password after admin set */
00086        int pwdAllowUserChange; /* 0 = users cannot change their passwords
00087                                                         1 = users can change them */
00088        int pwdSafeModify; /* 0 = old password doesn't need to come
00089                                                         with password change request
00090                                                  1 = password change must supply existing pwd */
00091        char pwdCheckModule[MODULE_NAME_SZ]; /* name of module to dynamically
00092                                                                           load to check password */
00093 } PassPolicy;
00094 
00095 typedef struct pw_hist {
00096        time_t t;     /* timestamp of history entry */
00097        struct berval pw;    /* old password hash */
00098        struct berval bv;    /* text of entire entry */
00099        struct pw_hist *next;
00100 } pw_hist;
00101 
00102 /* Operational attributes */
00103 static AttributeDescription *ad_pwdChangedTime, *ad_pwdAccountLockedTime,
00104        *ad_pwdFailureTime, *ad_pwdHistory, *ad_pwdGraceUseTime, *ad_pwdReset,
00105        *ad_pwdPolicySubentry;
00106 
00107 static struct schema_info {
00108        char *def;
00109        AttributeDescription **ad;
00110 } pwd_OpSchema[] = {
00111        {      "( 1.3.6.1.4.1.42.2.27.8.1.16 "
00112               "NAME ( 'pwdChangedTime' ) "
00113               "DESC 'The time the password was last changed' "
00114               "EQUALITY generalizedTimeMatch "
00115               "ORDERING generalizedTimeOrderingMatch "
00116               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
00117               "SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )",
00118               &ad_pwdChangedTime },
00119        {      "( 1.3.6.1.4.1.42.2.27.8.1.17 "
00120               "NAME ( 'pwdAccountLockedTime' ) "
00121               "DESC 'The time an user account was locked' "
00122               "EQUALITY generalizedTimeMatch "
00123               "ORDERING generalizedTimeOrderingMatch "
00124               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
00125               "SINGLE-VALUE "
00126 #if 0
00127               /* Not until Relax control is released */
00128               "NO-USER-MODIFICATION "
00129 #endif
00130               "USAGE directoryOperation )",
00131               &ad_pwdAccountLockedTime },
00132        {      "( 1.3.6.1.4.1.42.2.27.8.1.19 "
00133               "NAME ( 'pwdFailureTime' ) "
00134               "DESC 'The timestamps of the last consecutive authentication failures' "
00135               "EQUALITY generalizedTimeMatch "
00136               "ORDERING generalizedTimeOrderingMatch "
00137               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
00138               "NO-USER-MODIFICATION USAGE directoryOperation )",
00139               &ad_pwdFailureTime },
00140        {      "( 1.3.6.1.4.1.42.2.27.8.1.20 "
00141               "NAME ( 'pwdHistory' ) "
00142               "DESC 'The history of users passwords' "
00143               "EQUALITY octetStringMatch "
00144               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 "
00145               "NO-USER-MODIFICATION USAGE directoryOperation )",
00146               &ad_pwdHistory },
00147        {      "( 1.3.6.1.4.1.42.2.27.8.1.21 "
00148               "NAME ( 'pwdGraceUseTime' ) "
00149               "DESC 'The timestamps of the grace login once the password has expired' "
00150               "EQUALITY generalizedTimeMatch "
00151               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
00152               "NO-USER-MODIFICATION USAGE directoryOperation )",
00153               &ad_pwdGraceUseTime }, 
00154        {      "( 1.3.6.1.4.1.42.2.27.8.1.22 "
00155               "NAME ( 'pwdReset' ) "
00156               "DESC 'The indication that the password has been reset' "
00157               "EQUALITY booleanMatch "
00158               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
00159               "SINGLE-VALUE USAGE directoryOperation )",
00160               &ad_pwdReset },
00161        {      "( 1.3.6.1.4.1.42.2.27.8.1.23 "
00162               "NAME ( 'pwdPolicySubentry' ) "
00163               "DESC 'The pwdPolicy subentry in effect for this object' "
00164               "EQUALITY distinguishedNameMatch "
00165               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 "
00166               "SINGLE-VALUE "
00167 #if 0
00168               /* Not until Relax control is released */
00169               "NO-USER-MODIFICATION "
00170 #endif
00171               "USAGE directoryOperation )",
00172               &ad_pwdPolicySubentry },
00173        { NULL, NULL }
00174 };
00175 
00176 /* User attributes */
00177 static AttributeDescription *ad_pwdMinAge, *ad_pwdMaxAge, *ad_pwdInHistory,
00178        *ad_pwdCheckQuality, *ad_pwdMinLength, *ad_pwdMaxFailure, 
00179        *ad_pwdGraceAuthNLimit, *ad_pwdExpireWarning, *ad_pwdLockoutDuration,
00180        *ad_pwdFailureCountInterval, *ad_pwdCheckModule, *ad_pwdLockout,
00181        *ad_pwdMustChange, *ad_pwdAllowUserChange, *ad_pwdSafeModify,
00182        *ad_pwdAttribute;
00183 
00184 #define TAB(name)    { #name, &ad_##name }
00185 
00186 static struct schema_info pwd_UsSchema[] = {
00187        TAB(pwdAttribute),
00188        TAB(pwdMinAge),
00189        TAB(pwdMaxAge),
00190        TAB(pwdInHistory),
00191        TAB(pwdCheckQuality),
00192        TAB(pwdMinLength),
00193        TAB(pwdMaxFailure),
00194        TAB(pwdGraceAuthNLimit),
00195        TAB(pwdExpireWarning),
00196        TAB(pwdLockout),
00197        TAB(pwdLockoutDuration),
00198        TAB(pwdFailureCountInterval),
00199        TAB(pwdCheckModule),
00200        TAB(pwdMustChange),
00201        TAB(pwdAllowUserChange),
00202        TAB(pwdSafeModify),
00203        { NULL, NULL }
00204 };
00205 
00206 static ldap_pvt_thread_mutex_t chk_syntax_mutex;
00207 
00208 enum {
00209        PPOLICY_DEFAULT = 1,
00210        PPOLICY_HASH_CLEARTEXT,
00211        PPOLICY_USE_LOCKOUT
00212 };
00213 
00214 static ConfigDriver ppolicy_cf_default;
00215 
00216 static ConfigTable ppolicycfg[] = {
00217        { "ppolicy_default", "policyDN", 2, 2, 0,
00218          ARG_DN|ARG_QUOTE|ARG_MAGIC|PPOLICY_DEFAULT, ppolicy_cf_default,
00219          "( OLcfgOvAt:12.1 NAME 'olcPPolicyDefault' "
00220          "DESC 'DN of a pwdPolicy object for uncustomized objects' "
00221          "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
00222        { "ppolicy_hash_cleartext", "on|off", 1, 2, 0,
00223          ARG_ON_OFF|ARG_OFFSET|PPOLICY_HASH_CLEARTEXT,
00224          (void *)offsetof(pp_info,hash_passwords),
00225          "( OLcfgOvAt:12.2 NAME 'olcPPolicyHashCleartext' "
00226          "DESC 'Hash passwords on add or modify' "
00227          "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
00228        { "ppolicy_forward_updates", "on|off", 1, 2, 0,
00229          ARG_ON_OFF|ARG_OFFSET,
00230          (void *)offsetof(pp_info,forward_updates),
00231          "( OLcfgOvAt:12.4 NAME 'olcPPolicyForwardUpdates' "
00232          "DESC 'Allow policy state updates to be forwarded via updateref' "
00233          "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
00234        { "ppolicy_use_lockout", "on|off", 1, 2, 0,
00235          ARG_ON_OFF|ARG_OFFSET|PPOLICY_USE_LOCKOUT,
00236          (void *)offsetof(pp_info,use_lockout),
00237          "( OLcfgOvAt:12.3 NAME 'olcPPolicyUseLockout' "
00238          "DESC 'Warn clients with AccountLocked' "
00239          "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
00240        { NULL, NULL, 0, 0, 0, ARG_IGNORED }
00241 };
00242 
00243 static ConfigOCs ppolicyocs[] = {
00244        { "( OLcfgOvOc:12.1 "
00245          "NAME 'olcPPolicyConfig' "
00246          "DESC 'Password Policy configuration' "
00247          "SUP olcOverlayConfig "
00248          "MAY ( olcPPolicyDefault $ olcPPolicyHashCleartext $ "
00249          "olcPPolicyUseLockout $ olcPPolicyForwardUpdates ) )",
00250          Cft_Overlay, ppolicycfg },
00251        { NULL, 0, NULL }
00252 };
00253 
00254 static int
00255 ppolicy_cf_default( ConfigArgs *c )
00256 {
00257        slap_overinst *on = (slap_overinst *)c->bi;
00258        pp_info *pi = (pp_info *)on->on_bi.bi_private;
00259        int rc = ARG_BAD_CONF;
00260 
00261        assert ( c->type == PPOLICY_DEFAULT );
00262        Debug(LDAP_DEBUG_TRACE, "==> ppolicy_cf_default\n", 0, 0, 0);
00263 
00264        switch ( c->op ) {
00265        case SLAP_CONFIG_EMIT:
00266               Debug(LDAP_DEBUG_TRACE, "==> ppolicy_cf_default emit\n", 0, 0, 0);
00267               rc = 0;
00268               if ( !BER_BVISEMPTY( &pi->def_policy )) {
00269                      rc = value_add_one( &c->rvalue_vals,
00270                                        &pi->def_policy );
00271                      if ( rc ) return rc;
00272                      rc = value_add_one( &c->rvalue_nvals,
00273                                        &pi->def_policy );
00274               }
00275               break;
00276        case LDAP_MOD_DELETE:
00277               Debug(LDAP_DEBUG_TRACE, "==> ppolicy_cf_default delete\n", 0, 0, 0);
00278               if ( pi->def_policy.bv_val ) {
00279                      ber_memfree ( pi->def_policy.bv_val );
00280                      pi->def_policy.bv_val = NULL;
00281               }
00282               pi->def_policy.bv_len = 0;
00283               rc = 0;
00284               break;
00285        case SLAP_CONFIG_ADD:
00286               /* fallthrough to LDAP_MOD_ADD */
00287        case LDAP_MOD_ADD:
00288               Debug(LDAP_DEBUG_TRACE, "==> ppolicy_cf_default add\n", 0, 0, 0);
00289               if ( pi->def_policy.bv_val ) {
00290                      ber_memfree ( pi->def_policy.bv_val );
00291               }
00292               pi->def_policy = c->value_ndn;
00293               ber_memfree( c->value_dn.bv_val );
00294               BER_BVZERO( &c->value_dn );
00295               BER_BVZERO( &c->value_ndn );
00296               rc = 0;
00297               break;
00298        default:
00299               abort ();
00300        }
00301 
00302        return rc;
00303 }
00304 
00305 static time_t
00306 parse_time( char *atm )
00307 {
00308        struct lutil_tm tm;
00309        struct lutil_timet tt;
00310        time_t ret = (time_t)-1;
00311 
00312        if ( lutil_parsetime( atm, &tm ) == 0) {
00313               lutil_tm2time( &tm, &tt );
00314               ret = tt.tt_sec;
00315        }
00316        return ret;
00317 }
00318 
00319 static int
00320 account_locked( Operation *op, Entry *e,
00321               PassPolicy *pp, Modifications **mod ) 
00322 {
00323        Attribute       *la;
00324 
00325        assert(mod != NULL);
00326 
00327        if ( !pp->pwdLockout )
00328               return 0;
00329 
00330        if ( (la = attr_find( e->e_attrs, ad_pwdAccountLockedTime )) != NULL ) {
00331               BerVarray vals = la->a_nvals;
00332 
00333               /*
00334                * there is a lockout stamp - we now need to know if it's
00335                * a valid one.
00336                */
00337               if (vals[0].bv_val != NULL) {
00338                      time_t then, now;
00339                      Modifications *m;
00340 
00341                      if (!pp->pwdLockoutDuration)
00342                             return 1;
00343 
00344                      if ((then = parse_time( vals[0].bv_val )) == (time_t)0)
00345                             return 1;
00346 
00347                      now = slap_get_time();
00348 
00349                      if (now < then + pp->pwdLockoutDuration)
00350                             return 1;
00351 
00352                      m = ch_calloc( sizeof(Modifications), 1 );
00353                      m->sml_op = LDAP_MOD_DELETE;
00354                      m->sml_flags = 0;
00355                      m->sml_type = ad_pwdAccountLockedTime->ad_cname;
00356                      m->sml_desc = ad_pwdAccountLockedTime;
00357                      m->sml_next = *mod;
00358                      *mod = m;
00359               }
00360        }
00361 
00362        return 0;
00363 }
00364 
00365 /* IMPLICIT TAGS, all context-specific */
00366 #define PPOLICY_WARNING 0xa0L      /* constructed + 0 */
00367 #define PPOLICY_ERROR 0x81L        /* primitive + 1 */
00368  
00369 #define PPOLICY_EXPIRE 0x80L       /* primitive + 0 */
00370 #define PPOLICY_GRACE  0x81L       /* primitive + 1 */
00371 
00372 static const char ppolicy_ctrl_oid[] = LDAP_CONTROL_PASSWORDPOLICYRESPONSE;
00373 
00374 static LDAPControl *
00375 create_passcontrol( Operation *op, int exptime, int grace, LDAPPasswordPolicyError err )
00376 {
00377        BerElementBuffer berbuf, bb2;
00378        BerElement *ber = (BerElement *) &berbuf, *b2 = (BerElement *) &bb2;
00379        LDAPControl c = { 0 }, *cp;
00380        struct berval bv;
00381 
00382        BER_BVZERO( &c.ldctl_value );
00383 
00384        ber_init2( ber, NULL, LBER_USE_DER );
00385        ber_printf( ber, "{" /*}*/ );
00386 
00387        if ( exptime >= 0 ) {
00388               ber_init2( b2, NULL, LBER_USE_DER );
00389               ber_printf( b2, "ti", PPOLICY_EXPIRE, exptime );
00390               ber_flatten2( b2, &bv, 1 );
00391               (void)ber_free_buf(b2);
00392               ber_printf( ber, "tO", PPOLICY_WARNING, &bv );
00393               ch_free( bv.bv_val );
00394        } else if ( grace > 0 ) {
00395               ber_init2( b2, NULL, LBER_USE_DER );
00396               ber_printf( b2, "ti", PPOLICY_GRACE, grace );
00397               ber_flatten2( b2, &bv, 1 );
00398               (void)ber_free_buf(b2);
00399               ber_printf( ber, "tO", PPOLICY_WARNING, &bv );
00400               ch_free( bv.bv_val );
00401        }
00402 
00403        if (err != PP_noError ) {
00404               ber_printf( ber, "te", PPOLICY_ERROR, err );
00405        }
00406        ber_printf( ber, /*{*/ "N}" );
00407 
00408        if (ber_flatten2( ber, &c.ldctl_value, 0 ) == -1) {
00409               return NULL;
00410        }
00411        cp = op->o_tmpalloc( sizeof( LDAPControl ) + c.ldctl_value.bv_len, op->o_tmpmemctx );
00412        cp->ldctl_oid = (char *)ppolicy_ctrl_oid;
00413        cp->ldctl_iscritical = 0;
00414        cp->ldctl_value.bv_val = (char *)&cp[1];
00415        cp->ldctl_value.bv_len = c.ldctl_value.bv_len;
00416        AC_MEMCPY( cp->ldctl_value.bv_val, c.ldctl_value.bv_val, c.ldctl_value.bv_len );
00417        (void)ber_free_buf(ber);
00418        
00419        return cp;
00420 }
00421 
00422 static LDAPControl **
00423 add_passcontrol( Operation *op, SlapReply *rs, LDAPControl *ctrl )
00424 {
00425        LDAPControl **ctrls, **oldctrls = rs->sr_ctrls;
00426        int n;
00427 
00428        n = 0;
00429        if ( oldctrls ) {
00430               for ( ; oldctrls[n]; n++ )
00431                      ;
00432        }
00433        n += 2;
00434 
00435        ctrls = op->o_tmpcalloc( sizeof( LDAPControl * ), n, op->o_tmpmemctx );
00436 
00437        n = 0;
00438        if ( oldctrls ) {
00439               for ( ; oldctrls[n]; n++ ) {
00440                      ctrls[n] = oldctrls[n];
00441               }
00442        }
00443        ctrls[n] = ctrl;
00444        ctrls[n+1] = NULL;
00445 
00446        rs->sr_ctrls = ctrls;
00447 
00448        return oldctrls;
00449 }
00450 
00451 static void
00452 ppolicy_get( Operation *op, Entry *e, PassPolicy *pp )
00453 {
00454        slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
00455        pp_info *pi = on->on_bi.bi_private;
00456        Attribute *a;
00457        BerVarray vals;
00458        int rc;
00459        Entry *pe = NULL;
00460 #if 0
00461        const char *text;
00462 #endif
00463 
00464        memset( pp, 0, sizeof(PassPolicy) );
00465 
00466        pp->ad = slap_schema.si_ad_userPassword;
00467 
00468        /* Users can change their own password by default */
00469        pp->pwdAllowUserChange = 1;
00470 
00471        if ((a = attr_find( e->e_attrs, ad_pwdPolicySubentry )) == NULL) {
00472               /*
00473                * entry has no password policy assigned - use default
00474                */
00475               vals = &pi->def_policy;
00476               if ( !vals->bv_val )
00477                      goto defaultpol;
00478        } else {
00479               vals = a->a_nvals;
00480               if (vals[0].bv_val == NULL) {
00481                      Debug( LDAP_DEBUG_ANY,
00482                             "ppolicy_get: NULL value for policySubEntry\n", 0, 0, 0 );
00483                      goto defaultpol;
00484               }
00485        }
00486 
00487        op->o_bd->bd_info = (BackendInfo *)on->on_info;
00488        rc = be_entry_get_rw( op, vals, NULL, NULL, 0, &pe );
00489        op->o_bd->bd_info = (BackendInfo *)on;
00490 
00491        if ( rc ) goto defaultpol;
00492 
00493 #if 0  /* Only worry about userPassword for now */
00494        if ((a = attr_find( pe->e_attrs, ad_pwdAttribute )))
00495               slap_bv2ad( &a->a_vals[0], &pp->ad, &text );
00496 #endif
00497 
00498        if ( ( a = attr_find( pe->e_attrs, ad_pwdMinAge ) )
00499                      && lutil_atoi( &pp->pwdMinAge, a->a_vals[0].bv_val ) != 0 )
00500               goto defaultpol;
00501        if ( ( a = attr_find( pe->e_attrs, ad_pwdMaxAge ) )
00502                      && lutil_atoi( &pp->pwdMaxAge, a->a_vals[0].bv_val ) != 0 )
00503               goto defaultpol;
00504        if ( ( a = attr_find( pe->e_attrs, ad_pwdInHistory ) )
00505                      && lutil_atoi( &pp->pwdInHistory, a->a_vals[0].bv_val ) != 0 )
00506               goto defaultpol;
00507        if ( ( a = attr_find( pe->e_attrs, ad_pwdCheckQuality ) )
00508                      && lutil_atoi( &pp->pwdCheckQuality, a->a_vals[0].bv_val ) != 0 )
00509               goto defaultpol;
00510        if ( ( a = attr_find( pe->e_attrs, ad_pwdMinLength ) )
00511                      && lutil_atoi( &pp->pwdMinLength, a->a_vals[0].bv_val ) != 0 )
00512               goto defaultpol;
00513        if ( ( a = attr_find( pe->e_attrs, ad_pwdMaxFailure ) )
00514                      && lutil_atoi( &pp->pwdMaxFailure, a->a_vals[0].bv_val ) != 0 )
00515               goto defaultpol;
00516        if ( ( a = attr_find( pe->e_attrs, ad_pwdGraceAuthNLimit ) )
00517                      && lutil_atoi( &pp->pwdGraceAuthNLimit, a->a_vals[0].bv_val ) != 0 )
00518               goto defaultpol;
00519        if ( ( a = attr_find( pe->e_attrs, ad_pwdExpireWarning ) )
00520                      && lutil_atoi( &pp->pwdExpireWarning, a->a_vals[0].bv_val ) != 0 )
00521               goto defaultpol;
00522        if ( ( a = attr_find( pe->e_attrs, ad_pwdFailureCountInterval ) )
00523                      && lutil_atoi( &pp->pwdFailureCountInterval, a->a_vals[0].bv_val ) != 0 )
00524               goto defaultpol;
00525        if ( ( a = attr_find( pe->e_attrs, ad_pwdLockoutDuration ) )
00526                      && lutil_atoi( &pp->pwdLockoutDuration, a->a_vals[0].bv_val ) != 0 )
00527               goto defaultpol;
00528 
00529        if ( ( a = attr_find( pe->e_attrs, ad_pwdCheckModule ) ) ) {
00530               strncpy( pp->pwdCheckModule, a->a_vals[0].bv_val,
00531                      sizeof(pp->pwdCheckModule) );
00532               pp->pwdCheckModule[sizeof(pp->pwdCheckModule)-1] = '\0';
00533        }
00534 
00535        if ((a = attr_find( pe->e_attrs, ad_pwdLockout )))
00536               pp->pwdLockout = bvmatch( &a->a_nvals[0], &slap_true_bv );
00537        if ((a = attr_find( pe->e_attrs, ad_pwdMustChange )))
00538               pp->pwdMustChange = bvmatch( &a->a_nvals[0], &slap_true_bv );
00539        if ((a = attr_find( pe->e_attrs, ad_pwdAllowUserChange )))
00540               pp->pwdAllowUserChange = bvmatch( &a->a_nvals[0], &slap_true_bv );
00541        if ((a = attr_find( pe->e_attrs, ad_pwdSafeModify )))
00542               pp->pwdSafeModify = bvmatch( &a->a_nvals[0], &slap_true_bv );
00543     
00544        op->o_bd->bd_info = (BackendInfo *)on->on_info;
00545        be_entry_release_r( op, pe );
00546        op->o_bd->bd_info = (BackendInfo *)on;
00547 
00548        return;
00549 
00550 defaultpol:
00551        Debug( LDAP_DEBUG_TRACE,
00552               "ppolicy_get: using default policy\n", 0, 0, 0 );
00553        return;
00554 }
00555 
00556 static int
00557 password_scheme( struct berval *cred, struct berval *sch )
00558 {
00559        int e;
00560     
00561        assert( cred != NULL );
00562 
00563        if (sch) {
00564               sch->bv_val = NULL;
00565               sch->bv_len = 0;
00566        }
00567     
00568        if ((cred->bv_len == 0) || (cred->bv_val == NULL) ||
00569               (cred->bv_val[0] != '{')) return LDAP_OTHER;
00570 
00571        for(e = 1; cred->bv_val[e] && cred->bv_val[e] != '}'; e++);
00572        if (cred->bv_val[e]) {
00573               int rc;
00574               rc = lutil_passwd_scheme( cred->bv_val );
00575               if (rc) {
00576                      if (sch) {
00577                             sch->bv_val = cred->bv_val;
00578                             sch->bv_len = e;
00579                      }
00580                      return LDAP_SUCCESS;
00581               }
00582        }
00583        return LDAP_OTHER;
00584 }
00585 
00586 static int
00587 check_password_quality( struct berval *cred, PassPolicy *pp, LDAPPasswordPolicyError *err, Entry *e, char **txt )
00588 {
00589        int rc = LDAP_SUCCESS, ok = LDAP_SUCCESS;
00590        char *ptr;
00591        struct berval sch;
00592 
00593        assert( cred != NULL );
00594        assert( pp != NULL );
00595        assert( txt != NULL );
00596 
00597        ptr = cred->bv_val;
00598 
00599        *txt = NULL;
00600 
00601        if ((cred->bv_len == 0) || (pp->pwdMinLength > cred->bv_len)) {
00602               rc = LDAP_CONSTRAINT_VIOLATION;
00603               if ( err ) *err = PP_passwordTooShort;
00604               return rc;
00605        }
00606 
00607         /*
00608          * We need to know if the password is already hashed - if so
00609          * what scheme is it. The reason being that the "hash" of
00610          * {cleartext} still allows us to check the password.
00611          */
00612        rc = password_scheme( cred, &sch );
00613        if (rc == LDAP_SUCCESS) {
00614               if ((sch.bv_val) && (strncasecmp( sch.bv_val, "{cleartext}",
00615                      sch.bv_len ) == 0)) {
00616                      /*
00617                       * We can check the cleartext "hash"
00618                       */
00619                      ptr = cred->bv_val + sch.bv_len;
00620               } else {
00621                      /* everything else, we can't check */
00622                      if (pp->pwdCheckQuality == 2) {
00623                             rc = LDAP_CONSTRAINT_VIOLATION;
00624                             if (err) *err = PP_insufficientPasswordQuality;
00625                             return rc;
00626                      }
00627                      /*
00628                       * We can't check the syntax of the password, but it's not
00629                       * mandatory (according to the policy), so we return success.
00630                       */
00631                   
00632                      return LDAP_SUCCESS;
00633               }
00634        }
00635 
00636        rc = LDAP_SUCCESS;
00637 
00638        if (pp->pwdCheckModule[0]) {
00639 #ifdef SLAPD_MODULES
00640               lt_dlhandle mod;
00641               const char *err;
00642               
00643               if ((mod = lt_dlopen( pp->pwdCheckModule )) == NULL) {
00644                      err = lt_dlerror();
00645 
00646                      Debug(LDAP_DEBUG_ANY,
00647                      "check_password_quality: lt_dlopen failed: (%s) %s.\n",
00648                             pp->pwdCheckModule, err, 0 );
00649                      ok = LDAP_OTHER; /* internal error */
00650               } else {
00651                      /* FIXME: the error message ought to be passed thru a
00652                       * struct berval, with preallocated buffer and size
00653                       * passed in. Module can still allocate a buffer for
00654                       * it if the provided one is too small.
00655                       */
00656                      int (*prog)( char *passwd, char **text, Entry *ent );
00657 
00658                      if ((prog = lt_dlsym( mod, "check_password" )) == NULL) {
00659                             err = lt_dlerror();
00660                          
00661                             Debug(LDAP_DEBUG_ANY,
00662                                    "check_password_quality: lt_dlsym failed: (%s) %s.\n",
00663                                    pp->pwdCheckModule, err, 0 );
00664                             ok = LDAP_OTHER;
00665                      } else {
00666                             ldap_pvt_thread_mutex_lock( &chk_syntax_mutex );
00667                             ok = prog( ptr, txt, e );
00668                             ldap_pvt_thread_mutex_unlock( &chk_syntax_mutex );
00669                             if (ok != LDAP_SUCCESS) {
00670                                    Debug(LDAP_DEBUG_ANY,
00671                                           "check_password_quality: module error: (%s) %s.[%d]\n",
00672                                           pp->pwdCheckModule, *txt ? *txt : "", ok );
00673                             }
00674                      }
00675                          
00676                      lt_dlclose( mod );
00677               }
00678 #else
00679        Debug(LDAP_DEBUG_ANY, "check_password_quality: external modules not "
00680               "supported. pwdCheckModule ignored.\n", 0, 0, 0);
00681 #endif /* SLAPD_MODULES */
00682        }
00683               
00684                   
00685        if (ok != LDAP_SUCCESS) {
00686               rc = LDAP_CONSTRAINT_VIOLATION;
00687               if (err) *err = PP_insufficientPasswordQuality;
00688        }
00689        
00690        return rc;
00691 }
00692 
00693 static int
00694 parse_pwdhistory( struct berval *bv, char **oid, time_t *oldtime, struct berval *oldpw )
00695 {
00696        char *ptr;
00697        struct berval nv, npw;
00698        ber_len_t i, j;
00699        
00700        assert (bv && (bv->bv_len > 0) && (bv->bv_val) && oldtime && oldpw );
00701 
00702        if ( oid ) {
00703               *oid = 0;
00704        }
00705        *oldtime = (time_t)-1;
00706        BER_BVZERO( oldpw );
00707        
00708        ber_dupbv( &nv, bv );
00709 
00710        /* first get the time field */
00711        for ( i = 0; (i < nv.bv_len) && (nv.bv_val[i] != '#'); i++ )
00712               ;
00713        if ( i == nv.bv_len ) {
00714               goto exit_failure; /* couldn't locate the '#' separator */
00715        }
00716        nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */
00717        ptr = nv.bv_val;
00718        *oldtime = parse_time( ptr );
00719        if (*oldtime == (time_t)-1) {
00720               goto exit_failure;
00721        }
00722 
00723        /* get the OID field */
00724        for (ptr = &(nv.bv_val[i]); (i < nv.bv_len) && (nv.bv_val[i] != '#'); i++ )
00725               ;
00726        if ( i == nv.bv_len ) {
00727               goto exit_failure; /* couldn't locate the '#' separator */
00728        }
00729        nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */
00730        if ( oid ) {
00731               *oid = ber_strdup( ptr );
00732        }
00733        
00734        /* get the length field */
00735        for ( ptr = &(nv.bv_val[i]); (i < nv.bv_len) && (nv.bv_val[i] != '#'); i++ )
00736               ;
00737        if ( i == nv.bv_len ) {
00738               goto exit_failure; /* couldn't locate the '#' separator */
00739        }
00740        nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */
00741        oldpw->bv_len = strtol( ptr, NULL, 10 );
00742        if (errno == ERANGE) {
00743               goto exit_failure;
00744        }
00745 
00746        /* lastly, get the octets of the string */
00747        for ( j = i, ptr = &(nv.bv_val[i]); i < nv.bv_len; i++ )
00748               ;
00749        if ( i - j != oldpw->bv_len) {
00750               goto exit_failure; /* length is wrong */
00751        }
00752 
00753        npw.bv_val = ptr;
00754        npw.bv_len = oldpw->bv_len;
00755        ber_dupbv( oldpw, &npw );
00756        ber_memfree( nv.bv_val );
00757        
00758        return LDAP_SUCCESS;
00759 
00760 exit_failure:;
00761        if ( oid && *oid ) {
00762               ber_memfree(*oid);
00763               *oid = NULL;
00764        }
00765        if ( oldpw->bv_val ) {
00766               ber_memfree( oldpw->bv_val);
00767               BER_BVZERO( oldpw );
00768        }
00769        ber_memfree( nv.bv_val );
00770 
00771        return LDAP_OTHER;
00772 }
00773 
00774 static void
00775 add_to_pwd_history( pw_hist **l, time_t t,
00776                     struct berval *oldpw, struct berval *bv )
00777 {
00778        pw_hist *p, *p1, *p2;
00779     
00780        if (!l) return;
00781 
00782        p = ch_malloc( sizeof( pw_hist ));
00783        p->pw = *oldpw;
00784        ber_dupbv( &p->bv, bv );
00785        p->t = t;
00786        p->next = NULL;
00787        
00788        if (*l == NULL) {
00789               /* degenerate case */
00790               *l = p;
00791               return;
00792        }
00793        /*
00794         * advance p1 and p2 such that p1 is the node before the
00795         * new one, and p2 is the node after it
00796         */
00797        for (p1 = NULL, p2 = *l; p2 && p2->t <= t; p1 = p2, p2=p2->next );
00798        p->next = p2;
00799        if (p1 == NULL) { *l = p; return; }
00800        p1->next = p;
00801 }
00802 
00803 #ifndef MAX_PWD_HISTORY_SZ
00804 #define MAX_PWD_HISTORY_SZ 1024
00805 #endif /* MAX_PWD_HISTORY_SZ */
00806 
00807 static void
00808 make_pwd_history_value( char *timebuf, struct berval *bv, Attribute *pa )
00809 {
00810        char str[ MAX_PWD_HISTORY_SZ ];
00811        int nlen;
00812 
00813        snprintf( str, MAX_PWD_HISTORY_SZ,
00814                 "%s#%s#%lu#", timebuf,
00815                 pa->a_desc->ad_type->sat_syntax->ssyn_oid,
00816                 (unsigned long) pa->a_nvals[0].bv_len );
00817        str[MAX_PWD_HISTORY_SZ-1] = 0;
00818        nlen = strlen(str);
00819 
00820         /*
00821          * We have to assume that the string is a string of octets,
00822          * not readable characters. In reality, yes, it probably is
00823          * a readable (ie, base64) string, but we can't count on that
00824          * Hence, while the first 3 fields of the password history
00825          * are definitely readable (a timestamp, an OID and an integer
00826          * length), the remaining octets of the actual password
00827          * are deemed to be binary data.
00828          */
00829        AC_MEMCPY( str + nlen, pa->a_nvals[0].bv_val, pa->a_nvals[0].bv_len );
00830        nlen += pa->a_nvals[0].bv_len;
00831        bv->bv_val = ch_malloc( nlen + 1 );
00832        AC_MEMCPY( bv->bv_val, str, nlen );
00833        bv->bv_val[nlen] = '\0';
00834        bv->bv_len = nlen;
00835 }
00836 
00837 static void
00838 free_pwd_history_list( pw_hist **l )
00839 {
00840        pw_hist *p;
00841     
00842        if (!l) return;
00843        p = *l;
00844        while (p) {
00845               pw_hist *pp = p->next;
00846 
00847               free(p->pw.bv_val);
00848               free(p->bv.bv_val);
00849               free(p);
00850               p = pp;
00851        }
00852        *l = NULL;
00853 }
00854 
00855 typedef struct ppbind {
00856        slap_overinst *on;
00857        int send_ctrl;
00858        int set_restrict;
00859        LDAPControl **oldctrls;
00860        Modifications *mod;
00861        LDAPPasswordPolicyError pErr;
00862        PassPolicy pp;
00863 } ppbind;
00864 
00865 static void
00866 ctrls_cleanup( Operation *op, SlapReply *rs, LDAPControl **oldctrls )
00867 {
00868        int n;
00869 
00870        assert( rs->sr_ctrls != NULL );
00871        assert( rs->sr_ctrls[0] != NULL );
00872 
00873        for ( n = 0; rs->sr_ctrls[n]; n++ ) {
00874               if ( rs->sr_ctrls[n]->ldctl_oid == ppolicy_ctrl_oid ) {
00875                      op->o_tmpfree( rs->sr_ctrls[n], op->o_tmpmemctx );
00876                      rs->sr_ctrls[n] = (LDAPControl *)(-1);
00877                      break;
00878               }
00879        }
00880 
00881        if ( rs->sr_ctrls[n] == NULL ) {
00882               /* missed? */
00883        }
00884 
00885        op->o_tmpfree( rs->sr_ctrls, op->o_tmpmemctx );
00886 
00887        rs->sr_ctrls = oldctrls;
00888 }
00889 
00890 static int
00891 ppolicy_ctrls_cleanup( Operation *op, SlapReply *rs )
00892 {
00893        ppbind *ppb = op->o_callback->sc_private;
00894        if ( ppb->send_ctrl ) {
00895               ctrls_cleanup( op, rs, ppb->oldctrls );
00896        }
00897        return SLAP_CB_CONTINUE;
00898 }
00899 
00900 static int
00901 ppolicy_bind_response( Operation *op, SlapReply *rs )
00902 {
00903        ppbind *ppb = op->o_callback->sc_private;
00904        slap_overinst *on = ppb->on;
00905        Modifications *mod = ppb->mod, *m;
00906        int pwExpired = 0;
00907        int ngut = -1, warn = -1, age, rc;
00908        Attribute *a;
00909        time_t now, pwtime = (time_t)-1;
00910        char nowstr[ LDAP_LUTIL_GENTIME_BUFSIZE ];
00911        struct berval timestamp;
00912        BackendInfo *bi = op->o_bd->bd_info;
00913        Entry *e;
00914 
00915        /* If we already know it's locked, just get on with it */
00916        if ( ppb->pErr != PP_noError ) {
00917               goto locked;
00918        }
00919 
00920        op->o_bd->bd_info = (BackendInfo *)on->on_info;
00921        rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
00922        op->o_bd->bd_info = bi;
00923 
00924        if ( rc != LDAP_SUCCESS ) {
00925               return SLAP_CB_CONTINUE;
00926        }
00927 
00928        now = slap_get_time(); /* stored for later consideration */
00929        timestamp.bv_val = nowstr;
00930        timestamp.bv_len = sizeof(nowstr);
00931        slap_timestamp( &now, &timestamp );
00932 
00933        if ( rs->sr_err == LDAP_INVALID_CREDENTIALS ) {
00934               int i = 0, fc = 0;
00935 
00936               m = ch_calloc( sizeof(Modifications), 1 );
00937               m->sml_op = LDAP_MOD_ADD;
00938               m->sml_flags = 0;
00939               m->sml_type = ad_pwdFailureTime->ad_cname;
00940               m->sml_desc = ad_pwdFailureTime;
00941               m->sml_numvals = 1;
00942               m->sml_values = ch_calloc( sizeof(struct berval), 2 );
00943               m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
00944 
00945               ber_dupbv( &m->sml_values[0], &timestamp );
00946               ber_dupbv( &m->sml_nvalues[0], &timestamp );
00947               m->sml_next = mod;
00948               mod = m;
00949 
00950               /*
00951                * Count the pwdFailureTimes - if it's
00952                * greater than the policy pwdMaxFailure,
00953                * then lock the account.
00954                */
00955               if ((a = attr_find( e->e_attrs, ad_pwdFailureTime )) != NULL) {
00956                      for(i=0; a->a_nvals[i].bv_val; i++) {
00957 
00958                             /*
00959                              * If the interval is 0, then failures
00960                              * stay on the record until explicitly
00961                              * reset by successful authentication.
00962                              */
00963                             if (ppb->pp.pwdFailureCountInterval == 0) {
00964                                    fc++;
00965                             } else if (now <=
00966                                                  parse_time(a->a_nvals[i].bv_val) +
00967                                                  ppb->pp.pwdFailureCountInterval) {
00968 
00969                                    fc++;
00970                             }
00971                             /*
00972                              * We only count those failures
00973                              * which are not due to expire.
00974                              */
00975                      }
00976               }
00977               
00978               if ((ppb->pp.pwdMaxFailure > 0) &&
00979                      (fc >= ppb->pp.pwdMaxFailure - 1)) {
00980 
00981                      /*
00982                       * We subtract 1 from the failure max
00983                       * because the new failure entry hasn't
00984                       * made it to the entry yet.
00985                       */
00986                      m = ch_calloc( sizeof(Modifications), 1 );
00987                      m->sml_op = LDAP_MOD_REPLACE;
00988                      m->sml_flags = 0;
00989                      m->sml_type = ad_pwdAccountLockedTime->ad_cname;
00990                      m->sml_desc = ad_pwdAccountLockedTime;
00991                      m->sml_numvals = 1;
00992                      m->sml_values = ch_calloc( sizeof(struct berval), 2 );
00993                      m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
00994                      ber_dupbv( &m->sml_values[0], &timestamp );
00995                      ber_dupbv( &m->sml_nvalues[0], &timestamp );
00996                      m->sml_next = mod;
00997                      mod = m;
00998               }
00999        } else if ( rs->sr_err == LDAP_SUCCESS ) {
01000               if ((a = attr_find( e->e_attrs, ad_pwdChangedTime )) != NULL)
01001                      pwtime = parse_time( a->a_nvals[0].bv_val );
01002 
01003               /* delete all pwdFailureTimes */
01004               if ( attr_find( e->e_attrs, ad_pwdFailureTime )) {
01005                      m = ch_calloc( sizeof(Modifications), 1 );
01006                      m->sml_op = LDAP_MOD_DELETE;
01007                      m->sml_flags = 0;
01008                      m->sml_type = ad_pwdFailureTime->ad_cname;
01009                      m->sml_desc = ad_pwdFailureTime;
01010                      m->sml_next = mod;
01011                      mod = m;
01012               }
01013 
01014               /*
01015                * check to see if the password must be changed
01016                */
01017               if ( ppb->pp.pwdMustChange &&
01018                      (a = attr_find( e->e_attrs, ad_pwdReset )) &&
01019                      bvmatch( &a->a_nvals[0], &slap_true_bv ) )
01020               {
01021                      /*
01022                       * need to inject client controls here to give
01023                       * more information. For the moment, we ensure
01024                       * that we are disallowed from doing anything
01025                       * other than change password.
01026                       */
01027                      if ( ppb->set_restrict ) {
01028                             ber_dupbv( &pwcons[op->o_conn->c_conn_idx].dn,
01029                                    &op->o_conn->c_ndn );
01030                      }
01031 
01032                      ppb->pErr = PP_changeAfterReset;
01033 
01034               } else {
01035                      /*
01036                       * the password does not need to be changed, so
01037                       * we now check whether the password has expired.
01038                       *
01039                       * We can skip this bit if passwords don't age in
01040                       * the policy. Also, if there was no pwdChangedTime
01041                       * attribute in the entry, the password never expires.
01042                       */
01043                      if (ppb->pp.pwdMaxAge == 0) goto grace;
01044 
01045                      if (pwtime != (time_t)-1) {
01046                             /*
01047                              * Check: was the last change time of
01048                              * the password older than the maximum age
01049                              * allowed. (Ignore case 2 from I-D, it's just silly.)
01050                              */
01051                             if (now - pwtime > ppb->pp.pwdMaxAge ) pwExpired = 1;
01052                      }
01053               }
01054 
01055 grace:
01056               if (!pwExpired) goto check_expiring_password;
01057               
01058               if ((a = attr_find( e->e_attrs, ad_pwdGraceUseTime )) == NULL)
01059                      ngut = ppb->pp.pwdGraceAuthNLimit;
01060               else {
01061                      for(ngut=0; a->a_nvals[ngut].bv_val; ngut++);
01062                      ngut = ppb->pp.pwdGraceAuthNLimit - ngut;
01063               }
01064 
01065               /*
01066                * ngut is the number of remaining grace logins
01067                */
01068               Debug( LDAP_DEBUG_ANY,
01069                      "ppolicy_bind: Entry %s has an expired password: %d grace logins\n",
01070                      e->e_name.bv_val, ngut, 0);
01071               
01072               if (ngut < 1) {
01073                      ppb->pErr = PP_passwordExpired;
01074                      rs->sr_err = LDAP_INVALID_CREDENTIALS;
01075                      goto done;
01076               }
01077 
01078               /*
01079                * Add a grace user time to the entry
01080                */
01081               m = ch_calloc( sizeof(Modifications), 1 );
01082               m->sml_op = LDAP_MOD_ADD;
01083               m->sml_flags = 0;
01084               m->sml_type = ad_pwdGraceUseTime->ad_cname;
01085               m->sml_desc = ad_pwdGraceUseTime;
01086               m->sml_numvals = 1;
01087               m->sml_values = ch_calloc( sizeof(struct berval), 2 );
01088               m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
01089               ber_dupbv( &m->sml_values[0], &timestamp );
01090               ber_dupbv( &m->sml_nvalues[0], &timestamp );
01091               m->sml_next = mod;
01092               mod = m;
01093 
01094 check_expiring_password:
01095               /*
01096                * Now we need to check to see
01097                * if it is about to expire, and if so, should the user
01098                * be warned about it in the password policy control.
01099                *
01100                * If the password has expired, and we're in the grace period, then
01101                * we don't need to do this bit. Similarly, if we don't have password
01102                * aging, then there's no need to do this bit either.
01103                */
01104               if ((ppb->pp.pwdMaxAge < 1) || (pwExpired) || (ppb->pp.pwdExpireWarning < 1))
01105                      goto done;
01106 
01107               age = (int)(now - pwtime);
01108               
01109               /*
01110                * We know that there is a password Change Time attribute - if
01111                * there wasn't, then the pwdExpired value would be true, unless
01112                * there is no password aging - and if there is no password aging,
01113                * then this section isn't called anyway - you can't have an
01114                * expiring password if there's no limit to expire.
01115                */
01116               if (ppb->pp.pwdMaxAge - age < ppb->pp.pwdExpireWarning ) {
01117                      /*
01118                       * Set the warning value.
01119                       */
01120                      warn = ppb->pp.pwdMaxAge - age; /* seconds left until expiry */
01121                      if (warn < 0) warn = 0; /* something weird here - why is pwExpired not set? */
01122                      
01123                      Debug( LDAP_DEBUG_ANY,
01124                             "ppolicy_bind: Setting warning for password expiry for %s = %d seconds\n",
01125                             op->o_req_dn.bv_val, warn, 0 );
01126               }
01127        }
01128 
01129 done:
01130        op->o_bd->bd_info = (BackendInfo *)on->on_info;
01131        be_entry_release_r( op, e );
01132 
01133 locked:
01134        if ( mod ) {
01135               Operation op2 = *op;
01136               SlapReply r2 = { REP_RESULT };
01137               slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
01138               pp_info *pi = on->on_bi.bi_private;
01139               LDAPControl c, *ca[2];
01140 
01141               op2.o_tag = LDAP_REQ_MODIFY;
01142               op2.o_callback = &cb;
01143               op2.orm_modlist = mod;
01144               op2.orm_no_opattrs = 0;
01145               op2.o_dn = op->o_bd->be_rootdn;
01146               op2.o_ndn = op->o_bd->be_rootndn;
01147 
01148               /* If this server is a shadow and forward_updates is true,
01149                * use the frontend to perform this modify. That will trigger
01150                * the update referral, which can then be forwarded by the
01151                * chain overlay. Obviously the updateref and chain overlay
01152                * must be configured appropriately for this to be useful.
01153                */
01154               if ( SLAP_SHADOW( op->o_bd ) && pi->forward_updates ) {
01155                      op2.o_bd = frontendDB;
01156 
01157                      /* Must use Relax control since these are no-user-mod */
01158                      op2.o_relax = SLAP_CONTROL_CRITICAL;
01159                      op2.o_ctrls = ca;
01160                      ca[0] = &c;
01161                      ca[1] = NULL;
01162                      BER_BVZERO( &c.ldctl_value );
01163                      c.ldctl_iscritical = 1;
01164                      c.ldctl_oid = LDAP_CONTROL_RELAX;
01165               } else {
01166                      /* If not forwarding, don't update opattrs and don't replicate */
01167                      if ( SLAP_SINGLE_SHADOW( op->o_bd )) {
01168                             op2.orm_no_opattrs = 1;
01169                             op2.o_dont_replicate = 1;
01170                      }
01171                      op2.o_bd->bd_info = (BackendInfo *)on->on_info;
01172               }
01173               rc = op2.o_bd->be_modify( &op2, &r2 );
01174               slap_mods_free( mod, 1 );
01175        }
01176 
01177        if ( ppb->send_ctrl ) {
01178               LDAPControl *ctrl = NULL;
01179               pp_info *pi = on->on_bi.bi_private;
01180 
01181               /* Do we really want to tell that the account is locked? */
01182               if ( ppb->pErr == PP_accountLocked && !pi->use_lockout ) {
01183                      ppb->pErr = PP_noError;
01184               }
01185               ctrl = create_passcontrol( op, warn, ngut, ppb->pErr );
01186               ppb->oldctrls = add_passcontrol( op, rs, ctrl );
01187               op->o_callback->sc_cleanup = ppolicy_ctrls_cleanup;
01188        }
01189        op->o_bd->bd_info = bi;
01190        return SLAP_CB_CONTINUE;
01191 }
01192 
01193 static int
01194 ppolicy_bind( Operation *op, SlapReply *rs )
01195 {
01196        slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
01197 
01198        /* Reset lockout status on all Bind requests */
01199        if ( !BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn )) {
01200               ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val );
01201               BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn );
01202        }
01203 
01204        /* Root bypasses policy */
01205        if ( !be_isroot_dn( op->o_bd, &op->o_req_ndn )) {
01206               Entry *e;
01207               int rc;
01208               ppbind *ppb;
01209               slap_callback *cb;
01210 
01211               op->o_bd->bd_info = (BackendInfo *)on->on_info;
01212               rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
01213 
01214               if ( rc != LDAP_SUCCESS ) {
01215                      return SLAP_CB_CONTINUE;
01216               }
01217 
01218               cb = op->o_tmpcalloc( sizeof(ppbind)+sizeof(slap_callback),
01219                      1, op->o_tmpmemctx );
01220               ppb = (ppbind *)(cb+1);
01221               ppb->on = on;
01222               ppb->pErr = PP_noError;
01223               ppb->set_restrict = 1;
01224 
01225               /* Setup a callback so we can munge the result */
01226 
01227               cb->sc_response = ppolicy_bind_response;
01228               cb->sc_next = op->o_callback->sc_next;
01229               cb->sc_private = ppb;
01230               op->o_callback->sc_next = cb;
01231 
01232               /* Did we receive a password policy request control? */
01233               if ( op->o_ctrlflag[ppolicy_cid] ) {
01234                      ppb->send_ctrl = 1;
01235               }
01236 
01237               op->o_bd->bd_info = (BackendInfo *)on;
01238               ppolicy_get( op, e, &ppb->pp );
01239 
01240               rc = account_locked( op, e, &ppb->pp, &ppb->mod );
01241 
01242               op->o_bd->bd_info = (BackendInfo *)on->on_info;
01243               be_entry_release_r( op, e );
01244 
01245               if ( rc ) {
01246                      ppb->pErr = PP_accountLocked;
01247                      send_ldap_error( op, rs, LDAP_INVALID_CREDENTIALS, NULL );
01248                      return rs->sr_err;
01249               }
01250 
01251        }
01252 
01253        return SLAP_CB_CONTINUE;
01254 }
01255 
01256 /* Reset the restricted info for the next session on this connection */
01257 static int
01258 ppolicy_connection_destroy( BackendDB *bd, Connection *conn )
01259 {
01260        if ( !BER_BVISEMPTY( &pwcons[conn->c_conn_idx].dn )) {
01261               ch_free( pwcons[conn->c_conn_idx].dn.bv_val );
01262               BER_BVZERO( &pwcons[conn->c_conn_idx].dn );
01263        }
01264        return SLAP_CB_CONTINUE;
01265 }
01266 
01267 /* Check if this connection is restricted */
01268 static int
01269 ppolicy_restrict(
01270        Operation *op,
01271        SlapReply *rs )
01272 {
01273        slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
01274        int send_ctrl = 0;
01275 
01276        /* Did we receive a password policy request control? */
01277        if ( op->o_ctrlflag[ppolicy_cid] ) {
01278               send_ctrl = 1;
01279        }
01280 
01281        if ( op->o_conn && !BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn )) {
01282               LDAPControl **oldctrls;
01283               /* if the current authcDN doesn't match the one we recorded,
01284                * then an intervening Bind has succeeded and the restriction
01285                * no longer applies. (ITS#4516)
01286                */
01287               if ( !dn_match( &op->o_conn->c_ndn,
01288                             &pwcons[op->o_conn->c_conn_idx].dn )) {
01289                      ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val );
01290                      BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn );
01291                      return SLAP_CB_CONTINUE;
01292               }
01293 
01294               Debug( LDAP_DEBUG_TRACE,
01295                      "connection restricted to password changing only\n", 0, 0, 0);
01296               if ( send_ctrl ) {
01297                      LDAPControl *ctrl = NULL;
01298                      ctrl = create_passcontrol( op, -1, -1, PP_changeAfterReset );
01299                      oldctrls = add_passcontrol( op, rs, ctrl );
01300               }
01301               op->o_bd->bd_info = (BackendInfo *)on->on_info;
01302               send_ldap_error( op, rs, LDAP_INSUFFICIENT_ACCESS, 
01303                      "Operations are restricted to bind/unbind/abandon/StartTLS/modify password" );
01304               if ( send_ctrl ) {
01305                      ctrls_cleanup( op, rs, oldctrls );
01306               }
01307               return rs->sr_err;
01308        }
01309 
01310        return SLAP_CB_CONTINUE;
01311 }
01312 
01313 static int
01314 ppolicy_compare_response(
01315        Operation *op,
01316        SlapReply *rs )
01317 {
01318        /* map compare responses to bind responses */
01319        if ( rs->sr_err == LDAP_COMPARE_TRUE )
01320               rs->sr_err = LDAP_SUCCESS;
01321        else if ( rs->sr_err == LDAP_COMPARE_FALSE )
01322               rs->sr_err = LDAP_INVALID_CREDENTIALS;
01323 
01324        ppolicy_bind_response( op, rs );
01325 
01326        /* map back to compare */
01327        if ( rs->sr_err == LDAP_SUCCESS )
01328               rs->sr_err = LDAP_COMPARE_TRUE;
01329        else if ( rs->sr_err == LDAP_INVALID_CREDENTIALS )
01330               rs->sr_err = LDAP_COMPARE_FALSE;
01331 
01332        return SLAP_CB_CONTINUE;
01333 }
01334 
01335 static int
01336 ppolicy_compare(
01337        Operation *op,
01338        SlapReply *rs )
01339 {
01340        slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
01341 
01342        if ( ppolicy_restrict( op, rs ) != SLAP_CB_CONTINUE )
01343               return rs->sr_err;
01344 
01345        /* Did we receive a password policy request control?
01346         * Are we testing the userPassword?
01347         */
01348        if ( op->o_ctrlflag[ppolicy_cid] && 
01349               op->orc_ava->aa_desc == slap_schema.si_ad_userPassword ) {
01350               Entry *e;
01351               int rc;
01352               ppbind *ppb;
01353               slap_callback *cb;
01354 
01355               op->o_bd->bd_info = (BackendInfo *)on->on_info;
01356               rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
01357 
01358               if ( rc != LDAP_SUCCESS ) {
01359                      return SLAP_CB_CONTINUE;
01360               }
01361 
01362               cb = op->o_tmpcalloc( sizeof(ppbind)+sizeof(slap_callback),
01363                      1, op->o_tmpmemctx );
01364               ppb = (ppbind *)(cb+1);
01365               ppb->on = on;
01366               ppb->pErr = PP_noError;
01367               ppb->send_ctrl = 1;
01368               /* failures here don't lockout the connection */
01369               ppb->set_restrict = 0;
01370 
01371               /* Setup a callback so we can munge the result */
01372 
01373               cb->sc_response = ppolicy_compare_response;
01374               cb->sc_next = op->o_callback->sc_next;
01375               cb->sc_private = ppb;
01376               op->o_callback->sc_next = cb;
01377 
01378               op->o_bd->bd_info = (BackendInfo *)on;
01379               ppolicy_get( op, e, &ppb->pp );
01380 
01381               rc = account_locked( op, e, &ppb->pp, &ppb->mod );
01382 
01383               op->o_bd->bd_info = (BackendInfo *)on->on_info;
01384               be_entry_release_r( op, e );
01385 
01386               if ( rc ) {
01387                      ppb->pErr = PP_accountLocked;
01388                      send_ldap_error( op, rs, LDAP_COMPARE_FALSE, NULL );
01389                      return rs->sr_err;
01390               }
01391        }
01392        return SLAP_CB_CONTINUE;
01393 }
01394 
01395 static int
01396 ppolicy_add(
01397        Operation *op,
01398        SlapReply *rs )
01399 {
01400        slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
01401        pp_info *pi = on->on_bi.bi_private;
01402        PassPolicy pp;
01403        Attribute *pa;
01404        const char *txt;
01405 
01406        if ( ppolicy_restrict( op, rs ) != SLAP_CB_CONTINUE )
01407               return rs->sr_err;
01408 
01409        /* If this is a replica, assume the master checked everything */
01410        if ( be_shadow_update( op ))
01411               return SLAP_CB_CONTINUE;
01412 
01413        /* Check for password in entry */
01414        if ((pa = attr_find( op->oq_add.rs_e->e_attrs,
01415               slap_schema.si_ad_userPassword )))
01416        {
01417               assert( pa->a_vals != NULL );
01418               assert( !BER_BVISNULL( &pa->a_vals[ 0 ] ) );
01419 
01420               if ( !BER_BVISNULL( &pa->a_vals[ 1 ] ) ) {
01421                      send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION, "Password policy only allows one password value" );
01422                      return rs->sr_err;
01423               }
01424 
01425               /*
01426                * new entry contains a password - if we're not the root user
01427                * then we need to check that the password fits in with the
01428                * security policy for the new entry.
01429                */
01430               ppolicy_get( op, op->ora_e, &pp );
01431               if (pp.pwdCheckQuality > 0 && !be_isroot( op )) {
01432                      struct berval *bv = &(pa->a_vals[0]);
01433                      int rc, send_ctrl = 0;
01434                      LDAPPasswordPolicyError pErr = PP_noError;
01435                      char *txt;
01436 
01437                      /* Did we receive a password policy request control? */
01438                      if ( op->o_ctrlflag[ppolicy_cid] ) {
01439                             send_ctrl = 1;
01440                      }
01441                      rc = check_password_quality( bv, &pp, &pErr, op->ora_e, &txt );
01442                      if (rc != LDAP_SUCCESS) {
01443                             LDAPControl **oldctrls = NULL;
01444                             op->o_bd->bd_info = (BackendInfo *)on->on_info;
01445                             if ( send_ctrl ) {
01446                                    LDAPControl *ctrl = NULL;
01447                                    ctrl = create_passcontrol( op, -1, -1, pErr );
01448                                    oldctrls = add_passcontrol( op, rs, ctrl );
01449                             }
01450                             send_ldap_error( op, rs, rc, txt ? txt : "Password fails quality checking policy" );
01451                             if ( txt ) {
01452                                    free( txt );
01453                             }
01454                             if ( send_ctrl ) {
01455                                    ctrls_cleanup( op, rs, oldctrls );
01456                             }
01457                             return rs->sr_err;
01458                      }
01459               }
01460                      /*
01461                       * A controversial bit. We hash cleartext
01462                       * passwords provided via add and modify operations
01463                       * You're not really supposed to do this, since
01464                       * the X.500 model says "store attributes" as they
01465                       * get provided. By default, this is what we do
01466                       *
01467                       * But if the hash_passwords flag is set, we hash
01468                       * any cleartext password attribute values via the
01469                       * default password hashing scheme.
01470                       */
01471               if ((pi->hash_passwords) &&
01472                      (password_scheme( &(pa->a_vals[0]), NULL ) != LDAP_SUCCESS)) {
01473                      struct berval hpw;
01474 
01475                      slap_passwd_hash( &(pa->a_vals[0]), &hpw, &txt );
01476                      if (hpw.bv_val == NULL) {
01477                             /*
01478                              * hashing didn't work. Emit an error.
01479                              */
01480                             rs->sr_err = LDAP_OTHER;
01481                             rs->sr_text = txt;
01482                             send_ldap_error( op, rs, LDAP_OTHER, "Password hashing failed" );
01483                             return rs->sr_err;
01484                      }
01485 
01486                      memset( pa->a_vals[0].bv_val, 0, pa->a_vals[0].bv_len);
01487                      ber_memfree( pa->a_vals[0].bv_val );
01488                      pa->a_vals[0].bv_val = hpw.bv_val;
01489                      pa->a_vals[0].bv_len = hpw.bv_len;
01490               }
01491 
01492               /* If password aging is in effect, set the pwdChangedTime */
01493               if ( pp.pwdMaxAge || pp.pwdMinAge ) {
01494                      struct berval timestamp;
01495                      char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
01496                      time_t now = slap_get_time();
01497 
01498                      timestamp.bv_val = timebuf;
01499                      timestamp.bv_len = sizeof(timebuf);
01500                      slap_timestamp( &now, &timestamp );
01501 
01502                      attr_merge_one( op->ora_e, ad_pwdChangedTime, &timestamp, &timestamp );
01503               }
01504        }
01505        return SLAP_CB_CONTINUE;
01506 }
01507 
01508 static int
01509 ppolicy_mod_cb( Operation *op, SlapReply *rs )
01510 {
01511        slap_callback *sc = op->o_callback;
01512        op->o_callback = sc->sc_next;
01513        if ( rs->sr_err == LDAP_SUCCESS ) {
01514               ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val );
01515               BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn );
01516        }
01517        op->o_tmpfree( sc, op->o_tmpmemctx );
01518        return SLAP_CB_CONTINUE;
01519 }
01520 
01521 static int
01522 ppolicy_modify( Operation *op, SlapReply *rs )
01523 {
01524        slap_overinst        *on = (slap_overinst *)op->o_bd->bd_info;
01525        pp_info                     *pi = on->on_bi.bi_private;
01526        int                  i, rc, mod_pw_only, pwmod, pwmop = -1, deladd,
01527                             hsize = 0;
01528        PassPolicy           pp;
01529        Modifications        *mods = NULL, *modtail = NULL,
01530                             *ml, *delmod, *addmod;
01531        Attribute            *pa, *ha, at;
01532        const char           *txt;
01533        pw_hist                     *tl = NULL, *p;
01534        int                  zapReset, send_ctrl = 0, free_txt = 0;
01535        Entry                *e;
01536        struct berval        newpw = BER_BVNULL, oldpw = BER_BVNULL,
01537                             *bv, cr[2];
01538        LDAPPasswordPolicyError pErr = PP_noError;
01539        LDAPControl          *ctrl = NULL;
01540        LDAPControl          **oldctrls = NULL;
01541        int                  is_pwdexop = 0;
01542 
01543        op->o_bd->bd_info = (BackendInfo *)on->on_info;
01544        rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
01545        op->o_bd->bd_info = (BackendInfo *)on;
01546 
01547        if ( rc != LDAP_SUCCESS ) return SLAP_CB_CONTINUE;
01548 
01549        /* If this is a replica, we may need to tweak some of the
01550         * master's modifications. Otherwise, just pass it through.
01551         */
01552        if ( be_shadow_update( op )) {
01553               Modifications **prev;
01554               int got_del_grace = 0, got_del_lock = 0, got_pw = 0, got_del_fail = 0;
01555               Attribute *a_grace, *a_lock, *a_fail;
01556 
01557               a_grace = attr_find( e->e_attrs, ad_pwdGraceUseTime );
01558               a_lock = attr_find( e->e_attrs, ad_pwdAccountLockedTime );
01559               a_fail = attr_find( e->e_attrs, ad_pwdFailureTime );
01560 
01561               for( prev = &op->orm_modlist, ml = *prev; ml; ml = *prev ) {
01562 
01563                      if ( ml->sml_desc == slap_schema.si_ad_userPassword )
01564                             got_pw = 1;
01565 
01566                      /* If we're deleting an attr that didn't exist,
01567                       * drop this delete op
01568                       */
01569                      if ( ml->sml_op == LDAP_MOD_DELETE ) {
01570                             int drop = 0;
01571 
01572                             if ( ml->sml_desc == ad_pwdGraceUseTime ) {
01573                                    got_del_grace = 1;
01574                                    if ( !a_grace )
01575                                           drop = 1;
01576                             } else
01577                             if ( ml->sml_desc == ad_pwdAccountLockedTime ) {
01578                                    got_del_lock = 1;
01579                                    if ( !a_lock )
01580                                           drop = 1;
01581                             } else
01582                             if ( ml->sml_desc == ad_pwdFailureTime ) {
01583                                    got_del_fail = 1;
01584                                    if ( !a_fail )
01585                                           drop = 1;
01586                             }
01587                             if ( drop ) {
01588                                    *prev = ml->sml_next;
01589                                    ml->sml_next = NULL;
01590                                    slap_mods_free( ml, 1 );
01591                                    continue;
01592                             }
01593                      }
01594                      prev = &ml->sml_next;
01595               }
01596 
01597               /* If we're resetting the password, make sure grace, accountlock,
01598                * and failure also get removed.
01599                */
01600               if ( got_pw ) {
01601                      if ( a_grace && !got_del_grace ) {
01602                             ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
01603                             ml->sml_op = LDAP_MOD_DELETE;
01604                             ml->sml_flags = SLAP_MOD_INTERNAL;
01605                             ml->sml_type.bv_val = NULL;
01606                             ml->sml_desc = ad_pwdGraceUseTime;
01607                             ml->sml_numvals = 0;
01608                             ml->sml_values = NULL;
01609                             ml->sml_nvalues = NULL;
01610                             ml->sml_next = NULL;
01611                             *prev = ml;
01612                             prev = &ml->sml_next;
01613                      }
01614                      if ( a_lock && !got_del_lock ) {
01615                             ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
01616                             ml->sml_op = LDAP_MOD_DELETE;
01617                             ml->sml_flags = SLAP_MOD_INTERNAL;
01618                             ml->sml_type.bv_val = NULL;
01619                             ml->sml_desc = ad_pwdAccountLockedTime;
01620                             ml->sml_numvals = 0;
01621                             ml->sml_values = NULL;
01622                             ml->sml_nvalues = NULL;
01623                             ml->sml_next = NULL;
01624                             *prev = ml;
01625                      }
01626                      if ( a_fail && !got_del_fail ) {
01627                             ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
01628                             ml->sml_op = LDAP_MOD_DELETE;
01629                             ml->sml_flags = SLAP_MOD_INTERNAL;
01630                             ml->sml_type.bv_val = NULL;
01631                             ml->sml_desc = ad_pwdFailureTime;
01632                             ml->sml_numvals = 0;
01633                             ml->sml_values = NULL;
01634                             ml->sml_nvalues = NULL;
01635                             ml->sml_next = NULL;
01636                             *prev = ml;
01637                      }
01638               }
01639               op->o_bd->bd_info = (BackendInfo *)on->on_info;
01640               be_entry_release_r( op, e );
01641               return SLAP_CB_CONTINUE;
01642        }
01643 
01644        /* Did we receive a password policy request control? */
01645        if ( op->o_ctrlflag[ppolicy_cid] ) {
01646               send_ctrl = 1;
01647        }
01648 
01649        /* See if this is a pwdModify exop. If so, we can
01650         * access the plaintext passwords from that request.
01651         */
01652        {
01653               slap_callback *sc;
01654 
01655               for ( sc = op->o_callback; sc; sc=sc->sc_next ) {
01656                      if ( sc->sc_response == slap_null_cb &&
01657                             sc->sc_private ) {
01658                             req_pwdexop_s *qpw = sc->sc_private;
01659                             newpw = qpw->rs_new;
01660                             oldpw = qpw->rs_old;
01661                             is_pwdexop = 1;
01662                             break;
01663                      }
01664               }
01665        }
01666 
01667        ppolicy_get( op, e, &pp );
01668 
01669        for ( ml = op->orm_modlist,
01670                      pwmod = 0, mod_pw_only = 1,
01671                      deladd = 0, delmod = NULL,
01672                      addmod = NULL,
01673                      zapReset = 1;
01674               ml != NULL; modtail = ml, ml = ml->sml_next )
01675        {
01676               if ( ml->sml_desc == pp.ad ) {
01677                      pwmod = 1;
01678                      pwmop = ml->sml_op;
01679                      if ((deladd == 0) && (ml->sml_op == LDAP_MOD_DELETE) &&
01680                             (ml->sml_values) && !BER_BVISNULL( &ml->sml_values[0] ))
01681                      {
01682                             deladd = 1;
01683                             delmod = ml;
01684                      }
01685 
01686                      if ((ml->sml_op == LDAP_MOD_ADD) ||
01687                             (ml->sml_op == LDAP_MOD_REPLACE))
01688                      {
01689                             if ( ml->sml_values && !BER_BVISNULL( &ml->sml_values[0] )) {
01690                                    if ( deladd == 1 )
01691                                           deladd = 2;
01692 
01693                                    /* FIXME: there's no easy way to ensure
01694                                     * that add does not cause multiple
01695                                     * userPassword values; one way (that 
01696                                     * would be consistent with the single
01697                                     * password constraint) would be to turn
01698                                     * add into replace); another would be
01699                                     * to disallow add.
01700                                     *
01701                                     * Let's check at least that a single value
01702                                     * is being added
01703                                     */
01704                                    if ( addmod || !BER_BVISNULL( &ml->sml_values[ 1 ] ) ) {
01705                                           rs->sr_err = LDAP_CONSTRAINT_VIOLATION; 
01706                                           rs->sr_text = "Password policy only allows one password value";
01707                                           goto return_results;
01708                                    }
01709 
01710                                    addmod = ml;
01711                             } else {
01712                                    /* replace can have no values, add cannot */
01713                                    assert( ml->sml_op == LDAP_MOD_REPLACE );
01714                             }
01715                      }
01716 
01717               } else if ( !(ml->sml_flags & SLAP_MOD_INTERNAL) && !is_at_operational( ml->sml_desc->ad_type ) ) {
01718                      mod_pw_only = 0;
01719                      /* modifying something other than password */
01720               }
01721 
01722               /*
01723                * If there is a request to explicitly add a pwdReset
01724                * attribute, then we suppress the normal behaviour on
01725                * password change, which is to remove the pwdReset
01726                * attribute.
01727                *
01728                * This enables an administrator to assign a new password
01729                * and place a "must reset" flag on the entry, which will
01730                * stay until the user explicitly changes his/her password.
01731                */
01732               if (ml->sml_desc == ad_pwdReset ) {
01733                      if ((ml->sml_op == LDAP_MOD_ADD) ||
01734                             (ml->sml_op == LDAP_MOD_REPLACE))
01735                             zapReset = 0;
01736               }
01737        }
01738        
01739        if (!BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn ) && !mod_pw_only ) {
01740               if ( dn_match( &op->o_conn->c_ndn,
01741                             &pwcons[op->o_conn->c_conn_idx].dn )) {
01742                      Debug( LDAP_DEBUG_TRACE,
01743                             "connection restricted to password changing only\n", 0, 0, 0 );
01744                      rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 
01745                      rs->sr_text = "Operations are restricted to bind/unbind/abandon/StartTLS/modify password";
01746                      pErr = PP_changeAfterReset;
01747                      goto return_results;
01748               } else {
01749                      ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val );
01750                      BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn );
01751               }
01752        }
01753 
01754        /*
01755         * if we have a "safe password modify policy", then we need to check if we're doing
01756         * a delete (with the old password), followed by an add (with the new password).
01757         *
01758         * If we got just a delete with nothing else, just let it go. We also skip all the checks if
01759         * the root user is bound. Root can do anything, including avoid the policies.
01760         */
01761 
01762        if (!pwmod) goto do_modify;
01763 
01764        /*
01765         * Build the password history list in ascending time order
01766         * We need this, even if the user is root, in order to maintain
01767         * the pwdHistory operational attributes properly.
01768         */
01769        if (addmod && pp.pwdInHistory > 0 && (ha = attr_find( e->e_attrs, ad_pwdHistory ))) {
01770               struct berval oldpw;
01771               time_t oldtime;
01772 
01773               for(i=0; ha->a_nvals[i].bv_val; i++) {
01774                      rc = parse_pwdhistory( &(ha->a_nvals[i]), NULL,
01775                             &oldtime, &oldpw );
01776 
01777                      if (rc != LDAP_SUCCESS) continue; /* invalid history entry */
01778 
01779                      if (oldpw.bv_val) {
01780                             add_to_pwd_history( &tl, oldtime, &oldpw,
01781                                    &(ha->a_nvals[i]) );
01782                             oldpw.bv_val = NULL;
01783                             oldpw.bv_len = 0;
01784                      }
01785               }
01786               for(p=tl; p; p=p->next, hsize++); /* count history size */
01787        }
01788 
01789        if (be_isroot( op )) goto do_modify;
01790 
01791        /* NOTE: according to draft-behera-ldap-password-policy
01792         * pwdAllowUserChange == FALSE must only prevent pwd changes
01793         * by the user the pwd belongs to (ITS#7021) */
01794        if (!pp.pwdAllowUserChange && dn_match(&op->o_req_ndn, &op->o_ndn)) {
01795               rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
01796               rs->sr_text = "User alteration of password is not allowed";
01797               pErr = PP_passwordModNotAllowed;
01798               goto return_results;
01799        }
01800 
01801        /* Just deleting? */
01802        if (!addmod) {
01803               /* skip everything else */
01804               pwmod = 0;
01805               goto do_modify;
01806        }
01807 
01808        /* This is a pwdModify exop that provided the old pw.
01809         * We need to create a Delete mod for this old pw and 
01810         * let the matching value get found later
01811         */
01812        if (pp.pwdSafeModify && oldpw.bv_val ) {
01813               ml = (Modifications *)ch_calloc( sizeof( Modifications ), 1 );
01814               ml->sml_op = LDAP_MOD_DELETE;
01815               ml->sml_flags = SLAP_MOD_INTERNAL;
01816               ml->sml_desc = pp.ad;
01817               ml->sml_type = pp.ad->ad_cname;
01818               ml->sml_numvals = 1;
01819               ml->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
01820               ber_dupbv( &ml->sml_values[0], &oldpw );
01821               BER_BVZERO( &ml->sml_values[1] );
01822               ml->sml_next = op->orm_modlist;
01823               op->orm_modlist = ml;
01824               delmod = ml;
01825               deladd = 2;
01826        }
01827 
01828        if (pp.pwdSafeModify && deladd != 2) {
01829               Debug( LDAP_DEBUG_TRACE,
01830                      "change password must use DELETE followed by ADD/REPLACE\n",
01831                      0, 0, 0 );
01832               rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
01833               rs->sr_text = "Must supply old password to be changed as well as new one";
01834               pErr = PP_mustSupplyOldPassword;
01835               goto return_results;
01836        }
01837 
01838        /* Check age, but only if pwdReset is not TRUE */
01839        pa = attr_find( e->e_attrs, ad_pwdReset );
01840        if ((!pa || !bvmatch( &pa->a_nvals[0], &slap_true_bv )) &&
01841               pp.pwdMinAge > 0) {
01842               time_t pwtime = (time_t)-1, now;
01843               int age;
01844 
01845               if ((pa = attr_find( e->e_attrs, ad_pwdChangedTime )) != NULL)
01846                      pwtime = parse_time( pa->a_nvals[0].bv_val );
01847               now = slap_get_time();
01848               age = (int)(now - pwtime);
01849               if ((pwtime != (time_t)-1) && (age < pp.pwdMinAge)) {
01850                      rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
01851                      rs->sr_text = "Password is too young to change";
01852                      pErr = PP_passwordTooYoung;
01853                      goto return_results;
01854               }
01855        }
01856 
01857        /* pa is used in password history check below, be sure it's set */
01858        if ((pa = attr_find( e->e_attrs, pp.ad )) != NULL && delmod) {
01859               /*
01860                * we have a password to check
01861                */
01862               bv = oldpw.bv_val ? &oldpw : delmod->sml_values;
01863               /* FIXME: no access checking? */
01864               rc = slap_passwd_check( op, NULL, pa, bv, &txt );
01865               if (rc != LDAP_SUCCESS) {
01866                      Debug( LDAP_DEBUG_TRACE,
01867                             "old password check failed: %s\n", txt, 0, 0 );
01868                      
01869                      rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
01870                      rs->sr_text = "Must supply correct old password to change to new one";
01871                      pErr = PP_mustSupplyOldPassword;
01872                      goto return_results;
01873 
01874               } else {
01875                      int i;
01876                      
01877                      /*
01878                       * replace the delete value with the (possibly hashed)
01879                       * value which is currently in the password.
01880                       */
01881                      for ( i = 0; !BER_BVISNULL( &delmod->sml_values[i] ); i++ ) {
01882                             free( delmod->sml_values[i].bv_val );
01883                             BER_BVZERO( &delmod->sml_values[i] );
01884                      }
01885                      free( delmod->sml_values );
01886                      delmod->sml_values = ch_calloc( sizeof(struct berval), 2 );
01887                      BER_BVZERO( &delmod->sml_values[1] );
01888                      ber_dupbv( &(delmod->sml_values[0]),  &(pa->a_nvals[0]) );
01889               }
01890        }
01891 
01892        bv = newpw.bv_val ? &newpw : &addmod->sml_values[0];
01893        if (pp.pwdCheckQuality > 0) {
01894 
01895               rc = check_password_quality( bv, &pp, &pErr, e, (char **)&txt );
01896               if (rc != LDAP_SUCCESS) {
01897                      rs->sr_err = rc;
01898                      if ( txt ) {
01899                             rs->sr_text = txt;
01900                             free_txt = 1;
01901                      } else {
01902                             rs->sr_text = "Password fails quality checking policy";
01903                      }
01904                      goto return_results;
01905               }
01906        }
01907 
01908        /* If pwdInHistory is zero, passwords may be reused */
01909        if (pa && pp.pwdInHistory > 0) {
01910               /*
01911                * Last check - the password history.
01912                */
01913               /* FIXME: no access checking? */
01914               if (slap_passwd_check( op, NULL, pa, bv, &txt ) == LDAP_SUCCESS) {
01915                      /*
01916                       * This is bad - it means that the user is attempting
01917                       * to set the password to the same as the old one.
01918                       */
01919                      rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
01920                      rs->sr_text = "Password is not being changed from existing value";
01921                      pErr = PP_passwordInHistory;
01922                      goto return_results;
01923               }
01924        
01925               /*
01926                * Iterate through the password history, and fail on any
01927                * password matches.
01928                */
01929               at = *pa;
01930               at.a_vals = cr;
01931               cr[1].bv_val = NULL;
01932               for(p=tl; p; p=p->next) {
01933                      cr[0] = p->pw;
01934                      /* FIXME: no access checking? */
01935                      rc = slap_passwd_check( op, NULL, &at, bv, &txt );
01936                      
01937                      if (rc != LDAP_SUCCESS) continue;
01938                      
01939                      rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
01940                      rs->sr_text = "Password is in history of old passwords";
01941                      pErr = PP_passwordInHistory;
01942                      goto return_results;
01943               }
01944        }
01945 
01946 do_modify:
01947        if (pwmod) {
01948               struct berval timestamp;
01949               char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
01950               time_t now = slap_get_time();
01951 
01952               /* If the conn is restricted, set a callback to clear it
01953                * if the pwmod succeeds
01954                */
01955               if (!BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn )) {
01956                      slap_callback *sc = op->o_tmpcalloc( 1, sizeof( slap_callback ),
01957                             op->o_tmpmemctx );
01958                      sc->sc_next = op->o_callback;
01959                      /* Must use sc_response to insure we reset on success, before
01960                       * the client sees the response. Must use sc_cleanup to insure
01961                       * that it gets cleaned up if sc_response is not called.
01962                       */
01963                      sc->sc_response = ppolicy_mod_cb;
01964                      sc->sc_cleanup = ppolicy_mod_cb;
01965                      op->o_callback = sc;
01966               }
01967 
01968               /*
01969                * keep the necessary pwd.. operational attributes
01970                * up to date.
01971                */
01972 
01973               timestamp.bv_val = timebuf;
01974               timestamp.bv_len = sizeof(timebuf);
01975               slap_timestamp( &now, &timestamp );
01976 
01977               mods = NULL;
01978               if (pwmop != LDAP_MOD_DELETE) {
01979                      mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
01980                      mods->sml_op = LDAP_MOD_REPLACE;
01981                      mods->sml_numvals = 1;
01982                      mods->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
01983                      ber_dupbv( &mods->sml_values[0], &timestamp );
01984                      BER_BVZERO( &mods->sml_values[1] );
01985                      assert( !BER_BVISNULL( &mods->sml_values[0] ) );
01986               } else if (attr_find(e->e_attrs, ad_pwdChangedTime )) {
01987                      mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
01988                      mods->sml_op = LDAP_MOD_DELETE;
01989               }
01990               if (mods) {
01991                      mods->sml_desc = ad_pwdChangedTime;
01992                      mods->sml_flags = SLAP_MOD_INTERNAL;
01993                      mods->sml_next = NULL;
01994                      modtail->sml_next = mods;
01995                      modtail = mods;
01996               }
01997 
01998               if (attr_find(e->e_attrs, ad_pwdGraceUseTime )) {
01999                      mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
02000                      mods->sml_op = LDAP_MOD_DELETE;
02001                      mods->sml_desc = ad_pwdGraceUseTime;
02002                      mods->sml_flags = SLAP_MOD_INTERNAL;
02003                      mods->sml_next = NULL;
02004                      modtail->sml_next = mods;
02005                      modtail = mods;
02006               }
02007 
02008               if (attr_find(e->e_attrs, ad_pwdAccountLockedTime )) {
02009                      mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
02010                      mods->sml_op = LDAP_MOD_DELETE;
02011                      mods->sml_desc = ad_pwdAccountLockedTime;
02012                      mods->sml_flags = SLAP_MOD_INTERNAL;
02013                      mods->sml_next = NULL;
02014                      modtail->sml_next = mods;
02015                      modtail = mods;
02016               }
02017 
02018               if (attr_find(e->e_attrs, ad_pwdFailureTime )) {
02019                      mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
02020                      mods->sml_op = LDAP_MOD_DELETE;
02021                      mods->sml_desc = ad_pwdFailureTime;
02022                      mods->sml_flags = SLAP_MOD_INTERNAL;
02023                      mods->sml_next = NULL;
02024                      modtail->sml_next = mods;
02025                      modtail = mods;
02026               }
02027 
02028               /* Delete the pwdReset attribute, since it's being reset */
02029               if ((zapReset) && (attr_find(e->e_attrs, ad_pwdReset ))) {
02030                      mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
02031                      mods->sml_op = LDAP_MOD_DELETE;
02032                      mods->sml_desc = ad_pwdReset;
02033                      mods->sml_flags = SLAP_MOD_INTERNAL;
02034                      mods->sml_next = NULL;
02035                      modtail->sml_next = mods;
02036                      modtail = mods;
02037               }
02038 
02039               if (pp.pwdInHistory > 0) {
02040                      if (hsize >= pp.pwdInHistory) {
02041                             /*
02042                              * We use the >= operator, since we are going to add
02043                              * the existing password attribute value into the
02044                              * history - thus the cardinality of history values is
02045                              * about to rise by one.
02046                              *
02047                              * If this would push it over the limit of history
02048                              * values (remembering - the password policy could have
02049                              * changed since the password was last altered), we must
02050                              * delete at least 1 value from the pwdHistory list.
02051                              *
02052                              * In fact, we delete '(#pwdHistory attrs - max pwd
02053                              * history length) + 1' values, starting with the oldest.
02054                              * This is easily evaluated, since the linked list is
02055                              * created in ascending time order.
02056                              */
02057                             mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
02058                             mods->sml_op = LDAP_MOD_DELETE;
02059                             mods->sml_flags = SLAP_MOD_INTERNAL;
02060                             mods->sml_desc = ad_pwdHistory;
02061                             mods->sml_numvals = hsize - pp.pwdInHistory + 1;
02062                             mods->sml_values = ch_calloc( sizeof( struct berval ),
02063                                    hsize - pp.pwdInHistory + 2 );
02064                             BER_BVZERO( &mods->sml_values[ hsize - pp.pwdInHistory + 1 ] );
02065                             for(i=0,p=tl; i < (hsize - pp.pwdInHistory + 1); i++, p=p->next) {
02066                                    BER_BVZERO( &mods->sml_values[i] );
02067                                    ber_dupbv( &(mods->sml_values[i]), &p->bv );
02068                             }
02069                             mods->sml_next = NULL;
02070                             modtail->sml_next = mods;
02071                             modtail = mods;
02072                      }
02073                      free_pwd_history_list( &tl );
02074 
02075                      /*
02076                       * Now add the existing password into the history list.
02077                       * This will be executed even if the operation is to delete
02078                       * the password entirely.
02079                       *
02080                       * This isn't in the spec explicitly, but it seems to make
02081                       * sense that the password history list is the list of all
02082                       * previous passwords - even if they were deleted. Thus, if
02083                       * someone tries to add a historical password at some future
02084                       * point, it will fail.
02085                       */
02086                      if ((pa = attr_find( e->e_attrs, pp.ad )) != NULL) {
02087                             mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
02088                             mods->sml_op = LDAP_MOD_ADD;
02089                             mods->sml_flags = SLAP_MOD_INTERNAL;
02090                             mods->sml_type.bv_val = NULL;
02091                             mods->sml_desc = ad_pwdHistory;
02092                             mods->sml_nvalues = NULL;
02093                             mods->sml_numvals = 1;
02094                             mods->sml_values = ch_calloc( sizeof( struct berval ), 2 );
02095                             mods->sml_values[ 1 ].bv_val = NULL;
02096                             mods->sml_values[ 1 ].bv_len = 0;
02097                             make_pwd_history_value( timebuf, &mods->sml_values[0], pa );
02098                             mods->sml_next = NULL;
02099                             modtail->sml_next = mods;
02100                             modtail = mods;
02101 
02102                      } else {
02103                             Debug( LDAP_DEBUG_TRACE,
02104                             "ppolicy_modify: password attr lookup failed\n", 0, 0, 0 );
02105                      }
02106               }
02107 
02108               /*
02109                * Controversial bit here. If the new password isn't hashed
02110                * (ie, is cleartext), we probably should hash it according
02111                * to the default hash. The reason for this is that we want
02112                * to use the policy if possible, but if we hash the password
02113                * before, then we're going to run into trouble when it
02114                * comes time to check the password.
02115                *
02116                * Now, the right thing to do is to use the extended password
02117                * modify operation, but not all software can do this,
02118                * therefore it makes sense to hash the new password, now
02119                * we know it passes the policy requirements.
02120                *
02121                * Of course, if the password is already hashed, then we
02122                * leave it alone.
02123                */
02124 
02125               if ((pi->hash_passwords) && (addmod) && !newpw.bv_val && 
02126                      (password_scheme( &(addmod->sml_values[0]), NULL ) != LDAP_SUCCESS))
02127               {
02128                      struct berval hpw, bv;
02129                      
02130                      slap_passwd_hash( &(addmod->sml_values[0]), &hpw, &txt );
02131                      if (hpw.bv_val == NULL) {
02132                                    /*
02133                                     * hashing didn't work. Emit an error.
02134                                     */
02135                             rs->sr_err = LDAP_OTHER;
02136                             rs->sr_text = txt;
02137                             goto return_results;
02138                      }
02139                      bv = addmod->sml_values[0];
02140                             /* clear and discard the clear password */
02141                      memset(bv.bv_val, 0, bv.bv_len);
02142                      ber_memfree(bv.bv_val);
02143                      addmod->sml_values[0] = hpw;
02144               }
02145        }
02146        op->o_bd->bd_info = (BackendInfo *)on->on_info;
02147        be_entry_release_r( op, e );
02148        return SLAP_CB_CONTINUE;
02149 
02150 return_results:
02151        free_pwd_history_list( &tl );
02152        op->o_bd->bd_info = (BackendInfo *)on->on_info;
02153        be_entry_release_r( op, e );
02154        if ( send_ctrl ) {
02155               ctrl = create_passcontrol( op, -1, -1, pErr );
02156               oldctrls = add_passcontrol( op, rs, ctrl );
02157        }
02158        send_ldap_result( op, rs );
02159        if ( free_txt ) {
02160               free( (char *)txt );
02161               rs->sr_text = NULL;
02162        }
02163        if ( send_ctrl ) {
02164               if ( is_pwdexop ) {
02165                      if ( rs->sr_flags & REP_CTRLS_MUSTBEFREED ) {
02166                             op->o_tmpfree( oldctrls, op->o_tmpmemctx );
02167                      }
02168                      oldctrls = NULL;
02169                      rs->sr_flags |= REP_CTRLS_MUSTBEFREED;
02170 
02171               } else {
02172                      ctrls_cleanup( op, rs, oldctrls );
02173               }
02174        }
02175        return rs->sr_err;
02176 }
02177 
02178 static int
02179 ppolicy_parseCtrl(
02180        Operation *op,
02181        SlapReply *rs,
02182        LDAPControl *ctrl )
02183 {
02184        if ( !BER_BVISNULL( &ctrl->ldctl_value ) ) {
02185               rs->sr_text = "passwordPolicyRequest control value not absent";
02186               return LDAP_PROTOCOL_ERROR;
02187        }
02188        op->o_ctrlflag[ppolicy_cid] = ctrl->ldctl_iscritical
02189               ? SLAP_CONTROL_CRITICAL
02190               : SLAP_CONTROL_NONCRITICAL;
02191 
02192        return LDAP_SUCCESS;
02193 }
02194 
02195 static int
02196 attrPretty(
02197        Syntax *syntax,
02198        struct berval *val,
02199        struct berval *out,
02200        void *ctx )
02201 {
02202        AttributeDescription *ad = NULL;
02203        const char *err;
02204        int code;
02205 
02206        code = slap_bv2ad( val, &ad, &err );
02207        if ( !code ) {
02208               ber_dupbv_x( out, &ad->ad_type->sat_cname, ctx );
02209        }
02210        return code;
02211 }
02212 
02213 static int
02214 attrNormalize(
02215        slap_mask_t use,
02216        Syntax *syntax,
02217        MatchingRule *mr,
02218        struct berval *val,
02219        struct berval *out,
02220        void *ctx )
02221 {
02222        AttributeDescription *ad = NULL;
02223        const char *err;
02224        int code;
02225 
02226        code = slap_bv2ad( val, &ad, &err );
02227        if ( !code ) {
02228               ber_str2bv_x( ad->ad_type->sat_oid, 0, 1, out, ctx );
02229        }
02230        return code;
02231 }
02232 
02233 static int
02234 ppolicy_db_init(
02235        BackendDB *be,
02236        ConfigReply *cr
02237 )
02238 {
02239        slap_overinst *on = (slap_overinst *) be->bd_info;
02240 
02241        if ( SLAP_ISGLOBALOVERLAY( be ) ) {
02242               /* do not allow slapo-ppolicy to be global by now (ITS#5858) */
02243               if ( cr ){
02244                      snprintf( cr->msg, sizeof(cr->msg), 
02245                             "slapo-ppolicy cannot be global" );
02246                      Debug( LDAP_DEBUG_ANY, "%s\n", cr->msg, 0, 0 );
02247               }
02248               return 1;
02249        }
02250 
02251        /* Has User Schema been initialized yet? */
02252        if ( !pwd_UsSchema[0].ad[0] ) {
02253               const char *err;
02254               int i, code;
02255 
02256               for (i=0; pwd_UsSchema[i].def; i++) {
02257                      code = slap_str2ad( pwd_UsSchema[i].def, pwd_UsSchema[i].ad, &err );
02258                      if ( code ) {
02259                             if ( cr ){
02260                                    snprintf( cr->msg, sizeof(cr->msg), 
02261                                           "User Schema load failed for attribute \"%s\". Error code %d: %s",
02262                                           pwd_UsSchema[i].def, code, err );
02263                                    Debug( LDAP_DEBUG_ANY, "%s\n", cr->msg, 0, 0 );
02264                             }
02265                             return code;
02266                      }
02267               }
02268               {
02269                      Syntax *syn;
02270                      MatchingRule *mr;
02271 
02272                      syn = ch_malloc( sizeof( Syntax ));
02273                      *syn = *ad_pwdAttribute->ad_type->sat_syntax;
02274                      syn->ssyn_pretty = attrPretty;
02275                      ad_pwdAttribute->ad_type->sat_syntax = syn;
02276 
02277                      mr = ch_malloc( sizeof( MatchingRule ));
02278                      *mr = *ad_pwdAttribute->ad_type->sat_equality;
02279                      mr->smr_normalize = attrNormalize;
02280                      ad_pwdAttribute->ad_type->sat_equality = mr;
02281               }
02282        }
02283 
02284        on->on_bi.bi_private = ch_calloc( sizeof(pp_info), 1 );
02285 
02286        if ( dtblsize && !pwcons ) {
02287               /* accommodate for c_conn_idx == -1 */
02288               pwcons = ch_calloc( sizeof(pw_conn), dtblsize + 1 );
02289               pwcons++;
02290        }
02291 
02292        return 0;
02293 }
02294 
02295 static int
02296 ppolicy_db_open(
02297        BackendDB *be,
02298        ConfigReply *cr
02299 )
02300 {
02301        ov_count++;
02302        return overlay_register_control( be, LDAP_CONTROL_PASSWORDPOLICYREQUEST );
02303 }
02304 
02305 static int
02306 ppolicy_close(
02307        BackendDB *be,
02308        ConfigReply *cr
02309 )
02310 {
02311        slap_overinst *on = (slap_overinst *) be->bd_info;
02312        pp_info *pi = on->on_bi.bi_private;
02313 
02314 #ifdef SLAP_CONFIG_DELETE
02315        overlay_unregister_control( be, LDAP_CONTROL_PASSWORDPOLICYREQUEST );
02316 #endif /* SLAP_CONFIG_DELETE */
02317 
02318        /* Perhaps backover should provide bi_destroy hooks... */
02319        ov_count--;
02320        if ( ov_count <=0 && pwcons ) {
02321               pwcons--;
02322               free( pwcons );
02323               pwcons = NULL;
02324        }
02325        free( pi->def_policy.bv_val );
02326        free( pi );
02327 
02328        return 0;
02329 }
02330 
02331 static char *extops[] = {
02332        LDAP_EXOP_MODIFY_PASSWD,
02333        NULL
02334 };
02335 
02336 static slap_overinst ppolicy;
02337 
02338 int ppolicy_initialize()
02339 {
02340        int i, code;
02341 
02342        for (i=0; pwd_OpSchema[i].def; i++) {
02343               code = register_at( pwd_OpSchema[i].def, pwd_OpSchema[i].ad, 0 );
02344               if ( code ) {
02345                      Debug( LDAP_DEBUG_ANY,
02346                             "ppolicy_initialize: register_at failed\n", 0, 0, 0 );
02347                      return code;
02348               }
02349               /* Allow Manager to set these as needed */
02350               if ( is_at_no_user_mod( (*pwd_OpSchema[i].ad)->ad_type )) {
02351                      (*pwd_OpSchema[i].ad)->ad_type->sat_flags |=
02352                             SLAP_AT_MANAGEABLE;
02353               }
02354        }
02355 
02356        code = register_supported_control( LDAP_CONTROL_PASSWORDPOLICYREQUEST,
02357               SLAP_CTRL_ADD|SLAP_CTRL_BIND|SLAP_CTRL_MODIFY|SLAP_CTRL_HIDE, extops,
02358               ppolicy_parseCtrl, &ppolicy_cid );
02359        if ( code != LDAP_SUCCESS ) {
02360               Debug( LDAP_DEBUG_ANY, "Failed to register control %d\n", code, 0, 0 );
02361               return code;
02362        }
02363 
02364        ldap_pvt_thread_mutex_init( &chk_syntax_mutex );
02365 
02366        ppolicy.on_bi.bi_type = "ppolicy";
02367        ppolicy.on_bi.bi_db_init = ppolicy_db_init;
02368        ppolicy.on_bi.bi_db_open = ppolicy_db_open;
02369        ppolicy.on_bi.bi_db_close = ppolicy_close;
02370 
02371        ppolicy.on_bi.bi_op_add = ppolicy_add;
02372        ppolicy.on_bi.bi_op_bind = ppolicy_bind;
02373        ppolicy.on_bi.bi_op_compare = ppolicy_compare;
02374        ppolicy.on_bi.bi_op_delete = ppolicy_restrict;
02375        ppolicy.on_bi.bi_op_modify = ppolicy_modify;
02376        ppolicy.on_bi.bi_op_search = ppolicy_restrict;
02377        ppolicy.on_bi.bi_connection_destroy = ppolicy_connection_destroy;
02378 
02379        ppolicy.on_bi.bi_cf_ocs = ppolicyocs;
02380        code = config_register_schema( ppolicycfg, ppolicyocs );
02381        if ( code ) return code;
02382 
02383        return overlay_register( &ppolicy );
02384 }
02385 
02386 #if SLAPD_OVER_PPOLICY == SLAPD_MOD_DYNAMIC
02387 int init_module(int argc, char *argv[]) {
02388        return ppolicy_initialize();
02389 }
02390 #endif
02391 
02392 #endif /* defined(SLAPD_OVER_PPOLICY) */