/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    rpcapi.c

Abstract:

    This module contains the routines for the dssetup APIs that use RPC.  The
    routines in this module are merely wrappers that work as follows:

    Fuke copied lsa\uclient and hacked on to make it work for DsRole apis

Author:

    Mac McLain     (MacM)    April 14, 1997

Revision History:

--*/

#include <lsacomp.h>
#include "dssetup_c.h"
#include <rpcndr.h>
#include <dssetp.h>
#include <winreg.h>
#include <rpcasync.h>

#include <wxlpc.h>
#include <crypt.h>
#include <rc4.h>
#include <md5.h>
#include <winbase.h> //for RtlSecureZeroMemory

//
// Local prototypes
//
DWORD
DsRolepGetPrimaryDomainInformationDownlevel(
    IN LPWSTR Server,
    OUT PBYTE *Buffer
    );

DWORD
DsRolepGetProductTypeForServer(
    IN LPWSTR Server,
    IN OUT PNT_PRODUCT_TYPE ProductType
    );


DWORD
DsRolepEncryptPasswordStart(
    IN LPCWSTR ServerName OPTIONAL,
    IN LPCWSTR *Passwords,
    IN ULONG   Count,
    OUT RPC_BINDING_HANDLE *RpcBindingHandle,
    OUT HANDLE *RedirHandle,
    OUT OPTIONAL PUSER_SESSION_KEY UserSessionKey,
    IN OUT PDSROLEPR_ENCRYPTED_USER_PASSWORD *EncryptedUserPassword
    );

VOID
DsRolepEncryptPasswordEnd(
    IN RPC_BINDING_HANDLE RpcBindingHandle,
    IN HANDLE RedirHandle OPTIONAL,
    IN PDSROLEPR_ENCRYPTED_USER_PASSWORD *EncryptedUserPassword OPTIONAL,
    IN ULONG Count
    );

DWORD
DsRolepHashkey(
    IN OUT LPWSTR key,
    OUT PUNICODE_STRING Hash
    );

DWORD
DsRolepEncryptHash(
    IN PUSER_SESSION_KEY UserSessionKey,
    IN OUT PUNICODE_STRING Syskey,
    OUT PDSROLEPR_ENCRYPTED_HASH EncryptedSyskey
    );

////////////////////////////////////////////////////////////////////////////
//                                                                        //
// DS Setup and initialization routines                                   //
//                                                                        //
////////////////////////////////////////////////////////////////////////////

BOOLEAN
DsRolepIsSetupRunning(
    VOID
    )
/*++

Routine Description:

    This routine determines if this call is being made during setup or not

Arguments:

    VOID

Return Value:

    TRUE -- The call is being made during setup

    FALSE -- The call is not being made during setup

--*/
{
    NTSTATUS Status;
    HANDLE InstallationEvent;
    OBJECT_ATTRIBUTES EventAttributes;
    UNICODE_STRING EventName;
    BOOLEAN Setup = FALSE;

    //
    // If the following event exists, then we are in setup mode
    //
    RtlInitUnicodeString( &EventName,
                          L"\\INSTALLATION_SECURITY_HOLD" );
    InitializeObjectAttributes( &EventAttributes,
                                &EventName, 0, 0, NULL );

    Status = NtOpenEvent( &InstallationEvent,
                          SYNCHRONIZE,
                          &EventAttributes );

    if ( NT_SUCCESS( Status) ) {

        NtClose( InstallationEvent );
        Setup = TRUE;

    }

    return( Setup );
}


DWORD
DsRolepGetPrimaryDomainInfoServerBind(
    IN OPTIONAL PDSROLE_SERVER_NAME   ServerName,
    OUT handle_t *BindHandle
    )
/*++

Routine Description:

    This routine is called from the LSA client stubs when
    it is necessary to bind to the LSA on some server.

Arguments:

    ServerName - A pointer to a string containing the name of the server
        to bind with.

Return Value:

    The binding handle is returned to the stub routine.  If the
    binding is unsuccessful, a NULL will be returned.

--*/
{
    handle_t    BindingHandle = NULL;
    NTSTATUS Status;
    
    //
    // Can't go remote when running in setup mode
    //
    if ( DsRolepIsSetupRunning() && ServerName != NULL ) {

        return( STATUS_INVALID_SERVER_STATE );
    }

    Status = RpcpBindRpc ( ServerName,
                           L"lsarpc",
                           0,
                           &BindingHandle );

    if (!NT_SUCCESS(Status)) {

        DbgPrint("DsRolepGetPrimaryDomainInfoServerBind: RpcpBindRpc failed 0x%lx\n", Status);

    } else {

        *BindHandle = BindingHandle;
    }

    return RtlNtStatusToDosError( Status );

}


DWORD
DsRolepServerBind(
    IN OPTIONAL PDSROLE_SERVER_NAME   ServerName,
    OUT handle_t *BindHandle
    )
/*++

Routine Description:

    This routine is called from the LSA client stubs when
    it is necessary to bind to the LSA on some server.

Arguments:

    ServerName - A pointer to a string containing the name of the server
        to bind with.

Return Value:

    The binding handle is returned to the stub routine.  If the
    binding is unsuccessful, a NULL will be returned.

--*/
{
    handle_t    BindingHandle      = NULL;
    DWORD       Win32Err           = ERROR_SUCCESS;
    WCHAR       *pwszStringBinding = NULL;
    BOOL        fAuth              = TRUE;

    //
    // Can't go remote when running in setup mode
    //
    if ( DsRolepIsSetupRunning() && ServerName != NULL ) {

        return( STATUS_INVALID_SERVER_STATE );
    }

    RpcTryExcept
    {
        Win32Err = RpcStringBindingComposeW(NULL,
                                            L"ncalrpc",
                                            NULL,
                                            L"dsrole",
                                            NULL,
                                            &pwszStringBinding);

        if (RPC_S_OK != Win32Err) {
            __leave;
        }

        Win32Err = RpcBindingFromStringBindingW(pwszStringBinding, 
                                                &BindingHandle);
        if (RPC_S_OK != Win32Err) {
            __leave;
        }


        Win32Err = RpcEpResolveBinding(BindingHandle, 
                                       dsrole_ClientIfHandle);
        
        if ( RPC_S_OK != Win32Err ) {
            __leave;
        }

        // Set authentication info using our process's credentials.

        Win32Err = RpcBindingSetAuthInfo(BindingHandle,
                                         NULL,
                                         /*Uses the default authentication 
                                         level for the specified 
                                         authentication service.*/
                                         RPC_C_AUTHN_LEVEL_DEFAULT,
                                         /*RPC_C_AUTHN_DEFAULT is specified, 
                                         the RPC run-time library uses 
                                         the RPC_C_AUTHN_WINNT authentication 
                                         service for remote procedure calls 
                                         made using hBinding */
                                         RPC_C_AUTHN_DEFAULT,
                                         NULL,
                                         //Default authentication service.
                                         RPC_C_AUTHN_DEFAULT);
        if (RPC_S_OK != Win32Err) {
            __leave;
        }
    
        
    }
    RpcExcept( I_RpcExceptionFilter( RpcExceptionCode() ) )
    {
        Win32Err = RpcExceptionCode();        
    }
    RpcEndExcept;

    if ( RPC_S_OK == Win32Err ) {

        *BindHandle = BindingHandle;

    } else {

        if (BindingHandle) {

            RpcpUnbindRpc ( BindingHandle );

        }

    }

    if ( pwszStringBinding )
    {
        RpcStringFree(&pwszStringBinding);
    }

    return( Win32Err );

}



VOID
DsRolepServerUnbind (
    IN OPTIONAL PDSROLE_SERVER_NAME  ServerName,
    IN handle_t           BindingHandle
    )

/*++

Routine Description:

    This routine is called from the LSA client stubs when
    it is necessary to unbind from the LSA server.


Arguments:

    ServerName - This is the name of the server from which to unbind.

    BindingHandle - This is the binding handle that is to be closed.

Return Value:

    none.

--*/
{
    UNREFERENCED_PARAMETER( ServerName );     // This parameter is not used

    RpcpUnbindRpc ( BindingHandle );
    return;
}



DWORD
DsRolepApiReturnResult(
    ULONG ExceptionCode
    )

