/* Editor Settings: expandtabs and use 4 spaces for indentation
 * ex: set softtabstop=4 tabstop=8 expandtab shiftwidth=4: *
 */

/*
 * Copyright Likewise Software    2004-2008
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.  You should have received a copy of the GNU General
 * Public License along with this program.  If not, see
 * <http://www.gnu.org/licenses/>.
 *
 * LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING
 * TERMS AS WELL.  IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT
 * WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE
 * TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU
 * GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE.  IF YOU
 * HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING
 * TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT
 * license@likewisesoftware.com
 */

/*
 * Copyright (C) Likewise Software. All rights reserved.
 *
 * Module Name:
 *
 *        auth.c
 *
 * Abstract:
 *
 *        Likewise Security and Authentication Subsystem (LSASS)
 *
 *        Authentication API (Server)
 *
 * Authors: Krishna Ganugapati (krishnag@likewisesoftware.com)
 *          Sriram Nambakam (snambakam@likewisesoftware.com)
 */
#include "api.h"

static
VOID
LsaSrvAuthenticateRecordResults(
    HANDLE hServer,
    PCSTR pszAccountName,
    DWORD dwError);

DWORD
LsaSrvAuthenticateUserPam(
    HANDLE hServer,
    PLSA_AUTH_USER_PAM_PARAMS pParams,
    PLSA_AUTH_USER_PAM_INFO* ppPamAuthInfo
    )
{
    DWORD dwError = 0;
    DWORD dwTraceFlags[] = {LSA_TRACE_FLAG_AUTHENTICATION};
    BOOLEAN bInLock = FALSE;
    PLSA_AUTH_PROVIDER pProvider = NULL;
    HANDLE hProvider = NULL;
    PLSA_AUTH_USER_PAM_INFO pPamAuthInfo = NULL;

    LSA_TRACE_BEGIN_FUNCTION(dwTraceFlags);

    LW_ASSERT(pParams);
    BAIL_ON_INVALID_STRING(pParams->pszLoginName);

    ENTER_AUTH_PROVIDER_LIST_READER_LOCK(bInLock);

    for (pProvider = gpAuthProviderList; pProvider; pProvider = pProvider->pNext)
    {
        dwError = LsaSrvOpenProvider(
                      hServer,
                      pProvider,
                      NULL,
                      &hProvider);
        BAIL_ON_LSA_ERROR(dwError);

        if (pPamAuthInfo)
        {
            LsaFreeAuthUserPamInfo(pPamAuthInfo);
            pPamAuthInfo = NULL;
        }
        dwError = pProvider->pFnTable->pfnAuthenticateUserPam(
                                            hProvider,
                                            pParams,
                                            &pPamAuthInfo);

        LsaSrvWriteLoginEvent(
            hServer,
            pProvider->pszName,
            pParams->pszLoginName,
            pParams->pszPamSource,
            pParams->dwFlags,
            LSASS_EVENT_LOGON_PHASE_AUTHENTICATE,
            dwError);

        /*
         * Terminate search if the provider has a record for this
         * account regardless of success or failure of the request.
         */
        if ((dwError != LW_ERROR_NOT_HANDLED) &&
            (dwError != LW_ERROR_NO_SUCH_USER))
        {
            BAIL_ON_LSA_ERROR(dwError);
            break;
        }

        LsaSrvCloseProvider(pProvider, hProvider);
        hProvider = NULL;
    }

    if (pProvider == NULL)
    {
        dwError = LW_ERROR_NOT_HANDLED;
    }
    BAIL_ON_LSA_ERROR(dwError);

cleanup:

    if (ppPamAuthInfo)
    {
        *ppPamAuthInfo = pPamAuthInfo;
    }
    else if (pPamAuthInfo)
    {
        LsaFreeAuthUserPamInfo(pPamAuthInfo);
    }

    if (hProvider != NULL)
    {
        LsaSrvCloseProvider(pProvider, hProvider);
    }

    LEAVE_AUTH_PROVIDER_LIST_READER_LOCK(bInLock);

    LsaSrvAuthenticateRecordResults(hServer, pParams->pszLoginName, dwError);

    LSA_TRACE_END_FUNCTION(dwTraceFlags);

    return dwError;

error:

    goto cleanup;
}

