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

/*
 * Copyright Likewise Software    2004-2009
 * All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the license, or (at
 * your option) any later version.
 *
 * This library 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 Lesser
 * General Public License for more details.  You should have received a copy
 * of the GNU Lesser 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
 * LESSER 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:
 *
 *        samr_setuserinfo.c
 *
 * Abstract:
 *
 *        Remote Procedure Call (RPC) Server Interface
 *
 *        SamrSrvSetUserInfo function
 *
 * Authors: Rafal Szczesniak (rafal@likewise.com)
 */

#include "includes.h"


static
NTSTATUS
SamrSrvCheckPasswordPolicy(
    IN  PACCOUNT_CONTEXT pAcctCtx,
    IN  PWSTR            pwszPassword
    );


NTSTATUS
SamrSrvSetUserInfo(
    IN  handle_t        hBinding,
    IN  ACCOUNT_HANDLE  hUser,
    IN  UINT16          usLevel,
    IN  UserInfo       *pInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;

    ntStatus = SamrSrvSetUserInfoInternal(hBinding,
                                          hUser,
                                          usLevel,
                                          pInfo);
    return ntStatus;
}


#define SET_UNICODE_STRING_VALUE(var, obj, fieldFlag) \
    do { \
        PWSTR pwszValue = NULL; \
        \
        dwError = LwAllocateWc16StringFromUnicodeString( \
                                &pwszValue, \
                                &(var)); \
        if (dwError != ERROR_SUCCESS) \
        { \
            LW_SAFE_FREE_MEMORY(pwszValue); \
            BAIL_ON_LSA_ERROR(dwError); \
        } \
        \
        (obj) = pwszValue; \
        updateFields |= (fieldFlag); \
    } while (0);

#define SET_UINT32_VALUE(var, obj, mod)                           \
    do {                                                          \
        DWORD dwValue = (var);                                    \
                                                                  \
        (obj) = dwValue;                                          \
        updateFields |= (mod);                                     \
    } while (0);

#define SET_NTTIME_VALUE(var, obj, mod)                           \
    do {                                                          \
        ULONG64 llValue = (var);                                  \
                                                                  \
        (obj) = llValue;                                          \
        updateFields |= (mod);                                     \
    } while (0);

#define SET_BLOB_VALUE(var, len, blob, mod)                       \
    do {                                                          \
        blob.Size = (len);                                        \
        blob.Data = (var);                                        \
        /* KKJA: not safe if this gets freed in samdb object */   \
        updateFields |= (mod);                                     \
    } while (0);

#define TEST_ACCOUNT_FIELD_FLAG(field, flag)                      \
    if (!((field) & (flag)))                                      \
    {                                                             \
        break;                                                    \
    }

#define SET_UNICODE_STRING_VALUE_BY_FLAG(pinfo, field, flag,      \
                                         obj, mod)                \
    do {                                                          \
        TEST_ACCOUNT_FIELD_FLAG((pinfo)->fields_present,          \
                                (flag));                          \
        SET_UNICODE_STRING_VALUE((pinfo)->field,                  \
                                 (obj), (mod));                   \
    } while (0);

#define SET_UINT32_VALUE_BY_FLAG(pinfo, field, flag, obj, mod)    \
    do {                                                          \
        TEST_ACCOUNT_FIELD_FLAG((pinfo)->fields_present,          \
                                (flag));                          \
        SET_UINT32_VALUE((pinfo)->field, (obj), (mod));           \
    } while (0);

#define SET_NTTIME_VALUE_BY_FLAG(pinfo, field, flag, obj, mod)    \
    do {                                                          \
        TEST_ACCOUNT_FIELD_FLAG((pinfo)->fields_present,          \
                                (flag));                          \
        SET_NTTIME_VALUE((pinfo)->field, (obj), (mod));           \
    } while (0);

#define SET_BLOB_VALUE_BY_FLAG(pinfo, field, flag, blob, mod)     \
    do {                                                          \
        TEST_ACCOUNT_FIELD_FLAG((pinfo)->fields_present,          \
                                (flag));                          \
        SET_BLOB_VALUE((pinfo)->field.Buffer,                     \
                       (pinfo)->field.Length,                     \
                       blob, mod);                                \
    } while (0);