/*++

Routine Description:

    This function converts an exception code or status value returned
    from the client stub to a value suitable for return by the API to
    the client.

Arguments:

    ExceptionCode - The exception code to be converted.

Return Value:

    DWORD - The converted Nt Status code.

--*/

{
    //
    // Return the actual value if compatible with Nt status codes,
    // otherwise, return STATUS_UNSUCCESSFUL.
    //
    NTSTATUS Status;
    DWORD Results;

    if ( !NT_SUCCESS( ( NTSTATUS )ExceptionCode ) ) {

        Results = RtlNtStatusToDosError( ( NTSTATUS )ExceptionCode );

    } else {

        Results = ExceptionCode;
    }

    return( Results );
}



VOID
WINAPI
DsRoleFreeMemory(
    IN PVOID    Buffer
    )
/*++

Routine Description:


    Some setup services that return a potentially large amount of memory,
    such as an enumeration might, allocate the buffer in which the data
    is returned.  This function is used to free those buffers when they
    are no longer needed.

Parameters:

    Buffer - Pointer to the buffer to be freed.  This buffer must
        have been allocated by a previous dssetup service call.

Return Values:

    STATUS_SUCCESS - normal, successful completion.

--*/
{
    MIDL_user_free( Buffer );
}



DWORD
WINAPI
DsRoleDnsNameToFlatName(
    IN  LPCWSTR lpServer OPTIONAL,
    IN  LPCWSTR lpDnsName,
    OUT LPWSTR *lpFlatName,
    OUT PULONG  lpStatusFlag
    )
/*++

Routine Description:

    This routine will get the default NetBIOS (or flat) domain name for the given Dns domain name

Arguments:

    lpServer - Server on which to remote the call (NULL is local)

    lpDnsName - Dns domain name to generate the flat name for

    lpFlatName - Where the flat name is returned. Must be freed via MIDL_user_free
        (or DsRoleFreeMemory)

    lpStatusFlag - Flags that indicate information about the returned name.  Valid flags are:
        DSROLE_FLATNAME_DEFAULT -- This is the default NetBIOS name for this dns domain name
        DSROLE_FLATNAME_UPGRADE -- This is the name that is current in use by this machine as
            a flat name and cannot be changed.

Return Values:

    ERROR_SUCCESS - Success

--*/
{
    DWORD Win32Err = ERROR_SUCCESS;
    handle_t Handle = NULL;

    Win32Err = DsRolepServerBind( (PDSROLE_SERVER_NAME)lpServer,
                                  &Handle );

    if ( Win32Err != ERROR_SUCCESS ) {

        return( Win32Err );
    }

    RpcTryExcept {

        *lpFlatName = NULL;
        *lpStatusFlag = 0;

        Win32Err = DsRolerDnsNameToFlatName(
                         Handle,
                         ( LPWSTR )lpDnsName,
                         lpFlatName,
                         lpStatusFlag );

    } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {

        Win32Err = DsRolepApiReturnResult( RpcExceptionCode( ) );

    } RpcEndExcept;


    DsRolepServerUnbind( (PDSROLE_SERVER_NAME)lpServer, Handle );

    return( Win32Err );
}



DWORD
WINAPI
DsRoleDcAsDc(
    IN  LPCWSTR lpServer OPTIONAL,
    IN  LPCWSTR lpDnsDomainName,
    IN  LPCWSTR lpFlatDomainName,
    IN  LPCWSTR lpDomainAdminPassword OPTIONAL,
    IN  LPCWSTR lpSiteName OPTIONAL,
    IN  LPCWSTR lpDsDatabasePath,
    IN  LPCWSTR lpDsLogPath,
    IN  LPCWSTR lpSystemVolumeRootPath,
    IN  LPCWSTR lpParentDnsDomainName OPTIONAL,
    IN  LPCWSTR lpParentServer OPTIONAL,
    IN  LPCWSTR lpAccount OPTIONAL,
    IN  LPCWSTR lpPassword OPTIONAL,
    IN  LPCWSTR lpDsRepairPassword OPTIONAL,
    IN  ULONG Options,
    OUT DSROLE_SERVEROP_HANDLE *DsOperationHandle
    )
/*++

Routine Description:

    This routine will get the promote a server to be a DC in a domain

Arguments:

    lpServer - Server on which to remote the call (NULL is local)

    lpDnsDomainName - Dns domain name of the domain to install

    lpFlatDomainName - NetBIOS domain name of the domain to install

    lpDomainAdminPassword - Password to set on the administrator account if it is a new install

    SiteName - Name of the site this DC should belong to

    lpDsDatabasePath - Absolute path on the local machine where the Ds DIT should go

    lpDsLogPath - Absolute path on the local machine where the Ds log files should go

    lpSystemVolumeRootPath - Absolute path on the local machine which will be the root of
        the system volume.

    lpParentDnsDomainName - Optional.  If present, set this domain up as a child of the
        specified domain

    lpParentServer - Optional.  If present, use this server in the parent domain to replicate
        the required information from

    lpAccount - User account to use when setting up as a child domain

    lpPassword - Password to use with the above account
    
    lpDsRepairPassword - Password to use for the admin account of the repair mode

    Options - Options to control the creation of the domain

    DsOperationHandle - Handle to the operation is returned here.

Return Values:

    ERROR_SUCCESS - Success

--*/
{
    DWORD Win32Err = ERROR_SUCCESS;
    handle_t Handle = NULL;
    HANDLE RedirHandle = NULL;

#define DSROLEP_DC_AS_DC_DA_PWD_INDEX        0
#define DSROLEP_DC_AS_DC_PWD_INDEX           1
#define DSROLEP_DC_AS_DC_DS_REPAIR_PWD_INDEX 2
#define DSROLEP_DC_AS_DC_MAX_PWD_COUNT       3

    PDSROLEPR_ENCRYPTED_USER_PASSWORD EncryptedPasswords[DSROLEP_DC_AS_DC_MAX_PWD_COUNT];
    LPCWSTR Passwords[DSROLEP_DC_AS_DC_MAX_PWD_COUNT];

    Passwords[DSROLEP_DC_AS_DC_DA_PWD_INDEX]        = lpDomainAdminPassword;
    Passwords[DSROLEP_DC_AS_DC_PWD_INDEX]           = lpPassword;
    Passwords[DSROLEP_DC_AS_DC_DS_REPAIR_PWD_INDEX] = lpDsRepairPassword;
    
    RtlZeroMemory( EncryptedPasswords, sizeof(EncryptedPasswords) );

    Win32Err = DsRolepEncryptPasswordStart( lpServer,
                                            Passwords,
                                            NELEMENTS(Passwords),
                                            &Handle,
                                            &RedirHandle,
                                            NULL,
                                            EncryptedPasswords );

    if ( Win32Err != ERROR_SUCCESS ) {

        return( Win32Err );
    }


    RpcTryExcept {

        Win32Err = DsRolerDcAsDc( Handle,
                                  ( LPWSTR )lpDnsDomainName,
                                  ( LPWSTR )lpFlatDomainName,
                                  EncryptedPasswords[DSROLEP_DC_AS_DC_DA_PWD_INDEX],
                                  ( LPWSTR )lpSiteName,
                                  ( LPWSTR )lpDsDatabasePath,
                                  ( LPWSTR )lpDsLogPath,
                                  ( LPWSTR )lpSystemVolumeRootPath,
                                  ( LPWSTR )lpParentDnsDomainName,
                                  ( LPWSTR )lpParentServer,
                                  ( LPWSTR )lpAccount,
                                  EncryptedPasswords[DSROLEP_DC_AS_DC_PWD_INDEX],
                                  EncryptedPasswords[DSROLEP_DC_AS_DC_DS_REPAIR_PWD_INDEX],
                                  Options,
                                  ( PDSROLER_HANDLE )DsOperationHandle );

    } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {

        Win32Err = DsRolepApiReturnResult( RpcExceptionCode( ) );

    } RpcEndExcept;


    DsRolepEncryptPasswordEnd( Handle,
                               RedirHandle,
                               EncryptedPasswords,
                               NELEMENTS(EncryptedPasswords) );

    return( Win32Err );
}