static
VOID
LsaSrvAuthenticateRecordResults(
    HANDLE hServer,
    PCSTR pszAccountName,
    DWORD dwError)
{
    if (dwError != ERROR_IO_PENDING)
    {
        LSA_SRV_INCREMENT_SUCCESS_FAIL_METRICS(
            Authentications,
            dwError == LW_ERROR_SUCCESS);

        if (dwError == LW_ERROR_NOT_HANDLED ||
            dwError == LW_ERROR_NO_SUCH_USER)
        {
            LSA_LOG_VERBOSE_ENTRY_NOT_FOUND(
                hServer,
                dwError,
                "authenticate user (name = '%s')",
                LSA_SAFE_LOG_STRING(pszAccountName));
        }
        else if (dwError != LW_ERROR_SUCCESS)
        {
            LSA_LOG_ERROR_API_FAILED(
                hServer,
                dwError,
                "authenticate user (name = '%s')",
                LSA_SAFE_LOG_STRING(pszAccountName));
        }
    }
}

static
DWORD
LsaSrvAuthenticateUserExPlainText(
    HANDLE hServer,
    PCSTR pszTargetProvider,
    PLSA_AUTH_USER_PARAMS pUserParams,
    PLSA_AUTH_USER_INFO *ppUserInfo
    );

static
DWORD
LsaSrvAuthenticateUserExChap(
    HANDLE hServer,
    PCSTR pszTargetProvider,
    PLSA_AUTH_USER_PARAMS pUserParams,
    PLSA_AUTH_USER_INFO *ppUserInfo,
    PLSA_ASYNC_CONTROL_BLOCK pAsyncBlock,
    PLSA_STATUS_BLOCK pStatusBlock
    );

DWORD
LsaSrvAuthenticateUserEx(
    HANDLE hServer,
    PCSTR pszTargetProvider,
    PLSA_AUTH_USER_PARAMS pUserParams,
    PLSA_AUTH_USER_INFO *ppUserInfo,
    PLSA_ASYNC_CONTROL_BLOCK pAsyncBlock,
    PLSA_STATUS_BLOCK pStatusBlock
    )
{
    DWORD dwTraceFlags[] = {LSA_TRACE_FLAG_AUTHENTICATION};
    DWORD dwError = LW_ERROR_INTERNAL;

    LSA_TRACE_BEGIN_FUNCTION(dwTraceFlags);

    BAIL_ON_INVALID_POINTER(pUserParams);
    BAIL_ON_INVALID_POINTER(ppUserInfo);

    switch (pUserParams->AuthType)
    {
        case LSA_AUTH_PLAINTEXT:
            // Currently synchronous.
            dwError = LsaSrvAuthenticateUserExPlainText(
                            hServer,
                            pszTargetProvider,
                            pUserParams,
                            ppUserInfo);
            BAIL_ON_LSA_ERROR(dwError);
            break;

        case LSA_AUTH_CHAP:
            dwError = LsaSrvAuthenticateUserExChap(
                            hServer,
                            pszTargetProvider,
                            pUserParams,
                            ppUserInfo,
                            pAsyncBlock,
                            pStatusBlock);
            BAIL_ON_LSA_ERROR(dwError);
            break;

        default:
            /* Bad AuthType */
            dwError = LW_ERROR_INVALID_PARAMETER;
            LsaSrvAuthenticateRecordResults(hServer, NULL, dwError);
            BAIL_ON_LSA_ERROR(dwError);
            break;
    }

cleanup:

    if (dwError != ERROR_IO_PENDING)
    {
        pStatusBlock->Error = dwError;
    }

    LSA_TRACE_END_FUNCTION(dwTraceFlags);

    return dwError;

error:

    goto cleanup;
}

