/*++ 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 #include #include #include #include #include "debug.h" #include "llsutil.h" #include #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 //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