DWORD
WINAPI
DsRoleDcAsReplica(
    IN  LPCWSTR lpServer OPTIONAL,
    IN  LPCWSTR lpDnsDomainName,
    IN  LPCWSTR lpReplicaServer,
    IN  LPCWSTR lpSiteName OPTIONAL,
    IN  LPCWSTR lpDsDatabasePath,
    IN  LPCWSTR lpDsLogPath,
    IN  LPCWSTR lpRestorePath OPTIONAL,   
    IN  LPCWSTR lpSystemVolumeRootPath,
    IN OUT LPWSTR lpBootkey OPTIONAL,
    IN  LPCWSTR lpAccount OPTIONAL,
    IN  LPCWSTR lpPassword OPTIONAL,
    IN  LPCWSTR lpDsRepairPassword OPTIONAL,
    IN  ULONG Options,
    OUT DSROLE_SERVEROP_HANDLE *DsOperationHandle
    )
/*++

Routine Description:

    This routine will install a server as a replica of an existing domain.

Arguments:

    lpServer - OPTIONAL. Server to remote the call to.

    lpDnsDomainName - Dns domain name of the domain to install into

    lpReplicaServer -  The name of a Dc within the existing domain, against which to replicate

    lpSiteName - Name of the site this DC should belong to

    lpDsDatabasePath - Absolute path on the local machine where the Ds DIT should go

    lpDsLogPath - Absolute path on the local machine where the Ds log files should go
    
    lpRestorepath - This is the path to a restored directory.

    lpSystemVolumeRootPath - Absolute path on the local machine which will be the root of
        the system volume.

    lpAccount - User account to use when setting up as a child domain

    lpPassword - Password to use with the above account
    
    lpDsRepairPassword - Password to use for the admin account of the repair mode

    Options - Options to control the creation of the domain

    DsOperationHandle - Handle to the operation is returned here.

Return Values:

--*/
{

    DWORD Win32Err = ERROR_SUCCESS;
    NTSTATUS Status = STATUS_SUCCESS;
    handle_t Handle = NULL;
    HANDLE RedirHandle = NULL;
    USER_SESSION_KEY UserSessionKey;
    UNICODE_STRING ClearBootKeyHash;
    DSROLEPR_ENCRYPTED_HASH EncryptedBootKey;
    
#define DSROLEP_DC_AS_REPLICA_PWD_INDEX           0
#define DSROLEP_DC_AS_REPLICA_DS_REPAIR_PWD_INDEX 1
#define DSROLEP_DC_AS_REPLICA_MAX_PWD_COUNT       2

    PDSROLEPR_ENCRYPTED_USER_PASSWORD EncryptedPasswords[DSROLEP_DC_AS_REPLICA_MAX_PWD_COUNT];
    LPCWSTR Passwords[DSROLEP_DC_AS_REPLICA_MAX_PWD_COUNT];

    Passwords[DSROLEP_DC_AS_REPLICA_PWD_INDEX]           = lpPassword;
    Passwords[DSROLEP_DC_AS_REPLICA_DS_REPAIR_PWD_INDEX] = lpDsRepairPassword;
    RtlZeroMemory( EncryptedPasswords, sizeof(EncryptedPasswords) );

    Win32Err = DsRolepEncryptPasswordStart( lpServer,
                                            Passwords,
                                            NELEMENTS(Passwords),
                                            &Handle,
                                            &RedirHandle,
                                            &UserSessionKey,
                                            EncryptedPasswords );

    if ( Win32Err != ERROR_SUCCESS ) {

        return( Win32Err );
    }

    if(lpBootkey)
    {
        Win32Err = DsRolepHashkey(lpBootkey,
                                  &ClearBootKeyHash
                                  );
        if (Win32Err != ERROR_SUCCESS) {
            return Win32Err;
        }

        Win32Err = DsRolepEncryptHash(&UserSessionKey,
                                      &ClearBootKeyHash,
                                      &EncryptedBootKey);
        if (Win32Err != ERROR_SUCCESS) {
            return Win32Err;
        }
    } else {
        EncryptedBootKey.EncryptedHash.Buffer = NULL;
        EncryptedBootKey.EncryptedHash.Length = 0;
        EncryptedBootKey.EncryptedHash.MaximumLength = 0;
    }
                                                  
    RpcTryExcept {

            Win32Err = DsRolerDcAsReplica( Handle,
                                         ( LPWSTR )lpDnsDomainName,
                                         ( LPWSTR )lpReplicaServer,
                                         ( LPWSTR )lpSiteName,
                                         ( LPWSTR )lpDsDatabasePath,
                                         ( LPWSTR )lpDsLogPath,
                                         ( LPWSTR )lpRestorePath,
                                         ( LPWSTR )lpSystemVolumeRootPath,
                                         EncryptedBootKey.EncryptedHash.Buffer==NULL?
                                           NULL:&EncryptedBootKey,
                                         ( LPWSTR )lpAccount,
                                         EncryptedPasswords[DSROLEP_DC_AS_REPLICA_PWD_INDEX],
                                         EncryptedPasswords[DSROLEP_DC_AS_REPLICA_DS_REPAIR_PWD_INDEX],
                                         Options,
                                         ( PDSROLER_HANDLE )DsOperationHandle );

    } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {

        Win32Err = DsRolepApiReturnResult( RpcExceptionCode( ) );

    } RpcEndExcept;

    if(EncryptedBootKey.EncryptedHash.Buffer)
    {
        MIDL_user_free(EncryptedBootKey.EncryptedHash.Buffer);
        EncryptedBootKey.EncryptedHash.Length=0;
        EncryptedBootKey.EncryptedHash.MaximumLength=0;
    }



    DsRolepEncryptPasswordEnd( Handle,
                               RedirHandle,
                               EncryptedPasswords,
                               NELEMENTS(EncryptedPasswords) );

    return( Win32Err );
}



DWORD
WINAPI
DsRoleDemoteDc(
    IN  LPCWSTR lpServer OPTIONAL,
    IN  LPCWSTR lpDnsDomainName,
    IN  DSROLE_SERVEROP_DEMOTE_ROLE ServerRole,
    IN  LPCWSTR lpAccount OPTIONAL,
    IN  LPCWSTR lpPassword OPTIONAL,
    IN  ULONG Options,
    IN  BOOL fLastDcInDomain,
    IN  ULONG cRemoveNCs,
    IN  LPCWSTR *pszRemoveNCs OPTIONAL,
    IN  LPCWSTR lpAdminPassword OPTIONAL,
    OUT DSROLE_SERVEROP_HANDLE *DsOperationHandle
    )
/*++

Routine Description:

    This routine will demote an existing Dc to a standalone or member server.

Arguments:

    lpServer - Server to remote the call to

    lpDnsDomainName - Name of the domain on this machine to demote.  If NULL, demote all of the
        domains on this machine

    ServerRole - The new role this machine should take

    lpAccount - OPTIONAL User account to use when deleting the trusted domain object

    lpPassword - Password to use with the above account

    Options - Options to control the demotion of the domain

    fLastDcInDomain - If TRUE, this is the last dc in the domain

    cRemoveNCs - Count of string pointers in pszRemoveNCs

    pszRemoveNCs - Array of (cRemoveNCs) strings. Strings are DNs of NDNCs to be removed

    lpAdminPassword - New local addmin password

    DsOperationHandle - Handle to the operation is returned here.

Return Values:

    ERROR_SUCCESS - Success
--*/

{

    DWORD Win32Err = ERROR_SUCCESS;
    handle_t Handle = NULL;
    HANDLE RedirHandle = NULL;

#define DSROLEP_DEMOTE_PWD_INDEX          0
#define DSROLEP_DEMOTE_ADMIN_PWD_INDEX    1
#define DSROLEP_DEMOTE_MAX_PWD_COUNT      2

    PDSROLEPR_ENCRYPTED_USER_PASSWORD EncryptedPasswords[DSROLEP_DEMOTE_MAX_PWD_COUNT];
    LPCWSTR Passwords[DSROLEP_DEMOTE_MAX_PWD_COUNT];

    Passwords[DSROLEP_DEMOTE_PWD_INDEX]   = lpPassword;
    Passwords[DSROLEP_DEMOTE_ADMIN_PWD_INDEX] = lpAdminPassword;
    RtlZeroMemory( EncryptedPasswords, sizeof(EncryptedPasswords) );

    Win32Err = DsRolepEncryptPasswordStart( lpServer,
                                            Passwords,
                                            NELEMENTS(Passwords),
                                            &Handle,
                                            &RedirHandle,
                                            NULL,
                                            EncryptedPasswords );

    if ( Win32Err != ERROR_SUCCESS ) {

        return( Win32Err );
    }

    RpcTryExcept {

        Win32Err = DsRolerDemoteDc( Handle,
                                      ( LPWSTR )lpDnsDomainName,
                                      ServerRole,
                                      ( LPWSTR )lpAccount,
                                      EncryptedPasswords[DSROLEP_DEMOTE_PWD_INDEX],
                                      Options,
                                      fLastDcInDomain,
                                      cRemoveNCs,
                                      pszRemoveNCs,
                                      EncryptedPasswords[DSROLEP_DEMOTE_ADMIN_PWD_INDEX],
                                      ( PDSROLER_HANDLE )DsOperationHandle );


    } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {

        Win32Err = DsRolepApiReturnResult( RpcExceptionCode( ) );

    } RpcEndExcept;


    DsRolepEncryptPasswordEnd( Handle,
                               RedirHandle,
                               EncryptedPasswords,
                               NELEMENTS(EncryptedPasswords) );

    return( Win32Err );
}