static
DWORD
LsaSrvAuthenticateUserExPlainText(
    HANDLE hServer,
    PCSTR pszTargetProvider,
    PLSA_AUTH_USER_PARAMS pUserParams,
    PLSA_AUTH_USER_INFO *ppUserInfo
    )
{
    PSTR pszAccountName = NULL;
    DWORD dwError = LW_ERROR_INTERNAL;
    DWORD dwTraceFlags[] = {LSA_TRACE_FLAG_AUTHENTICATION};
    LSA_AUTH_USER_PAM_PARAMS params = { 0 };

    LSA_TRACE_BEGIN_FUNCTION(dwTraceFlags);

    LwAllocateStringPrintf(
        &pszAccountName,
        "%s%s%s",
        pUserParams->pszDomain,
        pUserParams->pszDomain ? "\\" : "",
        pUserParams->pszAccountName);
    BAIL_ON_LSA_ERROR(dwError);

    params.pszLoginName = pszAccountName;
    params.pszPassword = pUserParams->pass.clear.pszPassword;

    /* Pass off plain text auth to AuthenticateUser() */
    dwError = LsaSrvAuthenticateUserPam(hServer, &params, NULL);

cleanup:

    LW_SAFE_FREE_MEMORY(pszAccountName);

    LSA_TRACE_END_FUNCTION(dwTraceFlags);

    return dwError;

error:

    /*
     * LsaSrvAuthenticateUserPam performs it's own reporting. Only
     * record results if we fail prior to invoking that function.
     */
    LsaSrvAuthenticateRecordResults(hServer, pszAccountName, dwError);

    goto cleanup;
}

typedef enum {
    LSA_SRV_AUTH_USER_EX_CHAP_STAGE_TRY_PROVIDER = 0,
    LSA_SRV_AUTH_USER_EX_CHAP_STAGE_PROCESS_RESPONSE,
    LSA_SRV_AUTH_USER_EX_CHAP_STAGE_NEXT_PROVIDER,
    LSA_SRV_AUTH_USER_EX_CHAP_STAGE_INVALID
} LSA_SRV_AUTH_USER_EX_CHAP_STAGE;

typedef struct _LSA_SRV_AUTH_USER_EX_CHAP_CONTEXT {
    LSA_ASYNC_CONTROL_BLOCK AsyncBlock;
    PLSA_ASYNC_CONTROL_BLOCK pCallerAsyncBlock;
    PLSA_STATUS_BLOCK pStatusBlock;

    LSA_SRV_AUTH_USER_EX_CHAP_STAGE stage;
    HANDLE hServer;
    PSTR pszTargetInstance;
    PLSA_AUTH_USER_PARAMS pUserParams;
    PLSA_AUTH_USER_INFO *ppUserInfo;

    HANDLE hProvider;
    PLSA_AUTH_PROVIDER pProvider;

    BOOLEAN bInProviderListLock;
    PLSA_LOGIN_NAME_INFO pLoginInfo;
    LSA_AUTH_USER_PARAMS localUserParams;
    BOOLEAN bFoundProvider;
    PSTR pszTargetProviderName;
} LSA_SRV_AUTH_USER_EX_CHAP_CONTEXT,
 *PLSA_SRV_AUTH_USER_EX_CHAP_CONTEXT;

static
DWORD
LsaSrvAuthenticateUserExChapCreateContext(
    IN HANDLE hServer,
    PCSTR pszTargetProvider,
    IN PLSA_AUTH_USER_PARAMS pUserParams,
    IN PLSA_AUTH_USER_INFO *ppUserInfo,
    IN PLSA_ASYNC_CONTROL_BLOCK pCallerAsyncBlock,
    IN PLSA_STATUS_BLOCK pStatusBlock,
    OUT PLSA_SRV_AUTH_USER_EX_CHAP_CONTEXT* ppContext
    );

static
VOID
LsaSrvAuthenticateUserExChapFreeContext(
    PLSA_SRV_AUTH_USER_EX_CHAP_CONTEXT* ppContext
    );

static
DWORD
LsaSrvAuthenticateUserExChapComplete(
    IN PLSA_SRV_AUTH_USER_EX_CHAP_CONTEXT pContext
    );

