X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=servers%2Fslapd%2Foverlays%2Fppolicy.c;h=b6a833c02bbca690ab0de9bc922cdc0daf37ac56;hb=34f4c2cb97dd6c7851e30298c7e014e170f54665;hp=503a9c63fa108dc643f8ae35c52b21cd67a33c19;hpb=01fbca597471f8be24c633c1c5154573b8070449;p=openldap diff --git a/servers/slapd/overlays/ppolicy.c b/servers/slapd/overlays/ppolicy.c index 503a9c63fa..b6a833c02b 100644 --- a/servers/slapd/overlays/ppolicy.c +++ b/servers/slapd/overlays/ppolicy.c @@ -1,8 +1,8 @@ /* $OpenLDAP$ */ /* This work is part of OpenLDAP Software . * - * Copyright 2004-2005 The OpenLDAP Foundation. - * Portions Copyright 2004 Howard Chu, Symas Corporation. + * Copyright 2004-2006 The OpenLDAP Foundation. + * Portions Copyright 2004-2005 Howard Chu, Symas Corporation. * Portions Copyright 2004 Hewlett-Packard Company. * All rights reserved. * @@ -23,7 +23,7 @@ #include "portable.h" /* This file implements "Password Policy for LDAP Directories", - * based on draft behera-ldap-password-policy-08 + * based on draft behera-ldap-password-policy-09 */ #ifdef SLAPD_OVER_PPOLICY @@ -55,7 +55,7 @@ typedef struct pp_info { * used by all instances */ typedef struct pw_conn { - int restricted; /* TRUE if connection is restricted */ + struct berval dn; /* DN of restricted user */ } pw_conn; static pw_conn *pwcons; @@ -111,7 +111,7 @@ static struct schema_info { "EQUALITY generalizedTimeMatch " "ORDERING generalizedTimeOrderingMatch " "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 " - "SINGLE-VALUE USAGE directoryOperation NO-USER-MODIFICATION )", + "SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )", &ad_pwdChangedTime }, { "( 1.3.6.1.4.1.42.2.27.8.1.17 " "NAME ( 'pwdAccountLockedTime' ) " @@ -119,7 +119,12 @@ static struct schema_info { "EQUALITY generalizedTimeMatch " "ORDERING generalizedTimeOrderingMatch " "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 " - "SINGLE-VALUE USAGE directoryOperation )", + "SINGLE-VALUE " +#if 0 + /* Not until MANAGEDIT control is released */ + "NO-USER-MODIFICATION " +#endif + "USAGE directoryOperation )", &ad_pwdAccountLockedTime }, { "( 1.3.6.1.4.1.42.2.27.8.1.19 " "NAME ( 'pwdFailureTime' ) " @@ -127,21 +132,21 @@ static struct schema_info { "EQUALITY generalizedTimeMatch " "ORDERING generalizedTimeOrderingMatch " "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 " - "USAGE directoryOperation )", + "NO-USER-MODIFICATION USAGE directoryOperation )", &ad_pwdFailureTime }, { "( 1.3.6.1.4.1.42.2.27.8.1.20 " "NAME ( 'pwdHistory' ) " "DESC 'The history of users passwords' " "EQUALITY octetStringMatch " "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 " - "USAGE directoryOperation NO-USER-MODIFICATION )", + "NO-USER-MODIFICATION USAGE directoryOperation )", &ad_pwdHistory }, { "( 1.3.6.1.4.1.42.2.27.8.1.21 " "NAME ( 'pwdGraceUseTime' ) " "DESC 'The timestamps of the grace login once the password has expired' " "EQUALITY generalizedTimeMatch " "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 " - "USAGE directoryOperation NO-USER-MODIFICATION )", + "NO-USER-MODIFICATION USAGE directoryOperation )", &ad_pwdGraceUseTime }, { "( 1.3.6.1.4.1.42.2.27.8.1.22 " "NAME ( 'pwdReset' ) " @@ -155,7 +160,12 @@ static struct schema_info { "DESC 'The pwdPolicy subentry in effect for this object' " "EQUALITY distinguishedNameMatch " "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 " - "SINGLE-VALUE USAGE directoryOperation )", + "SINGLE-VALUE " +#if 0 + /* Not until MANAGEDIT control is released */ + "NO-USER-MODIFICATION " +#endif + "USAGE directoryOperation )", &ad_pwdPolicySubentry }, { NULL, NULL } }; @@ -249,11 +259,12 @@ account_locked( Operation *op, Entry *e, return 0; } -#define PPOLICY_WARNING 0xa0L -#define PPOLICY_ERROR 0xa1L +/* IMPLICIT TAGS, all context-specific */ +#define PPOLICY_WARNING 0xa0L /* constructed + 0 */ +#define PPOLICY_ERROR 0x81L /* primitive + 1 */ -#define PPOLICY_EXPIRE 0xa0L -#define PPOLICY_GRACE 0xa1L +#define PPOLICY_EXPIRE 0x80L /* primitive + 0 */ +#define PPOLICY_GRACE 0x81L /* primitive + 1 */ static LDAPControl * create_passcontrol( int exptime, int grace, LDAPPasswordPolicyError err ) @@ -315,6 +326,8 @@ ppolicy_get( Operation *op, Entry *e, PassPolicy *pp ) memset( pp, 0, sizeof(PassPolicy) ); + pp->ad = slap_schema.si_ad_userPassword; + /* Users can change their own password by default */ pp->pwdAllowUserChange = 1; @@ -343,34 +356,42 @@ ppolicy_get( Operation *op, Entry *e, PassPolicy *pp ) #if 0 /* Only worry about userPassword for now */ if ((a = attr_find( pe->e_attrs, ad_pwdAttribute ))) slap_bv2ad( &a->a_vals[0], &pp->ad, &text ); -#else - pp->ad = slap_schema.si_ad_userPassword; #endif - if ((a = attr_find( pe->e_attrs, ad_pwdMinAge ))) - pp->pwdMinAge = atoi(a->a_vals[0].bv_val ); - if ((a = attr_find( pe->e_attrs, ad_pwdMaxAge ))) - pp->pwdMaxAge = atoi(a->a_vals[0].bv_val ); - if ((a = attr_find( pe->e_attrs, ad_pwdInHistory ))) - pp->pwdInHistory = atoi(a->a_vals[0].bv_val ); - if ((a = attr_find( pe->e_attrs, ad_pwdCheckQuality ))) - pp->pwdCheckQuality = atoi(a->a_vals[0].bv_val ); - if ((a = attr_find( pe->e_attrs, ad_pwdMinLength ))) - pp->pwdMinLength = atoi(a->a_vals[0].bv_val ); - if ((a = attr_find( pe->e_attrs, ad_pwdMaxFailure ))) - pp->pwdMaxFailure = atoi(a->a_vals[0].bv_val ); - if ((a = attr_find( pe->e_attrs, ad_pwdGraceAuthNLimit ))) - pp->pwdGraceAuthNLimit = atoi(a->a_vals[0].bv_val ); - if ((a = attr_find( pe->e_attrs, ad_pwdExpireWarning ))) - pp->pwdExpireWarning = atoi(a->a_vals[0].bv_val ); - if ((a = attr_find( pe->e_attrs, ad_pwdFailureCountInterval ))) - pp->pwdFailureCountInterval = atoi(a->a_vals[0].bv_val ); - if ((a = attr_find( pe->e_attrs, ad_pwdLockoutDuration ))) - pp->pwdLockoutDuration = atoi(a->a_vals[0].bv_val ); - - if ((a = attr_find( pe->e_attrs, ad_pwdCheckModule ))) { - strncpy(pp->pwdCheckModule, a->a_vals[0].bv_val, - sizeof(pp->pwdCheckModule)); + if ( ( a = attr_find( pe->e_attrs, ad_pwdMinAge ) ) + && lutil_atoi( &pp->pwdMinAge, a->a_vals[0].bv_val ) != 0 ) + goto defaultpol; + if ( ( a = attr_find( pe->e_attrs, ad_pwdMaxAge ) ) + && lutil_atoi( &pp->pwdMaxAge, a->a_vals[0].bv_val ) != 0 ) + goto defaultpol; + if ( ( a = attr_find( pe->e_attrs, ad_pwdInHistory ) ) + && lutil_atoi( &pp->pwdInHistory, a->a_vals[0].bv_val ) != 0 ) + goto defaultpol; + if ( ( a = attr_find( pe->e_attrs, ad_pwdCheckQuality ) ) + && lutil_atoi( &pp->pwdCheckQuality, a->a_vals[0].bv_val ) != 0 ) + goto defaultpol; + if ( ( a = attr_find( pe->e_attrs, ad_pwdMinLength ) ) + && lutil_atoi( &pp->pwdMinLength, a->a_vals[0].bv_val ) != 0 ) + goto defaultpol; + if ( ( a = attr_find( pe->e_attrs, ad_pwdMaxFailure ) ) + && lutil_atoi( &pp->pwdMaxFailure, a->a_vals[0].bv_val ) != 0 ) + goto defaultpol; + if ( ( a = attr_find( pe->e_attrs, ad_pwdGraceAuthNLimit ) ) + && lutil_atoi( &pp->pwdGraceAuthNLimit, a->a_vals[0].bv_val ) != 0 ) + goto defaultpol; + if ( ( a = attr_find( pe->e_attrs, ad_pwdExpireWarning ) ) + && lutil_atoi( &pp->pwdExpireWarning, a->a_vals[0].bv_val ) != 0 ) + goto defaultpol; + if ( ( a = attr_find( pe->e_attrs, ad_pwdFailureCountInterval ) ) + && lutil_atoi( &pp->pwdFailureCountInterval, a->a_vals[0].bv_val ) != 0 ) + goto defaultpol; + if ( ( a = attr_find( pe->e_attrs, ad_pwdLockoutDuration ) ) + && lutil_atoi( &pp->pwdLockoutDuration, a->a_vals[0].bv_val ) != 0 ) + goto defaultpol; + + if ( ( a = attr_find( pe->e_attrs, ad_pwdCheckModule ) ) ) { + strncpy( pp->pwdCheckModule, a->a_vals[0].bv_val, + sizeof(pp->pwdCheckModule) ); pp->pwdCheckModule[sizeof(pp->pwdCheckModule)-1] = '\0'; } @@ -390,7 +411,7 @@ ppolicy_get( Operation *op, Entry *e, PassPolicy *pp ) return; defaultpol: - Debug( LDAP_DEBUG_ANY, + Debug( LDAP_DEBUG_TRACE, "ppolicy_get: using default policy\n", 0, 0, 0 ); return; } @@ -414,9 +435,11 @@ password_scheme( struct berval *cred, struct berval *sch ) if (cred->bv_val[e]) { int rc; rc = lutil_passwd_scheme( cred->bv_val ); - if (rc && sch) { - sch->bv_val = cred->bv_val; - sch->bv_len = e; + if (rc) { + if (sch) { + sch->bv_val = cred->bv_val; + sch->bv_len = e; + } return LDAP_SUCCESS; } } @@ -711,8 +734,10 @@ ppolicy_bind_resp( Operation *op, SlapReply *rs ) m->sml_type = ad_pwdFailureTime->ad_cname; m->sml_desc = ad_pwdFailureTime; m->sml_values = ch_calloc( sizeof(struct berval), 2 ); + m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 ); ber_dupbv( &m->sml_values[0], ×tamp ); + ber_dupbv( &m->sml_nvalues[0], ×tamp ); m->sml_next = mod; mod = m; @@ -758,7 +783,9 @@ ppolicy_bind_resp( Operation *op, SlapReply *rs ) m->sml_type = ad_pwdAccountLockedTime->ad_cname; m->sml_desc = ad_pwdAccountLockedTime; m->sml_values = ch_calloc( sizeof(struct berval), 2 ); + m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 ); ber_dupbv( &m->sml_values[0], ×tamp ); + ber_dupbv( &m->sml_nvalues[0], ×tamp ); m->sml_next = mod; mod = m; } @@ -789,7 +816,8 @@ ppolicy_bind_resp( Operation *op, SlapReply *rs ) * that we are disallowed from doing anything * other than change password. */ - pwcons[op->o_conn->c_conn_idx].restricted = 1; + ber_dupbv( &pwcons[op->o_conn->c_conn_idx].dn, + &op->o_conn->c_ndn ); ppb->pErr = PP_changeAfterReset; @@ -860,7 +888,9 @@ grace: m->sml_type = ad_pwdGraceUseTime->ad_cname; m->sml_desc = ad_pwdGraceUseTime; m->sml_values = ch_calloc( sizeof(struct berval), 2 ); + m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 ); ber_dupbv( &m->sml_values[0], ×tamp ); + ber_dupbv( &m->sml_nvalues[0], ×tamp ); m->sml_next = mod; mod = m; @@ -944,6 +974,12 @@ ppolicy_bind( Operation *op, SlapReply *rs ) { slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; + /* Reset lockout status on all Bind requests */ + if ( !BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn )) { + ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val ); + BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn ); + } + /* Root bypasses policy */ if ( !be_isroot_dn( op->o_bd, &op->o_req_ndn )) { Entry *e; @@ -996,11 +1032,14 @@ ppolicy_bind( Operation *op, SlapReply *rs ) return SLAP_CB_CONTINUE; } -/* Reset the restricted flag for the next session on this connection */ +/* Reset the restricted info for the next session on this connection */ static int ppolicy_connection_destroy( BackendDB *bd, Connection *conn ) { - pwcons[conn->c_conn_idx].restricted = 0; + if ( !BER_BVISEMPTY( &pwcons[conn->c_conn_idx].dn )) { + ch_free( pwcons[conn->c_conn_idx].dn.bv_val ); + BER_BVZERO( &pwcons[conn->c_conn_idx].dn ); + } return SLAP_CB_CONTINUE; } @@ -1018,7 +1057,18 @@ ppolicy_restrict( send_ctrl = 1; } - if ( op->o_conn && pwcons[op->o_conn->c_conn_idx].restricted ) { + if ( op->o_conn && !BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn )) { + /* if the current authcDN doesn't match the one we recorded, + * then an intervening Bind has succeeded and the restriction + * no longer applies. (ITS#4516) + */ + if ( !dn_match( &op->o_conn->c_ndn, + &pwcons[op->o_conn->c_conn_idx].dn )) { + ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val ); + BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn ); + return SLAP_CB_CONTINUE; + } + Debug( LDAP_DEBUG_TRACE, "connection restricted to password changing only\n", 0, 0, 0); if ( send_ctrl ) { @@ -1052,6 +1102,10 @@ ppolicy_add( if ( ppolicy_restrict( op, rs ) != SLAP_CB_CONTINUE ) return rs->sr_err; + /* If this is a replica, assume the master checked everything */ + if ( be_shadow_update( op )) + return SLAP_CB_CONTINUE; + /* Check for password in entry */ if ((pa = attr_find( op->oq_add.rs_e->e_attrs, slap_schema.si_ad_userPassword ))) @@ -1085,40 +1139,41 @@ ppolicy_add( send_ldap_error( op, rs, rc, "Password fails quality checking policy" ); return rs->sr_err; } - /* - * A controversial bit. We hash cleartext - * passwords provided via add and modify operations - * You're not really supposed to do this, since - * the X.500 model says "store attributes" as they - * get provided. By default, this is what we do - * - * But if the hash_passwords flag is set, we hash - * any cleartext password attribute values via the - * default password hashing scheme. - */ - if ((pi->hash_passwords) && - (password_scheme( &(pa->a_vals[0]), NULL ) != LDAP_SUCCESS)) { - struct berval hpw; - - slap_passwd_hash( &(pa->a_vals[0]), &hpw, &txt ); - if (hpw.bv_val == NULL) { - /* - * hashing didn't work. Emit an error. - */ - rs->sr_err = LDAP_OTHER; - rs->sr_text = txt; - send_ldap_error( op, rs, LDAP_OTHER, "Password hashing failed" ); - return rs->sr_err; - } + } + /* + * A controversial bit. We hash cleartext + * passwords provided via add and modify operations + * You're not really supposed to do this, since + * the X.500 model says "store attributes" as they + * get provided. By default, this is what we do + * + * But if the hash_passwords flag is set, we hash + * any cleartext password attribute values via the + * default password hashing scheme. + */ + if ((pi->hash_passwords) && + (password_scheme( &(pa->a_vals[0]), NULL ) != LDAP_SUCCESS)) { + struct berval hpw; - memset( pa->a_vals[0].bv_val, 0, pa->a_vals[0].bv_len); - ber_memfree( pa->a_vals[0].bv_val ); - pa->a_vals[0].bv_val = hpw.bv_val; - pa->a_vals[0].bv_len = hpw.bv_len; + slap_passwd_hash( &(pa->a_vals[0]), &hpw, &txt ); + if (hpw.bv_val == NULL) { + /* + * hashing didn't work. Emit an error. + */ + rs->sr_err = LDAP_OTHER; + rs->sr_text = txt; + send_ldap_error( op, rs, LDAP_OTHER, "Password hashing failed" ); + return rs->sr_err; } + + memset( pa->a_vals[0].bv_val, 0, pa->a_vals[0].bv_len); + ber_memfree( pa->a_vals[0].bv_val ); + pa->a_vals[0].bv_val = hpw.bv_val; + pa->a_vals[0].bv_len = hpw.bv_len; } + /* If password aging is in effect, set the pwdChangedTime */ - if (( pp.pwdMaxAge || pp.pwdMinAge ) && !be_shadow_update( op )) { + if ( pp.pwdMaxAge || pp.pwdMinAge ) { struct berval timestamp; char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ]; time_t now = slap_get_time(); @@ -1127,21 +1182,35 @@ ppolicy_add( timestamp.bv_len = sizeof(timebuf); slap_timestamp( &now, ×tamp ); - attr_merge_one( op->ora_e, ad_pwdChangedTime, ×tamp, NULL ); + attr_merge_one( op->ora_e, ad_pwdChangedTime, ×tamp, ×tamp ); } } return SLAP_CB_CONTINUE; } +static int +ppolicy_mod_cb( Operation *op, SlapReply *rs ) +{ + slap_callback *sc = op->o_callback; + op->o_callback = sc->sc_next; + if ( rs->sr_err == LDAP_SUCCESS ) { + ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val ); + BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn ); + } + op->o_tmpfree( sc, op->o_tmpmemctx ); + return SLAP_CB_CONTINUE; +} + static int ppolicy_modify( Operation *op, SlapReply *rs ) { slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; pp_info *pi = on->on_bi.bi_private; - int i, rc, mod_pw_only, pwmod, pwmop, deladd, + int i, rc, mod_pw_only, pwmod, pwmop = -1, deladd, hsize = 0; PassPolicy pp; - Modifications *mods = NULL, *modtail, *ml, *delmod, *addmod; + Modifications *mods = NULL, *modtail = NULL, + *ml, *delmod, *addmod; Attribute *pa, *ha, at; const char *txt; pw_hist *tl = NULL, *p; @@ -1157,6 +1226,98 @@ ppolicy_modify( Operation *op, SlapReply *rs ) if ( rc != LDAP_SUCCESS ) return SLAP_CB_CONTINUE; + /* If this is a replica, we may need to tweak some of the + * master's modifications. Otherwise, just pass it through. + */ + if ( be_shadow_update( op )) { + Modifications **prev; + int got_del_grace = 0, got_del_lock = 0, got_pw = 0, got_del_fail = 0; + Attribute *a_grace, *a_lock, *a_fail; + + a_grace = attr_find( e->e_attrs, ad_pwdGraceUseTime ); + a_lock = attr_find( e->e_attrs, ad_pwdAccountLockedTime ); + a_fail = attr_find( e->e_attrs, ad_pwdFailureTime ); + + for( prev = &op->oq_modify.rs_modlist, ml = *prev; ml; ml = *prev ) { + + if ( ml->sml_desc == slap_schema.si_ad_userPassword ) + got_pw = 1; + + /* If we're deleting an attr that didn't exist, + * drop this delete op + */ + if ( ml->sml_op == LDAP_MOD_DELETE ) { + int drop = 0; + + if ( ml->sml_desc == ad_pwdGraceUseTime ) { + got_del_grace = 1; + if ( !a_grace ) + drop = 1; + } else + if ( ml->sml_desc == ad_pwdAccountLockedTime ) { + got_del_lock = 1; + if ( !a_lock ) + drop = 1; + } else + if ( ml->sml_desc == ad_pwdFailureTime ) { + got_del_fail = 1; + if ( !a_fail ) + drop = 1; + } + if ( drop ) { + *prev = ml->sml_next; + ml->sml_next = NULL; + slap_mods_free( ml, 1 ); + continue; + } + } + prev = &ml->sml_next; + } + + /* If we're resetting the password, make sure grace, accountlock, + * and failure also get removed. + */ + if ( got_pw ) { + if ( a_grace && !got_del_grace ) { + ml = (Modifications *) ch_malloc( sizeof( Modifications ) ); + ml->sml_op = LDAP_MOD_DELETE; + ml->sml_flags = SLAP_MOD_INTERNAL; + ml->sml_type.bv_val = NULL; + ml->sml_desc = ad_pwdGraceUseTime; + ml->sml_values = NULL; + ml->sml_nvalues = NULL; + ml->sml_next = NULL; + *prev = ml; + prev = &ml->sml_next; + } + if ( a_lock && !got_del_lock ) { + ml = (Modifications *) ch_malloc( sizeof( Modifications ) ); + ml->sml_op = LDAP_MOD_DELETE; + ml->sml_flags = SLAP_MOD_INTERNAL; + ml->sml_type.bv_val = NULL; + ml->sml_desc = ad_pwdAccountLockedTime; + ml->sml_values = NULL; + ml->sml_nvalues = NULL; + ml->sml_next = NULL; + *prev = ml; + } + if ( a_fail && !got_del_fail ) { + ml = (Modifications *) ch_malloc( sizeof( Modifications ) ); + ml->sml_op = LDAP_MOD_DELETE; + ml->sml_flags = SLAP_MOD_INTERNAL; + ml->sml_type.bv_val = NULL; + ml->sml_desc = ad_pwdFailureTime; + ml->sml_values = NULL; + ml->sml_nvalues = NULL; + ml->sml_next = NULL; + *prev = ml; + } + } + op->o_bd->bd_info = (BackendInfo *)on->on_info; + be_entry_release_r( op, e ); + return SLAP_CB_CONTINUE; + } + /* Did we receive a password policy request control? */ if ( op->o_ctrlflag[ppolicy_cid] ) { send_ctrl = 1; @@ -1181,12 +1342,13 @@ ppolicy_modify( Operation *op, SlapReply *rs ) ppolicy_get( op, e, &pp ); - for(ml = op->oq_modify.rs_modlist, + for ( ml = op->oq_modify.rs_modlist, pwmod = 0, mod_pw_only = 1, deladd = 0, delmod = NULL, addmod = NULL, zapReset = 1; - ml != NULL; modtail = ml, ml = ml->sml_next ) { + ml != NULL; modtail = ml, ml = ml->sml_next ) + { if ( ml->sml_desc == pp.ad ) { pwmod = 1; pwmop = ml->sml_op; @@ -1225,13 +1387,19 @@ ppolicy_modify( Operation *op, SlapReply *rs ) } } - if (pwcons[op->o_conn->c_conn_idx].restricted && !mod_pw_only) { - Debug( LDAP_DEBUG_TRACE, - "connection restricted to password changing only\n", 0, 0, 0 ); - rs->sr_err = LDAP_INSUFFICIENT_ACCESS; - rs->sr_text = "Operations are restricted to bind/unbind/abandon/StartTLS/modify password"; - pErr = PP_changeAfterReset; - goto return_results; + if (!BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn ) && !mod_pw_only ) { + if ( dn_match( &op->o_conn->c_ndn, + &pwcons[op->o_conn->c_conn_idx].dn )) { + Debug( LDAP_DEBUG_TRACE, + "connection restricted to password changing only\n", 0, 0, 0 ); + rs->sr_err = LDAP_INSUFFICIENT_ACCESS; + rs->sr_text = "Operations are restricted to bind/unbind/abandon/StartTLS/modify password"; + pErr = PP_changeAfterReset; + goto return_results; + } else { + ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val ); + BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn ); + } } /* @@ -1426,11 +1594,27 @@ ppolicy_modify( Operation *op, SlapReply *rs ) } do_modify: - if ((pwmod) && (!be_shadow_update( op ))) { + if (pwmod) { struct berval timestamp; char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ]; time_t now = slap_get_time(); - + + /* If the conn is restricted, set a callback to clear it + * if the pwmod succeeds + */ + if (!BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn )) { + slap_callback *sc = op->o_tmpcalloc( 1, sizeof( slap_callback ), + op->o_tmpmemctx ); + sc->sc_next = op->o_callback; + /* Must use sc_response to insure we reset on success, before + * the client sees the response. Must use sc_cleanup to insure + * that it gets cleaned up if sc_response is not called. + */ + sc->sc_response = ppolicy_mod_cb; + sc->sc_cleanup = ppolicy_mod_cb; + op->o_callback = sc; + } + /* * keep the necessary pwd.. operational attributes * up to date. @@ -1446,7 +1630,9 @@ do_modify: if (pwmop != LDAP_MOD_DELETE) { mods->sml_op = LDAP_MOD_REPLACE; mods->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) ); + mods->sml_nvalues = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) ); ber_dupbv( &mods->sml_values[0], ×tamp ); + ber_dupbv( &mods->sml_nvalues[0], ×tamp ); mods->sml_values[1].bv_len = 0; mods->sml_values[1].bv_val = NULL; assert( mods->sml_values[0].bv_val != NULL ); @@ -1473,6 +1659,32 @@ do_modify: modtail = mods; } + if (attr_find(e->e_attrs, ad_pwdAccountLockedTime )) { + mods = (Modifications *) ch_malloc( sizeof( Modifications ) ); + mods->sml_op = LDAP_MOD_DELETE; + mods->sml_flags = SLAP_MOD_INTERNAL; + mods->sml_type.bv_val = NULL; + mods->sml_desc = ad_pwdAccountLockedTime; + mods->sml_values = NULL; + mods->sml_nvalues = NULL; + mods->sml_next = NULL; + modtail->sml_next = mods; + modtail = mods; + } + + if (attr_find(e->e_attrs, ad_pwdFailureTime )) { + mods = (Modifications *) ch_malloc( sizeof( Modifications ) ); + mods->sml_op = LDAP_MOD_DELETE; + mods->sml_flags = SLAP_MOD_INTERNAL; + mods->sml_type.bv_val = NULL; + mods->sml_desc = ad_pwdFailureTime; + mods->sml_values = NULL; + mods->sml_nvalues = NULL; + mods->sml_next = NULL; + modtail->sml_next = mods; + modtail = mods; + } + /* Delete the pwdReset attribute, since it's being reset */ if ((zapReset) && (attr_find(e->e_attrs, ad_pwdReset ))) { mods = (Modifications *) ch_malloc( sizeof( Modifications ) ); @@ -1626,15 +1838,51 @@ ppolicy_parseCtrl( rs->sr_text = "passwordPolicyRequest control value not empty"; return LDAP_PROTOCOL_ERROR; } - if ( ctrl->ldctl_iscritical ) { - rs->sr_text = "passwordPolicyRequest control invalid criticality"; - return LDAP_PROTOCOL_ERROR; - } - op->o_ctrlflag[ppolicy_cid] = SLAP_CONTROL_NONCRITICAL; + op->o_ctrlflag[ppolicy_cid] = ctrl->ldctl_iscritical + ? SLAP_CONTROL_CRITICAL + : SLAP_CONTROL_NONCRITICAL; return LDAP_SUCCESS; } +static int +attrPretty( + Syntax *syntax, + struct berval *val, + struct berval *out, + void *ctx ) +{ + AttributeDescription *ad = NULL; + const char *err; + int code; + + code = slap_bv2ad( val, &ad, &err ); + if ( !code ) { + ber_dupbv_x( out, &ad->ad_type->sat_cname, ctx ); + } + return code; +} + +static int +attrNormalize( + slap_mask_t use, + Syntax *syntax, + MatchingRule *mr, + struct berval *val, + struct berval *out, + void *ctx ) +{ + AttributeDescription *ad = NULL; + const char *err; + int code; + + code = slap_bv2ad( val, &ad, &err ); + if ( !code ) { + ber_str2bv_x( ad->ad_type->sat_oid, 0, 1, out, ctx ); + } + return code; +} + static int ppolicy_db_init( BackendDB *be @@ -1654,6 +1902,20 @@ ppolicy_db_init( return code; } } + { + Syntax *syn; + MatchingRule *mr; + + syn = ch_malloc( sizeof( Syntax )); + *syn = *ad_pwdAttribute->ad_type->sat_syntax; + syn->ssyn_pretty = attrPretty; + ad_pwdAttribute->ad_type->sat_syntax = syn; + + mr = ch_malloc( sizeof( MatchingRule )); + *mr = *ad_pwdAttribute->ad_type->sat_equality; + mr->smr_normalize = attrNormalize; + ad_pwdAttribute->ad_type->sat_equality = mr; + } } on->on_bi.bi_private = ch_calloc( sizeof(pp_info), 1 ); @@ -1730,6 +1992,7 @@ ppolicy_config( return ( 1 ); } pi->hash_passwords = 1; + return 0; } return SLAP_CONF_UNKNOWN; } @@ -1741,7 +2004,7 @@ static char *extops[] = { static slap_overinst ppolicy; -int ppolicy_init() +int ppolicy_initialize() { LDAPAttributeType *at; const char *err; @@ -1765,6 +2028,11 @@ int ppolicy_init() scherr2str(code), err ); return code; } + /* Allow Manager to set these as needed */ + if ( is_at_no_user_mod( (*pwd_OpSchema[i].ad)->ad_type )) { + (*pwd_OpSchema[i].ad)->ad_type->sat_flags |= + SLAP_AT_MANAGEABLE; + } } code = register_supported_control( LDAP_CONTROL_PASSWORDPOLICYREQUEST, @@ -1796,7 +2064,7 @@ int ppolicy_init() #if SLAPD_OVER_PPOLICY == SLAPD_MOD_DYNAMIC int init_module(int argc, char *argv[]) { - return ppolicy_init(); + return ppolicy_initialize(); } #endif