int VmDirSendSearchEntry( PVDIR_OPERATION pOperation, PVDIR_ENTRY pSrEntry ) { int retVal = LDAP_SUCCESS; BerElementBuffer berbuf; BerElement * ber = (BerElement *) &berbuf; BOOLEAN bFreeBer = FALSE; ber_len_t iBlobSize = 0; BerValue lberBervEntryBlob = {0}; int nAttrs = 0; int nVals = 0; BOOLEAN attrMetaDataReqd = FALSE; SearchReq * sr = &(pOperation->request.searchReq); int i = 0; BOOLEAN nonTrivialAttrsInReplScope = FALSE; uint32_t iSearchReqSpecialChars = 0; PATTRIBUTE_META_DATA_NODE pAttrMetaData = NULL; int numAttrMetaData = 0; PVDIR_ATTRIBUTE pAttr = NULL; USN usnChanged = 0; PSTR pszLocalErrorMsg = NULL; if ( !pOperation || !pSrEntry ) { retVal = ERROR_INVALID_PARAMETER; BAIL_ON_VMDIR_ERROR(retVal); } pSrEntry->bSearchEntrySent = FALSE; // see if client request has "*" and/or "+" ("-" for userpassword internal to vmdir) // WEI TODO: when we have attribute level ACL check, this information will be useful // return result will depend on ACL each on each attribute client is asking before // generating a final result to send back SetSpecialReturnChar(&pOperation->request.searchReq, &iSearchReqSpecialChars); if ( pSrEntry->eId == DSE_ROOT_ENTRY_ID && pOperation->request.searchReq.attrs == NULL ) { // For ADSI, if no specific attributes requested of DSE ROOT search, // return ALL (include operational) attributes. iSearchReqSpecialChars |= LDAP_SEARCH_REQUEST_CHAR_OP; } // ACL check before processing/sending the current srEntry back retVal = VmDirSrvAccessCheck( pOperation, &pOperation->conn->AccessInfo, pSrEntry, VMDIR_RIGHT_DS_READ_PROP ); BAIL_ON_VMDIR_ERROR( retVal ); if ( pOperation->opType == VDIR_OPERATION_TYPE_INTERNAL ) { ; // no op in INTERNAL case } else { // If not replication, and showDeletedObjectsCtrl not present, => don't send back Deleted objects (tombstones). if (pOperation->syncReqCtrl == NULL && pOperation->showDeletedObjectsCtrl == NULL) { pAttr = VmDirEntryFindAttribute(ATTR_IS_DELETED, pSrEntry); if (pAttr) { if (VmDirStringCompareA((PSTR)pAttr->vals[0].lberbv.bv_val, VMDIR_IS_DELETED_TRUE_STR, FALSE) == 0) { goto cleanup; // Don't send this entry } } } // In case of replication request, skip certain updates if (pOperation->syncReqCtrl != NULL) { PVDIR_ATTRIBUTE pAttrUsnCreated = NULL; USN usnCreated = 0; USN limitUsn = 0; VMDIR_REPLICATION_AGREEMENT * replAgr = NULL; pAttr = VmDirEntryFindAttribute(ATTR_USN_CHANGED, pSrEntry); assert( pAttr != NULL ); usnChanged = VmDirStringToLA( pAttr->vals[0].lberbv.bv_val, NULL, 10); // Check if usnChanged is beyond successful replication update state limitUsn = VmDirdGetLimitLocalUsnToBeSupplied(); if (limitUsn != 0 && usnChanged >= limitUsn) { VMDIR_LOG_INFO(VMDIR_LOG_MASK_ALL, "SendSearchEntry: bug# 863244 RACE CONDITION encountered., " "usnChanged = %ld, limitLocalUsnToBeSupplied = %ld, skipping entry: %s", usnChanged, limitUsn, pSrEntry->dn.lberbv.bv_val ); goto cleanup; // Don't send this entry } // Check if usnChanged is beyond lowestPendingUncommittedUsn recorded at the beginning of replication search if (pOperation->lowestPendingUncommittedUsn != 0 && usnChanged >= pOperation->lowestPendingUncommittedUsn) { VMDIR_LOG_INFO( LDAP_DEBUG_REPL, "SendSearchEntry: usnChanged = %ld, lowestPendingUncommittedUsn = %ld, " "skipping entry: %s", usnChanged, pOperation->lowestPendingUncommittedUsn, pSrEntry->dn.lberbv.bv_val ); goto cleanup; // Don't send this entry } // Don't send (skip) modifications to my server object, and my RAs pAttrUsnCreated = VmDirEntryFindAttribute(ATTR_USN_CREATED, pSrEntry); assert( pAttrUsnCreated != NULL ); usnCreated = VmDirStringToLA( pAttrUsnCreated->vals[0].lberbv.bv_val, NULL, 10); // Only send back creation of certain objects, and not their modifications. // Check if consumer has already seen the creation. If yes, we are dealing with mods, which should be skipped // for my server object, and my RAs // Note: Skipping mods for RAs and Server Objects will cause inconsistencies with replicas. But these // two types only have local scope regarding functional effects. But if we are looking up / processing // information for these two types of objects on a replica, we need to watch out for potential // inconsistencies against the original source. if (pOperation->syncReqCtrl->value.syncReqCtrlVal.intLastLocalUsnProcessed > usnCreated) { if (strcmp(pSrEntry->dn.bvnorm_val, gVmdirServerGlobals.serverObjDN.bvnorm_val) == 0) { VMDIR_LOG_INFO( LDAP_DEBUG_REPL, "SendSearchEntry: Not sending modifications to my server object, DN: %s", gVmdirServerGlobals.serverObjDN.lberbv.bv_val ); goto cleanup; // Don't send this entry } for (replAgr = gVmdirReplAgrs; replAgr != NULL; replAgr = replAgr->next ) { if (strcmp(pSrEntry->dn.bvnorm_val, replAgr->dn.bvnorm_val) == 0) { VMDIR_LOG_INFO( LDAP_DEBUG_REPL, "SendSearchEntry: Not sending modifications to my RA object, DN: %s", replAgr->dn.bvnorm_val ); goto cleanup; // Don't send this entry } } } // do not replicate DSE Root entry, because it is a "local" entry. if (pSrEntry->eId == DSE_ROOT_ENTRY_ID) { VMDIR_LOG_INFO( LDAP_DEBUG_REPL, "SendSearchEntry: Not sending modifications to DSE Root entry, DN: %s", pSrEntry->dn.bvnorm_val ); goto cleanup; // Don't send this entry } } // Approximate calculation for the required ber size, because apart from lengths and strings, ber also includes // tags. if ( VmDirComputeEncodedEntrySize(pSrEntry, &nAttrs, &nVals, &iBlobSize) != 0 || VmDirAllocateMemory(iBlobSize, (PVOID*)&lberBervEntryBlob.bv_val) != 0 ) { retVal = LDAP_OPERATIONS_ERROR; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, (pszLocalErrorMsg), "no memory"); } lberBervEntryBlob.bv_len = iBlobSize; ber_init2( ber, &lberBervEntryBlob, LBER_USE_DER ); // ber takes over lberBervEntryBlob.lberbv.bv_val ownership bFreeBer = TRUE; if ( ber_printf( ber, "{it{O{", pOperation->msgId, LDAP_RES_SEARCH_ENTRY, &pSrEntry->dn ) == -1) { VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL, "SendSearchEntry: ber_printf (to print msgId ...) failed" ); retVal = LDAP_OTHER; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, (pszLocalErrorMsg), "Encoding msgId, RES_SEARCH_ENTRY, DN failed"); } // Determine if we need to send back the attribute metaData if ( pOperation->syncReqCtrl != NULL ) // Replication { attrMetaDataReqd = TRUE; } else // check if attrMetaData attribute has been requested explicitly. { if (sr->attrs != NULL) { for (i = 0; sr->attrs[i].lberbv.bv_val != NULL; i++) { if (VmDirStringCompareA( sr->attrs[i].lberbv.bv_val, ATTR_ATTR_META_DATA, FALSE) == 0) { attrMetaDataReqd = TRUE; break; } } } } if (attrMetaDataReqd) { if ((pOperation->pBEIF->pfnBEGetAllAttrsMetaData( pOperation->pBECtx, pSrEntry->eId, &pAttrMetaData, &numAttrMetaData )) != 0) { VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL, "SendSearchEntry: pfnBEGetAllAttrsMetaData failed for entryId: %ld", pSrEntry->eId); retVal = LDAP_OPERATIONS_ERROR; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, (pszLocalErrorMsg), "pfnBEGetAllAttrsMetaData failed."); } // SJ-TBD: Following double for loop to be optimized // Copy attrMetaData to corresponding attributes for (i=0; i<numAttrMetaData; i++) { for ( pAttr = pSrEntry->attrs; pAttr != NULL; pAttr = pAttr->next) { if (pAttr->pATDesc->usAttrID == pAttrMetaData[i].attrID) { VmDirStringCpyA( pAttr->metaData, VMDIR_MAX_ATTR_META_DATA_LEN, pAttrMetaData[i].metaData ); pAttrMetaData[i].metaData[0] = '\0'; } } } } retVal = WriteAttributes( pOperation, pSrEntry, iSearchReqSpecialChars , ber, &pszLocalErrorMsg ); BAIL_ON_VMDIR_ERROR( retVal ); if (attrMetaDataReqd) { retVal = WriteMetaDataAttribute( pOperation, pSrEntry->attrs, numAttrMetaData, pAttrMetaData, ber, &nonTrivialAttrsInReplScope, &pszLocalErrorMsg ); BAIL_ON_VMDIR_ERROR( retVal ); } if (ber_printf( ber, "N}N}" ) == -1) { VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL, "ber_printf (to terminate the entry and the complete search result entry message ...) failed" ); retVal = LDAP_OTHER; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, (pszLocalErrorMsg), "Encoding terminating the entry failed."); } if ( pOperation->syncReqCtrl != NULL ) // Replication, => write Sync State Control { retVal = WriteSyncStateControl( pOperation, pSrEntry->attrs, ber, &pszLocalErrorMsg ); BAIL_ON_VMDIR_ERROR( retVal ); } if (ber_printf( ber, "N}" ) == -1) { VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL, "ber_printf (to terminate the entry and the complete search result entry message ...) failed" );; retVal = LDAP_OTHER; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, (pszLocalErrorMsg), "Encoding terminating the entry failed."); } if ((pOperation->syncReqCtrl == NULL) || (pOperation->syncReqCtrl != NULL && nonTrivialAttrsInReplScope )) { if (WriteBerOnSocket( pOperation->conn, ber ) != 0) { VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL, "SendSearchEntry: WriteBerOnSocket failed." ); retVal = LDAP_UNAVAILABLE; BAIL_ON_VMDIR_ERROR( retVal ); } pSrEntry->bSearchEntrySent = TRUE; sr->iNumEntrySent++; VMDIR_LOG_INFO( LDAP_DEBUG_REPL, "SendSearchEntry: Send entry: %s", pSrEntry->dn.lberbv.bv_val); } else { VMDIR_LOG_INFO( LDAP_DEBUG_REPL, "SendSearchEntry: NOT Sending entry: %s %p %d", pSrEntry->dn.lberbv.bv_val, pOperation->syncReqCtrl, nonTrivialAttrsInReplScope); } // record max local usnChanged in syncControlDone if (pOperation->syncReqCtrl != NULL) { if (usnChanged > pOperation->syncDoneCtrl->value.syncDoneCtrlVal.intLastLocalUsnProcessed) { pOperation->syncDoneCtrl->value.syncDoneCtrlVal.intLastLocalUsnProcessed = usnChanged; } } retVal = LDAP_SUCCESS; } cleanup: if (bFreeBer) { ber_free_buf( ber ); } VMDIR_SAFE_FREE_MEMORY( pAttrMetaData ); VMDIR_SAFE_FREE_MEMORY(pszLocalErrorMsg); return( retVal ); error: VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL, "SendSearchEntry failed DN=(%s), (%u)(%s)", (pSrEntry && pSrEntry->dn.lberbv.bv_val) ? pSrEntry->dn.lberbv.bv_val : "", retVal, VDIR_SAFE_STRING( pszLocalErrorMsg)); if ( !pOperation->ldapResult.pszErrMsg && pszLocalErrorMsg ) { pOperation->ldapResult.pszErrMsg = pszLocalErrorMsg; pszLocalErrorMsg = NULL; } goto cleanup; }
/* VmDirInternalDeleteEntry: Interface that can be used "internally" by the server code. One of the main differences between * this function and MLDelete is that this function does not send back an LDAP result to the client. * * Return: VmDir level error code. Also, pOperation->ldapResult content is set. */ int VmDirInternalDeleteEntry( PVDIR_OPERATION pOperation ) { int retVal = LDAP_SUCCESS; int deadLockRetries = 0; VDIR_ENTRY entry = {0}; PVDIR_ENTRY pEntry = NULL; BOOLEAN leafNode = FALSE; DeleteReq * delReq = &(pOperation->request.deleteReq); ModifyReq * modReq = &(pOperation->request.modifyReq); BOOLEAN bIsDomainObject = FALSE; BOOLEAN bHasTxn = FALSE; PSTR pszLocalErrMsg = NULL; assert(pOperation && pOperation->pBECtx->pBE); if (VmDirdState() == VMDIRD_STATE_READ_ONLY) { retVal = VMDIR_ERROR_UNWILLING_TO_PERFORM; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Server in read-only mode" ); } // Normalize DN retVal = VmDirNormalizeDN( &(delReq->dn), pOperation->pSchemaCtx ); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "DN normalization failed - (%u)(%s)", retVal, VDIR_SAFE_STRING(VmDirSchemaCtxGetErrorMsg(pOperation->pSchemaCtx)) ); if (pOperation->opType != VDIR_OPERATION_TYPE_REPL) { // Execute pre modify apply Delete plugin logic retVal = VmDirExecutePreModApplyDeletePlugins(pOperation, NULL, retVal); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "PreModApplyDelete plugin failed - (%u)", retVal ); } retVal = VmDirNormalizeMods( pOperation->pSchemaCtx, modReq->mods, &pszLocalErrMsg ); BAIL_ON_VMDIR_ERROR( retVal ); // make sure VDIR_BACKEND_CTX has usn change number by now if ( pOperation->pBECtx->wTxnUSN <= 0 ) { retVal = VMDIR_ERROR_NO_USN; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "BECtx.wTxnUSN not set"); } // BUGBUG, need to protect some system entries such as schema,domain....etc? // ************************************************************************************ // transaction retry loop begin. make sure all function within are retry agnostic. // ************************************************************************************ txnretry: if (bHasTxn) { pOperation->pBEIF->pfnBETxnAbort( pOperation->pBECtx ); bHasTxn = FALSE; } deadLockRetries++; if (deadLockRetries > MAX_DEADLOCK_RETRIES) { retVal = VMDIR_ERROR_LOCK_DEADLOCK; BAIL_ON_VMDIR_ERROR( retVal ); } else { if (pEntry) { VmDirFreeEntryContent(pEntry); memset(pEntry, 0, sizeof(VDIR_ENTRY)); pEntry = NULL; } retVal = pOperation->pBEIF->pfnBETxnBegin( pOperation->pBECtx, VDIR_BACKEND_TXN_WRITE); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "txn begin (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); bHasTxn = TRUE; // Read current entry from DB retVal = pOperation->pBEIF->pfnBEDNToEntry( pOperation->pBECtx, pOperation->pSchemaCtx, &(delReq->dn), &entry, VDIR_BACKEND_ENTRY_LOCK_WRITE); if (retVal != 0) { switch (retVal) { case VMDIR_ERROR_BACKEND_DEADLOCK: goto txnretry; // Possible retry. default: BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "(%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); } } pEntry = &entry; // Parse Parent DN retVal = VmDirGetParentDN( &pEntry->dn, &pEntry->pdn ); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Get ParentDn failed - (%u)", retVal ); // get parent entry if (pEntry->pdn.lberbv.bv_val) { PVDIR_ENTRY pParentEntry = NULL; retVal = VmDirAllocateMemory(sizeof(*pEntry), (PVOID)&pParentEntry); BAIL_ON_VMDIR_ERROR(retVal); retVal = pOperation->pBEIF->pfnBEDNToEntry( pOperation->pBECtx, pOperation->pSchemaCtx, &pEntry->pdn, pParentEntry, VDIR_BACKEND_ENTRY_LOCK_READ); if (retVal) { VmDirFreeEntryContent(pParentEntry); VMDIR_SAFE_FREE_MEMORY(pParentEntry); switch (retVal) { case VMDIR_ERROR_BACKEND_DEADLOCK: goto txnretry; // Possible retry. case VMDIR_ERROR_BACKEND_ENTRY_NOTFOUND: BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "parent (%s) not found, (%s)", pEntry->pdn.lberbv_val, VDIR_SAFE_STRING(pOperation->pBEErrorMsg) ); default: BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "parent (%s) lookup failed, (%s)", pEntry->pdn.lberbv_val, VDIR_SAFE_STRING(pOperation->pBEErrorMsg) ); } } pEntry->pParentEntry = pParentEntry; // pEntry takes over pParentEntry pParentEntry = NULL; } // SJ-TBD: Once ACLs are enabled, following check should go in ACLs logic. if (VmDirIsInternalEntry( pEntry ) || VmDirIsProtectedEntry(pEntry)) { retVal = VMDIR_ERROR_UNWILLING_TO_PERFORM; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "An internal entry (%s) can NOT be deleted.", pEntry->dn.lberbv_val ); } // only when there is parent Entry, ACL check is done if (pEntry->pParentEntry) { retVal = VmDirSrvAccessCheck( pOperation, &pOperation->conn->AccessInfo, pEntry->pParentEntry, VMDIR_RIGHT_DS_DELETE_CHILD); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "VmDirSrvAccessCheck failed - (%u)(%s)", retVal, VMDIR_ACCESS_DENIED_ERROR_MSG); } // Make sure it is a leaf node retVal = pOperation->pBEIF->pfnBEChkIsLeafEntry( pOperation->pBECtx, pEntry->eId, &leafNode); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "BEChkIsLeafEntry failed, (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg) ); if (leafNode == FALSE) { retVal = VMDIR_ERROR_NOT_ALLOWED_ON_NONLEAF; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Delete of a non-leaf node is not allowed." ); } // Retrieve to determine whether it is domain object earlier // before attribute modifications // ('bIsDomainObject' is needed for a domain object deletion) retVal = VmDirIsDomainObjectWithEntry(pEntry, &bIsDomainObject); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "VmDirIsDomainObjectWithEntry failed - (%u)", retVal ); if (pOperation->opType != VDIR_OPERATION_TYPE_REPL) { // Generate mods to delete attributes that need not be present in a DELETED entry // Note: in case of executing the deadlock while loop multiple times, same attribute Delete mod be added // multiple times in the modReq, which is expected to work correctly. retVal = GenerateDeleteAttrsMods( pOperation, pEntry ); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "GenerateDeleteAttrsMods failed - (%u)", retVal); // Generate new meta-data for the attributes being updated if ((retVal = VmDirGenerateModsNewMetaData( pOperation, modReq->mods, pEntry->eId )) != 0) { switch (retVal) { case VMDIR_ERROR_LOCK_DEADLOCK: goto txnretry; // Possible retry. BUGBUG, is modReq->mods in above call good for retry? default: BAIL_ON_VMDIR_ERROR( retVal ); } } } // Normalize attribute values in mods retVal = VmDirNormalizeMods( pOperation->pSchemaCtx, modReq->mods, &pszLocalErrMsg ); BAIL_ON_VMDIR_ERROR( retVal ); // Apply modify operations to the current entry in the DB. retVal = VmDirApplyModsToEntryStruct( pOperation->pSchemaCtx, modReq, pEntry, &pszLocalErrMsg ); BAIL_ON_VMDIR_ERROR( retVal ); // Update DBs // Update Entry retVal = pOperation->pBEIF->pfnBEEntryDelete( pOperation->pBECtx, modReq->mods, pEntry ); if (retVal != 0) { switch (retVal) { case VMDIR_ERROR_BACKEND_DEADLOCK: goto txnretry; // Possible retry. default: BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "BEEntryDelete (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); } } retVal = DeleteRefAttributesValue(pOperation, &(pEntry->dn)); if (retVal != 0) { switch (retVal) { case VMDIR_ERROR_LOCK_DEADLOCK: goto txnretry; // Possible retry. default: BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "BEEntryDelete (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); } } // Use normalized DN value if (bIsDomainObject) { retVal = VmDirInternalRemoveOrgConfig(pOperation, BERVAL_NORM_VAL(pEntry->dn)); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Update domain list entry failed." ); } retVal = pOperation->pBEIF->pfnBETxnCommit( pOperation->pBECtx); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "txn commit (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); bHasTxn = FALSE; } // ************************************************************************************ // transaction retry loop end. // ************************************************************************************ VMDIR_LOG_INFO( VMDIR_LOG_MASK_ALL, "Delete Entry (%s)", VDIR_SAFE_STRING(pEntry->dn.lberbv_val)); // Post delete entry // TODO, make it into a separate file deletePlugin.c // clean lockout cache record if exists VdirLockoutCacheRemoveRec(pEntry->dn.bvnorm_val); cleanup: if (pOperation->opType != VDIR_OPERATION_TYPE_REPL) { if (retVal == LDAP_SUCCESS) { int iPostCommitPluginRtn = 0; // Execute post Delete commit plugin logic iPostCommitPluginRtn = VmDirExecutePostDeleteCommitPlugins(pOperation, pEntry, retVal); if ( iPostCommitPluginRtn != LDAP_SUCCESS && iPostCommitPluginRtn != pOperation->ldapResult.errCode // pass through ) { VmDirLog( LDAP_DEBUG_ANY, "InternalDeleteEntry: VdirExecutePostDeleteCommitPlugins - code(%d)", iPostCommitPluginRtn); } } // In case of replication, modReq is owned by the Replication thread/logic DeleteMods ( modReq ); } VmDirFreeEntryContent ( &entry ); VMDIR_SAFE_FREE_MEMORY(pszLocalErrMsg); return retVal; error: if (bHasTxn) { pOperation->pBEIF->pfnBETxnAbort( pOperation->pBECtx ); } VMDIR_SET_LDAP_RESULT_ERROR( &(pOperation->ldapResult), retVal, pszLocalErrMsg); goto cleanup; }
static int WriteAttributes( VDIR_OPERATION * op, PVDIR_ENTRY pEntry, uint32_t iSearchReqSpecialChars, BerElement * ber, PSTR* ppszErrorMsg ) { int retVal = LDAP_SUCCESS; unsigned int i = 0; SearchReq * sr = &(op->request.searchReq); BOOLEAN mapFSPDN = FALSE; PVDIR_ATTRIBUTE pAttr = NULL; PVDIR_ATTRIBUTE pRetAttrs[3] = {pEntry->attrs, pEntry->pComputedAttrs, NULL}; DWORD dwCnt = 0; PSTR pszLocalErrorMsg = NULL; // loop through both normal and computed attributes for ( dwCnt = 0, pAttr = pRetAttrs[dwCnt]; pAttr != NULL; ++dwCnt, pAttr = pRetAttrs[dwCnt]) { for ( ; pAttr != NULL; pAttr = pAttr->next ) { BOOLEAN bSendAttribute = FALSE; if (op->syncReqCtrl != NULL) // Replication, { // Filter attributes based on the input utdVector, and attribute's meta-data retVal = IsAttrInReplScope( op, pAttr->type.lberbv.bv_val, pAttr->metaData, &bSendAttribute, &pszLocalErrorMsg ); BAIL_ON_VMDIR_ERROR( retVal ); } else { // WEI: the correct way to handle this is to have attribute level 'ACL' check // For instance, the 'userPassword' attribute ACL defines: // Deny 'READ' access to everyone including administrator/admins/SELF // Possibly only allow 'backup Operators' 'READ' access // Allow 'administrator/admins' WRITE (set password) // Allow 'SELF' WRITE (reset password) // For now, do not send 'userPassword' attribute for everyone when implicitly/explicitly requested // normal ldap client search request // check if we need to return this attribute if (VmDirStringCompareA(pAttr->type.lberbv.bv_val, ATTR_USER_PASSWORD, FALSE) == 0) { if ((iSearchReqSpecialChars & LDAP_SEARCH_REQUEST_CHAR_PASSWD) != 0) { // vmdir specific - return password only if special char '-' is requested bSendAttribute = TRUE; } } else if (VmDirStringCompareA(pAttr->type.lberbv.bv_val, ATTR_KRB_MASTER_KEY, FALSE) == 0) { //only return master key if all of following conditions are satisfied if (op->showMasterKeyCtrl && // server control must be present op->conn->bIsLdaps && // must be ldaps op->conn->AccessInfo.pszNormBindedDn && // can't be anonymous VmDirStringCompareA(op->reqDn.bvnorm_val, gVmdirServerGlobals.systemDomainDN.bvnorm_val, FALSE) == 0 && // must query system domain object op->request.searchReq.scope == LDAP_SCOPE_BASE && // scope must be base op->request.searchReq.attrs != NULL && // attribute must be krbMKey only VmDirStringCompareA(op->request.searchReq.attrs[0].lberbv.bv_val, ATTR_KRB_MASTER_KEY, FALSE) == 0 && op->request.searchReq.attrs[1].lberbv.bv_val == NULL) { retVal = VmDirSrvAccessCheckIsAdminRole( //must be administrator op, op->conn->AccessInfo.pszNormBindedDn, &op->conn->AccessInfo, &bSendAttribute); BAIL_ON_VMDIR_ERROR( retVal ); } } else if ( (VmDirStringCompareA(pAttr->type.lberbv.bv_val, ATTR_VMW_STS_PASSWORD, FALSE) == 0) || (VmDirStringCompareA(pAttr->type.lberbv.bv_val, ATTR_VMW_STS_TENANT_KEY, FALSE) == 0) ) { //only admin users can read ATTR_VMW_STS_PASSWORD, ATTR_VMW_STS_TENANT_KEY attribute { retVal = VmDirSrvAccessCheckIsAdminRole( //must be administrator op, op->conn->AccessInfo.pszNormBindedDn, &op->conn->AccessInfo, &bSendAttribute); BAIL_ON_VMDIR_ERROR( retVal ); } } else if (((iSearchReqSpecialChars & LDAP_SEARCH_REQUEST_CHAR_USER) != 0) && pAttr->pATDesc->usage == VDIR_LDAP_USER_APPLICATIONS_ATTRIBUTE) { bSendAttribute = TRUE; // return all user attributes - "*" } else if (((iSearchReqSpecialChars & LDAP_SEARCH_REQUEST_CHAR_OP) != 0) && (pAttr->pATDesc->usage == VDIR_LDAP_DIRECTORY_OPERATION_ATTRIBUTE || pAttr->pATDesc->usage == VDIR_LDAP_DSA_OPERATION_ATTRIBUTE || pAttr->pATDesc->usage == VDIR_LDAP_DISTRIBUTED_OPERATION_ATTRIBUTE)) { bSendAttribute = TRUE; // return all operational attributes - "+" } else { for (i = 0; sr->attrs && sr->attrs[i].lberbv.bv_val != NULL; i++) { if (VmDirStringCompareA( sr->attrs[i].lberbv.bv_val, pAttr->type.lberbv.bv_val, FALSE) == 0) { // // Access checks for a search request requires the // caller to have VMDIR_RIGHT_DS_READ_PROP access // to the entry. This will allow them to "see" the // entry and any attributes EXCEPT for the entry's // security descriptor. The SD is goverened by a // separate permission, VMDIR_ENTRY_READ_ACL. So, // if the caller requested that attribute we have // to make sure they have the permission required. // if (VmDirStringCompareA(pAttr->type.lberbv.bv_val, ATTR_ACL_STRING, FALSE) == 0 || VmDirStringCompareA(pAttr->type.lberbv.bv_val, ATTR_OBJECT_SECURITY_DESCRIPTOR, FALSE) == 0) { bSendAttribute = (VmDirSrvAccessCheck(op, &op->conn->AccessInfo, pEntry, VMDIR_ENTRY_READ_ACL) == 0); } else { bSendAttribute = TRUE; } break; } } } } if (bSendAttribute) { if (ber_printf( ber, "{O[", &(pAttr->type) ) == -1 ) { VMDIR_LOG_VERBOSE( VMDIR_LOG_MASK_ALL, "WriteAttributes: ber_printf (to print attribute name ...) failed" ); retVal = LDAP_OTHER; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, (pszLocalErrorMsg), "Encoding attribute type failed."); } // Non-replication/normal search request, and the attribute is FSP enabled (SJ-TBD, for now ATTR_MEMBER) if (op->syncReqCtrl == NULL && VmDirStringCompareA(pAttr->type.lberbv.bv_val, ATTR_MEMBER, FALSE) == 0) { mapFSPDN = TRUE; } else { mapFSPDN = FALSE; } if ( VmDirStringCompareA( pAttr->type.lberbv_val, ATTR_OBJECT_CLASS, FALSE ) == 0 ) { if ( op->syncReqCtrl == NULL ) // Not replication { BerValue bvOCTop = {OC_TOP_LEN, OC_TOP}; // explicitly send TOP as we don't store it in Entry/DB if (ber_printf( ber, "O", &bvOCTop ) == -1 ) { VMDIR_LOG_VERBOSE( VMDIR_LOG_MASK_ALL, "WriteAttributes: ber_printf (to print an attribute value ...) failed." ); retVal = LDAP_OTHER; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, (pszLocalErrorMsg), "Encoding an attribute value failed."); } } // ADSI wants structure objetclass at the end to display correctly. for ( i = pAttr->numVals ; i > 0; i-- ) { if (ber_printf( ber, "O", &(pAttr->vals[i-1]) ) == -1 ) { VMDIR_LOG_VERBOSE( VMDIR_LOG_MASK_ALL, "WriteAttributes: ber_printf (to print an attribute value ...) failed." ); retVal = LDAP_OTHER; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, (pszLocalErrorMsg), "Encoding an attribute value failed."); } } } else { for ( i = 0; i< pAttr->numVals; i++ ) { // SJ-TBD: A better/bit-more-expensive way is to Normalize the value, do GetParentDN, check if the 1st // RDN is FSP container RDN ... if (mapFSPDN && VmDirStringStrA(pAttr->vals[i].lberbv.bv_val, FSP_CONTAINER_RDN_ATTR_VALUE) != NULL) { char * tmpPos = VmDirStringChrA(pAttr->vals[i].lberbv.bv_val, ','); assert( tmpPos != NULL); *tmpPos = '\0'; pAttr->vals[i].lberbv.bv_len = tmpPos - pAttr->vals[i].lberbv.bv_val; } if (ber_printf( ber, "O", &(pAttr->vals[i]) ) == -1 ) { VMDIR_LOG_VERBOSE( VMDIR_LOG_MASK_ALL, "WriteAttributes: ber_printf (to print an attribute value ...) failed." ); retVal = LDAP_OTHER; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, (pszLocalErrorMsg), "Encoding an attribute value failed."); } } } if ( ber_printf( ber, "]N}" ) == -1 ) { VMDIR_LOG_VERBOSE( VMDIR_LOG_MASK_ALL, "WriteAttributes: ber_printf (to terminate an attribute's values ...) failed" ); retVal = LDAP_OTHER; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, (pszLocalErrorMsg), "Encoding terminating an attribute type failed."); } } } } cleanup: if (ppszErrorMsg) { *ppszErrorMsg = pszLocalErrorMsg; } else { VMDIR_SAFE_FREE_MEMORY(pszLocalErrorMsg); } return( retVal ); error: VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL, "WriteAttributes failed (%u)(%s)", retVal, VDIR_SAFE_STRING(pszLocalErrorMsg) ); goto cleanup; }
/* VmDirInternalDeleteEntry: Interface that can be used "internally" by the server code. One of the main differences between * this function and MLDelete is that this function does not send back an LDAP result to the client. * * Return: VmDir level error code. Also, pOperation->ldapResult content is set. */ int VmDirInternalDeleteEntry( PVDIR_OPERATION pOperation ) { int retVal = LDAP_SUCCESS; VDIR_ENTRY entry = {0}; PVDIR_ENTRY pEntry = NULL; BOOLEAN leafNode = FALSE; DeleteReq* delReq = &(pOperation->request.deleteReq); ModifyReq* modReq = &(pOperation->request.modifyReq); BOOLEAN bIsDomainObject = FALSE; BOOLEAN bHasTxn = FALSE; PSTR pszLocalErrMsg = NULL; PVDIR_OPERATION_ML_METRIC pMLMetrics = NULL; extern DWORD VmDirDeleteRaftPreCommit(PVDIR_SCHEMA_CTX, EntryId, char *, PVDIR_OPERATION); assert(pOperation && pOperation->pBECtx->pBE); pMLMetrics = &pOperation->MLMetrics; VMDIR_COLLECT_TIME(pMLMetrics->iMLStartTime); if (VmDirdState() == VMDIRD_STATE_READ_ONLY) { retVal = VMDIR_ERROR_UNWILLING_TO_PERFORM; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Server in read-only mode"); } // make sure we have minimum DN length if (delReq->dn.lberbv_len < 3) { retVal = VMDIR_ERROR_INVALID_REQUEST; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Invalid DN length - (%u)", delReq->dn.lberbv_len); } // Normalize DN retVal = VmDirNormalizeDN(&(delReq->dn), pOperation->pSchemaCtx); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "DN normalization failed - (%u)(%s)", retVal, VDIR_SAFE_STRING(VmDirSchemaCtxGetErrorMsg(pOperation->pSchemaCtx))); VMDIR_COLLECT_TIME(pMLMetrics->iBETxnBeginStartTime); retVal = pOperation->pBEIF->pfnBETxnBegin(pOperation->pBECtx, VDIR_BACKEND_TXN_WRITE, &bHasTxn); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "txn begin (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); VMDIR_COLLECT_TIME(pMLMetrics->iBETxnBeginEndTime); if (bHasTxn) { retVal = VmDirValidateOp(pOperation, __func__); BAIL_ON_VMDIR_ERROR(retVal); } // Execute pre modify apply Delete plugin logic VMDIR_COLLECT_TIME(pMLMetrics->iPrePluginsStartTime); retVal = VmDirExecutePreModApplyDeletePlugins(pOperation, NULL, retVal); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "PreModApplyDelete plugin failed - (%u)", retVal); VMDIR_COLLECT_TIME(pMLMetrics->iPrePlugunsEndTim); retVal = VmDirNormalizeMods(pOperation->pSchemaCtx, modReq->mods, &pszLocalErrMsg); BAIL_ON_VMDIR_ERROR(retVal); // BUGBUG, need to protect some system entries such as schema,domain....etc? // Read current entry from DB retVal = pOperation->pBEIF->pfnBEDNToEntry( pOperation->pBECtx, pOperation->pSchemaCtx, &(delReq->dn), &entry, VDIR_BACKEND_ENTRY_LOCK_WRITE); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "(%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); pEntry = &entry; // Parse Parent DN retVal = VmDirGetParentDN(&pEntry->dn, &pEntry->pdn); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Get ParentDn failed - (%u)", retVal); // get parent entry if (pEntry->pdn.lberbv.bv_val) { PVDIR_ENTRY pParentEntry = NULL; retVal = VmDirAllocateMemory(sizeof(*pEntry), (PVOID)&pParentEntry); BAIL_ON_VMDIR_ERROR(retVal); retVal = pOperation->pBEIF->pfnBEDNToEntry( pOperation->pBECtx, pOperation->pSchemaCtx, &pEntry->pdn, pParentEntry, VDIR_BACKEND_ENTRY_LOCK_READ); if (retVal) { VmDirFreeEntryContent(pParentEntry); VMDIR_SAFE_FREE_MEMORY(pParentEntry); switch (retVal) { case VMDIR_ERROR_BACKEND_ENTRY_NOTFOUND: BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "parent (%s) not found, (%s)", pEntry->pdn.lberbv_val, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); default: BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "parent (%s) lookup failed, (%s)", pEntry->pdn.lberbv_val, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); } } pEntry->pParentEntry = pParentEntry; // pEntry takes over pParentEntry pParentEntry = NULL; } // // The delete will succeed if the caller either has the explicit right // to delete this object or if they have the right to delete children // of this object's parent. // retVal = VmDirSrvAccessCheck( pOperation, &pOperation->conn->AccessInfo, pEntry, VMDIR_RIGHT_DS_DELETE_OBJECT); if (retVal != ERROR_SUCCESS && pEntry->pParentEntry) { retVal = VmDirSrvAccessCheck( pOperation, &pOperation->conn->AccessInfo, pEntry->pParentEntry, VMDIR_RIGHT_DS_DELETE_CHILD); } BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "VmDirSrvAccessCheck failed - (%u)(%s)", retVal, VMDIR_ACCESS_DENIED_ERROR_MSG); // Make sure it is a leaf node retVal = pOperation->pBEIF->pfnBEChkIsLeafEntry( pOperation->pBECtx, pEntry->eId, &leafNode); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "BEChkIsLeafEntry failed, (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); if (leafNode == FALSE) { retVal = VMDIR_ERROR_NOT_ALLOWED_ON_NONLEAF; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Delete of a non-leaf node is not allowed."); } // Retrieve to determine whether it is domain object earlier // before attribute modifications // ('bIsDomainObject' is needed for a domain object deletion) retVal = VmDirIsDomainObjectWithEntry(pEntry, &bIsDomainObject); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "VmDirIsDomainObjectWithEntry failed - (%u)", retVal); retVal = GenerateDeleteAttrsMods(pOperation, pEntry); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "GenerateDeleteAttrsMods failed - (%u)", retVal); // Normalize attribute values in mods retVal = VmDirNormalizeMods(pOperation->pSchemaCtx, modReq->mods, &pszLocalErrMsg); BAIL_ON_VMDIR_ERROR(retVal); // Apply modify operations to the current entry in the DB. retVal = VmDirApplyModsToEntryStruct(pOperation->pSchemaCtx, modReq, pEntry, NULL, &pszLocalErrMsg); BAIL_ON_VMDIR_ERROR(retVal); // Update Entry retVal = pOperation->pBEIF->pfnBEEntryDelete(pOperation->pBECtx, modReq->mods, pEntry); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "BEEntryDelete (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); retVal = DeleteRefAttributesValue(pOperation, &(pEntry->dn)); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "DeleteRefAttributesValue (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); // Use normalized DN value if (bIsDomainObject) { retVal = VmDirInternalRemoveOrgConfig(pOperation, BERVAL_NORM_VAL(pEntry->dn)); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Update domain list entry failed."); } if (pOperation->bNoRaftLog == FALSE) { retVal = VmDirDeleteRaftPreCommit( pOperation->pSchemaCtx, pEntry->eId, BERVAL_NORM_VAL(pEntry->dn), pOperation); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "VmDirDeleteRaftPreCommit error (%u)", retVal); } if (bHasTxn) { VMDIR_COLLECT_TIME(pMLMetrics->iBETxnCommitStartTime); retVal = pOperation->pBEIF->pfnBETxnCommit(pOperation->pBECtx); bHasTxn = FALSE; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "txn commit logIndex %llu (%u)(%s)", pOperation->logIndex, retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); VMDIR_COLLECT_TIME(pMLMetrics->iBETxnCommitEndTime); } if (!pOperation->bSuppressLogInfo) { VMDIR_LOG_INFO( VMDIR_LOG_MASK_ALL, "Delete Entry (%s) logIndex %llu", VDIR_SAFE_STRING(pEntry->dn.lberbv_val), pOperation->logIndex); } // Post delete entry // TODO, make it into a separate file deletePlugin.c // clean lockout cache record if exists VdirLockoutCacheRemoveRec(pEntry->dn.bvnorm_val); cleanup: if (retVal == 0) { int iPostCommitPluginRtn = 0; VMDIR_COLLECT_TIME(pMLMetrics->iPostPluginsStartTime); // Execute post Delete commit plugin logic iPostCommitPluginRtn = VmDirExecutePostDeleteCommitPlugins(pOperation, pEntry, retVal); if (iPostCommitPluginRtn != LDAP_SUCCESS && iPostCommitPluginRtn != pOperation->ldapResult.errCode) // pass through { VMDIR_LOG_ERROR( LDAP_DEBUG_ANY, "InternalDeleteEntry: VdirExecutePostDeleteCommitPlugins - code(%d)", iPostCommitPluginRtn); } VMDIR_COLLECT_TIME(pMLMetrics->iPostPlugunsEndTime); } // collect metrics VMDIR_COLLECT_TIME(pMLMetrics->iMLEndTime); VmDirInternalMetricsUpdate(pOperation); VmDirInternalMetricsLogInefficientOp(pOperation); if (pOperation->opType != VDIR_OPERATION_TYPE_REPL) { // In case of replication, modReq is owned by the Replication thread/logic DeleteMods(modReq); } VmDirFreeEntryContent(&entry); VMDIR_SAFE_FREE_MEMORY(pszLocalErrMsg); return retVal; error: if (bHasTxn) { pOperation->pBEIF->pfnBETxnAbort(pOperation->pBECtx); } VMDIR_SET_LDAP_RESULT_ERROR(&pOperation->ldapResult, retVal, pszLocalErrMsg); goto cleanup; }
int VmDirModifyEntryCoreLogic( VDIR_OPERATION * pOperation, /* IN */ ModifyReq * modReq, /* IN */ ENTRYID entryId, /* IN */ BOOLEAN bNoRaftLog, /* IN */ VDIR_ENTRY * pEntry /* OUT */ ) { int retVal = LDAP_SUCCESS; PSTR pszLocalErrMsg = NULL; BOOLEAN bDnModified = FALSE; BOOLEAN bLeafNode = FALSE; PVDIR_ATTRIBUTE pAttrMemberOf = NULL; extern DWORD VmDirModifyRaftPreCommit(PVDIR_SCHEMA_CTX, ENTRYID, char *, PVDIR_MODIFICATION, PVDIR_OPERATION); retVal = pOperation->pBEIF->pfnBEIdToEntry( pOperation->pBECtx, pOperation->pSchemaCtx, entryId, pEntry, VDIR_BACKEND_ENTRY_LOCK_WRITE); BAIL_ON_VMDIR_ERROR(retVal); if (pOperation->pCondWriteCtrl) { retVal = VmDirMatchEntryWithFilter( pOperation, pEntry, pOperation->pCondWriteCtrl->value.condWriteCtrlVal.pszFilter); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Conditional Write pre-conditions (%s) failed - (%d)", VDIR_SAFE_STRING(pOperation->pCondWriteCtrl->value.condWriteCtrlVal.pszFilter), retVal); } if (modReq->dn.lberbv.bv_val == NULL) // If not already set by the caller { // e.g. delete membership case via index lookup to get EID. retVal = VmDirBervalContentDup(&pEntry->dn, &modReq->dn); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "VmDirBervalContentDup failed - (%d)", retVal); } retVal = VmDirSrvAccessCheck( pOperation, &pOperation->conn->AccessInfo, pEntry, VMDIR_RIGHT_DS_WRITE_PROP); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "VmDirSrvAccessCheck failed - (%u)", retVal); // Apply modify operations to the current entry (in pack format) retVal = VmDirApplyModsToEntryStruct( pOperation->pSchemaCtx, modReq, pEntry, &bDnModified, &pszLocalErrMsg); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "ApplyModsToEntryStruct failed - (%d)(%s)", retVal, pszLocalErrMsg); if (bDnModified) { retVal = pOperation->pBEIF->pfnBEChkIsLeafEntry( pOperation->pBECtx, entryId, &bLeafNode); BAIL_ON_VMDIR_ERROR(retVal); if (bLeafNode == FALSE) { retVal = LDAP_NOT_ALLOWED_ON_NONLEAF; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Rename of a non-leaf node is not allowed."); } // Verify not a member of any groups retVal = VmDirFindMemberOfAttribute(pEntry, &pAttrMemberOf); if (pAttrMemberOf && pAttrMemberOf->numVals > 0) { retVal = LDAP_UNWILLING_TO_PERFORM; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Rename of a node with memberships is not allowed."); } } if (pOperation->opType != VDIR_OPERATION_TYPE_REPL) { // Schema check retVal = VmDirSchemaCheck(pEntry); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Schema check failed - (%u)(%s)", retVal, VDIR_SAFE_STRING(VmDirSchemaCtxGetErrorMsg(pEntry->pSchemaCtx))); // check and read lock dn referenced entries retVal = pOperation->pBEIF->pfnBEChkDNReference(pOperation->pBECtx, pEntry); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "BECheckDnRef, (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBECtx->pszBEErrorMsg)); } // Execute plugin logic that require final entry image. (Do this for both normal and repl routes) retVal = VmDirExecutePreModifyPlugins(pOperation, pEntry, retVal); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "PreModifyPlugins failed - (%u)", retVal); // Update DB retVal = pOperation->pBEIF->pfnBEEntryModify(pOperation->pBECtx, modReq->mods, pEntry); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "BEEntryModify, (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); if (bNoRaftLog == FALSE) { // Generate raft log only on the orignal Add/Modify/Delete, but not on the derived operation. // For instance, a delete may cause a Modify on the referenced entry which shouldn't // initiate a raft log generation. retVal = VmDirModifyRaftPreCommit( pEntry->pSchemaCtx, entryId, modReq->dn.bvnorm_val, modReq->mods, pOperation); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "VmDirModifyRaftPreCommit, (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); } cleanup: VmDirFreeAttribute(pAttrMemberOf); VMDIR_SAFE_FREE_MEMORY(pszLocalErrMsg); return retVal; error: if (retVal == VMDIR_LDAP_ERROR_PRE_CONDITION) { VMDIR_LOG_VERBOSE( LDAP_DEBUG_ANY, "CoreLogicModifyEntry failed, DN = %s, (%u)(%s)", VDIR_SAFE_STRING(modReq->dn.lberbv.bv_val), retVal, VDIR_SAFE_STRING(pszLocalErrMsg)); } else { VMDIR_LOG_ERROR( LDAP_DEBUG_ANY, "CoreLogicModifyEntry failed, DN = %s, (%u)(%s)", VDIR_SAFE_STRING(modReq->dn.lberbv.bv_val), retVal, VDIR_SAFE_STRING(pszLocalErrMsg)); } if (pOperation->ldapResult.pszErrMsg == NULL) { pOperation->ldapResult.pszErrMsg = pszLocalErrMsg; pszLocalErrMsg = NULL; } goto cleanup; }