/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:

    setutl.c

Abstract:

    Miscellaneous helper functions

Author:

    Mac McLain          (MacM)       Feb 10, 1997

Environment:

    User Mode

Revision History:

--*/
#include <setpch.h>
#include <dssetp.h>
#include <lsarpc.h>
#include <samrpc.h>
#include <samisrv.h>
#include <db.h>
#include <confname.h>
#include <loadfn.h>
#include <ntdsa.h>
#include <dsconfig.h>
#include <attids.h>
#include <samisrv.h>
#include <dsp.h>
#include <lsaisrv.h>
#include <malloc.h>
#include <dsgetdc.h>
#include <lmcons.h>
#include <lmaccess.h>
#include <lmapibuf.h>
#include <lmerr.h>
#include <netsetp.h>
#include <winsock2.h>
#include <nspapi.h>
#include <dsgetdcp.h>
#include <lmremutl.h>
#include <spmgr.h>  // For SetupPhase definition
#include <ntdsetup.h>
#include <shlwapi.h>

#include "secure.h"
#include "cancel.h"

#if DBG
    DEFINE_DEBUG2(DsRole);

    DEBUG_KEY   DsRoleDebugKeys[] = {
        {DEB_ERROR,         "Error"},
        {DEB_WARN,          "Warn"},
        {DEB_TRACE,         "Trace"},
        {DEB_TRACE_DS,      "NtDs"},
        {DEB_TRACE_UPDATE,  "Update"},
        {DEB_TRACE_LOCK,    "Lock"},
        {DEB_TRACE_SERVICES,"Services"},
        {DEB_TRACE_NET,     "Net"},
        {0,                 NULL }
        };

VOID
DsRoleDebugInitialize()
{
    DsRoleInitDebug(DsRoleDebugKeys);
}

#endif // DBG


BOOL
DsRolepShutdownNotification(
    DWORD   dwCtrlType
    );


//
// Global data for this module
//
BOOLEAN GlobalOpLockHeld = FALSE;



NTSTATUS
DsRolepInitialize(
    VOID
    )
/*++

Routine Description:

    Initializes the server portion of the DsRole APIs.  Called from LsaSrv
    DsRolerGetDcOperationProgress return init

Arguments:

    VOID


Returns:

    STATUS_SUCCESS - Success

--*/
{
    NTSTATUS Status = STATUS_SUCCESS;
    RPC_STATUS RPCError = RPC_S_OK;
    PWSTR KerbPrinc;

    //
    // Zero out global operation handle
    //
    RtlZeroMemory( &DsRolepCurrentOperationHandle, sizeof(DsRolepCurrentOperationHandle));

    //
    // Init the lock
    //
    RtlInitializeResource( &DsRolepCurrentOperationHandle.CurrentOpLock );


    //
    // Grab the lock
    //
    LockOpHandle();
    GlobalOpLockHeld = TRUE;

    DsRolepResetOperationHandleLockHeld();

    DsRoleDebugInitialize();

    RPCError = RpcServerRegisterIf( dssetup_ServerIfHandle,
                                       NULL,
                                       NULL );
    if (RPC_S_OK != RPCError) {
        DsRoleDebugOut(( DEB_ERROR,
                         "RpcServerRegisterIf failed %d\n",
                         RPCError ));
    }
    

    DsRolepInitSetupFunctions();

    //
    // Create the SD's that are used to perform access checks for DsRoler
    // callers
    //
    if ( !DsRolepCreateInterfaceSDs() ) {

        return STATUS_NO_MEMORY;

    }

    try {

        Status = RtlInitializeCriticalSection( &LogFileCriticalSection );

        } except ( 1 ) {

        Status =  STATUS_NO_MEMORY;
    }

    if(NT_SUCCESS(Status)) {
        //
        // Register our shutdown routine
        //
    
        if (!SetConsoleCtrlHandler(DsRolepShutdownNotification, TRUE)) {
            DsRoleDebugOut(( DEB_ERROR,
                             "SetConsoleCtrlHandler failed %d\n",
                             GetLastError() ));
        }
    
        if (!SetProcessShutdownParameters(480, SHUTDOWN_NORETRY)) {
            DsRoleDebugOut(( DEB_ERROR,
                             "SetProcessShutdownParameters failed %d\n",
                             GetLastError() ));
        }
    }

    

    return( Status );
}




NTSTATUS
DsRolepInitializePhase2(
    VOID
    )
/*++

Routine Description:

    Second phase of the promotion/demotion api initialization.  This initialization is slated
    to happen after the Lsa has finished all of it's initializations

Arguments:

    VOID


Returns:

    STATUS_SUCCESS - Success

    STATUS_UNSUCCESSFUL -- The function was called when the global lock wasn't held

--*/
{
    ULONG RpcStatus = STATUS_SUCCESS;
    PWSTR KerbPrinc;

    ASSERT( GlobalOpLockHeld );

    if ( !GlobalOpLockHeld ) {

        return( STATUS_UNSUCCESSFUL );
    }

    if ( !SetupPhase ) {

        //
        // Register the Rpc authenticated server info
        //
        RpcStatus = RpcServerInqDefaultPrincName(RPC_C_AUTHN_GSS_KERBEROS,
                                                 &KerbPrinc);

        if ( RpcStatus == RPC_S_OK ) {

            DsRoleDebugOut(( DEB_TRACE_DS, "Kerberos Principal name: %ws\n",
                             KerbPrinc ));

            RpcStatus = RpcServerRegisterAuthInfo(KerbPrinc,
                                                  RPC_C_AUTHN_GSS_NEGOTIATE,
                                                  NULL,
                                                  NULL);
            RpcStringFree( &KerbPrinc );

        } else {

            DsRoleDebugOut(( DEB_TRACE_DS, "RpcServerInqDefaultPrincName failed with %lu\n",
                             RpcStatus ));

            RpcStatus = RPC_S_OK;

        }

        if ( RpcStatus == RPC_S_OK) {

            RpcStatus = RpcServerRegisterAuthInfo( DSROLEP_SERVER_PRINCIPAL_NAME,
                                                   RPC_C_AUTHN_GSS_NEGOTIATE,
                                                   NULL,
                                                   NULL );

            if ( RpcStatus != RPC_S_OK ) {

                DsRoleDebugOut(( DEB_ERROR,
                                 "RpcServerRegisterAuthInfo for %ws failed with 0x%lx\n",
                                 DSROLEP_SERVER_PRINCIPAL_NAME,
                                 RpcStatus ));
                RpcStatus = RPC_S_OK;
            }

        }
    }


    //
    // Release the lock, as was opened in Initialization, phase 1
    //
    GlobalOpLockHeld = FALSE;
    RtlReleaseResource( &DsRolepCurrentOperationHandle.CurrentOpLock );

    return( RpcStatus == RPC_S_OK ? STATUS_SUCCESS : RPC_NT_UNKNOWN_AUTHZ_SERVICE );
}




