Example #1
0
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;
}
Example #2
0
/* 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;
}
Example #3
0
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;
}
Example #4
0
/* 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;
}
Example #5
0
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;
}