|
|
/*++
Copyright (c) 1995 Microsoft Corporation
Module Name:
repl.c
Abstract:
Author:
Arthur Hanson (arth) 06-Jan-1995
Revision History:
Jeff Parham (jeffparh) 05-Dec-1995 o Added replication of certificate database and secure service list. o Log failure to connect during replication only if the target server is running a build in which license server should be available (i.e., 1057 (3.51) or greater). If the target server does not support license server, log a message to that effect only once.
--*/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <dsgetdc.h>
#include "debug.h"
#include "llsutil.h"
#include <llsapi.h>
#include "llssrv.h"
#include "mapping.h"
#include "msvctbl.h"
#include "svctbl.h"
#include "perseat.h"
#include "server.h"
#include "repl.h"
#include "llsrpc_s.h"
#include "pack.h"
#include "llsevent.h"
#include "certdb.h"
#include "registry.h"
#include <strsafe.h> //include last
HANDLE ReplicationEvent;
volatile HANDLE LlsRPCHandle = NULL; // Make sure it's reread after we enter critsec
PLLS_REPL_CONNECT_W pLlsReplConnect = NULL; PLLS_REPL_CLOSE pLlsReplClose = NULL; PLLS_FREE_MEMORY pLlsFreeMemory = NULL;
PLLS_REPLICATION_REQUEST_W pLlsReplicationRequestW = NULL; PLLS_REPLICATION_SERVER_ADD_W pLlsReplicationServerAddW = NULL; PLLS_REPLICATION_SERVER_SERVICE_ADD_W pLlsReplicationServerServiceAddW = NULL; PLLS_REPLICATION_SERVICE_ADD_W pLlsReplicationServiceAddW = NULL; PLLS_REPLICATION_USER_ADD_W pLlsReplicationUserAddW = NULL;
PLLS_CAPABILITY_IS_SUPPORTED pLlsCapabilityIsSupported = NULL; PLLS_REPLICATION_CERT_DB_ADD_W pLlsReplicationCertDbAddW = NULL; PLLS_REPLICATION_PRODUCT_SECURITY_ADD_W pLlsReplicationProductSecurityAddW = NULL; PLLS_REPLICATION_USER_ADD_EX_W pLlsReplicationUserAddExW = NULL; PLLS_CONNECT_W pLlsConnectW = NULL; PLLS_CLOSE pLlsClose = NULL; PLLS_LICENSE_ADD_W pLlsLicenseAddW = NULL;
NTSTATUS ReplicationInitDelayed () { NTSTATUS Status = NOERROR;
if (LlsRPCHandle != NULL) { return NOERROR; }
RtlEnterCriticalSection(&g_csLock);
if (LlsRPCHandle == NULL) { //
// Open up our RPC DLL and init our function references.
//
LlsRPCHandle = LoadLibrary(TEXT("LLSRPC.DLL"));
if (LlsRPCHandle != NULL) { pLlsReplConnect = (PLLS_REPL_CONNECT_W)GetProcAddress(LlsRPCHandle, ("LlsReplConnectW")); pLlsReplClose = (PLLS_REPL_CLOSE)GetProcAddress(LlsRPCHandle, ("LlsReplClose")); pLlsFreeMemory = (PLLS_FREE_MEMORY)GetProcAddress(LlsRPCHandle, ("LlsFreeMemory")); pLlsReplicationRequestW = (PLLS_REPLICATION_REQUEST_W)GetProcAddress(LlsRPCHandle, ("LlsReplicationRequestW")); pLlsReplicationServerAddW = (PLLS_REPLICATION_SERVER_ADD_W)GetProcAddress(LlsRPCHandle, ("LlsReplicationServerAddW")); pLlsReplicationServerServiceAddW = (PLLS_REPLICATION_SERVER_SERVICE_ADD_W)GetProcAddress(LlsRPCHandle, ("LlsReplicationServerServiceAddW")); pLlsReplicationServiceAddW = (PLLS_REPLICATION_SERVICE_ADD_W)GetProcAddress(LlsRPCHandle, ("LlsReplicationServiceAddW")); pLlsReplicationUserAddW = (PLLS_REPLICATION_USER_ADD_W)GetProcAddress(LlsRPCHandle, ("LlsReplicationUserAddW")); pLlsReplicationCertDbAddW = (PLLS_REPLICATION_CERT_DB_ADD_W) GetProcAddress(LlsRPCHandle, ("LlsReplicationCertDbAddW")); pLlsReplicationProductSecurityAddW = (PLLS_REPLICATION_PRODUCT_SECURITY_ADD_W) GetProcAddress(LlsRPCHandle, ("LlsReplicationProductSecurityAddW")); pLlsReplicationUserAddExW = (PLLS_REPLICATION_USER_ADD_EX_W) GetProcAddress(LlsRPCHandle, ("LlsReplicationUserAddExW")); pLlsCapabilityIsSupported = (PLLS_CAPABILITY_IS_SUPPORTED) GetProcAddress(LlsRPCHandle, ("LlsCapabilityIsSupported")); pLlsConnectW = (PLLS_CONNECT_W) GetProcAddress(LlsRPCHandle, ("LlsConnectW")); pLlsClose = (PLLS_CLOSE) GetProcAddress(LlsRPCHandle, ("LlsClose")); pLlsLicenseAddW = (PLLS_LICENSE_ADD_W) GetProcAddress(LlsRPCHandle, ("LlsLicenseAddW")); if ((pLlsReplConnect == NULL) || (pLlsReplClose == NULL) || (pLlsFreeMemory == NULL) || (pLlsReplicationRequestW == NULL) || (pLlsReplicationServerAddW == NULL) || (pLlsReplicationServerServiceAddW == NULL) || (pLlsReplicationServiceAddW == NULL) || (pLlsReplicationUserAddW == NULL) || (pLlsReplicationCertDbAddW == NULL) || (pLlsReplicationProductSecurityAddW == NULL) || (pLlsReplicationUserAddExW == NULL) || (pLlsCapabilityIsSupported == NULL) || (pLlsConnectW == NULL) || (pLlsClose == NULL) || (pLlsLicenseAddW == NULL)) { Status = STATUS_INTERNAL_ERROR; }
} else { Status = GetLastError(); } }
RtlLeaveCriticalSection(&g_csLock);
return Status; }
/////////////////////////////////////////////////////////////////////////
NTSTATUS ReplicationInit ( )
/*++
Routine Description:
Arguments:
Return Value:
--*/
{ DWORD Ignore; HANDLE Thread; NTSTATUS Status;
#if DBG
if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_REPLICATION)) dprintf(TEXT("LLS TRACE: ReplicationInit\n")); #endif
//
// Create the Replication Management event
//
Status = NtCreateEvent( &ReplicationEvent, EVENT_QUERY_STATE | EVENT_MODIFY_STATE | SYNCHRONIZE, NULL, SynchronizationEvent, FALSE );
ASSERT(NT_SUCCESS(Status));
//
// Fire off the thread to watch for replication.
//
Thread = CreateThread( NULL, 0L, (LPTHREAD_START_ROUTINE) ReplicationManager, 0L, 0L, &Ignore );
if (NULL != Thread) CloseHandle(Thread);
return STATUS_SUCCESS; } // ReplicationInit
/////////////////////////////////////////////////////////////////////////
NTSTATUS ReplicationDo ( LLS_HANDLE LlsHandle, LLS_REPL_HANDLE ReplHandle, DWORD LastReplicated )
/*++
Routine Description:
Arguments:
Return Value:
--*/
{ NTSTATUS Status; PREPL_SERVICE_RECORD Services = NULL; ULONG ServicesTotalRecords = 0; PREPL_SERVER_RECORD Servers = NULL; ULONG ServersTotalRecords = 0; PREPL_SERVER_SERVICE_RECORD ServerServices = NULL; ULONG ServerServicesTotalRecords = 0;
REPL_CERTIFICATE_DB_0 CertificateDB; REPL_PRODUCT_SECURITY_0 ProductSecurity;
DWORD UserLevel = 0; REPL_USER_RECORD_CONTAINER UserDB;
#if DBG
if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_REPLICATION)) dprintf(TEXT("LLS TRACE: ReplicationDo\n")); #endif
//
// Pack all of our data into linear / self-relative buffers so we
// can send them over.
//
ZeroMemory( &UserDB, sizeof( UserDB ) ); ZeroMemory( &CertificateDB, sizeof( CertificateDB ) ); ZeroMemory( &ProductSecurity, sizeof( ProductSecurity ) );
if ( (*pLlsCapabilityIsSupported)( LlsHandle, LLS_CAPABILITY_REPLICATE_USERS_EX ) ) { UserLevel = 1; Status = PackAll( LastReplicated, &ServicesTotalRecords, &Services, &ServersTotalRecords, &Servers, &ServerServicesTotalRecords, &ServerServices, 1, &UserDB.Level1.NumUsers, &UserDB.Level1.Users ); if (Status != STATUS_SUCCESS) goto ReplicationDoExit; } else { UserLevel = 0; Status = PackAll( LastReplicated, &ServicesTotalRecords, &Services, &ServersTotalRecords, &Servers, &ServerServicesTotalRecords, &ServerServices, 0, &UserDB.Level0.NumUsers, &UserDB.Level0.Users ); if (Status != STATUS_SUCCESS) goto ReplicationDoExit; }
if ( (*pLlsCapabilityIsSupported)( LlsHandle, LLS_CAPABILITY_REPLICATE_CERT_DB ) ) { Status = CertDbPack( &CertificateDB.StringSize, &CertificateDB.Strings, &CertificateDB.HeaderContainer.Level0.NumHeaders, &CertificateDB.HeaderContainer.Level0.Headers, &CertificateDB.ClaimContainer.Level0.NumClaims, &CertificateDB.ClaimContainer.Level0.Claims ); if (Status != STATUS_SUCCESS) goto ReplicationDoExit; }
if ( (*pLlsCapabilityIsSupported)( LlsHandle, LLS_CAPABILITY_REPLICATE_PRODUCT_SECURITY ) ) { Status = ProductSecurityPack( &ProductSecurity.StringSize, &ProductSecurity.Strings ); if (Status != STATUS_SUCCESS) goto ReplicationDoExit; }
//
// Transmit...
//
Status = (*pLlsReplicationServiceAddW) ( ReplHandle, ServicesTotalRecords, Services ); if (Status != STATUS_SUCCESS) goto ReplicationDoExit;
Status = (*pLlsReplicationServerAddW) ( ReplHandle, ServersTotalRecords, Servers ); if (Status != STATUS_SUCCESS) goto ReplicationDoExit;
Status = (*pLlsReplicationServerServiceAddW) ( ReplHandle, ServerServicesTotalRecords, ServerServices ); if (Status != STATUS_SUCCESS) goto ReplicationDoExit;
if ( (*pLlsCapabilityIsSupported)( LlsHandle, LLS_CAPABILITY_REPLICATE_USERS_EX ) ) { Status = (*pLlsReplicationUserAddExW)( ReplHandle, UserLevel, &UserDB ); if (Status != STATUS_SUCCESS) goto ReplicationDoExit; } else { Status = (*pLlsReplicationUserAddW) ( ReplHandle, UserDB.Level0.NumUsers, UserDB.Level0.Users ); if (Status != STATUS_SUCCESS) goto ReplicationDoExit; }
if ( (*pLlsCapabilityIsSupported)( LlsHandle, LLS_CAPABILITY_REPLICATE_CERT_DB ) ) { Status = (*pLlsReplicationCertDbAddW)( ReplHandle, 0, &CertificateDB ); if (Status != STATUS_SUCCESS) goto ReplicationDoExit; }
if ( (*pLlsCapabilityIsSupported)( LlsHandle, LLS_CAPABILITY_REPLICATE_PRODUCT_SECURITY ) ) { Status = (*pLlsReplicationProductSecurityAddW)( ReplHandle, 0, &ProductSecurity ); }
ReplicationDoExit: if (Status != STATUS_SUCCESS) { #if DBG
dprintf(TEXT("LLS Replication ABORT: 0x%lX\n"), Status); #endif
}
if (Services != NULL) MIDL_user_free(Services);
if (Servers != NULL) MIDL_user_free(Servers);
if ( 0 == UserLevel ) { if (UserDB.Level0.Users != NULL) MIDL_user_free(UserDB.Level0.Users); } else { if (UserDB.Level1.Users != NULL) MIDL_user_free(UserDB.Level1.Users); }
if (CertificateDB.Strings != NULL) MIDL_user_free(CertificateDB.Strings);
if (CertificateDB.HeaderContainer.Level0.Headers != NULL) MIDL_user_free(CertificateDB.HeaderContainer.Level0.Headers);
if (CertificateDB.ClaimContainer.Level0.Claims != NULL) MIDL_user_free(CertificateDB.ClaimContainer.Level0.Claims);
if (ProductSecurity.Strings != NULL) MIDL_user_free(ProductSecurity.Strings);
#if DBG
if (TraceFlags & TRACE_REPLICATION) dprintf(TEXT(" LLS Replication Finished\n")); #endif
return Status;
} // ReplicationDo
/////////////////////////////////////////////////////////////////////////
VOID ReplicationManager ( IN PVOID ThreadParameter )
/*++
Routine Description:
Arguments:
ThreadParameter - Not used.
Return Value:
This thread never exits.
--*/
{ NTSTATUS Status; LLS_REPL_HANDLE ReplHandle = NULL; LLS_HANDLE LlsHandle = NULL; PREPL_REQUEST pReplInfo; TCHAR ReplicateTo[MAX_PATH + 1]; DWORD LastReplicated; LPTSTR pReplicateTo = ReplicateTo; TCHAR LastFailedConnectionDownlevelReplicateTo[MAX_PATH + 1] = TEXT(""); BOOL Replicate; HRESULT hr;
UNREFERENCED_PARAMETER(ThreadParameter);
#if DBG
if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_REPLICATION)) dprintf(TEXT("LLS TRACE: ReplicationManager\n")); #endif
//
// Loop forever waiting to be given the opportunity to serve the
// greater good.
//
for ( ; ; ) { //
// Wait to be notified that there is work to be done
//
Status = NtWaitForSingleObject( ReplicationEvent, TRUE, NULL );
#if DELAY_INITIALIZATION
EnsureInitialized(); #endif
// Delayed LoadLibrary
Status = ReplicationInitDelayed();
if (NOERROR != Status) { //
// Can't replicate
//
break; }
// Update the replication-related ConfigInfo information.
//
ConfigInfoUpdate(NULL,TRUE);
RtlEnterCriticalSection(&ConfigInfoLock); Replicate = ConfigInfo.Replicate; RtlLeaveCriticalSection(&ConfigInfoLock);
if (Replicate) { //
// So they said, go replicate my son... Yeah, but first we must ask
// the master for permission.
//
//
// Construct our repl record
//
pReplInfo = MIDL_user_allocate(sizeof(REPL_REQUEST)); ASSERT(pReplInfo != NULL); if (pReplInfo != NULL) { ReplicateTo[0] = 0; pReplInfo->EnterpriseServer[0] = 0;
RtlEnterCriticalSection(&ConfigInfoLock); if (ConfigInfo.ReplicateTo != NULL) { lstrcpyn(ReplicateTo, ConfigInfo.ReplicateTo, min(MAX_PATH, lstrlen(ConfigInfo.ReplicateTo) + 1)); }
if (ConfigInfo.EnterpriseServer != NULL) { hr = StringCbCopy(pReplInfo->EnterpriseServer, sizeof(pReplInfo->EnterpriseServer), ConfigInfo.EnterpriseServer); ASSERT(SUCCEEDED(hr)); } pReplInfo->EnterpriseServerDate = ConfigInfo.EnterpriseServerDate;
pReplInfo->LastReplicated = ConfigInfo.LastReplicatedSeconds; pReplInfo->CurrentTime = LastUsedTime; pReplInfo->NumberServices = 0; pReplInfo->NumberUsers = 0;
pReplInfo->ReplSize = MAX_REPL_SIZE;
pReplInfo->Backoff = 0; RtlLeaveCriticalSection(&ConfigInfoLock);
#if DBG
if (TraceFlags & TRACE_REPLICATION) dprintf(TEXT("LLS Starting Replication to: %s @ %s\n"), ReplicateTo, TimeToString(pReplInfo->CurrentTime)); #endif
Status = (*pLlsReplConnect) ( ReplicateTo, &ReplHandle );
if ( STATUS_SUCCESS != Status ) { #if DBG
dprintf(TEXT("LLS Error: LlsReplConnect failed: 0x%lX\n"), Status); #endif
ReplHandle = NULL; } else { Status = (*pLlsConnectW)( ReplicateTo, &LlsHandle );
if ( STATUS_SUCCESS != Status ) { #if DBG
dprintf(TEXT("LLS Error: LlsConnectW failed: 0x%lX\n"), Status); #endif
LlsHandle = NULL; } }
if (Status != STATUS_SUCCESS) { DWORD dwWinError; DWORD dwBuildNumber;
dwWinError = WinNtBuildNumberGet( ReplicateTo, &dwBuildNumber );
if ( (ERROR_SUCCESS == dwWinError) && (dwBuildNumber < 1057L) ) { // the ReplicateTo machine does not support the license
// service
if ( lstrcmpi(ReplicateTo, LastFailedConnectionDownlevelReplicateTo ) ) { hr = StringCbCopy( LastFailedConnectionDownlevelReplicateTo, sizeof(LastFailedConnectionDownlevelReplicateTo), ReplicateTo ); ASSERT(SUCCEEDED(hr));
LogEvent( LLS_EVENT_REPL_DOWNLEVEL_TARGET, 1, &pReplicateTo, Status ); } } else { // the ReplicateTo machine should be running the license
// service
*LastFailedConnectionDownlevelReplicateTo = TEXT( '\0' );
ThrottleLogEvent( LLS_EVENT_REPL_NO_CONNECTION, 1, &pReplicateTo, Status ); } } else { *LastFailedConnectionDownlevelReplicateTo = TEXT( '\0' );
Status = (*pLlsReplicationRequestW) ( ReplHandle, REPL_VERSION, pReplInfo );
if (Status != STATUS_SUCCESS) { LogEvent( LLS_EVENT_REPL_REQUEST_FAILED, 1, &pReplicateTo, Status ); } else { RtlEnterCriticalSection(&ConfigInfoLock); #ifdef DISABLED_FOR_NT5
//swi, code review, ConfigInfo.EnterpriseServer is hard code allocated from heap for sizeof MAX_COMPUTERNAME_LENGTH+3 chars. This seems a strange allocation. It should dynamically changed whenever it needs to be refilled.
hr = StringCchCopy(ConfigInfo.EnterpriseServer, MAX_COMPUTERNAME_LENGTH+3, pReplInfo->EnterpriseServer); ASSERT(SUCCEEDED(hr)); #endif // DISABLED_FOR_NT5
ConfigInfo.EnterpriseServerDate = pReplInfo->EnterpriseServerDate; ConfigInfo.IsReplicating = TRUE; LastReplicated = pReplInfo->LastReplicated;
//
// And lo, thou may proceed...
//
if (pReplInfo->Backoff == 0) { if ( ConfigInfo.LogLevel ) { LogEvent( LLS_EVENT_REPL_START, 1, &pReplicateTo, ERROR_SUCCESS ); }
Status = ReplicationDo( LlsHandle, ReplHandle, LastReplicated );
if ( STATUS_SUCCESS != Status ) { LogEvent( LLS_EVENT_REPL_FAILED, 1, &pReplicateTo, Status ); } else if ( ConfigInfo.LogLevel ) { LogEvent( LLS_EVENT_REPL_END, 1, &pReplicateTo, ERROR_SUCCESS ); }
//
// Need to update when next we should replicate
//
ConfigInfo.LastReplicatedSeconds = DateSystemGet(); GetLocalTime(&ConfigInfo.LastReplicated); ReplicationTimeSet(); } else { LogEvent( LLS_EVENT_REPL_BACKOFF, 1, &pReplicateTo, ERROR_SUCCESS ); }
ConfigInfo.IsReplicating = FALSE; RtlLeaveCriticalSection(&ConfigInfoLock); } }
//
// Disconnect from Master Server
//
if ( NULL != LlsHandle ) { (*pLlsClose)( LlsHandle ); LlsHandle = NULL; }
if ( NULL != ReplHandle ) { Status = (*pLlsReplClose) ( &ReplHandle ); if ((STATUS_SUCCESS != Status) && (NULL != ReplHandle)) { RpcSmDestroyClientContext( &ReplHandle );
ReplHandle = NULL; } }
MIDL_user_free( pReplInfo ); } } }
} // ReplicationManager
/////////////////////////////////////////////////////////////////////////
VOID ReplicationTimeSet ( )
/*++
Routine Description:
Arguments:
Return Value:
--*/
{ DWORD CurrTime, ReplTime, Time;
#if DBG
if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_REPLICATION)) dprintf(TEXT("LLS TRACE: ReplicationTimeSet\n")); #endif
ReplTime = Time = 0;
//
// Figure out what new time to set it to
//
if (!ConfigInfo.Replicate) return;
//
// If REPLICATE_DELTA it is easy as we just take the delta and apply it to
// the last replication time. Otherwise we have to convert the time from
// midnight.
//
//
// Figure out how long since we last replicated
//
ReplTime = ConfigInfo.ReplicationTime;
if (ConfigInfo.ReplicationType == REPLICATE_DELTA) { Time = DateSystemGet() - ConfigInfo.LastReplicatedSeconds;
//
// If we have already gone past when we should replicate then schedule
// one real soon now (10 minutes).
//
if (Time > ReplTime) Time = 10 * 60; else Time = ReplTime - Time;
Time += DateLocalGet(); } else { //
// Need to adjust time to midnight - do this by MOD of seconds
// per day.
//
CurrTime = DateLocalGet(); Time = CurrTime - ((CurrTime / (60 * 60 * 24)) * (60 * 60 * 24)); CurrTime = CurrTime - Time;
//
// Time = seconds past midnight.
// CurrTime = Todays @ 12:00 AM
// Figure out if we are past the replication time, if so schedule it
// for tomorrow, else today.
//
if (Time > ReplTime) Time = CurrTime + (60 * 60 * 24) + ReplTime; else Time = CurrTime + ReplTime;
}
ConfigInfo.NextReplication = Time;
} // ReplicationTimeSet
|