DWORD
WINAPI
DsRoleGetDcOperationProgress(
    IN  LPCWSTR lpServer OPTIONAL,
    IN  DSROLE_SERVEROP_HANDLE DsOperationHandle,
    OUT PDSROLE_SERVEROP_STATUS *ServerOperationStatus
    )
/*++

Routine Description:

    Gets the progress of the currently running operation

Arguments:

    lpServer - Server to remote the call to

    DsOperationHandle - Handle of currently running operation.  Returned by one of the DsRoleDcAs
        apis

    ServerOperationStatus - Where the current operation status is returned.  Must be freed via
        MIDL_user_free (or DsRoleFreeMemory)

Return Values:

    ERROR_SUCCESS - Success

--*/
{
    DWORD Win32Err = ERROR_SUCCESS;
    PDSROLER_SERVEROP_STATUS ServerStatus = NULL;
    handle_t Handle = NULL;

    Win32Err = DsRolepServerBind( (PDSROLE_SERVER_NAME)lpServer,
                                  &Handle );

    if ( Win32Err != ERROR_SUCCESS ) {

        return( Win32Err );
    }

    RpcTryExcept {

        Win32Err = DsRolerGetDcOperationProgress(
                         Handle,
                         (PDSROLER_HANDLE)&DsOperationHandle,
                         &ServerStatus );

        if ( Win32Err == ERROR_SUCCESS || Win32Err == ERROR_IO_PENDING ) {

            *ServerOperationStatus = ( PDSROLE_SERVEROP_STATUS )ServerStatus;
        }

    } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {

        Win32Err = DsRolepApiReturnResult( RpcExceptionCode( ) );

    } RpcEndExcept;


    DsRolepServerUnbind( (PDSROLE_SERVER_NAME)lpServer, Handle );

    return( Win32Err );
}



DWORD
WINAPI
DsRoleGetDcOperationResults(
    IN  LPCWSTR lpServer OPTIONAL,
    IN  DSROLE_SERVEROP_HANDLE DsOperationHandle,
    OUT PDSROLE_SERVEROP_RESULTS *ServerOperationResults
    )
/*++

Routine Description:

    Gets the final results of an attempted promotion/demotion operation

Arguments:

    lpServer - Server to remote the call to

    DsOperationHandle - Handle of currently running operation.  Returned by one of the DsRoleDcAs
        apis

    ServerOperationResults - Where the current operation result is returned.  Must be freed via
        MIDL_user_free (or DsRoleFreeMemory)

Return Values:

    ERROR_SUCCESS - Success

--*/
{
    DWORD Win32Err = ERROR_SUCCESS;
    PDSROLER_SERVEROP_RESULTS ServerResults = NULL;
    handle_t Handle = NULL;

    Win32Err = DsRolepServerBind( (PDSROLE_SERVER_NAME)lpServer,
                                  &Handle );

    if ( Win32Err != ERROR_SUCCESS ) {

        return( Win32Err );
    }

    RpcTryExcept {

        *ServerOperationResults = 0;
        Win32Err = DsRolerGetDcOperationResults(
                         Handle,
                         (PDSROLER_HANDLE)&DsOperationHandle,
                         &ServerResults );

        if ( Win32Err == ERROR_SUCCESS ) {

            *ServerOperationResults = ( PDSROLE_SERVEROP_RESULTS )ServerResults;
        }

    } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {

        Win32Err = DsRolepApiReturnResult( RpcExceptionCode( ) );

    } RpcEndExcept;


    DsRolepServerUnbind( (PDSROLE_SERVER_NAME)lpServer, Handle );

    return( Win32Err );
}



DWORD
WINAPI
DsRoleGetPrimaryDomainInformation(
    IN LPCWSTR lpServer OPTIONAL,
    IN DSROLE_PRIMARY_DOMAIN_INFO_LEVEL InfoLevel,
    OUT PBYTE *Buffer )
/*++

Routine Description:

    Gets information on the machine

Arguments:

    lpServer - Server to remote the call to

    InfoLevel - What level of information is being requested.  Currently supported levels are:
        DsRolePrimaryDomainInfoBasic

    Buffer - Where the information is returned.  The returned pointer should be cast to the
        appropriate type for the info level passed in.  The returned buffer should be freed via
        MIDL_user_free (or DsRoleFreeMemory)


Return Values:

    ERROR_SUCCESS - Success

--*/
{
    DWORD Win32Err = ERROR_SUCCESS;
    PDSROLER_PRIMARY_DOMAIN_INFORMATION PrimaryDomainInfo = NULL;
    handle_t Handle = NULL;
    NTSTATUS Status = STATUS_SUCCESS;

    if ( Buffer == NULL ) {

        return( ERROR_INVALID_PARAMETER );
    }

    Win32Err = DsRolepGetPrimaryDomainInfoServerBind( (PDSROLE_SERVER_NAME)lpServer,
                                                      &Handle );

    if ( Win32Err != ERROR_SUCCESS ) {

        return( Win32Err );
    }

    if ( NULL == Handle) {

        return( ERROR_NOT_ENOUGH_MEMORY );
    }

    RpcTryExcept {

        *Buffer = NULL;
        Win32Err = DsRolerGetPrimaryDomainInformation(
                         Handle,
                         InfoLevel,
                         &PrimaryDomainInfo );

        *Buffer = ( PBYTE )PrimaryDomainInfo;

    } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {

        Status = I_RpcMapWin32Status( RpcExceptionCode() );
        Win32Err = DsRolepApiReturnResult( RpcExceptionCode( ) );

    } RpcEndExcept;

    
    DsRolepServerUnbind( (PDSROLE_SERVER_NAME)lpServer, Handle );
    
    //
    // If this fails because we are calling a downlevel server, cobble up the information here
    //
    if ( ( Status == RPC_NT_UNKNOWN_IF || Status == RPC_NT_PROCNUM_OUT_OF_RANGE ) &&
         InfoLevel == DsRolePrimaryDomainInfoBasic ) {

         Win32Err = DsRolepGetPrimaryDomainInformationDownlevel( ( LPWSTR )lpServer, Buffer );

    }




    return( Win32Err );
}



DWORD
WINAPI
DsRoleCancel(
    IN  LPCWSTR lpServer OPTIONAL,
    IN  DSROLE_SERVEROP_HANDLE DsOperationHandle
    )
/*++

Routine Description:

    Cancels a currently running operation

Arguments:

    lpServer - Server to remote the call to

    DsOperationHandle - Handle of currently running operation.  Returned by one of the DsRoleDcAs
        apis


Return Values:

    ERROR_SUCCESS - Success

--*/
{
    DWORD Win32Err = ERROR_SUCCESS;
    handle_t Handle = NULL;

    Win32Err = DsRolepServerBind( (PDSROLE_SERVER_NAME)lpServer,
                                  &Handle );

    if ( Win32Err != ERROR_SUCCESS ) {

        return( Win32Err );
    }

    RpcTryExcept {

        Win32Err = DsRolerCancel( Handle,
                                  ( PDSROLER_HANDLE )&DsOperationHandle );

    } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {

        Win32Err = DsRolepApiReturnResult( RpcExceptionCode( ) );

    } RpcEndExcept;


    DsRolepServerUnbind( (PDSROLE_SERVER_NAME)lpServer, Handle );

    return( Win32Err );
}


DWORD
WINAPI
DsRoleIfmHandleFree(
    IN  LPCWSTR lpServer OPTIONAL,
    IN  DSROLE_IFM_OPERATION_HANDLE * pIfmHandle
    )