static
DWORD
LsaSrvAuthenticateUserExChap(
    HANDLE hServer,
    PCSTR pszTargetProvider,
    PLSA_AUTH_USER_PARAMS pUserParams,
    PLSA_AUTH_USER_INFO *ppUserInfo,
    PLSA_ASYNC_CONTROL_BLOCK pAsyncBlock,
    PLSA_STATUS_BLOCK pStatusBlock
    )
{
    DWORD dwError = LW_ERROR_INTERNAL;
    PLSA_SRV_AUTH_USER_EX_CHAP_CONTEXT pContext = NULL;
    DWORD dwTraceFlags[] = {LSA_TRACE_FLAG_AUTHENTICATION};

    LSA_TRACE_BEGIN_FUNCTION(dwTraceFlags);

    dwError = LsaSrvAuthenticateUserExChapCreateContext(
                    hServer,
                    pszTargetProvider,
                    pUserParams,
                    ppUserInfo,
                    pAsyncBlock,
                    pStatusBlock,
                    &pContext);
    BAIL_ON_LSA_ERROR(dwError);

    dwError = LsaSrvAuthenticateUserExChapComplete(pContext);
    BAIL_ON_LSA_ERROR(dwError);

cleanup:

    if (dwError != ERROR_IO_PENDING)
    {
        pStatusBlock->Error = dwError;
        LsaSrvAuthenticateUserExChapFreeContext(&pContext);
    }

    LSA_TRACE_END_FUNCTION(dwTraceFlags);

    return dwError;

error:

    goto cleanup;
}

static
DWORD
LsaSrvAuthenticateUserExChapComplete(
    IN PLSA_SRV_AUTH_USER_EX_CHAP_CONTEXT pContext
    )
{
    DWORD dwTraceFlags[] = {LSA_TRACE_FLAG_AUTHENTICATION};
    DWORD dwError = pContext->pStatusBlock->Error;

    LSA_TRACE_BEGIN_FUNCTION(dwTraceFlags);

step:
    LW_ASSERT(pContext->pProvider);
    switch (pContext->stage)
    {
        case LSA_SRV_AUTH_USER_EX_CHAP_STAGE_TRY_PROVIDER:
        {
            PCSTR pszTargetProviderName = pContext->pszTargetProviderName;
            PFNAUTHENTICATEUSEREX pfnAuthenticateUserEx =
                pContext->pProvider->pFnTable->pfnAuthenticateUserEx;

            LSA_LOG_TRACE("LSA_SRV_AUTH_USER_EX_CHAP_STAGE_TRY_PROVIDER");
            
            if (pszTargetProviderName)
            {
                if (!strcmp(pszTargetProviderName,
                            pContext->pProvider->pszName))
                {
                    pContext->bFoundProvider = TRUE;
                }
                else
                {
                    pContext->stage =
                        LSA_SRV_AUTH_USER_EX_CHAP_STAGE_NEXT_PROVIDER;
                    goto step;
                }
            }

            dwError = LsaSrvOpenProvider(
                          pContext->hServer,
                          pContext->pProvider,
                          pContext->pszTargetInstance,
                          &pContext->hProvider);
            BAIL_ON_LSA_ERROR(dwError);

            pContext->stage = LSA_SRV_AUTH_USER_EX_CHAP_STAGE_PROCESS_RESPONSE;

            dwError = pfnAuthenticateUserEx(
                            pContext->hProvider,
                            &pContext->localUserParams,
                            pContext->ppUserInfo,
                            &pContext->AsyncBlock,
                            pContext->pStatusBlock);

            if (dwError == ERROR_IO_PENDING)
            {
                BAIL_ON_LSA_ERROR(dwError);
            }

            // Intentional fall through
        }
        case LSA_SRV_AUTH_USER_EX_CHAP_STAGE_PROCESS_RESPONSE:

            LSA_LOG_TRACE("LSA_SRV_AUTH_USER_EX_CHAP_STAGE_PROCESS_RESPONSE");

            LsaSrvWriteLoginEvent(
                pContext->hServer,
                pContext->pProvider->pszName,
                pContext->pUserParams &&
                pContext->pUserParams->pszAccountName ?
                pContext->pUserParams->pszAccountName : "",
                NULL,
                0,
                LSASS_EVENT_LOGON_PHASE_AUTHENTICATE,
                dwError);

            /*
             * Terminate search if the provider has a record for this
             * account regardless of success or failure of the request.
             */
            if ((dwError != LW_ERROR_NOT_HANDLED) &&
                (dwError != LW_ERROR_NO_SUCH_USER))
            {
                BAIL_ON_LSA_ERROR(dwError);
                break;
            }

            pContext->stage = LSA_SRV_AUTH_USER_EX_CHAP_STAGE_NEXT_PROVIDER;
            // Intentional fall through

        case LSA_SRV_AUTH_USER_EX_CHAP_STAGE_NEXT_PROVIDER:

            LSA_LOG_TRACE("LSA_SRV_AUTH_USER_EX_CHAP_STAGE_NEXT_PROVIDER");

            dwError = LW_ERROR_SUCCESS;

            LsaSrvCloseProvider(pContext->pProvider, pContext->hProvider);
            pContext->hProvider = NULL;

            pContext->pProvider = pContext->pProvider->pNext;
            if (pContext->pProvider == NULL)
            {
                if (pContext->pszTargetProviderName &&
                    !pContext->bFoundProvider)
                {
                    dwError = LW_ERROR_INVALID_AUTH_PROVIDER;
                }
                else
                {
                    dwError = LW_ERROR_NOT_HANDLED;
                }
            }
            BAIL_ON_LSA_ERROR(dwError);

            pContext->stage = LSA_SRV_AUTH_USER_EX_CHAP_STAGE_TRY_PROVIDER;
            goto step;

        default:
            dwError = LW_ERROR_INTERNAL;
            BAIL_ON_LSA_ERROR(dwError);
    }

cleanup:
    if (dwError != ERROR_IO_PENDING)
    {
        // We should not get invoked again for this call context.
        pContext->stage = LSA_SRV_AUTH_USER_EX_CHAP_STAGE_INVALID;

        LsaSrvAuthenticateRecordResults(
            pContext->hServer,
            pContext->pUserParams ?
            pContext->pUserParams->pszAccountName : NULL,
            dwError);
        pContext->pStatusBlock->Error = dwError;
    }

    LSA_TRACE_END_FUNCTION(dwTraceFlags);

    return dwError;

error:

    goto cleanup;
}