DWORD
DsRolepGetMachineType(
    IN OUT PDSROLEP_MACHINE_TYPE MachineType
    )
/*++

Routine Description:

    Determines the type of machine this is being run on.

Arguments:

    MachineType - Where the machine type is being returned

Returns:

    STATUS_SUCCESS - Success

--*/
{
    DWORD Win32Err = ERROR_SUCCESS;

    if ( LsapProductType == NtProductWinNt ) {

        *MachineType = DSROLEP_MT_CLIENT;

    } else if ( LsapProductType == NtProductServer ) {

        *MachineType = DSROLEP_MT_STANDALONE;

    } else {

        *MachineType = DSROLEP_MT_MEMBER;

    }

    return( Win32Err );
}


DWORD
DsRolepSetProductType(
    IN DSROLEP_MACHINE_TYPE MachineType
    )
/*++

Routine Description:

    Changes the role of the product to the type specified.

Arguments:

    MachineType - Type of ProductRole to set

Returns:

    ERROR_SUCCESS - Success

    ERROR_INVALID_PARAMETER - A bad service option was given

--*/
{
    DWORD Win32Err = ERROR_SUCCESS;
    PWSTR MachineSz = NULL;
    HKEY ProductHandle;
    ULONG Size = 0;

    switch ( MachineType ) {
    case DSROLEP_MT_STANDALONE:
        MachineSz = L"ServerNT";
        Size = sizeof( L"ServerNT" );
        break;

    case DSROLEP_MT_MEMBER:
        MachineSz = L"LanmanNT";
        Size = sizeof( L"LanmanNT");
        break;

    case DSROLEP_MT_CLIENT:
    default:

        Win32Err = ERROR_INVALID_PARAMETER;
        break;
    }

    if ( Win32Err == ERROR_SUCCESS ) {

        Win32Err = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
                                 DSROLEP_PROD_KEY_PATH,
                                 REG_OPTION_NON_VOLATILE,
                                 KEY_WRITE,            // desired access
                                 &ProductHandle );

        if ( Win32Err == ERROR_SUCCESS ) {

            Win32Err = RegSetValueEx( ProductHandle,
                                      (LPCWSTR)DSROLEP_PROD_VALUE,
                                      0,
                                      REG_SZ,
                                      (CONST BYTE *)MachineSz,
                                      Size );


            RegCloseKey( ProductHandle );
        }
    }

    DsRoleDebugOut(( DEB_TRACE_DS, "SetProductType to %ws returned %lu\n",
                     MachineSz, Win32Err ));

    DsRolepLogPrint(( DEB_TRACE,
                      "SetProductType to %lu [%ws] returned %lu\n",
                       MachineType,
                       DsRolepDisplayOptional(MachineSz),
                       Win32Err ));

    DSROLEP_FAIL1( Win32Err, DSROLERES_PRODUCT_TYPE, MachineSz );


    return( Win32Err );
}

DWORD
DsRolepCreateAuthIdentForCreds(
    IN PWSTR Account,
    IN PWSTR Password,
    OUT PSEC_WINNT_AUTH_IDENTITY *AuthIdent
    )
/*++

Routine Description:

    Internal routine to create an AuthIdent structure for the given creditentials

Arguments:

    Account - Account name

    Password - Password for the account

    AuthIdent - AuthIdentity struct to allocate and fill in.


Returns:

    ERROR_SUCCESS - Success

    ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed.

--*/
{
    DWORD Win32Err = ERROR_SUCCESS;
    PWSTR UserCredentialString = NULL;

    ASSERT( AuthIdent );

    //
    // If there are no creds, just return
    //
    if ( Account == NULL ) {

        *AuthIdent = NULL;
        return( Win32Err );
    }

    *AuthIdent = RtlAllocateHeap( RtlProcessHeap(), 0, sizeof( SEC_WINNT_AUTH_IDENTITY ) );

    if ( *AuthIdent == NULL ) {

        Win32Err = ERROR_NOT_ENOUGH_MEMORY;

    } else {

        RtlZeroMemory( *AuthIdent, sizeof( SEC_WINNT_AUTH_IDENTITY ) );
        UserCredentialString = RtlAllocateHeap( RtlProcessHeap(), 0,
                                                ( wcslen( Account ) + 1 ) * sizeof( WCHAR ) );
        if ( UserCredentialString ) {

            wcscpy( UserCredentialString, Account );

            ( *AuthIdent )->User = wcsstr( UserCredentialString, L"\\" );

            if ( ( *AuthIdent )->User ) {

               //
               // There is a domain name
               //
               *( ( *AuthIdent )->User ) = L'\0';
               ( ( *AuthIdent )->User )++;
               ( *AuthIdent )->Domain = UserCredentialString;

            } else {

               ( *AuthIdent )->User = UserCredentialString;
               ( *AuthIdent )->Domain = L"";

            }

            if ( ( *AuthIdent )->User ) {

                ( *AuthIdent )->UserLength = wcslen( ( *AuthIdent )->User );
            }

            if ( ( *AuthIdent )->Domain ) {

                ( *AuthIdent )->DomainLength = wcslen( ( *AuthIdent )->Domain );
            }

            ( *AuthIdent )->Password = Password;

            if ( Password ) {

                ( *AuthIdent )->PasswordLength = wcslen( Password );
            }

            ( *AuthIdent )->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;

        } else {

            Win32Err = ERROR_NOT_ENOUGH_MEMORY;

            //
            // Free the memory allocated for the top level structure
            //
            RtlFreeHeap( RtlProcessHeap(), 0, *AuthIdent );
            *AuthIdent = NULL;
        }
    }

    return( Win32Err );
}


VOID
DsRolepFreeAuthIdentForCreds(
    IN  PSEC_WINNT_AUTH_IDENTITY AuthIdent
    )
/*++

Routine Description:

    Free the authident structure allocated above

Arguments:

    AuthIdent - AuthIdentity struct to free


Returns:

    VOID

--*/
{

    if ( AuthIdent ) {

        if ( AuthIdent->Domain == NULL ) {

            RtlFreeHeap( RtlProcessHeap(), 0, AuthIdent->User );

        } else {

            if ( *AuthIdent->Domain != L'\0' ) {

                RtlFreeHeap( RtlProcessHeap(), 0, AuthIdent->Domain );
            }
        }

        RtlFreeHeap( RtlProcessHeap(), 0, AuthIdent );
    }

}