/*++

Routine Description:

    Cancels a currently running operation

Arguments:

    lpServer - Server to remote the call to

    DsOperationHandle - Handle of currently running operation.  Returned by one of the DsRoleDcAs
        apis


Return Values:

    ERROR_SUCCESS - Success

--*/
{
    DWORD Win32Err = ERROR_SUCCESS;
    handle_t Handle = NULL;

    Win32Err = DsRolepServerBind( (PDSROLE_SERVER_NAME)lpServer,
                                  &Handle );

    if ( Win32Err != ERROR_SUCCESS ) {

        return( Win32Err );
    }

    RpcTryExcept {

        Win32Err = DsRolerIfmHandleFree( Handle,
                                         ( DSROLER_IFM_HANDLE ) pIfmHandle );

    } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {

        Win32Err = DsRolepApiReturnResult( RpcExceptionCode( ) );

    } RpcEndExcept;


    DsRolepServerUnbind( (PDSROLE_SERVER_NAME)lpServer, Handle );

    return( Win32Err );
}



DWORD
WINAPI
DsRoleServerSaveStateForUpgrade(
    IN LPCWSTR lpAnswerFile OPTIONAL
    )
/*++

Routine Description:

    This function is to be invoked during setup and saves the required server state to
    complete the promotion following the reboot.  Following the successful completion
    of this API call, the server will be demoted to a member server in the same domain.

Arguments:

    lpAnswerFile -- Optional path to an answer file to be used by DCPROMO when it is
        invoked to do the upgrade


Return Values:

    ERROR_SUCCESS - Success

--*/
{
    DWORD Win32Err = ERROR_SUCCESS;
    handle_t Handle = NULL;

    Win32Err = DsRolepServerBind( NULL,
                                  &Handle );

    if ( Win32Err != ERROR_SUCCESS ) {

        return( Win32Err );
    }

    RpcTryExcept {

        Win32Err = DsRolerServerSaveStateForUpgrade( Handle, ( LPWSTR )lpAnswerFile );

    } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {

        Win32Err = DsRolepApiReturnResult( RpcExceptionCode( ) );

    } RpcEndExcept;


    DsRolepServerUnbind( NULL, Handle );

    return( Win32Err );
}


DWORD
WINAPI
DsRoleUpgradeDownlevelServer(
    IN  LPCWSTR lpDnsDomainName,
    IN  LPCWSTR lpSiteName,
    IN  LPCWSTR lpDsDatabasePath,
    IN  LPCWSTR lpDsLogPath,
    IN  LPCWSTR lpSystemVolumeRootPath,
    IN  LPCWSTR lpParentDnsDomainName OPTIONAL,
    IN  LPCWSTR lpParentServer OPTIONAL,
    IN  LPCWSTR lpAccount OPTIONAL,
    IN  LPCWSTR lpPassword OPTIONAL,
    IN  LPCWSTR lpDsRepairPassword OPTIONAL,
    IN  ULONG Options,
    OUT DSROLE_SERVEROP_HANDLE *DsOperationHandle
    )
/*++

Routine Description:

    This routine process the information saved from a DsRoleServerSaveStateForUpgrade to
    promote a downlevel (NT4 or previous) server to an NT5 DC

Arguments:

    lpDnsDomainName - Dns domain name of the domain to install

    SiteName - Name of the site this DC should belong to

    lpDsDatabasePath - Absolute path on the local machine where the Ds DIT should go

    lpDsLogPath - Absolute path on the local machine where the Ds log files should go

    lpSystemVolumeRootPath - Absolute path on the local machine which will be the root of
        the system volume.

    lpParentDnsDomainName - Optional.  If present, set this domain up as a child of the
        specified domain

    lpParentServer - Optional.  If present, use this server in the parent domain to replicate
        the required information from

    lpAccount - User account to use when setting up as a child domain

    lpPassword - Password to use with the above account
    
    lpDsRepairPassword - Password to use for the admin account of the repair mode

    Options - Options to control the creation of the domain

    DsOperationHandle - Handle to the operation is returned here.


Return Values:

    ERROR_SUCCESS - Success

--*/
{

    DWORD Win32Err = ERROR_SUCCESS;
    handle_t Handle = NULL;
    HANDLE RedirHandle = NULL;

#define DSROLEP_UPGRADE_PWD_INDEX              0
#define DSROLEP_UPGRADE_DS_REPAIR_PWD_INDEX    1
#define DSROLEP_UPGRADE_MAX_PWD_COUNT          2

    PDSROLEPR_ENCRYPTED_USER_PASSWORD EncryptedPasswords[DSROLEP_UPGRADE_MAX_PWD_COUNT];
    LPCWSTR Passwords[DSROLEP_UPGRADE_MAX_PWD_COUNT];

    Passwords[DSROLEP_UPGRADE_PWD_INDEX]   = lpPassword;
    Passwords[DSROLEP_UPGRADE_DS_REPAIR_PWD_INDEX] = lpDsRepairPassword;
    RtlZeroMemory( EncryptedPasswords, sizeof(EncryptedPasswords) );

    Win32Err = DsRolepEncryptPasswordStart( NULL,
                                            Passwords,
                                            NELEMENTS(Passwords),
                                            &Handle,
                                            &RedirHandle,
                                            NULL,
                                            EncryptedPasswords );

    if ( Win32Err != ERROR_SUCCESS ) {

        return( Win32Err );
    }


    RpcTryExcept {

        Win32Err = DsRolerUpgradeDownlevelServer( Handle,
                                                  ( LPWSTR )lpDnsDomainName,
                                                  ( LPWSTR )lpSiteName,
                                                  ( LPWSTR )lpDsDatabasePath,
                                                  ( LPWSTR )lpDsLogPath,
                                                  ( LPWSTR )lpSystemVolumeRootPath,
                                                  ( LPWSTR )lpParentDnsDomainName,
                                                  ( LPWSTR )lpParentServer,
                                                  ( LPWSTR )lpAccount,
                                                  EncryptedPasswords[DSROLEP_UPGRADE_PWD_INDEX],
                                                  EncryptedPasswords[DSROLEP_UPGRADE_DS_REPAIR_PWD_INDEX],
                                                  Options,
                                                  ( PDSROLER_HANDLE )DsOperationHandle );
        
    } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {

        Win32Err = DsRolepApiReturnResult( RpcExceptionCode( ) );

    } RpcEndExcept;


    DsRolepEncryptPasswordEnd( Handle,
                               RedirHandle,
                               EncryptedPasswords,
                               NELEMENTS(EncryptedPasswords) );


    return( Win32Err );
}


DWORD
WINAPI
DsRoleAbortDownlevelServerUpgrade(
    IN LPCWSTR lpAdminPassword,
    IN LPCWSTR lpAccount, OPTIONAL
    IN LPCWSTR lpPassword, OPTIONAL
    IN ULONG Options
    )
/*++

Routine Description:

    This routine cleans up the information saved from a DsRoleSaveServerStateForUpgrade call,
    leaving the machine as a member or standalone server

Arguments:

    lpAdminPassword - New local administrator account password

    lpAccount - User account to use when setting up as a child domain

    lpPassword - Password to use with the above account


Return Values:

    ERROR_SUCCESS - Success

--*/
{

    DWORD Win32Err = ERROR_SUCCESS;
    handle_t Handle = NULL;
    HANDLE RedirHandle = NULL;

#define DSROLEP_ABORT_PWD_INDEX         0
#define DSROLEP_ABORT_ADMIN_PWD_INDEX   1
#define DSROLEP_ABORT_MAX_PWD_COUNT     2

    PDSROLEPR_ENCRYPTED_USER_PASSWORD EncryptedPasswords[DSROLEP_ABORT_MAX_PWD_COUNT];
    LPCWSTR Passwords[DSROLEP_ABORT_MAX_PWD_COUNT];

    Passwords[DSROLEP_ABORT_PWD_INDEX]   = lpPassword;
    Passwords[DSROLEP_ABORT_ADMIN_PWD_INDEX] = lpAdminPassword;
    RtlZeroMemory( EncryptedPasswords, sizeof(EncryptedPasswords) );

    Win32Err = DsRolepEncryptPasswordStart( NULL,
                                            Passwords,
                                            NELEMENTS(Passwords),
                                            &Handle,
                                            &RedirHandle,
                                            NULL,
                                            EncryptedPasswords );

    if ( Win32Err != ERROR_SUCCESS ) {

        return( Win32Err );
    }

    RpcTryExcept {

        Win32Err = DsRolerAbortDownlevelServerUpgrade( Handle,
                                                       ( LPWSTR )lpAccount,
                                                       EncryptedPasswords[DSROLEP_ABORT_PWD_INDEX],
                                                       EncryptedPasswords[DSROLEP_ABORT_ADMIN_PWD_INDEX],
                                                       Options );
    } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {

        Win32Err = DsRolepApiReturnResult( RpcExceptionCode( ) );

    } RpcEndExcept;


    DsRolepEncryptPasswordEnd( Handle,
                               RedirHandle,
                               EncryptedPasswords,
                               NELEMENTS(EncryptedPasswords) );


    return( Win32Err );
}

