/* * If an entry is deleted or renamed, a new winner may be * chosen from its naming competitors. * The entry with the smallest dncsn restores its original DN. */ static int urp_naming_conflict_removal ( Slapi_PBlock *pb, char *sessionid, CSN *opcsn, const char *optype ) { Slapi_Entry *min_naming_conflict_entry; Slapi_RDN *oldrdn, *newrdn; const char *oldrdnstr, *newrdnstr; int op_result; /* * Backend op has set SLAPI_URP_NAMING_COLLISION_DN to the basedn. */ min_naming_conflict_entry = urp_get_min_naming_conflict_entry (pb, sessionid, opcsn); if (min_naming_conflict_entry == NULL) { return 0; } /* Step 1: Restore the entry's original DN */ oldrdn = slapi_rdn_new_sdn(slapi_entry_get_sdn_const(min_naming_conflict_entry)); oldrdnstr = slapi_rdn_get_rdn ( oldrdn ); /* newrdnstr is the old rdn of the entry minus the nsuniqueid part */ newrdn = slapi_rdn_new_rdn ( oldrdn ); slapi_rdn_remove_attr (newrdn, SLAPI_ATTR_UNIQUEID ); newrdnstr = slapi_rdn_get_rdn ( newrdn ); /* * Set OP_FLAG_ACTION_INVOKE_FOR_REPLOP since this operation * is done after DB lock was released. The backend modrdn * will acquire the DB lock if it sees this flag. */ op_result = urp_fixup_rename_entry((const Slapi_Entry *)min_naming_conflict_entry, newrdnstr, OP_FLAG_ACTION_INVOKE_FOR_REPLOP); if ( op_result != LDAP_SUCCESS ) { slapi_log_error (slapi_log_urp, sessionid, "Failed to restore RDN of %s, err=%d\n", oldrdnstr, op_result); goto bailout; } slapi_log_error (slapi_log_urp, sessionid, "Naming conflict removed by %s. RDN of %s was restored\n", optype, oldrdnstr); /* Step2: Remove ATTR_NSDS5_REPLCONFLICT from the winning entry */ /* * A fixup op will not invoke urp_modrdn_operation(). Even it does, * urp_modrdn_operation() will do nothing because of the same CSN. */ op_result = del_replconflict_attr (min_naming_conflict_entry, opcsn, OP_FLAG_ACTION_INVOKE_FOR_REPLOP); if (op_result != LDAP_SUCCESS) { slapi_log_error(SLAPI_LOG_REPL, sessionid, "Failed to remove nsds5ReplConflict for %s, err=%d\n", newrdnstr, op_result); } bailout: slapi_entry_free(min_naming_conflict_entry); slapi_rdn_free(&oldrdn); slapi_rdn_free(&newrdn); return op_result; }
/* 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 occurs. */ static int check_rdn_for_created_attrs(const char *newrdn) { int i, rc = 0; Slapi_RDN *rdn = NULL; char *value = NULL; char *type[] = {"modifytimestamp", "createtimestamp", "creatorsname", "modifiersname", 0}; if (newrdn && *newrdn && (rdn = slapi_rdn_new())) { slapi_rdn_init_dn(rdn, newrdn); for (i = 0; type[i] != NULL; i++) { if (slapi_rdn_contains_attr(rdn, type[i], &value)) { LDAPDebug(LDAP_DEBUG_TRACE, "Invalid DN. RDN contains %s attribute\n", type[i], 0, 0); rc = 1; break; } } slapi_rdn_free(&rdn); } else { LDAPDebug(LDAP_DEBUG_TRACE, "check_rdn_for_created_attrs: Error allocating RDN\n", 0, 0, 0); rc = -1; } return rc; }
/* 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; }
static char * get_rdn_plus_uniqueid(char *sessionid, const char *olddn, const char *uniqueid) { char *newrdn; /* Check if the RDN already contains the Unique ID */ Slapi_DN *sdn= slapi_sdn_new_dn_byval(olddn); Slapi_RDN *rdn= slapi_rdn_new(); slapi_sdn_get_rdn(sdn,rdn); PR_ASSERT(uniqueid!=NULL); if(slapi_rdn_contains(rdn,SLAPI_ATTR_UNIQUEID,uniqueid,strlen(uniqueid))) { /* The Unique ID is already in the RDN. * This is a highly improbable collision. * It suggests that a duplicate UUID was generated. * This will cause replication divergence and will * require admin intercession */ slapi_log_error(SLAPI_LOG_FATAL, sessionid, "Annotated DN %s has naming conflict\n", olddn ); newrdn= NULL; } else { slapi_rdn_add(rdn,SLAPI_ATTR_UNIQUEID,uniqueid); newrdn= slapi_ch_strdup(slapi_rdn_get_rdn(rdn)); } slapi_sdn_free(&sdn); slapi_rdn_free(&rdn); return newrdn; }
/* * Get the PAM identity from the value of the leftmost RDN in the BIND DN. */ static char * derive_from_bind_dn(Slapi_PBlock *pb, const Slapi_DN *bindsdn, MyStrBuf *pam_id) { Slapi_RDN *rdn; char *type = NULL; char *value = NULL; rdn = slapi_rdn_new_sdn(bindsdn); slapi_rdn_get_first(rdn, &type, &value); init_my_str_buf(pam_id, value); slapi_rdn_free(&rdn); return pam_id->str; }
/* * Callers should have already allocated *gerstr to hold at least * "entryLevelRights: adnvxxx\n". */ unsigned long _ger_get_entry_rights ( Slapi_PBlock *gerpb, Slapi_Entry *e, const char *subjectndn, char **gerstr, size_t *gerstrsize, size_t *gerstrcap, char **errbuf ) { unsigned long entryrights = 0; Slapi_RDN *rdn = NULL; char *rdntype = NULL; char *rdnvalue = NULL; _append_gerstr(gerstr, gerstrsize, gerstrcap, "entryLevelRights: ", NULL); slapi_log_err(SLAPI_LOG_ACL, plugin_name, "_ger_get_entry_rights - SLAPI_ACL_READ\n" ); if (acl_access_allowed(gerpb, e, "*", NULL, SLAPI_ACL_READ) == LDAP_SUCCESS) { /* v - view e */ entryrights |= SLAPI_ACL_READ; _append_gerstr(gerstr, gerstrsize, gerstrcap, "v", NULL); } slapi_log_err(SLAPI_LOG_ACL, plugin_name, "_ger_get_entry_rights - SLAPI_ACL_ADD\n" ); if (acl_access_allowed(gerpb, e, NULL, NULL, SLAPI_ACL_ADD) == LDAP_SUCCESS) { /* a - add child entry below e */ entryrights |= SLAPI_ACL_ADD; _append_gerstr(gerstr, gerstrsize, gerstrcap, "a", NULL); } slapi_log_err(SLAPI_LOG_ACL, plugin_name, "_ger_get_entry_rights - SLAPI_ACL_DELETE\n" ); if (acl_access_allowed(gerpb, e, NULL, NULL, SLAPI_ACL_DELETE) == LDAP_SUCCESS) { /* d - delete e */ entryrights |= SLAPI_ACL_DELETE; _append_gerstr(gerstr, gerstrsize, gerstrcap, "d", NULL); } if (config_get_moddn_aci()) { /* The server enforces the new MODDN aci right. * So the status 'n' is set if this right is granted. * Opposed to the legacy mode where this flag is set if * WRITE was granted on rdn attrbibute */ if (acl_access_allowed(gerpb, e, NULL, NULL, SLAPI_ACL_MODDN) == LDAP_SUCCESS) { slapi_log_err(SLAPI_LOG_ACL, plugin_name, "_ger_get_entry_rights - SLAPI_ACL_MODDN %s\n", slapi_entry_get_ndn(e)); /* n - rename e */ entryrights |= SLAPI_ACL_MODDN; _append_gerstr(gerstr, gerstrsize, gerstrcap, "n", NULL); } } else { /* * Some limitation/simplification applied here: * - The modrdn right requires the rights to delete the old rdn and * the new one. However we have no knowledge of what the new rdn * is going to be. * - In multi-valued RDN case, we check the right on * the first rdn type only for now. */ rdn = slapi_rdn_new_dn(slapi_entry_get_ndn(e)); slapi_rdn_get_first(rdn, &rdntype, &rdnvalue); if (NULL != rdntype) { slapi_log_err(SLAPI_LOG_ACL, plugin_name, "_ger_get_entry_rights - SLAPI_ACL_WRITE_DEL & _ADD %s\n", rdntype); if (acl_access_allowed(gerpb, e, rdntype, NULL, ACLPB_SLAPI_ACL_WRITE_DEL) == LDAP_SUCCESS && acl_access_allowed(gerpb, e, rdntype, NULL, ACLPB_SLAPI_ACL_WRITE_ADD) == LDAP_SUCCESS) { /* n - rename e */ entryrights |= SLAPI_ACL_WRITE; _append_gerstr(gerstr, gerstrsize, gerstrcap, "n", NULL); } } slapi_rdn_free(&rdn); } if ( entryrights == 0 ) { _append_gerstr(gerstr, gerstrsize, gerstrcap, "none", NULL); } _append_gerstr(gerstr, gerstrsize, gerstrcap, "\n", NULL); return entryrights; }
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 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; }