WindowsXP-SP1/admin/dcpromo/exe/credentialspage.cpp
2020-09-30 16:53:49 +02:00

573 lines
13 KiB
C++

// Copyright (c) 1997-1999 Microsoft Corporation
//
// get credentials page
//
// 12-22-97 sburns
#include "headers.hxx"
#include "page.hpp"
#include "CredentialsPage.hpp"
#include "resource.h"
#include "state.hpp"
#include "ds.hpp"
#include "CredentialUiHelpers.hpp"
#include "common.hpp"
#include <DiagnoseDcNotFound.hpp>
CredentialsPage::CredentialsPage()
:
DCPromoWizardPage(
IDD_GET_CREDENTIALS,
IDS_CREDENTIALS_PAGE_TITLE,
IDS_CREDENTIALS_PAGE_SUBTITLE),
readAnswerfile(false)
{
LOG_CTOR(CredentialsPage);
CredUIInitControls();
}
CredentialsPage::~CredentialsPage()
{
LOG_DTOR(CredentialsPage);
}
void
CredentialsPage::OnInit()
{
LOG_FUNCTION(CredentialsPage::OnInit);
HWND hwndCred = Win::GetDlgItem(hwnd, IDC_CRED);
Credential_SetUserNameMaxChars(hwndCred, DS::MAX_USER_NAME_LENGTH);
Credential_SetPasswordMaxChars(hwndCred, DS::MAX_PASSWORD_LENGTH);
// Only use the smartcard flag when the machine is joined to a domain. On a
// standalone machine, the smartcard won't have access to any domain
// authority to authenticate it.
// NTRAID#NTBUG9-287538-2001/01/23-sburns
State& state = State::GetInstance();
Computer& computer = state.GetComputer();
DWORD flags = CRS_NORMAL | CRS_USERNAMES;
if (
computer.IsJoinedToDomain()
// can only use smartcards on replica promotions
// NTRAID#NTBUG9-311150-2001/02/19-sburns
&& state.GetOperation() == State::REPLICA)
{
flags |= CRS_SMARTCARDS;
}
Credential_InitStyle(hwndCred, flags);
Win::Edit_LimitText(
Win::GetDlgItem(hwnd, IDC_DOMAIN),
Dns::MAX_NAME_LENGTH);
}
void
CredentialsPage::Enable()
{
// LOG_FUNCTION(CredentialsPage::Enable);
int next =
( !CredUi::GetUsername(Win::GetDlgItem(hwnd, IDC_CRED)).empty()
&& !Win::GetTrimmedDlgItemText(hwnd, IDC_DOMAIN).empty() )
? PSWIZB_NEXT : 0;
Win::PropSheet_SetWizButtons(
Win::GetParent(hwnd),
PSWIZB_BACK | next);
}
bool
CredentialsPage::OnCommand(
HWND /* windowFrom */ ,
unsigned controlIDFrom,
unsigned code)
{
// LOG_FUNCTION(CredentialsPage::OnCommand);
switch (controlIDFrom)
{
case IDC_CRED:
{
if (code == CRN_USERNAMECHANGE)
{
SetChanged(controlIDFrom);
Enable();
return true;
}
break;
}
case IDC_DOMAIN:
{
if (code == EN_CHANGE)
{
SetChanged(controlIDFrom);
Enable();
return true;
}
break;
}
default:
{
// do nothing
break;
}
}
return false;
}
bool
CredentialsPage::ShouldSkipPage()
{
LOG_FUNCTION(CredentialsPage::ShouldSkipPage);
State& state = State::GetInstance();
State::Operation oper = state.GetOperation();
bool result = false;
switch (oper)
{
case State::FOREST:
{
// never need credentials for new forest.
result = true;
break;
}
case State::DEMOTE:
{
// The demote page should circumvent this page if necessary, so if
// we make it here, don't skip the page.
break;
}
case State::ABORT_BDC_UPGRADE:
case State::REPLICA:
case State::TREE:
case State::CHILD:
{
break;
}
case State::NONE:
default:
{
ASSERT(false);
break;
}
}
return result;
}
int
CredentialsPage::DetermineNextPage()
{
LOG_FUNCTION(CredentialsPage::DetermineNextPage);
State& state = State::GetInstance();
int id = IDD_PATHS;
switch (state.GetOperation())
{
case State::DEMOTE:
case State::ABORT_BDC_UPGRADE:
{
id = IDD_ADMIN_PASSWORD;
break;
}
case State::FOREST:
{
id = IDD_NEW_FOREST;
break;
}
case State::REPLICA:
{
if (state.GetRunContext() == State::BDC_UPGRADE)
{
id = IDD_PATHS;
}
else
{
id = IDD_REPLICA;
}
break;
}
case State::TREE:
{
id = IDD_NEW_TREE;
break;
}
case State::CHILD:
{
id = IDD_NEW_CHILD;
break;
}
case State::NONE:
default:
{
ASSERT(false);
break;
}
}
return id;
}
String
GetMessage()
{
String message;
State& state = State::GetInstance();
switch (state.GetOperation())
{
case State::ABORT_BDC_UPGRADE:
{
message = String::load(IDS_ABORT_BDC_UPGRADE_CREDENTIALS);
break;
}
case State::TREE:
case State::CHILD:
case State::REPLICA:
{
message = String::load(IDS_PROMOTION_CREDENTIALS);
break;
}
case State::DEMOTE:
{
// 318736 demote requires enterprise admin credentials -- for
// root and child domains alike.
message =
String::format(
IDS_ROOT_DOMAIN_CREDENTIALS,
state.GetComputer().GetForestDnsName().c_str());
break;
}
case State::FOREST:
{
// do nothing, the page will be skipped.
break;
}
case State::NONE:
default:
{
ASSERT(false);
break;
}
}
return message;
}
String
DefaultUserDomainName()
{
String d;
State& state = State::GetInstance();
const Computer& computer = state.GetComputer();
switch (state.GetOperation())
{
case State::ABORT_BDC_UPGRADE:
case State::FOREST:
{
// do nothing
break;
}
case State::DEMOTE: // 301361
case State::TREE:
{
d = computer.GetForestDnsName();
break;
}
case State::CHILD:
{
d = computer.GetDomainDnsName();
break;
}
case State::REPLICA:
{
d = computer.GetDomainDnsName();
if (d.empty() && state.ReplicateFromMedia())
{
d = state.GetReplicaDomainDNSName();
}
break;
}
case State::NONE:
default:
{
ASSERT(false);
break;
}
}
return d;
}
bool
CredentialsPage::OnSetActive()
{
LOG_FUNCTION(CredentialsPage::OnSetActive);
Win::WaitCursor cursor;
State& state = State::GetInstance();
Wizard& wiz = GetWizard();
HWND hwndCred = Win::GetDlgItem(hwnd, IDC_CRED);
if (ShouldSkipPage())
{
LOG(L"skipping CredentialsPage");
if (wiz.IsBacktracking())
{
// backup once again
wiz.Backtrack(hwnd);
}
else
{
wiz.SetNextPageID(hwnd, DetermineNextPage());
}
return true;
}
if (!readAnswerfile && state.UsingAnswerFile())
{
CredUi::SetUsername(
hwndCred,
state.GetAnswerFileOption(State::OPTION_USERNAME));
CredUi::SetPassword(
hwndCred,
state.GetEncodedAnswerFileOption(State::OPTION_PASSWORD));
String domain = state.GetAnswerFileOption(State::OPTION_USER_DOMAIN);
if (domain.empty())
{
domain = DefaultUserDomainName();
}
Win::SetDlgItemText(
hwnd,
IDC_DOMAIN,
domain);
readAnswerfile = true;
}
if (state.RunHiddenUnattended())
{
int nextPage = CredentialsPage::Validate();
if (nextPage != -1)
{
wiz.SetNextPageID(hwnd, nextPage);
return true;
}
else
{
state.ClearHiddenWhileUnattended();
}
}
// use the credentials last entered (for browsing, etc.)
CredUi::SetUsername(hwndCred, state.GetUsername());
CredUi::SetPassword(hwndCred, state.GetPassword());
Win::SetDlgItemText(hwnd, IDC_DOMAIN, state.GetUserDomainName());
Win::PropSheet_SetWizButtons(
Win::GetParent(hwnd),
PSWIZB_BACK);
Enable();
Win::SetDlgItemText(hwnd, IDC_MESSAGE, GetMessage());
if (Win::GetTrimmedDlgItemText(hwnd, IDC_DOMAIN).empty())
{
// supply a default domain if none already present
Win::SetDlgItemText(hwnd, IDC_DOMAIN, DefaultUserDomainName());
}
return true;
}
int
CredentialsPage::Validate()
{
LOG_FUNCTION(CredentialsPage::Validate);
int nextPage = -1;
do
{
if (
!WasChanged(IDC_CRED)
&& !WasChanged(IDC_DOMAIN))
{
// nothing changed => nothing to validate
nextPage = DetermineNextPage();
break;
}
State& state = State::GetInstance();
HWND hwndCred = Win::GetDlgItem(hwnd, IDC_CRED);
String username = CredUi::GetUsername(hwndCred);
if (username.empty())
{
popup.Gripe(hwnd, IDC_CRED, IDS_MUST_ENTER_USERNAME);
break;
}
String domain = Win::GetTrimmedDlgItemText(hwnd, IDC_DOMAIN);
if (domain.empty())
{
popup.Gripe(hwnd, IDC_DOMAIN, IDS_MUST_ENTER_USER_DOMAIN);
break;
}
Win::WaitCursor cursor;
// the domain must be an NT 5 domain: no user of a downlevel domain
// could perform operations in an NT 5 forest. We get the forest name
// of the domain ('cause that may be useful for the new tree scenario)
// as a means of validating the domain name. If the domain does not
// exist, or is not an NT5 domain, then this call will fail.
String forest = GetForestName(domain);
if (forest.empty())
{
ShowDcNotFoundErrorDialog(
hwnd,
IDC_DOMAIN,
domain,
String::load(IDS_WIZARD_TITLE),
String::format(IDS_DC_NOT_FOUND, domain.c_str()),
false);
break;
}
if (state.GetOperation() == State::TREE)
{
// For the new tree case, we need to validate the forest name (a dns
// domain name) by ensuring that we can find a writable DS DC in that
// domain. The user may have supplied a netbios domain name, and it
// is possible that the domain's DNS registration is broken. Since
// we will use the forest name as the parent domain name in the call
// to DsRoleDcAsDc, we need to make sure we can find DCs with that
// name. 122886
DOMAIN_CONTROLLER_INFO* info = 0;
HRESULT hr =
MyDsGetDcName(
0,
forest,
// force discovery to ensure that we don't pick up a cached
// entry for a domain that may no longer exist, writeable
// and DS because we happen to know that's what the
// DsRoleDcAsDc API will require.
DS_FORCE_REDISCOVERY
| DS_WRITABLE_REQUIRED
| DS_DIRECTORY_SERVICE_REQUIRED,
info);
if (FAILED(hr) || !info)
{
ShowDcNotFoundErrorDialog(
hwnd,
IDC_DOMAIN,
forest,
String::load(IDS_WIZARD_TITLE),
String::format(IDS_DC_FOR_ROOT_NOT_FOUND, forest.c_str()),
// we know the name can't be netbios: forest names are always
// DNS names
true);
break;
}
::NetApiBufferFree(info);
}
state.SetUserForestName(forest);
// set these now so we can read the domain topology
state.SetUsername(username);
state.SetPassword(CredUi::GetPassword(hwndCred));
state.SetUserDomainName(domain);
// cache the domain topology: this is used to validate new tree,
// child, and replica domain names in later pages. It's also a
// pretty good validation of the credentials.
HRESULT hr = state.ReadDomains();
if (FAILED(hr))
{
popup.Gripe(
hwnd,
IDC_DOMAIN,
hr,
String::format(IDS_UNABLE_TO_READ_FOREST));
break;
}
// valid
ClearChanges();
nextPage = DetermineNextPage();
}
while (0);
return nextPage;
}