//+-----------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (c) Microsoft Corporation 1992 - 1999
//
// File:        changepw.cxx
//
// Contents:    Code for KerbSetPassword and KerbChangePassword
//
//
// History:     24-May-1999     MikeSw          Created
//
//------------------------------------------------------------------------

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <sspi.h>
#include <ntsecapi.h>
#include <align.h>
#include <dsgetdc.h>
#include <kerbcli.h>




//+-------------------------------------------------------------------------
//
//  Function:   KerbChangePasswordUserEx
//
//  Synopsis:   Changes a users password. If the user is logged on,
//              it also updates the in-memory password.
//
//  Effects:
//
//  Arguments:
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------


NTSTATUS
KerbChangePasswordUser(
    IN LPWSTR DomainName,
    IN LPWSTR UserName,
    IN LPWSTR OldPassword,
    IN LPWSTR NewPassword
    )
{
    NTSTATUS Status;
    BOOLEAN WasEnabled;
    STRING Name;
    ULONG Dummy;
    HANDLE LogonHandle = NULL;
    ULONG PackageId;
    PVOID Response = NULL ;
    ULONG ResponseSize;
    NTSTATUS SubStatus;
    PKERB_CHANGEPASSWORD_REQUEST ChangeRequest = NULL;
    ULONG ChangeSize;
    UNICODE_STRING User,Domain,OldPass,NewPass;

    Status = LsaConnectUntrusted(
                &LogonHandle
                );

    if (!NT_SUCCESS(Status))
    {
        goto Cleanup;
    }

    RtlInitString(
        &Name,
        MICROSOFT_KERBEROS_NAME_A
        );

    Status = LsaLookupAuthenticationPackage(
                LogonHandle,
                &Name,
                &PackageId
                );
    if (!NT_SUCCESS(Status))
    {
        goto Cleanup;
    }

    RtlInitUnicodeString(
        &User,
        UserName
        );
    RtlInitUnicodeString(
        &Domain,
        DomainName
        );
    RtlInitUnicodeString(
        &OldPass,
        OldPassword
        );
    RtlInitUnicodeString(
        &NewPass,
        NewPassword
        );

    if ( OldPass.Length > (127*sizeof(WCHAR)) ||
         NewPass.Length > (127*sizeof(WCHAR)) )
    {
        Status = STATUS_NAME_TOO_LONG;
        goto Cleanup;
    }

    ChangeSize = ROUND_UP_COUNT(sizeof(KERB_CHANGEPASSWORD_REQUEST),4)+
                                    User.Length +
                                    Domain.Length +
                                    OldPass.Length +
                                    NewPass.Length ;
    ChangeRequest = (PKERB_CHANGEPASSWORD_REQUEST) LocalAlloc(LMEM_ZEROINIT, ChangeSize );
    if (NULL == ChangeRequest)
    {
        goto Cleanup;
    }

    ChangeRequest->MessageType = KerbChangePasswordMessage;

    ChangeRequest->AccountName = User;
    ChangeRequest->AccountName.Buffer = (LPWSTR) ROUND_UP_POINTER(sizeof(KERB_CHANGEPASSWORD_REQUEST) + (PBYTE) ChangeRequest,4);

    RtlCopyMemory(
        ChangeRequest->AccountName.Buffer,
        User.Buffer,
        User.Length
        );

    ChangeRequest->DomainName = Domain;
    ChangeRequest->DomainName.Buffer = ChangeRequest->AccountName.Buffer + ChangeRequest->AccountName.Length / sizeof(WCHAR);

    RtlCopyMemory(
        ChangeRequest->DomainName.Buffer,
        Domain.Buffer,
        Domain.Length
        );

    ChangeRequest->OldPassword = OldPass;
    ChangeRequest->OldPassword.Buffer = ChangeRequest->DomainName.Buffer + ChangeRequest->DomainName.Length / sizeof(WCHAR);

    RtlCopyMemory(
        ChangeRequest->OldPassword.Buffer,
        OldPass.Buffer,
        OldPass.Length
        );

    ChangeRequest->NewPassword = NewPass;
    ChangeRequest->NewPassword.Buffer = ChangeRequest->OldPassword.Buffer + ChangeRequest->OldPassword.Length / sizeof(WCHAR);

    RtlCopyMemory(
        ChangeRequest->NewPassword.Buffer,
        NewPass.Buffer,
        NewPass.Length
        );


    //
    // We are running as the caller, so state we are impersonating
    //

    ChangeRequest->Impersonating = TRUE;

    Status = LsaCallAuthenticationPackage(
                LogonHandle,
                PackageId,
                ChangeRequest,
                ChangeSize,
                &Response,
                &ResponseSize,
                &SubStatus
                );
    if (!NT_SUCCESS(Status) || !NT_SUCCESS(SubStatus))
    {
        if (NT_SUCCESS(Status))
        {
            Status = SubStatus;
        }
        goto Cleanup;
    }

Cleanup:

    if (LogonHandle != NULL)
    {
        LsaDeregisterLogonProcess(LogonHandle);
    }

    if (Response != NULL)
    {
        LsaFreeReturnBuffer(Response);
    }

    if (ChangeRequest != NULL)
    {
        LocalFree(ChangeRequest);
    }
    return(Status);
}