static
VOID
LsaSrvAuthenticateUserExChapAsyncComplete(
    PVOID pCallbackContext
    );

static
DWORD
LsaSrvAuthenticateUserExChapCreateContext(
    IN HANDLE hServer,
    PCSTR pszTargetProvider,
    IN PLSA_AUTH_USER_PARAMS pUserParams,
    IN PLSA_AUTH_USER_INFO *ppUserInfo,
    PLSA_ASYNC_CONTROL_BLOCK pCallerAsyncBlock,
    PLSA_STATUS_BLOCK pStatusBlock,
    OUT PLSA_SRV_AUTH_USER_EX_CHAP_CONTEXT* ppContext
    )
{
    DWORD dwError = LW_ERROR_INTERNAL;
    PLSA_SRV_AUTH_USER_EX_CHAP_CONTEXT pContext = NULL;

    dwError = LwAllocateMemory(sizeof(*pContext), OUT_PPVOID(&pContext));
    BAIL_ON_LSA_ERROR(dwError);

    pContext->AsyncBlock.Callback = LsaSrvAuthenticateUserExChapAsyncComplete;
    pContext->AsyncBlock.CallbackContext = pContext;
    pContext->pCallerAsyncBlock = pCallerAsyncBlock;
    pContext->pStatusBlock = pStatusBlock;

    pContext->hServer = hServer;
    pContext->pUserParams = pUserParams;
    pContext->ppUserInfo = ppUserInfo;
    pContext->hProvider = NULL;
    pContext->pProvider = NULL;
    pContext->bInProviderListLock = FALSE;
    pContext->pLoginInfo = NULL;
    pContext->bFoundProvider = FALSE;
    pContext->pszTargetProviderName = NULL;
    pContext->pszTargetInstance = NULL;

    /* Copy of input parameters structure so we can modify it. */
    pContext->localUserParams = *pUserParams;

    /* Fix up the name.  This allows us to handle a UPN */
    dwError = LsaSrvCrackDomainQualifiedName(
                    pUserParams->pszAccountName,
                    &pContext->pLoginInfo);
    BAIL_ON_LSA_ERROR(dwError);

    if (pContext->pLoginInfo->nameType == NameType_UPN)
    {
        /* If they gave us a UPN, our domain MUST be NULL */
        pContext->localUserParams.pszDomain = NULL;
    }

    if (pszTargetProvider)
    {
        dwError = LsaSrvGetTargetElements(
                        pszTargetProvider,
                        &pContext->pszTargetProviderName,
                        &pContext->pszTargetInstance);
        BAIL_ON_LSA_ERROR(dwError);
    }

    ENTER_AUTH_PROVIDER_LIST_READER_LOCK(pContext->bInProviderListLock);

    pContext->pProvider = gpAuthProviderList;

cleanup:

    *ppContext = pContext;

    return dwError;

error:

    LsaSrvAuthenticateUserExChapFreeContext(&pContext);

    goto cleanup;
}