NTSTATUS
ImpLsaOpenPolicy(
    IN HANDLE CallerToken,
    IN PLSA_UNICODE_STRING SystemName OPTIONAL,
    IN PLSA_OBJECT_ATTRIBUTES ObjectAttributes,
    IN ACCESS_MASK DesiredAccess,
    IN OUT PLSA_HANDLE PolicyHandle
    )
/*++

Routine Description:

    This routine impersonates CallerToken and then calls into LsaOpenPolicy.

    This purpose of this routine is call into the LSA on a different machine
    using the RDR session for the caller of the DsRole API.  The caller is
    represented by CallerToken.  This is necessary because the RDR sessions
    are keyed by (logon id/remote server name) and we don't want to use the
    logon id of the lsass.exe process since this is a shared logon id for
    lsass.exe and services.exe and will lead to unresolable credentials
    conflict.

    N.B.  The LSA rpc calls that follow the (Imp)LsaOpenPolicy will use the
    handle returned by this function and then magically uses the right RDR
    session to make the RPC call.

Arguments:

    CallerToken - the token of the DsRole involker

    Others -- see LsaOpenPolicy


Returns:

    STATUS_ACCESS_DENIED if the impersonattion fails.

--*/
{
    NTSTATUS Status = STATUS_SUCCESS;
    BOOL fSuccess;

    fSuccess = ImpersonateLoggedOnUser( CallerToken );
    if ( fSuccess ) {

        Status = LsaOpenPolicy( SystemName,
                                ObjectAttributes,
                                DesiredAccess,
                                PolicyHandle );

        fSuccess = RevertToSelf();
        ASSERT( fSuccess );
    } else {

        DsRolepLogPrint(( DEB_TRACE,
                          "Failed to impersonate caller, error %lu\n",
                          GetLastError() ));

        //
        // We couldn't impersonate?
        //
        Status = STATUS_ACCESS_DENIED;
    }

    return Status;

}

DWORD                         
ImpDsRolepDsGetDcForAccount(
    IN HANDLE CallerToken,
    IN LPWSTR Server OPTIONAL,
    IN LPWSTR Domain,
    IN LPWSTR Account,
    IN ULONG Flags,
    IN ULONG AccountBits,
    OUT PDOMAIN_CONTROLLER_INFOW *DomainControllerInfo
    )
/*++

Routine Description:

    This function will impersoniate logged on user and call DsRolepDsGetDcForAccount

Arguments:

    CallerToken - The Token of the DsRole involker.

    Server - The server to call GetDc on.

    Domain - Domain to find the Dc for

    Account - Account to look for.  If NULL, the current computer name is used

    Flags - Flags to bas in to the GetDc call

    AccountBits - Account control bits to search for

    DomainControllerInfo - Where the info is returned

Returns:

    ERROR_SUCCESS - Success

--*/
{
    DWORD WinError = ERROR_SUCCESS;
    BOOL fSuccess;

    fSuccess = ImpersonateLoggedOnUser( CallerToken );
    if ( fSuccess ) {

        WinError = DsRolepDsGetDcForAccount(Server,
                                            Domain,
                                            Account,
                                            Flags,
                                            AccountBits,
                                            DomainControllerInfo
                                            );

        fSuccess = RevertToSelf();
        ASSERT( fSuccess );

    } else {

        DsRolepLogPrint(( DEB_TRACE,
                          "Failed to impersonate caller, error %lu\n",
                          GetLastError() ));

        //
        // We couldn't impersonate?
        //
        WinError = ERROR_ACCESS_DENIED;
    }

    return WinError;
}

NET_API_STATUS
NET_API_FUNCTION
ImpNetpManageIPCConnect(
    IN  HANDLE  CallerToken,
    IN  LPWSTR  lpServer,
    IN  LPWSTR  lpAccount,
    IN  LPWSTR  lpPassword,
    IN  ULONG   fOptions
    )
/*++

Routine Description:

    This routine impersonates CallerToken and then calls into
    NetpManageIPCConnect.

    This purpose of this routine is to create a RDR using the logon id of
    the caller of the DsRole api's.  The caller is represented by CallerToken.
    This is necessary because the RDR sessions are keyed by
    (logon id/remote server name) and we don't want to use the
    logon id of the lsass.exe process since this is a shared logon id for
    lsass.exe and services.exe and will lead to unresolable credentials
    conflict.

Arguments:

    CallerToken - the token of the DsRole involker

    Others -- see LsaOpenPolicy


Returns:

    STATUS_ACCESS_DENIED if the impersonattion fails.

--*/
{
    DWORD WinError = ERROR_SUCCESS;
    BOOL fSuccess;

    fSuccess = ImpersonateLoggedOnUser( CallerToken );
    if ( fSuccess ) {

        WinError = NetpManageIPCConnect( lpServer,
                                         lpAccount,
                                         lpPassword,
                                         fOptions );

        fSuccess = RevertToSelf();
        ASSERT( fSuccess );

    } else {

        DsRolepLogPrint(( DEB_TRACE,
                          "Failed to impersonate caller, error %lu\n",
                          GetLastError() ));

        //
        // We couldn't impersonate?
        //
        WinError = ERROR_ACCESS_DENIED;
    }

    return WinError;

}


DWORD
DsRolepGenerateRandomPassword(
    IN ULONG Length,
    IN WCHAR *Buffer
    )
/*++

Routine Description:

    This local function is used to generate a random password of no more than the
    specified length.  It is assumed that the destination buffer is of sufficient length.

Arguments:

    Length - Length of the buffer

    Buffer - Buffer to fill

Return Values:

    ERROR_SUCCESS - Success

--*/
{
    DWORD Win32Err = ERROR_SUCCESS;
    ULONG PwdLength, i;
    LARGE_INTEGER Time;
    HCRYPTPROV CryptProvider = 0;


    PwdLength = Length;

    //
    // Generate a random password.
    //
    if ( CryptAcquireContext( &CryptProvider,
                              NULL,
                              NULL,
                              PROV_RSA_FULL,
                              CRYPT_VERIFYCONTEXT ) ) {

        if ( CryptGenRandom( CryptProvider,
                              PwdLength * sizeof( WCHAR ),
                              ( LPBYTE )Buffer ) ) {

            Buffer[ PwdLength ] = UNICODE_NULL;

            //
            // Make sure there are no NULL's in the middle of the list
            //
            for ( i = 0; i < PwdLength; i++ ) {

                if ( Buffer[ i ] == UNICODE_NULL ) {

                    Buffer[ i ] = 0xe;
                }
            }

        } else {

            Win32Err = GetLastError();
        }

        CryptReleaseContext( CryptProvider, 0 );


    } else {

        Win32Err = GetLastError();
    }

    return( Win32Err );

}
DWORD
DsRolepCopyDsDitFiles(
    IN LPWSTR DsPath
    )
