/****************************************************************************
PROGRAM: LSA.C
PURPOSE: Utility routines that access the LSA.
****************************************************************************/
#include "msgina.h"
// #define DEBUG_LSA
#ifdef DEBUG_LSA
#define VerbosePrint(s) WLPrint(s)
#else
#define VerbosePrint(s)
#endif
/***************************************************************************\
* OpenLsaOnController
*
* Purpose : Attempts to open the Lsa on the specified domain controller.
* If the open is successful, this routine checks that the
* controller is still a controller for the specified domain
*
* Notes: If the controller name is NULL, the local Lsa is opened.
*
* Returns : TRUE on success, FALSE on failure.
*
* Notes: Desired access must include POLICY_VIEW_LOCAL_INFO
*
* History:
* 11-03-92 Davidc Created.
\***************************************************************************/
BOOL
OpenLsaOnController(
PUNICODE_STRING ControllerName OPTIONAL,
ACCESS_MASK DesiredAccess,
PUNICODE_STRING PrimaryDomainName,
PLSA_HANDLE LsaHandle
)
{
NTSTATUS Status, IgnoreStatus;
OBJECT_ATTRIBUTES ObjectAttributes;
SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
PPOLICY_PRIMARY_DOMAIN_INFO PrimaryDomainInfo;
PPOLICY_ACCOUNT_DOMAIN_INFO AccountDomainInfo;
BOOL Success = FALSE;
//
// Attempt to open the Lsa on the controller
//
SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation;
SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
SecurityQualityOfService.EffectiveOnly = FALSE;
InitializeObjectAttributes(&ObjectAttributes, NULL, 0L, NULL, NULL);
ObjectAttributes.SecurityQualityOfService = &SecurityQualityOfService;
Status = LsaOpenPolicy(
ControllerName,
&ObjectAttributes,
DesiredAccess,
LsaHandle
);
if (!NT_SUCCESS(Status)) {
DebugLog((DEB_TRACE, "Failed to open lsa on <%wZ>, status = 0x%lx\n", ControllerName, Status));
return(FALSE);
}
//
// Check the controller is actually in this domain
//
ASSERT(DesiredAccess & POLICY_VIEW_LOCAL_INFORMATION);
Status = LsaQueryInformationPolicy(*LsaHandle,
PolicyPrimaryDomainInformation,
(PVOID *)&PrimaryDomainInfo);
if (!NT_SUCCESS(Status)) {
DebugLog((DEB_TRACE, "Failed to query primary domain from Lsa on <%wZ>, Status = 0x%lx\n", ControllerName, Status));
} else {
//
// Check the controller is in our domain
//
Success = RtlEqualUnicodeString(PrimaryDomainName,
&PrimaryDomainInfo->Name,
TRUE // Case insensitive
);
if (!Success) {
DebugLog((DEB_TRACE, "Controller <%wZ> is not in our domain <%wZ>, ignoring it\n", ControllerName, PrimaryDomainName));
}
if (Success) {
//
// Computer is in the domain, is it still a DC?
//
Status = LsaQueryInformationPolicy(*LsaHandle,
PolicyAccountDomainInformation,
(PVOID *)&AccountDomainInfo);
if ( NT_SUCCESS( Status )) {
Success = RtlEqualUnicodeString(&AccountDomainInfo->DomainName,
&PrimaryDomainInfo->Name,
TRUE // Case insensitive
);
IgnoreStatus = LsaFreeMemory(AccountDomainInfo);
ASSERT(NT_SUCCESS(IgnoreStatus));
} else {
Success = FALSE;
}
}
//
// Free up returned Lsa structure
//
IgnoreStatus = LsaFreeMemory(PrimaryDomainInfo);
ASSERT(NT_SUCCESS(IgnoreStatus));
}
//
// Clean up the Lsa handle on failure
//
if (!Success) {
//
// The following call may fail if RPC has invalidated the handle
//
IgnoreStatus = LsaClose(*LsaHandle);
}
return(Success);
}
/***************************************************************************\
* OpenLsaOnDomain
*
* Purpose : Opens the Lsa on a domain controller in the domain.
*
* Notes: On successful return, the caller should free the ControllerName
* using RtlFreeUnicodeString()
*
* Returns : ThreadExitCode
*
* History:
* 11-12-92 Davidc Created.
\***************************************************************************/
BOOL
OpenLsaOnDomain(
PUNICODE_STRING PrimaryDomainName IN,
ACCESS_MASK DesiredAccess IN,
PUNICODE_STRING SuggestedControllerName IN OPTIONAL,
PUNICODE_STRING ControllerName OUT,
PLSA_HANDLE ControllerHandle OUT
)
{
NT_PRODUCT_TYPE NtProductType;
//
// Find out what product we are installed as
// This always defaults to something useful even on failure
//
RtlGetNtProductType(&NtProductType);
//
// Prepare for failure
//
*ControllerHandle = NULL;
RtlInitUnicodeString(ControllerName, NULL);
if (!IsWorkstation(NtProductType)) {
//
// LanmanNT machine - controller is local machine
//
if (!OpenLsaOnController( NULL,
DesiredAccess,
PrimaryDomainName,
ControllerHandle)) {
DebugLog((DEB_TRACE, "Failed to open local lsa, desired access = 0x%lx\n", DesiredAccess));
*ControllerHandle = NULL;
return(FALSE);
}
return(TRUE);
} else {
//
// WinNT machine - try the suggested controller, if that fails
// use I_NetGetDCList to get controller list and try each one
// until we open the Lsa successfully.
//
DWORD Error;
LPBYTE DCNameBuffer;
BOOLEAN Result;
UNICODE_STRING DCName;
//
// Wait for the network to start
//
if (!WaitForNetworkToStart(SERVICE_NETLOGON)) {
DebugLog((DEB_ERROR, "Failed to wait for network to start\n"));
return(FALSE);
}
//
// Try suggested controller
// Don't bother if it's the local LSA
//
if ( SuggestedControllerName &&
(SuggestedControllerName->Buffer != NULL) &&
(SuggestedControllerName->Length != 0) ) {
if (OpenLsaOnController( SuggestedControllerName,
DesiredAccess,
PrimaryDomainName,
ControllerHandle)) {
//
// Success - the suggested controller came up trumps
//
if (!DuplicateUnicodeString(ControllerName,
SuggestedControllerName)) {
RtlInitUnicodeString(ControllerName, NULL);
}
DebugLog((DEB_TRACE, "Successfully opened Lsa on suggested controller\n"));
return(TRUE);
} else {
DebugLog((DEB_TRACE, "Failed to open Lsa on suggested controller <%wZ>, trying other controllers\n", SuggestedControllerName));
*ControllerHandle = NULL;
}
}
//
// Go get the list of domain controllers for the domain
//
ASSERT(PrimaryDomainName->Length < PrimaryDomainName->MaximumLength);
PrimaryDomainName->Buffer[ PrimaryDomainName->Length/
sizeof(*(PrimaryDomainName->Buffer)) ] = 0;
Error = NetGetAnyDCName (
NULL,
PrimaryDomainName->Buffer,
&DCNameBuffer
);
if (Error != ERROR_SUCCESS) {
DebugLog((DEB_ERROR, "NetGetAnyDCName failed, error = %d\n", Error));
return(FALSE);
}
RtlInitUnicodeString( &DCName, (PCWSTR)DCNameBuffer );
//
// Attempt to open the LSA for one of the controllers on the list.
// For now, just scan the list from the end backwards. Later,
// use a more random method. The active Domain Controller is
// at the beginning of the list.
//
DebugLog((DEB_TRACE, "Trying to open Lsa on controller <%wZ>\n", &DCName));
Result = OpenLsaOnController( &DCName,
DesiredAccess,
PrimaryDomainName,
ControllerHandle);
if ( Result ) {
if (!DuplicateUnicodeString(ControllerName,
&DCName)) {
RtlInitUnicodeString(ControllerName, NULL);
}
} else {
DebugLog((DEB_TRACE, "Unable to open Lsa on controller <%s>\n", DCNameBuffer));
}
Error = NetApiBufferFree(DCNameBuffer);
ASSERT( Error == NO_ERROR );
return( Result );
}
}
/***************************************************************************\
* GetPrimaryDomain
*
* Purpose : Returns the primary domain name for authentication
*
* Returns : TRUE if primary domain exists and returned, otherwise FALSE
*
* The primary domain name should be freed using RtlFreeUnicodeString().
* The primary domain sid should be freed using Free()
*
* History:
* 02-13-92 Davidc Created.
\***************************************************************************/
BOOL
GetPrimaryDomain(
PUNICODE_STRING PrimaryDomainName,
PSID *PrimaryDomainSid OPTIONAL
)
{
NTSTATUS Status, IgnoreStatus;
OBJECT_ATTRIBUTES ObjectAttributes;
LSA_HANDLE LsaHandle;
SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
PPOLICY_PRIMARY_DOMAIN_INFO PrimaryDomainInfo;
BOOL PrimaryDomainPresent = FALSE;
//
// Set up the Security Quality Of Service
//
SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation;
SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
SecurityQualityOfService.EffectiveOnly = FALSE;
//
// Set up the object attributes to open the Lsa policy object
//
InitializeObjectAttributes(&ObjectAttributes,
NULL,
0L,
(HANDLE)NULL,
NULL);
ObjectAttributes.SecurityQualityOfService = &SecurityQualityOfService;
//
// Open the local LSA policy object
//
Status = LsaOpenPolicy( NULL,
&ObjectAttributes,
POLICY_VIEW_LOCAL_INFORMATION,
&LsaHandle
);
if (!NT_SUCCESS(Status)) {
DebugLog((DEB_ERROR, "Failed to open local LsaPolicyObject, Status = 0x%lx\n", Status));
return(FALSE);
}
//
// Get the primary domain info
//
Status = LsaQueryInformationPolicy(LsaHandle,
PolicyPrimaryDomainInformation,
(PVOID *)&PrimaryDomainInfo);
if (!NT_SUCCESS(Status)) {
DebugLog((DEB_ERROR, "Failed to query primary domain from Lsa, Status = 0x%lx\n", Status));
IgnoreStatus = LsaClose(LsaHandle);
ASSERT(NT_SUCCESS(IgnoreStatus));
return(FALSE);
}
//
// Copy the primary domain name into the return string
//
if (PrimaryDomainInfo->Sid != NULL) {
PrimaryDomainPresent = TRUE;
if (PrimaryDomainName)
{
if (DuplicateUnicodeString(PrimaryDomainName, &(PrimaryDomainInfo->Name))) {
if (PrimaryDomainSid != NULL) {
ULONG SidLength = RtlLengthSid(PrimaryDomainInfo->Sid);
*PrimaryDomainSid = Alloc(SidLength);
if (*PrimaryDomainSid != NULL) {
Status = RtlCopySid(SidLength, *PrimaryDomainSid, PrimaryDomainInfo->Sid);
ASSERT(NT_SUCCESS(Status));
} else {
RtlFreeUnicodeString(PrimaryDomainName);
PrimaryDomainPresent = FALSE;
}
}
} else {
PrimaryDomainPresent = FALSE;
}
}
}
//
// We're finished with the Lsa
//
IgnoreStatus = LsaFreeMemory(PrimaryDomainInfo);
ASSERT(NT_SUCCESS(IgnoreStatus));
IgnoreStatus = LsaClose(LsaHandle);
ASSERT(NT_SUCCESS(IgnoreStatus));
return(PrimaryDomainPresent);
}