2077 lines
46 KiB
C
2077 lines
46 KiB
C
|
/*++
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|