#define EXPIRE_PASSWORD_BY_FLAG(pinfo, field, flag, obj, mod)     \
    do {                                                          \
        TEST_ACCOUNT_FIELD_FLAG((pinfo)->fields_present,          \
                                (flag));                          \
        if ((pinfo)->field)                                       \
        {                                                         \
            SET_NTTIME_VALUE(0, (obj), (mod));                    \
        }                                                         \
    } while (0);



NTSTATUS
SamrSrvSetUserInfoInternal(
    IN  handle_t        hBinding,
    IN  ACCOUNT_HANDLE  hUser,
    IN  UINT16          level,
    IN  UserInfo       *pInfo
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    DWORD dwError = ERROR_SUCCESS;
    PCONNECT_CONTEXT pConnCtx = NULL;
    PDOMAIN_CONTEXT pDomCtx = NULL;
    PACCOUNT_CONTEXT pAcctCtx = NULL;
    HANDLE hDirectory = NULL;
    PWSTR pwszPassword = NULL;
    DWORD dwPasswordLen = 0;
    SAMDB_OBJECT_INFO updateObject = {0};
    PSAMDB_USER_INFO userObject = NULL;
    SAMDB_FIELDS updateFields = 0;
    SAMDB_UF_FLAGS userAccountFlags = 0;

    pAcctCtx = (PACCOUNT_CONTEXT)hUser;

    if (pAcctCtx == NULL || pAcctCtx->Type != SamrContextAccount)
    {
        ntStatus = STATUS_INVALID_HANDLE;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    if (!(pAcctCtx->dwAccessGranted & USER_ACCESS_SET_ATTRIBUTES))
    {
        ntStatus = STATUS_ACCESS_DENIED;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    pDomCtx       = pAcctCtx->pDomCtx;
    pConnCtx      = pDomCtx->pConnCtx;
    hDirectory    = pConnCtx->hDirectory;

    /*
     * Check if there's an account rename pending
     */
    if (level == 6)
    {
        ntStatus = SamrSrvRenameAccount(hUser,
                                        &pInfo->info6.account_name);
    }
    else if (level == 7)
    {
        ntStatus = SamrSrvRenameAccount(hUser,
                                        &pInfo->info7.account_name);

        /* No further modification of user account is needed */
        goto error;
    }
    else if (level == 21 &&
             pInfo->info21.fields_present & SAMR_FIELD_ACCOUNT_NAME)
    {
        ntStatus = SamrSrvRenameAccount(hUser,
                                        &pInfo->info21.account_name);
    }
    BAIL_ON_NT_STATUS(ntStatus);

    switch (level)
    {
    case 6:
    case 8:
        if (!(pAcctCtx->dwAccessGranted & USER_ACCESS_SET_LOC_COM) ||
            !(pAcctCtx->dwAccessGranted & USER_ACCESS_SET_ATTRIBUTES))
        {
            ntStatus = STATUS_ACCESS_DENIED;
        }
        break;

    case 25:
        if (!(pAcctCtx->dwAccessGranted & USER_ACCESS_SET_PASSWORD) ||
            !(pAcctCtx->dwAccessGranted & USER_ACCESS_SET_ATTRIBUTES))
        {
            ntStatus = STATUS_ACCESS_DENIED;
        }
        break;

    case 9:
    case 10:
    case 11:
    case 12:
    case 13:
    case 14:
    case 16:
    case 17:
    case 20:
    case 21:
        if (!(pAcctCtx->dwAccessGranted & USER_ACCESS_SET_ATTRIBUTES))
        {
            ntStatus = STATUS_ACCESS_DENIED;
        }
        break;

    case 26:
        if (!(pAcctCtx->dwAccessGranted & USER_ACCESS_SET_PASSWORD))
        {
            ntStatus = STATUS_ACCESS_DENIED;
        }
        break;
    }
    BAIL_ON_NT_STATUS(ntStatus);

    if (pAcctCtx->pSid == NULL)
    {
        ntStatus = STATUS_INVALID_HANDLE;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    updateObject.Type = pAcctCtx->ObjectType;
    updateObject.Sid  = pAcctCtx->pSid;

    userObject = &updateObject.Info.User;

    /*
     * Now update other fields
     */
    switch (level)
    {
    case 6:
        SET_UNICODE_STRING_VALUE(pInfo->info6.full_name,
                                 userObject->FullName,
                                 SAMDB_FIELD_USER_FULL_NAME);
        break;

    case 8:
        SET_UNICODE_STRING_VALUE(pInfo->info8.full_name,
                                 userObject->FullName,
                                 SAMDB_FIELD_USER_FULL_NAME);
        break;

    case 9:
        SET_UINT32_VALUE(pInfo->info9.primary_gid,
                         userObject->PrimaryGroupRid,
                         SAMDB_FIELD_USER_PRIMARY_GROUP_RID);
        break;

    case 10:
        SET_UNICODE_STRING_VALUE(pInfo->info10.home_directory,
                                 userObject->HomeDirectory,
                                 SAMDB_FIELD_USER_HOME_DIRECTORY);
        SET_UNICODE_STRING_VALUE(pInfo->info10.home_drive,
                                 userObject->HomeDirectoryDrive,
                                 SAMDB_FIELD_USER_HOME_DIRECTORY_DRIVE);
        break;

    case 11:
        SET_UNICODE_STRING_VALUE(pInfo->info11.logon_script,
                                 userObject->ScriptPath,
                                 SAMDB_FIELD_USER_SCRIPT_PATH);
        break;

    case 12:
        SET_UNICODE_STRING_VALUE(pInfo->info12.profile_path,
                                 userObject->ProfilePath,
                                 SAMDB_FIELD_USER_PROFILE_PATH);
        break;

    case 13:
        SET_UNICODE_STRING_VALUE(pInfo->info13.description,
                                 userObject->Description,
                                 SAMDB_FIELD_USER_DESCRIPTION);
        break;

    case 14:
        ntStatus = STATUS_NOT_SUPPORTED;
        BAIL_ON_NT_STATUS(ntStatus);
        break;

    case 16:
        userAccountFlags = SamDbMapSamrToDatabaseFlags(
                                pInfo->info16.account_flags,
                                NULL);
        SET_UINT32_VALUE(userAccountFlags,
                         userObject->UserAccountControl,
                         SAMDB_FIELD_USER_ACCOUNT_CONTROL);
        break;

    case 17:
        SET_NTTIME_VALUE(pInfo->info17.account_expiry,
                         userObject->AccountExpires,
                         SAMDB_FIELD_USER_ACCOUNT_EXPIRES);
        break;

    case 20:
        SET_BLOB_VALUE(pInfo->info20.parameters.Buffer,
                       pInfo->info20.parameters.Length,
                       userObject->Parameters,
                       SAMDB_FIELD_USER_PARAMETERS);
        break;

    case 21:
        SET_NTTIME_VALUE_BY_FLAG(&pInfo->info21, last_password_change,
                                 SAMR_FIELD_LAST_PWD_CHANGE,
                                 userObject->PasswordLastSet,
                                 SAMDB_FIELD_USER_PASSWORD_LAST_SET);
        SET_NTTIME_VALUE_BY_FLAG(&pInfo->info21, account_expiry,
                                 SAMR_FIELD_ACCT_EXPIRY,
                                 userObject->AccountExpires,
                                 SAMDB_FIELD_USER_ACCOUNT_EXPIRES);
        SET_UNICODE_STRING_VALUE_BY_FLAG(&pInfo->info21, full_name,
                                         SAMR_FIELD_FULL_NAME,
                                         userObject->FullName,
                                         SAMDB_FIELD_USER_FULL_NAME);
        SET_UNICODE_STRING_VALUE_BY_FLAG(&pInfo->info21, home_directory,
                                         SAMR_FIELD_HOME_DIRECTORY,
                                         userObject->HomeDirectory,
                                         SAMDB_FIELD_USER_HOME_DIRECTORY);
        SET_UNICODE_STRING_VALUE_BY_FLAG(&pInfo->info21, home_drive,
                                         SAMR_FIELD_HOME_DRIVE,
                                         userObject->HomeDirectoryDrive,
                                         SAMDB_FIELD_USER_HOME_DIRECTORY_DRIVE);
        SET_UNICODE_STRING_VALUE_BY_FLAG(&pInfo->info21, logon_script,
                                         SAMR_FIELD_LOGON_SCRIPT,
                                         userObject->ScriptPath,
                                         SAMDB_FIELD_USER_SCRIPT_PATH);
        SET_UNICODE_STRING_VALUE_BY_FLAG(&pInfo->info21, profile_path,
                                         SAMR_FIELD_PROFILE_PATH,
                                         userObject->ProfilePath,
                                         SAMDB_FIELD_USER_PROFILE_PATH);
        SET_UNICODE_STRING_VALUE_BY_FLAG(&pInfo->info21, description,
                                         SAMR_FIELD_DESCRIPTION,
                                         userObject->Description,
                                         SAMDB_FIELD_USER_DESCRIPTION);
        SET_UNICODE_STRING_VALUE_BY_FLAG(&pInfo->info21, comment,
                                         SAMR_FIELD_COMMENT,
                                         userObject->UserComment,
                                         SAMDB_FIELD_USER_USER_COMMENT);
        SET_BLOB_VALUE_BY_FLAG(&pInfo->info21, parameters,
                               SAMR_FIELD_PARAMETERS,
                               userObject->Parameters,
                               SAMDB_FIELD_USER_PARAMETERS);
        SET_UINT32_VALUE_BY_FLAG(&pInfo->info21, primary_gid,
                                 SAMR_FIELD_PRIMARY_GID,
                                 userObject->PrimaryGroupRid,
                                 SAMDB_FIELD_USER_PRIMARY_GROUP_RID);

        //
        // Map SAMR flags to SAMDB flags first
        //
        pInfo->info21.account_flags = SamDbMapSamrToDatabaseFlags(
                                             pInfo->info21.account_flags,
                                             NULL);
        SET_UINT32_VALUE_BY_FLAG(&pInfo->info21, account_flags,
                                 SAMR_FIELD_ACCT_FLAGS,
                                 userObject->UserAccountControl,
                                 SAMDB_FIELD_USER_ACCOUNT_CONTROL);

        SET_UINT32_VALUE_BY_FLAG(&pInfo->info21, bad_password_count,
                                 SAMR_FIELD_BAD_PWD_COUNT,
                                 userObject->BadPasswordCount,
                                 SAMDB_FIELD_USER_BAD_PASSWORD_COUNT);
        SET_UINT32_VALUE_BY_FLAG(&pInfo->info21, country_code,
                                 SAMR_FIELD_COUNTRY_CODE,
                                 userObject->CountryCode,
                                 SAMDB_FIELD_USER_COUNTRY_CODE);
        SET_UINT32_VALUE_BY_FLAG(&pInfo->info21, code_page,
                                 SAMR_FIELD_CODE_PAGE,
                                 userObject->CodePage,
                                 SAMDB_FIELD_USER_CODE_PAGE);
        break;

    case 25:
        SET_NTTIME_VALUE_BY_FLAG(&pInfo->info25.info, last_password_change,
                                 SAMR_FIELD_LAST_PWD_CHANGE,
                                 userObject->PasswordLastSet,
                                 SAMDB_FIELD_USER_PASSWORD_LAST_SET);
        SET_NTTIME_VALUE_BY_FLAG(&pInfo->info25.info, account_expiry,
                                 SAMR_FIELD_ACCT_EXPIRY,
                                 userObject->AccountExpires,
                                 SAMDB_FIELD_USER_ACCOUNT_EXPIRES);
        SET_UNICODE_STRING_VALUE_BY_FLAG(&pInfo->info25.info, full_name,
                                         SAMR_FIELD_FULL_NAME,
                                         userObject->FullName,
                                         SAMDB_FIELD_USER_FULL_NAME);
        SET_UNICODE_STRING_VALUE_BY_FLAG(&pInfo->info25.info, home_directory,
                                         SAMR_FIELD_HOME_DIRECTORY,
                                         userObject->HomeDirectory,
                                         SAMDB_FIELD_USER_HOME_DIRECTORY);
        SET_UNICODE_STRING_VALUE_BY_FLAG(&pInfo->info25.info, home_drive,
                                         SAMR_FIELD_HOME_DRIVE,
                                         userObject->HomeDirectoryDrive,
                                         SAMDB_FIELD_USER_HOME_DIRECTORY_DRIVE);
        SET_UNICODE_STRING_VALUE_BY_FLAG(&pInfo->info25.info, logon_script,
                                         SAMR_FIELD_LOGON_SCRIPT,
                                         userObject->ScriptPath,
                                         SAMDB_FIELD_USER_SCRIPT_PATH);
        SET_UNICODE_STRING_VALUE_BY_FLAG(&pInfo->info25.info, profile_path,
                                         SAMR_FIELD_PROFILE_PATH,
                                         userObject->ProfilePath,
                                         SAMDB_FIELD_USER_PROFILE_PATH);
        SET_UNICODE_STRING_VALUE_BY_FLAG(&pInfo->info25.info, description,
                                         SAMR_FIELD_DESCRIPTION,
                                         userObject->Description,
                                         SAMDB_FIELD_USER_DESCRIPTION);
        SET_UNICODE_STRING_VALUE_BY_FLAG(&pInfo->info25.info, comment,
                                         SAMR_FIELD_COMMENT,
                                         userObject->UserComment,
                                         SAMDB_FIELD_USER_USER_COMMENT);
        SET_BLOB_VALUE_BY_FLAG(&pInfo->info25.info, parameters,
                               SAMR_FIELD_PARAMETERS,
                               userObject->Parameters,
                               SAMDB_FIELD_USER_PARAMETERS);
        SET_UINT32_VALUE_BY_FLAG(&pInfo->info25.info, primary_gid,
                                 SAMR_FIELD_PRIMARY_GID,
                                 userObject->PrimaryGroupRid,
                                 SAMDB_FIELD_USER_PRIMARY_GROUP_RID);

        //
        // Map SAMR flags to SAMDB flags first
        //
        pInfo->info25.info.account_flags = SamDbMapSamrToDatabaseFlags(
                                               pInfo->info25.info.account_flags,
                                               NULL);
        SET_UINT32_VALUE_BY_FLAG(&pInfo->info25.info, account_flags,
                                 SAMR_FIELD_ACCT_FLAGS,
                                 userObject->UserAccountControl,
                                 SAMDB_FIELD_USER_ACCOUNT_CONTROL);

        SET_UINT32_VALUE_BY_FLAG(&pInfo->info25.info, bad_password_count,
                                 SAMR_FIELD_BAD_PWD_COUNT,
                                         userObject->BadPasswordCount,
                                         SAMDB_FIELD_USER_BAD_PASSWORD_COUNT);
        SET_UINT32_VALUE_BY_FLAG(&pInfo->info25.info, country_code,
                                 SAMR_FIELD_COUNTRY_CODE,
                                         userObject->CountryCode,
                                         SAMDB_FIELD_USER_COUNTRY_CODE);
        SET_UINT32_VALUE_BY_FLAG(&pInfo->info25.info, code_page,
                                 SAMR_FIELD_CODE_PAGE,
                                         userObject->CodePage,
                                         SAMDB_FIELD_USER_CODE_PAGE);

        ntStatus = SamrSrvDecryptPasswordBlobEx(pConnCtx,
                                                &pInfo->info25.password,
                                                NULL,
                                                0,
                                                0,
                                                &pwszPassword);
        BAIL_ON_NT_STATUS(ntStatus);

        ntStatus = SamrSrvCheckPasswordPolicy(pAcctCtx,
                                              pwszPassword);
        BAIL_ON_NT_STATUS(ntStatus);

        if (pwszPassword)
        {
            ntStatus = SamrCreateNtOwfPassword(
                                       pwszPassword,
                                       userObject->NtOwfPassword.Data,
                                       sizeof(userObject->NtOwfPassword.Data));
            BAIL_ON_NT_STATUS(ntStatus);

            userObject->NtOwfPassword.IsPresent = TRUE;

            updateFields |= SAMDB_FIELD_USER_NT_OWF_PASSWORD;
        }
        break;

    case 26:
        dwPasswordLen = pInfo->info26.password_len;
        ntStatus = SamrSrvDecryptPasswordBlobEx(pConnCtx,
                                                &pInfo->info26.password,
                                                NULL,
                                                0,
                                                dwPasswordLen,
                                                &pwszPassword);
        BAIL_ON_NT_STATUS(ntStatus);

        ntStatus = SamrSrvCheckPasswordPolicy(pAcctCtx,
                                              pwszPassword);
        BAIL_ON_NT_STATUS(ntStatus);

        if (pwszPassword)
        {
            ntStatus = SamrCreateNtOwfPassword(
                                       pwszPassword,
                                       userObject->NtOwfPassword.Data,
                                       sizeof(userObject->NtOwfPassword.Data));
            BAIL_ON_NT_STATUS(ntStatus);

            userObject->NtOwfPassword.IsPresent = TRUE;

            updateFields |= SAMDB_FIELD_USER_NT_OWF_PASSWORD;
        }
        break;

    default:
        ntStatus = STATUS_INVALID_INFO_CLASS;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    //
    // Attempt to update user account only if there's anything left
    // to update (there could have only been a rename to do)
    //
    if (updateFields)
    {
        dwError = SamDbUpdateObject(
                           &updateObject,
                           updateFields);
        BAIL_ON_LSA_ERROR(dwError);
    }

error:
    LW_SECURE_FREE_WSTRING(pwszPassword);

    // Release the object but not the SID because the pointer
    // belongs to account context
    updateObject.Sid = NULL;
    SamDbReleaseObject(&updateObject);

    if (ntStatus == STATUS_SUCCESS &&
        dwError != ERROR_SUCCESS)
    {
        ntStatus = LwWin32ErrorToNtStatus(dwError);
    }

    return ntStatus;
}


static
NTSTATUS
SamrSrvCheckPasswordPolicy(
    IN  PACCOUNT_CONTEXT pAcctCtx,
    IN  PWSTR            pwszPassword
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    DWORD dwError = ERROR_SUCCESS;
    PDOMAIN_CONTEXT pDomCtx = NULL;
    PCONNECT_CONTEXT pConnCtx = NULL;
    SAMDB_QUERY query = {0};
    PSAMDB_OBJECT_INFO* userObjects = NULL;
    PSAMDB_OBJECT_INFO userObject = NULL;
    LONG64 llLastPasswordChange = 0;
    LONG64 llCurrentTime = 0;
    size_t sPasswordLen = 0;

    pDomCtx  = pAcctCtx->pDomCtx;
    pConnCtx = pDomCtx->pConnCtx;

    query.Sid = pAcctCtx->pSid;

    dwError = SamDbFindObjects(pAcctCtx->ObjectType,
                               SAMDB_QUERY_TYPE_SID,
                               1,
                               &query,
                               &userObjects);
    BAIL_ON_LSA_ERROR(dwError);

    userObject = userObjects[0];
    if (userObject == NULL)
    {
        ntStatus = STATUS_INVALID_HANDLE;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    llLastPasswordChange = userObject->Info.User.PasswordLastSet;

    dwError = LwGetNtTime((PULONG64)&llCurrentTime);
    BAIL_ON_LSA_ERROR(dwError);

    /*
     * Check if password can be change give its age
     */
    if (llCurrentTime - llLastPasswordChange < pDomCtx->ntMinPasswordAge)
    {
        ntStatus = STATUS_PASSWORD_RESTRICTION;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    dwError = LwWc16sLen(pwszPassword, &sPasswordLen);
    BAIL_ON_LSA_ERROR(dwError);

    /*
     * Check if password is longer than required minimum
     */
    if (sPasswordLen < pDomCtx->dwMinPasswordLen)
    {
        ntStatus = STATUS_PASSWORD_RESTRICTION;
        BAIL_ON_NT_STATUS(ntStatus);
    }

error:
    if (userObjects)
    {
        SamDbFreeObjects(1, userObjects);
    }

    if (ntStatus == STATUS_SUCCESS &&
        dwError != ERROR_SUCCESS)
    {
        ntStatus = LwWin32ErrorToNtStatus(dwError);
    }

    return ntStatus;
}


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