static
VOID
LsaSrvAuthenticateUserExChapFreeContext(
    PLSA_SRV_AUTH_USER_EX_CHAP_CONTEXT* ppContext
    )
{
    PLSA_SRV_AUTH_USER_EX_CHAP_CONTEXT pContext = *ppContext;

    if (pContext)
    {
        LW_SAFE_FREE_STRING(pContext->pszTargetProviderName);
        LW_SAFE_FREE_STRING(pContext->pszTargetInstance);

        if (pContext->hProvider != NULL)
        {
            LsaSrvCloseProvider(pContext->pProvider, pContext->hProvider);
        }

        LEAVE_AUTH_PROVIDER_LIST_READER_LOCK(pContext->bInProviderListLock);

        if (pContext->pLoginInfo)
        {
            LsaSrvFreeNameInfo(pContext->pLoginInfo);
        }

        LW_SAFE_FREE_MEMORY(pContext);
        *ppContext = NULL;
    }
}

static
VOID
LsaSrvAuthenticateUserExChapAsyncComplete(
    PVOID pCallbackContext
    )
{
    PLSA_SRV_AUTH_USER_EX_CHAP_CONTEXT pContext = pCallbackContext;
    PLSA_ASYNC_CONTROL_BLOCK pCallerAsyncBlock = pContext->pCallerAsyncBlock;
    DWORD dwError = LW_ERROR_INTERNAL;

    dwError = LsaSrvAuthenticateUserExChapComplete(pContext);
    if (dwError != ERROR_IO_PENDING)
    {
        pCallerAsyncBlock->Callback(pCallerAsyncBlock->CallbackContext);
        LsaSrvAuthenticateUserExChapFreeContext(&pContext);
    }
}

DWORD
LsaSrvValidateUser(
    HANDLE hServer,
    PCSTR  pszLoginId,
    PCSTR  pszPassword
    )
{
    DWORD dwError = 0;
    DWORD dwTraceFlags[] = {LSA_TRACE_FLAG_AUTHENTICATION};
    BOOLEAN bInLock = FALSE;
    PLSA_AUTH_PROVIDER pProvider = NULL;
    HANDLE hProvider = NULL;

    LSA_TRACE_BEGIN_FUNCTION(dwTraceFlags);

    BAIL_ON_INVALID_STRING(pszLoginId);

    ENTER_AUTH_PROVIDER_LIST_READER_LOCK(bInLock);

    dwError = LW_ERROR_NOT_HANDLED;

    for (pProvider = gpAuthProviderList; pProvider; pProvider = pProvider->pNext)
    {
        dwError = LsaSrvOpenProvider(
                      hServer,
                      pProvider,
                      NULL,
                      &hProvider);
        BAIL_ON_LSA_ERROR(dwError);

        dwError = pProvider->pFnTable->pfnValidateUser(
                                            hProvider,
                                            pszLoginId,
                                            pszPassword);
        if (!dwError)
        {
           break;
        }
        else if ((dwError == LW_ERROR_NOT_HANDLED) ||
                 (dwError == LW_ERROR_NO_SUCH_USER))
        {
            LsaSrvCloseProvider(pProvider, hProvider);
            hProvider = NULL;
            continue;
        }
        else
        {
            BAIL_ON_LSA_ERROR(dwError);
        }
    }

cleanup:

    if (hProvider != NULL)
    {
        LsaSrvCloseProvider(pProvider, hProvider);
    }

    LEAVE_AUTH_PROVIDER_LIST_READER_LOCK(bInLock);

    LSA_TRACE_END_FUNCTION(dwTraceFlags);

    return dwError;

error:
    if (dwError == LW_ERROR_NOT_HANDLED ||
        dwError == LW_ERROR_NO_SUCH_USER)
    {
        LSA_LOG_VERBOSE_ENTRY_NOT_FOUND(
            hServer,
            dwError,
            "validate user for login (name = '%s')",
            LSA_SAFE_LOG_STRING(pszLoginId));
    }
    else
    {
        LSA_LOG_ERROR_API_FAILED(
            hServer,
            dwError,
            "validate user for login (name = '%s')",
            LSA_SAFE_LOG_STRING(pszLoginId));
    }

    goto cleanup;
}