/*++

Routine Description:

    This function copies the initial database files from the install point to the
    specified Ds database directory

Arguments:

    DsPath - Path where the Ds database files are to reside

Returns:

    ERROR_SUCCESS - Success

--*/
{
    DWORD Win32Err = ERROR_SUCCESS;
    WCHAR Source[MAX_PATH + 1];
    WCHAR Dest[MAX_PATH + 1];
    ULONG SrcLen = 0, DestLen = 0;
    PWSTR Current;
    ULONG i;
    PWSTR DsDitFiles[] = {
        L"ntds.dit"
        };



    if( ExpandEnvironmentStrings( L"%WINDIR%\\system32\\", Source, MAX_PATH ) == FALSE ) {

        Win32Err = GetLastError();

    } else {

        SrcLen = wcslen( Source );
        wcscpy( Dest, DsPath );

        if ( *(Dest + (wcslen( DsPath ) - 1 )) != L'\\' ) {

            wcscat( Dest, L"\\" );
        }

        DestLen = wcslen( Dest );

    }

    //
    // Then, create the destination directory
    //
    if ( Win32Err == ERROR_SUCCESS ) {

        Current = wcschr( DsPath + 4, L'\\' );

        while ( Win32Err == ERROR_SUCCESS ) {

            if ( Current != NULL ) {

                *Current = UNICODE_NULL;

            }

            if ( CreateDirectory( DsPath, NULL ) == FALSE ) {

            
                Win32Err = GetLastError();
    
                if ( Win32Err == ERROR_ALREADY_EXISTS) {
    
                    Win32Err = ERROR_SUCCESS;
    
                } else if ( Win32Err == ERROR_ACCESS_DENIED ) {
    
                    if ( PathIsRoot(DsPath) ) {

                        //If the path given to CreateDirectory is a root path then
                        //it will fail with ERROR_ACCESS_DENIED instead of
                        //ERROR_ALREADY_EXISTS but the path is still a valid one for
                        //ntds.dit and the log files to be placed in.

                        Win32Err = ERROR_SUCCESS;

                    }
                }
            }

            if ( Current != NULL ) {

                *Current = L'\\';

                Current = wcschr( Current + 1, L'\\' );

            } else {

                break;

            }

        }
    }

    //
    // Then copy them.
    //
    for ( i = 0; i < sizeof( DsDitFiles) / sizeof( PWSTR ) && Win32Err == ERROR_SUCCESS ; i++ ) {

        wcscpy( Source + SrcLen, DsDitFiles[i] );
        wcscpy( Dest + DestLen, DsDitFiles[i] );

        DSROLEP_CURRENT_OP2( DSROLEEVT_COPY_DIT, Source, Dest );
        if ( CopyFile( Source, Dest, TRUE ) == FALSE ) {

            Win32Err = GetLastError();

            if ( Win32Err == ERROR_ALREADY_EXISTS ||
                 Win32Err == ERROR_FILE_EXISTS ) {

                Win32Err = ERROR_SUCCESS;

            } else {

                DsRolepLogPrint(( DEB_ERROR, "Failed to copy install file %ws to %ws: %lu\n",
                                  Source, Dest, Win32Err ));
            }
        }
    }

    return( Win32Err );
}


#define DSROLEP_SEC_SYSVOL   L"SYSVOL"
#define DSROLEP_SEC_DSDIT    L"DSDIT"
#define DSROLEP_SEC_DSLOG    L"DSLOG"

DWORD
DsRolepSetDcSecurity(
    IN HANDLE ClientToken,
    IN LPWSTR SysVolRootPath,
    IN LPWSTR DsDatabasePath,
    IN LPWSTR DsLogPath,
    IN BOOLEAN Upgrade,
    IN BOOLEAN Replica
    )
/*++

Routine Description:

    This function will invoke the security editor to set the security on the Dc install files

Arguments:

    SysVolRootPath - Root used for the system volume

    DsDatabasePath - Path to where the Ds database files go

    DsLogPath - Path to where the Ds log files go

    Upgrade - If TRUE, the machine is undergoing an upgrade

    Replica - If TRUE, the machine is going through an upgrade

Returns:

    ERROR_SUCCESS - Success

--*/
{

    DWORD Win32Err = ERROR_SUCCESS, i;
    WCHAR InfPath[ MAX_PATH + 1 ];
    PWSTR Paths[ 3 ], Tags[ 3 ];
    ULONG Options = 0;

    Paths[ 0 ] = SysVolRootPath;
    Paths[ 1 ] = DsDatabasePath;
    Paths[ 2 ] = DsLogPath;
    Tags[ 0 ] = DSROLEP_SEC_SYSVOL;
    Tags[ 1 ] = DSROLEP_SEC_DSDIT;
    Tags[ 2 ] = DSROLEP_SEC_DSLOG;

    //
    // Set the environment variables.  secedt uses the environment variables to pass around
    // information, so we will set the for the duration of this function
    //
    if ( Win32Err == ERROR_SUCCESS ) {


        ASSERT( sizeof( Paths ) / sizeof( PWSTR ) == sizeof( Tags ) / sizeof( PWSTR ) );
        for ( i = 0; i < sizeof( Paths ) / sizeof( PWSTR ) && Win32Err == ERROR_SUCCESS; i++ ) {

            if ( SetEnvironmentVariable( Tags[ i ], Paths[ i ] ) == FALSE ) {

                Win32Err = GetLastError();
                DsRolepLogPrint(( DEB_TRACE,
                                  "SetEnvironmentVariable %ws = %ws failed with %lu\n",
                                  Tags[ i ],
                                  Paths[ i ],
                                  Win32Err ));
                break;
            }
        }
    }

    //
    // Now, invoke the security editing code
    //
    if ( Win32Err == ERROR_SUCCESS ) {

        DsRolepSetAndClearLog();
        DSROLEP_CURRENT_OP0( DSROLEEVT_SETTING_SECURITY );

        Options |= Upgrade ? SCE_PROMOTE_FLAG_UPGRADE : 0;
        Options |= Replica ? SCE_PROMOTE_FLAG_REPLICA : 0;

        Win32Err = ( *DsrSceDcPromoteSecurityEx )( ClientToken,
                                                   Options,
                                                   DsRolepStringUpdateCallback );
        DsRolepSetAndClearLog();
        DsRolepLogOnFailure( Win32Err,
                             DsRolepLogPrint(( DEB_ERROR,
                                               "Setting security on Dc files failed with %lu\n",
                                               Win32Err )) );
    }


    //
    // Delete the environment variables
    //
    for ( i = 0; i < sizeof( Paths ) / sizeof( PWSTR ); i++ ) {

        if ( SetEnvironmentVariable( Tags[ i ], NULL ) == FALSE ) {

            DsRolepLogPrint(( DEB_TRACE,
                             "SetEnvironmentVariable %ws = NULL failed with %lu\n",
                             Tags[ i ],
                             GetLastError() ));
        }
    }

    //
    // Currently, setting the security will not cause the promote to fail
    //
    if ( Win32Err != ERROR_SUCCESS ) {

        //
        // Raise an event
        //
        SpmpReportEvent( TRUE,
                         EVENTLOG_WARNING_TYPE,
                         DSROLERES_FAIL_SET_SECURITY,
                         0,
                         sizeof( ULONG ),
                         &Win32Err,
                         1,
                         SCE_DCPROMO_LOG_PATH );

        DSROLEP_SET_NON_FATAL_ERROR( Win32Err );

    }

    Win32Err = ERROR_SUCCESS;

    return( Win32Err );
}




