/* * Read configuration and create a configuration data structure. * This is called after the server has configured itself so we can check * schema and whatnot. * Returns an LDAP error code (LDAP_SUCCESS if all goes well). */ int ipa_winsync_config(Slapi_Entry *config_e) { int returncode = LDAP_SUCCESS; char returntext[SLAPI_DSE_RETURNTEXT_SIZE]; if ( inited ) { LOG_FATAL("Error: IPA WinSync plug-in already configured. " "Please remove the plugin config entry [%s]\n", slapi_entry_get_dn_const(config_e)); return( LDAP_PARAM_ERROR ); } /* initialize fields */ if ((theConfig.lock = slapi_new_mutex()) == NULL) { return( LDAP_LOCAL_ERROR ); } /* init defaults */ theConfig.config_e = slapi_entry_alloc(); slapi_entry_init(theConfig.config_e, slapi_ch_strdup(""), NULL); theConfig.flatten = PR_TRUE; if (SLAPI_DSE_CALLBACK_OK == ipa_winsync_validate_config(NULL, NULL, config_e, &returncode, returntext, NULL)) { ipa_winsync_apply_config(NULL, NULL, config_e, &returncode, returntext, NULL); } /* config DSE must be initialized before we get here */ if (returncode == LDAP_SUCCESS) { const char *config_dn = slapi_entry_get_dn_const(config_e); slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, config_dn, LDAP_SCOPE_BASE, IPA_WINSYNC_CONFIG_FILTER, ipa_winsync_validate_config,NULL); slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_POSTOP, config_dn, LDAP_SCOPE_BASE, IPA_WINSYNC_CONFIG_FILTER, ipa_winsync_apply_config,NULL); slapi_config_register_callback(SLAPI_OPERATION_MODRDN, DSE_FLAG_PREOP, config_dn, LDAP_SCOPE_BASE, IPA_WINSYNC_CONFIG_FILTER, dont_allow_that, NULL); slapi_config_register_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP, config_dn, LDAP_SCOPE_BASE, IPA_WINSYNC_CONFIG_FILTER, dont_allow_that, NULL); slapi_config_register_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, config_dn, LDAP_SCOPE_BASE, IPA_WINSYNC_CONFIG_FILTER, ipa_winsync_search,NULL); } inited = 1; if (returncode != LDAP_SUCCESS) { LOG_FATAL("Error %d: %s\n", returncode, returntext); } return returncode; }
/* Checks if created attributes are used in the RDN. * Returns 1 if created attrs are in the RDN, and * 0 if created attrs are not in the RDN. Returns * -1 if an error occurred. */ static int check_rdn_for_created_attrs(Slapi_Entry *e) { int i, rc = 0; Slapi_RDN *rdn = NULL; char *value = NULL; char *type[] = {SLAPI_ATTR_UNIQUEID, "modifytimestamp", "modifiersname", "internalmodifytimestamp", "internalmodifiersname", "createtimestamp", "creatorsname", 0}; if ((rdn = slapi_rdn_new())) { slapi_rdn_init_dn(rdn, slapi_entry_get_dn_const(e)); for (i = 0; type[i] != NULL; i++) { if (slapi_rdn_contains_attr(rdn, type[i], &value)) { slapi_log_err(SLAPI_LOG_TRACE, "check_rdn_for_created_attrs", "Invalid DN. RDN contains %s attribute\n", type[i]); rc = 1; break; } } slapi_rdn_free(&rdn); } else { slapi_log_err(SLAPI_LOG_TRACE, "check_rdn_for_created_attrs", "Error allocating RDN\n"); rc = -1; } return rc; }
/* * urp_annotate_dn: * Returns 0 on failure * Returns > 0 on success (1 on general conflict resolution success, LDAP_NO_SUCH_OBJECT on no-conflict success) * * Use this function to annotate an existing entry only. To annotate * a new entry (the operation entry) see urp_add_operation. */ static int urp_annotate_dn (char *sessionid, const Slapi_Entry *entry, CSN *opcsn, const char *optype) { int rc = 0; /* Fail */ int op_result; char *newrdn; const char *uniqueid; const Slapi_DN *basesdn; const char *basedn; uniqueid = slapi_entry_get_uniqueid (entry); basesdn = slapi_entry_get_sdn_const (entry); basedn = slapi_entry_get_dn_const (entry); newrdn = get_rdn_plus_uniqueid ( sessionid, basedn, uniqueid ); if(newrdn!=NULL) { mod_namingconflict_attr (uniqueid, basesdn, basesdn, opcsn); op_result = urp_fixup_rename_entry ( entry, newrdn, 0 ); switch(op_result) { case LDAP_SUCCESS: slapi_log_error(slapi_log_urp, sessionid, "Naming conflict %s. Renamed existing entry to %s\n", optype, newrdn); rc = 1; break; case LDAP_NO_SUCH_OBJECT: /* This means that entry did not really exist!!! * This is clearly indicating that there is a * get_copy_of_entry -> dn2entry returned * an entry (entry) that was already removed * from the ldbm database... * This is bad, because it clearly indicates * some kind of db or cache corruption. We need to print * this fact clearly in the errors log to try * to solve this corruption one day. * However, as far as the conflict is concerned, * this error is completely harmless: * if thew entry did not exist in the first place, * there was never a room * for a conflict!! After fix for 558293, this * state can't be reproduced anymore (5-Oct-01) */ slapi_log_error( SLAPI_LOG_FATAL, sessionid, "Entry %s exists in cache but not in DB\n", basedn ); rc = LDAP_NO_SUCH_OBJECT; break; default: slapi_log_error( slapi_log_urp, sessionid, "Failed to annotate %s, err=%d\n", newrdn, op_result); } slapi_ch_free ( (void**)&newrdn ); } return rc; }
/* * Conflict removal */ int urp_post_delete_operation( Slapi_PBlock *pb ) { Slapi_Operation *op; Slapi_Entry *entry; CSN *opcsn; char sessionid[REPL_SESSION_ID_SIZE]; int op_result; /* * Do not abandon the post op - the processed CSN needs to be * committed to keep the consistency between the changelog * and the backend DB * if ( slapi_op_abandoned(pb) ) return 0; */ get_repl_session_id (pb, sessionid, &opcsn); /* * Conflict removal from the parent entry: * If the parent is glue and has no more children, * turn the parent to tombstone */ slapi_pblock_get ( pb, SLAPI_DELETE_GLUE_PARENT_ENTRY, &entry ); if ( entry != NULL ) { op_result = entry_to_tombstone ( pb, entry ); if ( op_result == LDAP_SUCCESS ) { slapi_log_error ( slapi_log_urp, sessionid, "Tombstoned glue entry %s since it has no more children\n", slapi_entry_get_dn_const (entry) ); } } slapi_pblock_get( pb, SLAPI_OPERATION, &op); if (!operation_is_flag_set(op, OP_FLAG_REPL_FIXUP)) { /* * Conflict removal from the peers of the old dn */ urp_naming_conflict_removal (pb, sessionid, opcsn, "DEL"); } return 0; }
static int add_uniqueid (Slapi_Entry *e) { char *uniqueid; int rc; /* generate uniqueID for the entry */ rc = slapi_uniqueIDGenerateString (&uniqueid); if (rc == UID_SUCCESS) { slapi_entry_set_uniqueid (e, uniqueid); } else { slapi_log_err(SLAPI_LOG_ERR, "add_uniqueid", "Uniqueid generation failed for %s; error = %d\n", slapi_entry_get_dn_const(e), rc); } return( rc ); }
/* This function is called to process operation that come over external connections */ void do_add( Slapi_PBlock *pb ) { Slapi_Operation *operation; BerElement *ber; char *last; ber_len_t len = LBER_ERROR; ber_tag_t tag; Slapi_Entry *e = NULL; int err; int rc; PRBool searchsubentry=PR_TRUE; slapi_log_err(SLAPI_LOG_TRACE, "do_add", "==>\n"); slapi_pblock_get( pb, SLAPI_OPERATION, &operation); ber = operation->o_ber; /* count the add request */ slapi_counter_increment(g_get_global_snmp_vars()->ops_tbl.dsAddEntryOps); /* * Parse the add request. It looks like this: * * AddRequest := [APPLICATION 14] SEQUENCE { * name DistinguishedName, * attrs SEQUENCE OF SEQUENCE { * type AttributeType, * values SET OF AttributeValue * } * } */ /* get the name */ { char *rawdn = NULL; Slapi_DN mysdn; if ( ber_scanf( ber, "{a", &rawdn ) == LBER_ERROR ) { slapi_ch_free_string(&rawdn); slapi_log_err(SLAPI_LOG_ERR, "do_add", "ber_scanf failed (op=Add; params=DN)\n"); op_shared_log_error_access (pb, "ADD", "???", "decoding error"); send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL, "decoding error", 0, NULL ); return; } /* Check if we should be performing strict validation. */ if (config_get_dn_validate_strict()) { /* check that the dn is formatted correctly */ rc = slapi_dn_syntax_check(pb, rawdn, 1); if (rc) { /* syntax check failed */ op_shared_log_error_access(pb, "ADD", rawdn?rawdn:"", "strict: invalid dn"); send_ldap_result(pb, LDAP_INVALID_DN_SYNTAX, NULL, "invalid dn", 0, NULL); slapi_ch_free_string(&rawdn); return; } } slapi_sdn_init_dn_passin(&mysdn, rawdn); if (rawdn && (strlen(rawdn) > 0) && (NULL == slapi_sdn_get_dn(&mysdn))) { /* normalization failed */ op_shared_log_error_access(pb, "ADD", rawdn, "invalid dn"); send_ldap_result(pb, LDAP_INVALID_DN_SYNTAX, NULL, "invalid dn", 0, NULL); slapi_sdn_done(&mysdn); return; } e = slapi_entry_alloc(); /* Responsibility for DN is passed to the Entry. */ slapi_entry_init_ext(e, &mysdn, NULL); slapi_sdn_done(&mysdn); } slapi_log_err(SLAPI_LOG_ARGS, "do_add", "dn (%s)\n", (char *)slapi_entry_get_dn_const(e)); /* get the attrs */ for ( tag = ber_first_element( ber, &len, &last ); tag != LBER_DEFAULT && tag != LBER_END_OF_SEQORSET; tag = ber_next_element( ber, &len, last ) ) { char *type = NULL, *normtype = NULL; struct berval **vals = NULL; len = -1; /* reset - not used in loop */ if ( ber_scanf( ber, "{a{V}}", &type, &vals ) == LBER_ERROR ) { op_shared_log_error_access (pb, "ADD", slapi_sdn_get_dn (slapi_entry_get_sdn_const(e)), "decoding error"); send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL, "decoding error", 0, NULL ); slapi_ch_free_string(&type); ber_bvecfree( vals ); goto free_and_return; } if ( vals == NULL ) { slapi_log_err(SLAPI_LOG_ERR, "do_add - no values for type %s\n", type, 0, 0 ); op_shared_log_error_access (pb, "ADD", slapi_sdn_get_dn (slapi_entry_get_sdn_const(e)), "null value"); send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL, NULL, 0, NULL ); slapi_ch_free_string(&type); goto free_and_return; } normtype = slapi_attr_syntax_normalize(type); if ( !normtype || !*normtype ) { char ebuf[SLAPI_DSE_RETURNTEXT_SIZE]; rc = LDAP_INVALID_SYNTAX; slapi_create_errormsg(ebuf, sizeof(ebuf), "invalid type '%s'", type); op_shared_log_error_access (pb, "ADD", slapi_sdn_get_dn (slapi_entry_get_sdn_const(e)), ebuf); send_ldap_result( pb, rc, NULL, ebuf, 0, NULL ); slapi_ch_free_string(&type); slapi_ch_free( (void**)&normtype ); ber_bvecfree( vals ); goto free_and_return; } slapi_ch_free_string(&type); /* for now we just ignore attributes that client is not allowed to modify so not to break existing clients */ if (op_shared_is_allowed_attr (normtype, pb->pb_conn->c_isreplication_session)){ if (( rc = slapi_entry_add_values( e, normtype, vals )) != LDAP_SUCCESS ) { slapi_log_access( LDAP_DEBUG_STATS, "conn=%" NSPRIu64 " op=%d ADD dn=\"%s\", add values for type %s failed\n", pb->pb_conn->c_connid, operation->o_opid, slapi_entry_get_dn_const(e), normtype ); send_ldap_result( pb, rc, NULL, NULL, 0, NULL ); slapi_ch_free( (void**)&normtype ); ber_bvecfree( vals ); goto free_and_return; } /* if this is uniqueid attribute, set uniqueid field of the entry */ if (strcasecmp (normtype, SLAPI_ATTR_UNIQUEID) == 0) { e->e_uniqueid = slapi_ch_strdup (vals[0]->bv_val); } if(searchsubentry) searchsubentry=check_oc_subentry(e,vals,normtype); } slapi_ch_free( (void**)&normtype ); ber_bvecfree( vals ); } /* Ensure that created attributes are not used in the RDN. */ if (check_rdn_for_created_attrs(e)) { op_shared_log_error_access (pb, "ADD", slapi_sdn_get_dn(slapi_entry_get_sdn_const(e)), "invalid DN"); send_ldap_result( pb, LDAP_INVALID_DN_SYNTAX, NULL, "illegal attribute in RDN", 0, NULL ); goto free_and_return; } /* len, is ber_len_t, which is uint. Can't be -1. May be better to remove (len != 0) check */ if ( (tag != LBER_END_OF_SEQORSET) && (len != -1) ) { op_shared_log_error_access (pb, "ADD", slapi_sdn_get_dn (slapi_entry_get_sdn_const(e)), "decoding error"); send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL, "decoding error", 0, NULL ); goto free_and_return; } /* * in LDAPv3 there can be optional control extensions on * the end of an LDAPMessage. we need to read them in and * pass them to the backend. */ if ( (err = get_ldapmessage_controls( pb, ber, NULL )) != 0 ) { op_shared_log_error_access (pb, "ADD", slapi_sdn_get_dn (slapi_entry_get_sdn_const(e)), "failed to decode LDAP controls"); send_ldap_result( pb, err, NULL, NULL, 0, NULL ); goto free_and_return; } slapi_pblock_set( pb, SLAPI_REQUESTOR_ISROOT, &operation->o_isroot ); slapi_pblock_set( pb, SLAPI_ADD_ENTRY, e ); if (pb->pb_conn->c_flags & CONN_FLAG_IMPORT) { /* this add is actually part of a bulk import -- punt */ handle_fast_add(pb, e); } else { op_shared_add ( pb ); } /* make sure that we don't free entry if it is successfully added */ e = NULL; free_and_return:; if (e) slapi_entry_free (e); }
/* 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); }
/* return the value(s) of the given attribute in the entry that matches the given criteria. The criteria must match one and only one entry. Returns: -1 - problem doing internal search LDAP_UNWILLING_TO_PERFORM - more than one matching entry LDAP_NO_SUCH_OBJECT - no entry found that matched 0 and attrval == NULL - entry found but no attribute other ldap error - error doing search for given basedn */ static int internal_find_entry_get_attr_val(const Slapi_DN *basedn, int scope, const char *filter, const char *attrname, Slapi_ValueSet **svs, char **attrval) { Slapi_Entry **entries = NULL; Slapi_PBlock *pb = NULL; const char *search_basedn = slapi_sdn_get_dn(basedn); int search_scope = scope; int ret = LDAP_SUCCESS; const char *attrs[2] = {attrname, NULL}; if (svs) { *svs = NULL; } if (attrval) { *attrval = NULL; } pb = slapi_pblock_new(); slapi_search_internal_set_pb(pb, search_basedn, search_scope, filter, (char **)attrs, 0, NULL, NULL, ipa_winsync_get_plugin_identity(), 0); slapi_search_internal_pb(pb); /* This search may return no entries, but should never return an error */ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret); if (ret != LDAP_SUCCESS) { LOG_FATAL("Error [%d:%s] searching for base [%s] filter [%s]" " attr [%s]\n", ret, ldap_err2string(ret), search_basedn, filter, attrs[0]); goto out1; } slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries); if (entries && entries[0] && entries[1]) { /* error - should never be more than one matching entry */ LOG_FATAL("Error: more than one entry matches search for " "base [%s] filter [%s] attr [%s]\n", search_basedn, filter, attrs[0]); ret = LDAP_UNWILLING_TO_PERFORM; goto out1; } if (entries && entries[0]) { /* found one */ if (svs) { Slapi_Attr *attr = NULL; if (!slapi_entry_attr_find(entries[0], attrname, &attr) && (NULL != attr)) { /* slapi_attr_get_valueset allocates svs - must be freed later */ slapi_attr_get_valueset(attr, svs); } } if (attrval) { if (!strcmp(attrname, "dn")) { /* special - to just get the DN */ *attrval = slapi_ch_strdup(slapi_entry_get_dn_const(entries[0])); } else { *attrval = slapi_entry_attr_get_charptr(entries[0], attrname); } } } else { ret = LDAP_NO_SUCH_OBJECT; LOG("Did not find an entry for search " "base [%s] filter [%s] attr [%s]\n", search_basedn, filter, attrs[0]); } out1: if (pb) { slapi_free_search_results_internal(pb); slapi_pblock_destroy(pb); pb = NULL; } return ret; }
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 */ int urp_delete_operation( Slapi_PBlock *pb ) { const Slapi_Entry *deleteentry; CSN *opcsn= NULL; char sessionid[REPL_SESSION_ID_SIZE]; int op_result= 0; int rc = SLAPI_PLUGIN_SUCCESS; /* OK */ if ( slapi_op_abandoned(pb) ) { return rc; } slapi_pblock_get(pb, SLAPI_DELETE_EXISTING_ENTRY, &deleteentry); get_repl_session_id (pb, sessionid, &opcsn); if(deleteentry==NULL) /* uniqueid can't be found */ { op_result= LDAP_NO_SUCH_OBJECT; slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result); rc = SLAPI_PLUGIN_FAILURE; /* Don't apply the Delete */ PROFILE_POINT; /* Delete Operation; Entry not exist. */ } else if(is_tombstone_entry(deleteentry)) { /* The entry is already a Tombstone, ignore this delete. */ op_result= LDAP_SUCCESS; slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result); rc = SLAPI_PLUGIN_NOOP; /* Don't apply the Delete */ slapi_log_error(slapi_log_urp, sessionid, "urp_delete: Entry \"%s\" is already a Tombstone.\n", slapi_entry_get_dn_const(deleteentry)); PROFILE_POINT; /* Delete Operation; Already a Tombstone. */ } else /* The entry to be deleted exists and is not a tombstone */ { get_repl_session_id (pb, sessionid, &opcsn); /* Check if the entry has children. */ if(!slapi_entry_has_children(deleteentry)) { /* Remove possible conflict attributes */ del_replconflict_attr (deleteentry, opcsn, 0); rc = SLAPI_PLUGIN_SUCCESS; /* OK, to delete the entry */ PROFILE_POINT; /* Delete Operation; OK. */ } else { /* Turn this entry into a glue_absent_parent entry */ entry_to_glue(sessionid, deleteentry, REASON_RESURRECT_ENTRY, opcsn); /* Turn the Delete into a No-Op */ op_result= LDAP_SUCCESS; slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result); rc = SLAPI_PLUGIN_NOOP; /* Don't apply the Delete */ slapi_log_error(slapi_log_urp, sessionid, "urp_delete: Turn entry \"%s\" into a glue_absent_parent entry.\n", slapi_entry_get_dn_const(deleteentry)); PROFILE_POINT; /* Delete Operation; Entry has children. */ } } 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; }
/* * Return 0 for OK, * -1 for Ignore or Error depending on SLAPI_RESULT_CODE, * >0 for action code * Action Code Bit 0: Fetch existing entry. * Action Code Bit 1: Fetch parent entry. * The function is called as a be pre-op on consumers. */ int urp_add_operation( Slapi_PBlock *pb ) { Slapi_Entry *existing_uniqueid_entry; Slapi_Entry *existing_dn_entry; Slapi_Entry *addentry; const char *adduniqueid; CSN *opcsn; const char *basedn; char sessionid[REPL_SESSION_ID_SIZE]; int r; int op_result= 0; int rc= 0; /* OK */ Slapi_DN *sdn = NULL; if ( slapi_op_abandoned(pb) ) { return rc; } get_repl_session_id (pb, sessionid, &opcsn); slapi_pblock_get( pb, SLAPI_ADD_EXISTING_UNIQUEID_ENTRY, &existing_uniqueid_entry ); if (existing_uniqueid_entry!=NULL) { /* * An entry with this uniqueid already exists. * - It could be a replay of the same Add, or * - It could be a UUID generation collision, or */ /* * 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 */ /* op_result = LDAP_SUCCESS; */ slapi_log_error(slapi_log_urp, sessionid, "urp_add (%s): an entry with this uniqueid already exists.\n", slapi_entry_get_dn_const(existing_uniqueid_entry)); op_result= LDAP_UNWILLING_TO_PERFORM; slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result); rc = SLAPI_PLUGIN_NOOP; /* Ignore this Operation */ PROFILE_POINT; /* Add Conflict; UniqueID Exists; Ignore */ goto bailout; } slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &addentry ); slapi_pblock_get( pb, SLAPI_ADD_EXISTING_DN_ENTRY, &existing_dn_entry ); if (existing_dn_entry==NULL) /* The target DN does not exist */ { /* Check for parent entry... this could be an orphan. */ Slapi_Entry *parententry; slapi_pblock_get( pb, SLAPI_ADD_PARENT_ENTRY, &parententry ); rc = urp_add_resolve_parententry (pb, sessionid, addentry, parententry, opcsn); PROFILE_POINT; /* Add Entry */ goto bailout; } /* * Naming conflict: an entry with the target DN already exists. * Compare the DistinguishedNameCSN of the existing entry * and the OperationCSN. The smaller CSN wins. The loser changes * its RDN to uniqueid+baserdn, and adds operational attribute * ATTR_NSDS5_REPLCONFLIC. */ basedn = slapi_entry_get_ndn (addentry); adduniqueid = slapi_entry_get_uniqueid (addentry); r = csn_compare (entry_get_dncsn(existing_dn_entry), opcsn); if (r<0) { /* Entry to be added is a loser */ char *newdn= get_dn_plus_uniqueid (sessionid, basedn, adduniqueid); if(newdn==NULL) { op_result= LDAP_OPERATIONS_ERROR; slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result); rc = SLAPI_PLUGIN_NOOP; /* Abort this Operation */ slapi_log_error(slapi_log_urp, sessionid, "urp_add (%s): Add conflict; Unique ID (%s) already in RDN\n", basedn, adduniqueid); PROFILE_POINT; /* Add Conflict; Entry Exists; Unique ID already in RDN - Abort this update. */ } else { /* Add the nsds5ReplConflict attribute in the mods */ Slapi_Attr *attr = NULL; Slapi_Value **vals = NULL; Slapi_RDN *rdn; char buf[BUFSIZ]; PR_snprintf(buf, BUFSIZ, "%s %s", REASON_ANNOTATE_DN, basedn); if (slapi_entry_attr_find (addentry, ATTR_NSDS5_REPLCONFLICT, &attr) == 0) { /* ATTR_NSDS5_REPLCONFLICT exists */ slapi_log_error(SLAPI_LOG_FATAL, sessionid, "urp_add: New entry has nsds5ReplConflict already\n"); vals = attr_get_present_values (attr); /* this returns a pointer to the contents */ } if ( vals == NULL || *vals == NULL ) { /* Add new attribute */ slapi_entry_add_string (addentry, ATTR_NSDS5_REPLCONFLICT, buf); } else { /* * Replace old attribute. We don't worry about the index * change here since the entry is yet to be added. */ slapi_value_set_string (*vals, buf); } /* slapi_pblock_get(pb, SLAPI_ADD_TARGET, &dn); */ slapi_pblock_get(pb, SLAPI_ADD_TARGET_SDN, &sdn); slapi_sdn_free(&sdn); slapi_entry_set_normdn(addentry, newdn); /* dn: passin */ sdn = slapi_sdn_dup(slapi_entry_get_sdn_const(addentry)); slapi_pblock_set(pb, SLAPI_ADD_TARGET_SDN, sdn); rdn = slapi_rdn_new_sdn ( slapi_entry_get_sdn_const(addentry) ); slapi_log_error (slapi_log_urp, sessionid, "urp_add: Naming conflict ADD. Add %s instead\n", slapi_rdn_get_rdn(rdn)); slapi_rdn_free(&rdn); rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_EXISTING_DN_ENTRY); PROFILE_POINT; /* Add Conflict; Entry Exists; Rename Operation Entry */ } } else if(r>0) { /* Existing entry is a loser */ if (!urp_annotate_dn(sessionid, existing_dn_entry, opcsn, "ADD")) { op_result= LDAP_OPERATIONS_ERROR; slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result); slapi_log_error(slapi_log_urp, sessionid, "urp_add (%s): Entry to be added is a loser; " "urp_annotate_dn failed.\n", basedn); rc = SLAPI_PLUGIN_NOOP; /* Ignore this Operation */ } else { /* The backend add code should now search for the existing entry 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; Entry Exists; Rename Existing Entry */ } else /* r==0 */ { /* The CSN of the Operation and the Entry DN are the same. * This could only happen if: * a) There are two replicas with the same ReplicaID. * b) We've seen the Operation before. * Let's go with (b) and ignore the little bastard. */ /* * 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 */ /* op_result = LDAP_SUCCESS; */ slapi_log_error(slapi_log_urp, sessionid, "urp_add (%s): The CSN of the Operation and the Entry DN are the same.", slapi_entry_get_dn_const(existing_dn_entry)); op_result= LDAP_UNWILLING_TO_PERFORM; slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result); rc = SLAPI_PLUGIN_NOOP; /* Ignore this Operation */ PROFILE_POINT; /* Add Conflict; Entry Exists; Same CSN */ } bailout: 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 ); } }
/* * Thread routine for sending search results to a client * which is persistently waiting for them. * * This routine will terminate when either (a) the ps_complete * flag is set, or (b) the associated operation is abandoned. * In any case, the thread won't notice until it wakes from * sleeping on the ps_list condition variable, so it needs * to be awakened. */ static void ps_send_results( void *arg ) { PSearch *ps = (PSearch *)arg; PSEQNode *peq, *peqnext; struct slapi_filter *filter = 0; char *base = NULL; Slapi_DN *sdn = NULL; char *fstr = NULL; char **pbattrs = NULL; int conn_acq_flag = 0; g_incr_active_threadcnt(); /* need to acquire a reference to this connection so that it will not be released or cleaned up out from under us */ PR_Lock( ps->ps_pblock->pb_conn->c_mutex ); conn_acq_flag = connection_acquire_nolock(ps->ps_pblock->pb_conn); PR_Unlock( ps->ps_pblock->pb_conn->c_mutex ); if (conn_acq_flag) { slapi_log_error(SLAPI_LOG_CONNS, "Persistent Search", "conn=%" NSPRIu64 " op=%d Could not acquire the connection - psearch aborted\n", ps->ps_pblock->pb_conn->c_connid, ps->ps_pblock->pb_op->o_opid); } PR_Lock( psearch_list->pl_cvarlock ); while ( (conn_acq_flag == 0) && !ps->ps_complete ) { /* Check for an abandoned operation */ if ( ps->ps_pblock->pb_op == NULL || slapi_op_abandoned( ps->ps_pblock ) ) { slapi_log_error(SLAPI_LOG_CONNS, "Persistent Search", "conn=%" NSPRIu64 " op=%d The operation has been abandoned\n", ps->ps_pblock->pb_conn->c_connid, ps->ps_pblock->pb_op->o_opid); break; } if ( NULL == ps->ps_eq_head ) { /* Nothing to do */ PR_WaitCondVar( psearch_list->pl_cvar, PR_INTERVAL_NO_TIMEOUT ); } else { /* dequeue the item */ int attrsonly; char **attrs; LDAPControl **ectrls; Slapi_Entry *ec; Slapi_Filter *f = NULL; PR_Lock( ps->ps_lock ); peq = ps->ps_eq_head; ps->ps_eq_head = peq->pe_next; if ( NULL == ps->ps_eq_head ) { ps->ps_eq_tail = NULL; } PR_Unlock( ps->ps_lock ); /* Get all the information we need to send the result */ ec = peq->pe_entry; slapi_pblock_get( ps->ps_pblock, SLAPI_SEARCH_ATTRS, &attrs ); slapi_pblock_get( ps->ps_pblock, SLAPI_SEARCH_ATTRSONLY, &attrsonly ); if ( !ps->ps_send_entchg_controls || peq->pe_ctrls[0] == NULL ) { ectrls = NULL; } else { ectrls = peq->pe_ctrls; } /* * Send the result. Since send_ldap_search_entry can block for * up to 30 minutes, we relinquish all locks before calling it. */ PR_Unlock(psearch_list->pl_cvarlock); /* * The entry is in the right scope and matches the filter * but we need to redo the filter test here to check access * controls. See the comments at the slapi_filter_test() * call in ps_service_persistent_searches(). */ slapi_pblock_get( ps->ps_pblock, SLAPI_SEARCH_FILTER, &f ); /* See if the entry meets the filter and ACL criteria */ if ( slapi_vattr_filter_test( ps->ps_pblock, ec, f, 1 /* verify_access */ ) == 0 ) { int rc = 0; slapi_pblock_set( ps->ps_pblock, SLAPI_SEARCH_RESULT_ENTRY, ec ); rc = send_ldap_search_entry( ps->ps_pblock, ec, ectrls, attrs, attrsonly ); if (rc) { slapi_log_error(SLAPI_LOG_CONNS, "Persistent Search", "conn=%" NSPRIu64 " op=%d Error %d sending entry %s with op status %d\n", ps->ps_pblock->pb_conn->c_connid, ps->ps_pblock->pb_op->o_opid, rc, slapi_entry_get_dn_const(ec), ps->ps_pblock->pb_op->o_status); } } PR_Lock(psearch_list->pl_cvarlock); /* Deallocate our wrapper for this entry */ pe_ch_free( &peq ); } } PR_Unlock( psearch_list->pl_cvarlock ); ps_remove( ps ); /* indicate the end of search */ plugin_call_plugins( ps->ps_pblock , SLAPI_PLUGIN_POST_SEARCH_FN ); /* free things from the pblock that were not free'd in do_search() */ /* we strdup'd this in search.c - need to free */ slapi_pblock_get( ps->ps_pblock, SLAPI_ORIGINAL_TARGET_DN, &base ); slapi_pblock_set( ps->ps_pblock, SLAPI_ORIGINAL_TARGET_DN, NULL ); slapi_ch_free_string(&base); /* Free SLAPI_SEARCH_* before deleting op since those are held by op */ slapi_pblock_get( ps->ps_pblock, SLAPI_SEARCH_TARGET_SDN, &sdn ); slapi_pblock_set( ps->ps_pblock, SLAPI_SEARCH_TARGET_SDN, NULL ); slapi_sdn_free(&sdn); slapi_pblock_get( ps->ps_pblock, SLAPI_SEARCH_STRFILTER, &fstr ); slapi_pblock_set( ps->ps_pblock, SLAPI_SEARCH_STRFILTER, NULL ); slapi_ch_free_string(&fstr); slapi_pblock_get( ps->ps_pblock, SLAPI_SEARCH_ATTRS, &pbattrs ); slapi_pblock_set( ps->ps_pblock, SLAPI_SEARCH_ATTRS, NULL ); if ( pbattrs != NULL ) { charray_free( pbattrs ); } slapi_pblock_get(ps->ps_pblock, SLAPI_SEARCH_FILTER, &filter ); slapi_pblock_set(ps->ps_pblock, SLAPI_SEARCH_FILTER, NULL ); slapi_filter_free(filter, 1); /* Clean up the connection structure */ PR_Lock( ps->ps_pblock->pb_conn->c_mutex ); slapi_log_error(SLAPI_LOG_CONNS, "Persistent Search", "conn=%" NSPRIu64 " op=%d Releasing the connection and operation\n", ps->ps_pblock->pb_conn->c_connid, ps->ps_pblock->pb_op->o_opid); /* Delete this op from the connection's list */ connection_remove_operation( ps->ps_pblock->pb_conn, ps->ps_pblock->pb_op ); operation_free(&(ps->ps_pblock->pb_op),ps->ps_pblock->pb_conn); ps->ps_pblock->pb_op=NULL; /* Decrement the connection refcnt */ if (conn_acq_flag == 0) { /* we acquired it, so release it */ connection_release_nolock (ps->ps_pblock->pb_conn); } PR_Unlock( ps->ps_pblock->pb_conn->c_mutex ); PR_DestroyLock ( ps->ps_lock ); ps->ps_lock = NULL; slapi_ch_free((void **) &ps->ps_pblock ); for ( peq = ps->ps_eq_head; peq; peq = peqnext) { peqnext = peq->pe_next; pe_ch_free( &peq ); } slapi_ch_free((void **) &ps ); g_decr_active_threadcnt(); }
/* * This plugin entry point is called whenever an NSDS50ReplicationEntry * extended operation is received. */ int multimaster_extop_NSDS50ReplicationEntry(Slapi_PBlock *pb) { int rc; Slapi_Entry *e = NULL; Slapi_Connection *conn = NULL; PRUint64 connid = 0; int opid = 0; slapi_pblock_get(pb, SLAPI_CONN_ID, &connid); slapi_pblock_get(pb, SLAPI_OPERATION_ID, &opid); /* Decode the extended operation */ rc = decode_total_update_extop(pb, &e); if (0 == rc) { #ifdef notdef /* * Just spew LDIF so we're sure we got it right. Later we'll firehose * this into the database import code */ int len; char *str = slapi_entry2str_with_options(e, &len,SLAPI_DUMP_UNIQUEID); puts(str); free(str); #endif rc = slapi_import_entry (pb, e); /* slapi_import_entry returns an LDAP error in case of a * problem. If there's a problem, it's our responsibility * to free the slapi_entry that we're trying to import. */ if (rc != LDAP_SUCCESS) { const char *dn = slapi_entry_get_dn_const(e); slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "Error %d: could not import entry dn %s " "for total update operation conn=%" NSPRIu64 " op=%d\n", rc, dn, connid, opid); rc = -1; } } else { slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "Error %d: could not decode the total update extop " "for total update operation conn=%" NSPRIu64 " op=%d\n", rc, connid, opid); } if (rc != 0) { /* just disconnect from the supplier. bulk import is stopped when connection object is destroyed */ slapi_pblock_get (pb, SLAPI_CONNECTION, &conn); if (conn) { slapi_disconnect_server(conn); } /* cleanup */ if (e) { slapi_entry_free (e); } } return rc; }