DWORD
LsaSrvCheckUserInList(
    HANDLE hServer,
    PCSTR  pszLoginId,
    PCSTR  pszListName
    )
{
    DWORD dwError = 0;
    DWORD dwTraceFlags[] = {LSA_TRACE_FLAG_AUTHENTICATION};
    BOOLEAN bInLock = FALSE;
    PLSA_AUTH_PROVIDER pProvider = NULL;
    HANDLE hProvider = NULL;

    LSA_TRACE_BEGIN_FUNCTION(dwTraceFlags);

    ENTER_AUTH_PROVIDER_LIST_READER_LOCK(bInLock);

    dwError = LW_ERROR_NOT_HANDLED;

    for (pProvider = gpAuthProviderList; pProvider; pProvider = pProvider->pNext)
    {
        dwError = LsaSrvOpenProvider(
                      hServer,
                      pProvider,
                      NULL,
                      &hProvider);
        BAIL_ON_LSA_ERROR(dwError);

        dwError = pProvider->pFnTable->pfnCheckUserInList(
                                            hProvider,
                                            pszLoginId,
                                            pszListName);
        LsaSrvWriteLoginEvent(
            hServer,
            pProvider->pszName,
            pszLoginId,
            NULL,
            0,
            LSASS_EVENT_LOGON_PHASE_CHECK_USER,
            dwError);

        /*
         * Terminate search if the provider has a record for this
         * account regardless of success or failure of the request.
         */
        if ((dwError != LW_ERROR_NOT_HANDLED) &&
            (dwError != LW_ERROR_NO_SUCH_USER))
        {
            BAIL_ON_LSA_ERROR(dwError);
            break;
        }

        LsaSrvCloseProvider(pProvider, hProvider);
        hProvider = NULL;
    }

cleanup:

    if (hProvider != NULL)
    {
        LsaSrvCloseProvider(pProvider, hProvider);
    }

    LEAVE_AUTH_PROVIDER_LIST_READER_LOCK(bInLock);

    LSA_TRACE_END_FUNCTION(dwTraceFlags);

    return dwError;

error:
    if (dwError == LW_ERROR_ACCESS_DENIED)
    {
        LSA_LOG_VERBOSE_ENTRY_NOT_FOUND(
            hServer,
            dwError,
            "find user in list (user = '%s', list = '%s')",
            LSA_SAFE_LOG_STRING(pszLoginId),
            LSA_SAFE_LOG_STRING(pszListName));
    }
    else
    {
        LSA_LOG_ERROR_API_FAILED(
            hServer,
            dwError,
            "find user in list (user = '%s', list = '%s')",
            LSA_SAFE_LOG_STRING(pszLoginId),
            LSA_SAFE_LOG_STRING(pszListName));
    }

    goto cleanup;
}