DWORD                         
DsRolepDsGetDcForAccount(
    IN LPWSTR Server OPTIONAL,
    IN LPWSTR Domain,
    IN LPWSTR Account,
    IN ULONG Flags,
    IN ULONG AccountBits,
    OUT PDOMAIN_CONTROLLER_INFOW *DomainControllerInfo
    )
/*++

Routine Description:

    This function is equivalent to DsGetDcName but will search for the Dc that holds the
    specified account.

Arguments:

    ReplicaServer - The server to call GetDc on.

    Domain - Domain to find the Dc for

    Account - Account to look for.  If NULL, the current computer name is used

    Flags - Flags to bas in to the GetDc call

    AccountBits - Account control bits to search for

    DomainControllerInfo - Where the info is returned

Returns:

    ERROR_SUCCESS - Success

--*/
{
    DWORD Win32Err = ERROR_SUCCESS;
    WCHAR ComputerName[ MAX_COMPUTERNAME_LENGTH + 2 ];
    ULONG Length = MAX_COMPUTERNAME_LENGTH + 1;

    //
    // If we have no account, use the computer name
    //
    if ( Account == NULL ) {

        if ( GetComputerName( ComputerName, &Length ) == FALSE ) {

            Win32Err = GetLastError();

        } else {

            wcscat( ComputerName, SSI_SECRET_PREFIX );
            Account = ComputerName;
        }
    }

    //
    // Now, do the find
    //
    if ( Win32Err == ERROR_SUCCESS ) {

        DSROLEP_CURRENT_OP2( DSROLEEVT_FIND_DC_FOR_ACCOUNT, Domain, Account );
        Win32Err = DsGetDcNameWithAccountW( Server,
                                            Account,
                                            AccountBits,
                                            Domain,
                                            NULL,
                                            NULL,
                                            Flags,
                                            DomainControllerInfo );

        if ( ERROR_NO_SUCH_USER == Win32Err ) {

            //
            // The error should read "no machine account", not "no user"
            // since we are searching for a machine account.
            //

            Win32Err = ERROR_NO_TRUST_SAM_ACCOUNT;
        }

        if ( Win32Err == ERROR_SUCCESS ) {

            DSROLEP_CURRENT_OP2( DSROLEEVT_FOUND_DC,
                                 ( PWSTR ) ( ( *DomainControllerInfo )->DomainControllerName + 2 ),
                                 Domain );

        } else {

            DsRolepLogPrint(( DEB_ERROR, "Failed to find a DC for domain %ws: %lu\n",
                              Domain, Win32Err ));

        }


    }



    return( Win32Err );
}




DWORD
DsRolepSetMachineAccountType(
    IN LPWSTR Dc,
    IN HANDLE ClientToken,
    IN LPWSTR User,
    IN LPWSTR Password,
    IN LPWSTR AccountName,
    IN ULONG AccountBits,
    IN OUT WCHAR** AccountDn
    )
{
    DWORD Win32Err = ERROR_SUCCESS, Win32Err2;
    USER_INFO_1 *CurrentUI1;
    WCHAR ComputerName[ MAX_COMPUTERNAME_LENGTH + 2 ];
    ULONG Length = MAX_COMPUTERNAME_LENGTH + 1;
    PSEC_WINNT_AUTH_IDENTITY AuthIdent = NULL;

    //
    // If we have no account, use the computer name
    //
    if ( AccountName == NULL ) {

        if ( GetComputerName( ComputerName, &Length ) == FALSE ) {

            Win32Err = GetLastError();

        } else {

            wcscat( ComputerName, SSI_SECRET_PREFIX );
            AccountName = ComputerName;
        }
    }

    if ( Win32Err == ERROR_SUCCESS ) {

        Win32Err = DsRolepCreateAuthIdentForCreds( User, Password, &AuthIdent );
    }

    //
    // Call the support dll
    //
    if ( Win32Err == ERROR_SUCCESS ) {

        DsRolepLogPrint(( DEB_TRACE, "Searching for the machine account for %ws on %ws...\n",
                           AccountName, Dc ));

        DSROLEP_CURRENT_OP0( DSROLEEVT_MACHINE_ACCT );

        DSROLE_GET_SETUP_FUNC( Win32Err, DsrNtdsSetReplicaMachineAccount );

        if ( Win32Err == ERROR_SUCCESS ) {

            if ( Dc && *Dc == L'\\' ) {

                Dc += 2;
            }

            Win32Err = (*DsrNtdsSetReplicaMachineAccount)( AuthIdent,
                                                           ClientToken, 
                                                           Dc,
                                                           AccountName,
                                                           AccountBits,
                                                           AccountDn );
        }

        DsRolepLogPrint(( DEB_TRACE, "NtdsSetReplicaMachineAccount returned %d\n", Win32Err ));

        DsRolepFreeAuthIdentForCreds( AuthIdent );
    }

    return( Win32Err );
}



DWORD
DsRolepForceTimeSync(
    IN HANDLE ImpToken,
    IN PWSTR TimeSource
    )