DWORD
WINAPI
DsRoleGetDatabaseFacts(
    IN  LPCWSTR lpServer OPTIONAL,
    IN  LPCWSTR lpRestorePath,
    OUT LPWSTR *lpDNSDomainName,
    OUT PULONG State,
    OUT DSROLE_IFM_OPERATION_HANDLE * pIfmHandle
    )
/*++

Routine Description:

    This function is the RPC procedure exposed to setup the server side
    IFM handle DsRolepCurrentIfmOperationHandle, which caches the information
    we'll need from the IFM system's registry.  This function also returns
    the relevant subset of this IFM system information to the caller (dcpromo).
    
    Note: We do this only once, because in the case that the IFM registry
    is in a non-writeable location (such as a CD), we will need to copy off
    the registry to a temporary location to use it.
    
    This function returns to the caller:
    1. the way the syskey is stored (State)
    2. the domain that the database came from (lpDNSDomainName)
    3. where the backup was taken from a GC or not (State)

Arguments:

    lpServer - The server to get the Facts from

    lpRestorePath - The location of the restored files.
    
    lpDNSDomainName - This parameter will recieve the name of the domain that this backup came
                      from

    State - The return Values that report How the syskey is stored and If the back was likely
              taken form a GC or not.

    pIfmHandle - Pointer to the IFM handle handed back.  This is primarily used
        to "free" the IFM System Info.


Return Values:

    Win32 Error

--*/
{
    DWORD Win32Err = ERROR_SUCCESS;
    handle_t Handle = NULL;

    if(lpDNSDomainName == NULL ||
       IsBadWritePtr(lpDNSDomainName, sizeof(LPWSTR)) ||
       State == NULL ||
       IsBadWritePtr(State, sizeof(DWORD)) ||
       pIfmHandle == NULL ||
       IsBadWritePtr(pIfmHandle, sizeof(void *)) ){
        return ERROR_INVALID_PARAMETER;
    }

    Win32Err = DsRolepServerBind( (PDSROLE_SERVER_NAME)lpServer,
                                  &Handle );

    if ( Win32Err != ERROR_SUCCESS ) {

        return( Win32Err );                     
    }
    
    RpcTryExcept {

        Win32Err = DsRolerGetDatabaseFacts( Handle,
                                          ( LPWSTR )lpRestorePath,
                                          ( LPWSTR * )lpDNSDomainName,
                                          ( PULONG )State,
                                          ( DSROLER_IFM_HANDLE * ) pIfmHandle );
    } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) {

        Win32Err = DsRolepApiReturnResult( RpcExceptionCode( ) );

    } RpcEndExcept;

    DsRolepServerUnbind( (PDSROLE_SERVER_NAME)lpServer, Handle );


    return Win32Err;
}



//
// Local functions
//
DWORD
DsRolepGetPrimaryDomainInformationDownlevel(
    IN LPWSTR Server,
    OUT PBYTE *Buffer
    )
{
    DWORD Win32Err = ERROR_SUCCESS;
    NTSTATUS Status;
    LSA_HANDLE PolicyHandle;
    PPOLICY_PRIMARY_DOMAIN_INFO PDI = NULL;
    PPOLICY_LSA_SERVER_ROLE_INFO ServerRole = NULL;
    PPOLICY_ACCOUNT_DOMAIN_INFO ADI = NULL;
    UNICODE_STRING UnicodeServer;
    OBJECT_ATTRIBUTES OA;
    PDSROLE_PRIMARY_DOMAIN_INFO_BASIC PDIB = NULL;
    DSROLE_MACHINE_ROLE MachineRole = DsRole_RoleStandaloneServer;
    NT_PRODUCT_TYPE ProductType;

    Win32Err = DsRolepGetProductTypeForServer( Server, &ProductType );

    if ( Win32Err != ERROR_SUCCESS ) {

        return( Win32Err );
    }


    InitializeObjectAttributes( &OA, NULL, 0, NULL, NULL);
    if ( Server ) {

        RtlInitUnicodeString( &UnicodeServer, Server );
    }

    Status = LsaOpenPolicy( Server ? &UnicodeServer : NULL,
                            &OA,
                            POLICY_VIEW_LOCAL_INFORMATION,
                            &PolicyHandle );

    if ( NT_SUCCESS( Status ) ) {

        Status = LsaQueryInformationPolicy( PolicyHandle,
                                            PolicyPrimaryDomainInformation,
                                            ( PVOID * ) &PDI );

        if ( NT_SUCCESS( Status ) ) {

            switch ( ProductType ) {
            case NtProductWinNt:
                if ( PDI->Sid == NULL ) {

                    MachineRole = DsRole_RoleStandaloneWorkstation;

                } else {

                    MachineRole = DsRole_RoleMemberWorkstation;

                }
                break;

            case NtProductServer:
                if ( PDI->Sid == NULL ) {

                    MachineRole = DsRole_RoleStandaloneServer;

                } else {

                    MachineRole = DsRole_RoleMemberServer;

                }
                break;

            case NtProductLanManNt:

                Status = LsaQueryInformationPolicy( PolicyHandle,
                                                    PolicyLsaServerRoleInformation,
                                                    ( PVOID * )&ServerRole );
                if ( NT_SUCCESS( Status ) ) {

                    if ( ServerRole->LsaServerRole == PolicyServerRolePrimary ) {

                        //
                        // If we think we're a primary domain controller, we'll need to
                        // guard against the case where we're actually standalone during setup
                        //
                        Status = LsaQueryInformationPolicy( PolicyHandle,
                                                            PolicyAccountDomainInformation,
                                                            ( PVOID * )&ADI );
                        if ( NT_SUCCESS( Status ) ) {


                            if ( PDI->Sid == NULL ||
                                 ADI->DomainSid == NULL ||
                                 !RtlEqualSid( ADI->DomainSid, PDI->Sid ) ) {

                                MachineRole = DsRole_RoleStandaloneServer;

                            } else {

                                MachineRole = DsRole_RolePrimaryDomainController;

                            }
                        }


                    } else {

                        MachineRole = DsRole_RoleBackupDomainController;
                    }
                }

                break;

            default:

                Status = STATUS_INVALID_PARAMETER;
                break;
            }

        }

        //
        // Build the return buffer
        //
        if ( NT_SUCCESS( Status ) ) {

            PDIB = MIDL_user_allocate( sizeof( DSROLE_PRIMARY_DOMAIN_INFO_BASIC ) +
                                       PDI->Name.Length + sizeof( WCHAR ) );

            if ( PDIB == NULL ) {

                Status = STATUS_INSUFFICIENT_RESOURCES;

            } else {

                RtlZeroMemory( PDIB, sizeof( DSROLE_PRIMARY_DOMAIN_INFO_BASIC ) +
                                       PDI->Name.Length + sizeof( WCHAR ) );

                PDIB->MachineRole = MachineRole;
                PDIB->DomainNameFlat = ( LPWSTR ) ( ( PBYTE )PDIB +
                                                sizeof( DSROLE_PRIMARY_DOMAIN_INFO_BASIC ) );
                RtlCopyMemory( PDIB->DomainNameFlat, PDI->Name.Buffer, PDI->Name.Length );

                *Buffer = ( PBYTE )PDIB;
            }
        }

        LsaClose( PolicyHandle );

        LsaFreeMemory( PDI );

        if ( ADI != NULL ) {

            LsaFreeMemory( ADI );
        }

        if ( ServerRole != NULL ) {

            LsaFreeMemory( ServerRole );
        }
    }

    Win32Err = RtlNtStatusToDosError( Status );

    return( Win32Err );
}


