/* * detect if the plugin should handle this entry and return the entry type */ int ipa_topo_check_entry_type(Slapi_Entry *entry) { int ret = TOPO_IGNORE_ENTRY; Slapi_DN *add_dn = NULL; char **ocs; add_dn = slapi_entry_get_sdn(entry); if (slapi_sdn_issuffix(add_dn,ipa_topo_get_plugin_shared_topo_dn())) { /* check if it is a toplogy or a segment */ /* check if segment's left or right node is the local server*/ int i; ocs = slapi_entry_attr_get_charray(entry,"objectclass"); for (i=0; ocs && ocs[i]; i++) { if (strcasecmp(ocs[i],"ipaReplTopoConf") == 0) { ret = TOPO_CONFIG_ENTRY; break; } else if (strcasecmp(ocs[i],"ipaReplTopoSegment") == 0) { ret = TOPO_SEGMENT_ENTRY; break; } } } else if (slapi_sdn_isparent(ipa_topo_get_plugin_shared_hosts_dn(),add_dn)) { ret = TOPO_HOST_ENTRY; } else if (slapi_sdn_issuffix(add_dn,ipa_topo_get_domain_level_entry_dn())) { ret = TOPO_DOMLEVEL_ENTRY; } return ret; }
static int linked_attrs_remove_backlinks_callback(Slapi_Entry *e, void *callback_data) { int rc = 0; Slapi_DN *sdn = slapi_entry_get_sdn(e); char *type = (char *)callback_data; Slapi_PBlock *pb = slapi_pblock_new(); char *val[1]; LDAPMod mod; LDAPMod *mods[2]; /* Remove all values of the passed in type. */ val[0] = 0; mod.mod_op = LDAP_MOD_DELETE; mod.mod_type = type; mod.mod_values = val; mods[0] = &mod; mods[1] = 0; slapi_log_error(SLAPI_LOG_PLUGIN, LINK_PLUGIN_SUBSYSTEM, "Removing backpointer attribute (%s) from entry (%s)\n", type, slapi_sdn_get_dn(sdn)); /* Perform the operation. */ slapi_modify_internal_set_pb_ext(pb, sdn, mods, 0, 0, linked_attrs_get_plugin_id(), 0); slapi_modify_internal_pb(pb); slapi_pblock_destroy(pb); return rc; }
/* * This routine sets up a "context" for evaluation of access control * on a given entry for an anonymous user. * It just factors out the scope and targetfilter info into a list * of indices of the global anom profile list, that apply to this * entry, and stores them in the aclpb. * It's use relies on the way that access control is checked in the mailine search * code in the core server, namely: check filter, check entry, then check each * attribute. So, we call this in acl_access_allowed() before calling * aclanom_match_profile()--therafter, aclanom_match_profile() uses the * context to evaluate access to the entry and attributes. * * If there are no anom profiles, or the anom profiles get cancelled * due to complex anon acis, then that's OK, aclanom_match_profile() * returns -1 and the mainline acl code kicks in. * * The lifetime of this context info is the time it takes to check * access control for all parts of this entry (filter, entry, attributes). * So, if for an example an entry changes and a given anom profile entry * no longer applies, we will not notice until the next round of access * control checking on the entry--this is acceptable. * * The gain on doing this factoring in the following type of search * was approx 6%: * anon bind, 20 threads, exact match, ~20 attributes returned, * (searchrate & DirectoryMark). * */ void aclanom_get_suffix_info(Slapi_Entry *e, struct acl_pblock *aclpb ) { int i; char *ndn = NULL; Slapi_DN *e_sdn; const char *aci_ndn; struct scoped_entry_anominfo *s_e_anominfo = &aclpb->aclpb_scoped_entry_anominfo; ANOM_LOCK_READ (); s_e_anominfo->anom_e_nummatched=0; ndn = slapi_entry_get_ndn ( e ) ; e_sdn= slapi_entry_get_sdn ( e ) ; for (i=acl_anom_profile->anom_numacls-1; i >= 0; i-- ) { aci_ndn = slapi_sdn_get_ndn (acl_anom_profile->anom_targetinfo[i].anom_target); if (!slapi_sdn_issuffix(e_sdn,acl_anom_profile->anom_targetinfo[i].anom_target) || (!slapi_is_rootdse(ndn) && slapi_is_rootdse(aci_ndn))) continue; if ( acl_anom_profile->anom_targetinfo[i].anom_filter ) { if ( slapi_vattr_filter_test( aclpb->aclpb_pblock, e, acl_anom_profile->anom_targetinfo[i].anom_filter, 0 /*don't do acess chk*/) != 0) continue; } s_e_anominfo->anom_e_targetInfo[s_e_anominfo->anom_e_nummatched]=i; s_e_anominfo->anom_e_nummatched++; } ANOM_UNLOCK_READ (); }
static int handle_agmt_search(Slapi_Entry *e, void *callback_data) { int *agmtcount = (int *)callback_data; int rc; slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "Found replication agreement named \"%s\".\n", slapi_sdn_get_dn(slapi_entry_get_sdn(e))); rc = add_new_agreement(e); if (0 == rc) { (*agmtcount)++; } else { slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "The replication " "agreement named \"%s\" could not be correctly parsed. No " "replication will occur with this replica.\n", slapi_sdn_get_dn(slapi_entry_get_sdn(e))); } return rc; }
/* This function is now fully executed for internal and replicated ops. */ int plugin_call_acl_mods_update ( Slapi_PBlock *pb, int optype ) { struct slapdplugin *p; int rc = 0; void *change = NULL; void *mychange[2]; Slapi_Entry *te = NULL; Slapi_DN *sdn = NULL; Operation *operation; slapi_pblock_get (pb, SLAPI_OPERATION, &operation); (void)slapi_pblock_get( pb, SLAPI_TARGET_SDN, &sdn ); switch ( optype ) { case SLAPI_OPERATION_MODIFY: (void)slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &change ); break; case SLAPI_OPERATION_ADD: (void)slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &change ); te = (Slapi_Entry *)change; if(!slapi_sdn_isempty(slapi_entry_get_sdn(te))) { sdn = slapi_entry_get_sdn(te); } break; case SLAPI_OPERATION_MODRDN: { char *newrdn = NULL; Slapi_DN *psdn = NULL; char *pdn = NULL; /* newrdn: "change" is normalized but not case-ignored */ /* The acl plugin expects normalized newrdn, but no need to be case- * ignored. */ (void)slapi_pblock_get( pb, SLAPI_MODRDN_NEWRDN, &newrdn ); (void)slapi_pblock_get( pb, SLAPI_MODRDN_NEWSUPERIOR_SDN, &psdn ); if (psdn) { pdn = (char *)slapi_sdn_get_dn(psdn); } else { (void)slapi_pblock_get( pb, SLAPI_MODRDN_NEWSUPERIOR, &pdn ); } mychange[0] = newrdn; mychange[1] = pdn; change = mychange; break; } } if (NULL == sdn) { slapi_log_err(SLAPI_LOG_ERR, "plugin_call_acl_mods_update", "NULL target DN\n"); return LDAP_INVALID_DN_SYNTAX; } /* call the global plugins first and then the backend specific */ for ( p = get_plugin_list(PLUGIN_LIST_ACL); p != NULL; p = p->plg_next ) { if (plugin_invoke_plugin_sdn(p, SLAPI_PLUGIN_ACL_MODS_UPDATE, pb, sdn)){ rc = (*p->plg_acl_mods_update)(pb, optype, sdn, change ); if ( rc != LDAP_SUCCESS ) break; } } return rc; }
static int _ger_g_permission_granted ( Slapi_PBlock *pb, Slapi_Entry *e, const char *subjectdn, char **errbuf ) { char *proxydn = NULL; Slapi_DN *requestor_sdn, *entry_sdn; char *errtext = NULL; int isroot; int rc; /* * Theorically, we should check if the entry has "g" * permission granted to the requestor. If granted, * allows the effective rights on that entry and its * attributes within the entry to be returned for * ANY subject. * * "G" permission granting has not been implemented yet, * the current release assumes that "g" permission be * granted to root and owner of any entry. */ /* * The requestor may be either the bind dn or a proxy dn */ if ((proxyauth_get_dn( pb, &proxydn, &errtext ) == LDAP_SUCCESS) && ( proxydn != NULL )) { requestor_sdn = slapi_sdn_new_dn_passin ( proxydn ); } else { slapi_ch_free_string(&proxydn); /* this could still have been set - free it */ requestor_sdn = &(pb->pb_op->o_sdn); } if ( slapi_sdn_get_dn (requestor_sdn) == NULL ) { slapi_log_err(SLAPI_LOG_ACL, plugin_name, "_ger_g_permission_granted - Anonymous has no g permission\n" ); rc = LDAP_INSUFFICIENT_ACCESS; goto bailout; } isroot = slapi_dn_isroot ( slapi_sdn_get_dn (requestor_sdn) ); if ( isroot ) { /* Root has "g" permission on any entry */ rc = LDAP_SUCCESS; goto bailout; } entry_sdn = slapi_entry_get_sdn ( e ); if ( entry_sdn == NULL || slapi_sdn_get_dn (entry_sdn) == NULL ) { rc = LDAP_SUCCESS; goto bailout; } if ( slapi_sdn_compare ( requestor_sdn, entry_sdn ) == 0 ) { /* Owner has "g" permission on his own entry */ rc = LDAP_SUCCESS; goto bailout; } /* if the requestor and the subject user are identical, let's grant it */ if ( strcasecmp ( slapi_sdn_get_ndn(requestor_sdn), subjectdn ) == 0) { /* Requestor should see his own permission rights on any entry */ rc = LDAP_SUCCESS; goto bailout; } aclutil_str_append ( errbuf, "get-effective-rights: requestor has no g permission on the entry" ); slapi_log_err(SLAPI_LOG_ACL, plugin_name, "_ger_g_permission_granted - %s\n", *errbuf); rc = LDAP_INSUFFICIENT_ACCESS; bailout: if ( proxydn ) { /* The ownership of proxydn has passed to requestor_sdn */ slapi_sdn_free ( &requestor_sdn ); } return rc; }
/*************************************************************************** * * __aclinit_handler * * For each entry, finds if there is any ACL in that entry. If there is * then the ACL is processed and stored in the ACL LIST. * * * Input: * * * Returns: * None. * * Error Handling: * If any error found during the ACL generation, the ACL is * logged. Also, set in the callback_data so that caller can act upon it. * **************************************************************************/ static int __aclinit_handler ( Slapi_Entry *e, void *callback_data) { Slapi_Attr *attr; aclinit_handler_callback_data_t *call_back_data = (aclinit_handler_callback_data_t*)callback_data; Slapi_DN *e_sdn; int rv; Slapi_Value *sval=NULL; call_back_data->retCode = 0; /* assume success--if there's an error we overwrite it */ if (e != NULL) { e_sdn = slapi_entry_get_sdn ( e ); /* * Take the write lock around all the mods--so that * other operations will see the acicache either before the whole mod * or after but not, as it was before, during the mod. * This is in line with the LDAP concept of the operation * on the whole entry being the atomic unit. * */ if ( call_back_data->op == ACL_ADD_ACIS ) { slapi_log_err(SLAPI_LOG_ACL, plugin_name, "Adding acis for entry '%s'\n", slapi_sdn_get_dn(e_sdn)); slapi_entry_attr_find ( e, aci_attr_type, &attr ); if ( attr ) { const struct berval *attrValue; int i; if ( call_back_data->lock_flag == DO_TAKE_ACLCACHE_WRITELOCK) { acllist_acicache_WRITE_LOCK(); } i= slapi_attr_first_value ( attr, &sval ); while(i != -1) { attrValue = slapi_value_get_berval(sval); if ( 0 != (rv=acllist_insert_aci_needsLock (e_sdn, attrValue))) { aclutil_print_err(rv, e_sdn, attrValue, NULL); /* We got an error; Log it and then march along */ slapi_log_err(SLAPI_LOG_ERR, plugin_name, "__aclinit_handler - This (%s) ACL will not be considered for evaluation" " because of syntax errors.\n", attrValue->bv_val ? attrValue->bv_val: "NULL"); call_back_data->retCode = rv; } i= slapi_attr_next_value( attr, i, &sval ); }/* while */ if ( call_back_data->lock_flag == DO_TAKE_ACLCACHE_WRITELOCK) { acllist_acicache_WRITE_UNLOCK(); } } } else if (call_back_data->op == ACL_REMOVE_ACIS) { /* Here we are deleting the acis. */ slapi_log_err(SLAPI_LOG_ACL, plugin_name, "__aclinit_handler - Removing acis\n"); if ( call_back_data->lock_flag == DO_TAKE_ACLCACHE_WRITELOCK) { acllist_acicache_WRITE_LOCK(); } if ( 0 != (rv=acllist_remove_aci_needsLock(e_sdn, NULL))) { aclutil_print_err(rv, e_sdn, NULL, NULL); /* We got an error; Log it and then march along */ slapi_log_err(SLAPI_LOG_ERR, plugin_name, "__aclinit_handler - ACLs not deleted from %s\n", slapi_sdn_get_dn(e_sdn)); call_back_data->retCode = rv; } if ( call_back_data->lock_flag == DO_TAKE_ACLCACHE_WRITELOCK) { acllist_acicache_WRITE_UNLOCK(); } } } /* * If we get here it's success. * The call_back_data->error is the error code that counts as it's the * one that the original caller will see--this routine is called off a callbacl. */ return ACL_FALSE; /* "local" error code--it's 0 */ }
int update_integrity(char **argv, Slapi_DN *origSDN, char *newrDN, Slapi_DN *newsuperior, int logChanges) { Slapi_PBlock *search_result_pb = NULL; Slapi_PBlock *mod_pb = slapi_pblock_new(); Slapi_Entry **search_entries = NULL; Slapi_DN *sdn = NULL; Slapi_Attr *attr = NULL; void *node = NULL; const char *origDN = slapi_sdn_get_dn(origSDN); const char *search_base = NULL; char *attrName = NULL; char *filter = NULL; char *attrs[2]; int search_result; int nval = 0; int i, j; int rc; if ( argv == NULL ){ slapi_log_error( SLAPI_LOG_FATAL, REFERINT_PLUGIN_SUBSYSTEM, "referint_postop required config file arguments missing\n" ); rc = -1; goto free_and_return; } /* * For now, just putting attributes to keep integrity on in conf file, * until resolve the other timing mode issue */ search_result_pb = slapi_pblock_new(); /* Search each namingContext in turn */ for ( sdn = slapi_get_first_suffix( &node, 0 ); sdn != NULL; sdn = slapi_get_next_suffix( &node, 0 )) { Slapi_Backend *be = slapi_be_select(sdn); search_base = slapi_sdn_get_dn( sdn ); for(i = 3; argv[i] != NULL; i++){ filter = slapi_filter_sprintf("(%s=*%s%s)", argv[i], ESC_NEXT_VAL, origDN); if ( filter ) { /* Need only the current attribute and its subtypes */ attrs[0] = argv[i]; attrs[1] = NULL; /* Use new search API */ slapi_pblock_init(search_result_pb); slapi_pblock_set(search_result_pb, SLAPI_BACKEND, be); slapi_search_internal_set_pb(search_result_pb, search_base, LDAP_SCOPE_SUBTREE, filter, attrs, 0 /* attrs only */, NULL, NULL, referint_plugin_identity, 0); slapi_search_internal_pb(search_result_pb); slapi_pblock_get( search_result_pb, SLAPI_PLUGIN_INTOP_RESULT, &search_result); /* if search successfull then do integrity update */ if(search_result == LDAP_SUCCESS) { slapi_pblock_get(search_result_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &search_entries); for(j = 0; search_entries[j] != NULL; j++){ attr = NULL; attrName = NULL; /* * Loop over all the attributes of the entry and search * for the integrity attribute and its subtypes */ for (slapi_entry_first_attr(search_entries[j], &attr); attr; slapi_entry_next_attr(search_entries[j], attr, &attr)) { /* * Take into account only the subtypes of the attribute * in argv[i] having the necessary value - origDN */ slapi_attr_get_type(attr, &attrName); if (slapi_attr_type_cmp(argv[i], attrName, SLAPI_TYPE_CMP_SUBTYPE) == 0) { nval = 0; slapi_attr_get_numvalues(attr, &nval); /* * We want to reduce the "modify" call as much as * possible. But if an entry contains 1000s of * attributes which need to be updated by the * referint plugin (e.g., a group containing 1000s * of members), we want to avoid to allocate too * many mods * in one "modify" call. * This is a compromise: If an attribute type has * more than 128 values, we update the attribute * value one by one. Otherwise, update all values * in one "modify" call. */ if (nval > 128) { rc = _update_one_per_mod( slapi_entry_get_sdn(search_entries[j]), attr, attrName, origSDN, newrDN, slapi_sdn_get_dn(newsuperior), mod_pb); } else { rc = _update_all_per_mod( slapi_entry_get_sdn(search_entries[j]), attr, attrName, origSDN, newrDN, slapi_sdn_get_dn(newsuperior), mod_pb); } /* Should we stop if one modify returns an error? */ } } } } else { if (isFatalSearchError(search_result)){ slapi_log_error( SLAPI_LOG_FATAL, REFERINT_PLUGIN_SUBSYSTEM, "update_integrity search (base=%s filter=%s) returned " "error %d\n", search_base, filter, search_result); rc = -1; goto free_and_return; } } slapi_ch_free_string(&filter); } slapi_free_search_results_internal(search_result_pb); } } /* if got here, then everything good rc = 0 */ rc = 0; free_and_return: /* free filter and search_results_pb */ slapi_ch_free_string(&filter); slapi_pblock_destroy(mod_pb); if (search_result_pb) { slapi_free_search_results_internal(search_result_pb); slapi_pblock_destroy(search_result_pb); } return(rc); }
/* * Read and load configuration. Validation will also * be performed unless skip_validate is set to non-0. * Returns PAM_PASSTHRU_SUCCESS if all is well. */ int pam_passthru_load_config(int skip_validate) { int status = PAM_PASSTHRU_SUCCESS; int result; int i; int alternate = 0; Slapi_PBlock *search_pb; Slapi_Entry **entries = NULL; slapi_log_err(SLAPI_LOG_TRACE, PAM_PASSTHRU_PLUGIN_SUBSYSTEM, "=> pam_passthru_load_config\n"); pam_passthru_write_lock(); pam_passthru_delete_config(); search_pb = slapi_pblock_new(); /* Find all entries in the active config area. */ slapi_search_internal_set_pb(search_pb, slapi_sdn_get_ndn(pam_passthru_get_config_area()), LDAP_SCOPE_SUBTREE, "objectclass=*", NULL, 0, NULL, NULL, pam_passthruauth_get_plugin_identity(), 0); slapi_search_internal_pb(search_pb); slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &result); if (LDAP_SUCCESS != result) { status = PAM_PASSTHRU_FAILURE; goto cleanup; } slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries); if (NULL == entries || NULL == entries[0]) { status = PAM_PASSTHRU_FAILURE; goto cleanup; } /* Check if we are using an alternate config area. We do this here * so we don't have to check each every time in the loop below. */ if (slapi_sdn_compare(pam_passthru_get_config_area(), pam_passthruauth_get_plugin_sdn()) != 0) { alternate = 1; } /* Validate and apply config if valid. If skip_validate is set, we skip * validation and just apply the config. This should only be done if the * configuration has already been validated. */ for (i = 0; (entries[i] != NULL); i++) { /* If this is the alternate config container, skip it since * we don't consider it to be an actual config entry. */ if (alternate && (slapi_sdn_compare(pam_passthru_get_config_area(), slapi_entry_get_sdn(entries[i])) == 0)) { continue; } if (skip_validate || (PAM_PASSTHRU_SUCCESS == pam_passthru_validate_config(entries[i], NULL))) { if (PAM_PASSTHRU_FAILURE == pam_passthru_apply_config(entries[i])) { slapi_log_err(SLAPI_LOG_ERR, PAM_PASSTHRU_PLUGIN_SUBSYSTEM, "pam_passthru_load_config - Unable to apply config " "for entry \"%s\"\n", slapi_entry_get_ndn(entries[i])); } } else { slapi_log_err(SLAPI_LOG_ERR, PAM_PASSTHRU_PLUGIN_SUBSYSTEM, "pam_passthru_load_config - Skipping invalid config " "entry \"%s\"\n", slapi_entry_get_ndn(entries[i])); } } cleanup: slapi_free_search_results_internal(search_pb); slapi_pblock_destroy(search_pb); pam_passthru_unlock(); slapi_log_err(SLAPI_LOG_TRACE, PAM_PASSTHRU_PLUGIN_SUBSYSTEM, "<= pam_passthru_load_config\n"); return status; }
/* 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); }
/* * preop_modrdn - Pre-operation call for modify RDN * * Check that the new RDN does not include attributes that * cause a constraint violation */ static int preop_modrdn(Slapi_PBlock *pb) { int result = LDAP_SUCCESS; Slapi_Entry *e = NULL; Slapi_Value *sv_requiredObjectClass = NULL; char *errtext = NULL; char *attrName = NULL; #ifdef DEBUG slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name, "MODRDN begin\n"); #endif BEGIN int err; char *markerObjectClass=NULL; char *requiredObjectClass=NULL; Slapi_DN *sdn = NULL; Slapi_DN *superior; char *rdn; int deloldrdn = 0; int isupdatedn; Slapi_Attr *attr; int argc; char **argv = NULL; /* * If this is a replication update, just be a noop. */ err = slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &isupdatedn); if (err) { result = uid_op_error(30); break; } if (isupdatedn) { break; } /* * Get the arguments */ result = getArguments(pb, &attrName, &markerObjectClass, &requiredObjectClass); if (UNTAGGED_PARAMETER == result) { result = LDAP_SUCCESS; /* Statically defined subtrees to monitor */ err = slapi_pblock_get(pb, SLAPI_PLUGIN_ARGC, &argc); if (err) { result = uid_op_error(53); break; } err = slapi_pblock_get(pb, SLAPI_PLUGIN_ARGV, &argv); if (err) { result = uid_op_error(54); break; } argc--; /* First argument was attribute name */ argv++; } else if (0 != result) { break; } /* Create a Slapi_Value for the requiredObjectClass to use * for checking the entry. */ if (requiredObjectClass) { sv_requiredObjectClass = slapi_value_new_string(requiredObjectClass); } /* Get the DN of the entry being renamed */ err = slapi_pblock_get(pb, SLAPI_MODRDN_TARGET_SDN, &sdn); if (err) { result = uid_op_error(31); break; } /* Get superior value - unimplemented in 3.0/4.0/5.0 DS */ err = slapi_pblock_get(pb, SLAPI_MODRDN_NEWSUPERIOR_SDN, &superior); if (err) { result = uid_op_error(32); break; } /* * No superior means the entry is just renamed at * its current level in the tree. Use the target DN for * determining which managed tree this belongs to */ if (!superior) superior = sdn; /* Get the new RDN - this has the attribute values */ err = slapi_pblock_get(pb, SLAPI_MODRDN_NEWRDN, &rdn); if (err) { result = uid_op_error(33); break; } #ifdef DEBUG slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name, "MODRDN newrdn=%s\n", rdn); #endif /* See if the old RDN value is being deleted. */ err = slapi_pblock_get(pb, SLAPI_MODRDN_DELOLDRDN, &deloldrdn); if (err) { result = uid_op_error(34); break; } /* Get the entry that is being renamed so we can make a dummy copy * of what it will look like after the rename. */ err = slapi_search_internal_get_entry(sdn, NULL, &e, plugin_identity); if (err != LDAP_SUCCESS) { result = uid_op_error(35); /* We want to return a no such object error if the target doesn't exist. */ if (err == LDAP_NO_SUCH_OBJECT) { result = err; } break; } /* Apply the rename operation to the dummy entry. */ /* slapi_entry_rename does not expect rdn normalized */ err = slapi_entry_rename(e, rdn, deloldrdn, superior); if (err != LDAP_SUCCESS) { result = uid_op_error(36); break; } /* * Find any unique attribute data in the new RDN */ err = slapi_entry_attr_find(e, attrName, &attr); if (err) break; /* no UID attribute */ /* * Check if it has the required object class */ if (requiredObjectClass && !slapi_entry_attr_has_syntax_value(e, SLAPI_ATTR_OBJECTCLASS, sv_requiredObjectClass)) { break; } /* * Passed all the requirements - this is an operation we * need to enforce uniqueness on. Now find all parent entries * with the marker object class, and do a search for each one. */ if (NULL != markerObjectClass) { /* Subtree defined by location of marker object class */ result = findSubtreeAndSearch(slapi_entry_get_sdn(e), attrName, attr, NULL, requiredObjectClass, sdn, markerObjectClass); } else { /* Subtrees listed on invocation line */ result = searchAllSubtrees(argc, argv, attrName, attr, NULL, requiredObjectClass, sdn); } END /* Clean-up */ slapi_value_free(&sv_requiredObjectClass); if (e) slapi_entry_free(e); if (result) { slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name, "MODRDN result %d\n", result); if (result == LDAP_CONSTRAINT_VIOLATION) { errtext = slapi_ch_smprintf(moreInfo, attrName); } else { errtext = slapi_ch_strdup("Error checking for attribute uniqueness."); } slapi_send_ldap_result(pb, result, 0, errtext, 0, 0); slapi_ch_free_string(&errtext); } return (result==LDAP_SUCCESS)?0:-1; }
static int search_one_berval(Slapi_DN *baseDN, const char *attrName, const struct berval *value, const char *requiredObjectClass, Slapi_DN *target) { int result; char *filter; Slapi_PBlock *spb; result = LDAP_SUCCESS; /* If no value, can't possibly be a conflict */ if ( (struct berval *)NULL == value ) return result; filter = 0; spb = 0; BEGIN int err; int sres; Slapi_Entry **entries; static char *attrs[] = { "1.1", 0 }; /* Create the filter - this needs to be freed */ filter = create_filter(attrName, value, requiredObjectClass); #ifdef DEBUG slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name, "SEARCH filter=%s\n", filter); #endif /* Perform the search using the new internal API */ spb = slapi_pblock_new(); if (!spb) { result = uid_op_error(2); break; } slapi_search_internal_set_pb_ext(spb, baseDN, LDAP_SCOPE_SUBTREE, filter, attrs, 0 /* attrs only */, NULL, NULL, plugin_identity, 0 /* actions */); slapi_search_internal_pb(spb); err = slapi_pblock_get(spb, SLAPI_PLUGIN_INTOP_RESULT, &sres); if (err) { result = uid_op_error(3); break; } /* Allow search to report that there is nothing in the subtree */ if (sres == LDAP_NO_SUCH_OBJECT) break; /* Other errors are bad */ if (sres) { result = uid_op_error(3); break; } err = slapi_pblock_get(spb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries); if (err) { result = uid_op_error(4); break; } /* * Look at entries returned. Any entry found must be the * target entry or the constraint fails. */ for(;*entries;entries++) { #ifdef DEBUG slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name, "SEARCH entry dn=%s\n", slapi_entry_get_dn(*entries)); #endif /* * It is a Constraint Violation if any entry is found, unless * the entry is the target entry (if any). */ if (!target || slapi_sdn_compare(slapi_entry_get_sdn(*entries), target) != 0) { result = LDAP_CONSTRAINT_VIOLATION; break; } } #ifdef DEBUG slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name, "SEARCH complete result=%d\n", result); #endif END /* Clean-up */ if (spb) { slapi_free_search_results_internal(spb); slapi_pblock_destroy(spb); } slapi_ch_free((void**)&filter); return result; }
static int urp_add_resolve_parententry (Slapi_PBlock *pb, char *sessionid, Slapi_Entry *entry, Slapi_Entry *parententry, CSN *opcsn) { Slapi_DN *parentdn = NULL; Slapi_RDN *add_rdn = NULL; char *newdn = NULL; int ldap_rc; int rc = 0; Slapi_DN *sdn = NULL; if( is_suffix_entry (pb, entry, &parentdn) ) { /* It's OK for the suffix entry's parent to be absent */ rc= 0; PROFILE_POINT; /* Add Conflict; Suffix Entry */ goto bailout; } /* The entry is not a suffix. */ if(parententry==NULL) /* The parent entry was not found. */ { /* Create a glue entry to stand in for the absent parent */ slapi_operation_parameters *op_params; slapi_pblock_get( pb, SLAPI_OPERATION_PARAMETERS, &op_params ); ldap_rc = create_glue_entry (pb, sessionid, parentdn, op_params->p.p_add.parentuniqueid, opcsn); if ( LDAP_SUCCESS == ldap_rc ) { /* The backend code should now search for the parent again. */ rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_EXISTING_DN_ENTRY); rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_PARENT_ENTRY); PROFILE_POINT; /* Add Conflict; Orphaned Entry; Glue Parent */ } else { /* * Error. The parent can't be created as a glue entry. * This will cause replication divergence and will * require admin intercession */ ldap_rc= LDAP_OPERATIONS_ERROR; slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ldap_rc); rc= -1; /* Abort this Operation */ PROFILE_POINT; /* Add Conflict; Orphaned Entry; Impossible to create parent; Refuse Change. */ } goto bailout; } if(is_tombstone_entry(parententry)) /* The parent is a tombstone */ { /* The parent entry must be resurected from the dead. */ ldap_rc = tombstone_to_glue (pb, sessionid, parententry, parentdn, REASON_RESURRECT_ENTRY, opcsn); if ( ldap_rc != LDAP_SUCCESS ) { ldap_rc= LDAP_OPERATIONS_ERROR; slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ldap_rc); rc = -1; /* Abort the operation */ } else { /* The backend add code should now search for the parent again. */ rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_EXISTING_DN_ENTRY); rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_PARENT_ENTRY); } PROFILE_POINT; /* Add Conflict; Orphaned Entry; Parent Was Tombstone */ goto bailout; } /* The parent is healthy */ /* Now we need to check that the parent has the correct DN */ if (slapi_sdn_isparent(slapi_entry_get_sdn(parententry), slapi_entry_get_sdn(entry))) { rc= 0; /* OK, Add the entry */ PROFILE_POINT; /* Add Conflict; Parent Exists */ goto bailout; } /* * Parent entry doesn't have a DN parent to the entry. * This can happen if parententry was renamed due to * conflict and the child entry was created before * replication occured. See defect 530942. * We need to rename the entry to be child of its parent. */ add_rdn = slapi_rdn_new_dn(slapi_entry_get_dn_const (entry)); newdn = slapi_dn_plus_rdn(slapi_entry_get_dn_const (parententry), slapi_rdn_get_rdn(add_rdn)); slapi_entry_set_normdn ( entry, newdn ); /* slapi_pblock_get(pb, SLAPI_ADD_TARGET, &dn); */ slapi_pblock_get(pb, SLAPI_ADD_TARGET_SDN, &sdn); slapi_sdn_free(&sdn); sdn = slapi_sdn_dup(slapi_entry_get_sdn_const(entry)); slapi_pblock_set(pb, SLAPI_ADD_TARGET_SDN, sdn); slapi_log_error ( slapi_log_urp, sessionid, "Parent was renamed. Renamed the child to %s\n", newdn ); rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_EXISTING_DN_ENTRY); PROFILE_POINT; /* Add Conflict; Parent Renamed; Rename Operation Entry */ bailout: if (parentdn) slapi_sdn_free(&parentdn); slapi_rdn_free(&add_rdn); return rc; }
/* * Return 0 for OK, -1 for Error, >0 for action code * Action Code Bit 0: Fetch existing entry. * Action Code Bit 1: Fetch parent entry. */ int urp_modrdn_operation( Slapi_PBlock *pb ) { slapi_operation_parameters *op_params = NULL; Slapi_Entry *parent_entry; Slapi_Entry *new_parent_entry; Slapi_DN *newsuperior = NULL; Slapi_DN *parentdn = NULL; const Slapi_Entry *target_entry; Slapi_Entry *existing_entry; const CSN *target_entry_dncsn; CSN *opcsn= NULL; char *op_uniqueid = NULL; const char *existing_uniqueid = NULL; const Slapi_DN *target_sdn; const Slapi_DN *existing_sdn; char *newrdn; char sessionid[REPL_SESSION_ID_SIZE]; int r; int op_result= 0; int rc= 0; /* OK */ int del_old_replconflict_attr = 0; if ( slapi_op_abandoned(pb) ) { return rc; } slapi_pblock_get (pb, SLAPI_MODRDN_TARGET_ENTRY, &target_entry); if(target_entry==NULL) { /* An entry can't be found for the Unique Identifier */ op_result= LDAP_NO_SUCH_OBJECT; slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result); rc= -1; /* No entry to modrdn */ PROFILE_POINT; /* ModRDN Conflict; Entry does not Exist; Discard ModRDN */ goto bailout; } get_repl_session_id (pb, sessionid, &opcsn); target_entry_dncsn = entry_get_dncsn (target_entry); if ( csn_compare (target_entry_dncsn, opcsn) >= 0 ) { /* * The Operation CSN is not newer than the DN CSN. * Either we're beaten by another ModRDN or we've applied the op. */ /* op_result= LDAP_SUCCESS; */ /* * This operation won't be replayed. That is, this CSN won't update * the max csn in RUV. The CSN is left uncommitted in RUV unless an * error is set to op_result. Just to get rid of this CSN from RUV, * setting an error to op_result */ slapi_log_error(slapi_log_urp, sessionid, "urp_modrdn (%s): operation CSN is newer than the DN CSN.\n", slapi_entry_get_dn_const(target_entry)); op_result= LDAP_UNWILLING_TO_PERFORM; slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result); rc = SLAPI_PLUGIN_NOOP; /* Ignore the modrdn */ PROFILE_POINT; /* ModRDN Conflict; Entry with Target DN Exists; OPCSN is not newer. */ goto bailout; } /* The DN CSN is older than the Operation CSN. Apply the operation */ target_sdn = slapi_entry_get_sdn_const (target_entry); /* newrdn is no need to be case-ignored (get_rdn_plus_uniqueid) */ slapi_pblock_get(pb, SLAPI_MODRDN_NEWRDN, &newrdn); slapi_pblock_get(pb, SLAPI_TARGET_UNIQUEID, &op_uniqueid); slapi_pblock_get(pb, SLAPI_MODRDN_PARENT_ENTRY, &parent_entry); slapi_pblock_get(pb, SLAPI_MODRDN_NEWPARENT_ENTRY, &new_parent_entry); slapi_pblock_get(pb, SLAPI_MODRDN_NEWSUPERIOR_SDN, &newsuperior); if ( is_tombstone_entry (target_entry) ) { /* * It is a non-trivial task to rename a tombstone. * This op has been ignored so far by * setting SLAPI_RESULT_CODE to LDAP_NO_SUCH_OBJECT * and rc to -1. */ /* Turn the tombstone to glue before rename it */ /* op_result = tombstone_to_glue (pb, sessionid, target_entry, slapi_entry_get_sdn (target_entry), "renameTombstone", opcsn); */ op_result = LDAP_NO_SUCH_OBJECT; slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result); if (op_result == 0) { /* * Remember to turn this entry back to tombstone in post op. * We'll just borrow an obsolete pblock type here. */ slapi_pblock_set (pb, SLAPI_URP_TOMBSTONE_UNIQUEID, slapi_ch_strdup(op_uniqueid)); rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_TARGET_ENTRY); rc = 0; } else { slapi_log_error(slapi_log_urp, sessionid, "urp_modrdn (%s): target entry is a tombstone.\n", slapi_entry_get_dn_const(target_entry)); rc = SLAPI_PLUGIN_NOOP; /* Ignore the modrdn */ } PROFILE_POINT; /* ModRDN Conflict; Entry with Target DN Exists; OPCSN is not newer. */ goto bailout; } slapi_pblock_get(pb, SLAPI_MODRDN_EXISTING_ENTRY, &existing_entry); if(existing_entry!=NULL) { /* * An entry with the target DN already exists. * The smaller dncsn wins. The loser changes its RDN to * uniqueid+baserdn, and adds operational attribute * ATTR_NSDS5_REPLCONFLIC */ existing_uniqueid = slapi_entry_get_uniqueid (existing_entry); existing_sdn = slapi_entry_get_sdn_const ( existing_entry); /* * It used to dismiss the operation if the existing entry is * the same as the target one. * But renaming the RDN with the one which only cases are different, * cn=ABC --> cn=Abc, this case matches. We should go forward the op. */ if (strcmp(op_uniqueid, existing_uniqueid) == 0) { op_result= LDAP_SUCCESS; slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result); rc = 0; /* Don't ignore the op */ PROFILE_POINT; /* ModRDN Replay */ goto bailout; } r= csn_compare ( entry_get_dncsn (existing_entry), opcsn); if (r == 0) { /* * The CSN of the Operation and the Entry DN are the same * but the uniqueids are not. * There might be two replicas with the same ReplicaID. */ slapi_log_error(slapi_log_urp, sessionid, "urp_modrdn: Duplicated CSN for different uniqueids [%s][%s]", existing_uniqueid, op_uniqueid); op_result= LDAP_OPERATIONS_ERROR; slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result); rc = SLAPI_PLUGIN_NOOP; /* Ignore this Operation */ PROFILE_POINT; /* ModRDN Conflict; Duplicated CSN for Different Entries */ goto bailout; } if(r<0) { /* The target entry is a loser */ char *newrdn_with_uniqueid; newrdn_with_uniqueid= get_rdn_plus_uniqueid (sessionid, newrdn, op_uniqueid); if(newrdn_with_uniqueid==NULL) { op_result= LDAP_OPERATIONS_ERROR; slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result); rc= -1; /* Ignore this Operation */ PROFILE_POINT; /* ModRDN Conflict; Entry with Target DN Exists; Unique ID already in RDN - Change to Lost and Found entry */ goto bailout; } mod_namingconflict_attr (op_uniqueid, target_sdn, existing_sdn, opcsn); slapi_pblock_set(pb, SLAPI_MODRDN_NEWRDN, newrdn_with_uniqueid); slapi_log_error(slapi_log_urp, sessionid, "urp_modrdn: Naming conflict MODRDN. Rename target entry to %s\n", newrdn_with_uniqueid ); rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_EXISTING_DN_ENTRY); PROFILE_POINT; /* ModRDN Conflict; Entry with Target DN Exists; Rename Operation Entry */ goto bailout; } if ( r>0 ) { /* The existing entry is a loser */ int resolve = urp_annotate_dn (sessionid, existing_entry, opcsn, "MODRDN"); if(!resolve) { op_result= LDAP_OPERATIONS_ERROR; slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result); rc= -1; /* Abort this Operation */ goto bailout; } rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_EXISTING_DN_ENTRY); rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_NEWPARENT_ENTRY); if (LDAP_NO_SUCH_OBJECT == resolve) { /* This means that existing_dn_entry did not really exist!!! * This indicates that a get_copy_of_entry -> dn2entry returned * an entry (existing_dn_entry) that was already removed from the ldbm. * This is bad, because it indicates a dn cache or DB corruption. * However, as far as the conflict is concerned, this error is harmless: * if the existing_dn_entry did not exist in the first place, there was no * conflict!! Return 0 for success to break the ldbm_back_modrdn loop * and get out of this inexistent conflict resolution ASAP. */ rc = 0; } /* Set flag to remove possible old naming conflict */ del_old_replconflict_attr = 1; PROFILE_POINT; /* ModRDN Conflict; Entry with Target DN Exists; Rename Entry with Target DN */ goto bailout; } } else { /* * No entry with the target DN exists. */ /* Set flag to remove possible old naming conflict */ del_old_replconflict_attr = 1; if(new_parent_entry!=NULL) { /* The new superior entry exists */ rc= 0; /* OK, Apply the ModRDN */ PROFILE_POINT; /* ModRDN Conflict; OK */ goto bailout; } /* The new superior entry doesn't exist */ slapi_pblock_get(pb, SLAPI_MODRDN_NEWSUPERIOR_SDN, &newsuperior); if(newsuperior == NULL) { /* (new_parent_entry==NULL && newsuperiordn==NULL) * This is ok - SLAPI_MODRDN_NEWPARENT_ENTRY will * only be set if SLAPI_MODRDN_NEWSUPERIOR_SDN was * suplied by the client. If it wasn't, we're just * changing the RDN of the entry. In that case, * if the entry exists, its parent won't change * when it's renamed, and therefore we can assume * its parent exists. */ rc=0; PROFILE_POINT; /* ModRDN OK */ goto bailout; } if((0 == slapi_sdn_compare(slapi_entry_get_sdn(parent_entry), newsuperior)) || is_suffix_dn (pb, newsuperior, &parentdn) ) { /* * The new superior is the same as the current one, or * this entry is a suffix whose parent can be absent. */ rc= 0; /* OK, Move the entry */ PROFILE_POINT; /* ModRDN Conflict; Absent Target Parent; Create Suffix Entry */ goto bailout; } /* * This entry is not a suffix entry, so the parent entry should exist. * (This shouldn't happen in a ds5 server) */ slapi_pblock_get ( pb, SLAPI_OPERATION_PARAMETERS, &op_params ); op_result = create_glue_entry (pb, sessionid, newsuperior, op_params->p.p_modrdn.modrdn_newsuperior_address.uniqueid, opcsn); if (LDAP_SUCCESS != op_result) { /* * FATAL ERROR * We should probably just abort the rename * this will cause replication divergence requiring * admin intercession */ slapi_log_error( SLAPI_LOG_FATAL, sessionid, "urp_modrdn: Parent %s couldn't be found, nor recreated as a glue entry\n", slapi_sdn_get_dn(newsuperior) ); op_result= LDAP_OPERATIONS_ERROR; slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result); rc = SLAPI_PLUGIN_FAILURE; /* Ignore this Operation */ PROFILE_POINT; goto bailout; } /* The backend add code should now search for the parent again. */ rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_NEWPARENT_ENTRY); PROFILE_POINT; /* ModRDN Conflict; Absent Target Parent - Change to Lost and Found entry */ goto bailout; } bailout: if ( del_old_replconflict_attr && rc == 0 ) { del_replconflict_attr (target_entry, opcsn, 0); } if ( parentdn ) slapi_sdn_free(&parentdn); return rc; }
static int is_suffix_entry ( Slapi_PBlock *pb, Slapi_Entry *entry, Slapi_DN **parentdn ) { return is_suffix_dn ( pb, slapi_entry_get_sdn(entry), parentdn ); }
/* * Get a Slapi_Entry ready to send over the wire as part of * a total update protocol stream. Convert the entry and all * of its state information to a BerElement which will be the * payload of an extended LDAP operation. * * Entries consist of: * - An entry DN * - A uniqueID * - A set of present attributes, each of which consists of: * - A set of present values, each of which consists of: * - A value * - A set of CSNs * - A set of deleted values, each of which consists of: * - A value * - A set of CSNs * - A set of deleted attibutes, each of which consists of: * - An attribute type * - A set of CSNs. Note that this list of CSNs will always contain exactly one CSN. * * This all gets mashed into one BerElement, ready to be blasted over the wire to * a replica. * */ BerElement * entry2bere(const Slapi_Entry *e, char **excluded_attrs) { BerElement *ber = NULL; const char *str = NULL; const char *dnstr = NULL; char *type; Slapi_DN *sdn = NULL; Slapi_Attr *attr = NULL, *prev_attr; int rc; PR_ASSERT(NULL != e); if ((ber = ber_alloc()) == NULL) { goto loser; } BER_DEBUG("{"); if (ber_printf(ber, "{") == -1) /* Begin outer sequence */ { goto loser; } /* Get the entry's uniqueid */ if ((str = slapi_entry_get_uniqueid(e)) == NULL) { goto loser; } BER_DEBUG("s(uniqueid)"); if (ber_printf(ber, "s", str) == -1) { goto loser; } /* Get the entry's DN */ if ((sdn = slapi_entry_get_sdn((Slapi_Entry *)e)) == NULL) /* XXXggood had to cast away const */ { goto loser; } if ((dnstr = slapi_sdn_get_dn(sdn)) == NULL) { goto loser; } BER_DEBUG("s(dn)"); if (ber_printf(ber, "s", dnstr) == -1) { goto loser; } /* Next comes the annoted list of the entry's attributes */ BER_DEBUG("["); if (ber_printf(ber, "[") == -1) /* Begin set of attributes */ { goto loser; } /* * We iterate over all of the non-deleted attributes first. */ slapi_entry_first_attr(e, &attr); while (NULL != attr) { /* ONREPL - skip uniqueid attribute since we already sent uniqueid This is a hack; need to figure a better way of storing uniqueid in an entry */ slapi_attr_get_type (attr, &type); if (strcasecmp (type, SLAPI_ATTR_UNIQUEID) != 0) { /* Check to see if this attribute is excluded by the fractional list */ if ( (NULL == excluded_attrs) || !charray_inlist(excluded_attrs,type)) { /* Process this attribute */ rc = my_ber_printf_attr (ber, attr, PR_FALSE); if (rc != 0) { goto loser; } } } prev_attr = attr; slapi_entry_next_attr(e, prev_attr, &attr); } /* * Now iterate over the deleted attributes. */ entry_first_deleted_attribute(e, &attr); while (attr != NULL) { slapi_attr_get_type (attr, &type); /* Check to see if this attribute is excluded by the fractional list */ if ( (NULL == excluded_attrs) || !charray_inlist(excluded_attrs,type)) { /* Process this attribute */ rc = my_ber_printf_attr (ber, attr, PR_TRUE); if (rc != 0) { goto loser; } } entry_next_deleted_attribute(e, &attr); } BER_DEBUG("]"); if (ber_printf(ber, "]") == -1) /* End set for attributes */ { goto loser; } BER_DEBUG("}"); if (ber_printf(ber, "}") == -1) /* End sequence for this entry */ { goto loser; } /* If we get here, everything went ok */ BER_DEBUG("\n"); goto free_and_return; loser: if (NULL != ber) { ber_free(ber, 1); ber = NULL; } free_and_return: return ber; }