You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1798 lines
46 KiB
1798 lines
46 KiB
/*++
|
|
|
|
Copyright (c) 1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
acctlist.c
|
|
|
|
Abstract:
|
|
|
|
This code builds a list of domains and queries each of them to
|
|
locate an account. It is different than LookupAccountName because
|
|
it returns accounts for each match instead of just the first match.
|
|
|
|
Author:
|
|
|
|
Jim Schmidt (jimschm) 26-Jun-1997
|
|
|
|
Revision History:
|
|
|
|
ovidiut 14-Mar-2000 Added support for encrypted passwords
|
|
jimschm 23-Sep-1998 UI changes
|
|
jimschm 11-Jun-1998 User Profile Path now stored in account
|
|
list. Added GetProfilePathForUser.
|
|
jimschm 18-Mar-1998 Added support for random passwords and
|
|
auto-logon.
|
|
jimschm 17-Feb-1998 Updated share security for NT 5 changes
|
|
marcw 10-Dec-1997 Added unattended local account password
|
|
support.
|
|
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
#include "migmainp.h"
|
|
#include "security.h"
|
|
|
|
#define DBG_ACCOUNTS "AcctList"
|
|
|
|
POOLHANDLE g_UserPool;
|
|
PVOID g_UserTable;
|
|
|
|
typedef struct {
|
|
PCWSTR Domain;
|
|
PSID Sid;
|
|
PCWSTR ProfilePath;
|
|
} USERDETAILS, *PUSERDETAILS;
|
|
|
|
BOOL g_DomainProblem;
|
|
|
|
BOOL g_RandomPassword = FALSE;
|
|
|
|
BOOL
|
|
pAddUser (
|
|
IN PCWSTR User,
|
|
IN PCWSTR Domain
|
|
);
|
|
|
|
BOOL
|
|
pAddLocalGroup (
|
|
IN PCWSTR Group
|
|
);
|
|
|
|
BOOL
|
|
pAddDomainGroup (
|
|
IN PCWSTR Group
|
|
);
|
|
|
|
VOID
|
|
pGenerateRandomPassword (
|
|
OUT PTSTR Password
|
|
);
|
|
|
|
VOID
|
|
pResolveUserDomains (
|
|
VOID
|
|
);
|
|
|
|
BOOL
|
|
pMakeSureAccountsAreValid (
|
|
VOID
|
|
);
|
|
|
|
BOOL
|
|
pWasWin9xOnTheNet (
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
FindAccountInit (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialization routine for account management routines, initialized
|
|
when the migration module begins and terminated when the migration
|
|
module ends.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
g_UserPool = PoolMemInitNamedPool ("User Accounts");
|
|
g_UserTable = pSetupStringTableInitializeEx (sizeof (USERDETAILS), 0);
|
|
}
|
|
|
|
|
|
VOID
|
|
FindAccountTerminate (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Termination routine for the account management routines, called when
|
|
migmain's entry point is called for cleanup.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
PushError();
|
|
|
|
//
|
|
// Free user list
|
|
//
|
|
|
|
PoolMemDestroyPool (g_UserPool);
|
|
pSetupStringTableDestroy (g_UserTable);
|
|
|
|
//
|
|
// Restore error value
|
|
//
|
|
|
|
PopError();
|
|
}
|
|
|
|
|
|
BOOL
|
|
SearchDomainsForUserAccounts (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Routine to resolve all account names into fully qualified
|
|
domain names with SIDs, and user profile paths.
|
|
|
|
The account names come from the Autosearch and KnownDomain
|
|
categories in memdb. They are validated and placed in a
|
|
string table called the user list. Functions in this source
|
|
file access the user list.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return value:
|
|
|
|
TRUE if the account list was resolved, or FALSE if a failure
|
|
occurs. In the failure case, the account list may not be
|
|
accurate, but the installer is informed when network problems
|
|
occur, and the installer also has several chances to correct
|
|
the problem.
|
|
|
|
--*/
|
|
|
|
{
|
|
MEMDB_ENUM e;
|
|
PTSTR p, UserName;
|
|
TCHAR DomainName[MAX_SERVER_NAME];
|
|
BOOL FallbackToLocal = FALSE;
|
|
BOOL LocalizeWarning = FALSE;
|
|
BOOL b = FALSE;
|
|
INFCONTEXT ic;
|
|
TCHAR Buffer[256];
|
|
|
|
__try {
|
|
|
|
//
|
|
// Put the Administrator password in the list
|
|
//
|
|
|
|
if (MemDbGetValueEx (&e, MEMDB_CATEGORY_STATE, MEMDB_ITEM_ADMIN_PASSWORD, NULL)) {
|
|
MemDbSetValueEx (
|
|
MEMDB_CATEGORY_USERPASSWORD,
|
|
g_AdministratorStr,
|
|
e.szName,
|
|
NULL,
|
|
e.dwValue,
|
|
NULL
|
|
);
|
|
} else {
|
|
MYASSERT (FALSE);
|
|
}
|
|
|
|
//
|
|
// Create the status dialog (initially hidden)
|
|
//
|
|
|
|
CreateStatusPopup();
|
|
|
|
//
|
|
// Prepare the account list
|
|
//
|
|
|
|
InitAccountList();
|
|
|
|
//
|
|
// Get all trusted domains
|
|
//
|
|
|
|
if (!BuildDomainList()) {
|
|
FallbackToLocal = TRUE;
|
|
}
|
|
|
|
//
|
|
// Put all users who need autosearch to resolve their domain names
|
|
// in the unknown domain.
|
|
//
|
|
|
|
if (MemDbEnumItems (&e, MEMDB_CATEGORY_AUTOSEARCH)) {
|
|
do {
|
|
if (FallbackToLocal) {
|
|
if (!pAddUser (e.szName, NULL)) {
|
|
LOG ((
|
|
LOG_ERROR,
|
|
"Can't create local account for %s, this user can't be processed.",
|
|
e.szName
|
|
));
|
|
}
|
|
} else {
|
|
AddUserToDomainList (e.szName, S_UNKNOWN_DOMAIN);
|
|
}
|
|
} while (MemDbEnumNextValue (&e));
|
|
}
|
|
|
|
//
|
|
// Put all users whos domain is known in the appropriate domain. If
|
|
// the add fails, the domain does not exist and the account must be
|
|
// added to the autosearch (causing a silent repair if possible).
|
|
//
|
|
|
|
if (MemDbGetValueEx (&e, MEMDB_CATEGORY_KNOWNDOMAIN, NULL, NULL)) {
|
|
do {
|
|
if(_tcslen(e.szName) >= ARRAYSIZE(DomainName)){
|
|
DEBUGMSG((DBG_WARNING, "SearchDomainsForUserAccounts does not provide enough buffer for string copy %s", e.szName));
|
|
continue;
|
|
}
|
|
StackStringCopy (DomainName, e.szName);
|
|
p = _tcschr (DomainName, TEXT('\\'));
|
|
if (!p) {
|
|
DEBUGMSG ((
|
|
DBG_WHOOPS,
|
|
"Unexpected string in %s: %s",
|
|
MEMDB_CATEGORY_KNOWNDOMAIN,
|
|
e.szName
|
|
));
|
|
continue;
|
|
}
|
|
|
|
UserName = _tcsinc (p);
|
|
*p = 0;
|
|
|
|
//
|
|
// Verify that this isn't some irrelavent user
|
|
//
|
|
|
|
if (!GetUserDatLocation (UserName, NULL)) {
|
|
|
|
DEBUGMSG ((
|
|
DBG_WARNING,
|
|
"Ignoring irrelavent user specified in UserDomain of unattend.txt: %s",
|
|
e.szName
|
|
));
|
|
|
|
continue;
|
|
}
|
|
|
|
if (p == DomainName || FallbackToLocal) {
|
|
//
|
|
// This user has a local account
|
|
//
|
|
|
|
if (!pAddUser (UserName, NULL)) {
|
|
LOG ((
|
|
LOG_ERROR,
|
|
"Can't create local account for %s, this user can't be processed. (2)",
|
|
e.szName
|
|
));
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// This user's domain name needs verification
|
|
//
|
|
|
|
if (!AddUserToDomainList (UserName, DomainName)) {
|
|
AddUserToDomainList (UserName, S_UNKNOWN_DOMAIN);
|
|
}
|
|
}
|
|
} while (MemDbEnumNextValue (&e));
|
|
}
|
|
|
|
//
|
|
// Now resolve all the domain names
|
|
//
|
|
|
|
if (!FallbackToLocal) {
|
|
do {
|
|
g_DomainProblem = FALSE;
|
|
pResolveUserDomains();
|
|
|
|
PrepareForRetry();
|
|
|
|
} while (pMakeSureAccountsAreValid());
|
|
}
|
|
|
|
//
|
|
// If we had no choice but to make some accounts local,
|
|
// put a message in the PSS log.
|
|
//
|
|
|
|
if (FallbackToLocal) {
|
|
if (pWasWin9xOnTheNet() && !g_ConfigOptions.UseLocalAccountOnError) {
|
|
LOG ((LOG_WARNING, (PCSTR)MSG_ALL_USERS_ARE_LOCAL, g_ComputerName));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make sure Administrator is in the account list
|
|
//
|
|
|
|
if (!GetSidForUser (g_AdministratorStr)) {
|
|
if (!pAddUser (g_AdministratorStr, NULL)) {
|
|
LOG ((LOG_ERROR, "Account name mismatch: %s", g_AdministratorStr));
|
|
LocalizeWarning = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Add local "Everyone" for network account case
|
|
//
|
|
|
|
if (!pAddLocalGroup (g_EveryoneStr)) {
|
|
LOG ((LOG_ERROR, "Account name mismatch: %s", g_EveryoneStr));
|
|
LocalizeWarning = TRUE;
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Add Administrators group
|
|
//
|
|
|
|
if (!pAddLocalGroup (g_AdministratorsGroupStr)) {
|
|
LOG ((LOG_ERROR, "Account name mismatch: %s", g_AdministratorsGroupStr));
|
|
LocalizeWarning = TRUE;
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Add domain users group if domain is enabled, or add "none" group otherwise
|
|
//
|
|
|
|
if (!FallbackToLocal) {
|
|
if (!pAddDomainGroup (g_DomainUsersGroupStr)) {
|
|
DEBUGMSG ((DBG_WARNING, "Domain enabled but GetPrimaryDomainName failed"));
|
|
}
|
|
} else {
|
|
if (!pAddLocalGroup (g_NoneGroupStr)) {
|
|
LOG ((LOG_ERROR, "Account name mismatch: %s", g_NoneGroupStr));
|
|
LocalizeWarning = TRUE;
|
|
__leave;
|
|
}
|
|
}
|
|
|
|
//
|
|
// All user accounts and SIDs now exist in a user table, so we do not
|
|
// need the account list anymore.
|
|
//
|
|
|
|
TerminateAccountList();
|
|
DestroyStatusPopup();
|
|
|
|
b = TRUE;
|
|
}
|
|
__finally {
|
|
|
|
#ifdef PRERELEASE
|
|
if (LocalizeWarning) {
|
|
LOG ((
|
|
LOG_ERROR,
|
|
"Account name mismatches are usually caused by improper localization. "
|
|
"Make sure w95upgnt.dll account names match the LSA database."
|
|
));
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
VOID
|
|
AutoStartProcessing (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
AutoStartProcessing fills in the winlogon key's auto-admin logon, and sets
|
|
migpwd.exe in Run.
|
|
|
|
If necessary, Winlogon will prompt the user for their password. As a backup,
|
|
a RunOnce and Run entry is made. (If some bug caused winlogon not to run
|
|
this app, then it would be impossible to log on.)
|
|
|
|
If an administrator password was already set via the AdminPassword line in
|
|
[GUIUnattended], then the migpwd.exe entry is not used.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
HKEY WinlogonKey;
|
|
HKEY RunKey;
|
|
TCHAR Buf[32];
|
|
PCTSTR MigPwdPath;
|
|
BOOL AutoLogonOk = TRUE;
|
|
LONG rc;
|
|
MEMDB_ENUM e;
|
|
DWORD One = 1;
|
|
|
|
//
|
|
// Enable auto-logon if random password was used
|
|
//
|
|
|
|
if (!MemDbGetValueEx (&e, MEMDB_CATEGORY_STATE, MEMDB_ITEM_ADMIN_PASSWORD, NULL)) {
|
|
MYASSERT (FALSE);
|
|
e.dwValue = PASSWORD_ATTR_RANDOM;
|
|
e.szName[0] = 0;
|
|
ClearAdminPassword();
|
|
}
|
|
|
|
MigPwdPath = JoinPaths (g_System32Dir, S_MIGPWD_EXE);
|
|
|
|
//if ((e.dwValue & PASSWORD_ATTR_RANDOM) == PASSWORD_ATTR_RANDOM || g_RandomPassword) {
|
|
|
|
//
|
|
// Set AutoAdminLogon, DefaultUser, DefaultUserDomain and DefaultPassword
|
|
//
|
|
|
|
WinlogonKey = OpenRegKeyStr (S_WINLOGON_REGKEY);
|
|
if (WinlogonKey) {
|
|
rc = RegSetValueEx (
|
|
WinlogonKey,
|
|
S_DEFAULT_USER_NAME_VALUE,
|
|
0,
|
|
REG_SZ,
|
|
(PBYTE) g_AdministratorStr,
|
|
SizeOfString (g_AdministratorStr)
|
|
);
|
|
|
|
if (rc != ERROR_SUCCESS) {
|
|
DEBUGMSG ((DBG_WHOOPS, "Can't set default user name"));
|
|
AutoLogonOk = FALSE;
|
|
}
|
|
|
|
rc = RegSetValueEx (
|
|
WinlogonKey,
|
|
S_DEFAULT_DOMAIN_NAME_VALUE,
|
|
0,
|
|
REG_SZ,
|
|
(PBYTE) g_ComputerName,
|
|
SizeOfString (g_ComputerName)
|
|
);
|
|
|
|
if (rc != ERROR_SUCCESS) {
|
|
DEBUGMSG ((DBG_WHOOPS, "Can't set default domain name"));
|
|
AutoLogonOk = FALSE;
|
|
}
|
|
|
|
rc = RegSetValueEx (
|
|
WinlogonKey,
|
|
S_DEFAULT_PASSWORD_VALUE,
|
|
0,
|
|
REG_SZ,
|
|
(PBYTE) e.szName,
|
|
SizeOfString (e.szName)
|
|
);
|
|
|
|
if (rc != ERROR_SUCCESS) {
|
|
DEBUGMSG ((DBG_WHOOPS, "Can't set administrator password"));
|
|
AutoLogonOk = FALSE;
|
|
}
|
|
|
|
wsprintf (Buf, TEXT("%u"), 1);
|
|
|
|
rc = RegSetValueEx (
|
|
WinlogonKey,
|
|
S_AUTOADMIN_LOGON_VALUE,
|
|
0,
|
|
REG_SZ,
|
|
(PBYTE) Buf,
|
|
SizeOfString (Buf)
|
|
);
|
|
|
|
if (rc != ERROR_SUCCESS) {
|
|
DEBUGMSG ((DBG_WHOOPS, "Can't turn on auto logon"));
|
|
AutoLogonOk = FALSE;
|
|
}
|
|
|
|
rc = RegSetValueEx (
|
|
WinlogonKey,
|
|
TEXT("SetWin9xUpgradePasswords"),
|
|
0,
|
|
REG_DWORD,
|
|
(PBYTE) &One,
|
|
sizeof (One)
|
|
);
|
|
|
|
if (rc != ERROR_SUCCESS) {
|
|
DEBUGMSG ((DBG_WHOOPS, "Can't enable boot-time password prompt"));
|
|
}
|
|
|
|
CloseRegKey (WinlogonKey);
|
|
|
|
} else {
|
|
DEBUGMSG ((DBG_WHOOPS, "Can't open winlogon key"));
|
|
AutoLogonOk = FALSE;
|
|
}
|
|
|
|
//
|
|
// Add migpwd.exe to Run.
|
|
//
|
|
|
|
RunKey = OpenRegKeyStr (S_RUN_KEY);
|
|
|
|
if (RunKey) {
|
|
rc = RegSetValueEx (
|
|
RunKey,
|
|
S_MIGPWD,
|
|
0,
|
|
REG_SZ,
|
|
(PBYTE) MigPwdPath,
|
|
SizeOfString (MigPwdPath)
|
|
);
|
|
|
|
if (rc != ERROR_SUCCESS) {
|
|
DEBUGMSG ((DBG_WHOOPS, "Can't set Run key value"));
|
|
AutoLogonOk = FALSE;
|
|
}
|
|
|
|
CloseRegKey (RunKey);
|
|
|
|
} else {
|
|
DEBUGMSG ((DBG_WHOOPS, "Can't open Run key"));
|
|
AutoLogonOk = FALSE;
|
|
}
|
|
|
|
RunKey = OpenRegKeyStr (S_RUNONCE_KEY);
|
|
|
|
if (RunKey) {
|
|
rc = RegSetValueEx (
|
|
RunKey,
|
|
S_MIGPWD,
|
|
0,
|
|
REG_SZ,
|
|
(PBYTE) MigPwdPath,
|
|
SizeOfString (MigPwdPath)
|
|
);
|
|
|
|
if (rc != ERROR_SUCCESS) {
|
|
DEBUGMSG ((DBG_WHOOPS, "Can't set RunOnce key value"));
|
|
AutoLogonOk = FALSE;
|
|
}
|
|
|
|
CloseRegKey (RunKey);
|
|
|
|
} else {
|
|
DEBUGMSG ((DBG_WHOOPS, "Can't open RunOnce key"));
|
|
AutoLogonOk = FALSE;
|
|
}
|
|
|
|
#if 0
|
|
} else {
|
|
DEBUGMSG ((DBG_ACCOUNTS, "Deleting %s because it is not needed", MigPwdPath));
|
|
DeleteFile (MigPwdPath);
|
|
}
|
|
#endif
|
|
|
|
if (!AutoLogonOk) {
|
|
LOG ((
|
|
LOG_ACCOUNTS,
|
|
"An error occurred preparing autologon. There will be password problems."
|
|
));
|
|
|
|
//
|
|
// Set the Admin password to blank
|
|
//
|
|
|
|
ClearAdminPassword();
|
|
}
|
|
|
|
FreePathString (MigPwdPath);
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
pResolveUserDomains (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This private function searches the network for all users with unknown
|
|
domains as well as users with manually entered domains. It resolves
|
|
as many users as it can, and if no network problems occur, all users
|
|
are resolved.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
ACCT_ENUM KnownDomain, UnknownDomain;
|
|
ACCT_ENUM UserEnum;
|
|
BOOL UnknownFlag, KnownFlag;
|
|
PCWSTR Domain;
|
|
UINT TotalDomains;
|
|
UINT CurrentDomain;
|
|
PCTSTR Message;
|
|
PCTSTR ArgArray[3];
|
|
BOOL FlashSuppress = TRUE;
|
|
|
|
//
|
|
// Determine if there are any users who do not have domains
|
|
//
|
|
|
|
UnknownFlag = FindDomainInList (&UnknownDomain, S_UNKNOWN_DOMAIN);
|
|
if (UnknownFlag && !CountUsersInDomain (&UnknownDomain)) {
|
|
UnknownFlag = FALSE;
|
|
}
|
|
|
|
//
|
|
// Count the number of domains
|
|
//
|
|
|
|
Domain = ListFirstDomain (&KnownDomain);
|
|
TotalDomains = 0;
|
|
|
|
while (Domain) {
|
|
//
|
|
// We will query a domain when:
|
|
//
|
|
// - it is trusted
|
|
// - there is one or more users who we think is in the domain
|
|
// - or, there is one or more users which we don't know the domain
|
|
//
|
|
|
|
if (IsTrustedDomain (&KnownDomain)) {
|
|
if (UnknownFlag) {
|
|
TotalDomains++;
|
|
} else if (CountUsersInDomain (&KnownDomain)) {
|
|
TotalDomains++;
|
|
}
|
|
}
|
|
|
|
Domain = ListNextDomain (&KnownDomain);
|
|
}
|
|
|
|
|
|
//
|
|
// Enumerate each trusted domain
|
|
//
|
|
|
|
Domain = ListFirstDomain (&KnownDomain);
|
|
CurrentDomain = 0;
|
|
|
|
if (TotalDomains <= 4) {
|
|
//
|
|
// No status on small nets
|
|
//
|
|
|
|
HideStatusPopup (INFINITE);
|
|
FlashSuppress = FALSE;
|
|
}
|
|
|
|
while (Domain) {
|
|
if (IsTrustedDomain (&KnownDomain)) {
|
|
//
|
|
// Determine if there are any users for the current domain
|
|
//
|
|
|
|
KnownFlag = CountUsersInDomain (&KnownDomain) != 0;
|
|
|
|
//
|
|
// Process only if this domain needs to be queried -- either
|
|
// domain is unknown, or it is known but unverified.
|
|
//
|
|
|
|
if (UnknownFlag || KnownFlag) {
|
|
|
|
//
|
|
// Update the status window
|
|
//
|
|
|
|
CurrentDomain++;
|
|
|
|
ArgArray[0] = Domain;
|
|
ArgArray[1] = (PCTSTR) CurrentDomain;
|
|
ArgArray[2] = (PCTSTR) TotalDomains;
|
|
|
|
Message = ParseMessageID (MSG_DOMAIN_STATUS_POPUP, ArgArray);
|
|
UpdateStatusPopup (Message);
|
|
FreeStringResource (Message);
|
|
|
|
if (FlashSuppress) {
|
|
if (IsStatusPopupVisible()) {
|
|
FlashSuppress = FALSE;
|
|
} else if ((TotalDomains - CurrentDomain) <= 3) {
|
|
//
|
|
// Not much left, we better suspend the status dialog
|
|
//
|
|
|
|
HideStatusPopup (INFINITE);
|
|
FlashSuppress = FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Enumerate all users with unknown domains and look for them
|
|
// in this domain, unless user wants to abort search.
|
|
//
|
|
|
|
if (g_RetryCount != DOMAIN_RETRY_ABORT) {
|
|
if (ListFirstUserInDomain (&UnknownDomain, &UserEnum)) {
|
|
do {
|
|
if (QueryDomainForUser (&KnownDomain, &UserEnum)) {
|
|
UserMayBeInDomain (&UserEnum, &KnownDomain);
|
|
}
|
|
if (g_RetryCount == DOMAIN_RETRY_ABORT) {
|
|
break;
|
|
}
|
|
} while (ListNextUserInDomain (&UserEnum));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Enumerate all users that are supposed to be in this domain,
|
|
// unless user wants to abort search.
|
|
//
|
|
|
|
if (ListFirstUserInDomain (&KnownDomain, &UserEnum)) {
|
|
do {
|
|
if (g_RetryCount == DOMAIN_RETRY_ABORT ||
|
|
!QueryDomainForUser (&KnownDomain, &UserEnum)) {
|
|
//
|
|
// User was not found! Put them in the "failed" domain
|
|
//
|
|
|
|
MoveUserToNewDomain (&UserEnum, S_FAILED_DOMAIN);
|
|
}
|
|
} while (ListNextUserInDomain (&UserEnum));
|
|
}
|
|
}
|
|
}
|
|
|
|
Domain = ListNextDomain (&KnownDomain);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
pAddUserToRegistryList (
|
|
PCTSTR User,
|
|
BOOL DomainFixList
|
|
)
|
|
{
|
|
HKEY Key;
|
|
PCTSTR KeyStr;
|
|
|
|
KeyStr = DomainFixList ? S_WINLOGON_USER_LIST_KEY : S_USER_LIST_KEY;
|
|
|
|
Key = CreateRegKeyStr (KeyStr);
|
|
|
|
if (Key) {
|
|
RegSetValueEx (
|
|
Key,
|
|
User,
|
|
0,
|
|
REG_SZ,
|
|
(PBYTE) S_EMPTY,
|
|
sizeof (TCHAR)
|
|
);
|
|
|
|
CloseRegKey (Key);
|
|
}
|
|
ELSE_DEBUGMSG ((DBG_WHOOPS, "Can't create %s", KeyStr));
|
|
}
|
|
|
|
|
|
BOOL
|
|
pAddUser (
|
|
IN PCWSTR User,
|
|
IN PCWSTR Domain OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds a user and domain to our user list. It obtains the SID for the user
|
|
and saves the user name, domain and SID in a string table. The profile
|
|
path for the user is obtained via a call to CreateUserProfile (in userenv.dll).
|
|
|
|
This function also maintains g_RandomPassword - a flag that is set to TRUE
|
|
if a random password is used.
|
|
|
|
Arguments:
|
|
|
|
User - Specifies fixed user name
|
|
|
|
Domain - Specifies domain where user account exists, NULL for local machine
|
|
|
|
Return value:
|
|
|
|
TRUE if no errors occur, or FALSE if an unexpected problem occurs and the
|
|
user cannot be added.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL DupDomain = TRUE;
|
|
USERDETAILS UserDetails;
|
|
ACCOUNTPROPERTIES Account;
|
|
DWORD rc;
|
|
GROWBUFFER SidBuf = GROWBUF_INIT;
|
|
TCHAR UserProfilePath[MAX_TCHAR_PATH];
|
|
BOOL CreateAccountFlag;
|
|
MEMDB_ENUM e;
|
|
DWORD attribs = PASSWORD_ATTR_DEFAULT;
|
|
TCHAR pattern[MEMDB_MAX];
|
|
TCHAR randomPwd[16];
|
|
TCHAR copyPwd[MEMDB_MAX];
|
|
PCTSTR ArgList[1];
|
|
|
|
CreateAccountFlag = MemDbGetValueEx (
|
|
&e,
|
|
MEMDB_CATEGORY_USER_DAT_LOC,
|
|
User,
|
|
NULL
|
|
);
|
|
|
|
ZeroMemory (&UserDetails, sizeof (UserDetails));
|
|
|
|
//
|
|
// Make sure the administrator account is created
|
|
//
|
|
if (CreateAccountFlag || StringIMatch (User, g_AdministratorStr)) {
|
|
//
|
|
// If a local account, create it
|
|
//
|
|
|
|
if (!Domain) {
|
|
Account.User = User;
|
|
Account.FullName = User;
|
|
ArgList[0] = g_Win95Name;
|
|
Account.AdminComment = ParseMessageID (MSG_MIGRATED_ACCOUNT_DESCRIPTION, ArgList);
|
|
Account.EncryptedPassword = NULL;
|
|
|
|
//
|
|
// Set password. If installer specified a password for this user via an unattend
|
|
// setting, we'll use that. Otherwise, if they have specified a default
|
|
// password, we'll use that. Finally, if neither of those settings were
|
|
// specified, we'll use a random password.
|
|
//
|
|
|
|
MemDbBuildKey (pattern, MEMDB_CATEGORY_USERPASSWORD, User, TEXT("*"), NULL);
|
|
if (MemDbEnumFirstValue (
|
|
&e,
|
|
pattern,
|
|
MEMDB_ALL_SUBLEVELS,
|
|
MEMDB_ENDPOINTS_ONLY
|
|
)) {
|
|
|
|
StackStringCopy (copyPwd, e.szName);
|
|
attribs = e.dwValue;
|
|
if (attribs & PASSWORD_ATTR_ENCRYPTED) {
|
|
//
|
|
// cannot use this hashed pwd to create the account
|
|
// create a random one that will be replaced
|
|
// using OWF hashing functions
|
|
//
|
|
pGenerateRandomPassword (randomPwd);
|
|
Account.Password = randomPwd;
|
|
Account.EncryptedPassword = copyPwd;
|
|
DEBUGMSG ((
|
|
DBG_ACCOUNTS,
|
|
"Per-user encrypted password specified for %s: %s",
|
|
Account.User,
|
|
Account.EncryptedPassword
|
|
));
|
|
} else {
|
|
Account.Password = copyPwd;
|
|
DEBUGMSG ((
|
|
DBG_ACCOUNTS,
|
|
"Per-user password specified for %s: %s",
|
|
Account.User,
|
|
Account.Password
|
|
));
|
|
}
|
|
|
|
} else {
|
|
if (g_ConfigOptions.DefaultPassword && !StringMatch (g_ConfigOptions.DefaultPassword, TEXT("*"))) {
|
|
if (g_ConfigOptions.EncryptedUserPasswords) {
|
|
pGenerateRandomPassword (randomPwd);
|
|
Account.Password = randomPwd;
|
|
Account.EncryptedPassword = g_ConfigOptions.DefaultPassword;
|
|
DEBUGMSG ((
|
|
DBG_ACCOUNTS,
|
|
"Default encrypted password specified for %s: %s",
|
|
Account.User,
|
|
Account.EncryptedPassword
|
|
));
|
|
} else {
|
|
Account.Password = g_ConfigOptions.DefaultPassword;
|
|
DEBUGMSG ((
|
|
DBG_ACCOUNTS,
|
|
"Default password specified for %s: %s",
|
|
Account.User,
|
|
Account.Password
|
|
));
|
|
}
|
|
} else {
|
|
MemDbBuildKey (pattern, MEMDB_CATEGORY_USERPASSWORD, S_DEFAULTUSER, TEXT("*"), NULL);
|
|
if (MemDbEnumFirstValue (
|
|
&e,
|
|
pattern,
|
|
MEMDB_ALL_SUBLEVELS,
|
|
MEMDB_ENDPOINTS_ONLY
|
|
)) {
|
|
|
|
//
|
|
// check for the placeholder
|
|
//
|
|
if (StringMatch (e.szName, TEXT("*"))) {
|
|
e.szName[0] = 0;
|
|
}
|
|
|
|
StackStringCopy (copyPwd, e.szName);
|
|
attribs = e.dwValue;
|
|
if (attribs & PASSWORD_ATTR_ENCRYPTED) {
|
|
//
|
|
// cannot use this hashed pwd to create the account
|
|
// create a random one that will be replaced
|
|
// using OWF hashing functions
|
|
//
|
|
pGenerateRandomPassword (randomPwd);
|
|
Account.Password = randomPwd;
|
|
Account.EncryptedPassword = copyPwd;
|
|
DEBUGMSG ((
|
|
DBG_ACCOUNTS,
|
|
"Default encrypted password specified for %s: %s",
|
|
Account.User,
|
|
Account.EncryptedPassword
|
|
));
|
|
} else {
|
|
Account.Password = copyPwd;
|
|
DEBUGMSG ((
|
|
DBG_ACCOUNTS,
|
|
"Per-user password specified for %s: %s",
|
|
Account.User,
|
|
Account.Password
|
|
));
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Random password generation code removed, blank
|
|
// password used instead
|
|
//
|
|
|
|
//pGenerateRandomPassword (randomPwd);
|
|
//Account.Password = randomPwd;
|
|
//LOG ((LOG_ACCOUNTS, "Random password for %s is %s", Account.User, Account.Password));
|
|
//
|
|
//g_RandomPassword = TRUE;
|
|
//attribs = PASSWORD_ATTR_RANDOM;
|
|
//pAddUserToRegistryList (Account.User, FALSE);
|
|
|
|
Account.Password = TEXT("");
|
|
}
|
|
}
|
|
}
|
|
Account.PasswordAttribs = attribs;
|
|
|
|
//
|
|
// put this user in the domain fix list if necessary
|
|
//
|
|
|
|
if (StringIMatch (User, g_AdministratorStr)) {
|
|
if (!MemDbGetValueEx (&e, MEMDB_CATEGORY_STATE, MEMDB_ITEM_ADMIN_PASSWORD, NULL)) {
|
|
MYASSERT (FALSE);
|
|
e.dwValue = PASSWORD_ATTR_RANDOM;
|
|
}
|
|
if (e.dwValue & PASSWORD_ATTR_RANDOM) {
|
|
//
|
|
// change the password for admin, too, because it was randomly generated
|
|
//
|
|
pAddUserToRegistryList (g_AdministratorStr, FALSE);
|
|
}
|
|
//
|
|
// don't change admin password now if the account is already created
|
|
//
|
|
Account.PasswordAttribs |= PASSWORD_ATTR_DONT_CHANGE_IF_EXIST;
|
|
} else if (pWasWin9xOnTheNet()) {
|
|
pAddUserToRegistryList (Account.User, TRUE);
|
|
}
|
|
|
|
//
|
|
// Now create the local account
|
|
//
|
|
|
|
rc = CreateLocalAccount (&Account, NULL);
|
|
|
|
FreeStringResource (Account.AdminComment);
|
|
|
|
if (rc != ERROR_SUCCESS) {
|
|
if (rc != ERROR_PASSWORD_RESTRICTION && rc != ERROR_INVALID_PARAMETER) {
|
|
//
|
|
// account could not be created
|
|
//
|
|
SetLastError (rc);
|
|
LOG ((LOG_ERROR, (PCSTR)MSG_CREATE_ACCOUNT_FAILED, User));
|
|
|
|
return FALSE;
|
|
}
|
|
//
|
|
// this user's password must be re-set
|
|
//
|
|
pAddUserToRegistryList (Account.User, FALSE);
|
|
}
|
|
|
|
DupDomain = FALSE;
|
|
|
|
}
|
|
} else {
|
|
|
|
DupDomain = FALSE;
|
|
|
|
}
|
|
|
|
if (DupDomain) {
|
|
UserDetails.Domain = PoolMemDuplicateString (g_UserPool, Domain);
|
|
}
|
|
|
|
//
|
|
// Get SID, looping until it's valid
|
|
//
|
|
|
|
do {
|
|
if (GetUserSid (User, Domain, &SidBuf)) {
|
|
//
|
|
// User SID was found, so we copy it to our pool and throw away the
|
|
// grow buffer.
|
|
//
|
|
|
|
UserDetails.Sid = (PSID) PoolMemGetMemory (g_UserPool, SidBuf.End);
|
|
CopyMemory (UserDetails.Sid, SidBuf.Buf, SidBuf.End);
|
|
FreeGrowBuffer (&SidBuf);
|
|
}
|
|
else {
|
|
//
|
|
// User SID was not found. We ask the user if they wish to retry.
|
|
//
|
|
|
|
PCWSTR ArgArray[1];
|
|
|
|
ArgArray[0] = User;
|
|
|
|
if (!RetryMessageBox (MSG_SID_RETRY, ArgArray)) {
|
|
|
|
if (!Domain) {
|
|
LOG ((LOG_ERROR, (PCSTR)MSG_SID_LOOKUP_FAILED, User));
|
|
return FALSE;
|
|
} else {
|
|
LOG ((LOG_ERROR, "Can't get SID for %s on %s, going to local account", User, Domain));
|
|
return pAddUser (User, NULL);
|
|
}
|
|
}
|
|
}
|
|
} while (!UserDetails.Sid);
|
|
|
|
if (CreateAccountFlag) {
|
|
//
|
|
// Get the user profile path
|
|
//
|
|
|
|
if (!CreateUserProfile (UserDetails.Sid, User, NULL, UserProfilePath, MAX_TCHAR_PATH)) {
|
|
|
|
LOG ((LOG_ERROR, (PCSTR)MSG_PROFILE_LOOKUP_FAILED, User));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
UserDetails.ProfilePath = PoolMemDuplicateString (g_UserPool, UserProfilePath);
|
|
DEBUGMSG ((DBG_ACCOUNTS, "User %s has profile path %s", User, UserProfilePath));
|
|
}
|
|
|
|
//
|
|
// Save user details in string table
|
|
//
|
|
|
|
pSetupStringTableAddStringEx (
|
|
g_UserTable,
|
|
(PWSTR) User,
|
|
STRTAB_CASE_INSENSITIVE,
|
|
(PBYTE) &UserDetails,
|
|
sizeof (USERDETAILS)
|
|
);
|
|
|
|
DEBUGMSG_IF ((Domain != NULL, DBG_ACCOUNTS, "%s\\%s added to user list", Domain, User));
|
|
DEBUGMSG_IF ((Domain == NULL, DBG_ACCOUNTS, "%s added to user list", User));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pAddLocalGroup (
|
|
IN PCWSTR Group
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds a local group to the array of users. This function allows local accounts
|
|
to be added to the user list.
|
|
|
|
Arguments:
|
|
|
|
Group - Specifies the name of the local group to add to the list
|
|
|
|
Return value:
|
|
|
|
TRUE if add was successful
|
|
|
|
--*/
|
|
|
|
{
|
|
USERDETAILS UserDetails;
|
|
GROWBUFFER SidBuf = GROWBUF_INIT;
|
|
|
|
UserDetails.Domain = NULL;
|
|
UserDetails.Sid = NULL;
|
|
|
|
if (GetUserSid (Group, NULL, &SidBuf)) {
|
|
//
|
|
// User SID was found, so we copy it to our pool and throw away the
|
|
// grow buffer.
|
|
//
|
|
|
|
UserDetails.Sid = (PSID) PoolMemGetMemory (g_UserPool, SidBuf.End);
|
|
CopyMemory (UserDetails.Sid, SidBuf.Buf, SidBuf.End);
|
|
FreeGrowBuffer (&SidBuf);
|
|
} else {
|
|
LOG ((LOG_ERROR, "%s is not a local group", Group));
|
|
return FALSE;
|
|
}
|
|
|
|
pSetupStringTableAddStringEx (
|
|
g_UserTable,
|
|
(PWSTR) Group,
|
|
STRTAB_CASE_INSENSITIVE,
|
|
(PBYTE) &UserDetails,
|
|
sizeof (USERDETAILS)
|
|
);
|
|
|
|
DEBUGMSG ((DBG_ACCOUNTS, "%s added to user list", Group));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pAddDomainGroup (
|
|
IN PCWSTR Group
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds a domain group to the array of users. This function is used to
|
|
add network domain groups to the user list.
|
|
|
|
Arguments:
|
|
|
|
Group - Specifies the name of the domain group to add to the list
|
|
|
|
Return value:
|
|
|
|
TRUE if add was successful
|
|
|
|
--*/
|
|
|
|
{
|
|
TCHAR DomainName[MAX_SERVER_NAME];
|
|
BYTE SidBuffer[MAX_SID_SIZE];
|
|
USERDETAILS UserDetails;
|
|
GROWBUFFER SidBuf = GROWBUF_INIT;
|
|
|
|
if (!GetPrimaryDomainName (DomainName)) {
|
|
DEBUGMSG ((DBG_WARNING, "Can't get primary domain name"));
|
|
return FALSE;
|
|
}
|
|
|
|
if (!GetPrimaryDomainSid (SidBuffer, sizeof (SidBuffer))) {
|
|
LOG ((LOG_ERROR, "Can't get primary domain SID of %s", DomainName));
|
|
return FALSE;
|
|
}
|
|
|
|
UserDetails.Domain = DomainName;
|
|
UserDetails.Sid = (PSID) SidBuffer;
|
|
|
|
if (GetUserSid (Group, DomainName, &SidBuf)) {
|
|
//
|
|
// User SID was found, so we copy it to our pool and throw away the
|
|
// grow buffer.
|
|
//
|
|
|
|
UserDetails.Sid = (PSID) PoolMemGetMemory (g_UserPool, SidBuf.End);
|
|
CopyMemory (UserDetails.Sid, SidBuf.Buf, SidBuf.End);
|
|
FreeGrowBuffer (&SidBuf);
|
|
} else {
|
|
DEBUGMSG ((DBG_WARNING, "Can't get SID of %s in %s", Group, DomainName));
|
|
FreeGrowBuffer (&SidBuf);
|
|
return FALSE;
|
|
}
|
|
|
|
pSetupStringTableAddStringEx (
|
|
g_UserTable,
|
|
(PWSTR) Group,
|
|
STRTAB_CASE_INSENSITIVE,
|
|
(PBYTE) &UserDetails,
|
|
sizeof (USERDETAILS)
|
|
);
|
|
|
|
DEBUGMSG ((DBG_ACCOUNTS, "%s\\%s added to user list", DomainName, Group));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
pResolveMultipleDomains (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Perpares an array of users and presents a dialog allowing the installer
|
|
to choose an action for each user. A user may be a local user, multiple domains
|
|
may be resolved, or the installer may choose to retry the account search.
|
|
|
|
The account list is updated based on the installer's choices.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
GROWBUFFER UserList = GROWBUF_INIT;
|
|
POOLHANDLE UserListData = NULL;
|
|
ACCT_ENUM UserEnum;
|
|
DWORD PossibleDomains;
|
|
PRESOLVE_ACCOUNTS_ARRAY Array = NULL;
|
|
PCTSTR User;
|
|
PCTSTR Domain;
|
|
PCTSTR *DomainList;
|
|
|
|
__try {
|
|
|
|
UserListData = PoolMemInitNamedPool ("UserListData");
|
|
|
|
//
|
|
// Create list of user accounts and allow the installer to decide weather
|
|
// to retry the search, use a local account, or choose a domain when
|
|
// multiple choices exist.
|
|
//
|
|
|
|
if (FindDomainInList (&UserEnum, S_UNKNOWN_DOMAIN)) {
|
|
User = ListFirstUserInDomain (&UserEnum, &UserEnum);
|
|
while (User) {
|
|
PossibleDomains = CountPossibleDomains (&UserEnum);
|
|
|
|
Array = (PRESOLVE_ACCOUNTS_ARRAY) GrowBuffer (
|
|
&UserList,
|
|
sizeof (RESOLVE_ACCOUNTS_ARRAY)
|
|
);
|
|
ZeroMemory (Array, sizeof (RESOLVE_ACCOUNTS_ARRAY));
|
|
|
|
Array->UserName = PoolMemDuplicateString (UserListData, User);
|
|
|
|
Array->DomainArray = (PCTSTR *) PoolMemGetAlignedMemory (
|
|
UserListData,
|
|
(PossibleDomains + 1) * sizeof (PCTSTR)
|
|
);
|
|
|
|
DomainList = Array->DomainArray;
|
|
|
|
Domain = ListFirstPossibleDomain (&UserEnum, &UserEnum);
|
|
while (Domain) {
|
|
*DomainList = PoolMemDuplicateString (UserListData, Domain);
|
|
DomainList++;
|
|
|
|
Domain = ListNextPossibleDomain (&UserEnum);
|
|
}
|
|
|
|
*DomainList = NULL;
|
|
|
|
//
|
|
// If UseLocalAccountOnError config option is TRUE, we set OutboundDomain
|
|
// to NULL, ensuring a Local Account.
|
|
//
|
|
if (g_ConfigOptions.UseLocalAccountOnError) {
|
|
Array->OutboundDomain = NULL;
|
|
Array->RetryFlag = FALSE;
|
|
}
|
|
else {
|
|
Array->OutboundDomain = *Array->DomainArray;
|
|
Array->RetryFlag = TRUE;
|
|
}
|
|
|
|
User = ListNextUserInDomain (&UserEnum);
|
|
}
|
|
}
|
|
|
|
if (!Array) {
|
|
//
|
|
// No unresolved users
|
|
//
|
|
|
|
__leave;
|
|
}
|
|
|
|
|
|
//
|
|
// If UseLocalAccountOnError is specified, we've already set all unresolved accounts to
|
|
// Local account and we aren't going to throw up any UI. Otherwise, we'll give the user
|
|
// the resolve UI.
|
|
//
|
|
if (!g_ConfigOptions.UseLocalAccountOnError) {
|
|
|
|
Array = (PRESOLVE_ACCOUNTS_ARRAY) GrowBuffer (
|
|
&UserList,
|
|
sizeof (RESOLVE_ACCOUNTS_ARRAY)
|
|
);
|
|
|
|
ZeroMemory (Array, sizeof (RESOLVE_ACCOUNTS_ARRAY));
|
|
|
|
ResolveAccounts ((PRESOLVE_ACCOUNTS_ARRAY) UserList.Buf);
|
|
}
|
|
|
|
//
|
|
// Now process the installer's changes to the user list
|
|
//
|
|
|
|
Array = (PRESOLVE_ACCOUNTS_ARRAY) UserList.Buf;
|
|
FindDomainInList (&UserEnum, S_UNKNOWN_DOMAIN);
|
|
|
|
while (Array->UserName) {
|
|
if (!FindUserInDomain (&UserEnum, &UserEnum, Array->UserName)) {
|
|
DEBUGMSG ((DBG_WHOOPS, "Could not find user name %s in array!", Array->UserName));
|
|
}
|
|
else if (!Array->RetryFlag) {
|
|
//
|
|
// The installer chose either to make the account local, or to
|
|
// use one of the several possible domains.
|
|
//
|
|
|
|
pAddUser (Array->UserName, Array->OutboundDomain);
|
|
DeleteUserFromDomainList (&UserEnum);
|
|
} else {
|
|
ClearPossibleDomains (&UserEnum);
|
|
}
|
|
|
|
Array++;
|
|
}
|
|
}
|
|
__finally {
|
|
FreeGrowBuffer (&UserList);
|
|
PoolMemDestroyPool (UserListData);
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
pMakeSureAccountsAreValid (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Scans the domain lists and adds all valid users to our user list. Any
|
|
invalid users are put back in the unknown domain.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return value:
|
|
|
|
TRUE if unresolved users exist (meaning the installer wants to retry
|
|
searching), or FALSE if all domains for each user has been resolved.
|
|
|
|
--*/
|
|
|
|
{
|
|
ACCT_ENUM UserEnum;
|
|
INT PossibleDomains;
|
|
PCWSTR Domain;
|
|
PCWSTR User;
|
|
BOOL b;
|
|
|
|
//
|
|
// Scan the unknown list for matches where one domain was found
|
|
//
|
|
|
|
if (FindDomainInList (&UserEnum, S_UNKNOWN_DOMAIN)) {
|
|
User = ListFirstUserInDomain (&UserEnum, &UserEnum);
|
|
while (User) {
|
|
PossibleDomains = CountPossibleDomains (&UserEnum);
|
|
|
|
if (PossibleDomains == 1 && !g_DomainProblem) {
|
|
Domain = ListFirstPossibleDomain (&UserEnum, &UserEnum);
|
|
|
|
pAddUser (User, Domain);
|
|
DeleteUserFromDomainList (&UserEnum);
|
|
}
|
|
|
|
User = ListNextUserInDomain (&UserEnum);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Add all correct known accounts to our user list
|
|
//
|
|
|
|
Domain = ListFirstDomain (&UserEnum);
|
|
while (Domain) {
|
|
if (!IsTrustedDomain (&UserEnum)) {
|
|
Domain = ListNextDomain (&UserEnum);
|
|
continue;
|
|
}
|
|
|
|
User = ListFirstUserInDomain (&UserEnum, &UserEnum);
|
|
while (User) {
|
|
pAddUser (User, Domain);
|
|
DeleteUserFromDomainList (&UserEnum);
|
|
User = ListNextUserInDomain (&UserEnum);
|
|
}
|
|
|
|
Domain = ListNextDomain (&UserEnum);
|
|
}
|
|
|
|
//
|
|
// Move the failed list to the unknown list
|
|
//
|
|
|
|
if (FindDomainInList (&UserEnum, S_FAILED_DOMAIN)) {
|
|
|
|
User = ListFirstUserInDomain (&UserEnum, &UserEnum);
|
|
|
|
while (User) {
|
|
|
|
if (g_ConfigOptions.UseLocalAccountOnError) {
|
|
pAddUser (User, NULL);
|
|
DeleteUserFromDomainList (&UserEnum);
|
|
} else {
|
|
MoveUserToNewDomain (&UserEnum, S_UNKNOWN_DOMAIN);
|
|
}
|
|
|
|
User = ListNextUserInDomain (&UserEnum);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Provide UI for remaining unknowns, allowing manual decision
|
|
// weather to make account local, or to decide between multiple
|
|
// domain matches.
|
|
//
|
|
|
|
HideStatusPopup (INFINITE);
|
|
pResolveMultipleDomains();
|
|
|
|
//
|
|
// Return TRUE if the unknown domain is not empty
|
|
//
|
|
|
|
b = FindDomainInList (&UserEnum, S_UNKNOWN_DOMAIN);
|
|
if (b) {
|
|
b = CountUsersInDomain (&UserEnum) != 0;
|
|
|
|
if (b) {
|
|
HideStatusPopup (STATUS_DELAY);
|
|
}
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pGetUserDetails (
|
|
IN PCWSTR User,
|
|
OUT PUSERDETAILS DetailsPtr
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
A common routine that locates the USERDETAILS structure for a
|
|
given user.
|
|
|
|
Arguments:
|
|
|
|
User - Specifies fixed user name
|
|
|
|
DetailsPtr - Receives the structure for the user
|
|
|
|
Return value:
|
|
|
|
TRUE if the user details were found, or FALSE if the user does
|
|
not exist.
|
|
|
|
--*/
|
|
|
|
{
|
|
return (-1 != pSetupStringTableLookUpStringEx (
|
|
g_UserTable,
|
|
(PWSTR) User,
|
|
STRTAB_CASE_INSENSITIVE,
|
|
(PBYTE) DetailsPtr,
|
|
sizeof(USERDETAILS)
|
|
));
|
|
}
|
|
|
|
|
|
PCWSTR
|
|
GetDomainForUser (
|
|
IN PCWSTR User
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Given a user name, this function returns a pointer to the domain name
|
|
maintained in the user list for the specified user.
|
|
|
|
Since Windows 95 does not support multiple domains for a single user,
|
|
the user list doesn't either.
|
|
|
|
Arguments:
|
|
|
|
User - Specifies fixed user name
|
|
|
|
Return value:
|
|
|
|
A pointer to the domain name for the user, or NULL if the user was not
|
|
found.
|
|
|
|
--*/
|
|
|
|
{
|
|
USERDETAILS Details;
|
|
|
|
if (pGetUserDetails (User, &Details)) {
|
|
return Details.Domain;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
PSID
|
|
GetSidForUser (
|
|
PCWSTR User
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Given a user name, this function returns a pointer to the SID maintained
|
|
in the user list for the specified user.
|
|
|
|
Arguments:
|
|
|
|
User - Specifies fixed user name
|
|
|
|
Return value:
|
|
|
|
A pointer to the SID for the user, or NULL if the user was not found.
|
|
|
|
--*/
|
|
|
|
{
|
|
USERDETAILS Details;
|
|
|
|
if (pGetUserDetails (User, &Details)) {
|
|
return Details.Sid;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
PCWSTR
|
|
GetProfilePathForUser (
|
|
IN PCWSTR User
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Given a user name, this function returns a pointer to the user's
|
|
profile, maintained in the user list for the specified user.
|
|
|
|
Arguments:
|
|
|
|
User - Specifies fixed user name
|
|
|
|
Return value:
|
|
|
|
A pointer to the user's profile path, or NULL if the specified user
|
|
was not in the list.
|
|
|
|
--*/
|
|
|
|
{
|
|
USERDETAILS Details;
|
|
|
|
if (pGetUserDetails (User, &Details)) {
|
|
return Details.ProfilePath;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pWasWin9xOnTheNet (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Queries memdb to determine if the machine had the MS Networking Client
|
|
installed.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return value:
|
|
|
|
TRUE if the Win9x configuration had MSNP32 installed, FALSE if not.
|
|
|
|
--*/
|
|
|
|
{
|
|
TCHAR Node[MEMDB_MAX];
|
|
|
|
MemDbBuildKey (Node, MEMDB_CATEGORY_STATE, MEMDB_ITEM_MSNP32, NULL, NULL);
|
|
return MemDbGetValue (Node, NULL);
|
|
}
|
|
|
|
|
|
VOID
|
|
pGenerateRandomPassword (
|
|
OUT PTSTR Password
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pGenerateRandomPassword creates a password of upper-case, lower-case and
|
|
numeric letters. The password has a length between 8 and 14
|
|
characters.
|
|
|
|
Arguments:
|
|
|
|
Password - Receives the generated password
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
INT Length;
|
|
TCHAR Offset;
|
|
INT Limit;
|
|
PTSTR p;
|
|
|
|
//
|
|
// Generate a random length based on the tick count
|
|
//
|
|
|
|
srand (GetTickCount());
|
|
|
|
Length = (rand() % 6) + 8;
|
|
|
|
p = Password;
|
|
while (Length) {
|
|
Limit = rand() % 3;
|
|
Offset = TEXT(' ');
|
|
|
|
if (Limit == 0) {
|
|
Limit = 10;
|
|
Offset = TEXT('0');
|
|
} else if (Limit == 1) {
|
|
Limit = 26;
|
|
Offset = TEXT('a');
|
|
} else if (Limit == 2) {
|
|
Limit = 26;
|
|
Offset = TEXT('A');
|
|
}
|
|
|
|
*p = Offset + (rand() % Limit);
|
|
p++;
|
|
|
|
Length--;
|
|
}
|
|
|
|
*p = 0;
|
|
|
|
DEBUGMSG ((DBG_ACCOUNTS, "Generated password: %s", Password));
|
|
}
|