windows-nt/Source/XPSP1/NT/base/ntsetup/win95upg/w95upgnt/migmain/domain.c

2077 lines
46 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
domain.c
Abstract:
This code implements a set of linked list data structures used to
resolve domain name lookups. It achieves a similar structure to
memdb but provides the ability to move an item from list to list
efficiently.
Author:
Jim Schmidt (jimschm) 18-Jun-1997
Revision History:
jimschm 23-Sep-1998 Fixed trusted domains
jimschm 17-Feb-1998 Updated share security for NT 5 changes
--*/
#include "pch.h"
#include "migmainp.h"
#include "security.h"
#define DBG_ACCTLIST "Accounts"
PACCT_DOMAINS g_FirstDomain;
POOLHANDLE g_DomainPool;
INT g_RetryCount;
BOOL
pAddAllLogonDomains (
IN PCTSTR DCName OPTIONAL
);
VOID
InitAccountList (
VOID
)
/*++
Routine Description:
Initializer for the account list that is used during account
lookup. This memory is freed after all accounts are found.
Arguments:
none
Return value:
none
--*/
{
g_FirstDomain = NULL;
g_DomainPool = PoolMemInitNamedPool ("Domain List");
PoolMemDisableTracking (g_DomainPool);
}
VOID
TerminateAccountList (
VOID
)
/*++
Routine Description:
Termination routine for the account lookup code.
Arguments:
none
Return value:
none
--*/
{
g_FirstDomain = NULL;
PoolMemDestroyPool (g_DomainPool);
}
PCWSTR
pReturnDomainFromEnum (
IN PACCT_ENUM EnumPtr
)
/*++
Routine Description:
Common code for ListFirstDomain and ListNextDomain.
Implements return parameter check.
Arguments:
EnumPtr - The current enumeration pointer supplied by
ListFirstDomain and ListNextDomain.
Return value:
A pointer to the domain name allocated in our private
pool, or NULL if no more domains remain.
--*/
{
if (!EnumPtr->DomainPtr) {
return NULL;
}
return EnumPtr->DomainPtr->Domain;
}
/*++
Routine Description:
ListFirstDomain and ListNextDomain are enumerators that list
trusted domains added by BuildDomainList. They return a
pointer to the domain name (managed by a private pool).
The enumeration structure can be passed to all other functions
that take a DomainEnumPtr as a parameter.
Arguments:
DomainEnumPtr - A pointer to a caller-allocated ACCT_ENUM
structure, typically allocated on the stack.
It does not need to be initialized.
Return value:
A pointer to the domain name, or NULL if no more domains
exist in the list.
--*/
PCWSTR
ListFirstDomain (
OUT PACCT_ENUM DomainEnumPtr
)
{
DomainEnumPtr->DomainPtr = g_FirstDomain;
return pReturnDomainFromEnum (DomainEnumPtr);
}
PCWSTR
ListNextDomain (
IN OUT PACCT_ENUM DomainEnumPtr
)
{
DomainEnumPtr->DomainPtr = DomainEnumPtr->DomainPtr->Next;
return pReturnDomainFromEnum (DomainEnumPtr);
}
BOOL
FindDomainInList (
OUT PACCT_ENUM DomainEnumPtr,
IN PCWSTR DomainToFind
)
/*++
Routine Description:
FindDomainInList searches (sequentially) through all trusted
domains for the specified domain. If found, enumeration
stops and TRUE is returned. If not found, the enumeration
pointer is invalid and FALSE is returned.
Use this function to obtain an enumeration pointer that is
used in subsequent calls to the user list.
The search is case-insensitive.
Arguments:
DomainEnumPtr - An uninitialized, caller-allocated ACCT_ENUM
structure, typically allocated on the stack.
When a search match is found, this structure
can be used with any other function that
requires a DomainEnumPtr.
DomainToFind - The name of the domain to find.
Return value:
TRUE if a match was found (and DomainEnumPtr is valid), or
FALSE if a match was not found (and DomainEnumPtr is not
valid).
--*/
{
PCWSTR DomainName;
DomainName = ListFirstDomain (DomainEnumPtr);
while (DomainName) {
if (StringIMatchW (DomainName, DomainToFind)) {
return TRUE;
}
DomainName = ListNextDomain (DomainEnumPtr);
}
return FALSE;
}
PCWSTR
pReturnUserFromEnum (
IN PACCT_ENUM UserEnumPtr
)
/*++
Routine Description:
Implements common code for ListFirstUserInDomain and
ListNextUserInDomain. Performs return parameter validation.
Arguments:
UserEnumPtr - The current enum pointer supplied by
ListFirstUserInDomain or ListNextUserInDomain.
Return value:
The name of the user being enumerated (not domain-qualified),
or NULL if no more users exist in the domain.
--*/
{
if (UserEnumPtr->UserPtr) {
return UserEnumPtr->UserPtr->User;
}
return NULL;
}
/*++
Routine Description:
ListFirstUserInDomain and ListNextUserInDomain enumerate all
users in the specified domain.
Arguments:
DomainEnumPtr - The caller-allocated ACCT_ENUM structure that
has been initialized by a domain lookup function
above.
UserEnumPtr - Used to keep track of the current user. May be
the same pointer as DomainEnumPtr.
Return value:
The name of the user being enumerated (not domain-qualified),
or NULL if no more users exist in the domain.
--*/
PCWSTR
ListFirstUserInDomain (
IN PACCT_ENUM DomainEnumPtr,
OUT PACCT_ENUM UserEnumPtr
)
{
UserEnumPtr->UserPtr = DomainEnumPtr->DomainPtr->FirstUserPtr;
return pReturnUserFromEnum (UserEnumPtr);
}
PCWSTR
ListNextUserInDomain (
IN OUT PACCT_ENUM UserEnumPtr
)
{
if (UserEnumPtr->UserPtr) {
UserEnumPtr->UserPtr = UserEnumPtr->UserPtr->Next;
} else {
UserEnumPtr->UserPtr = UserEnumPtr->DomainPtr->FirstUserPtr;
}
return pReturnUserFromEnum (UserEnumPtr);
}
BOOL
IsTrustedDomain (
IN PACCT_ENUM DomainEnumPtr
)
/*++
Routine Description:
Returns TRUE if the domain is an officially trusted domain,
or FALSE if the domain is an artificially added domain. The
account lookup code adds artificial domains to track the
state of users. For example, the domain \unknown is used
to track users who need auto-lookup. The domain \failed is
used to track users who aren't in the domain they were
expected to be in. All artifical domains start with a
backslash.
Arguments:
DomainEnumPtr - Specifies the domain to examine. This structure
must be the return of a domain enumeration
function above.
Return value:
TRUE - The domain is a trusted domain
FALSE - The domain is not really a domain but is instead an
artifically added domain
--*/
{
PCWSTR Domain;
Domain = DomainEnumPtr->DomainPtr->Domain;
//
// Test domain name to see if it is one of the reserved names
//
if (*Domain == TEXT('\\')) {
return FALSE;
}
return TRUE;
}
BOOL
FindUserInDomain (
IN PACCT_ENUM DomainEnumPtr,
OUT PACCT_ENUM UserEnumPtr,
IN PCWSTR UserToFind
)
/*++
Routine Description:
Uses ListFirstUserInDomain and ListNextUserInDomain to
sequentially search for a user. The search is case-insensitive.
Arguments:
DomainEnumPtr - Specifies the domain to search. This structure
must be the return of a domain enumeration function
above.
UserEnumPtr - Receives the results of the search if a user match
is found. Can be the same as DomainEnumPtr.
UserToFind - Specifies the name of the user to find (not
domain-qualified).
Return value:
TRUE - A match was found and UserEnumPtr is valid
FALSE - A match was not found and UserEnumPtr is not valid
--*/
{
PCWSTR UserName;
UserName = ListFirstUserInDomain (DomainEnumPtr, UserEnumPtr);
while (UserName) {
if (StringIMatchW (UserName, UserToFind)) {
return TRUE;
}
UserName = ListNextUserInDomain (UserEnumPtr);
}
return FALSE;
}
INT
CountUsersInDomain (
IN PACCT_ENUM DomainEnumPtr
)
/*++
Routine Description:
Returns the number of users in our domain enumeration structure.
Arguments:
DomainEnumPtr - Specifies the domain to search. This structure
must be the return of a domain enumeration function
above.
Return value:
The count of the users in the domain.
--*/
{
return DomainEnumPtr->DomainPtr->UserCount;
}
VOID
AddDomainToList (
IN PCWSTR Domain
)
/*++
Routine Description:
Allows domains to be added to the list of trusted domains. Normally,
BuildDomainList is the only caller to this API, because it is the
one who knows what the trusted domains are. However, artificial
domains are added in other places through this call.
Arguments:
Domain - Specifies the name of the domain to add
Return value:
none
--*/
{
PACCT_DOMAINS NewDomain;
DEBUGMSG ((DBG_ACCTLIST, "Adding domain '%s' to domain list", Domain));
NewDomain = (PACCT_DOMAINS) PoolMemGetAlignedMemory (
g_DomainPool,
sizeof (ACCT_DOMAINS)
);
ZeroMemory (NewDomain, sizeof (ACCT_DOMAINS));
NewDomain->Next = g_FirstDomain;
g_FirstDomain = NewDomain;
NewDomain->Domain = PoolMemDuplicateString (g_DomainPool, Domain);
}
VOID
pLinkUser (
IN PACCT_USERS UserPtr,
IN PACCT_DOMAINS DomainPtr
)
/*++
Routine Description:
The memory structures in this file are linked-list based. There
is a linked-list of domains, and for each domain there is a linked-
list of users. Each user has a linked list of possible domains.
The linked lists are designed to be changed while enumerations are
in progress.
This function performs the simple link operation for the user list.
Arguments:
UserPtr - A pointer to the internally maintained ACCT_USERS structure.
DomainPtr - Specifies the domain in which UserPtr is linked to.
Return value:
none
--*/
{
UserPtr->Next = DomainPtr->FirstUserPtr;
if (UserPtr->Next) {
UserPtr->Next->Prev = UserPtr;
}
DomainPtr->FirstUserPtr = UserPtr;
UserPtr->DomainPtr = DomainPtr;
DomainPtr->UserCount++;
}
BOOL
AddUserToDomainList (
IN PCWSTR User,
IN PCWSTR Domain
)
/*++
Routine Description:
This function searches for the domain name specified
and adds the user to the user list for that domain. If
the domain cannot be found, the function fails.
Arguments:
User - Specifies the name of the user to add
Domain - Specifies the name of the domain that the user
is added to
Return value:
TRUE if the user was added successfully, or FALSE if the
domain is not a trusted domain.
--*/
{
ACCT_ENUM e;
PACCT_DOMAINS DomainPtr;
PACCT_USERS NewUser;
//
// Find Domain (it must exist in the list)
//
if (!FindDomainInList (&e, Domain)) {
return FALSE;
}
DomainPtr = e.DomainPtr;
//
// Allocate structure for the user
//
NewUser = (PACCT_USERS) PoolMemGetAlignedMemory (
g_DomainPool,
sizeof (ACCT_USERS)
);
ZeroMemory (NewUser, sizeof (ACCT_USERS));
pLinkUser (NewUser, DomainPtr);
NewUser->User = PoolMemDuplicateString (g_DomainPool, User);
return TRUE;
}
VOID
pDelinkUser (
IN PACCT_USERS UserPtr
)
/*++
Routine Description:
The memory structures in this file are linked-list based. There
is a linked-list of domains, and for each domain there is a linked-
list of users. Each user has a linked list of possible domains.
The linked lists are designed to be changed while enumerations are
in progress.
This function performs the simple delink operation for the user list.
Arguments:
UserPtr - A pointer to the internally maintained ACCT_USERS structure.
Return value:
none
--*/
{
if (UserPtr->Prev) {
UserPtr->Prev->Next = UserPtr->Next;
} else {
UserPtr->DomainPtr->FirstUserPtr = UserPtr->Next;
}
if (UserPtr->Next) {
UserPtr->Next->Prev = UserPtr->Prev;
}
UserPtr->DomainPtr->UserCount--;
}
VOID
DeleteUserFromDomainList (
IN PACCT_ENUM UserEnumPtr
)
/*++
Routine Description:
Performs a delete operation for a user in a domain's user list.
The memory for this user is not freed right away, because doing
so may cause enumeration positions to become invalid. Instead,
the links are adjusted to skip over this user.
Memory is freed at termination.
Arguments:
UserEnumPtr - A pointer to the user to delete, obtained by calling
a user enumeration or user search function that
returns UserEnumPtr as an OUT.
Return value:
none
--*/
{
//
// Don't actually delete, just delink. This allows all in-progress
// enumerations to continue working.
//
pDelinkUser (UserEnumPtr->UserPtr);
}
BOOL
MoveUserToNewDomain (
IN OUT PACCT_ENUM UserEnumPtr,
IN PCWSTR NewDomain
)
/*++
Routine Description:
Moves a user from one domain to another by adjusting links only.
The current enumeration pointer is adjusted to point to the previous
user so enumeration can continue. This function may change the
behavoir other enumerations that are pointing to this user, so
be careful. It will never break an enumeration though.
Arguments:
UserEnumPtr - A pointer to the user to move, obtained by calling a
user enumeration or user search function that returns
UserEnumPtr as an OUT.
NewDomain - The name of the new domain to move the user to.
Return value:
TRUE if NewDomain is a trusted domain, or FALSE if it is not.
The user can only be moved to domains in the trust list.
--*/
{
ACCT_ENUM e;
PACCT_DOMAINS DomainPtr;
PACCT_DOMAINS OrgDomainPtr;
PACCT_USERS PrevUser;
//
// Find NewDomain (it must exist in the list)
//
if (!FindDomainInList (&e, NewDomain)) {
return FALSE;
}
DomainPtr = e.DomainPtr;
OrgDomainPtr = UserEnumPtr->UserPtr->DomainPtr;
//
// Remove user from original domain
//
PrevUser = UserEnumPtr->UserPtr->Prev;
pDelinkUser (UserEnumPtr->UserPtr);
//
// Add user to new domain
//
pLinkUser (UserEnumPtr->UserPtr, DomainPtr);
if (!PrevUser) {
UserEnumPtr->DomainPtr = OrgDomainPtr;
}
UserEnumPtr->UserPtr = PrevUser;
return TRUE;
}
VOID
UserMayBeInDomain (
IN PACCT_ENUM UserEnumPtr,
IN PACCT_ENUM DomainEnumPtr
)
/*++
Routine Description:
Provides the caller with a way to flag a domain as a possible
domain holding the account. During search, all trusted
domains are queried, and because an account can be in more
than one, a list of possible domains is developed. If the
final list of possible domains has only one entry, that
domain is used for the user. Otherwise, a dialog is presented,
allowing the installer to choose an action to take for the user.
The action can be to retry, make a local account, or select
one of the possible domains.
Arguments:
UserEnumPtr - Specifies the user that may be in a domain
DomainEnumPtr - Specifies the domain that the user may be in
Return value:
none
--*/
{
PACCT_POSSIBLE_DOMAINS PossibleDomainPtr;
PossibleDomainPtr = (PACCT_POSSIBLE_DOMAINS)
PoolMemGetAlignedMemory (
g_DomainPool,
sizeof (ACCT_POSSIBLE_DOMAINS)
);
PossibleDomainPtr->DomainPtr = DomainEnumPtr->DomainPtr;
PossibleDomainPtr->Next = UserEnumPtr->UserPtr->FirstPossibleDomain;
UserEnumPtr->UserPtr->FirstPossibleDomain = PossibleDomainPtr;
UserEnumPtr->UserPtr->PossibleDomains++;
}
VOID
ClearPossibleDomains (
IN PACCT_ENUM UserEnumPtr
)
/*++
Routine Description:
Provides the caller with a way to reset the possible domain
list. This is required if the installer chose to retry the search.
Arguments:
UserEnumPtr - Specifies the user to reset
Return value:
none
--*/
{
PACCT_POSSIBLE_DOMAINS This, Next;
This = UserEnumPtr->UserPtr->FirstPossibleDomain;
while (This) {
Next = This->Next;
PoolMemReleaseMemory (g_DomainPool, This);
This = Next;
}
UserEnumPtr->UserPtr->FirstPossibleDomain = 0;
UserEnumPtr->UserPtr->PossibleDomains = 0;
}
PCWSTR
pReturnPossibleDomainFromEnum (
IN PACCT_ENUM EnumPtr
)
/*++
Routine Description:
Common code for ListFirstPossibleDomain and ListNextPossibleDomain.
Implements return parameter checking.
Arguments:
EnumPtr - The current enumeration pointer as supplied by
ListFirstPossibleDomain or ListNextPossibleDomain.
Return value:
The name of the domain enumerated, or NULL if no more possible
domains exist. (A possible domain is one that a user may or
may not be in, but a matching account was found in the domain.)
--*/
{
if (EnumPtr->PossibleDomainPtr) {
EnumPtr->DomainPtr = EnumPtr->PossibleDomainPtr->DomainPtr;
return EnumPtr->DomainPtr->Domain;
}
return NULL;
}
/*++
Routine Description:
ListFirstPossibleDomain and ListNextPossibleDomain are enumerators
that list domains added by UserMayBeInDomain. They return a
pointer to the domain name (managed by a private pool).
A possible domain list is maintained to allow the installer to
choose between multiple domains when a user has an account on
more than one domain.
Arguments:
UserEnumPtr - A pointer to a caller-allocated ACCT_ENUM
structure, typically allocated on the stack.
It must be initialized by a user enum or user
search function.
PossibleDomainEnumPtr - Maintains the state of the possible
domain enumeration. It can be the
same pointer as UserEnumPtr.
Return value:
A pointer to the possible domain name, or NULL if no more domains
exist in the list.
--*/
PCWSTR
ListFirstPossibleDomain (
IN PACCT_ENUM UserEnumPtr,
OUT PACCT_ENUM PossibleDomainEnumPtr
)
{
PossibleDomainEnumPtr->PossibleDomainPtr = UserEnumPtr->UserPtr->FirstPossibleDomain;
return pReturnPossibleDomainFromEnum (PossibleDomainEnumPtr);
}
PCWSTR
ListNextPossibleDomain (
IN OUT PACCT_ENUM PossibleDomainEnumPtr
)
{
PossibleDomainEnumPtr->PossibleDomainPtr = PossibleDomainEnumPtr->
PossibleDomainPtr->Next;
return pReturnPossibleDomainFromEnum (PossibleDomainEnumPtr);
}
INT
CountPossibleDomains (
IN PACCT_ENUM UserEnumPtr
)
/*++
Routine Description:
Returns the number of possible domains in a user.
Arguments:
UserEnumPtr - Specifies the user to examine. This structure
must be the return of a user enumeration or user
search function above.
Return value:
The count of the possible domains for a user.
--*/
{
return UserEnumPtr->UserPtr->PossibleDomains;
}
NET_API_STATUS
pGetDcNameAllowingRetry (
IN PCWSTR DomainName,
OUT PWSTR ServerName,
IN BOOL ForceNewServer
)
/*++
Routine Description:
Implements NetGetDCName, but provides a retry capability.
Arguments:
DomainName - Specifies the domain to obtain the server name for
ServerName - Specifies a buffer to receive the name of the server
ForceNewServer - Specifies TRUE if the function should obtain a new
server for the domain. Specifies FALSE if the
function should use any existing connection if
available.
Return value:
Win32 error code indicating outcome
--*/
{
NET_API_STATUS nas;
UINT ShortCircuitRetries = 1;
//PCWSTR ArgArray[1];
do {
nas = GetAnyDC (
DomainName,
ServerName,
ForceNewServer
);
if (nas != NO_ERROR) {
//
// Short-circuited, so user isn't bothered. The alternate behavior
// is to prompt the user for retry when any domain is down. (See
// RetryMessageBox code below.)
//
ShortCircuitRetries--;
if (!ShortCircuitRetries) {
DEBUGMSG ((DBG_WARNING, "Unable to connect to domain %s", DomainName));
break;
}
#if 0
ArgArray[0] = DomainName;
if (!RetryMessageBox (MSG_GETPRIMARYDC_RETRY, ArgArray)) {
DEBUGMSG ((DBG_WARNING, "Unable to connect to domain %s; user chose to cancel", DomainName));
break;
}
#endif
ForceNewServer = TRUE;
}
} while (nas != NO_ERROR);
return nas;
}
VOID
pDisableDomain (
IN OUT PACCT_DOMAINS DomainPtr
)
/*++
Routine Description:
Disable the specified domain.
Arguments:
DomainPtr - A pointer to the internally maintained ACCT_DOMAINS
structure. This structure is updated to contain
an empty server name upon return.
Return value:
None
--*/
{
g_DomainProblem = TRUE;
if (DomainPtr->Server && *DomainPtr->Server) {
PoolMemReleaseMemory (g_DomainPool, (PVOID) DomainPtr->Server);
}
DomainPtr->Server = S_EMPTY;
}
NET_API_STATUS
pNetUseAddAllowingRetry (
IN OUT PACCT_DOMAINS DomainPtr
)
/*++
Routine Description:
Implements NetUseAdd, but provides a retry capability.
Arguments:
DomainPtr - A pointer to the internally maintained ACCT_DOMAINS
structure. This structure is updated to contain
the server name upon success.
Return value:
Win32 error code indicating outcome
--*/
{
NET_API_STATUS rc;
DWORD DontCare;
PCWSTR ReplacementName;
NET_API_STATUS nas;
USE_INFO_2 ui2;
WCHAR LocalShareName[72];
WCHAR NewServerBuf[MAX_SERVER_NAMEW];
ReplacementName = NULL;
do {
//
// Initialize USE_INFO_2 structure
//
ZeroMemory (&ui2, sizeof (ui2));
StringCopyW (LocalShareName, ReplacementName ? ReplacementName : DomainPtr->Server);
StringCatW (LocalShareName, L"\\IPC$");
ui2.ui2_remote = LocalShareName;
ui2.ui2_asg_type = USE_IPC;
rc = NetUseAdd (NULL, 2, (PBYTE) &ui2, &DontCare);
//
// If NetUseAdd fails, give the user a chance to retry with a different server
//
if (rc != NO_ERROR) {
PCWSTR ArgArray[2];
DEBUGMSG ((
DBG_WARNING,
"User was alerted to problem establishing nul session to %s (domain %s), rc=%u",
DomainPtr->Server,
DomainPtr->Domain,
rc
));
ArgArray[0] = DomainPtr->Server;
ArgArray[1] = DomainPtr->Domain;
if (!RetryMessageBox (MSG_NULSESSION_RETRY, ArgArray)) {
DEBUGMSG ((DBG_WARNING, "Unable to connect to domain %s; user chose to cancel", DomainPtr->Domain));
pDisableDomain (DomainPtr);
ReplacementName = NULL;
break;
}
if (ReplacementName) {
ReplacementName = NULL;
}
//
// Get a new server because current server is not responding. If we fail to
// obtain a server name, allow the user to try again.
//
do {
nas = GetAnyDC (DomainPtr->Domain, NewServerBuf, TRUE);
if (nas != NO_ERROR) {
PCWSTR ArgArray[1];
DEBUGMSG ((DBG_WARNING, "User was alerted to problem locating server for domain %s", DomainPtr->Domain));
ArgArray[0] = DomainPtr->Domain;
if (!RetryMessageBox (MSG_GETANYDC_RETRY, ArgArray)) {
DEBUGMSG ((DBG_WARNING, "Unable to find a server for domain %s; user chose to cancel", DomainPtr->Domain));
// Disable domain and return an error
pDisableDomain (DomainPtr);
ReplacementName = NULL;
break;
}
} else {
ReplacementName = NewServerBuf;
}
} while (nas != NO_ERROR);
}
} while (rc != NO_ERROR);
//
// If ReplacementName is not NULL, we need to free the buffer. Also, if the
// NetUseAdd call succeeded, we now have to use another server to query the
// domain.
//
if (ReplacementName) {
if (rc == NO_ERROR) {
if (DomainPtr->Server && *DomainPtr->Server) {
PoolMemReleaseMemory (g_DomainPool, (PVOID) DomainPtr->Server);
}
DomainPtr->Server = PoolMemDuplicateString (g_DomainPool, ReplacementName);
}
}
return rc;
}
PCWSTR
pLsaStringToCString (
IN PLSA_UNICODE_STRING UnicodeString,
OUT PWSTR Buffer
)
/*++
Routine Description:
A safe string extraction that takes the string in UnicodeString
and copies it to Buffer. The caller must ensure Buffer is
big enough.
Arguments:
UnicodeString - Specifies the source string to convert
Buffer - Specifies the buffer that receives the converted string
Return value:
The Buffer pointer
--*/
{
StringCopyABW (
Buffer,
UnicodeString->Buffer,
(PWSTR) ((PBYTE) UnicodeString->Buffer + UnicodeString->Length)
);
return Buffer;
}
BOOL
BuildDomainList(
VOID
)
/*++
Routine Description:
Creates a trusted domain list by:
1. Determining the computer domain in which the machine participates in
2. Opening the DC's policy
3. Querying the trust list
4. Adding it to our internal domain list (ACCT_DOMAINS)
This function will fail if the machine does not participate in a domain, or
if the domain controller cannot be contacted.
Arguments:
none
Return value:
TRUE if the trust list was completely built, or FALSE if an error occurred
and the trust list is incomplete and is probably empty. GetLastError
provides the failure code.
--*/
{
LSA_HANDLE PolicyHandle;
BOOL DomainControllerFlag = FALSE;
NTSTATUS Status;
NET_API_STATUS nas = NO_ERROR;
BOOL b = FALSE;
WCHAR PrimaryDomainName[MAX_SERVER_NAMEW];
PPOLICY_PRIMARY_DOMAIN_INFO PrimaryDomain;
WCHAR ServerName[MAX_SERVER_NAMEW];
#if DOMAINCONTROLLER
PPOLICY_ACCOUNT_DOMAIN_INFO AccountDomain;
#endif
if (!IsMemberOfDomain()) {
DEBUGMSG ((DBG_VERBOSE, "Workstation does not participate in a domain."));
return FALSE;
}
//
// Add special domains used for management of user state
//
// users whos domain is not known
AddDomainToList (S_UNKNOWN_DOMAIN);
// users whos domain was known but the account doesn't exist
AddDomainToList (S_FAILED_DOMAIN);
//
// Open the policy on this machine
//
Status = OpenPolicy (
NULL,
POLICY_VIEW_LOCAL_INFORMATION,
&PolicyHandle
);
if (Status != STATUS_SUCCESS) {
SetLastError (LsaNtStatusToWinError (Status));
return FALSE;
}
#if DOMAINCONTROLLER // disabled, but may be needed for DC installation
//
// Obtain the AccountDomain, which is common to all three cases
//
Status = LsaQueryInformationPolicy (
PolicyHandle,
PolicyAccountDomainInformation,
&AccountDomain
);
if (Status != STATUS_SUCCESS)
goto cleanup;
//
// Note: AccountDomain->DomainSid will contain binary Sid
//
AddDomainToList (pLsaStringToCString (&AccountDomain->DomainName, PrimaryDomainName));
//
// Free memory allocated for account domain
//
LsaFreeMemory (AccountDomain);
//
// Find out if this machine is a domain controller
//
if (!IsDomainController (NULL, &DomainControllerFlag)) {
// IsDomainController couldn't find the answer
goto cleanup;
}
#endif
// If not a domain controller...
if(!DomainControllerFlag) {
//
// Get the primary domain
//
Status = LsaQueryInformationPolicy (
PolicyHandle,
POLICY_PRIMARY_DOMAIN_INFORMATION,
&PrimaryDomain
);
if (Status != STATUS_SUCCESS) {
goto cleanup;
}
//
// If the primary domain SID is NULL, we are a non-member, and
// our work is done.
//
if (!PrimaryDomain->Sid) {
LsaFreeMemory (PrimaryDomain);
b = TRUE;
goto cleanup;
}
//
// We found our computer domain, add it to our list
//
AddDomainToList (pLsaStringToCString (&PrimaryDomain->Name, PrimaryDomainName));
LsaFreeMemory (PrimaryDomain);
//
// Get the primary domain controller computer name. If the API fails,
// alert the user and allow them to retry. ServerName is allocated by
// the Net APIs.
//
nas = pGetDcNameAllowingRetry (PrimaryDomainName, ServerName, FALSE);
if (nas != NO_ERROR) {
goto cleanup;
}
//
// Re-enable the code to open the policy on the domain controller
//
//
// Close the prev policy handle, because we don't need it anymore.
//
LsaClose (PolicyHandle);
PolicyHandle = INVALID_HANDLE_VALUE; // invalidate handle value
//
// Open the policy on the domain controller
//
Status = OpenPolicy(
ServerName,
POLICY_VIEW_LOCAL_INFORMATION,
&PolicyHandle
);
if (Status != STATUS_SUCCESS) {
goto cleanup;
}
}
//
// Build additional trusted logon domain(s) list and
// indicate if successful
//
b = pAddAllLogonDomains (DomainControllerFlag ? NULL : ServerName);
cleanup:
//
// Close the policy handle
//
if (PolicyHandle != INVALID_HANDLE_VALUE && PolicyHandle) {
LsaClose (PolicyHandle);
}
if (!b) {
if (Status != STATUS_SUCCESS)
SetLastError (LsaNtStatusToWinError (Status));
else if (nas != NO_ERROR)
SetLastError (nas);
}
return b;
}
BOOL
pAddAllLogonDomains (
IN PCTSTR DCName OPTIONAL
)
{
NET_API_STATUS rc;
PWSTR Domains;
PCWSTR p;
rc = NetEnumerateTrustedDomains ((PTSTR)DCName, &Domains);
if (rc != ERROR_SUCCESS) {
SetLastError (rc);
return FALSE;
}
for (p = Domains ; *p ; p = GetEndOfStringW (p) + 1) {
AddDomainToList (p);
}
NetApiBufferFree (Domains);
return TRUE;
}
BOOL
pEstablishNulSessionWithDomain (
IN OUT PACCT_DOMAINS DomainPtr,
IN BOOL ForceNewServer
)
/*++
Routine Description:
If a nul session has not been established with a domain, this
routine finds a server in the domain and establishes the nul
session. Every network call is wrapped within a retry loop,
so the user can retry when network failures occur.
Arguments:
DomainPtr - Specifies a pointer to our private domain structure.
This structure indicates the domain to establish
the nul session with, and it receives the name
of the server upon successful connection.
ForceNewServer - Specifies TRUE if the function should obtain a new
server for the domain.
Return value:
TRUE if a nul session was established, or FALSE if an
error occurred while establishing the nul session. GetLastError
provides a failure code.
--*/
{
NET_API_STATUS nas;
WCHAR ServerName[MAX_SERVER_NAMEW];
DWORD rc;
//
// Release old name if necessary
//
if (ForceNewServer && DomainPtr->Server) {
if (*DomainPtr->Server) {
PoolMemReleaseMemory (g_DomainPool, (PVOID) DomainPtr->Server);
}
DomainPtr->Server = NULL;
}
//
// Obtain a server name if necessary
//
if (!DomainPtr->Server) {
//
// Get the primary DC name
//
nas = pGetDcNameAllowingRetry (DomainPtr->Domain, ServerName, ForceNewServer);
if (nas != NO_ERROR) {
pDisableDomain (DomainPtr);
return FALSE;
}
DomainPtr->Server = PoolMemDuplicateString (g_DomainPool, ServerName);
//
// Connect to the server, possibly finding a server that will
// service us.
//
rc = pNetUseAddAllowingRetry (DomainPtr);
if (rc != NO_ERROR) {
//
// Remove the server name because we never connected
//
pDisableDomain (DomainPtr);
SetLastError (rc);
LOG ((LOG_ERROR, "NetUseAdd failed"));
return FALSE;
}
}
return *DomainPtr->Server != 0;
}
BOOL
QueryDomainForUser (
IN PACCT_ENUM DomainEnumPtr,
IN PACCT_ENUM UserEnumPtr
)
/*++
Routine Description:
Checks the domain controller for a user account via NetUserGetInfo.
Arguments:
DomainEnumPtr - Specifies the domain to search. This structure
must be the return of a domain enumeration function
above.
UserEnumPtr - Specifies the user to look up over the network.
Return value:
TRUE if the user exists, or FALSE if the user does not exist. If
an error occurs, a retry popup appears, allowing the installer to
retry the search if necessary.
--*/
{
PACCT_DOMAINS DomainPtr;
PACCT_USERS UserPtr;
NET_API_STATUS rc;
BOOL ForceNewServer = FALSE;
TCHAR DomainQualifiedUserName[MAX_USER_NAME];
BYTE SidBuf[MAX_SID_SIZE];
DWORD SizeOfSidBuf;
TCHAR DontCareStr[MAX_SERVER_NAME];
DWORD DontCareSize;
SID_NAME_USE SidNameUse;
DomainPtr = DomainEnumPtr->DomainPtr;
UserPtr = UserEnumPtr->UserPtr;
do {
if (!pEstablishNulSessionWithDomain (DomainPtr, ForceNewServer)) {
LOG ((LOG_ERROR, "Could not query domain %s for user %s.",
DomainPtr->Domain, UserPtr->User));
return FALSE;
}
//
// Do query
//
DEBUGMSG ((DBG_ACCTLIST, "Querying %s for %s", DomainPtr->Server, UserPtr->User));
rc = NO_ERROR;
wsprintf (DomainQualifiedUserName, TEXT("%s\\%s"), DomainPtr->Domain, UserPtr->User);
SizeOfSidBuf = sizeof (SidBuf);
DontCareSize = sizeof (DontCareStr);
if (!LookupAccountName (
DomainPtr->Server,
DomainQualifiedUserName,
SidBuf,
&SizeOfSidBuf,
DontCareStr,
&DontCareSize,
&SidNameUse
)) {
rc = GetLastError();
}
if (rc != NO_ERROR && rc != ERROR_NONE_MAPPED) {
PCWSTR ArgArray[2];
DEBUGMSG ((
DBG_WARNING,
"User was alerted to problem querying account %s (domain %s), rc=%u",
DomainPtr->Server,
DomainPtr->Domain,
rc
));
ArgArray[0] = DomainPtr->Server;
ArgArray[1] = DomainPtr->Domain;
if (!RetryMessageBox (MSG_NULSESSION_RETRY, ArgArray)) {
DEBUGMSG ((DBG_WARNING, "Unable to connect to domain %s; user chose to cancel", DomainPtr->Domain));
pDisableDomain (DomainPtr);
break;
}
ForceNewServer = TRUE;
}
} while (rc != NO_ERROR && rc != ERROR_NONE_MAPPED);
if (rc == NO_ERROR && SidNameUse != SidTypeUser) {
rc = ERROR_NONE_MAPPED;
}
if (rc != NO_ERROR && rc != ERROR_NONE_MAPPED) {
LOG ((
LOG_ERROR,
"User %s not found in %s, rc=%u",
UserPtr->User, DomainPtr->Domain,
rc
));
}
return rc == NO_ERROR;
}
BOOL
pGetUserSecurityInfo (
IN PCWSTR User,
IN PCWSTR Domain,
IN OUT PGROWBUFFER SidBufPtr,
OUT SID_NAME_USE *UseType OPTIONAL
)
/*++
Routine Description:
A common routine that searches for a user in our private structures
and returns the SID and/or the type of user via LookupAccountName.
The lookup operation is enclosed in a retry loop.
Arguments:
User - The name of the user to get security info on
Domain - The name of the domain where the user exists. Can be
NULL for the local machine.
SidBufPtr - A pointer to a GROWBUFFER. The SID is appended to
the GROWBUFFER.
UseType - Specifies the address of a SID_NAME_USE variable, or
NULL if use type is not needed.
Return value:
TRUE if the lookup succeeded, or FALSE if an error occurred from
either establishing a nul session for a domain or looking up an
account on the domain. GetLastError provides the failure code.
--*/
{
ACCT_ENUM e;
PACCT_DOMAINS DomainPtr;
PSID Sid;
DWORD Size;
PCWSTR FullUserName = NULL;
WCHAR DomainName[MAX_SERVER_NAMEW];
DWORD DomainNameSize;
SID_NAME_USE use = 0;
DWORD End;
BOOL b = FALSE;
DWORD rc;
__try {
End = SidBufPtr->End;
if (Domain) {
//
// Domain account case -- verify domain is in trust list
//
if (!FindDomainInList (&e, Domain)) {
__leave;
}
DomainPtr = e.DomainPtr;
FullUserName = JoinPaths (Domain, User);
} else {
//
// Local account case
//
DomainPtr = NULL;
if (StringIMatch (User, g_EveryoneStr) ||
StringIMatch (User, g_NoneGroupStr) ||
StringIMatch (User, g_AdministratorsGroupStr)
) {
FullUserName = DuplicatePathString (User, 0);
} else {
FullUserName = JoinPaths (g_ComputerName, User);
}
}
Sid = (PSID) GrowBuffer (SidBufPtr, MAX_SID_SIZE);
if (DomainPtr && !pEstablishNulSessionWithDomain (DomainPtr, FALSE)) {
LOG ((
LOG_ERROR,
"Could not query domain %s for user %s security info.",
Domain,
User
));
__leave;
}
Size = MAX_SID_SIZE;
DomainNameSize = MAX_SERVER_NAMEW;
do {
//
// Look up account name in form of domain\user or computer\user
//
b = LookupAccountName (
DomainPtr ? DomainPtr->Server : NULL,
FullUserName,
Sid,
&Size,
DomainName,
&DomainNameSize,
&use
);
if (!b) {
rc = GetLastError();
//
// In the local account case, try the lookup again, without
// the computer name decoration. This works around a
// GetComputerName bug.
//
if (rc != ERROR_INSUFFICIENT_BUFFER) {
if (!DomainPtr) {
b = LookupAccountName (
NULL,
User,
Sid,
&Size,
DomainName,
&DomainNameSize,
&use
);
rc = GetLastError();
}
}
if (!b) {
if (rc == ERROR_INSUFFICIENT_BUFFER) {
//
// Grow the buffer if necessary, then try again
//
SidBufPtr->End = End;
Sid = (PSID) GrowBuffer (SidBufPtr, Size);
continue;
}
//
// API failed with an error
//
LOG ((
LOG_ERROR,
"Lookup Account On Network: LookupAccountName failed for %s (domain: %s)",
FullUserName,
Domain ? Domain : TEXT("*local*")
));
//
// Ignore the error in the case of the local account "none"
//
if (StringIMatch (User, g_NoneGroupStr)) {
b = TRUE;
}
__leave;
}
}
} while (!b);
//
// We now have successfully gotten a sid. Adjust pointers, return type.
//
if (UseType) {
*UseType = use;
}
SidBufPtr->End = End + GetLengthSid (Sid);
//
// As a debugging aid, output the type
//
DEBUGMSG_IF ((use == SidTypeUser, DBG_VERBOSE, "%s is SidTypeUser", FullUserName));
DEBUGMSG_IF ((use == SidTypeGroup, DBG_VERBOSE, "%s is SidTypeGroup", FullUserName));
DEBUGMSG_IF ((use == SidTypeDomain, DBG_VERBOSE, "%s is SidTypeDomain", FullUserName));
DEBUGMSG_IF ((use == SidTypeAlias, DBG_VERBOSE, "%s is SidTypeAlias", FullUserName));
DEBUGMSG_IF ((use == SidTypeWellKnownGroup, DBG_VERBOSE, "%s is SidTypeWellKnownGroup", FullUserName));
DEBUGMSG_IF ((use == SidTypeDeletedAccount, DBG_VERBOSE, "%s is SidTypeDeletedAccount", FullUserName));
DEBUGMSG_IF ((use == SidTypeInvalid, DBG_VERBOSE, "%s is SidTypeInvalid", FullUserName));
DEBUGMSG_IF ((use == SidTypeUnknown, DBG_VERBOSE, "%s is SidTypeUnknown", FullUserName));
DEBUGMSG_IF ((use == SidTypeComputer, DBG_VERBOSE, "%s is SidTypeComputer", FullUserName));
}
__finally {
FreePathString (FullUserName);
}
return b;
}
BOOL
GetUserSid (
IN PCWSTR User,
IN PCWSTR Domain,
IN OUT PGROWBUFFER SidBufPtr
)
/*++
Routine Description:
This routine is vaild only after the domain list is perpared.
It queries a domain for a user SID.
Arguments:
User - Specifies name of user to look up
Domain - Specifies name of domain to query, or NULL for local machine
SidBufPtr - A ponter to a GROWBUFFER. The SID is appended to
the GROWBUFFER.
Return value:
TRUE if the lookup succeeded, or FALSE if an error occurred from
either establishing a nul session for a domain or looking up an
account on the domain. GetLastError provides the failure code.
--*/
{
return pGetUserSecurityInfo (User, Domain, SidBufPtr, NULL);
}
BOOL
GetUserType (
IN PCWSTR User,
IN PCWSTR Domain,
OUT SID_NAME_USE *UseType
)
/*++
Routine Description:
This routine is valid only after the domain list is prepared.
It queries a domain for a user SID type.
Arguments:
User - Specifies name of user to look up
Domain - Specifies name of domain to query, or NULL for local machine
UseType - Specifies the address of a SID_NAME_USE variable
Return value:
TRUE if the lookup succeeded, or FALSE if an error occurred from
either establishing a nul session for a domain or looking up an
account on the domain. GetLastError provides the failure code.
--*/
{
GROWBUFFER SidBuf = GROWBUF_INIT;
BOOL b;
b = pGetUserSecurityInfo (User, Domain, &SidBuf, UseType);
FreeGrowBuffer (&SidBuf);
return b;
}
VOID
PrepareForRetry (
VOID
)
/*++
Routine Description:
Provides caller a way to reset the abandoned domains. When an error
occurs during account lookup, and the installer chooses not to retry,
the domain is disabled for the rest of the lookup until the installer
is presented with a dialog detailing the problems. If they choose to
retry the search, all disabled domains must be re-enabled, and thats
what this routine does.
Arguments:
none
Return value:
none
--*/
{
ACCT_ENUM Domain;
//
// Enumerate all domains and remove any empty server names
//
if (ListFirstDomain (&Domain)) {
do {
if (Domain.DomainPtr->Server && Domain.DomainPtr->Server[0] == 0) {
Domain.DomainPtr->Server = NULL;
}
} while (ListNextDomain (&Domain));
}
//
// Reset domain lookup retry count
//
g_RetryCount = DOMAIN_RETRY_RESET;
}
BOOL
RetryMessageBox (
DWORD Id,
PCTSTR *ArgArray
)
/*++
Routine Description:
A wrapper that allows retry message box code to be simplified.
Arguments:
Id - Specifies the message ID
ArgArray - Specifies the argument array eventually passed to FormatMessage
Return value:
TRUE if the user chooses YES, FALSE if the user chooses NO
--*/
{
DWORD rc;
if (g_RetryCount < 0) {
// Either DOMAIN_RETRY_ABORT or DOMAIN_RETRY_NO
return FALSE;
}
if (g_ConfigOptions.IgnoreNetworkErrors) {
return FALSE;
}
rc = ResourceMessageBox (
g_ParentWnd,
Id,
MB_YESNO|MB_ICONQUESTION,
ArgArray
);
if (rc == IDNO && g_RetryCount < DOMAIN_RETRY_MAX) {
// disabled so the IDD_NETWORK_DOWN dialog never appears
//g_RetryCount++;
if (g_RetryCount == DOMAIN_RETRY_MAX) {
DWORD Result;
Result = DialogBoxParam (
g_hInst,
MAKEINTRESOURCE (IDD_NETWORK_DOWN),
g_ParentWnd,
NetworkDownDlgProc,
(LPARAM) (PINT) &g_RetryCount
);
if (Result == IDC_STOP) {
g_RetryCount = DOMAIN_RETRY_ABORT;
} else if (Result == IDC_NO_RETRY) {
g_RetryCount = DOMAIN_RETRY_NO;
} else {
g_RetryCount = DOMAIN_RETRY_RESET;
}
}
}
return rc != IDNO;
}