/*++

Routine Description:

    This function forces a time sync with the specified server

Arguments:

    TimeSource - Server to use for the time source

Returns:

    ERROR_SUCCESS - Success

--*/
{
    DWORD Win32Err = ERROR_SUCCESS;
    NTSTATUS Status = STATUS_SUCCESS;
    PWSTR ServerName = NULL;
    PTIME_OF_DAY_INFO TOD;
    HANDLE ThreadToken = 0;
    TOKEN_PRIVILEGES Enabled, Previous;
    DWORD PreviousSize;
    TIME_FIELDS TimeFields;
    LARGE_INTEGER SystemTime;

    BOOL connected=FALSE;
    NETRESOURCE NetResource;
    WCHAR *remotename=NULL;

    BOOL fSuccess = FALSE;

    if ( !TimeSource ) {
        Win32Err = ERROR_INVALID_PARAMETER;
        goto cleanup;
    }
    
    //
    // Build the server name with preceeding \\'s
    //
    if ( *TimeSource != L'\\' ) {

        ServerName = RtlAllocateHeap( RtlProcessHeap(), 0,
                                      ( wcslen( TimeSource ) + 3 ) * sizeof( WCHAR ) );

        if ( ServerName == NULL ) {

            Win32Err = ERROR_NOT_ENOUGH_MEMORY;
            DsRolepLogPrint(( DEB_ERROR, "Failed to open a NULL session with %ws for time sync.  Out of Memory. Failed with %d\n",
                             TimeSource,
                             Win32Err ));
            goto cleanup;

        } else {

            swprintf( ServerName, L"\\\\%ws", TimeSource );
        }

    } else {

        ServerName = TimeSource;
    }

    //
    // Enable the systemtime privilege
    //
    if ( Win32Err == ERROR_SUCCESS ) {

        Status = NtOpenThreadToken( NtCurrentThread(),
                                    TOKEN_READ | TOKEN_WRITE,
                                    TRUE,
                                    &ThreadToken );

        if ( Status == STATUS_NO_TOKEN ) {

            Status = NtOpenProcessToken( NtCurrentProcess(),
                                         TOKEN_WRITE | TOKEN_READ,
                                         &ThreadToken );
        }

        if ( NT_SUCCESS( Status ) ) {

            Enabled.PrivilegeCount = 1;
            Enabled.Privileges[0].Luid.LowPart = SE_SYSTEMTIME_PRIVILEGE;
            Enabled.Privileges[0].Luid.HighPart = 0;
            Enabled.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
            PreviousSize = sizeof( Previous );

            Status = NtAdjustPrivilegesToken( ThreadToken,
                                              FALSE,
                                              &Enabled,
                                              sizeof( Enabled ),
                                              &Previous,
                                              &PreviousSize );
            //
            // Since we modified the thread token and the thread is shortlived, we won't bother
            // restoring it later.
            //
        }

        if ( ThreadToken ) {

            NtClose( ThreadToken );
            
        }



        Win32Err = RtlNtStatusToDosError( Status );
        DsRolepLogOnFailure( Win32Err,
                             DsRolepLogPrint(( DEB_ERROR,
                                               "Failed to enable the SE_SYSTEMTIME_PRIVILEGE: %lu\n",
                                               Win32Err )) );

    }


    //
    // Get the remote time
    //
    if ( Win32Err == ERROR_SUCCESS ) {

        DSROLEP_CURRENT_OP1( DSROLEEVT_TIMESYNC, TimeSource );

        fSuccess = ImpersonateLoggedOnUser( ImpToken );
        if ( !fSuccess ) {

            DsRolepLogPrint(( DEB_TRACE,
                              "Failed to impersonate caller, error %lu\n",
                              GetLastError() ));
        
            //
            // We couldn't impersonate?
            //
            

            // We will continue anyway

        }
        
    }
    


    remotename = RtlAllocateHeap(
                  RtlProcessHeap(), 0,
                   sizeof(WCHAR)*(wcslen(L"\\ipc$")+wcslen(ServerName)+1));
    if ( remotename == NULL ) {

        Win32Err = ERROR_NOT_ENOUGH_MEMORY;
        DsRolepLogPrint(( DEB_ERROR, "Failed to open a NULL session with %ws for time sync.  Out of Memory. Failed with %d\n",
                             ServerName,
                             Win32Err ));
        
    } 
                                                                            
    wsprintf(remotename,L"%s\\ipc$",ServerName);
    
    NetResource.dwType=RESOURCETYPE_ANY;
    NetResource.lpLocalName=NULL;
    NetResource.lpRemoteName=remotename;
    NetResource.lpProvider=NULL;
    
    //get permission to access the server
    Win32Err=WNetAddConnection2W(&NetResource,
                             L"",
                             L"",
                             0);
    if ( Win32Err == NO_ERROR ) {
        connected=TRUE;
    }
    else {
        DsRolepLogPrint(( DEB_WARN, "Failed to open a NULL session with %ws for time sync.  Failed with %d\n",
                         ServerName,
                         Win32Err ));
        //We will attempt to Time sync anyway
    }

    Win32Err = NetRemoteTOD( ServerName, ( LPBYTE * )&TOD );

    if ( Win32Err == ERROR_SUCCESS ) {

        TimeFields.Hour = ( WORD )TOD->tod_hours;
        TimeFields.Minute = ( WORD )TOD->tod_mins;
        TimeFields.Second = ( WORD )TOD->tod_secs;
        TimeFields.Milliseconds = ( WORD )TOD->tod_hunds * 10;
        TimeFields.Day = ( WORD )TOD->tod_day;
        TimeFields.Month = ( WORD )TOD->tod_month;
        TimeFields.Year = ( WORD )TOD->tod_year;

        if ( !RtlTimeFieldsToTime( &TimeFields, &SystemTime ) ) {

            Status = STATUS_INVALID_PARAMETER;

        } else {

            if ( connected ) {
                WNetCancelConnection2(remotename,
                                  0,
                                  TRUE);
            }
        
            if( remotename ) {
        
                RtlFreeHeap( RtlProcessHeap(), 0, remotename );
        
            }
        
            fSuccess = RevertToSelf();
            ASSERT( fSuccess );
            connected=FALSE;

            Status = NtSetSystemTime( &SystemTime, NULL );

            if ( !NT_SUCCESS( Status ) ) {

                DsRolepLogPrint(( DEB_ERROR, "NtSetSystemTime failed with 0x%lx\n", Status ));
            }


        }
    

        Win32Err = RtlNtStatusToDosError( Status );

        NetApiBufferFree( TOD );

    } else {

        DsRolepLogPrint(( DEB_ERROR, "Failed to get the current time on %ws: %lu\n",
                          TimeSource, Win32Err ));

    }
    
            
        
        
    //
    // For the IDS, consider a failure here non-fatal
    //
    if ( Win32Err != ERROR_SUCCESS ) {

        DsRolepLogPrint(( DEB_ERROR, "NON-FATAL error forcing a time sync (%lu).  Ignoring\n",
                          Win32Err ));
        Win32Err = ERROR_SUCCESS;

    }

    cleanup:

    if ( connected ) {
        WNetCancelConnection2(remotename,
                          0,
                          TRUE);
    

        if( remotename ) {
    
            RtlFreeHeap( RtlProcessHeap(), 0, remotename );
    
        }
    
        fSuccess = RevertToSelf();
        ASSERT( fSuccess );
    }

    
    return( Win32Err );
}




