/////////////////////////////////////////////////////////////////////////////// // // Copyright (c) Microsoft Corp. All rights reserved. // // FILE // // dyninfo.c // // SYNOPSIS // // Defines and initializes global variables containing dynamic configuration // information. // // MODIFICATION HISTORY // // 08/15/1998 Original version. // 03/23/1999 Changes to ezsam API. // /////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include "statinfo.h" #include "ezsam.h" #include "dyninfo.h" #include "iastrace.h" ////////// // The primary domain. ////////// WCHAR thePrimaryDomain[DNLEN + 1]; ////////// // The default domain. ////////// PCWSTR theDefaultDomain; ////////// // The dns domain name. ////////// const LSA_UNICODE_STRING* theDnsDomainName; ////////// // Role of the local machine. ////////// IAS_ROLE ourRole; ////////// // Name of the guest account for the default domain. ////////// WCHAR theGuestAccount[DNLEN + UNLEN + 2]; ////////// // Change event and notification thread. ////////// HANDLE theChangeEvent, theNotificationThread; ////////// // Flag to bring down the notification thread. ////////// BOOL theShutdownFlag; CRITICAL_SECTION critSec; PPOLICY_DNS_DOMAIN_INFO ppdi; /////////////////////////////////////////////////////////////////////////////// // // FUNCTION // // IASQueryPrimaryDomain // // DESCRIPTION // // Reads the primary domain info and determines role of the local computer. // // NOTES // // This method is intentionally not synchronized. This method is // called *very* rarely and worst case a few packets will get // discarded because the new domain name hasn't been updated yet. // /////////////////////////////////////////////////////////////////////////////// DWORD WINAPI IASQueryPrimaryDomain( VOID ) { NTSTATUS status; DWORD result; LSA_HANDLE hLsa; WCHAR accountName[DNLEN + UNLEN + 2], *userName; ULONG guestRid; SAM_HANDLE hGuest; PUSER_ACCOUNT_NAME_INFORMATION uani; PPOLICY_DNS_DOMAIN_INFO newDomainInfo; ////////// // Open a handle to the LSA. ////////// status = LsaOpenPolicy( NULL, &theObjectAttributes, POLICY_VIEW_LOCAL_INFORMATION, &hLsa ); if (!NT_SUCCESS(status)) { goto exit; } EnterCriticalSection(&critSec); ////////// // Get the primary domain information. ////////// newDomainInfo = 0; status = LsaQueryInformationPolicy( hLsa, PolicyDnsDomainInformation, (PVOID*)&newDomainInfo ); if (!NT_SUCCESS(status)) { goto close_lsa; } // We're done with the info buffer. if (ppdi != 0) { LsaFreeMemory(ppdi); } ppdi = newDomainInfo; ////////// // Save the primary domain name and determine our role. ////////// if (ppdi->Sid == NULL) { thePrimaryDomain[0] = L'\0'; // No primary domain, so we must be standalone. ourRole = IAS_ROLE_STANDALONE; IASTraceString("Role: Standalone"); } else { wcsncpy(thePrimaryDomain, ppdi->Name.Buffer, DNLEN); if (RtlEqualSid(ppdi->Sid, theAccountDomainSid)) { // Account domain and primary domain are the same, so we must be DC. ourRole = IAS_ROLE_DC; IASTraceString("Role: Domain Controller"); } else { ourRole = IAS_ROLE_MEMBER; IASTraceString("Role: Domain member"); } } _wcsupr(thePrimaryDomain); IASTracePrintf("Primary domain: %S", thePrimaryDomain); theDnsDomainName = &(ppdi->DnsDomainName); if (theDnsDomainName->Buffer != NULL) { IASTracePrintf("Dns Domain name: %S", theDnsDomainName->Buffer); } ////////// // Determine the default domain. ////////// if (ourProductType == IAS_PRODUCT_WORKSTATION) { // For Workstation, the default domain is always the local domain. theDefaultDomain = theAccountDomain; } else if (ourRole == IAS_ROLE_STANDALONE) { // For Standalone there's nowhere to go besides local. theDefaultDomain = theAccountDomain; } else if (theRegistryDomain[0] != L'\0') { // For Server, a registry entry always takes precedence. theDefaultDomain = theRegistryDomain; } else { // Everyone else defaults to the primary domain. theDefaultDomain = thePrimaryDomain; } IASTracePrintf("Default domain: %S", theDefaultDomain); ////////// // Now that we know the default domain we can determine the guest account. ////////// wcscpy(accountName, theDefaultDomain); wcscat(accountName, L"\\"); // If we can't read the guest account name, we'll assume it's "Guest". userName = accountName + wcslen(accountName); wcscpy(userName, L"Guest"); guestRid = DOMAIN_USER_RID_GUEST; result = IASSamOpenUser( theDefaultDomain, NULL, USER_READ_GENERAL, 0, &guestRid, NULL, &hGuest ); if (result != ERROR_SUCCESS) { // keep status with a succesfull value // we do not want to prevent IAS from starting because of this error goto set_guest_account; } status = SamQueryInformationUser( hGuest, UserAccountNameInformation, (PVOID*)&uani ); if (!NT_SUCCESS(status)) { goto close_guest; } // Overwrite the default Guest name with the real one. wcsncpy(userName, uani->UserName.Buffer, UNLEN); SamFreeMemory(uani); close_guest: SamCloseHandle(hGuest); set_guest_account: // Copy the local buffer into the global buffer. wcscpy(theGuestAccount, accountName); IASTracePrintf("Guest account: %S", theGuestAccount); // Ignore any errors that occurred reading the guest account. status = NO_ERROR; close_lsa: LeaveCriticalSection(&critSec); LsaClose(hLsa); exit: return RtlNtStatusToDosError(status); } /////////////////////////////////////////////////////////////////////////////// // // FUNCTION // // NotificationProc // // DESCRIPTION // // Entry point for notification thread. // /////////////////////////////////////////////////////////////////////////////// DWORD WINAPI NotificationProc(PVOID lpArg) { DWORD status; while (1) { status = WaitForSingleObject( theChangeEvent, INFINITE ); // If we had an error or the shutdown flag is set, then we'll exit. if ((status != WAIT_OBJECT_0) || theShutdownFlag) { break; } IASTraceString("Received domain name change notification."); // Otherwise, read the new domain info. IASQueryPrimaryDomain(); } return 0; } /////////////////////////////////////////////////////////////////////////////// // // FUNCTION // // IASDynamicInfoInitialize // // DESCRIPTION // // Initializes the dynamic data defined above and creates a thread to // wait for change notifications. // /////////////////////////////////////////////////////////////////////////////// DWORD WINAPI IASDynamicInfoInitialize( VOID ) { DWORD status, threadID; if (!InitializeCriticalSectionAndSpinCount(&critSec, 0x00001000)) { return GetLastError(); } do { ////////// // Read the initial state of the info. ////////// status = IASQueryPrimaryDomain(); if (status != NO_ERROR) { break; } ////////// // Set up the thread that handles dynamic changes. ////////// // Get a notification event. status = NetRegisterDomainNameChangeNotification(&theChangeEvent); if (status != NERR_Success) { break; } // Reset the shutdown flag. theShutdownFlag = FALSE; // Create a thread to wait on the event. theNotificationThread = CreateThread( NULL, 0, NotificationProc, NULL, 0, &threadID ); if (!theNotificationThread) { NetUnregisterDomainNameChangeNotification(theChangeEvent); theChangeEvent = NULL; status = GetLastError(); break; } status = NO_ERROR; } while(FALSE); if (status != NO_ERROR) { DeleteCriticalSection(&critSec); if (ppdi != 0) { LsaFreeMemory(ppdi); ppdi = 0; } } return status; } /////////////////////////////////////////////////////////////////////////////// // // FUNCTION // // IASDynamicInfoShutdown // // DESCRIPTION // // Shuts down the notifation thread. // /////////////////////////////////////////////////////////////////////////////// VOID WINAPI IASDynamicInfoShutdown( VOID ) { // Set the shutdown flag. theShutdownFlag = TRUE; // Bring down the thread. SetEvent(theChangeEvent); WaitForSingleObject( theNotificationThread, INFINITE ); // Unregister the notification. NetUnregisterDomainNameChangeNotification(theChangeEvent); theChangeEvent = NULL; // Close the thread handle. CloseHandle(theNotificationThread); theNotificationThread = NULL; // Critical section not useful anymore DeleteCriticalSection(&critSec); // We're done with the info buffer. if (ppdi != 0) { LsaFreeMemory(ppdi); ppdi = 0; } theDnsDomainName = 0; }