Leaked source code of windows server 2003
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.
 
 
 
 
 
 

475 lines
13 KiB

// Copyright (C) 2002 Microsoft Corporation
//
// answerfile reader object
//
// 5 April 2002 sburns
#include "headers.hxx"
#include "AnswerFile.hpp"
#include "resource.h"
static const String SECTION_NAME(L"DCInstall");
const String AnswerFile::OPTION_ADMIN_PASSWORD (L"AdministratorPassword");
const String AnswerFile::OPTION_ALLOW_ANON_ACCESS (L"AllowAnonymousAccess");
const String AnswerFile::OPTION_AUTO_CONFIG_DNS (L"AutoConfigDNS");
const String AnswerFile::OPTION_CHILD_NAME (L"ChildName");
const String AnswerFile::OPTION_CRITICAL_REPLICATION_ONLY (L"CriticalReplicationOnly");
const String AnswerFile::OPTION_DATABASE_PATH (L"DatabasePath");
const String AnswerFile::OPTION_DISABLE_CANCEL_ON_DNS_INSTALL (L"DisableCancelForDnsInstall");
const String AnswerFile::OPTION_DNS_ON_NET (L"DNSOnNetwork");
const String AnswerFile::OPTION_GC_CONFIRM (L"ConfirmGc");
const String AnswerFile::OPTION_IS_LAST_DC (L"IsLastDCInDomain");
const String AnswerFile::OPTION_LOG_PATH (L"LogPath");
const String AnswerFile::OPTION_NEW_DOMAIN (L"NewDomain");
const String AnswerFile::OPTION_NEW_DOMAIN_NAME (L"NewDomainDNSName");
const String AnswerFile::OPTION_NEW_DOMAIN_NETBIOS_NAME (L"DomainNetbiosName");
const String AnswerFile::OPTION_PARENT_DOMAIN_NAME (L"ParentDomainDNSName");
const String AnswerFile::OPTION_PASSWORD (L"Password");
const String AnswerFile::OPTION_REBOOT (L"RebootOnSuccess");
const String AnswerFile::OPTION_REMOVE_APP_PARTITIONS (L"RemoveApplicationPartitions");
const String AnswerFile::OPTION_REPLICATION_SOURCE (L"ReplicationSourceDC");
const String AnswerFile::OPTION_REPLICA_DOMAIN_NAME (L"ReplicaDomainDNSName");
const String AnswerFile::OPTION_REPLICA_OR_MEMBER (L"ReplicaOrMember");
const String AnswerFile::OPTION_REPLICA_OR_NEW_DOMAIN (L"ReplicaOrNewDomain");
const String AnswerFile::OPTION_SAFE_MODE_ADMIN_PASSWORD (L"SafeModeAdminPassword");
const String AnswerFile::OPTION_SET_FOREST_VERSION (L"SetForestVersion");
const String AnswerFile::OPTION_SITE_NAME (L"SiteName");
const String AnswerFile::OPTION_SOURCE_PATH (L"ReplicationSourcePath");
const String AnswerFile::OPTION_SYSKEY (L"Syskey");
const String AnswerFile::OPTION_SYSVOL_PATH (L"SYSVOLPath");
const String AnswerFile::OPTION_USERNAME (L"UserName");
const String AnswerFile::OPTION_USER_DOMAIN (L"UserDomain");
const String AnswerFile::VALUE_CHILD (L"Child");
const String AnswerFile::VALUE_DOMAIN (L"Domain");
const String AnswerFile::VALUE_NO (L"No");
const String AnswerFile::VALUE_NO_DONT_PROMPT (L"NoAndNoPromptEither");
const String AnswerFile::VALUE_REPLICA (L"Replica");
const String AnswerFile::VALUE_TREE (L"Tree");
const String AnswerFile::VALUE_YES (L"Yes");
static StringList PASSWORD_OPTIONS_LIST;
static bool passwordOptionsListInitialized = false;
void
GetAllKeys(const String& filename, StringList& resultList)
{
LOG_FUNCTION(GetAllKeys);
ASSERT(FS::IsValidPath(filename));
resultList.clear();
// our first call is with a large buffer, hoping that it will suffice...
#ifdef DBG
// on chk builds, use a small buffer size so that our growth algorithm
// gets exercised
unsigned bufSizeInCharacters = 3;
#else
unsigned bufSizeInCharacters = 1023;
#endif
PWSTR buffer = 0;
do
{
buffer = new WCHAR[bufSizeInCharacters + 1];
// REVIEWED-2002/02/22-sburns byte count correctly passed in
::ZeroMemory(buffer, (bufSizeInCharacters + 1) * sizeof WCHAR);
DWORD result =
// REVIEWED-2002/02/22-sburns call correctly passes size in characters.
::GetPrivateProfileString(
SECTION_NAME.c_str(),
0,
L"default",
buffer,
bufSizeInCharacters,
filename.c_str());
if (!result)
{
break;
}
// A value was found. check to see if it was truncated. Since lpKeyName
// was null, check result against character count - 2
if (result == bufSizeInCharacters - 2)
{
// buffer was too small, so the value was truncated. Resize the
// buffer and try again.
// no need to scribble out the buffer: we're retrieving key names,
// not values.
delete[] buffer;
bufSizeInCharacters *= 2;
if (bufSizeInCharacters > USHRT_MAX) // effectively ~32K max
{
// too big. way too big. We'll make do with the truncated value.
ASSERT(false);
break;
}
continue;
}
// copy out the strings results into list elements
PWSTR p = buffer;
while (*p)
{
resultList.push_back(p);
// REVIEWED-2002/04/08-sburns wcslen is ok, since we arrange for
// the buffer to be null terminated
p += wcslen(p) + 1;
}
break;
}
//lint -e506 ok that this looks like "loop forever"
while (true);
delete[] buffer;
}
AnswerFile::AnswerFile(const String& filename_)
:
filename(filename_),
isSafeModePasswordPresent(false)
{
LOG_CTOR(AnswerFile);
// the caller is expected to have verified this
ASSERT(FS::PathExists(filename));
GetAllKeys(filename, keysPresent);
isSafeModePasswordPresent = IsKeyPresent(OPTION_SAFE_MODE_ADMIN_PASSWORD);
// remove read-only file attribute
DWORD attrs = 0;
HRESULT hr = Win::GetFileAttributes(filename, attrs);
if (SUCCEEDED(hr) && attrs & FILE_ATTRIBUTE_READONLY)
{
LOG(L"Removing readonly attribute on " + filename);
hr = Win::SetFileAttributes(filename, attrs & ~FILE_ATTRIBUTE_READONLY);
// if this failed, well, we tried. The user runs the risk of cleartext
// passwords left in his file.
LOG_HRESULT(hr);
}
// Read all the password options into the encrypted value map, erasing
// them as we go.
if (!passwordOptionsListInitialized)
{
ASSERT(PASSWORD_OPTIONS_LIST.empty());
PASSWORD_OPTIONS_LIST.clear();
PASSWORD_OPTIONS_LIST.push_back(OPTION_PASSWORD);
PASSWORD_OPTIONS_LIST.push_back(OPTION_ADMIN_PASSWORD);
PASSWORD_OPTIONS_LIST.push_back(OPTION_SYSKEY);
PASSWORD_OPTIONS_LIST.push_back(OPTION_SAFE_MODE_ADMIN_PASSWORD);
passwordOptionsListInitialized = true;
}
String empty;
for (
StringList::iterator i = PASSWORD_OPTIONS_LIST.begin();
i != PASSWORD_OPTIONS_LIST.end();
++i)
{
if (IsKeyPresent(*i))
{
ovMap[*i] = EncryptedReadKey(*i);
hr = WriteKey(*i, empty);
if (FAILED(hr))
{
popup.Error(
Win::GetDesktopWindow(),
hr,
String::format(
IDS_FAILED_PASSWORD_WRITE_TO_ANSWERFILE,
i->c_str(),
filename.c_str()));
}
}
}
}
AnswerFile::~AnswerFile()
{
LOG_DTOR(AnswerFile);
}
String
AnswerFile::ReadKey(const String& key) const
{
LOG_FUNCTION2(AnswerFile::ReadKey, key);
ASSERT(!key.empty());
String result =
// REVIEWED-2002/02/22-sburns no cch/cb issue here.
Win::GetPrivateProfileString(SECTION_NAME, key, String(), filename);
// Don't log the value, as it may be a password.
// LOG(L"value=" + result);
return result.strip(String::BOTH);
}
EncryptedString
AnswerFile::EncryptedReadKey(const String& key) const
{
LOG_FUNCTION2(AnswerFile::EncodedReadKey, key);
ASSERT(!key.empty());
EncryptedString retval;
#ifdef DBG
// on chk builds, use a small buffer size so that our growth algorithm
// gets exercised
unsigned bufSizeInCharacters = 3;
#else
unsigned bufSizeInCharacters = 1023;
#endif
PWSTR buffer = 0;
do
{
// +1 for extra null-termination paranoia
buffer = new WCHAR[bufSizeInCharacters + 1];
// REVIEWED-2002/02/22-sburns byte count correctly passed in
::ZeroMemory(buffer, (bufSizeInCharacters + 1) * sizeof WCHAR);
DWORD result =
// REVIEWED-2002/02/22-sburns call correctly passes size in characters.
::GetPrivateProfileString(
SECTION_NAME.c_str(),
key.c_str(),
L"",
buffer,
bufSizeInCharacters,
filename.c_str());
if (!result)
{
break;
}
// A value was found. check to see if it was truncated. neither
// lpAppName nor lpKeyName were null, so check result against character
// count - 1
if (result == bufSizeInCharacters - 1)
{
// buffer was too small, so the value was truncated. Resize the
// buffer and try again.
// Since the buffer may have contained passwords, scribble it
// out
// REVIEWED-2002/02/22-sburns byte count correctly passed in
::SecureZeroMemory(buffer, (bufSizeInCharacters + 1) * sizeof WCHAR);
delete[] buffer;
bufSizeInCharacters *= 2;
if (bufSizeInCharacters > USHRT_MAX) // effectively ~32K max
{
// too big. way too big. We'll make do with the truncated value.
ASSERT(false);
break;
}
continue;
}
// don't need to trim whitespace, GetPrivateProfileString does that
// for us.
retval.Encrypt(buffer);
break;
}
while (true);
// Since the buffer may have contained passwords, scribble it
// out
// REVIEWED-2002/02/22-sburns byte count correctly passed in
::SecureZeroMemory(buffer, (bufSizeInCharacters + 1) * sizeof WCHAR);
delete[] buffer;
// Don't log the value, as it may be a password.
// LOG(L"value=" + result);
return retval;
}
HRESULT
AnswerFile::WriteKey(const String& key, const String& value) const
{
LOG_FUNCTION2(AnswerFile::WriteKey, key);
ASSERT(!key.empty());
HRESULT hr =
Win::WritePrivateProfileString(SECTION_NAME, key, value, filename);
return hr;
}
bool
AnswerFile::IsKeyPresent(const String& key) const
{
LOG_FUNCTION2(AnswerFile::IsKeyPresent, key);
ASSERT(!key.empty());
bool result = false;
// If GetAllKeys failed, then we won't find the option in the keys list
// and will assume that the option is not specified. This is the best
// we can do in the case where we can't read the keys.
if (
std::find(keysPresent.begin(), keysPresent.end(), key)
!= keysPresent.end() )
{
result = true;
}
LOG_BOOL(result);
return result;
}
bool
IsPasswordOption(const String& option)
{
ASSERT(passwordOptionsListInitialized);
bool result = false;
if (
std::find(
PASSWORD_OPTIONS_LIST.begin(),
PASSWORD_OPTIONS_LIST.end(),
option)
!= PASSWORD_OPTIONS_LIST.end() )
{
result = true;
}
return result;
}
String
AnswerFile::GetOption(const String& option) const
{
LOG_FUNCTION2(AnswerFile::GetOption, option);
String result = ReadKey(option);
if (!IsPasswordOption(option))
{
LOG(result);
}
else
{
// should use GetEncryptedAnswerFileOption for passwords
ASSERT(false);
}
return result;
}
EncryptedString
AnswerFile::GetEncryptedOption(const String& option) const
{
LOG_FUNCTION2(AnswerFile::GetEncryptedOption, option);
OptionEncryptedValueMap::const_iterator ci = ovMap.find(option);
if (ci != ovMap.end())
{
return ci->second;
}
return EncryptedString();
}
bool
AnswerFile::IsSafeModeAdminPwdOptionPresent() const
{
LOG_FUNCTION(AnswerFile::IsSafeModeAdminPwdOptionPresent);
LOG_BOOL(isSafeModePasswordPresent);
return isSafeModePasswordPresent;
}