//+-------------------------------------------------------------------------
//
//  Function:   KerbSetPasswordUserEx
//
//  Synopsis:   Sets a password for a user in the specified domain
//
//  Effects:
//
//  Arguments:
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------


NTSTATUS
KerbSetPasswordUserEx(
    IN LPWSTR DomainName,
    IN LPWSTR UserName,
    IN LPWSTR NewPassword,
    IN OPTIONAL PCredHandle CredentialsHandle,
    IN OPTIONAL LPWSTR  KdcAddress
    )
{
    NTSTATUS Status;
    BOOLEAN WasEnabled;
    STRING Name;
    ULONG Dummy;
    HANDLE LogonHandle = NULL;
    ULONG PackageId;
    PVOID Response = NULL;
    ULONG ResponseSize;
    KERB_PROTOCOL_MESSAGE_TYPE MessageType = KerbSetPasswordMessage;
    NTSTATUS SubStatus;
    PKERB_SETPASSWORD_EX_REQUEST SetRequest = NULL;
    ULONG ChangeSize;
    UNICODE_STRING User,Domain,OldPass,NewPass, KdcAddr, ClientName, ClientRealm;
    
    // If you supply a KdcAddress, you must supply name type
    if (ARGUMENT_PRESENT(KdcAddress))
    {
       MessageType = KerbSetPasswordExMessage;
       
       RtlInitUnicodeString(
          &KdcAddr,
          KdcAddress
          );  
    } 
    else                                     
    {         
       RtlInitUnicodeString(
          &KdcAddr,
          NULL
          );  
    }
    
    Status = LsaConnectUntrusted(
                &LogonHandle
                );      

    if (!NT_SUCCESS(Status))
    {
        goto Cleanup;
    }
    
    RtlInitString(
        &Name,
        MICROSOFT_KERBEROS_NAME_A
        );
    Status = LsaLookupAuthenticationPackage(
                LogonHandle,
                &Name,
                &PackageId
                );
    if (!NT_SUCCESS(Status))
    {
        goto Cleanup;
    }

    RtlInitUnicodeString(
        &User,
        UserName
        );
    RtlInitUnicodeString(
        &Domain,
        DomainName
        );
    RtlInitUnicodeString(
        &NewPass,
        NewPassword
        );

    // These aren't used here (yet)
    RtlInitUnicodeString(
        &ClientName,
        NULL
        );
    
    RtlInitUnicodeString(
        &ClientRealm,
        NULL
        );          

    if ( NewPass.Length > (127 * sizeof(WCHAR)) )
    {
        Status = STATUS_NAME_TOO_LONG;
        goto Cleanup;
    }

    ChangeSize = ROUND_UP_COUNT(sizeof(KERB_SETPASSWORD_EX_REQUEST),4)+
                                    User.Length +
                                    Domain.Length +
                                    NewPass.Length +
                                    KdcAddr.Length +
                                    ClientName.Length +
                                    ClientRealm.Length;

    SetRequest = (PKERB_SETPASSWORD_EX_REQUEST) LocalAlloc(LMEM_ZEROINIT, ChangeSize );
    if (NULL == SetRequest)
    {
        goto Cleanup;
    }

    SetRequest->MessageType = MessageType;
    SetRequest->KdcAddressType = DS_UNKNOWN_ADDRESS_TYPE;
    SetRequest->AccountRealm = Domain;
    SetRequest->AccountRealm.Buffer = (LPWSTR) ROUND_UP_POINTER(sizeof(KERB_SETPASSWORD_EX_REQUEST) + (PBYTE) SetRequest,4);

    RtlCopyMemory(
        SetRequest->AccountRealm.Buffer,
        Domain.Buffer,
        Domain.Length
        );

    SetRequest->AccountName = User;
    SetRequest->AccountName.Buffer = SetRequest->AccountRealm.Buffer + SetRequest->AccountRealm.Length / sizeof(WCHAR);

    RtlCopyMemory(
        SetRequest->AccountName.Buffer,
        User.Buffer,
        User.Length
        );


    SetRequest->Password = NewPass;
    SetRequest->Password.Buffer = SetRequest->AccountName.Buffer + SetRequest->AccountName.Length / sizeof(WCHAR);

    RtlCopyMemory(
        SetRequest->Password.Buffer,
        NewPass.Buffer,
        NewPass.Length
        );
    
    // Not yet implemented
    SetRequest->ClientRealm = ClientRealm;
    SetRequest->ClientRealm.Buffer = SetRequest->Password.Buffer + SetRequest->Password.Length / sizeof(WCHAR);
         
    RtlCopyMemory(
         SetRequest->ClientRealm.Buffer,
         ClientRealm.Buffer,
         ClientRealm.Length   
         );                  
                           
    SetRequest->ClientName  = ClientName;
    SetRequest->ClientName.Buffer = SetRequest->ClientRealm.Buffer + SetRequest->ClientRealm.Length / sizeof(WCHAR);
                                    
    RtlCopyMemory(                    
        SetRequest->ClientName.Buffer,    
        ClientName.Buffer,                      
        ClientName.Length                          
        );
    //      

    SetRequest->KdcAddress = KdcAddr;
    SetRequest->KdcAddress.Buffer = SetRequest->ClientRealm.Buffer + SetRequest->ClientRealm.Length / sizeof(WCHAR);

    RtlCopyMemory(
       SetRequest->KdcAddress.Buffer,
       KdcAddr.Buffer,
       KdcAddr.Length
       );

    if (ARGUMENT_PRESENT(CredentialsHandle))
    {
        SetRequest->CredentialsHandle = *CredentialsHandle;
        SetRequest->Flags |= KERB_SETPASS_USE_CREDHANDLE;
    }

    Status = LsaCallAuthenticationPackage(
                LogonHandle,
                PackageId,
                SetRequest,
                ChangeSize,
                &Response,
                &ResponseSize,
                &SubStatus
                );
    if (!NT_SUCCESS(Status) || !NT_SUCCESS(SubStatus))
    {
        if (NT_SUCCESS(Status))
        {
            Status = SubStatus;
        }
        goto Cleanup;
    }

Cleanup:

    if (LogonHandle != NULL)
    {
        LsaDeregisterLogonProcess(LogonHandle);
    }                  

    if (Response != NULL)
    {
        LsaFreeReturnBuffer(Response);
    }

    if (SetRequest != NULL)
    {
        LocalFree(SetRequest);
    }
    return(Status);
}

//+-------------------------------------------------------------------------
//
//  Function:   KerbSetPasswordUser
//
//  Synopsis:   Sets a password for a user in the specified domain
//
//  Effects:
//
//  Arguments:
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
KerbSetPasswordUser(
    IN LPWSTR DomainName,
    IN LPWSTR UserName,
    IN LPWSTR NewPassword,
    IN OPTIONAL PCredHandle CredentialsHandle
    )
{
    
   return(KerbSetPasswordUserEx(
                  DomainName,
                  UserName,
                  NewPassword,
                  CredentialsHandle,
                  NULL
                  ));
}