NTSTATUS
DsRolepGetMixedModeFlags(
    IN PSID DomainSid,
    OUT PULONG Flags
    )
/*++

Routine Description:

    This routine will determine whether the machine is currently in mixed mode or not

Arguments:

    Flags - Pointer to a flags value to be altered.  If the machine is a mixed mode, we simply
        or in the proper value.

Return Values:

    NTSTATUS        

--*/
{
    NTSTATUS Status = STATUS_SUCCESS;
    BOOLEAN mixedDomain;

    Status = SamIMixedDomain2( DomainSid, &mixedDomain );

    if ( NT_SUCCESS( Status ) && mixedDomain) {
        *Flags |= DSROLE_PRIMARY_DS_MIXED_MODE;
    }

    return( Status );
}


BOOL
DsRolepShutdownNotification(
    DWORD   dwCtrlType
    )
/*++

Routine Description:

    This routine is called by the system when system shutdown is occuring.

    It stops a role change if one is in progress.
    
Arguments:

    dwCtrlType -- the notification


Return Value:

    FALSE - to allow any other shutdown routines in this process to
        also be called.

--*/
{
    if ( dwCtrlType == CTRL_SHUTDOWN_EVENT ) {

        //
        // Cancel the operation
        // 
        (VOID) DsRolepCancel( FALSE );  // Don't block

    }

    return FALSE;
}

DWORD
DsRolepDeregisterNetlogonDnsRecords(
    PNTDS_DNS_RR_INFO pInfo
    )
/*++

Routine Description:

    This routine is called during demotion to call netlogon to deregister
    its the service DNS records for this domain controller
   
Arguments:

    pInfo -- structure containing the parameters for the deregistration

Return Value:

    An error from DsDeregisterDnsHostRecordsW

--*/
{

    DWORD WinError = ERROR_SUCCESS;
    HKEY  hNetlogonParms = NULL;
    BOOL  fDoDeregistration = TRUE;

    if ( !pInfo ) {
        return STATUS_SUCCESS;
    }

#define NETLOGON_PATH L"SYSTEM\\CurrentControlSet\\Services\\Netlogon\\Parameters"
#define AVOID_DNS_DEREG_KEY L"AvoidDnsDeregOnShutdown"

    WinError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
                             NETLOGON_PATH,
                             0,
                             KEY_READ,
                             &hNetlogonParms );

    if ( ERROR_SUCCESS == WinError ) {

        DWORD val = 0;
        DWORD len = sizeof(DWORD);
        DWORD type;

        WinError = RegQueryValueEx( hNetlogonParms,
                                    AVOID_DNS_DEREG_KEY,
                                    0,
                                    &type,
                                    (BYTE*)&val,
                                    &len );

        if ( (ERROR_SUCCESS == WinError)
         &&  (type == REG_DWORD)
         &&  (val == 0)       ) {

            //
            // Don't bother; netlogon has already done the deregistration.
            //
            fDoDeregistration = FALSE;
        }

        RegCloseKey( hNetlogonParms );
    }

    if ( fDoDeregistration ) { 

        //
        // Ask netlogon to do the deregistration
        //
        WinError = DsDeregisterDnsHostRecordsW( NULL, // go local
                                                pInfo->DnsDomainName,
                                                &pInfo->DomainGuid,
                                                &pInfo->DsaGuid,
                                                pInfo->DnsHostName );
    } else {

        WinError = ERROR_SUCCESS;

    }

    return WinError;

}

NTSTATUS
ImpLsaDelete(
    IN HANDLE CallerToken,
    IN LSA_HANDLE ObjectHandle
    )
/*++

Routine Description:

    This routine is a wrapper for the Lsa call.  See The comments for
    ImpOpenLsaPolicy for details.                                                              

--*/
{

    NTSTATUS Status = STATUS_SUCCESS;
    BOOL fSuccess;

    fSuccess = ImpersonateLoggedOnUser( CallerToken );
    if ( fSuccess ) {

        Status = LsaDelete( ObjectHandle );

        fSuccess = RevertToSelf();
        ASSERT( fSuccess );

    } else {

        DsRolepLogPrint(( DEB_TRACE,
                          "Failed to impersonate caller, error %lu\n",
                          GetLastError() ));

        //
        // We couldn't impersonate?
        //
        Status = STATUS_ACCESS_DENIED;
    }

    return Status;

}

NTSTATUS
ImpLsaQueryInformationPolicy(
    IN HANDLE CallerToken,
    IN LSA_HANDLE PolicyHandle,
    IN POLICY_INFORMATION_CLASS InformationClass,
    OUT PVOID *Buffer
    )
/*++

Routine Description:

    This routine is a wrapper for the Lsa call.  See The comments for
    ImpOpenLsaPolicy for details.                                                              

--*/
{
    NTSTATUS Status = STATUS_SUCCESS;
    BOOL fSuccess;

    fSuccess = ImpersonateLoggedOnUser( CallerToken );
    if ( fSuccess ) {

        Status = LsaQueryInformationPolicy( PolicyHandle,
                                            InformationClass,
                                            Buffer );

        fSuccess = RevertToSelf();
        ASSERT( fSuccess );
    } else {

        DsRolepLogPrint(( DEB_TRACE,
                          "Failed to impersonate caller, error %lu\n",
                          GetLastError() ));

        //
        // We couldn't impersonate?
        //
        Status = STATUS_ACCESS_DENIED;
    }

    return Status;
}


NTSTATUS
ImpLsaOpenTrustedDomainByName(
    IN HANDLE CallerToken,
    IN LSA_HANDLE PolicyHandle,
    IN PLSA_UNICODE_STRING TrustedDomainName,
    IN ACCESS_MASK DesiredAccess,
    OUT PLSA_HANDLE TrustedDomainHandle
    )