DWORD
DsRolepGetProductTypeForServer(
    IN LPWSTR Server,
    IN OUT PNT_PRODUCT_TYPE ProductType
    )
{
    DWORD Win32Err = ERROR_SUCCESS;
    PWSTR RegServer = NULL;
    HKEY RemoteKey, ProductKey;
    PBYTE Buffer = NULL;
    ULONG Type, Size = 0;



    if ( Server == NULL ) {

        if ( RtlGetNtProductType( ProductType ) == FALSE ) {

            Win32Err = RtlNtStatusToDosError( STATUS_UNSUCCESSFUL );

        }

    } else {

        if ( wcslen( Server ) > 2 && *Server == L'\\' && *( Server + 1 ) == L'\\' ) {

            RegServer = Server;

        } else {

            RegServer = LocalAlloc( LMEM_FIXED, ( wcslen( Server ) + 3 ) * sizeof( WCHAR ) );

            if ( RegServer ) {

                swprintf( RegServer, L"\\\\%ws", Server );

            } else {

                Win32Err = ERROR_NOT_ENOUGH_MEMORY;

            }
        }

        if ( Win32Err == ERROR_SUCCESS ) {

            Win32Err = RegConnectRegistry( RegServer,
                                           HKEY_LOCAL_MACHINE,
                                           &RemoteKey );

            if ( Win32Err == ERROR_SUCCESS ) {

                Win32Err = RegOpenKeyEx( RemoteKey,
                                         L"system\\currentcontrolset\\control\\productoptions",
                                         0,
                                         KEY_READ,
                                         &ProductKey );

                if ( Win32Err == ERROR_SUCCESS ) {

                    Win32Err = RegQueryValueEx( ProductKey,
                                                L"ProductType",
                                                0,
                                                &Type,
                                                0,
                                                &Size );

                    if ( Win32Err == ERROR_SUCCESS ) {

                        Buffer = LocalAlloc( LMEM_FIXED, Size );

                        if ( Buffer ) {

                            Win32Err = RegQueryValueEx( ProductKey,
                                                        L"ProductType",
                                                        0,
                                                        &Type,
                                                        Buffer,
                                                        &Size );

                            if ( Win32Err == ERROR_SUCCESS ) {

                                if ( !_wcsicmp( ( PWSTR )Buffer, L"LanmanNt" ) ) {

                                    *ProductType = NtProductLanManNt;

                                } else if ( !_wcsicmp( ( PWSTR )Buffer, L"ServerNt" ) ) {

                                    *ProductType = NtProductServer;

                                } else if ( !_wcsicmp( ( PWSTR )Buffer, L"WinNt" ) ) {

                                    *ProductType = NtProductWinNt;

                                } else {

                                    Win32Err = ERROR_UNKNOWN_PRODUCT;
                                }
                            }

                            LocalFree( Buffer );

                        } else {

                            Win32Err = ERROR_NOT_ENOUGH_MEMORY;
                        }
                    }

                    RegCloseKey( ProductKey );
                }


                RegCloseKey( RemoteKey );
            }

        }

        if ( RegServer != Server ) {

            LocalFree( RegServer );
        }
    }

    return( Win32Err );

}

NTSTATUS
DsRolepRandomFill(
    IN ULONG BufferSize,
    IN OUT PUCHAR Buffer
)
/*++

Routine Description:

    This routine fills a buffer with random data.

Parameters:

    BufferSize - Length of the input buffer, in bytes.

    Buffer - Input buffer to be filled with random data.

Return Values:

    Errors from NtQuerySystemTime()


--*/
{
    if( RtlGenRandom(Buffer, BufferSize) )
    {
        return STATUS_SUCCESS;
    }

    return STATUS_UNSUCCESSFUL;
}

DWORD
DsRolepEncryptPasswordStart(
    IN LPCWSTR ServerName OPTIONAL,
    IN LPCWSTR *Passwords,
    IN ULONG   Count,
    OUT RPC_BINDING_HANDLE *RpcBindingHandle,
    OUT HANDLE *RedirHandle,
    OUT OPTIONAL PUSER_SESSION_KEY pUserSessionKey,
    IN OUT PDSROLEPR_ENCRYPTED_USER_PASSWORD *EncryptedUserPasswords
    )
/*++

Routine Description:

    This routine takes a number of cleartext unicode NT password from the user,
    and encrypts them with the session key.
    
    This routine's algorithm was taken from CliffV's work when encrypting the
    passwords for the NetrJoinDomain2 interface.

Parameters:

    ServerName - UNC server name of the server to remote the API to

    Passwords - the cleartext unicode NT passwords.
    
    Count - the number of password

    RpcBindingHandle - RPC handle used for acquiring a session key.

    RedirHandle - Returns a handle to the redir.  Since RpcBindingHandles don't represent
        and open connection to the server, we have to ensure the connection stays open
        until the server side has a chance to get this same UserSessionKey.  The only
        way to do that is to keep the connect open.

        Returns NULL if no handle is needed.
        
    UserSessionKey - OPTIONAL - Session Key used to encrypt passwords
    
    EncryptedUserPassword - receives the encrypted cleartext passwords.
        If lpPassword is NULL, a NULL is returned for that entry.

Return Values:

    If this routine returns NO_ERROR, the returned data must be freed using
        LocalFree.


--*/
{
    DWORD WinError = ERROR_SUCCESS;
    NTSTATUS NtStatus;
    RC4_KEYSTRUCT Rc4Key;
    MD5_CTX Md5Context;
    USER_SESSION_KEY UserSessionKey;
    PDSROLEPR_USER_PASSWORD UserPassword = NULL;
    ULONG PasswordSize;
    ULONG i;

    //
    // Initialization
    //

    *RpcBindingHandle = NULL;
    *RedirHandle = NULL;
    for ( i = 0; i < Count; i++ ) {
        EncryptedUserPasswords[i] = NULL;
    }

    //
    // Verify parameters
    //
    for ( i = 0; i < Count; i++ ) {
        if ( Passwords[i] ) {
            PasswordSize = wcslen( Passwords[i] ) * sizeof(WCHAR);
            if ( PasswordSize > DSROLE_MAX_PASSWORD_LENGTH * sizeof(WCHAR)) {
                WinError = ERROR_PASSWORD_RESTRICTION;
                goto Cleanup;
            }
        }
    }

    //
    // Get an RPC handle to the server.
    //

    WinError = DsRolepServerBind( (PDSROLE_SERVER_NAME) ServerName,
                                  RpcBindingHandle );

    if ( ERROR_SUCCESS != WinError ) {
        goto Cleanup;
    }

    //
    // Get the session key.
    //

    NtStatus = RtlGetUserSessionKeyClientBinding(
                   *RpcBindingHandle,
                   RedirHandle,
                   &UserSessionKey );

    if ( !NT_SUCCESS(NtStatus) ) {
        WinError = RtlNtStatusToDosError( NtStatus );
        goto Cleanup;
    }

    //Return the UserSessionKey if requested
    if (pUserSessionKey) {
        CopyMemory(pUserSessionKey, &UserSessionKey, sizeof(UserSessionKey));
    }

    //
    // Encrypt the passwords
    //
    for ( i = 0; i < Count; i++ ) {
        

        if ( NULL == Passwords[i] ) {
            // Nothing to encrypt
            continue;
        }

        PasswordSize = wcslen( Passwords[i] ) * sizeof(WCHAR);

        //
        // Allocate a buffer to encrypt and fill it in.
        //
    
        UserPassword = LocalAlloc( 0, sizeof(DSROLEPR_USER_PASSWORD) );
    
        if ( UserPassword == NULL ) {
            WinError = ERROR_NOT_ENOUGH_MEMORY;
            goto Cleanup;
        }
    
        //
        // Copy the password into the tail end of the buffer.
        //
    
        RtlCopyMemory(
            ((PCHAR) UserPassword->Buffer) +
                (DSROLE_MAX_PASSWORD_LENGTH * sizeof(WCHAR)) -
                PasswordSize,
            Passwords[i],
            PasswordSize );
    
        UserPassword->Length = PasswordSize;
    
        //
        // Fill the front of the buffer with random data
        //
    
        NtStatus = DsRolepRandomFill(
                    (DSROLE_MAX_PASSWORD_LENGTH * sizeof(WCHAR)) -
                        PasswordSize,
                    (PUCHAR) UserPassword->Buffer );
    
        if ( !NT_SUCCESS(NtStatus) ) {
            WinError = RtlNtStatusToDosError( NtStatus );
            goto Cleanup;
        }
    
        NtStatus = DsRolepRandomFill(
                    DSROLE_OBFUSCATOR_LENGTH,
                    (PUCHAR) UserPassword->Obfuscator );

        if ( !NT_SUCCESS(NtStatus) ) {
            WinError = RtlNtStatusToDosError( NtStatus );
            goto Cleanup;
        }
    
    
        //
        // The UserSessionKey is the same for the life of the session.  RC4'ing multiple
        //  strings with a single key is weak (if you crack one you've cracked them all).
        //  So compute a key that's unique for this particular encryption.
        //
        //
    
        MD5Init(&Md5Context);
    
        MD5Update( &Md5Context, (LPBYTE)&UserSessionKey, sizeof(UserSessionKey) );
        MD5Update( &Md5Context, UserPassword->Obfuscator, sizeof(UserPassword->Obfuscator) );
    
        MD5Final( &Md5Context );
    
        rc4_key( &Rc4Key, MD5DIGESTLEN, Md5Context.digest );

        //
        // Encrypt it.
        //  Don't encrypt the obfuscator.  The server needs that to compute the key.
        //
    
        rc4( &Rc4Key, sizeof(UserPassword->Buffer)+sizeof(UserPassword->Length), (LPBYTE) UserPassword->Buffer );

        EncryptedUserPasswords[i] = (PDSROLEPR_ENCRYPTED_USER_PASSWORD) UserPassword;
        UserPassword = NULL;

    }

    WinError = ERROR_SUCCESS;

Cleanup:

    if ( WinError != ERROR_SUCCESS ) {
        if ( UserPassword != NULL ) {
            LocalFree( UserPassword );
        }
        if ( *RpcBindingHandle != NULL ) {
            DsRolepServerUnbind( NULL, *RpcBindingHandle );
            *RpcBindingHandle = NULL;
        }
        if ( *RedirHandle != NULL ) {
            NtClose( *RedirHandle );
            *RedirHandle = NULL;
        }
        for ( i = 0; i < Count; i++ ) {
            if ( EncryptedUserPasswords[i] ) {
                LocalFree( EncryptedUserPasswords[i] );
                EncryptedUserPasswords[i] = NULL;
            }
        }
    }

    return WinError;
}


