windows-nt/Source/XPSP1/NT/ds/security/dsrole/server/setutl.c
2020-09-26 16:20:57 +08:00

2096 lines
48 KiB
C

/*++
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;
}