/*++

Routine Description:

    This routine is a wrapper for the Lsa call.  See The comments for
    ImpOpenLsaPolicy for details.                                                              

--*/
{

    NTSTATUS Status = STATUS_SUCCESS;
    BOOL fSuccess;

    fSuccess = ImpersonateLoggedOnUser( CallerToken );
    if ( fSuccess ) {

        Status = LsaOpenTrustedDomainByName( PolicyHandle,
                                             TrustedDomainName,
                                             DesiredAccess,
                                             TrustedDomainHandle );

        fSuccess = RevertToSelf();
        ASSERT( fSuccess );
    } else {

        DsRolepLogPrint(( DEB_TRACE,
                          "Failed to impersonate caller, error %lu\n",
                          GetLastError() ));

        //
        // We couldn't impersonate?
        //
        Status = STATUS_ACCESS_DENIED;
    }

    return Status;

}

NTSTATUS
ImpLsaOpenTrustedDomain(
    IN HANDLE CallerToken,
    IN LSA_HANDLE PolicyHandle,
    IN PSID TrustedDomainSid,
    IN ACCESS_MASK DesiredAccess,
    OUT PLSA_HANDLE TrustedDomainHandle
    )
/*++

Routine Description:

    This routine is a wrapper for the Lsa call.  See The comments for
    ImpOpenLsaPolicy for details.                                                              

--*/
{

    NTSTATUS Status = STATUS_SUCCESS;
    BOOL fSuccess;

    fSuccess = ImpersonateLoggedOnUser( CallerToken );
    if ( fSuccess ) {

        Status = LsaOpenTrustedDomain( PolicyHandle,
                                       TrustedDomainSid,
                                       DesiredAccess,
                                       TrustedDomainHandle );

        fSuccess = RevertToSelf();
        ASSERT( fSuccess );
    } else {

        DsRolepLogPrint(( DEB_TRACE,
                          "Failed to impersonate caller, error %lu\n",
                          GetLastError() ));

        //
        // We couldn't impersonate?
        //
        Status = STATUS_ACCESS_DENIED;
    }

    return Status;

}


NTSTATUS
ImpLsaCreateTrustedDomainEx(
    IN HANDLE CallerToken,
    IN LSA_HANDLE PolicyHandle,
    IN PTRUSTED_DOMAIN_INFORMATION_EX TrustedDomainInformation,
    IN PTRUSTED_DOMAIN_AUTH_INFORMATION AuthenticationInformation,
    IN ACCESS_MASK DesiredAccess,
    OUT PLSA_HANDLE TrustedDomainHandle
    )
/*++

Routine Description:

    This routine is a wrapper for the Lsa call.  See The comments for
    ImpOpenLsaPolicy for details.                                                              

--*/
{

    NTSTATUS Status = STATUS_SUCCESS;
    BOOL fSuccess;

    fSuccess = ImpersonateLoggedOnUser( CallerToken );
    if ( fSuccess ) {

        Status = LsaCreateTrustedDomainEx( PolicyHandle,
                                           TrustedDomainInformation,
                                           AuthenticationInformation,
                                           DesiredAccess,
                                           TrustedDomainHandle );

        fSuccess = RevertToSelf();
        ASSERT( fSuccess );
    } else {

        DsRolepLogPrint(( DEB_TRACE,
                          "Failed to impersonate caller, error %lu\n",
                          GetLastError() ));

        //
        // We couldn't impersonate?
        //
        Status = STATUS_ACCESS_DENIED;
    }

    return Status;

}

NTSTATUS
ImpLsaQueryTrustedDomainInfoByName(
    IN HANDLE CallerToken,
    IN LSA_HANDLE PolicyHandle,
    IN PLSA_UNICODE_STRING TrustedDomainName,
    IN TRUSTED_INFORMATION_CLASS InformationClass,
    OUT PVOID *Buffer
    )
/*++

Routine Description:

    This routine is a wrapper for the Lsa call.  See The comments for
    ImpOpenLsaPolicy for details.                                                              

--*/
{

    NTSTATUS Status = STATUS_SUCCESS;
    BOOL fSuccess;

    fSuccess = ImpersonateLoggedOnUser( CallerToken );
    if ( fSuccess ) {

        Status = LsaQueryTrustedDomainInfoByName( PolicyHandle,
                                                  TrustedDomainName,
                                                  InformationClass,
                                                  Buffer );

        fSuccess = RevertToSelf();
        ASSERT( fSuccess );
    } else {

        DsRolepLogPrint(( DEB_TRACE,
                          "Failed to impersonate caller, error %lu\n",
                          GetLastError() ));

        //
        // We couldn't impersonate?
        //
        Status = STATUS_ACCESS_DENIED;
    }

    return Status;

}


NTSTATUS
ImpLsaQueryDomainInformationPolicy(
    IN HANDLE CallerToken,
    IN LSA_HANDLE PolicyHandle,
    IN POLICY_DOMAIN_INFORMATION_CLASS InformationClass,
    OUT PVOID *Buffer
    )
/*++

Routine Description:

    This routine is a wrapper for the Lsa call.  See The comments for
    ImpOpenLsaPolicy for details.                                                              

--*/
{

    NTSTATUS Status = STATUS_SUCCESS;
    BOOL fSuccess;

    fSuccess = ImpersonateLoggedOnUser( CallerToken );
    if ( fSuccess ) {

        Status = LsaQueryDomainInformationPolicy( PolicyHandle,
                                                  InformationClass,
                                                  Buffer );

        fSuccess = RevertToSelf();
        ASSERT( fSuccess );
    } else {

        DsRolepLogPrint(( DEB_TRACE,
                          "Failed to impersonate caller, error %lu\n",
                          GetLastError() ));

        //
        // We couldn't impersonate?
        //
        Status = STATUS_ACCESS_DENIED;
    }

    return Status;

}

NTSTATUS
ImpLsaClose(
    IN HANDLE CallerToken,
    IN LSA_HANDLE ObjectHandle
    )

/*++

Routine Description:

    This routine is a wrapper for the Lsa call.  See The comments for
    ImpOpenLsaPolicy for details.                                                              

--*/
{

    NTSTATUS Status = STATUS_SUCCESS;
    BOOL fSuccess;

    fSuccess = ImpersonateLoggedOnUser( CallerToken );
    if ( fSuccess ) {

        Status = LsaClose( ObjectHandle );

        fSuccess = RevertToSelf();
        ASSERT( fSuccess );
    } else {

        DsRolepLogPrint(( DEB_TRACE,
                          "Failed to impersonate caller, error %lu\n",
                          GetLastError() ));

        //
        // We couldn't impersonate?
        //
        Status = STATUS_ACCESS_DENIED;
    }

    return Status;

}