Exemple #1
0
DWORD
VmDirCreateTransientSecurityDescriptor(
    BOOLEAN                     bAllowAnonymousRead,
    PVMDIR_SECURITY_DESCRIPTOR  pvsd
    )
{
    DWORD   dwError = 0;
    PSTR    pszDomainDN = NULL;
    PSTR    pszAdminsGroupSid = NULL;
    PSTR    pszDomainAdminsGroupSid = NULL;
    VMDIR_SECURITY_DESCRIPTOR   SecDesc = {0};

    pszDomainDN = BERVAL_NORM_VAL(gVmdirServerGlobals.systemDomainDN);

    dwError = VmDirGenerateWellknownSid(
            pszDomainDN, VMDIR_DOMAIN_ALIAS_RID_ADMINS, &pszAdminsGroupSid);
    BAIL_ON_VMDIR_ERROR(dwError);

    dwError = VmDirGenerateWellknownSid(
            pszDomainDN, VMDIR_DOMAIN_ADMINS_RID, &pszDomainAdminsGroupSid);
    BAIL_ON_VMDIR_ERROR(dwError);

    // Create default security descriptor for internally-created entries.
    dwError = VmDirSrvCreateSecurityDescriptor(
            VMDIR_ENTRY_ALL_ACCESS_NO_DELETE_CHILD_BUT_DELETE_OBJECT,
            BERVAL_NORM_VAL(gVmdirServerGlobals.bvDefaultAdminDN),
            pszAdminsGroupSid,
            pszDomainAdminsGroupSid,
            FALSE,
            bAllowAnonymousRead,
            bAllowAnonymousRead,
            FALSE,
            FALSE,
            &SecDesc);
    BAIL_ON_VMDIR_ERROR(dwError);

    pvsd->pSecDesc = SecDesc.pSecDesc;
    pvsd->ulSecDesc = SecDesc.ulSecDesc;
    pvsd->SecInfo = SecDesc.SecInfo;

cleanup:
    VMDIR_SAFE_FREE_STRINGA(pszAdminsGroupSid);
    VMDIR_SAFE_FREE_STRINGA(pszDomainAdminsGroupSid);
    return dwError;

error:
    goto cleanup;
}
Exemple #2
0
DWORD
VmDirSchemaModMutexAcquire(
    PVDIR_OPERATION pOperation
    )
{
    DWORD   dwError = 0;
    PSTR    pszDN = NULL;

    if (!pOperation)
    {
        dwError = ERROR_INVALID_PARAMETER;
        BAIL_ON_VMDIR_ERROR(dwError);
    }

    if (pOperation->reqCode == LDAP_REQ_ADD)
    {
        pszDN = BERVAL_NORM_VAL(pOperation->request.addReq.pEntry->dn);
    }
    else if (pOperation->reqCode == LDAP_REQ_MODIFY)
    {
        pszDN = BERVAL_NORM_VAL(pOperation->request.modifyReq.dn);
    }

    if (VmDirStringEndsWith(pszDN, SCHEMA_NAMING_CONTEXT_DN, FALSE) &&
        pszDN[SCHEMA_NAMING_CONTEXT_DN_LEN])
    {
        if (VmDirIsInUserTxn(pOperation->pBECtx))
        {
            dwError = VMDIR_ERROR_UNWILLING_TO_PERFORM;
            VMDIR_LOG_ERROR(VMDIR_LOG_MASK_ALL,
              "%s: schema change not supported within user transaction %d", __func__, dwError);
            BAIL_ON_VMDIR_ERROR(dwError);
        }

        if (pOperation->dwSchemaWriteOp == 0)
        {
            dwError = VmDirLockMutex(gVdirSchemaGlobals.cacheModMutex);
            BAIL_ON_VMDIR_ERROR(dwError);
        }
        pOperation->dwSchemaWriteOp++;
    }

error:
    return dwError;
}
Exemple #3
0
/****************************************************************************
 * RFC 4517 - 3.3.16.  Integer
 *    Integer = ( HYPHEN LDIGIT *DIGIT ) / number
 *    number  = DIGIT / ( LDIGIT 1*DIGIT )
 *    DIGIT   = %x30 / LDIGIT       ; "0"-"9"
 *    LDIGIT  = %x31-39             ; "1"-"9"
 *    HYPHEN  = %x2D ; hyphen ("-")
 *
 ****************************************************************************
 *
 * Assume pAssert/pBerv already normalized and have correct syntax
 */
