static int slapi_int_search_entry_callback( Slapi_Entry *entry, void *callback_data ) { int nentries = 0, i = 0; Slapi_Entry **head = NULL, **tp; Slapi_PBlock *pb = (Slapi_PBlock *)callback_data; PBLOCK_ASSERT_INTOP( pb, LDAP_REQ_SEARCH ); entry = slapi_entry_dup( entry ); if ( entry == NULL ) { return LDAP_NO_MEMORY; } slapi_pblock_get( pb, SLAPI_NENTRIES, &nentries ); slapi_pblock_get( pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &head ); i = nentries + 1; if ( nentries == 0 ) { tp = (Slapi_Entry **)slapi_ch_malloc( 2 * sizeof(Slapi_Entry *) ); if ( tp == NULL ) { slapi_entry_free( entry ); return LDAP_NO_MEMORY; } tp[0] = entry; } else { tp = (Slapi_Entry **)slapi_ch_realloc( (char *)head, sizeof(Slapi_Entry *) * ( i + 1 ) ); if ( tp == NULL ) { slapi_entry_free( entry ); return LDAP_NO_MEMORY; } tp[i - 1] = entry; } tp[i] = NULL; slapi_pblock_set( pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, (void *)tp ); slapi_pblock_set( pb, SLAPI_NENTRIES, (void *)&i ); return LDAP_SUCCESS; }
/* * Check if we are modifying the config, or changing the shared config entry */ int memberof_shared_config_validate(Slapi_PBlock *pb) { Slapi_Entry *e = 0; Slapi_Entry *resulting_e = 0; Slapi_Entry *config_entry = NULL; Slapi_DN *sdn = NULL; Slapi_DN *config_sdn = NULL; Slapi_Mods *smods = 0; Slapi_Mod *smod = NULL, *nextmod = NULL; LDAPMod **mods = NULL; char returntext[SLAPI_DSE_RETURNTEXT_SIZE]; char *configarea_dn = NULL; int ret = SLAPI_PLUGIN_SUCCESS; slapi_pblock_get(pb, SLAPI_TARGET_SDN, &sdn); if (slapi_sdn_compare(sdn, memberof_get_plugin_area()) == 0 || slapi_sdn_compare(sdn, memberof_get_config_area()) == 0) { slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &e); if(e){ /* * Create a copy of the entry and apply the * mods to create the resulting entry. */ slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods); smods = slapi_mods_new(); slapi_mods_init_byref(smods, mods); resulting_e = slapi_entry_dup(e); if (mods && (slapi_entry_apply_mods(resulting_e, mods) != LDAP_SUCCESS)) { /* we don't care about this, the update is invalid and will be caught later */ goto bail; } if (slapi_sdn_compare(sdn, memberof_get_plugin_area())){ /* * This entry is a plugin config area entry, validate it. */ if( SLAPI_DSE_CALLBACK_ERROR == memberof_validate_config (pb, NULL, resulting_e, &ret, returntext,0)) { ret = LDAP_UNWILLING_TO_PERFORM; } } else { /* * This is the memberOf plugin entry, check if we are adding/replacing the * plugin config area. */ nextmod = slapi_mod_new(); for (smod = slapi_mods_get_first_smod(smods, nextmod); smod != NULL; smod = slapi_mods_get_next_smod(smods, nextmod) ) { if ( PL_strcasecmp(SLAPI_PLUGIN_SHARED_CONFIG_AREA, slapi_mod_get_type(smod)) == 0 ) { /* * Okay, we are modifying the plugin config area, we only care about * adds and replaces. */ if(SLAPI_IS_MOD_REPLACE(slapi_mod_get_operation(smod)) || SLAPI_IS_MOD_ADD(slapi_mod_get_operation(smod))) { struct berval *bv = NULL; int rc = 0; bv = slapi_mod_get_first_value(smod); configarea_dn = slapi_ch_strdup(bv->bv_val); if(configarea_dn){ /* Check the DN syntax */ rc = slapi_dn_syntax_check(pb, configarea_dn, 1); if (rc) { /* syntax check failed */ PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, "%s does not contain a valid DN (%s)", SLAPI_PLUGIN_SHARED_CONFIG_AREA, configarea_dn); ret = LDAP_UNWILLING_TO_PERFORM; goto bail; } /* Check if the plugin config area entry exists */ if((config_sdn = slapi_sdn_new_dn_byval(configarea_dn))){ rc = slapi_search_internal_get_entry(config_sdn, NULL, &config_entry, memberof_get_plugin_id()); if(config_entry){ int err = 0; /* * Validate the settings from the new config area. */ if ( memberof_validate_config(pb, NULL, config_entry, &err, returntext,0) == SLAPI_DSE_CALLBACK_ERROR ) { ret = LDAP_UNWILLING_TO_PERFORM; goto bail; } } else { /* The config area does not exist */ PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, "Unable to locate shared config entry (%s) error %d", slapi_sdn_get_dn(memberof_get_config_area()), rc); ret = LDAP_UNWILLING_TO_PERFORM; goto bail; } } } slapi_ch_free_string(&configarea_dn); slapi_sdn_free(&config_sdn); } } } } } else { PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,"Unable to locate shared config entry (%s)", slapi_sdn_get_dn(memberof_get_config_area())); ret = LDAP_UNWILLING_TO_PERFORM; } } bail: if (ret){ slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ret); slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, returntext); slapi_log_err(SLAPI_LOG_ERR, MEMBEROF_PLUGIN_SUBSYSTEM, "memberof_shared_config_validate - %s/n", returntext); } slapi_sdn_free(&config_sdn); if(nextmod) slapi_mod_free(&nextmod); slapi_mods_free(&smods); slapi_entry_free(resulting_e); slapi_entry_free(config_entry); slapi_ch_free_string(&configarea_dn); return ret; }
/* Code shared between regular and internal add operation */ static void op_shared_add (Slapi_PBlock *pb) { Slapi_Operation *operation; Slapi_Entry *e, *pse; Slapi_Backend *be = NULL; int err; int internal_op, repl_op, legacy_op, lastmod; char *pwdtype = NULL; Slapi_Attr *attr = NULL; Slapi_Entry *referral; char errorbuf[SLAPI_DSE_RETURNTEXT_SIZE]; struct slapdplugin *p = NULL; char *proxydn = NULL; char *proxystr = NULL; int proxy_err = LDAP_SUCCESS; char *errtext = NULL; Slapi_DN *sdn = NULL; passwdPolicy *pwpolicy; slapi_pblock_get (pb, SLAPI_OPERATION, &operation); slapi_pblock_get (pb, SLAPI_ADD_ENTRY, &e); slapi_pblock_get (pb, SLAPI_IS_REPLICATED_OPERATION, &repl_op); slapi_pblock_get (pb, SLAPI_IS_LEGACY_REPLICATED_OPERATION, &legacy_op); internal_op= operation_is_flag_set(operation, OP_FLAG_INTERNAL); pwpolicy = new_passwdPolicy(pb, slapi_entry_get_dn(e)); /* target spec is used to decide which plugins are applicable for the operation */ operation_set_target_spec (operation, slapi_entry_get_sdn (e)); if ((err = slapi_entry_add_rdn_values(e)) != LDAP_SUCCESS) { send_ldap_result(pb, err, NULL, "failed to add RDN values", 0, NULL); goto done; } /* get the proxy auth dn if the proxy auth control is present */ proxy_err = proxyauth_get_dn(pb, &proxydn, &errtext); if (operation_is_flag_set(operation,OP_FLAG_ACTION_LOG_ACCESS)) { if (proxydn) { proxystr = slapi_ch_smprintf(" authzid=\"%s\"", proxydn); } if ( !internal_op ) { slapi_log_access(LDAP_DEBUG_STATS, "conn=%" NSPRIu64 " op=%d ADD dn=\"%s\"%s\n", pb->pb_conn->c_connid, operation->o_opid, slapi_entry_get_dn_const(e), proxystr ? proxystr : ""); } else { slapi_log_access(LDAP_DEBUG_ARGS, "conn=%s op=%d ADD dn=\"%s\"\n", LOG_INTERNAL_OP_CON_ID, LOG_INTERNAL_OP_OP_ID, slapi_entry_get_dn_const(e)); } } /* If we encountered an error parsing the proxy control, return an error * to the client. We do this here to ensure that we log the operation first. */ if (proxy_err != LDAP_SUCCESS) { send_ldap_result(pb, proxy_err, NULL, errtext, 0, NULL); goto done; } /* * We could be serving multiple database backends. Select the * appropriate one. */ if ((err = slapi_mapping_tree_select(pb, &be, &referral, errorbuf, sizeof(errorbuf))) != LDAP_SUCCESS) { send_ldap_result(pb, err, NULL, errorbuf, 0, NULL); be = NULL; goto done; } 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 done; } slapi_pblock_set(pb, SLAPI_TARGET_SDN, (void*)operation_get_target_spec (operation)); send_referrals_from_entry(pb,referral); slapi_entry_free(referral); goto done; } if (!slapi_be_is_flag_set(be,SLAPI_BE_FLAG_REMOTE_DATA)) { Slapi_Value **unhashed_password_vals = NULL; Slapi_Value **present_values = NULL; /* Setting unhashed password to the entry extension. */ if (repl_op) { /* replicated add ==> get unhashed pw from entry, if any. * set it to the extension */ slapi_entry_attr_find(e, PSEUDO_ATTR_UNHASHEDUSERPASSWORD, &attr); if (attr) { present_values = attr_get_present_values(attr); valuearray_add_valuearray(&unhashed_password_vals, present_values, 0); #if !defined(USE_OLD_UNHASHED) /* and remove it from the entry. */ slapi_entry_attr_delete(e, PSEUDO_ATTR_UNHASHEDUSERPASSWORD); #endif } } else { /* ordinary add ==> * get unhashed pw from userpassword before encrypting it */ /* look for user password attribute */ slapi_entry_attr_find(e, SLAPI_USERPWD_ATTR, &attr); if (attr) { Slapi_Value **vals = NULL; /* Set the backend in the pblock. * The slapi_access_allowed function * needs this set to work properly. */ slapi_pblock_set(pb, SLAPI_BACKEND, slapi_be_select(slapi_entry_get_sdn_const(e))); /* Check ACI before checking password syntax */ if ((err = slapi_access_allowed(pb, e, SLAPI_USERPWD_ATTR, NULL, SLAPI_ACL_ADD)) != LDAP_SUCCESS) { send_ldap_result(pb, err, NULL, "Insufficient 'add' privilege to the " "'userPassword' attribute", 0, NULL); goto done; } /* * Check password syntax, unless this is a pwd admin/rootDN */ present_values = attr_get_present_values(attr); if (!pw_is_pwp_admin(pb, pwpolicy) && check_pw_syntax(pb, slapi_entry_get_sdn_const(e), present_values, NULL, e, 0) != 0) { /* error result is sent from check_pw_syntax */ goto done; } /* pw syntax is valid */ valuearray_add_valuearray(&unhashed_password_vals, present_values, 0); valuearray_add_valuearray(&vals, present_values, 0); pw_encodevals_ext(pb, slapi_entry_get_sdn (e), vals); add_password_attrs(pb, operation, e); slapi_entry_attr_replace_sv(e, SLAPI_USERPWD_ATTR, vals); valuearray_free(&vals); #if defined(USE_OLD_UNHASHED) /* Add the unhashed password pseudo-attribute to the entry */ pwdtype = slapi_attr_syntax_normalize(PSEUDO_ATTR_UNHASHEDUSERPASSWORD); slapi_entry_add_values_sv(e, pwdtype, unhashed_password_vals); #endif } } if (unhashed_password_vals && (SLAPD_UNHASHED_PW_OFF != config_get_unhashed_pw_switch())) { /* unhashed_password_vals is consumed if successful. */ err = slapi_pw_set_entry_ext(e, unhashed_password_vals, SLAPI_EXT_SET_ADD); if (err) { valuearray_free(&unhashed_password_vals); } } #if defined(THISISTEST) { /* test code to retrieve an unhashed pw from the entry extention & * PSEUDO_ATTR_UNHASHEDUSERPASSWORD attribute */ char *test_str = slapi_get_first_clear_text_pw(e); if (test_str) { slapi_log_err(SLAPI_LOG_ERR, "Value from extension: %s\n", test_str); slapi_ch_free_string(&test_str); } #if defined(USE_OLD_UNHASHED) test_str = slapi_entry_attr_get_charptr(e, PSEUDO_ATTR_UNHASHEDUSERPASSWORD); if (test_str) { slapi_log_err(SLAPI_LOG_ERR, "Value from attr: %s\n", test_str); slapi_ch_free_string(&test_str); } #endif /* USE_OLD_UNHASHED */ } #endif /* THISISTEST */ /* look for multiple backend local credentials or replication local credentials */ for ( p = get_plugin_list(PLUGIN_LIST_REVER_PWD_STORAGE_SCHEME); p != NULL && !repl_op; p = p->plg_next ) { char *L_attr = NULL; int i=0; /* Get the appropriate decoding function */ for ( L_attr = p->plg_argv[i]; i<p->plg_argc; L_attr = p->plg_argv[++i]) { /* look for multiple backend local credentials or replication local credentials */ char *L_normalized = slapi_attr_syntax_normalize(L_attr); slapi_entry_attr_find(e, L_normalized, &attr); if (attr) { Slapi_Value **present_values = NULL; Slapi_Value **vals = NULL; present_values= attr_get_present_values(attr); valuearray_add_valuearray(&vals, present_values, 0); pw_rever_encode(vals, L_normalized); slapi_entry_attr_replace_sv(e, L_normalized, vals); valuearray_free(&vals); } if (L_normalized) slapi_ch_free ((void**)&L_normalized); } } } slapi_pblock_set(pb, SLAPI_BACKEND, be); if (!repl_op) { /* can get lastmod only after backend is selected */ slapi_pblock_get(pb, SLAPI_BE_LASTMOD, &lastmod); if (lastmod && add_created_attrs(pb, e) != 0) { send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL, "cannot insert computed attributes", 0, NULL); goto done; } /* expand objectClass values to reflect the inheritance hierarchy */ slapi_schema_expand_objectclasses( e ); } /* uniqueid needs to be generated for entries added during legacy replication */ if (legacy_op){ if (add_uniqueid(e) != UID_SUCCESS) { send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL, "cannot insert computed attributes", 0, NULL); goto done; } } /* * call the pre-add plugins. if they succeed, call * the backend add function. then call the post-add * plugins. */ sdn = slapi_sdn_dup(slapi_entry_get_sdn_const(e)); slapi_pblock_set(pb, SLAPI_ADD_TARGET_SDN, (void *)sdn); if (plugin_call_plugins(pb, internal_op ? SLAPI_PLUGIN_INTERNAL_PRE_ADD_FN : SLAPI_PLUGIN_PRE_ADD_FN) == SLAPI_PLUGIN_SUCCESS) { int rc; Slapi_Entry *ec; Slapi_DN *add_target_sdn = NULL; Slapi_Entry *save_e = NULL; slapi_pblock_set(pb, SLAPI_PLUGIN, be->be_database); set_db_default_result_handlers(pb); /* because be_add frees the entry */ ec = slapi_entry_dup(e); add_target_sdn = slapi_sdn_dup(slapi_entry_get_sdn_const(ec)); slapi_pblock_get(pb, SLAPI_ADD_TARGET_SDN, &sdn); slapi_sdn_free(&sdn); slapi_pblock_set(pb, SLAPI_ADD_TARGET_SDN, add_target_sdn); if (be->be_add != NULL) { rc = (*be->be_add)(pb); /* backend may change this if errors and not consumed */ slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &save_e); slapi_pblock_set(pb, SLAPI_ADD_ENTRY, ec); if (rc == 0) { /* acl is not enabled 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_ADD); } if (operation_is_flag_set(operation,OP_FLAG_ACTION_LOG_AUDIT)) { write_audit_log_entry(pb); /* Record the operation in the audit log */ } slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &pse); do_ps_service(pse, NULL, LDAP_CHANGETYPE_ADD, 0); /* * If be_add succeeded, then e is consumed except the resurrect case. * If it is resurrect, the corresponding tombstone entry is resurrected * and put into the cache. * Otherwise, we set e to NULL to prevent freeing it ourselves. */ if (operation_is_flag_set(operation,OP_FLAG_RESURECT_ENTRY) && save_e) { e = save_e; } else { e = NULL; } } else { /* PR_ASSERT(!save_e); save_e is supposed to be freed in the backend. */ e = save_e; if (rc == SLAPI_FAIL_DISKFULL) { operation_out_of_disk_space(); goto done; } /* If the disk is full we don't want to make it worse ... */ if (operation_is_flag_set(operation,OP_FLAG_ACTION_LOG_AUDIT)) { write_auditfail_log_entry(pb); /* Record the operation in the audit log */ } } } else { send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL, "Function not implemented", 0, NULL); } slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, &rc); plugin_call_plugins(pb, internal_op ? SLAPI_PLUGIN_INTERNAL_POST_ADD_FN : SLAPI_PLUGIN_POST_ADD_FN); slapi_entry_free(ec); } slapi_pblock_get(pb, SLAPI_ADD_TARGET_SDN, &sdn); slapi_sdn_free(&sdn); done: if (be) slapi_be_Unlock(be); slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &pse); slapi_entry_free(pse); slapi_ch_free((void **)&operation->o_params.p.p_add.parentuniqueid); slapi_entry_free(e); slapi_pblock_set(pb, SLAPI_ADD_ENTRY, NULL); slapi_ch_free((void**)&pwdtype); slapi_ch_free_string(&proxydn); slapi_ch_free_string(&proxystr); }
/* * Perform the agreement/domain specific configuration. * IPA stores its configuration in the tree. We use the * ds_subtree to search for the domain/realm specific * configuration entries. */ void ipa_winsync_config_refresh_domain( void *cbdata, const Slapi_DN *ds_subtree, const Slapi_DN *ad_subtree ) { IPA_WinSync_Domain_Config *iwdc = (IPA_WinSync_Domain_Config *)cbdata; Slapi_DN *config_dn = slapi_sdn_dup(ds_subtree); char *realm_filter = NULL; char *realm_attr = NULL; char *new_entry_filter = NULL; char *new_user_oc_attr = NULL; /* don't care about groups for now */ char *homedir_prefix_attr = NULL; char *login_shell_attr = NULL; char *default_group_attr = NULL; char *default_group_filter = NULL; char *default_group_name = NULL; char *real_group_filter = NULL; char *default_gid = NULL; Slapi_ValueSet *new_user_objclasses = NULL; /* don't care about groups for now */ int loopdone = 0; int search_scope = LDAP_SCOPE_SUBTREE; int ret = LDAP_SUCCESS; int acct_disable; char *inactivated_filter = NULL; char *activated_filter = NULL; char *inactivated_group_dn = NULL; char *activated_group_dn = NULL; int upg = -1; slapi_lock_mutex(theConfig.lock); realm_filter = slapi_ch_strdup(theConfig.realm_filter); realm_attr = slapi_ch_strdup(theConfig.realm_attr); new_entry_filter = slapi_ch_strdup(theConfig.new_entry_filter); new_user_oc_attr = slapi_ch_strdup(theConfig.new_user_oc_attr); homedir_prefix_attr = slapi_ch_strdup(theConfig.homedir_prefix_attr); if (theConfig.login_shell_attr) { login_shell_attr = slapi_ch_strdup(theConfig.login_shell_attr); } default_group_attr = slapi_ch_strdup(theConfig.default_group_attr); default_group_filter = slapi_ch_strdup(theConfig.default_group_filter); acct_disable = theConfig.acct_disable; if (acct_disable != ACCT_DISABLE_NONE) { if (theConfig.inactivated_filter) { inactivated_filter = slapi_ch_strdup(theConfig.inactivated_filter); } if (theConfig.activated_filter) { activated_filter = slapi_ch_strdup(theConfig.activated_filter); } } slapi_unlock_mutex(theConfig.lock); /* starting at ds_subtree, search for the entry containing the Kerberos realm to use */ slapi_ch_free_string(&iwdc->realm_name); while(!loopdone && !slapi_sdn_isempty(config_dn)) { ret = internal_find_entry_get_attr_val(config_dn, search_scope, realm_filter, realm_attr, NULL, &iwdc->realm_name); if ((0 == ret) && iwdc->realm_name) { loopdone = 1; } else if ((LDAP_NO_SUCH_OBJECT == ret) && !iwdc->realm_name) { /* try again */ Slapi_DN *parent_dn = slapi_sdn_new(); slapi_sdn_get_parent(config_dn, parent_dn); slapi_sdn_free(&config_dn); config_dn = parent_dn; } else { /* error */ goto out; } } if (!iwdc->realm_name) { /* error - could not find the IPA config entry with the realm name */ LOG_FATAL("Error: could not find the entry containing the realm name " "[%d] ds subtree [%s] filter [%s] attr [%s]\n", ret, slapi_sdn_get_dn(ds_subtree), realm_filter, realm_attr); goto out; } /* look for the entry containing the default objectclasses to add to new entries */ ret = internal_find_entry_get_attr_val(config_dn, search_scope, new_entry_filter, new_user_oc_attr, &new_user_objclasses, NULL); if (!new_user_objclasses) { /* error - could not find the entry containing list of objectclasses */ LOG_FATAL("Error: could not find the entry containing the new user objectclass list " "[%d] ds subtree [%s] filter [%s] attr [%s]\n", ret, slapi_sdn_get_dn(ds_subtree), new_entry_filter, new_user_oc_attr); goto out; } /* get the home directory prefix value */ /* note - this is in the same entry as the new entry template, so use the same filter */ slapi_ch_free_string(&iwdc->homedir_prefix); ret = internal_find_entry_get_attr_val(config_dn, search_scope, new_entry_filter, homedir_prefix_attr, NULL, &iwdc->homedir_prefix); if (!iwdc->homedir_prefix) { /* error - could not find the home dir prefix */ LOG_FATAL("Error: could not find the entry containing the home directory prefix " "[%d] ds subtree [%s] filter [%s] attr [%s]\n", ret, slapi_sdn_get_dn(ds_subtree), new_entry_filter, homedir_prefix_attr); goto out; } /* get the login shell value */ /* note - this is in the same entry as the new entry template, so use the same filter */ slapi_ch_free_string(&iwdc->login_shell); if (login_shell_attr) { ret = internal_find_entry_get_attr_val(config_dn, search_scope, new_entry_filter, login_shell_attr, NULL, &iwdc->login_shell); if (!iwdc->login_shell) { LOG("Warning: could not find the entry containing the login shell " "attribute [%d] ds subtree [%s] filter [%s] attr [%s]\n", ret, slapi_sdn_get_dn(ds_subtree), new_entry_filter, login_shell_attr); } } if (!iwdc->login_shell) { /* could not find the login shell or was not configured */ LOG("Warning: no login shell configured!"); } /* find the default group - the entry above contains the group name, but we need the gidNumber for posixAccount - so first find the entry and attr value which has the group name, then lookup the group number from the group name */ ret = internal_find_entry_get_attr_val(config_dn, search_scope, new_entry_filter, default_group_attr, NULL, &default_group_name); if (!default_group_name) { /* error - could not find the default group name */ LOG_FATAL("Error: could not find the entry containing the default group name " "[%d] ds subtree [%s] filter [%s] attr [%s]\n", ret, slapi_sdn_get_dn(ds_subtree), new_entry_filter, default_group_attr); goto out; } /* check if User Private Groups are enabled */ upg = ipa_winsync_upg_enabled(ds_subtree); /* next, find the group whose name is default_group_name - construct the filter based on the filter attribute value - assumes the group name is stored in the cn attribute value, and the gidNumber in the gidNumber attribute value */ real_group_filter = slapi_ch_smprintf("(&(cn=%s)%s)", default_group_name, default_group_filter); ret = internal_find_entry_get_attr_val(config_dn, search_scope, real_group_filter, "gidNumber", NULL, &default_gid); if (!default_gid) { /* error - could not find the default gidNumber This is not a fatal error if User Private Groups (UPG) are enabled. */ if (upg) { LOG_FATAL("Error: could not find the entry containing the default gidNumber " "UPG [%d] ds subtree [%s] filter [%s] attr [%s]\n", ret, slapi_sdn_get_dn(ds_subtree), new_entry_filter, "gidNumber"); goto out; } else { ret = LDAP_SUCCESS; } } /* If we are syncing account disable, we need to find the groups used to denote active and inactive users e.g. dn: cn=inactivated,cn=account inactivation,cn=accounts,$SUFFIX dn: cn=Activated,cn=Account Inactivation,cn=accounts,$SUFFIX */ if (acct_disable != ACCT_DISABLE_NONE) { if (inactivated_filter) { ret = internal_find_entry_get_attr_val(config_dn, search_scope, inactivated_filter, "dn", NULL, &inactivated_group_dn); if (!inactivated_group_dn) { /* error - could not find the inactivated group dn */ LOG("Could not find the DN of the inactivated users group " "[%d] ds subtree [%s] filter [%s]. Ignoring\n", ret, slapi_sdn_get_dn(ds_subtree), inactivated_filter); goto out; } } if (activated_filter) { ret = internal_find_entry_get_attr_val(config_dn, search_scope, activated_filter, "dn", NULL, &activated_group_dn); if (!activated_group_dn) { /* error - could not find the activated group dn */ LOG("Could not find the DN of the activated users group " "[%d] ds subtree [%s] filter [%s]. Ignoring\n", ret, slapi_sdn_get_dn(ds_subtree), activated_filter); goto out; } } } /* ok, we have our values */ /* first, clear out the old domain config */ slapi_entry_free(iwdc->domain_e); iwdc->domain_e = NULL; /* next, copy the global attr config */ slapi_lock_mutex(theConfig.lock); iwdc->domain_e = slapi_entry_dup(theConfig.config_e); slapi_unlock_mutex(theConfig.lock); /* set the objectclasses in the domain_e */ slapi_entry_attr_delete(iwdc->domain_e, "objectclass"); /* this copies new_user_objclasses */ slapi_entry_add_valueset(iwdc->domain_e, "objectclass", new_user_objclasses); /* When UPG is disabled, set the default gid number */ if (upg && default_gid) { slapi_entry_attr_set_charptr(iwdc->domain_e, "gidNumber", default_gid); } slapi_ch_free_string(&iwdc->inactivated_group_dn); iwdc->inactivated_group_dn = inactivated_group_dn; inactivated_group_dn = NULL; slapi_ch_free_string(&iwdc->activated_group_dn); iwdc->activated_group_dn = activated_group_dn; activated_group_dn = NULL; out: slapi_valueset_free(new_user_objclasses); slapi_sdn_free(&config_dn); slapi_ch_free_string(&realm_filter); slapi_ch_free_string(&realm_attr); slapi_ch_free_string(&new_entry_filter); slapi_ch_free_string(&new_user_oc_attr); slapi_ch_free_string(&homedir_prefix_attr); slapi_ch_free_string(&login_shell_attr); slapi_ch_free_string(&default_group_attr); slapi_ch_free_string(&default_group_filter); slapi_ch_free_string(&default_group_name); slapi_ch_free_string(&real_group_filter); slapi_ch_free_string(&default_gid); slapi_ch_free_string(&inactivated_filter); slapi_ch_free_string(&inactivated_group_dn); slapi_ch_free_string(&activated_filter); slapi_ch_free_string(&activated_group_dn); if (LDAP_SUCCESS != ret) { slapi_ch_free_string(&iwdc->realm_name); slapi_ch_free_string(&iwdc->homedir_prefix); slapi_ch_free_string(&iwdc->login_shell); slapi_entry_free(iwdc->domain_e); iwdc->domain_e = NULL; } return; }
/* * An URP Naming Collision helper function. Retreives a list of entries * that have the given dn excluding the unique id of the entry. Any * entries returned will be entries that have been added with the same * dn, but caused a naming conflict when replicated. The URP to fix * this constraint violation is to append the unique id of the entry * to its RDN. */ static Slapi_Entry * urp_get_min_naming_conflict_entry ( Slapi_PBlock *pb, char *sessionid, CSN *opcsn ) { Slapi_PBlock *newpb = NULL; LDAPControl **server_ctrls = NULL; Slapi_Entry **entries = NULL; Slapi_Entry *min_naming_conflict_entry = NULL; const CSN *min_csn = NULL; char *filter = NULL; char *parent_dn = NULL; char *basedn; int i = 0; int min_i = -1; int op_result = LDAP_SUCCESS; slapi_pblock_get (pb, SLAPI_URP_NAMING_COLLISION_DN, &basedn); if (NULL == basedn || strncmp (basedn, SLAPI_ATTR_UNIQUEID, strlen(SLAPI_ATTR_UNIQUEID)) == 0) return NULL; slapi_log_error ( SLAPI_LOG_REPL, sessionid, "Enter urp_get_min_naming_conflict_entry for %s\n", basedn); filter = slapi_filter_sprintf("(%s=%s %s%s)", ATTR_NSDS5_REPLCONFLICT, REASON_ANNOTATE_DN, ESC_NEXT_VAL, basedn); /* server_ctrls will be freed when newpb is destroyed */ server_ctrls = (LDAPControl **)slapi_ch_calloc (2, sizeof (LDAPControl *)); server_ctrls[0] = create_managedsait_control(); server_ctrls[1] = NULL; newpb = slapi_pblock_new(); parent_dn = slapi_dn_parent (basedn); slapi_search_internal_set_pb(newpb, parent_dn, /* Base DN */ LDAP_SCOPE_ONELEVEL, filter, NULL, /* Attrs */ 0, /* AttrOnly */ server_ctrls, /* Controls */ NULL, /* UniqueID */ repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION), 0); slapi_search_internal_pb(newpb); slapi_pblock_get(newpb, SLAPI_PLUGIN_INTOP_RESULT, &op_result); slapi_pblock_get(newpb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries); if ( (op_result != LDAP_SUCCESS) || (entries == NULL) ) { /* Log a message */ goto done; } /* For all entries, get the one with the smallest dn csn */ for (i = 0; NULL != entries[i]; i++) { const CSN *dncsn; dncsn = entry_get_dncsn(entries[i]); if ((dncsn != opcsn) && ((min_csn == NULL) || (csn_compare(dncsn, min_csn) < 0)) && !is_tombstone_entry (entries[i])) { min_csn = dncsn; min_i = i; } /* * If there are too many conflicts, the current urp code has no * guarantee for all servers to converge anyway, because the * urp and the backend can't be done in one transaction due * to either performance or the deadlock problem. * Don't sacrifice the performance too much for impossible. */ if (min_csn && i > 5) { break; } } if (min_csn != NULL) { /* Found one entry */ min_naming_conflict_entry = slapi_entry_dup(entries[min_i]); } done: slapi_ch_free_string(&parent_dn); if (filter) { PR_smprintf_free(filter); } slapi_free_search_results_internal(newpb); slapi_pblock_destroy(newpb); newpb = NULL; slapi_log_error ( SLAPI_LOG_REPL, sessionid, "Leave urp_get_min_naming_conflict_entry (found %d entries)\n", i); return min_naming_conflict_entry; }
int ldbm_back_modify( Slapi_PBlock *pb ) { backend *be; ldbm_instance *inst = NULL; struct ldbminfo *li; struct backentry *e = NULL, *ec = NULL; struct backentry *original_entry = NULL, *tmpentry = NULL; Slapi_Entry *postentry = NULL; LDAPMod **mods = NULL; LDAPMod **mods_original = NULL; Slapi_Mods smods = {0}; back_txn txn; back_txnid parent_txn; modify_context ruv_c = {0}; int ruv_c_init = 0; int retval = -1; char *msg; char *errbuf = NULL; int retry_count = 0; int disk_full = 0; int ldap_result_code= LDAP_SUCCESS; char *ldap_result_message= NULL; int rc = 0; Slapi_Operation *operation; entry_address *addr; int is_fixup_operation= 0; int is_ruv = 0; /* True if the current entry is RUV */ CSN *opcsn = NULL; int repl_op; int opreturn = 0; int mod_count = 0; int not_an_error = 0; int fixup_tombstone = 0; int ec_locked = 0; int result_sent = 0; slapi_pblock_get( pb, SLAPI_BACKEND, &be); slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li ); slapi_pblock_get( pb, SLAPI_TARGET_ADDRESS, &addr ); slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods ); slapi_pblock_get( pb, SLAPI_TXN, (void**)&parent_txn ); slapi_pblock_get( pb, SLAPI_IS_REPLICATED_OPERATION, &repl_op); slapi_pblock_get( pb, SLAPI_OPERATION, &operation ); fixup_tombstone = operation_is_flag_set(operation, OP_FLAG_TOMBSTONE_FIXUP); dblayer_txn_init(li,&txn); /* must do this before first goto error_return */ /* the calls to perform searches require the parent txn if any so set txn to the parent_txn until we begin the child transaction */ if (parent_txn) { txn.back_txn_txn = parent_txn; } else { parent_txn = txn.back_txn_txn; slapi_pblock_set( pb, SLAPI_TXN, parent_txn ); } if (NULL == operation) { ldap_result_code = LDAP_OPERATIONS_ERROR; goto error_return; } is_fixup_operation = operation_is_flag_set(operation, OP_FLAG_REPL_FIXUP); is_ruv = operation_is_flag_set(operation, OP_FLAG_REPL_RUV); inst = (ldbm_instance *) be->be_instance_info; if (NULL == addr) { goto error_return; } if (inst && inst->inst_ref_count) { slapi_counter_increment(inst->inst_ref_count); } else { slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify", "Instance \"%s\" does not exist.\n", inst ? inst->inst_name : "null instance"); goto error_return; } /* no need to check the dn syntax as this is a replicated op */ if(!repl_op){ ldap_result_code = slapi_dn_syntax_check(pb, slapi_sdn_get_dn(addr->sdn), 1); if (ldap_result_code) { ldap_result_code = LDAP_INVALID_DN_SYNTAX; slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message); goto error_return; } } /* The dblock serializes writes to the database, * which reduces deadlocking in the db code, * which means that we run faster. * * But, this lock is re-enterant for the fixup * operations that the URP code in the Replication * plugin generates. * * SERIALLOCK is moved to dblayer_txn_begin along with exposing be * transaction to plugins (see slapi_back_transaction_* APIs). * if(SERIALLOCK(li) && !operation_is_flag_set(operation,OP_FLAG_REPL_FIXUP)) { dblayer_lock_backend(be); dblock_acquired= 1; } */ if ( MANAGE_ENTRY_BEFORE_DBLOCK(li)) { /* find and lock the entry we are about to modify */ if (fixup_tombstone) { e = find_entry2modify_only_ext( pb, be, addr, TOMBSTONE_INCLUDED, &txn, &result_sent ); } else { e = find_entry2modify( pb, be, addr, &txn, &result_sent ); } if (e == NULL) { ldap_result_code = -1; goto error_return; /* error result sent by find_entry2modify() */ } } txn.back_txn_txn = NULL; /* ready to create the child transaction */ for (retry_count = 0; retry_count < RETRY_TIMES; retry_count++) { int cache_rc = 0; int new_mod_count = 0; if (txn.back_txn_txn && (txn.back_txn_txn != parent_txn)) { /* don't release SERIAL LOCK */ dblayer_txn_abort_ext(li, &txn, PR_FALSE); slapi_pblock_set(pb, SLAPI_TXN, parent_txn); /* * Since be_txn_preop functions could have modified the entry/mods, * We need to grab the current mods, free them, and restore the * originals. Same thing for the entry. */ slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods); ldap_mods_free(mods, 1); slapi_pblock_set(pb, SLAPI_MODIFY_MODS, copy_mods(mods_original)); /* reset ec set cache in id2entry_add_ext */ if (ec) { /* must duplicate ec before returning it to cache, * which could free the entry. */ if ((tmpentry = backentry_dup(original_entry?original_entry:ec)) == NULL) { ldap_result_code= LDAP_OPERATIONS_ERROR; goto error_return; } if (cache_is_in_cache(&inst->inst_cache, ec)) { CACHE_REMOVE(&inst->inst_cache, ec); } CACHE_RETURN(&inst->inst_cache, &ec); slapi_pblock_set( pb, SLAPI_MODIFY_EXISTING_ENTRY, original_entry->ep_entry ); ec = original_entry; original_entry = tmpentry; tmpentry = NULL; } if (ruv_c_init) { /* reset the ruv txn stuff */ modify_term(&ruv_c, be); ruv_c_init = 0; } slapi_log_err(SLAPI_LOG_BACKLDBM, "ldbm_back_modify", "Modify Retrying Transaction\n"); #ifndef LDBM_NO_BACKOFF_DELAY { PRIntervalTime interval; interval = PR_MillisecondsToInterval(slapi_rand() % 100); DS_Sleep(interval); } #endif } /* Nothing above here modifies persistent store, everything after here is subject to the transaction */ /* dblayer_txn_begin holds SERIAL lock, * which should be outside of locking the entry (find_entry2modify) */ if (0 == retry_count) { /* First time, hold SERIAL LOCK */ retval = dblayer_txn_begin(be, parent_txn, &txn); } else { /* Otherwise, no SERIAL LOCK */ retval = dblayer_txn_begin_ext(li, parent_txn, &txn, PR_FALSE); } if (0 != retval) { if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1; ldap_result_code= LDAP_OPERATIONS_ERROR; goto error_return; } /* stash the transaction for plugins */ slapi_pblock_set(pb, SLAPI_TXN, txn.back_txn_txn); if (0 == retry_count) { /* just once */ if ( !MANAGE_ENTRY_BEFORE_DBLOCK(li)) { /* find and lock the entry we are about to modify */ if (fixup_tombstone) { e = find_entry2modify_only_ext( pb, be, addr, TOMBSTONE_INCLUDED, &txn, &result_sent ); } else { e = find_entry2modify( pb, be, addr, &txn, &result_sent ); } if (e == NULL) { ldap_result_code = -1; goto error_return; /* error result sent by find_entry2modify() */ } } if ( !is_fixup_operation && !fixup_tombstone) { if (!repl_op && slapi_entry_flag_is_set(e->ep_entry, SLAPI_ENTRY_FLAG_TOMBSTONE)) { ldap_result_code = LDAP_UNWILLING_TO_PERFORM; ldap_result_message = "Operation not allowed on tombstone entry."; slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify", "Attempt to modify a tombstone entry %s\n", slapi_sdn_get_dn(slapi_entry_get_sdn_const( e->ep_entry ))); goto error_return; } opcsn = operation_get_csn (operation); if (NULL == opcsn && operation->o_csngen_handler) { /* * Current op is a user request. Opcsn will be assigned * if the dn is in an updatable replica. */ opcsn = entry_assign_operation_csn ( pb, e->ep_entry, NULL ); } if (opcsn) { entry_set_maxcsn (e->ep_entry, opcsn); } } /* Save away a copy of the entry, before modifications */ slapi_pblock_set( pb, SLAPI_ENTRY_PRE_OP, slapi_entry_dup( e->ep_entry )); if ( (ldap_result_code = plugin_call_acl_mods_access( pb, e->ep_entry, mods, &errbuf)) != LDAP_SUCCESS ) { ldap_result_message= errbuf; goto error_return; } /* create a copy of the entry and apply the changes to it */ if ( (ec = backentry_dup( e )) == NULL ) { ldap_result_code= LDAP_OPERATIONS_ERROR; goto error_return; } if(!repl_op){ remove_illegal_mods(mods); } /* ec is the entry that our bepreop should get to mess with */ slapi_pblock_set( pb, SLAPI_MODIFY_EXISTING_ENTRY, ec->ep_entry ); slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ldap_result_code); opreturn = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_PRE_MODIFY_FN); if (opreturn || (slapi_pblock_get(pb, SLAPI_RESULT_CODE, &ldap_result_code) && ldap_result_code) || (slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &opreturn) && opreturn)) { slapi_pblock_get(pb, SLAPI_RESULT_CODE, &ldap_result_code); slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &opreturn); if (!ldap_result_code) { slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify", "SLAPI_PLUGIN_BE_PRE_MODIFY_FN " "returned error but did not set SLAPI_RESULT_CODE\n"); ldap_result_code = LDAP_OPERATIONS_ERROR; } if (SLAPI_PLUGIN_NOOP == opreturn) { not_an_error = 1; rc = opreturn = LDAP_SUCCESS; } else if (!opreturn) { opreturn = SLAPI_PLUGIN_FAILURE; slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, &opreturn); } slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message); goto error_return; } /* The Plugin may have messed about with some of the PBlock parameters... ie. mods */ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods ); /* apply the mods, check for syntax, schema problems, etc. */ if (modify_apply_check_expand(pb, operation, mods, e, ec, &postentry, &ldap_result_code, &ldap_result_message)) { goto error_return; } /* the schema check could have added a repl conflict mod * get the mods again */ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods ); slapi_mods_init_byref(&smods,mods); mod_count = slapi_mods_get_num_mods(&smods); /* * Grab a copy of the mods and the entry in case the be_txn_preop changes * the them. If we have a failure, then we need to reset the mods to their * their original state; */ mods_original = copy_mods(mods); if ( (original_entry = backentry_dup( ec )) == NULL ) { ldap_result_code= LDAP_OPERATIONS_ERROR; goto error_return; } } /* if (0 == retry_count) just once */ /* call the transaction pre modify plugins just after creating the transaction */ retval = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_TXN_PRE_MODIFY_FN); if (retval) { slapi_log_err(SLAPI_LOG_TRACE, "ldbm_back_modify", "SLAPI_PLUGIN_BE_TXN_PRE_MODIFY_FN plugin " "returned error code %d\n", retval ); slapi_pblock_get(pb, SLAPI_RESULT_CODE, &ldap_result_code); slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &opreturn); if (SLAPI_PLUGIN_NOOP == retval) { not_an_error = 1; rc = retval = LDAP_SUCCESS; } if (!opreturn) { slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, ldap_result_code ? &ldap_result_code : &retval); } slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message); goto error_return; } /* the mods might have been changed, so get the latest */ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods ); /* make sure the betxnpreop did not alter any of the mods that had already previously been applied */ slapi_mods_done(&smods); slapi_mods_init_byref(&smods,mods); new_mod_count = slapi_mods_get_num_mods(&smods); if (new_mod_count < mod_count) { slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify", "Error: BE_TXN_PRE_MODIFY plugin has removed " "mods from the original list - mod count was [%d] now [%d] " "mods will not be applied - mods list changes must be done " "in the BE_PRE_MODIFY plugin, not the BE_TXN_PRE_MODIFY\n", mod_count, new_mod_count ); } else if (new_mod_count > mod_count) { /* apply the new betxnpremod mods */ /* apply the mods, check for syntax, schema problems, etc. */ if (modify_apply_check_expand(pb, operation, &mods[mod_count], e, ec, &postentry, &ldap_result_code, &ldap_result_message)) { goto error_return; } } /* else if new_mod_count == mod_count then betxnpremod plugin did nothing */ /* * Update the ID to Entry index. * Note that id2entry_add replaces the entry, so the Entry ID * stays the same. */ retval = id2entry_add_ext( be, ec, &txn, 1, &cache_rc ); if (DB_LOCK_DEADLOCK == retval) { /* Abort and re-try */ continue; } if (0 != retval) { slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify", "id2entry_add failed, err=%d %s\n", retval, (msg = dblayer_strerror( retval )) ? msg : ""); if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1; MOD_SET_ERROR(ldap_result_code, LDAP_OPERATIONS_ERROR, retry_count); goto error_return; } retval = index_add_mods( be, mods, e, ec, &txn ); if (DB_LOCK_DEADLOCK == retval) { /* Abort and re-try */ continue; } if (0 != retval) { slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify", "index_add_mods failed, err=%d %s\n", retval, (msg = dblayer_strerror( retval )) ? msg : ""); if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1; MOD_SET_ERROR(ldap_result_code, LDAP_OPERATIONS_ERROR, retry_count); goto error_return; } /* * Remove the old entry from the Virtual List View indexes. * Add the new entry to the Virtual List View indexes. * If the entry is ruv, no need to update vlv. */ if (!is_ruv) { retval= vlv_update_all_indexes(&txn, be, pb, e, ec); if (DB_LOCK_DEADLOCK == retval) { /* Abort and re-try */ continue; } if (0 != retval) { slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify", "vlv_update_index failed, err=%d %s\n", retval, (msg = dblayer_strerror( retval )) ? msg : ""); if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1; MOD_SET_ERROR(ldap_result_code, LDAP_OPERATIONS_ERROR, retry_count); goto error_return; } } if (!is_ruv && !is_fixup_operation && !NO_RUV_UPDATE(li)) { ruv_c_init = ldbm_txn_ruv_modify_context( pb, &ruv_c ); if (-1 == ruv_c_init) { slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify", "ldbm_txn_ruv_modify_context failed to construct RUV modify context\n"); ldap_result_code= LDAP_OPERATIONS_ERROR; retval = 0; goto error_return; } } if (ruv_c_init) { retval = modify_update_all( be, pb, &ruv_c, &txn ); if (DB_LOCK_DEADLOCK == retval) { /* Abort and re-try */ continue; } if (0 != retval) { slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify", "modify_update_all failed, err=%d %s\n", retval, (msg = dblayer_strerror( retval )) ? msg : ""); if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1; ldap_result_code= LDAP_OPERATIONS_ERROR; goto error_return; } } if (0 == retval) { break; } } if (retry_count == RETRY_TIMES) { slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify", "Retry count exceeded in modify\n"); ldap_result_code= LDAP_BUSY; goto error_return; } if (ruv_c_init) { if (modify_switch_entries(&ruv_c, be) != 0 ) { ldap_result_code= LDAP_OPERATIONS_ERROR; slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify", "modify_switch_entries failed\n"); goto error_return; } } if (cache_replace( &inst->inst_cache, e, ec ) != 0 ) { MOD_SET_ERROR(ldap_result_code, LDAP_OPERATIONS_ERROR, retry_count); goto error_return; } /* e uncached */ /* we must return both e (which has been deleted) and new entry ec to cache */ /* cache_replace removes e from the cache hash tables */ cache_unlock_entry( &inst->inst_cache, e ); CACHE_RETURN( &inst->inst_cache, &e ); /* lock new entry in cache to prevent usage until we are complete */ cache_lock_entry( &inst->inst_cache, ec ); ec_locked = 1; postentry = slapi_entry_dup( ec->ep_entry ); slapi_pblock_set( pb, SLAPI_ENTRY_POST_OP, postentry ); /* invalidate virtual cache */ ec->ep_entry->e_virtual_watermark = 0; /* * LP Fix of crash when the commit will fail: * If the commit fail, the common error path will * try to unlock the entry again and crash (PR_ASSERT * in debug mode. * By just setting e to NULL, we avoid this. It's OK since * we don't use e after that in the normal case. */ e = NULL; /* call the transaction post modify plugins just before the commit */ if ((retval = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN))) { slapi_log_err(SLAPI_LOG_TRACE, "ldbm_back_modify", "SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN plugin " "returned error code %d\n", retval ); if (!ldap_result_code) { slapi_pblock_get(pb, SLAPI_RESULT_CODE, &ldap_result_code); } if (!opreturn) { slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &opreturn); } if (!opreturn) { slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, ldap_result_code ? &ldap_result_code : &retval); } slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message); goto error_return; } /* Release SERIAL LOCK */ retval = dblayer_txn_commit(be, &txn); /* after commit - txn is no longer valid - replace SLAPI_TXN with parent */ slapi_pblock_set(pb, SLAPI_TXN, parent_txn); if (0 != retval) { if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1; ldap_result_code= LDAP_OPERATIONS_ERROR; goto error_return; } rc= 0; goto common_return; error_return: if ( postentry != NULL ) { slapi_entry_free( postentry ); postentry = NULL; slapi_pblock_set( pb, SLAPI_ENTRY_POST_OP, NULL ); } if (retval == DB_RUNRECOVERY) { dblayer_remember_disk_filled(li); ldbm_nasty("ldbm_back_modify","Modify",81,retval); disk_full = 1; } if (disk_full) { rc= return_on_disk_full(li); } else { if (txn.back_txn_txn && (txn.back_txn_txn != parent_txn)) { /* make sure SLAPI_RESULT_CODE and SLAPI_PLUGIN_OPRETURN are set */ int val = 0; slapi_pblock_get(pb, SLAPI_RESULT_CODE, &val); if (!val) { if (!ldap_result_code) { ldap_result_code = LDAP_OPERATIONS_ERROR; } slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ldap_result_code); } slapi_pblock_get( pb, SLAPI_PLUGIN_OPRETURN, &val ); if (!val) { opreturn = -1; slapi_pblock_set( pb, SLAPI_PLUGIN_OPRETURN, &opreturn ); } /* call the transaction post modify plugins just before the abort */ /* plugins called before abort should check for the OPRETURN or RESULT_CODE and skip processing if they don't want do anything - some plugins that keep track of a counter (usn, dna) may want to "rollback" the counter in this case */ if ((retval = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN))) { slapi_log_err(SLAPI_LOG_TRACE, "ldbm_back_modify", "SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN plugin returned error code %d\n", retval ); slapi_pblock_get(pb, SLAPI_RESULT_CODE, &ldap_result_code); slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message); slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &opreturn); if (!opreturn) { slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, ldap_result_code ? &ldap_result_code : &retval); } } /* It is safer not to abort when the transaction is not started. */ /* Release SERIAL LOCK */ dblayer_txn_abort(be, &txn); /* abort crashes in case disk full */ /* txn is no longer valid - reset the txn pointer to the parent */ slapi_pblock_set(pb, SLAPI_TXN, parent_txn); } if (!not_an_error) { rc = SLAPI_FAIL_GENERAL; } } /* if ec is in cache, remove it, then add back e if we still have it */ if (inst && cache_is_in_cache(&inst->inst_cache, ec)) { CACHE_REMOVE( &inst->inst_cache, ec ); /* if ec was in cache, e was not - add back e */ if (e) { if (CACHE_ADD( &inst->inst_cache, e, NULL ) < 0) { slapi_log_err(SLAPI_LOG_CACHE, "ldbm_back_modify", "CACHE_ADD %s failed\n", slapi_entry_get_dn(e->ep_entry)); } } } common_return: slapi_mods_done(&smods); if (inst) { if (ec_locked || cache_is_in_cache(&inst->inst_cache, ec)) { cache_unlock_entry(&inst->inst_cache, ec); } else if (e) { /* if ec was not in cache, cache_replace was not done. * i.e., e was not unlocked. */ cache_unlock_entry(&inst->inst_cache, e); CACHE_RETURN(&inst->inst_cache, &e); } CACHE_RETURN(&inst->inst_cache, &ec); if (inst->inst_ref_count) { slapi_counter_decrement(inst->inst_ref_count); } } /* result code could be used in the bepost plugin functions. */ slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ldap_result_code); /* The bepostop is called even if the operation fails. */ if (!disk_full) plugin_call_plugins (pb, SLAPI_PLUGIN_BE_POST_MODIFY_FN); if (ruv_c_init) { modify_term(&ruv_c, be); } if (ldap_result_code == -1) { /* Reset to LDAP_NO_SUCH_OBJECT*/ ldap_result_code = LDAP_NO_SUCH_OBJECT; slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ldap_result_code); } else { if (not_an_error) { /* This is mainly used by urp. Solved conflict is not an error. * And we don't want the supplier to halt sending the updates. */ ldap_result_code = LDAP_SUCCESS; } if (!result_sent) { /* result is already sent in find_entry. */ slapi_send_ldap_result( pb, ldap_result_code, NULL, ldap_result_message, 0, NULL ); } } /* free our backups */ ldap_mods_free(mods_original, 1); backentry_free(&original_entry); backentry_free(&tmpentry); slapi_ch_free_string(&errbuf); return rc; }
/** Apply the mods to the ec entry. Check for syntax, schema problems. Check for abandon. Return code: -1 - error - result code and message are set appropriately 0 - successfully applied and checked 1 - not an error - no mods to apply or op abandoned */ static int modify_apply_check_expand( Slapi_PBlock *pb, Slapi_Operation *operation, LDAPMod **mods, /* list of mods to apply */ struct backentry *e, /* original "before" entry */ struct backentry *ec, /* "after" entry with mods applied */ Slapi_Entry **postentry, int *ldap_result_code, char **ldap_result_message ) { int rc = 0; int i; int repl_op; int change_entry = 0; Slapi_Mods smods = {0}; CSN *csn = operation_get_csn(operation); slapi_pblock_get (pb, SLAPI_IS_REPLICATED_OPERATION, &repl_op); slapi_mods_init_byref( &smods, mods ); if ( (change_entry = mods_have_effect (ec->ep_entry, &smods)) ) { *ldap_result_code = entry_apply_mods_wsi(ec->ep_entry, &smods, csn, operation_is_flag_set(operation, OP_FLAG_REPLICATED)); /* * XXXmcs: it would be nice to get back an error message from * the above call so we could pass it along to the client, e.g., * "duplicate value for attribute givenName." */ } else { Slapi_Entry *epostop = NULL; /* If the entry was not actually changed, we still need to * set the SLAPI_ENTRY_POST_OP field in the pblock (post-op * plugins expect that field to be present for all modify * operations that return LDAP_SUCCESS). */ slapi_pblock_get ( pb, SLAPI_ENTRY_POST_OP, &epostop ); slapi_entry_free ( epostop ); /* free existing one, if any */ slapi_pblock_set ( pb, SLAPI_ENTRY_POST_OP, slapi_entry_dup( e->ep_entry ) ); *postentry = NULL; /* to avoid free in main error cleanup code */ } if ( !change_entry || *ldap_result_code != 0 ) { /* change_entry == 0 is not an error just a no-op */ rc = change_entry ? -1 : 1; goto done; } /* * If the objectClass attribute type was modified in any way, expand * the objectClass values to reflect the inheritance hierarchy. */ for ( i = 0; mods && mods[i]; ++i ) { if ( 0 == strcasecmp( SLAPI_ATTR_OBJECTCLASS, mods[i]->mod_type )) { slapi_schema_expand_objectclasses( ec->ep_entry ); break; } } /* * We are about to pass the last abandon test, so from now on we are * committed to finish this operation. Set status to "will complete" * before we make our last abandon check to avoid race conditions in * the code that processes abandon operations. */ operation->o_status = SLAPI_OP_STATUS_WILL_COMPLETE; if ( slapi_op_abandoned( pb ) ) { rc = 1; goto done; } /* multimaster replication can result in a schema violation, * although the individual operations on each master were valid * It is too late to resolve this. But we can check schema and * add a replication conflict attribute. */ /* check that the entry still obeys the schema */ if ((operation_is_flag_set(operation,OP_FLAG_ACTION_SCHEMA_CHECK)) && slapi_entry_schema_check_ext( pb, ec->ep_entry, 1 ) != 0 ) { if(repl_op){ Slapi_Attr *attr; Slapi_Mods smods; LDAPMod **lmods; if (slapi_entry_attr_find (ec->ep_entry, ATTR_NSDS5_REPLCONFLICT, &attr) == 0) { /* add value */ Slapi_Value *val = slapi_value_new_string("Schema violation"); slapi_attr_add_value(attr,val); slapi_value_free(&val); } else { /* Add new attribute */ slapi_entry_add_string (ec->ep_entry, ATTR_NSDS5_REPLCONFLICT, "Schema violation"); } /* the replconflict attribute is indexed and the index is built from the mods, * so we need to extend the mods */ slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &lmods); slapi_mods_init_passin(&smods, lmods); slapi_mods_add_string (&smods, LDAP_MOD_ADD, ATTR_NSDS5_REPLCONFLICT, "Schema violation"); lmods = slapi_mods_get_ldapmods_passout(&smods); slapi_pblock_set(pb, SLAPI_MODIFY_MODS, lmods); slapi_mods_done(&smods); } else { *ldap_result_code = LDAP_OBJECT_CLASS_VIOLATION; slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, ldap_result_message); rc = -1; goto done; } } if(!repl_op){ /* check attribute syntax for the new values */ if (slapi_mods_syntax_check(pb, mods, 0) != 0) { *ldap_result_code = LDAP_INVALID_SYNTAX; slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, ldap_result_message); rc = -1; goto done; } /* * make sure the entry contains all values in the RDN. * if not, the modification must have removed them. */ if ( ! slapi_entry_rdn_values_present( ec->ep_entry ) ) { *ldap_result_code= LDAP_NOT_ALLOWED_ON_RDN; rc = -1; goto done; } } done: slapi_mods_done( &smods ); return rc; }
/* * Check if there are any persistent searches. If so, * the check to see if the chgtype is one of those the * client is interested in. If so, then check to see if * the entry matches any of the filters the searches. * If so, then enqueue the entry on that persistent search's * ps_entryqueue and signal it to wake up and send the entry. * * Note that if eprev is NULL we assume that the entry's DN * was not changed by the op. that called this function. If * chgnum is 0 it is unknown so we won't ever send it to a * client in the EntryChangeNotification control. */ void ps_service_persistent_searches( Slapi_Entry *e, Slapi_Entry *eprev, ber_int_t chgtype, ber_int_t chgnum ) { LDAPControl *ctrl = NULL; PSearch *ps = NULL; PSEQNode *pe = NULL; int matched = 0; const char *edn; if ( !PS_IS_INITIALIZED()) { return; } if ( NULL == e ) { /* For now, some backends such as the chaining backend do not provide a post-op entry */ return; } PSL_LOCK_READ(); edn = slapi_entry_get_dn_const(e); for ( ps = psearch_list ? psearch_list->pl_head : NULL; NULL != ps; ps = ps->ps_next ) { char *origbase = NULL; Slapi_DN *base = NULL; Slapi_Filter *f; int scope; /* Skip the node that doesn't meet the changetype, * or is unable to use the change in ps_send_results() */ if (( ps->ps_changetypes & chgtype ) == 0 || ps->ps_pblock->pb_op == NULL || slapi_op_abandoned( ps->ps_pblock ) ) { continue; } slapi_log_error(SLAPI_LOG_CONNS, "Persistent Search", "conn=%" NSPRIu64 " op=%d entry %s with chgtype %d " "matches the ps changetype %d\n", ps->ps_pblock->pb_conn->c_connid, ps->ps_pblock->pb_op->o_opid, edn, chgtype, ps->ps_changetypes); slapi_pblock_get( ps->ps_pblock, SLAPI_SEARCH_FILTER, &f ); slapi_pblock_get( ps->ps_pblock, SLAPI_ORIGINAL_TARGET_DN, &origbase ); slapi_pblock_get( ps->ps_pblock, SLAPI_SEARCH_TARGET_SDN, &base ); slapi_pblock_get( ps->ps_pblock, SLAPI_SEARCH_SCOPE, &scope ); if (NULL == base) { base = slapi_sdn_new_dn_byref(origbase); slapi_pblock_set(ps->ps_pblock, SLAPI_SEARCH_TARGET_SDN, base); } /* * See if the entry meets the scope and filter criteria. * We cannot do the acl check here as this thread * would then potentially clash with the ps_send_results() * thread on the aclpb in ps->ps_pblock. * By avoiding the acl check in this thread, and leaving all the acl * checking to the ps_send_results() thread we avoid * the ps_pblock contention problem. * The lesson here is "Do not give multiple threads arbitary access * to the same pblock" this kind of muti-threaded access * to the same pblock must be done carefully--there is currently no * generic satisfactory way to do this. */ if ( slapi_sdn_scope_test( slapi_entry_get_sdn_const(e), base, scope ) && slapi_vattr_filter_test( ps->ps_pblock, e, f, 0 /* verify_access */ ) == 0 ) { PSEQNode *pOldtail; /* The scope and the filter match - enqueue it */ matched++; pe = (PSEQNode *)slapi_ch_calloc( 1, sizeof( PSEQNode )); pe->pe_entry = slapi_entry_dup( e ); if ( ps->ps_send_entchg_controls ) { /* create_entrychange_control() is more * expensive than slapi_dup_control() */ if ( ctrl == NULL ) { int rc; rc = create_entrychange_control( chgtype, chgnum, eprev ? slapi_entry_get_dn_const(eprev) : NULL, &ctrl ); if ( rc != LDAP_SUCCESS ) { LDAPDebug( LDAP_DEBUG_ANY, "ps_service_persistent_searches:" " unable to create EntryChangeNotification control for" " entry \"%s\" -- control won't be sent.\n", slapi_entry_get_dn_const(e), 0, 0 ); } } if ( ctrl ) { pe->pe_ctrls[0] = slapi_dup_control( ctrl ); } } /* Put it on the end of the list for this pers search */ PR_Lock( ps->ps_lock ); pOldtail = ps->ps_eq_tail; ps->ps_eq_tail = pe; if ( NULL == ps->ps_eq_head ) { ps->ps_eq_head = ps->ps_eq_tail; } else { pOldtail->pe_next = ps->ps_eq_tail; } PR_Unlock( ps->ps_lock ); } } PSL_UNLOCK_READ(); /* Were there any matches? */ if ( matched ) { ldap_control_free( ctrl ); /* Turn 'em loose */ ps_wakeup_all(); LDAPDebug( LDAP_DEBUG_TRACE, "ps_service_persistent_searches: enqueued entry " "\"%s\" on %d persistent search lists\n", slapi_entry_get_dn_const(e), matched, 0 ); } else { LDAPDebug( LDAP_DEBUG_TRACE, "ps_service_persistent_searches: entry " "\"%s\" not enqueued on any persistent search lists\n", slapi_entry_get_dn_const(e), 0, 0 ); } }