diff options
Diffstat (limited to 'ldap/servers/slapd/modify.c')
-rw-r--r-- | ldap/servers/slapd/modify.c | 966 |
1 files changed, 966 insertions, 0 deletions
diff --git a/ldap/servers/slapd/modify.c b/ldap/servers/slapd/modify.c new file mode 100644 index 00000000..56c4de7c --- /dev/null +++ b/ldap/servers/slapd/modify.c @@ -0,0 +1,966 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * Copyright (c) 1995 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ + +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <string.h> +#include <sys/types.h> +#ifndef _WIN32 +#include <sys/socket.h> +#endif +#include "slap.h" +#include "pratom.h" +#if defined(irix) || defined(aix) || defined(_WIN32) +#include <time.h> +#endif + +/* Forward declarations */ +static int modify_internal_pb (Slapi_PBlock *pb); +static void op_shared_modify (Slapi_PBlock *pb, int pw_change, char *old_pw); +static void remove_mod (Slapi_Mods *smods, const char *type, Slapi_Mods *smod_unhashed); +static int op_shared_allow_pw_change (Slapi_PBlock *pb, LDAPMod *mod, char **old_pw); + +#ifdef LDAP_DEBUG +static const char* +mod_op_image (int op) +{ + switch (op & ~LDAP_MOD_BVALUES) { + case LDAP_MOD_ADD: return "add"; + case LDAP_MOD_DELETE: return "delete"; + case LDAP_MOD_REPLACE: return "replace"; + default: break; + } + return "???"; +} +#endif + +/* an AttrCheckFunc function should return an LDAP result code (LDAP_SUCCESS if all goes well). */ +typedef int (*AttrCheckFunc)(const char *attr_name, char *value, long minval, long maxval, char *errorbuf); + +static struct attr_value_check { + const char *attr_name; /* the name of the attribute */ + AttrCheckFunc checkfunc; + long minval; + long maxval; +} AttrValueCheckList[] = { + {CONFIG_PW_SYNTAX_ATTRIBUTE, attr_check_onoff, 0, 0}, + {CONFIG_PW_CHANGE_ATTRIBUTE, attr_check_onoff, 0, 0}, + {CONFIG_PW_LOCKOUT_ATTRIBUTE, attr_check_onoff, 0, 0}, + {CONFIG_PW_MUSTCHANGE_ATTRIBUTE, attr_check_onoff, 0, 0}, + {CONFIG_PW_EXP_ATTRIBUTE, attr_check_onoff, 0, 0}, + {CONFIG_PW_UNLOCK_ATTRIBUTE, attr_check_onoff, 0, 0}, + {CONFIG_PW_HISTORY_ATTRIBUTE, attr_check_onoff, 0, 0}, + {CONFIG_PW_MINAGE_ATTRIBUTE, check_pw_minage_value, -1, -1}, + {CONFIG_PW_WARNING_ATTRIBUTE, attr_check_minmax, 0, -1}, + {CONFIG_PW_MINLENGTH_ATTRIBUTE, attr_check_minmax, 2, 512}, + {CONFIG_PW_MAXFAILURE_ATTRIBUTE, attr_check_minmax, 1, 32767}, + {CONFIG_PW_INHISTORY_ATTRIBUTE, attr_check_minmax, 2, 24}, + {CONFIG_PW_LOCKDURATION_ATTRIBUTE, check_pw_lockduration_value, -1, -1}, + {CONFIG_PW_RESETFAILURECOUNT_ATTRIBUTE, check_pw_resetfailurecount_value, -1, -1}, + {CONFIG_PW_GRACELIMIT_ATTRIBUTE, attr_check_minmax, 0, -1}, + {CONFIG_PW_STORAGESCHEME_ATTRIBUTE, check_pw_storagescheme_value, -1, -1} +}; + +/* This function is called to process operation that come over external connections */ +void +do_modify( Slapi_PBlock *pb ) +{ + Slapi_Operation *operation; + BerElement *ber; + char *last, *type; + unsigned long tag, len; + LDAPMod *mod; + LDAPMod **mods; + Slapi_Mods smods; + int err; + int pw_change = 0; /* 0= no password change */ + int ignored_some_mods = 0; + char *old_pw = NULL; /* remember the old password */ + char *dn; + LDAPControl **ctrlp = NULL; + + LDAPDebug( LDAP_DEBUG_TRACE, "do_modify\n", 0, 0, 0 ); + + slapi_pblock_get( pb, SLAPI_OPERATION, &operation); + ber = operation->o_ber; + + slapi_pblock_get(pb, SLAPI_REQCONTROLS, &ctrlp); + + /* count the modify request */ + PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsModifyEntryOps); + + /* + * Parse the modify request. It looks like this: + * + * ModifyRequest := [APPLICATION 6] SEQUENCE { + * name DistinguishedName, + * mods SEQUENCE OF SEQUENCE { + * operation ENUMERATED { + * add (0), + * delete (1), + * replace (2) + * }, + * modification SEQUENCE { + * type AttributeType, + * values SET OF AttributeValue + * } + * } + * } + */ + + { + if ( ber_scanf( ber, "{a", &dn ) == LBER_ERROR ) + { + LDAPDebug( LDAP_DEBUG_ANY, + "ber_scanf failed (op=Modify; params=DN)\n", 0, 0, 0 ); + op_shared_log_error_access (pb, "MOD", "???", "decoding error"); + send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL, NULL, 0, + NULL ); + return; + } + } + + LDAPDebug( LDAP_DEBUG_ARGS, "do_modify: dn (%s)\n", dn, 0, 0 ); + + slapi_pblock_set( pb, SLAPI_REQUESTOR_ISROOT, &pb->pb_op->o_isroot); + slapi_pblock_set( pb, SLAPI_ORIGINAL_TARGET, dn ); + + /* collect modifications & save for later */ + slapi_mods_init(&smods, 0); + for ( tag = ber_first_element( ber, &len, &last ); + tag != LBER_ERROR && tag != LBER_END_OF_SEQORSET; + tag = ber_next_element( ber, &len, last ) ) + { + long long_mod_op; + mod = (LDAPMod *) slapi_ch_malloc( sizeof(LDAPMod) ); + mod->mod_bvalues = NULL; + + if ( ber_scanf( ber, "{i{a[V]}}", &long_mod_op, &type, + &mod->mod_bvalues ) == LBER_ERROR ) + { + op_shared_log_error_access (pb, "MOD", dn, "decoding error"); + send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL, + "decoding error", 0, NULL ); + slapi_ch_free((void **)&mod); + goto free_and_return; + } + mod->mod_op = long_mod_op; + mod->mod_type = slapi_attr_syntax_normalize(type); + if ( !mod->mod_type || !*mod->mod_type ) { + char ebuf[BUFSIZ]; + PR_snprintf (ebuf, BUFSIZ, "invalid type '%s'", type); + ebuf[BUFSIZ-1] = '\0'; + op_shared_log_error_access (pb, "MOD", dn, ebuf); + send_ldap_result( pb, LDAP_INVALID_SYNTAX, NULL, ebuf, 0, NULL ); + slapi_ch_free((void **)&type); + ber_bvecfree(mod->mod_bvalues); + slapi_ch_free((void **)&mod); + goto free_and_return; + } + slapi_ch_free((void **)&type); + + if ( mod->mod_op != LDAP_MOD_ADD && + mod->mod_op != LDAP_MOD_DELETE && + mod->mod_op != LDAP_MOD_REPLACE ) + { + op_shared_log_error_access (pb, "MOD", dn, "unrecognized modify operation"); + send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL, + "unrecognized modify operation", 0, NULL ); + ber_bvecfree(mod->mod_bvalues); + slapi_ch_free((void **)&(mod->mod_type)); + slapi_ch_free((void **)&mod); + goto free_and_return; + } + + if ( mod->mod_bvalues == NULL + && mod->mod_op != LDAP_MOD_DELETE + && mod->mod_op != LDAP_MOD_REPLACE ) + { + op_shared_log_error_access (pb, "MOD", dn, "no values given"); + send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL, + "no values given", 0, NULL ); + ber_bvecfree(mod->mod_bvalues); + slapi_ch_free((void **)&(mod->mod_type)); + slapi_ch_free((void **)&mod); + goto free_and_return; + } + + /* check if user is allowed to modify the specified attribute */ + if (!op_shared_is_allowed_attr (mod->mod_type, pb->pb_conn->c_isreplication_session)) + { + /* for now we just ignore attributes that client is not allowed + to modify so not to break existing clients */ + ++ignored_some_mods; + ber_bvecfree(mod->mod_bvalues); + slapi_ch_free((void **)&(mod->mod_type)); + slapi_ch_free((void **)&mod); + continue; + /* send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL, NULL, 0, NULL ); + goto free_and_return; */ + } + + /* check for password change */ + if ( mod->mod_bvalues != NULL && + strcasecmp( mod->mod_type, SLAPI_USERPWD_ATTR ) == 0 ){ + if ( (err = get_ldapmessage_controls( pb, ber, NULL )) != 0 ) { + op_shared_log_error_access (pb, "MOD", dn, "failed to decode LDAP controls"); + send_ldap_result( pb, err, NULL, NULL, 0, NULL ); + goto free_and_return; + } + pw_change = op_shared_allow_pw_change (pb, mod, &old_pw); + if (pw_change == -1) + { + ber_bvecfree(mod->mod_bvalues); + slapi_ch_free((void **)&(mod->mod_type)); + slapi_ch_free((void **)&mod); + goto free_and_return; + } + } + + mod->mod_op |= LDAP_MOD_BVALUES; + slapi_mods_add_ldapmod (&smods, mod); + } + + if ( tag == LBER_ERROR && !ctrlp ) + { + op_shared_log_error_access (pb, "MOD", dn, "decoding error"); + send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL, "decoding error", 0, NULL ); + goto free_and_return; + } + + if ( slapi_mods_get_num_mods (&smods) == 0 ) + { + int lderr; + char *emsg; + + if ( ignored_some_mods ) { + lderr = LDAP_UNWILLING_TO_PERFORM; + emsg = "no modifiable attributes specified"; + } else { + lderr = LDAP_PROTOCOL_ERROR; + emsg = "no modifications specified"; + } + op_shared_log_error_access (pb, "MOD", dn, emsg); + send_ldap_result( pb, lderr, NULL, emsg, 0, NULL ); + goto free_and_return; + } + + if (!pb->pb_conn->c_isreplication_session && + pb->pb_conn->c_needpw && pw_change == 0 ) + { + (void)add_pwd_control ( pb, LDAP_CONTROL_PWEXPIRED, 0); + op_shared_log_error_access (pb, "MOD", dn, "need new password"); + send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL, NULL, 0, NULL ); + goto free_and_return; + } + + /* + * in LDAPv3 there can be optional control extensions on + * the end of an LDAPMessage. we need to read them in and + * pass them to the backend. + */ + if ( !ctrlp ) { + if ( (err = get_ldapmessage_controls( pb, ber, NULL )) != 0 ) + { + op_shared_log_error_access (pb, "MOD", dn, "failed to decode LDAP controls"); + send_ldap_result( pb, err, NULL, NULL, 0, NULL ); + goto free_and_return; + } + } + +#ifdef LDAP_DEBUG + LDAPDebug( LDAP_DEBUG_ARGS, "modifications:\n", 0, 0, 0 ); + for (mod = slapi_mods_get_first_mod(&smods); mod != NULL; + mod = slapi_mods_get_next_mod(&smods)) + { + LDAPDebug( LDAP_DEBUG_ARGS, "\t%s: %s\n", + mod_op_image( mod->mod_op ), mod->mod_type, 0 ); + } +#endif + + mods = slapi_mods_get_ldapmods_passout (&smods); + + slapi_pblock_set( pb, SLAPI_MODIFY_MODS, mods); + + op_shared_modify ( pb, pw_change, old_pw ); + + slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods); + ldap_mods_free (mods, 1 /* Free the Array and the Elements */); + +free_and_return:; + slapi_ch_free ((void**)&dn); + slapi_mods_done(&smods); +} + +/* This function is used to issue internal modify operation + This is an old style API. Its use is discoraged because it is not extendable and + because it does not allow to check whether plugin has right to access part of the + tree it is trying to modify. Use slapi_modify_internal_pb instead */ +Slapi_PBlock* +slapi_modify_internal(const char *idn, + LDAPMod **mods, + LDAPControl **controls, + int dummy) +{ + Slapi_PBlock pb; + Slapi_PBlock *result_pb = NULL; + int opresult; + + pblock_init(&pb); + + slapi_modify_internal_set_pb (&pb, idn, (LDAPMod**)mods, controls, NULL, + (void *)plugin_get_default_component_id(), 0); + + modify_internal_pb (&pb); + + result_pb = slapi_pblock_new(); + if (result_pb) + { + slapi_pblock_get(&pb, SLAPI_PLUGIN_INTOP_RESULT, &opresult); + slapi_pblock_set(result_pb, SLAPI_PLUGIN_INTOP_RESULT, &opresult); + } + pblock_done(&pb); + + return result_pb; +} + +/* This is new style API to issue internal modify operation. + pblock should contain the following data (can be set via call to slapi_modify_internal_set_pb): + For uniqueid based operation: + SLAPI_TARGET_DN set to dn that allows to select right backend, can be stale + SLAPI_TARGET_UNIQUEID set to the uniqueid of the entry we are looking for + SLAPI_MODIFY_MODS set to the mods + SLAPI_CONTROLS_ARG set to request controls if present + + For dn based search: + SLAPI_TARGET_DN set to the entry dn + SLAPI_MODIFY_MODS set to the mods + SLAPI_CONTROLS_ARG set to request controls if present + */ +int slapi_modify_internal_pb (Slapi_PBlock *pb) +{ + if (pb == NULL) + return -1; + + if (!allow_operation (pb)) + { + slapi_send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL, + "This plugin is not configured to access operation target data", 0, NULL ); + return 0; + } + + return modify_internal_pb (pb); +} + +/* Initialize a pblock for a call to slapi_modify_internal_pb() */ +void slapi_modify_internal_set_pb (Slapi_PBlock *pb, const char *dn, LDAPMod **mods, LDAPControl **controls, + const char *uniqueid, Slapi_ComponentId *plugin_identity, int operation_flags) +{ + Operation *op; + PR_ASSERT (pb != NULL); + if (pb == NULL || dn == NULL || mods == NULL) + { + slapi_log_error(SLAPI_LOG_FATAL, NULL, + "slapi_modify_internal_set_pb: NULL parameter\n"); + return; + } + + op= internal_operation_new(SLAPI_OPERATION_MODIFY,operation_flags); + slapi_pblock_set(pb, SLAPI_OPERATION, op); + slapi_pblock_set(pb, SLAPI_ORIGINAL_TARGET, (void*)dn); + slapi_pblock_set(pb, SLAPI_MODIFY_MODS, mods); + slapi_pblock_set(pb, SLAPI_CONTROLS_ARG, controls); + if (uniqueid) + { + slapi_pblock_set(pb, SLAPI_TARGET_UNIQUEID, (void*)uniqueid); + } + slapi_pblock_set(pb, SLAPI_PLUGIN_IDENTITY, plugin_identity); +} + +/* Helper functions */ + +static int modify_internal_pb (Slapi_PBlock *pb) +{ + LDAPControl **controls; + Operation *op; + int opresult = 0; + LDAPMod **normalized_mods = NULL; + LDAPMod **mods; + LDAPMod **mod; + int pw_change = 0; + char *old_pw = NULL; + + PR_ASSERT (pb != NULL); + + slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods); + slapi_pblock_get(pb, SLAPI_CONTROLS_ARG, &controls); + + if(mods == NULL) + { + opresult = LDAP_PARAM_ERROR; + slapi_pblock_set(pb, SLAPI_PLUGIN_INTOP_RESULT, &opresult); + return 0; + } + + /* first normalize the mods so they are bvalue + * Note: We don't add any special + * attributes such as "creatorsname". + * for CIR we don't want to change them, for other + * plugins the writer should change these if it wants too by explicitly + * adding them to the mods + */ + normalized_mods = normalize_mods2bvals((const LDAPMod**)mods); + if (normalized_mods == NULL) + { + opresult = LDAP_PARAM_ERROR; + slapi_pblock_set(pb, SLAPI_PLUGIN_INTOP_RESULT, &opresult); + return 0; + } + + /* check for password change */ + mod = normalized_mods; + while (*mod) + { + if ((*mod)->mod_bvalues != NULL && strcasecmp((*mod)->mod_type, SLAPI_USERPWD_ATTR) == 0) + { + pw_change = op_shared_allow_pw_change (pb, *mod, &old_pw); + if (pw_change == -1) + { + opresult = LDAP_PARAM_ERROR; + slapi_pblock_set(pb, SLAPI_PLUGIN_INTOP_RESULT, &opresult); + return 0; + } + } + + mod ++; + } + + slapi_pblock_get(pb, SLAPI_OPERATION, &op); + op->o_handler_data = &opresult; + op->o_result_handler = internal_getresult_callback; + + slapi_pblock_set(pb, SLAPI_MODIFY_MODS, normalized_mods); + slapi_pblock_set(pb, SLAPI_REQCONTROLS, controls); + + /* set parameters common for all internal operations */ + set_common_params (pb); + + /* set actions taken to process the operation */ + set_config_params (pb); + + /* perform modify operation */ + op_shared_modify (pb, pw_change, old_pw); + + /* free the normalized_mods don't forget to add this*/ + slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &normalized_mods); + if (normalized_mods != NULL) + { + ldap_mods_free(normalized_mods, 1); + } + + /* return original mods here */ + slapi_pblock_set(pb, SLAPI_MODIFY_MODS, mods); + /* set result */ + slapi_pblock_set(pb, SLAPI_PLUGIN_INTOP_RESULT, &opresult); + + return 0; +} + +static void op_shared_modify (Slapi_PBlock *pb, int pw_change, char *old_pw) +{ + Slapi_Backend *be = NULL; + Slapi_Entry *pse; + Slapi_Entry *referral; + Slapi_Entry *ecopy = NULL; + Slapi_Entry *e = NULL; + char ebuf[BUFSIZ]; + char *dn; + Slapi_DN sdn; + LDAPMod **mods, *pw_mod, **tmpmods = NULL; + Slapi_Mods smods; + Slapi_Mods unhashed_pw_smod; + int repl_op, internal_op, lastmod; + char *unhashed_pw_attr = NULL; + Slapi_Operation *operation; + char errorbuf[BUFSIZ]; + int err; + LDAPMod *lc_mod = NULL; + struct slapdplugin *p = NULL; + int numattr, i; + + slapi_pblock_get (pb, SLAPI_ORIGINAL_TARGET, &dn); + slapi_pblock_get (pb, SLAPI_MODIFY_MODS, &mods); + slapi_pblock_get (pb, SLAPI_MODIFY_MODS, &tmpmods); + slapi_pblock_get (pb, SLAPI_IS_REPLICATED_OPERATION, &repl_op); + slapi_pblock_get (pb, SLAPI_OPERATION, &operation); + internal_op= operation_is_flag_set(operation, OP_FLAG_INTERNAL); + + if (dn == NULL) + { + slapi_sdn_init_dn_byref (&sdn, ""); + } + else + { + slapi_sdn_init_dn_byref (&sdn, dn); + } + + slapi_pblock_set(pb, SLAPI_MODIFY_TARGET, (void*)slapi_sdn_get_ndn (&sdn)); + + slapi_mods_init_passin (&smods, mods); + + slapi_mods_init(&unhashed_pw_smod, 0); + + /* target spec is used to decide which plugins are applicable for the operation */ + operation_set_target_spec (pb->pb_op, &sdn); + + if (operation_is_flag_set(operation,OP_FLAG_ACTION_LOG_ACCESS)) + { + if ( !internal_op ) + { + slapi_log_access(LDAP_DEBUG_STATS, "conn=%d op=%d MOD dn=\"%s\"\n", + pb->pb_conn->c_connid, + pb->pb_op->o_opid, + escape_string(slapi_sdn_get_dn(&sdn), ebuf)); + } + else + { + slapi_log_access(LDAP_DEBUG_ARGS, "conn=%s op=%d MOD dn=\"%s\"\n", + LOG_INTERNAL_OP_CON_ID, + LOG_INTERNAL_OP_OP_ID, + escape_string(slapi_sdn_get_dn(&sdn), ebuf)); + } + } + + /* + * We could be serving multiple database backends. Select the + * appropriate one. + */ + if ((err = slapi_mapping_tree_select(pb, &be, &referral, errorbuf)) != LDAP_SUCCESS) { + send_ldap_result(pb, err, NULL, errorbuf, 0, NULL); + be = NULL; + goto free_and_return; + } + + if (referral) + { + int managedsait; + + slapi_pblock_get(pb, SLAPI_MANAGEDSAIT, &managedsait); + if (managedsait) + { + send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL, + "cannot update referral", 0, NULL); + slapi_entry_free(referral); + goto free_and_return; + } + + send_referrals_from_entry(pb,referral); + slapi_entry_free(referral); + goto free_and_return; + } + + slapi_pblock_set(pb, SLAPI_BACKEND, be); + + /* The following section checks the valid values of fine-grained + * password policy attributes. + * 1. First, it checks if the entry has "passwordpolicy" objectclass. + * 2. If yes, then if the mods contain any passwdpolicy specific attributes. + * 3. If yes, then it invokes corrosponding checking function. + */ + if ( !repl_op && !internal_op && dn && + (e = get_entry(pb, slapi_dn_normalize(dn))) ) + { + Slapi_Value target; + slapi_value_init(&target); + slapi_value_set_string(&target,"passwordpolicy"); + if ((slapi_entry_attr_has_syntax_value(e, "objectclass", &target)) == 1) + { + numattr = sizeof(AttrValueCheckList)/sizeof(AttrValueCheckList[0]); + while ( tmpmods && *tmpmods ) + { + if ((*tmpmods)->mod_bvalues != NULL && + (((*tmpmods)->mod_op & ~LDAP_MOD_BVALUES) != LDAP_MOD_DELETE)) + { + for (i=0; i < numattr; i++) + { + if (slapi_attr_type_cmp((*tmpmods)->mod_type, + AttrValueCheckList[i].attr_name, SLAPI_TYPE_CMP_SUBTYPE) == 0) + { + /* The below function call is good for + * single-valued attrs only + */ + if ( (err = AttrValueCheckList[i].checkfunc (AttrValueCheckList[i].attr_name, + (*tmpmods)->mod_bvalues[0]->bv_val, AttrValueCheckList[i].minval, + AttrValueCheckList[i].maxval, errorbuf)) + != LDAP_SUCCESS) + { + /* return error */ + send_ldap_result(pb, err, NULL, errorbuf, 0, NULL); + goto free_and_return; + } + } + } + } + tmpmods++; + } /* end of (while */ + } /* end of if (found */ + value_done (&target); + } /* end of if (!repl_op */ + + /* can get lastmod only after backend is selected */ + slapi_pblock_get(pb, SLAPI_BE_LASTMOD, &lastmod); + + /* if this is replication session - leave mod attributes alone */ + if (!repl_op && lastmod) + { + modify_update_last_modified_attr(pb, &smods); + } + + /* + * Add the unhashed password pseudo-attribute before + * calling the preop plugins + */ + + if (pw_change) + { + Slapi_Value **va= NULL; + + unhashed_pw_attr = slapi_attr_syntax_normalize(PSEUDO_ATTR_UNHASHEDUSERPASSWORD); + + for ( pw_mod = slapi_mods_get_first_mod(&smods); pw_mod; + pw_mod = slapi_mods_get_next_mod(&smods) ) + { + if (strcasecmp (pw_mod->mod_type, SLAPI_USERPWD_ATTR) != 0) + continue; + + /* add pseudo password attribute */ + valuearray_init_bervalarray(pw_mod->mod_bvalues, &va); + slapi_mods_add_mod_values(&smods, pw_mod->mod_op, unhashed_pw_attr, va); + valuearray_free(&va); + + /* Init new value array for hashed value */ + valuearray_init_bervalarray(pw_mod->mod_bvalues, &va); + + /* encode password */ + pw_encodevals(va); + + /* remove current clear value of userpassword */ + ber_bvecfree(pw_mod->mod_bvalues); + /* add the cipher in the structure */ + valuearray_get_bervalarray(va, &pw_mod->mod_bvalues); + + valuearray_free(&va); + } + } + for ( p = get_plugin_list(PLUGIN_LIST_REVER_PWD_STORAGE_SCHEME); p != NULL; p = p->plg_next ) + { + char *L_attr = NULL; + int i = 0; + + /* Get the appropriate encoding function */ + for ( L_attr = p->plg_argv[i]; i<p->plg_argc; L_attr = p->plg_argv[++i]) + { + char *L_normalized = slapi_attr_syntax_normalize(L_attr); + + for ( lc_mod = slapi_mods_get_first_mod(&smods); lc_mod; + lc_mod = slapi_mods_get_next_mod(&smods) ) + { + Slapi_Value **va= NULL; + + if (strcasecmp (lc_mod->mod_type, L_normalized) != 0) + continue; + + switch (lc_mod->mod_op & ~LDAP_MOD_BVALUES) + { + case LDAP_MOD_ADD: + case LDAP_MOD_REPLACE: + + /* Init new value array for hashed value */ + valuearray_init_bervalarray(lc_mod->mod_bvalues, &va); + if ( va ) + { + /* encode local credentials */ + pw_rever_encode(va, L_normalized); + /* remove current clear value of userpassword */ + ber_bvecfree(lc_mod->mod_bvalues); + /* add the cipher in the structure */ + valuearray_get_bervalarray(va, &lc_mod->mod_bvalues); + + valuearray_free(&va); + } + break; + default: + /* for LDAP_MOD_DELETE, don't do anything */ + /* for LDAP_MOD_BVALUES, don't do anything */ + ; + } + } + if (L_normalized) + slapi_ch_free ((void**)&L_normalized); + } + } + + /* + * call the pre-mod plugins. if they succeed, call + * the backend mod function. then call the post-mod + * plugins. + */ + slapi_pblock_set (pb, SLAPI_MODIFY_MODS, (void*)slapi_mods_get_ldapmods_passout (&smods)); + if (plugin_call_plugins(pb, internal_op ? SLAPI_PLUGIN_INTERNAL_PRE_MODIFY_FN : + SLAPI_PLUGIN_PRE_MODIFY_FN) == 0) + { + int rc; + + slapi_pblock_set(pb, SLAPI_PLUGIN, be->be_database); + set_db_default_result_handlers(pb); + + /* Remove the unhashed password pseudo-attribute prior */ + /* to db access */ + if (pw_change) + { + slapi_pblock_get (pb, SLAPI_MODIFY_MODS, &mods); + slapi_mods_init_passin (&smods, mods); + remove_mod (&smods, unhashed_pw_attr, &unhashed_pw_smod); + slapi_pblock_set (pb, SLAPI_MODIFY_MODS, + (void*)slapi_mods_get_ldapmods_passout (&smods)); + } + + if (be->be_modify != NULL) + { + if ((rc = (*be->be_modify)(pb)) == 0) + { + /* acl is not used for internal operations */ + /* don't update aci store for remote acis */ + if ((!internal_op) && + (!slapi_be_is_flag_set(be,SLAPI_BE_FLAG_REMOTE_DATA))) + { + plugin_call_acl_mods_update (pb, SLAPI_OPERATION_MODIFY); + } + + if (operation_is_flag_set(operation,OP_FLAG_ACTION_LOG_AUDIT)) + write_audit_log_entry(pb); /* Record the operation in the audit log */ + + if (pw_change && (!slapi_be_is_flag_set(be,SLAPI_BE_FLAG_REMOTE_DATA))) + { + /* update the password info */ + update_pw_info (pb, old_pw); + } + slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &pse); + do_ps_service(pse, NULL, LDAP_CHANGETYPE_MODIFY, 0); + } + else + { + if (rc == SLAPI_FAIL_DISKFULL) + { + operation_out_of_disk_space(); + goto free_and_return; + } + } + } + else + { + send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL, + "Function not implemented", 0, NULL); + } + /* Add the pseudo-attribute prior to calling the postop plugins */ + if (pw_change) + { + LDAPMod *lc_mod = NULL; + + slapi_pblock_get (pb, SLAPI_MODIFY_MODS, &mods); + slapi_mods_init_passin (&smods, mods); + for ( lc_mod = slapi_mods_get_first_mod(&unhashed_pw_smod); lc_mod; + lc_mod = slapi_mods_get_next_mod(&unhashed_pw_smod) ) + { + Slapi_Mod lc_smod; + slapi_mod_init_byval(&lc_smod, lc_mod); /* copies lc_mod */ + /* this extracts the copy of lc_mod and finalizes lc_smod too */ + slapi_mods_add_ldapmod(&smods, + slapi_mod_get_ldapmod_passout(&lc_smod)); + } + slapi_pblock_set (pb, SLAPI_MODIFY_MODS, + (void*)slapi_mods_get_ldapmods_passout (&smods)); + slapi_mods_done(&unhashed_pw_smod); /* can finalize now */ + } + + slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, &rc); + plugin_call_plugins(pb, internal_op ? SLAPI_PLUGIN_INTERNAL_POST_MODIFY_FN : + SLAPI_PLUGIN_POST_MODIFY_FN); + + } + +free_and_return: + slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &ecopy); + slapi_entry_free(ecopy); + slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &ecopy); + slapi_entry_free(ecopy); + slapi_entry_free(e); + + if (be) + slapi_be_Unlock(be); + slapi_sdn_done(&sdn); + + if (unhashed_pw_attr) + slapi_ch_free ((void**)&unhashed_pw_attr); +} + +static void remove_mod (Slapi_Mods *smods, const char *type, Slapi_Mods *smod_unhashed) +{ + LDAPMod *mod; + Slapi_Mod smod; + + for (mod = slapi_mods_get_first_mod(smods); mod; mod = slapi_mods_get_next_mod(smods)) + { + if (strcasecmp (mod->mod_type, type) == 0) + { + slapi_mod_init_byval (&smod, mod); + slapi_mods_add_smod(smod_unhashed, &smod); + slapi_mods_remove (smods); + } + } +} + +static int op_shared_allow_pw_change (Slapi_PBlock *pb, LDAPMod *mod, char **old_pw) +{ + int isroot, internal_op, repl_op, pwresponse_req = 0; + char *dn; + Slapi_DN sdn; + passwdPolicy *pwpolicy; + int rc = 0; + char ebuf[BUFSIZ]; + Slapi_Value **values= NULL; + Slapi_Operation *operation; + + slapi_pblock_get (pb, SLAPI_IS_REPLICATED_OPERATION, &repl_op); + if (repl_op) { + /* Treat like there's no password */ + return (0); + } + + *old_pw = NULL; + + slapi_pblock_get (pb, SLAPI_ORIGINAL_TARGET, &dn); + slapi_pblock_get (pb, SLAPI_REQUESTOR_ISROOT, &isroot); + slapi_pblock_get (pb, SLAPI_OPERATION, &operation); + slapi_pblock_get (pb, SLAPI_PWPOLICY, &pwresponse_req); + internal_op= operation_is_flag_set(operation, OP_FLAG_INTERNAL); + + + slapi_sdn_init_dn_byref (&sdn, dn); + pwpolicy = new_passwdPolicy(pb, (char *)slapi_sdn_get_ndn(&sdn)); + + /* internal operation has root permisions for subtrees it is allowed to access */ + if (!internal_op) + { + /* Check first if password policy allows users to change their passwords.*/ + if (!pb->pb_op->o_isroot && slapi_sdn_compare(&sdn, &pb->pb_op->o_sdn)==0 && + !pb->pb_conn->c_needpw && !pwpolicy->pw_change) + { + if ( pwresponse_req == 1 ) { + pwpolicy_make_response_control ( pb, -1, -1, LDAP_PWPOLICY_PWDMODNOTALLOWED ); + } + send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL, + "user is not allowed to change password", 0, NULL); + + if (operation_is_flag_set(operation,OP_FLAG_ACTION_LOG_ACCESS)) + { + slapi_log_access(LDAP_DEBUG_STATS, "conn=%d op=%d MOD dn=\"%s\", %s\n", + pb->pb_conn->c_connid, pb->pb_op->o_opid, + escape_string(slapi_sdn_get_dn(&sdn), ebuf), + "user is not allowed to change password"); + } + + rc = -1; + goto done; + } + } + + /* check if password is within password minimum age; + error result is sent directly from check_pw_minage */ + if ((internal_op || !pb->pb_conn->c_needpw) && + check_pw_minage(pb, &sdn, mod->mod_bvalues) == 1) + { + if (operation_is_flag_set(operation,OP_FLAG_ACTION_LOG_ACCESS)) + { + if ( !internal_op ) + { + slapi_log_access(LDAP_DEBUG_STATS, "conn=%d op=%d MOD dn=\"%s\", %s\n", + pb->pb_conn->c_connid, + pb->pb_op->o_opid, + escape_string(slapi_sdn_get_dn(&sdn), ebuf), + "within password minimum age"); + } + else + { + slapi_log_access(LDAP_DEBUG_ARGS, "conn=%s op=%d MOD dn=\"%s\", %s\n", + LOG_INTERNAL_OP_CON_ID, + LOG_INTERNAL_OP_OP_ID, + escape_string(slapi_sdn_get_dn(&sdn), ebuf), + "within password minimum age"); + } + } + + rc = -1; + goto done; + } + + + /* check password syntax; remember the old password; + error sent directly from check_pw_syntax function */ + valuearray_init_bervalarray(mod->mod_bvalues, &values); + switch (check_pw_syntax (pb, &sdn, values, old_pw, NULL, 1)) + { + case 0: /* success */ + rc = 1; + break; + + case 1: /* failed checking */ + if (operation_is_flag_set(operation,OP_FLAG_ACTION_LOG_ACCESS)) + { + if ( !internal_op ) + { + slapi_log_access(LDAP_DEBUG_STATS, "conn=%d op=%d MOD dn=\"%s\", %s\n", + pb->pb_conn->c_connid, + pb->pb_op->o_opid, + escape_string(slapi_sdn_get_dn(&sdn), ebuf), "invalid password syntax"); + } + else + { + slapi_log_access(LDAP_DEBUG_ARGS, "conn=%s op=%d MOD dn=\"%s\", %s\n", + LOG_INTERNAL_OP_CON_ID, + LOG_INTERNAL_OP_OP_ID, + escape_string(slapi_sdn_get_dn(&sdn), ebuf), "invalid password syntax"); + } + } + rc = -1; + break; + + case -1: /* The entry is not found. No password checking is done. Countinue execution + and it should get caught later and send "no such object back. */ + rc = 0; + break; + + default: break; + } + valuearray_free(&values); + +done: + slapi_sdn_done (&sdn); + delete_passwdPolicy(&pwpolicy); + return rc; +} |