static
BOOLEAN
compareIntegerString(
    VDIR_SCHEMA_MATCH_TYPE type,
    PVDIR_BERVALUE    pAssert,
    PVDIR_BERVALUE    pBerv
    )
{
    BOOLEAN bRtn = FALSE;
    int     iCnt = 0;

    PCSTR   pAssertVal = NULL;
    size_t  iAssertValLen = 0;
    BOOLEAN bAssertSignPositive = TRUE;

    PCSTR   pBerVal = NULL;
    size_t  iBerValLen = 0;
    BOOLEAN bBerSignPositive = TRUE;

    if (!pAssert || !pAssert->lberbv.bv_val || !pBerv || !pBerv->lberbv.bv_val)
    {
        goto done;
    }

    pAssertVal    = BERVAL_NORM_VAL(*pAssert);
    iAssertValLen = BERVAL_NORM_LEN(*pAssert);
    pBerVal       = BERVAL_NORM_VAL(*pBerv);
    iBerValLen    = BERVAL_NORM_LEN(*pBerv);

    if (pAssertVal[0] == '-')
    {
        bAssertSignPositive = FALSE;
        pAssertVal = &pAssertVal[1];
        iAssertValLen--;
    }

    if (pBerVal[0] == '-')
    {
        bBerSignPositive = FALSE;
        pBerVal = &pBerVal[1];
        iBerValLen--;
    }

    if (type == VDIR_SCHEMA_MATCH_EQUAL ||
        type == VDIR_SCHEMA_MATCH_GE    ||
        type == VDIR_SCHEMA_MATCH_LE    )
    {
        // -0 , 0 case
        if (iAssertValLen == 1 && pAssertVal[0] == '0' &&
            iBerValLen    == 1 && pBerVal[0]    == '0')
        {
            bRtn = TRUE;
            goto done;
        }
    }

    switch (type)
    {
    case VDIR_SCHEMA_MATCH_EQUAL:

        if ((bAssertSignPositive != bBerSignPositive) ||
            (iAssertValLen != iBerValLen))
        {
            goto done;
        }

        bRtn = (memcmp(pAssertVal, pBerVal, iBerValLen) == 0);

        break;

    case VDIR_SCHEMA_MATCH_GE:

        if ((!bAssertSignPositive && bBerSignPositive))
        {
            goto done;
        }

        if ((bAssertSignPositive && !bBerSignPositive))
        {
            bRtn = TRUE;
            goto done;
        }

        // values have same sign
        if  (( bAssertSignPositive && iAssertValLen > iBerValLen) ||
             (!bAssertSignPositive && iAssertValLen < iBerValLen))
        {
            bRtn = TRUE;
            goto done;
        }

        // values have same sign and len
        if (iAssertValLen == iBerValLen)
        {
            bRtn = TRUE;
            for (iCnt=0; iCnt < iBerValLen; iCnt++)
            {
                if ( pAssertVal[iCnt] == pBerVal[iCnt] )
                {
                    continue;
                }
                if (( bAssertSignPositive && (pAssertVal[iCnt] < pBerVal[iCnt])) ||
                    (!bAssertSignPositive &&(pAssertVal[iCnt] > pBerVal[iCnt])))
                {
                    bRtn = FALSE;
                    goto done;
                }
                if (( bAssertSignPositive && (pAssertVal[iCnt] > pBerVal[iCnt])) ||
                    (!bAssertSignPositive &&(pAssertVal[iCnt] < pBerVal[iCnt])))
                {
                    bRtn = TRUE;
                    goto done;
                }
            }
            goto done;
        }

        break;

    case VDIR_SCHEMA_MATCH_LE:

        if ((bAssertSignPositive && !bBerSignPositive))
        {
            goto done;
        }

        if ((!bAssertSignPositive && bBerSignPositive))
        {
            bRtn = TRUE;
            goto done;
        }

        // values have same sign
        if  ((!bAssertSignPositive && iAssertValLen > iBerValLen) ||
             ( bAssertSignPositive && iAssertValLen < iBerValLen))
        {
            bRtn = TRUE;
            goto done;
        }

        // values have same sign and len
        if (iAssertValLen == iBerValLen)
        {
            bRtn = TRUE;
            for (iCnt=0; iCnt < iBerValLen; iCnt++)
            {
                if (( bAssertSignPositive && (pAssertVal[iCnt] > pBerVal[iCnt])) ||
                    (!bAssertSignPositive && (pAssertVal[iCnt] < pBerVal[iCnt])))
                {
                    bRtn = FALSE;
                    goto done;
                }
                else if (( bAssertSignPositive && (pAssertVal[iCnt] < pBerVal[iCnt])) ||
                         (!bAssertSignPositive && (pAssertVal[iCnt] > pBerVal[iCnt])))
                {
                    bRtn = TRUE;
                    goto done;
                }
                else
                {   // pAssertVal[iCnt] == pBerVal[iCnt] scenario
                    continue;
                }
            }
        }

        break;

    default:

        break;
    }

done:

    return bRtn;
}
Exemple #4
0
/* InternalSearch: Interface that can be used "internally" by the server code. One of the main differences between
 * this function and MLSearch 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
VmDirInternalSearch(
    PVDIR_OPERATION pOperation
    )
{
    int     retVal = LDAP_SUCCESS;
    DWORD   dwEntryCount = 0;
    BOOLEAN bHasTxn = FALSE;
    PSTR    pszLocalErrMsg = NULL;
    ENTRYID     eId = 0;
    ENTRYID*    pValidatedEntries = NULL;
    PVDIR_LDAP_RESULT   pResult = &(pOperation->ldapResult);
    PVDIR_OPERATION_ML_METRIC  pMLMetrics = NULL;

    assert(pOperation && pOperation->pBEIF);

    pMLMetrics = &pOperation->MLMetrics;
    VMDIR_COLLECT_TIME(pMLMetrics->iMLStartTime);

    // compute required access for this search
    ComputeRequiredAccess(&pOperation->request.searchReq);

    // Normalize (base) DN
    retVal = VmDirNormalizeDN( &(pOperation->reqDn), pOperation->pSchemaCtx );
    BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "DN normalization failed - (%u)(%s)",
                                  retVal, VDIR_SAFE_STRING(VmDirSchemaCtxGetErrorMsg(pOperation->pSchemaCtx)) );

    if (VmDirHandleSpecialSearch( pOperation, pResult )) // TODO, add &pszLocalErrMsg
    {
        retVal = pResult->errCode ? pResult->errCode : pResult->vmdirErrCode;
        BAIL_ON_VMDIR_ERROR_WITH_MSG(retVal, pszLocalErrMsg, "Special search failed - (%u)", retVal);

        goto cleanup;  // done special search
    }

    if (pOperation->dbCopyCtrl) {
        retVal = VmDirExecDbCopyCtrl(pOperation);
        BAIL_ON_VMDIR_ERROR(retVal);
    }

    if (pOperation->syncReqCtrl != NULL) // Replication
    {
        pOperation->opType = VDIR_OPERATION_TYPE_REPL;
    }

    if (pOperation->pReplAgrDisableCtrl)
    {
        retVal = VmDirExecReplAgrEnableDisableCtrl(BERVAL_NORM_VAL(pOperation->reqDn), FALSE);
        BAIL_ON_VMDIR_ERROR(retVal);
    }
    else if (pOperation->pReplAgrEnableCtrl)
    {
        retVal = VmDirExecReplAgrEnableDisableCtrl(BERVAL_NORM_VAL(pOperation->reqDn), TRUE);
        BAIL_ON_VMDIR_ERROR(retVal);
    }

    // If base is not ROOT, read lock the base object (DnToEntryId index entry) to make sure it exists, and it does
    // not get deleted during this search processing.
    if (pOperation->reqDn.lberbv.bv_len != 0)
    {
        VMDIR_COLLECT_TIME(pMLMetrics->iBETxnBeginStartTime);

        retVal = pOperation->pBEIF->pfnBETxnBegin( pOperation->pBECtx, VDIR_BACKEND_TXN_READ );
        BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "txn begin (%u)(%s)",
                retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg));
        bHasTxn = TRUE;

        VMDIR_COLLECT_TIME(pMLMetrics->iBETxnBeginEndTime);

        // Lookup in the DN index.
        retVal = pOperation->pBEIF->pfnBEDNToEntryId( pOperation->pBECtx, &(pOperation->reqDn), &eId );
        BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "DNToEID (%u)(%s)",
                retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg));
    }

    // start txn if not has one already.
    if (! pOperation->pBECtx->pBEPrivate)
    {
        retVal = pOperation->pBEIF->pfnBETxnBegin( pOperation->pBECtx, VDIR_BACKEND_TXN_READ );
        BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "txn begin (%u)(%s)",
                                      retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg));
        bHasTxn = TRUE;
    }

    retVal = AppendDNFilter( pOperation );
    BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Appending DN filter failed.");

    /*
     * TODO cleanup During background thread implementation of page search
     */
    if (gVmdirGlobals.bPagedSearchReadAhead)
    {
        if (pOperation->showPagedResultsCtrl != NULL &&
            !IsNullOrEmptyString(pOperation->showPagedResultsCtrl->value.pagedResultCtrlVal.cookie))
        {
            retVal = VmDirPagedSearchCacheRead(
                        pOperation->showPagedResultsCtrl->value.pagedResultCtrlVal.cookie,
                        &pValidatedEntries,
                        &dwEntryCount);
            BAIL_ON_VMDIR_ERROR(retVal);
        }
    }

    if (pOperation->showPagedResultsCtrl == NULL)
    {
        retVal = _VmDirInternalNormalSearch(pOperation);
        BAIL_ON_VMDIR_ERROR(retVal);
    }
    else
    {
        retVal = _VmDirInternalPagedSearch(pOperation);
        BAIL_ON_VMDIR_ERROR(retVal);
    }

    /*
     * TODO cleanup During background thread implementation of page search
     */
    if (pValidatedEntries != NULL)
    {
        retVal = ProcessPreValidatedEntries(
                    pOperation,
                    dwEntryCount,
                    pValidatedEntries);
        BAIL_ON_VMDIR_ERROR(retVal);
    }

    VMDIR_COLLECT_TIME(pMLMetrics->iBETxnCommitStartTime);

    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;

    VMDIR_COLLECT_TIME(pMLMetrics->iBETxnCommitEndTime);

cleanup:

    // collect metrics
    VMDIR_COLLECT_TIME(pMLMetrics->iMLEndTime);
    VmDirInternalMetricsUpdate(pOperation);
    VmDirInternalMetricsLogInefficientOp(pOperation);

    VMDIR_SAFE_FREE_MEMORY(pValidatedEntries);
    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;
}
Exemple #5
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;
}
Exemple #6
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;
}