VOID
DsRolepEncryptPasswordEnd(
    IN RPC_BINDING_HANDLE RpcBindingHandle,
    IN HANDLE RedirHandle OPTIONAL,
    IN PDSROLEPR_ENCRYPTED_USER_PASSWORD *EncryptedUserPasswords OPTIONAL,
    IN ULONG Count
    )
/*++

Routine Description:

    This routine takes the variables returned by DsRolepEncryptPasswordStart and
    frees them.

Parameters:

    RpcBindingHandle - RPC handle used for acquiring a session key.

    RedirHandle - Handle to the redirector

    EncryptedUserPasswords - the encrypted cleartext passwords.
    
    Count - the number of passwords

Return Values:

--*/
{
    ULONG i;

    //
    // Free the RPC binding handle.
    //

    if ( RpcBindingHandle != NULL ) {
        (VOID) DsRolepServerUnbind ( NULL, RpcBindingHandle );
    }

    //
    // Close the redir handle.
    //

    if ( RedirHandle != NULL ) {
        NtClose( RedirHandle );
    }

    //
    // Free the encrypted passwords.
    //

    for ( i = 0; i < Count; i++ ) {
        if ( EncryptedUserPasswords[i] != NULL ) {
            LocalFree( EncryptedUserPasswords[i] );
        }
    }

    return;
}

DWORD
DsRolepHashkey(
    IN OUT LPWSTR key,
    OUT PUNICODE_STRING Hash
)
/*++

    Routine Description

    This routine is used to store the boot type
    in the registry

    Paramaeters

        key - the user passed in Key
        
        Hash - the hash of the key

    Return Values

        STATUS_SUCCESS
        STATUS_UNSUCCESSFUL
--*/
{
    MD5_CTX Md5;
    if (!key) {
        return ERROR_INVALID_PARAMETER;
    }

    MD5Init( &Md5 );
    MD5Update( &Md5, (PUCHAR) key, wcslen(key)*sizeof(WCHAR) );
    MD5Final( &Md5 );

    RtlSecureZeroMemory( key, wcslen(key)*sizeof(WCHAR) );

    Hash->Buffer = (LPWSTR) MIDL_user_allocate(SYSKEY_SIZE);
    if (!Hash->Buffer) {
        return ERROR_NOT_ENOUGH_MEMORY;
    }
    RtlCopyMemory(Hash->Buffer, Md5.digest, SYSKEY_SIZE);
    RtlSecureZeroMemory( Md5.digest, SYSKEY_SIZE );
    Hash->Length=SYSKEY_SIZE;
    Hash->MaximumLength=SYSKEY_SIZE;
    
    return ERROR_SUCCESS;
}

DWORD
DsRolepEncryptHash(
    IN PUSER_SESSION_KEY UserSessionKey,
    IN OUT PUNICODE_STRING Syskey,
    OUT PDSROLEPR_ENCRYPTED_HASH EncryptedSyskey
    )
/*++

    Routine Description

    This routine is used to store the boot type
    in the registry

    Paramaeters

        NewType Indicates the new boot type

    Return Values

        STATUS_SUCCESS
        STATUS_UNSUCCESSFUL
--*/
{
    DWORD WinError = ERROR_SUCCESS;
    NTSTATUS NtStatus;
    RC4_KEYSTRUCT Rc4Key;
    MD5_CTX Md5Context;
    
    //Init the buffer
    if (EncryptedSyskey) {
        EncryptedSyskey->EncryptedHash.Buffer = NULL;
    } else {
        WinError = ERROR_INVALID_PARAMETER;
        goto Cleanup;
    }

    //
    //parameter checking
    //
    if ( !Syskey || !Syskey->Buffer || (Syskey->Length < 1) 
         || ! UserSessionKey) {
        WinError = ERROR_INVALID_PARAMETER;
        goto Cleanup;
    }

    //Init PDSROLEPR_ENCRYPTED_HASH structure
    EncryptedSyskey->EncryptedHash.Buffer = Syskey->Buffer;
    EncryptedSyskey->EncryptedHash.Length = Syskey->Length;
    EncryptedSyskey->EncryptedHash.MaximumLength = Syskey->MaximumLength;

    //Clear out the Syskey
    RtlSecureZeroMemory(Syskey,sizeof(*Syskey));
    
    //Create a Random Salt
    NtStatus = DsRolepRandomFill(
                DSROLE_SALT_LENGTH,
                EncryptedSyskey->Salt );

    if ( !NT_SUCCESS(NtStatus) ) {
        WinError = RtlNtStatusToDosError( NtStatus );
        goto Cleanup;
    }

    //
    // The UserSessionKey is the same for the life of the session.  RC4'ing multiple
    //  strings with a single key is weak (if you crack one you've cracked them all).
    //  So compute a key that's unique for this particular encryption.
    //
    //

    MD5Init(&Md5Context);

    MD5Update( &Md5Context, (LPBYTE)UserSessionKey, sizeof(*UserSessionKey) );
    MD5Update( &Md5Context, EncryptedSyskey->Salt, sizeof(EncryptedSyskey->Salt) );

    MD5Final( &Md5Context );

    //
    // Encrypt it.
    //  Don't encrypt the salt.  The server needs that to compute the key.
    //
    rc4_key( &Rc4Key, MD5DIGESTLEN, Md5Context.digest );
    rc4( &Rc4Key, EncryptedSyskey->EncryptedHash.Length, (LPBYTE) EncryptedSyskey->EncryptedHash.Buffer );

    WinError = ERROR_SUCCESS;

Cleanup:

    if ( ERROR_SUCCESS != WinError ) {
        if (Syskey && Syskey->Buffer) {
             MIDL_user_free(Syskey->Buffer);
        }
        else if (EncryptedSyskey && EncryptedSyskey->EncryptedHash.Buffer) {
            MIDL_user_free(EncryptedSyskey->EncryptedHash.Buffer);
        }
    }

    return WinError;
}