DWORD
LsaSrvChangePassword(
    HANDLE hServer,
    PCSTR  pszLoginId,
    PCSTR  pszPassword,
    PCSTR  pszOldPassword
    )
{
    DWORD  dwError = 0;
    DWORD dwTraceFlags[] = {LSA_TRACE_FLAG_AUTHENTICATION};
    PLSA_AUTH_PROVIDER pProvider = NULL;
    HANDLE hProvider = NULL;
    BOOLEAN bInLock = FALSE;

    LSA_TRACE_BEGIN_FUNCTION(dwTraceFlags);

    ENTER_AUTH_PROVIDER_LIST_READER_LOCK(bInLock);

    dwError = LW_ERROR_NOT_HANDLED;

    for (pProvider = gpAuthProviderList; pProvider; pProvider = pProvider->pNext)
    {
        dwError = LsaSrvOpenProvider(
                      hServer,
                      pProvider,
                      NULL,
                      &hProvider);
        BAIL_ON_LSA_ERROR(dwError);

        dwError = pProvider->pFnTable->pfnChangePassword(
                                            hProvider,
                                            pszLoginId,
                                            pszPassword,
                                            pszOldPassword);
        if (!dwError)
        {
            if (LsaSrvEventlogEnabled())
            {
                LsaSrvWriteUserPWChangeSuccessEvent(
                    hServer,
                    pProvider->pszName,
                    pszLoginId);
            }
            break;
        }
        else if ((dwError == LW_ERROR_NOT_HANDLED) ||
                 (dwError == LW_ERROR_NO_SUCH_USER))
        {
            LsaSrvCloseProvider(pProvider, hProvider);
            hProvider = NULL;
            continue;
        }
        else
        {
            if (LsaSrvEventlogEnabled())
            {
                LsaSrvWriteUserPWChangeFailureEvent(
                    hServer,
                    pProvider->pszName,
                    pszLoginId,
                    dwError);
            }
            BAIL_ON_LSA_ERROR(dwError);
        }
    }

cleanup:

    if (hProvider != NULL)
    {
        LsaSrvCloseProvider(pProvider, hProvider);
    }

    LEAVE_AUTH_PROVIDER_LIST_READER_LOCK(bInLock);

    LSA_SRV_INCREMENT_SUCCESS_FAIL_METRICS(
        ChangePassword,
        dwError == LW_ERROR_SUCCESS);

    LSA_TRACE_END_FUNCTION(dwTraceFlags);

    return dwError;

error:
    LSA_LOG_ERROR_API_FAILED(
        hServer,
        dwError,
        "change password of user (name = '%s')",
        LSA_SAFE_LOG_STRING(pszLoginId));

    goto cleanup;
}

DWORD
LsaSrvSetPassword(
    HANDLE hServer,
    PCSTR  pszLoginId,
    PCSTR  pszPassword
    )
{
    DWORD dwError = 0;
    DWORD dwTraceFlags[] = {LSA_TRACE_FLAG_AUTHENTICATION};
    PLSA_AUTH_PROVIDER pProvider = NULL;
    HANDLE hProvider = NULL;
    BOOLEAN bInLock = FALSE;

    LSA_TRACE_BEGIN_FUNCTION(dwTraceFlags);

    ENTER_AUTH_PROVIDER_LIST_READER_LOCK(bInLock);

    dwError = LW_ERROR_NOT_HANDLED;

    for (pProvider = gpAuthProviderList; pProvider; pProvider = pProvider->pNext)
    {
        dwError = LsaSrvOpenProvider(
                      hServer,
                      pProvider,
                      NULL,
                      &hProvider);
        BAIL_ON_LSA_ERROR(dwError);

        dwError = pProvider->pFnTable->pfnSetPassword(
                                            hProvider,
                                            pszLoginId,
                                            pszPassword);
        if (!dwError)
        {
            break;
        }
        else if ((dwError == LW_ERROR_NOT_HANDLED) ||
                 (dwError == LW_ERROR_NO_SUCH_USER))
        {
            LsaSrvCloseProvider(pProvider, hProvider);
            hProvider = NULL;
            continue;
        }
        else
        {
            BAIL_ON_LSA_ERROR(dwError);
        }
    }

cleanup:
    if (hProvider != NULL)
    {
        LsaSrvCloseProvider(pProvider, hProvider);
    }

    LEAVE_AUTH_PROVIDER_LIST_READER_LOCK(bInLock);

    LSA_TRACE_END_FUNCTION(dwTraceFlags);

    return dwError;

error:
    LSA_LOG_ERROR_API_FAILED(
        hServer,
        dwError,
        "set password of user (name = '%s')",
        LSA_SAFE_LOG_STRING(pszLoginId));

    goto cleanup;
}


/*
local variables:
mode: c
c-basic-offset: 4
indent-tabs-mode: nil
tab-width: 4
end:
*/
