windows-nt/Source/XPSP1/NT/ds/netapi/netlib/netjoin.c

4447 lines
141 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1997 - 1997 Microsoft Corporation
Module Name:
netjoin.c
Abstract:
Implementation of the private Net setup apis for joining/unjoinging
domains.
Author:
Mac McLain (MacM) 19-Feb-1997
Environment:
User mode only.
Revision History:
--*/
// Netlib uses DsGetDcName AND is linked into netapi32 where DsGetDcName is
// implemented. So define that we aren't importing the API.
#define _DSGETDCAPI_
#include <netsetp.h>
#include <lmaccess.h>
#include <lmsname.h>
#include <winreg.h>
#include <wincrypt.h>
#include <icanon.h>
#include <dnsapi.h>
#include <netlib.h>
#include <dsgetdcp.h>
#include <dsrole.h>
#include <names.h>
#include "joinp.h"
/* -----------------------------------------------------------------
Joining a domain
----------------
When a computer joins a domain, the following changes occur on
the computer and the dc of that domain. The changes are not made
in the order shown here.
changes on client computer:
---------------------------
NT4 & NT5
~~~~~~~~~
- create a LSA secret named $MACHINE.ACC. The value of this
secret is the password to be used for accessing the machine
account for this computer on the dc.
- set LSA PolicyPrimaryDomainInformation. this includes:
- domain name
- domain SID
- add certain user groups in the new domain to local groups
- enable and start netlogon service
NT5 only
~~~~~~~~~
- update the netlogon cache to indicate the new domain details
- set LSA PolicyDnsDomainInformation. this includes:
- domain name
- domain SID
- dns domain name
- dns forest name
- domain guid
- set ComputerNamePhysicalDnsDomain if changed.
- enable and start w32time service
- for performance reasons, record locally info about the dc
on which machine account is created.
whistler only
-------------
- instead of starting w32time, call w32time!W32TimeVerifyJoinConfig
(when unjoining, call w32time!W32TimeVerifyUnjoinConfig instead)
changes on dc:
--------------
NT4 & NT5
~~~~~~~~~
- create a computer object. the name of this object is generated by
appending a '$' to the uppercased name of the client computer.
This object is protected by a password that is stored in $MACHINE.ACC
as explained earlier.
NT5 only
~~~~~~~~~
- create SPN of the computer object
this is not directly created by the netjoin code. netjoin code
waits on netlogon to perform this creation.
----------------------------------------------------------------- */
NET_API_STATUS
NET_API_FUNCTION
NetpJoinWorkgroup(
IN LPWSTR lpMachine,
IN LPWSTR lpWrkgrp
)
/*++
Routine Description:
Joins the machine to the specified workgroup
Arguments:
lpMachine -- Name of the machine being run on
lpWrkgrp -- Workgroup to join
Returns:
NERR_Success -- Success
--*/
{
NET_API_STATUS NetStatus = NERR_Success;
NetSetuppOpenLog();
NetpLog(( "NetpJoinWorkgroup: joining computer '%ws' to workgroup '%ws'\n",
lpMachine, lpWrkgrp ));
//
// First, check the name...
//
NetStatus = NetpValidateName( lpMachine, lpWrkgrp, NULL, NULL,
NetSetupWorkgroup );
if ( NetStatus == NERR_Success )
{
NetStatus = NetpSetLsaPrimaryDomain(NULL, lpWrkgrp, NULL, NULL, NULL);
if ( NetStatus == NERR_Success )
{
NetStatus = NetpControlServices( NETSETUP_SVC_MANUAL,
NETSETUPP_SVC_NETLOGON );
if ( NetStatus == ERROR_SERVICE_DOES_NOT_EXIST )
{
NetStatus = STATUS_SUCCESS;
}
}
}
NetpLog(( "NetpJoinWorkgroup: status: 0x%lx\n",
NetStatus ));
NetSetuppCloseLog();
return( NetStatus );
}
NET_API_STATUS
NET_API_FUNCTION
NetpMachineValidToJoin(
IN LPWSTR lpMachine,
IN BOOL fJoiningDomain
)
/*++
Routine Description:
Determines whether it is valid for this machine to attempt to
join a domain/workgroup
Arguments:
lpMachine -- Name of the machine being run on
Returns:
NERR_Success -- Success
NERR_SetupAlreadyJoined -- The machine is already joined to a domain
--*/
{
NET_API_STATUS NetStatus = NERR_Success;
PPOLICY_PRIMARY_DOMAIN_INFO pPolicy;
PPOLICY_DNS_DOMAIN_INFO pDns;
BOOL fIsDC=FALSE;
NT_PRODUCT_TYPE ProductType;
if ( fJoiningDomain == TRUE )
{
//
// Determine if we are running Personal SKU. If so, return error (Personal SKU not allowed to join domain)
//
OSVERSIONINFOEXW osvi;
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
if(GetVersionExW((OSVERSIONINFOW*)&osvi))
{
if ( osvi.wProductType == VER_NT_WORKSTATION && (osvi.wSuiteMask & VER_SUITE_PERSONAL))
{
return NERR_PersonalSku;
}
}
}
NetpLog(( "NetpMachineValidToJoin: '%ws'\n", GetStrPtr(lpMachine) ));
//
// check to see if the machine being joined is a DC
// if it is, we cannot let this join another domain/workgroup.
//
if (!RtlGetNtProductType(&ProductType))
{
NetStatus = GetLastError();
}
else if (ProductType == NtProductLanManNt)
{
NetStatus = NERR_SetupDomainController;
NetpLog(( "NetpMachineValidToJoin: the specified machine is a domain controller.\n"));
}
if (NetStatus == NERR_Success)
{
NetStatus = NetpGetLsaPrimaryDomain(NULL, NULL, &pPolicy, &pDns, NULL);
if ( NetStatus == NERR_Success )
{
//
// See if we have a domain SID
//
if ( IS_CLIENT_JOINED(pPolicy) )
{
NetStatus = NERR_SetupAlreadyJoined;
NetpLog(( "NetpMachineValidToJoin: the specified machine is already joined to '%wZ'!\n", &pPolicy->Name));
}
LsaFreeMemory( pPolicy );
LsaFreeMemory( pDns );
}
}
NetpLog(( "NetpMachineValidToJoin: status: 0x%lx\n", NetStatus));
return( NetStatus );
}
void
NetpLogBuildInformation(
VOID
)
/*++
Routine Description:
Logs information to the setup log regarding the OS version and build number
Arguments:
VOID
Returns:
VOID
--*/
{
#ifdef NETSETUP_VERBOSE_LOGGING
OSVERSIONINFO OsVersionInfo;
OsVersionInfo.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );
if ( GetVersionEx( &OsVersionInfo ) ) {
NetpLog(( "\tOS Version: %lu.%lu\n",
OsVersionInfo.dwMajorVersion,
OsVersionInfo.dwMinorVersion ));
NetpLog(( "\tBuild number: %lu\n",
OsVersionInfo.dwBuildNumber ));
if ( OsVersionInfo.szCSDVersion[ 0 ] != L'\0' ) {
NetpLog(( "\tServicePack: %ws\n",
OsVersionInfo.szCSDVersion ));
}
}
#endif
}
NET_API_STATUS
NetpQueryService(
IN LPWSTR ServiceName,
OUT SERVICE_STATUS *ServiceStatus,
OUT LPQUERY_SERVICE_CONFIG *ServiceConfig
)
/*++
Routine Description:
Query the status and the configuration of the specified service
Arguments:
ServiceName - The name of the service to query
ServiceStatus - Returns the status of the service. The buffer
pointed to by this parameter must be supplied by the caller.
ServiceConfig - Returns the configuration of the service.
Must be freed by the caller by calling LocalFree.
Return Status:
NO_ERROR - Indicates service successfully queried.
Otherwise, an error is returned.
--*/
{
NET_API_STATUS NetStatus = NO_ERROR;
SC_HANDLE ScManagerHandle = NULL;
SC_HANDLE ServiceHandle = NULL;
LPQUERY_SERVICE_CONFIG LocalServiceConfig = NULL;
DWORD ServiceConfigSize = 0;
//
// Open a handle to the Service.
//
ScManagerHandle = OpenSCManager(
NULL,
NULL,
SC_MANAGER_CONNECT );
if ( ScManagerHandle == NULL ) {
NetStatus = GetLastError();
NetpLog(( "NetpQueryService: %ws: OpenSCManager failed: %lu\n",
ServiceName,
NetStatus ));
goto Cleanup;
}
ServiceHandle = OpenService(
ScManagerHandle,
ServiceName,
SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG );
if ( ServiceHandle == NULL ) {
NetStatus = GetLastError();
NetpLog(( "NetpQueryService: %ws: OpenService failed: %lu\n",
ServiceName,
NetStatus ));
goto Cleanup;
}
//
// Pre-allocate the service config struct since QueryServiceConfig
// won't allow a null pointer, yet.
//
LocalServiceConfig = LocalAlloc( 0, sizeof(*LocalServiceConfig) );
if ( LocalServiceConfig == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
if ( !QueryServiceConfig( ServiceHandle,
LocalServiceConfig,
sizeof(*LocalServiceConfig),
&ServiceConfigSize) ) {
//
// Handle the error
//
NetStatus = GetLastError();
if ( NetStatus != ERROR_INSUFFICIENT_BUFFER ) {
NetpLog(( "NetpQueryService: %ws: QueryServiceConfig failed: %lu\n",
ServiceName,
NetStatus ));
goto Cleanup;
}
if ( LocalServiceConfig != NULL ) {
LocalFree( LocalServiceConfig );
LocalServiceConfig = NULL;
}
LocalServiceConfig = LocalAlloc( 0, ServiceConfigSize );
if ( LocalServiceConfig == NULL ) {
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
if ( !QueryServiceConfig( ServiceHandle,
LocalServiceConfig,
ServiceConfigSize,
&ServiceConfigSize) ) {
NetStatus = GetLastError();
NetpLog(( "NetpQueryService: %ws: QueryServiceConfig failed again: %lu\n",
ServiceName,
NetStatus ));
goto Cleanup;
}
}
//
// Query the status of the service.
//
if ( !QueryServiceStatus(ServiceHandle, ServiceStatus) ) {
NetStatus = GetLastError();
NetpLog(( "NetpQueryService: %ws: QueryServiceStatus failed: %lu\n",
ServiceName,
NetStatus ));
goto Cleanup;
}
//
// Success
//
NetStatus = NO_ERROR;
Cleanup:
if ( ScManagerHandle != NULL ) {
(VOID) CloseServiceHandle(ScManagerHandle);
}
if ( ServiceHandle != NULL ) {
(VOID) CloseServiceHandle(ServiceHandle);
}
if ( NetStatus == NO_ERROR ) {
*ServiceConfig = LocalServiceConfig;
} else if ( LocalServiceConfig != NULL ) {
LocalFree( LocalServiceConfig );
}
return NetStatus;
}
NET_API_STATUS
NET_API_FUNCTION
NetpJoinDomain(
IN LPWSTR lpMachine,
IN LPWSTR lpDomainSpecifier,
IN LPWSTR lpMachineAccountOU, OPTIONAL
IN LPWSTR lpAccount,
IN LPWSTR lpPassword,
IN DWORD fJoinOpts
)
/*++
Routine Description:
Joins the machine to the specified domain
Arguments:
lpMachine -- Name of the machine being joined
lpDomainSpecifier -- Domain to join.
syntax of the parameter is:
domain-name[\preferred-domain-controller-name]
e.g.:
ntdev\ntdsdc01 or ntdev
lpMachineAccountOU -- Optional OU in which to create the machine account
lpAccount -- User account for validation
lpPassword -- Password to use for validation.
The password has been encoded and the
first WCHAR of the password buffer is the seed
fJoinOptions -- Options to employ when joining, see below
NETSETUP_JOIN_DOMAIN if set join domain otherwise join workgroup
NETSETUP_ACCT_CREATE Do the server side account creation/rename
NETSETUP_ACCT_DELETE Delete the account when a domain is left
NETSETUP_WIN9X_UPGRADE use only during upgrade of win9x to NT
NETSETUP_DOMAIN_JOIN_IF_JOINED Allow the client to join a new domain
even if it is already joined to a domain
NETSETUP_JOIN_UNSECURE Performs an unsecure join
NETSETUP_INSTALL_INVOCATION use only during system install
(not currently used)
NETSETUP_MACHINE_PWD_PASSED Indicates that the machine password is passed
in lpPassword. Valid only for unsecure joins (i.e.
NETSETUP_JOIN_UNSECURE must also be set). If set,
the passed in password will be used for machine
password and the user credentials will be assumed
to be NULL (i.e. join will happen over null session
to the DC which is the case for unsecure joins)
NETSETUP_DEFER_SPN_SET Specifies that writting SPN and DnsHostName
attributes on the computer object should be
defered until rename that will follow this join.
This flag will be set bu the UI in case the user
joins and renames the machine at the same time. In
this case we don't want to set SPN at join time because
the new computer name (as specified by "NV Domain" in
System\\CurrentControlSet\\Services\\Tcpip\\Parameters
registry) may contain the new value that doesn't
correspond to the current SamAccountName.
Returns:
NERR_Success -- Success
Notes:
--*/
{
NET_API_STATUS NetStatus = NERR_Success, NetStatus2;
PWSTR DomainControllerName = NULL;
ULONG DcFlags = 0;
NETSETUP_SAVED_JOIN_STATE SavedState;
PPOLICY_PRIMARY_DOMAIN_INFO pPolicyPDI = NULL, pPolicyLocalPDI = NULL;
PPOLICY_DNS_DOMAIN_INFO pPolicyDns = NULL, pPolicyLocalDns = NULL;
BOOLEAN SecretCreated = FALSE, AccountCreated = FALSE;
BOOLEAN GroupsSet = FALSE, DomainInfoSet = FALSE;
BOOLEAN IpcConnect = FALSE;
LSA_HANDLE hLsa = NULL, hDC = NULL;
WCHAR MachinePasswordBuffer[ PWLEN + 1], *lpMachinePassword = NULL;
ULONG MachinePasswordLen=0;
ULONG IPCConnectFlags = NETSETUPP_CONNECT_IPC;
ULONG GetDcFlags = 0;
UNICODE_STRING EncodedPassword;
UNICODE_STRING EncodedMachinePassword = {0};
UCHAR Seed, MachinePasswordSeed;
BOOL UseDefaultPassword = FALSE;
BOOL fIsNt4Dc=FALSE;
BOOL fRandomPwdPreferred=TRUE;
PDOMAIN_CONTROLLER_INFO DcInfo = NULL;
PDOMAIN_CONTROLLER_INFO NetbiosDcInfo = NULL;
LPWSTR DomainName = NULL;
LPWSTR NetbiosDomainName = NULL;
LPWSTR DnsHostName = NULL;
LPWSTR SamAccountName = NULL;
LPWSTR MachineAccount = NULL;
BOOL DomainControllerPassed = FALSE;
BOOLEAN SpnSet = FALSE;
BOOLEAN NetlogonStopped = FALSE;
BOOLEAN NetlogonStarted = FALSE;
BOOLEAN NetlogonEnabled = FALSE;
SERVICE_STATUS OldNetlogonServiceStatus;
LPQUERY_SERVICE_CONFIG OldNetlogonServiceConfig = NULL;
NetpLog(( "NetpJoinDomain\n" ));
NetpLog(( "\tMachine: %ws\n", GetStrPtr(lpMachine)));
NetpLog(( "\tDomain: %ws\n", GetStrPtr(lpDomainSpecifier)));
NetpLog(( "\tMachineAccountOU: %ws\n",
GetStrPtr(lpMachineAccountOU)));
NetpLog(( "\tAccount: %ws\n", GetStrPtr(lpAccount)));
NetpLog(( "\tOptions: 0x%lx\n", fJoinOpts ));
NetpLogBuildInformation();
//
// Process the special case when the machine password is passed
//
if ( FLAG_ON(fJoinOpts, NETSETUP_MACHINE_PWD_PASSED) ) {
//
// Verify that this is unsecure join
//
if ( !FLAG_ON(fJoinOpts, NETSETUP_JOIN_UNSECURE) ) {
NetpLog(( "NetpJoinDomain: Machine password is passed for secure join. Error out.\n" ));
NetStatus = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
//
// Verify that the account name is not passed
//
if ( lpAccount != NULL ) {
NetpLog(( "NetpJoinDomain: Machine password and the user account are passed. Error out.\n" ));
NetStatus = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
//
// Switch the passwords
//
lpMachinePassword = lpPassword;
lpPassword = NULL;
MachinePasswordLen = wcslen(lpMachinePassword);
if ( MachinePasswordLen < 1 ) {
NetStatus = ERROR_PASSWORD_RESTRICTION;
goto Cleanup;
}
MachinePasswordSeed = ( UCHAR )*lpMachinePassword;
RtlInitUnicodeString( &EncodedMachinePassword, lpMachinePassword + 1 );
RtlRunDecodeUnicodeString( MachinePasswordSeed, &EncodedMachinePassword );
//
// Advance the pointer to the machine password
// passing the encode byte
//
lpMachinePassword ++;
MachinePasswordLen --;
}
if ( lpPassword )
{
if ( wcslen( lpPassword ) < 1 )
{
NetStatus = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
Seed = ( UCHAR )*lpPassword;
RtlInitUnicodeString( &EncodedPassword, lpPassword + 1 );
}
else
{
RtlZeroMemory( &EncodedPassword, sizeof( UNICODE_STRING ) );
Seed = 0;
}
RtlZeroMemory( &SavedState, sizeof( NETSETUP_SAVED_JOIN_STATE ) );
if ( !FLAG_ON( fJoinOpts, NETSETUP_ACCT_CREATE ) )
{
IPCConnectFlags |= NETSETUPP_NULL_SESSION_IPC;
}
// check to see if a preferred-dc name is supplied by specifying
// lpDomainSpecifier in the format 'domain\dc'
NetStatus = NetpCrackDomainSpecifier( lpDomainSpecifier, &DomainName,
&DomainControllerName );
//
// First, check the name of the domain to which we want to join
//
if ( NetStatus == NERR_Success )
{
//
// Indicate that the DC name was passed
//
if ( DomainControllerName != NULL ) {
DomainControllerPassed = TRUE;
}
RtlRunDecodeUnicodeString( Seed, &EncodedPassword );
NetStatus = NetpValidateName( lpMachine, DomainName, lpAccount,
EncodedPassword.Buffer, NetSetupDomain );
RtlRunEncodeUnicodeString( &Seed, &EncodedPassword );
if ( NetStatus == DNS_ERROR_NON_RFC_NAME )
{
NetStatus = NERR_Success;
}
}
if ( NetStatus != NERR_Success )
{
goto Cleanup;
}
GetDcFlags = FLAG_ON( fJoinOpts, NETSETUP_ACCT_CREATE ) ?
NETSETUPP_DSGETDC_FLAGS : NETSETUP_DSGETDC_FLAGS_ACCOUNT_EXISTS;
//
// If DC was not passed, discover one
//
if ( !DomainControllerPassed )
{
//
// otherwise, find a DC in the domain using these steps:
// - find a writable dc that has this machine account
// - if we cannot find such dc, find any writable dc
//
NetStatus = NetpDsGetDcName( NULL,
DomainName, lpMachine, GetDcFlags,
&DcFlags, &DomainControllerName, &DcInfo );
}
//
// First establish connection with the dc we found
//
if ( NetStatus == NERR_Success )
{
RtlRunDecodeUnicodeString( Seed, &EncodedPassword );
NetStatus = NetpManageIPCConnect( DomainControllerName,
lpAccount, EncodedPassword.Buffer,
IPCConnectFlags );
RtlRunEncodeUnicodeString( &Seed, &EncodedPassword );
NetpLog(( "NetpJoinDomain: status of connecting to dc '%ws': 0x%lx\n", DomainControllerName, NetStatus ));
if ( NetStatus == NERR_Success ) {
IpcConnect = TRUE;
}
}
//
// If the DC is passed, execute DsGetDcName on that DC to get the
// DC info. Do this after setting up a connection to avoid access
// denied problems. Verify that the passed computer name is indeed
// a DC by comparing the name that the computer returns with the one
// passed to us.
//
if ( NetStatus == NERR_Success && DomainControllerPassed ) {
BOOL NameVerified = FALSE;
DNS_STATUS DnsStatus;
//
// If the passed DC name is valid DNS name, try getting
// the DNS name from that DC. Skipp \\ in the name.
//
DnsStatus = DnsValidateDnsName_W( DomainControllerName+2 );
if ( DnsStatus == ERROR_SUCCESS ||
DnsStatus == DNS_ERROR_NON_RFC_NAME ) {
NetStatus = DsGetDcName( DomainControllerName,
DomainName,
NULL,
NULL,
DS_RETURN_DNS_NAME,
&DcInfo );
//
// Check if this returned the info about the DC we wanted
//
if ( NetStatus == NERR_Success ) {
if ( DnsNameCompare_W( DomainControllerName+2,
DcInfo->DomainControllerName+2 ) ) {
NetpLog(( "NetpJoinDomain: Passed DC '%ws' verified as DNS name '%ws'\n",
DomainControllerName,
DcInfo->DomainControllerName ));
NameVerified = TRUE;
} else {
NetpLog(( "NetpJoinDomain: Passed DC '%ws' NOT verified as DNS name '%ws'\n",
DomainControllerName,
DcInfo->DomainControllerName ));
}
//
// If the DC runs NT4 and thus does not have the server part of DsGetDcName,
// passing the prefered DC name is not supported -- there is only one
// NT4.0 DC that can be used for join - the PDC.
//
} else if ( NetStatus == RPC_S_PROCNUM_OUT_OF_RANGE ) {
NetpLog(( "NetpJoinDomain: Passed DC '%ws' is not NT5\n",
DomainControllerName ));
NetStatus = ERROR_NOT_SUPPORTED;
goto Cleanup;
} else {
NetpLog(( "NetpJoinDomain: DsGetDcName on passed DC '%ws' failed: 0x%lx\n",
DomainControllerName,
NetStatus ));
}
} else {
NetpLog(( "NetpJoinDomain: Passed DC name '%ws' is not valid DNS name.\n",
DomainControllerName ));
}
//
// If DNS didn't work, try Netbios name
//
if ( !NameVerified ) {
//
// Skipp \\ in the name.
//
if ( NetpIsComputerNameValid( DomainControllerName+2 ) ) {
if ( DcInfo != NULL ) {
NetApiBufferFree( DcInfo );
DcInfo = NULL;
}
NetStatus = DsGetDcName( DomainControllerName,
DomainName,
NULL,
NULL,
DS_RETURN_FLAT_NAME,
&DcInfo );
//
// Check if this returned the info about the DC we wanted
//
if ( NetStatus == NERR_Success ) {
if ( I_NetNameCompare( NULL,
DomainControllerName+2,
DcInfo->DomainControllerName+2,
NAMETYPE_COMPUTER, 0 ) == 0 ) {
NetpLog(( "NetpJoinDomain: Passed DC '%ws' verified as Netbios name '%ws'\n",
DomainControllerName,
DcInfo->DomainControllerName ));
NameVerified = TRUE;
} else {
NetpLog(( "NetpJoinDomain: Passed DC '%ws' NOT verified as Netbios name '%ws'\n",
DomainControllerName,
DcInfo->DomainControllerName ));
}
} else {
NetpLog(( "NetpJoinDomain: DsGetDcName on passed DC '%ws' failed: 0x%lx\n",
DomainControllerName,
NetStatus ));
}
} else {
NetpLog(( "NetpJoinDomain: Passed DC name '%ws' is not valid Netbios name.\n",
DomainControllerName ));
}
}
if ( !NameVerified || DcInfo == NULL ) {
NetpLog(( "NetpJoinDomain: Passed DC '%ws' is not verified: 0x%lx\n",
DomainControllerName,
NetStatus ));
NetStatus = ERROR_INVALID_DOMAIN_ROLE;
goto Cleanup;
}
//
// Name has been verified. Save the DC flags.
//
DcFlags = DcInfo->Flags;
}
//
// REVIEW kahrent 03-March-00
// The design below doesn't work if a DC with different default
// locale exists but is not the one the join is using for join.
// The correct solution seems to be to use the locale insensitive
// string comparison in netlogon, DS, SAM. The locale insensitive
// string comparison is bug 23108 in Windows Bugs.
//
//$ kumarp 02-June-1999
// the following requires admin access to the dc so that the
// right regkeys could be read to decide the default locale.
// We need to get a remoted API to get default locale
// on the dc.
//
// make sure that the machine name is OEM codepage compatible with
// the default codepage being used on the dc. The way we determine this
// is by translating the machine name to oem string using the
// local code page and the dc code page and then binary comparing
// the resultant strings. if the strings are different, we
// will fail join.
//
// Note that this is not a limitation of join apis. netlogon uses
// oem translated netbios machine name internally. the translation
// is different if the code page on the dc and the local machine are
// not compatible. this causes lack of access to net resources.
//
/*
if (NetStatus == NERR_Success)
{
NetStatus = NetpVerifyStrOemCompatibleOnMachine(DomainControllerName,
lpMachine);
NetpLog(( "NetpJoinDomain: status of verifying OEM compatibility of computer name: 0x%lx\n", NetStatus ));
}
*/
if (NetStatus != NERR_Success)
{
goto Cleanup;
}
fIsNt4Dc = !FLAG_ON( DcFlags, DS_DS_FLAG);
//
// If the OU is specified we must have an NT5 DC
//
if ( lpMachineAccountOU != NULL && fIsNt4Dc ) {
NetpLog(( "NetpJoinDomain: OU is specified but couldn't get NT5 DC\n" ));
NetStatus = ERROR_NO_SUCH_DOMAIN;
goto Cleanup;
}
//
// Ensure that the domain and machine names are different
// If we already have the Netbios domain info, just use it
//
// BLACKCOMB: Move this check into NetpGetComputerObjectDn
// where we carck the DNS domain name into the Netbios
// domain name as needed.
//
if ( (DcFlags & DS_DNS_DOMAIN_FLAG) == 0 ) {
NetbiosDomainName = DcInfo->DomainName;
//
// Otherwise call DsGetDcName again to get the Netbios domain info.
// (This will return cached info unless the DC was passed to us)
//
} else {
NetStatus = DsGetDcName( NULL,
DomainName,
NULL,
NULL,
DS_RETURN_FLAT_NAME,
&NetbiosDcInfo );
if ( NetStatus != NO_ERROR ) {
NetpLog(( "NetpJoinDomain: DsGetDcName (for Netbios domain name) failed: 0x%lx\n",
NetStatus ));
goto Cleanup;
}
NetbiosDomainName = NetbiosDcInfo->DomainName;
}
//
// Verify that the computer name is not the Netbios domain name
//
if ( I_NetNameCompare( NULL,
lpMachine,
NetbiosDomainName,
NAMETYPE_COMPUTER, 0 ) == 0 ) {
NetpLog(( "NetpJoinDomain: Computer name is same as Netbios domain name %ws %ws\n",
lpMachine,
NetbiosDomainName ));
NetStatus = ERROR_INVALID_DOMAINNAME;
goto Cleanup;
}
//
// Get the lsa domain info on the DC
//
if ( NetStatus == NERR_Success )
{
NetStatus = NetpGetLsaPrimaryDomain(NULL, DomainControllerName,
&pPolicyPDI, &pPolicyDns, &hDC);
}
//
// Determine DnsHostName of this machine
//
if ( !fIsNt4Dc && pPolicyDns != NULL ) {
NetStatus = NetpGetDnsHostName( NULL, // read tne new host name from registry
&pPolicyDns->DnsDomainName,
&DnsHostName );
}
if (NetStatus == NERR_Success)
{
//
// If we are joining an NT4 domain and we are not doing a machine
// account creation, we'll treat this as a win95 upgrade
//
if ( fIsNt4Dc &&
!FLAG_ON( fJoinOpts, NETSETUP_ACCT_CREATE ) )
{
fJoinOpts |= NETSETUP_JOIN_UNSECURE;
}
//
// Generate the machine password if it was not passed
//
if ( lpMachinePassword == NULL ) {
fRandomPwdPreferred = !( FLAG_ON( fJoinOpts, NETSETUP_WIN9X_UPGRADE ) ||
FLAG_ON( fJoinOpts, NETSETUP_JOIN_UNSECURE ) );
//
// Generate the password to use on the machine account.
// This can either be the default password
// (the 1st 14 characters of the machine name, lower cased),
// or a randomly generated password.
//
NetStatus = NetpGeneratePassword( lpMachine,
fRandomPwdPreferred,
DomainControllerName,
fIsNt4Dc,
MachinePasswordBuffer );
if ( NetStatus == NERR_Success )
{
lpMachinePassword = MachinePasswordBuffer;
MachinePasswordLen = wcslen(lpMachinePassword);
}
}
}
//
// if we are already joined to a domain, save the join state.
//
if ((NetStatus == NERR_Success) &&
FLAG_ON( fJoinOpts, NETSETUP_DOMAIN_JOIN_IF_JOINED))
{
NetStatus = NetpHandleJoinedStateInfo( hLsa, &SavedState,
TRUE, &hLsa );
}
//
// set the local machine secret, create if not already present
//
if ( NetStatus == NERR_Success )
{
//
// If the machine password is passed, use the default
// password (lowercased machine name) as the old password.
// We do this because the passed in password is unsecure
// since it is read from an unsecure unattend.txt file by
// setup (that calls us). When netlogon starts, it will
// detect that the old password is the default one, it will
// set the new password to a new value it will generate, and
// it will set the old value to the one which used to be the
// new value (that was passed to us). The net effect is that
// the password passed to us will be set as the old value
// and the new value will be updated by netlogon.
//
NetStatus = NetpManageMachineSecret(
NULL,
lpMachine,
lpMachinePassword,
NETSETUPP_CREATE,
FLAG_ON(fJoinOpts, NETSETUP_MACHINE_PWD_PASSED) ?
TRUE : // use the default for old password
FALSE,
&hLsa );
}
//
// set the machine account on the dc, create if not present
//
if ( NetStatus == NERR_Success )
{
SecretCreated = TRUE;
if ( FLAG_ON( fJoinOpts, NETSETUP_ACCT_CREATE ) )
{
//
// if OU is specified, create the account in OU, otherwise
// create it globally
//
if ( lpMachineAccountOU )
{
RtlRunDecodeUnicodeString( Seed, &EncodedPassword );
NetStatus = NetpCreateComputerObjectInDs( DcInfo,
lpAccount,
EncodedPassword.Buffer,
lpMachine,
lpMachinePassword,
DnsHostName,
lpMachineAccountOU );
RtlRunEncodeUnicodeString( &Seed, &EncodedPassword );
NetpLog(( "NetpJoinDomain: status of creating account in OU: 0x%lx\n", NetStatus ));
//
// On success, indicate that we have set SPN
//
if ( NetStatus == NO_ERROR && DnsHostName != NULL ) {
SpnSet = TRUE;
}
}
else
{
NetStatus = NetpManageMachineAccountWithSid( lpMachine,
NULL,
DomainControllerName,
lpMachinePassword,
pPolicyPDI->Sid,
NETSETUPP_CREATE,
fJoinOpts,
fIsNt4Dc );
NetpLog(( "NetpJoinDomain: status of creating account: 0x%lx\n", NetStatus ));
}
if (( NetStatus == NERR_Success ) &&
( MachinePasswordLen != wcslen(lpMachinePassword) ))
{
//
// the password was weakened by NetpManageMachineAccountWithSid
// because the dc did not accpet strong password.
// we need to update the local secret to reflect this.
//
NetStatus = NetpManageMachineSecret( NULL, lpMachine,
lpMachinePassword,
NETSETUPP_CREATE, FALSE, &hLsa );
NetpLog(( "NetpJoinDomain: status of updating secret: 0x%lx\n",
NetStatus ));
}
}
else if ( FLAG_ON( fJoinOpts, NETSETUP_WIN9X_UPGRADE ) ||
FLAG_ON( fJoinOpts, NETSETUP_JOIN_UNSECURE ) )
{
NetStatus = NetpValidateMachineAccount( DomainControllerName,
DomainName,
lpMachine,
lpMachinePassword );
NetpLog(( "NetpJoinDomain: w9x: status of validating account: 0x%lx\n", NetStatus ));
}
else
{
// if we are not creating the machine account,
// just set the password
NetStatus = NetpSetMachineAccountPasswordAndTypeEx(
DomainControllerName, pPolicyPDI->Sid,
lpMachine, lpMachinePassword,
ACCOUNT_STATE_ENABLED,
fIsNt4Dc
);
NetpLog(( "NetpJoinDomain: status of setting machine password: 0x%lx\n", NetStatus ));
}
if ( NetStatus == NERR_Success )
{
AccountCreated = TRUE;
}
//
// If this is not an explicit OU case, we haven't yet set DnsHostName and SPN.
// Do it here.
//
// ISSUE: The original idea was to use the machine credentials (account=SamAccountName,
// password=MachinePassword) since we may not have user credentials at this point (for
// unsecure join). However problems with Kerberos authentication while binding to the
// DC with machine credentials do not currently allow this behavior. So for now we will
// set SPN here only if we have user credentials.
//
if ( NetStatus == NERR_Success &&
!FLAG_ON(fJoinOpts, NETSETUP_DEFER_SPN_SET) &&
!fIsNt4Dc &&
lpMachineAccountOU == NULL &&
DnsHostName != NULL &&
lpAccount != NULL && lpPassword != NULL ) {
//
// Use the machine credentials (account=SamAccountName, password=MachinePassword)
// since we may not have user credentials at this point (for unsecure join).
//
NetStatus = NetpGetMachineAccountName( lpMachine, &SamAccountName );
if ( NetStatus != NO_ERROR ) {
goto Cleanup;
}
NetStatus = NetApiBufferAllocate(
(wcslen(DomainName) + 1 + wcslen(SamAccountName) + 1) * sizeof(WCHAR),
&MachineAccount );
if ( NetStatus != NO_ERROR ) {
goto Cleanup;
}
swprintf( MachineAccount, L"%ws\\%ws", DomainName, SamAccountName );
RtlRunDecodeUnicodeString( Seed, &EncodedPassword );
NetStatus = NetpSetDnsHostNameAndSpn( DcInfo,
lpAccount, // MachineAccount
EncodedPassword.Buffer, // lpMachinePassword
lpMachine,
DnsHostName );
RtlRunEncodeUnicodeString( &Seed, &EncodedPassword );
NetpLog(( "NetpJoinDomain: status of setting DnsHostName and SPN: 0x%lx\n", NetStatus ));
//
// If the problem was that values were invalid,
// return a distinctive error indicating where that happened
//
if ( NetStatus == ERROR_INVALID_PARAMETER ) {
NetStatus = ERROR_DS_COULDNT_UPDATE_SPNS;
}
//
// On success, indicate that we have set SPN
//
if ( NetStatus == NO_ERROR ) {
SpnSet = TRUE;
}
}
}
//
// Read our old primary domain info in case we have to restore
//
if ( NetStatus == NERR_Success ) {
NetStatus = NetpGetLsaPrimaryDomain( hLsa, NULL,
&pPolicyLocalPDI,
&pPolicyLocalDns,
NULL );
}
//
// Remember the current sevice states in case we have rollback on failure
//
if ( NetStatus == NERR_Success ) {
NetStatus = NetpQueryService( SERVICE_NETLOGON,
&OldNetlogonServiceStatus,
&OldNetlogonServiceConfig );
}
//
// Stop the Netlogon service if it's not alredy stopped
//
if ( NetStatus == NERR_Success &&
OldNetlogonServiceStatus.dwCurrentState != SERVICE_STOPPED ) {
NetStatus = NetpControlServices( NETSETUP_SVC_STOPPED,
NETSETUPP_SVC_NETLOGON );
if ( NetStatus == NERR_Success ) {
NetlogonStopped = TRUE;
}
}
//
// Set the primary domain info from the Dc to the client
//
if ( NetStatus == NERR_Success ) {
NetStatus = NetpSetLsaPrimaryDomain(hLsa,
pPolicyPDI->Name.Buffer,
pPolicyPDI->Sid,
pPolicyDns,
NULL);
NetpLog(( "NetpJoinDomain: status of setting LSA pri. domain: 0x%lx\n", NetStatus ));
if ( NetStatus == NERR_Success ) {
DomainInfoSet = TRUE;
}
}
//
// Then we adjust our local group memberships
//
if ( NetStatus == NERR_Success )
{
NetStatus = NetpManageLocalGroups( pPolicyPDI->Sid, NETSETUPP_CREATE );
NetpLog(( "NetpJoinDomain: status of managing local groups: 0x%lx\n", NetStatus ));
if ( NetStatus == NERR_Success )
{
GroupsSet = TRUE;
}
}
//
// The netlogon cache needs to be initialized
//
if ( NetStatus == NERR_Success )
{
NetStatus = NetpSetNetlogonDomainCache( DomainControllerName );
NetpLog(( "NetpJoinDomain: status of setting netlogon cache: 0x%lx\n", NetStatus ));
}
//
// Save off the name of the initial domain controller we contacted
// if we successfully joined the domain. Ignore failure.
//
if ( NetStatus == NERR_Success && DcInfo != NULL ) {
NET_API_STATUS TmpNetStatus = NetpStoreIntialDcRecord( DcInfo );
if ( TmpNetStatus != NERR_Success ) {
NetpLog(( "NetpJoinDomain: NON FATAL: failed to store the initial Dc record for '%ws': 0x%lx\n",
DomainControllerName,
TmpNetStatus ));
}
}
//
// Next, if the Dns domain name changed, we'll need to potentially update the
// computer name as well...
//
if ( NetStatus == NERR_Success ) {
//
// If we have a new name, set it if it's different from the old one
//
if ( pPolicyDns != NULL ) {
if ( pPolicyLocalDns == NULL ||
RtlCompareUnicodeString( &pPolicyLocalDns->DnsDomainName,
&pPolicyDns->DnsDomainName,
TRUE ) != 0 ) {
NetStatus = NetpSetDnsComputerNameAsRequired( (PWSTR)pPolicyDns->DnsDomainName.Buffer );
NetpLog(( "NetpJoinDomain: status of setting ComputerNamePhysicalDnsDomain to '%wZ': 0x%lx\n",
&pPolicyDns->DnsDomainName,
NetStatus ));
}
//
// If we don't have a new name (must be an NT4 domain/DC),
// clear the old name, if any.
//
// Note, if this is an NT5 domain but we simply don't have
// an NT5 DC at this time, netlogon will eventually discover
// an NT5 DC and netlogon will set the new name then.
//
} else if ( pPolicyLocalDns != NULL ) {
NetStatus = NetpSetDnsComputerNameAsRequired( L"\0" );
NetpLog(( "NetpJoinDomain: status of clearing ComputerNamePhysicalDnsDomain: 0x%lx\n",
NetStatus ));
}
}
//
// Then, the netlogon servic needs to be set to automatic
//
if ( NetStatus == NERR_Success &&
OldNetlogonServiceConfig->dwStartType != SERVICE_AUTO_START ) {
NetStatus = NetpControlServices( NETSETUP_SVC_ENABLED,
NETSETUPP_SVC_NETLOGON );
if ( NetStatus == NERR_Success ) {
NetlogonEnabled = TRUE;
} else {
NetpLog(( "NetpJoinDomain: status of enabling Netlogon: 0x%lx\n", NetStatus ));
}
}
//
// Start netlogon
//
if ( NetStatus == NERR_Success ) {
NetStatus = NetpControlServices( NETSETUP_SVC_STARTED,
NETSETUPP_SVC_NETLOGON );
if ( NetStatus == NERR_Success ) {
NetlogonStarted = TRUE;
}
}
//
// call w32time!W32TimeVerifyJoinConfig so that it can update
// its internal config to match the join state
//
if ( NetStatus == NERR_Success ) {
NetpUpdateW32timeConfig( "W32TimeVerifyJoinConfig" );
}
// -----------------------------------------------------------------
// the real work of netjoin is over, now do some misc. stuff
// if this fails it does not cause a rollback
// -----------------------------------------------------------------
//
// If we successfully added ourselves to a new domain while we were joined to the
// old, then we'll remove the old domains group membership. This is not a
// catastrophic failure. Note that we don't try and remove the machine account
// if we are joining the domain we are already joined to...
//
if ( (NetStatus == NERR_Success) &&
FLAG_ON( fJoinOpts, NETSETUP_DOMAIN_JOIN_IF_JOINED ) &&
(SavedState.PrimaryDomainInfo != NULL) &&
(SavedState.PrimaryDomainInfo->Sid != NULL) &&
(pPolicyPDI != NULL) &&
!RtlEqualSid( pPolicyPDI->Sid, SavedState.PrimaryDomainInfo->Sid ) &&
(pPolicyPDI->Name.Buffer != NULL) &&
(SavedState.PrimaryDomainInfo->Name.Buffer != NULL) &&
_wcsicmp( pPolicyPDI->Name.Buffer,
SavedState.PrimaryDomainInfo->Name.Buffer ) )
{
NetStatus = NetpManageLocalGroups( SavedState.PrimaryDomainInfo->Sid,
NETSETUPP_DELETE );
NetpLog(( "NetpJoinDomain: status of removing groups related to domain '%wZ' from local groups: 0x%lx\n",
&SavedState.PrimaryDomainInfo->Name, NetStatus ));
//
// Try and remove the old domain's computer account as well
//
if ( FLAG_ON( fJoinOpts, NETSETUP_ACCT_DELETE ) )
{
PWSTR OldDomainControllerName = NULL;
ULONG OldDomainControllerFlags = 0;
//
// Find a domain controller in the old domain
//
NetStatus = NetpDsGetDcName( NULL,
SavedState.PrimaryDomainInfo->Name.Buffer,
lpMachine, GetDcFlags,
&OldDomainControllerFlags,
&OldDomainControllerName, NULL );
if ( NetStatus == NERR_Success )
{
NetStatus = NetpManageMachineAccount( lpMachine, NULL,
OldDomainControllerName,
NULL, NETSETUPP_DELETE,
fJoinOpts, fIsNt4Dc );
NetApiBufferFree( OldDomainControllerName );
}
NetpLog(( "NetpJoinDomain: status of disabling account for '%ws' on old domain '%wZ': 0x%lx\n",
lpMachine,
&SavedState.PrimaryDomainInfo->Name,
NetStatus ));
}
//
// Nothing in here is considered fatal if it fails
//
NetStatus = NERR_Success;
}
// ============== ROLLBACK CODE ====================================
//
// If something failed, we'll have to rollback.
// Note that we ignore all error codes
//
if ( NetStatus != NERR_Success )
{
NetpLog(( "NetpJoinDomain: initiaing a rollback due to earlier errors\n"));
//
// Reset netlogon's start type
//
if ( NetlogonEnabled ) {
DWORD SvcOpts = 0;
if ( OldNetlogonServiceConfig->dwStartType == SERVICE_DISABLED ) {
SvcOpts = NETSETUP_SVC_DISABLED;
} else if ( OldNetlogonServiceConfig->dwStartType == SERVICE_DEMAND_START ) {
SvcOpts = NETSETUP_SVC_MANUAL;
}
if ( SvcOpts != 0 ) {
NetStatus2 = NetpControlServices( SvcOpts,
NETSETUPP_SVC_NETLOGON );
NetpLog(( "NetpJoinDomain: rollback: status of setting netlogon start type to 0x%lx: 0x%lx\n",
SvcOpts,
NetStatus2));
}
}
//
// Restart netlogon
//
// Note that we don't need to worry abbout the time service
// state since we update it as the last non-critical operation.
//
if ( NetlogonStopped ) {
DWORD SvcOpts = 0;
if ( OldNetlogonServiceStatus.dwCurrentState == SERVICE_RUNNING ) {
SvcOpts = NETSETUP_SVC_STARTED;
}
if ( SvcOpts != 0 ) {
NetStatus2 = NetpControlServices( SvcOpts,
NETSETUPP_SVC_NETLOGON );
NetpLog(( "NetpJoinDomain: rollback: status of starting netlogon: 0x%lx\n", NetStatus2));
}
}
if ( NetlogonStarted ) {
DWORD SvcOpts = 0;
if ( OldNetlogonServiceStatus.dwCurrentState == SERVICE_STOPPED ) {
SvcOpts = NETSETUP_SVC_STOPPED;
}
if ( SvcOpts != 0 ) {
NetStatus2 = NetpControlServices( SvcOpts,
NETSETUPP_SVC_NETLOGON );
NetpLog(( "NetpJoinDomain: rollback: status of stopping netlogon: 0x%lx\n", NetStatus2));
}
}
//
// Take back what we told Netlogon about SPN setting
//
if ( SpnSet ) {
NetpAvoidNetlogonSpnSet( FALSE );
}
if ( GroupsSet )
{
NetStatus2 = NetpManageLocalGroups( pPolicyPDI->Sid,
NETSETUPP_DELETE );
NetpLog(( "NetpJoinDomain: rollback: local group management: 0x%lx\n", NetStatus2));
}
if ( DomainInfoSet )
{
//
// Set a NULL domain sid
//
NetStatus2 =
NetpSetLsaPrimaryDomain( hLsa, pPolicyLocalPDI->Name.Buffer,
NULL, NULL, NULL );
NetpLog(( "NetpJoinDomain: rollback: status of setting NULL domain sid: 0x%lx\n", NetStatus2));
}
if ( AccountCreated &&
FLAG_ON( fJoinOpts, NETSETUP_ACCT_CREATE ) )
{
if ( lpMachineAccountOU )
{
RtlRunDecodeUnicodeString( Seed, &EncodedPassword );
NetStatus2 = NetpDeleteComputerObjectInOU(
DomainControllerName, lpMachineAccountOU,
lpMachine, lpAccount, EncodedPassword.Buffer );
RtlRunEncodeUnicodeString( &Seed, &EncodedPassword );
NetpLog(( "NetpJoinDomain: rollback: status of deleting the computer account from OU: 0x%lx\n", NetStatus2));
}
else
{
RtlRunDecodeUnicodeString( Seed, &EncodedPassword );
NetStatus2 = NetpManageMachineAccount(
lpMachine, NULL, DomainControllerName,
EncodedPassword.Buffer, NETSETUPP_DELETE, 0, fIsNt4Dc );
RtlRunEncodeUnicodeString( &Seed, &EncodedPassword );
NetpLog(( "NetpJoinDomain: rollback: status of deleting computer account: 0x%lx\n", NetStatus2));
}
}
if ( SecretCreated )
{
if ( SavedState.MachineSecret )
{
NetStatus2 = NetpHandleJoinedStateInfo( hLsa, &SavedState,
FALSE, &hLsa );
NetpLog(( "NetpJoinDomain: rollback: status of restoring secret: 0x%lx\n", NetStatus2));
}
else
{
NetStatus2 = NetpManageMachineSecret( NULL, lpMachine, NULL,
NETSETUPP_DELETE, FALSE, NULL );
NetpLog(( "NetpJoinDomain: rollback: status of deleting secret: 0x%lx\n", NetStatus2));
}
}
}
// =================================================================
Cleanup:
if ( pPolicyPDI != NULL ) {
LsaFreeMemory( pPolicyPDI );
}
if ( pPolicyLocalPDI != NULL ) {
LsaFreeMemory( pPolicyLocalPDI );
}
if ( pPolicyDns != NULL ) {
LsaFreeMemory( pPolicyDns );
}
if ( pPolicyLocalDns != NULL ) {
LsaFreeMemory( pPolicyLocalDns );
}
if ( hLsa != NULL )
{
LsaClose( hLsa );
}
if ( hDC != NULL )
{
LsaClose( hDC );
}
//
// Now, we'll no longer need our session to our dc
//
if ( IpcConnect )
{
RtlRunDecodeUnicodeString( Seed, &EncodedPassword );
NetStatus2 = NetpManageIPCConnect( DomainControllerName, lpAccount,
EncodedPassword.Buffer,
NETSETUPP_DISCONNECT_IPC );
RtlRunEncodeUnicodeString( &Seed, &EncodedPassword );
NetpLog(( "NetpJoinDomain: status of disconnecting from '%ws': 0x%lx\n", DomainControllerName, NetStatus2));
}
// Note: NetApiBufferFree checks for NULL
NetApiBufferFree( DcInfo );
NetApiBufferFree( DomainControllerName );
NetApiBufferFree( DomainName );
NetApiBufferFree( SamAccountName );
NetApiBufferFree( MachineAccount );
NetApiBufferFree( DnsHostName );
NetApiBufferFree( NetbiosDcInfo );
if ( OldNetlogonServiceConfig != NULL ) {
LocalFree( OldNetlogonServiceConfig );
}
//
// If the passed in machine password was decrypted,
// encrypt is back
//
if ( EncodedMachinePassword.Buffer != NULL ) {
RtlRunEncodeUnicodeString( &MachinePasswordSeed, &EncodedMachinePassword );
}
return( NetStatus );
}
NET_API_STATUS
NET_API_FUNCTION
NetpCrackDomainSpecifier(
IN LPWSTR DomainSpecifier,
OUT LPWSTR* DomainName,
OUT LPWSTR* DomainControllerName
)
/*++
Routine Description:
Parse DomainSpecifier and separate out DomainName / DomainControllerName.
Caller must free the out arguments using NetApiBufferFree.
Arguments:
lpDomainSpecifier -- Domain to join.
syntax of the parameter is:
domain-name[\preferred-domain-controller-name]
e.g.:
ntdev\ntdsdc01 or ntdev
DomainName -- domain name extracted from lpDomainSpecifier
DomainControllerName -- domain controller name extracted from lpDomainSpecifier
Returns:
NERR_Success on success, otherwise NET_API_STATUS code
--*/
{
NET_API_STATUS Status;
LPWSTR pwsz;
LPWSTR pwszSave;
LPWSTR pwszDomain = NULL;
LPWSTR pwszDC = NULL;
pwszSave = pwsz = wcschr( DomainSpecifier, L'\\' );
if ( pwsz != NULL )
{
//
// Check if the passed DC name is NULL
//
if ( *(pwsz+1) == L'\0' ) {
return ERROR_INVALID_PARAMETER;
}
*pwsz = L'\0';
}
Status = NetpDuplicateString(DomainSpecifier, -1, &pwszDomain);
if ( Status == NERR_Success )
{
if ( pwsz != NULL )
{
pwsz++;
Status = NetApiBufferAllocate(
( wcslen( pwsz ) + 2 + 1 ) * sizeof( WCHAR ),
&pwszDC
);
if ( Status == NERR_Success )
{
wcscpy( pwszDC, L"\\\\" );
wcscat( pwszDC, pwsz );
}
else
{
NetApiBufferFree( pwszDomain );
pwszDomain = NULL;
}
}
}
if ( pwszSave != NULL )
{
*pwszSave = L'\\';
}
*DomainName = pwszDomain;
*DomainControllerName = pwszDC;
return( Status );
}
NET_API_STATUS
NET_API_FUNCTION
NetpManageMachineAccountWithSid(
IN LPWSTR lpMachine,
IN LPWSTR lpOldMachine,
IN LPWSTR lpDcName,
IN LPWSTR lpPassword,
IN PSID DomainSid,
IN ULONG fControl,
IN ULONG AccountOptions,
IN BOOL fIsNt4Dc
)
/*++
Routine Description:
Manages the creation/deletion, and manipulation of the machine account
Arguments:
lpMachine -- Name of the current machine /
name of the new machine for rename
lpOldMachine -- Old machine name for rename
lpDcName -- Name of a DC in the domain
lpPassword -- Password to use for machine object. If NULL, use the default.
DomainSid -- Sid for the domain holding the user account
fControl -- Whether to create/delete/rename
AccountOptions -- Options to determine the particular behavior of the account creation
fIsNt4Dc -- TRUE if the DC is NT4 or earlier
Returns:
NERR_Success -- Success
ERROR_INVALID_PASSWORD -- The machine account could not have a random password set
on it, so it must resort to using the default password
--*/
{
NET_API_STATUS NetStatus = NERR_Success;
LPWSTR lpMachAcc = NULL, lpOldMachAcc = NULL;
USER_INFO_1 NetUI1, *CurrentUI1;
USER_INFO_0 NetUI0;
DWORD BadParam = 0;
//
// Build the machine account name
//
NetStatus = NetpGetMachineAccountName(lpMachine, &lpMachAcc);
if ( NetStatus == NERR_Success )
{
if ( fControl == NETSETUPP_RENAME)
{
NetStatus = NetpGetMachineAccountName(lpOldMachine, &lpOldMachAcc);
}
}
//
// Now, either create or delete it
//
if ( NetStatus == NERR_Success )
{
if ( fControl == NETSETUPP_DELETE )
{
NetStatus = NetpSetMachineAccountPasswordAndTypeEx(
lpDcName, DomainSid, lpMachAcc,
NULL, ACCOUNT_STATE_DISABLED, fIsNt4Dc );
NetpLog(( "NetpManageMachineAccountWithSid: status of disabling account '%ws' on '%ws': 0x%lx\n",
lpMachAcc, lpDcName, NetStatus ));
}
else if (fControl == NETSETUPP_CREATE )
{
RtlZeroMemory( &NetUI1, sizeof( NetUI1 ) );
//
// Initialize it..
//
NetUI1.usri1_name = lpMachAcc;
NetUI1.usri1_password = lpPassword;
NetUI1.usri1_flags = UF_WORKSTATION_TRUST_ACCOUNT | UF_SCRIPT;
NetUI1.usri1_priv = USER_PRIV_USER;
NetStatus = NetUserAdd( lpDcName, 1, (PBYTE)&NetUI1, &BadParam );
if ( NetStatus != NERR_Success )
{
NetpLog(( "NetpManageMachineAccountWithSid: NetUserAdd on '%ws' for '%ws' failed: 0x%lx\n", lpDcName, lpMachAcc, NetStatus ));
if ( NetStatus == NERR_PasswordTooShort )
{
//
// SAM did not accpet a long password, try LM20_PWLEN
//
// Please refer to comments in NetpSetMachineAccountPasswordAndTypeEx
// regarding the reasons why we weaken the passwords in
// this order.
//
lpPassword[LM20_PWLEN] = UNICODE_NULL;
NetStatus = NetUserAdd( lpDcName, 1, (PBYTE)&NetUI1,
&BadParam );
if ( NetStatus == NERR_PasswordTooShort )
{
NetpLog(( "NetpManageMachineAccountWithSid: NetUserAdd on '%ws' for '%ws' failed:2: 0x%lx\n", lpDcName, lpMachAcc, NetStatus ));
//
// SAM did not accpet a LM20_PWLEN password,
// try a shorter one
//
lpPassword[LM20_PWLEN/2] = UNICODE_NULL;
NetStatus = NetUserAdd( lpDcName, 1, (PBYTE)&NetUI1,
&BadParam );
if ( NetStatus == NERR_PasswordTooShort )
{
NetpLog(( "NetpManageMachineAccountWithSid: NetUserAdd on '%ws' for '%ws' failed:3: 0x%lx\n", lpDcName, lpMachAcc, NetStatus ));
//
// SAM did not accpet a short pwd, try default pwd
//
NetpGenerateDefaultPassword(lpMachAcc, lpPassword);
NetStatus = NetUserAdd( lpDcName, 1, (PBYTE)&NetUI1,
&BadParam );
}
}
if (NetStatus == NERR_Success)
{
NetpLog(( "NetpManageMachineAccountWithSid: successfully created computer account\n" ));
}
else
{
NetpLog(( "NetpManageMachineAccountWithSid: NetUserAdd on '%ws' for '%ws' failed:4: 0x%lx\n", lpDcName, lpMachAcc, NetStatus ));
}
}
//
// See if it exists and we just need to adjust the password
//
else if ( NetStatus == NERR_UserExists || NetStatus == ERROR_LOGON_FAILURE )
{
NetStatus = NetpSetMachineAccountPasswordAndTypeEx(
lpDcName,
DomainSid,
lpMachAcc,
lpPassword,
ACCOUNT_STATE_ENABLED,
fIsNt4Dc );
NetpLog(( "NetpManageMachineAccountWithSid: status of attempting to set password on '%ws' for '%ws': 0x%lx\n", lpDcName, lpMachAcc, NetStatus ));
}
}
} else if ( fControl == NETSETUPP_RENAME )
{
NetUI0.usri0_name = lpMachAcc;
NetStatus = NetUserSetInfo( lpDcName, lpOldMachAcc,
0, (PBYTE)&NetUI0, NULL );
NetpLog(( "NetpManageMachineAccountWithSid: status of NetUserSetInfo on '%ws' for '%ws': 0x%lx\n", lpDcName, lpOldMachAcc, NetStatus ));
//
// Update the display name as well.
// Ignore error as this is not critical.
//
if ( NetStatus == NERR_Success ) {
NET_API_STATUS TmpNetStatus;
PUSER_INFO_10 usri10 = NULL;
//
// First get the current display name
//
TmpNetStatus = NetUserGetInfo( lpDcName, lpMachAcc,
10, (PBYTE *)&usri10 );
if ( TmpNetStatus != NERR_Success ) {
NetpLog(( "NetpManageMachineAccountWithSid: failed to get display name (ignored) 0x%lx\n", TmpNetStatus ));
//
// If the display name exists and is
// different from the new one, update it
//
} else if ( usri10->usri10_full_name != NULL &&
_wcsicmp(usri10->usri10_full_name, lpMachAcc) != 0 ) {
USER_INFO_1011 usri1011;
usri1011.usri1011_full_name = lpMachAcc; // new name
TmpNetStatus = NetUserSetInfo( lpDcName, lpMachAcc,
1011, (PBYTE)&usri1011, NULL );
if ( TmpNetStatus != NERR_Success ) {
NetpLog(( "NetpManageMachineAccountWithSid: failed to update display name (ignored) 0x%lx\n", TmpNetStatus ));
}
}
//
// Free the user info we got
//
if ( usri10 != NULL ) {
NetApiBufferFree( usri10 );
}
}
}
else if ( fControl == NETSETUPP_SET_PASSWORD )
{
NetStatus = NetpSetMachineAccountPasswordAndType( lpDcName, DomainSid,
lpMachAcc, lpPassword );
NetpLog(( "NetpManageMachineAccountWithSid: status of setting password on '%ws' for '%ws': 0x%lx\n", lpDcName, lpMachAcc, NetStatus ));
}
else
{
NetStatus = ERROR_INVALID_PARAMETER;
}
}
NetApiBufferFree( lpMachAcc );
NetApiBufferFree( lpOldMachAcc );
return( NetStatus );
}
NET_API_STATUS
NET_API_FUNCTION
NetpManageMachineAccount(
IN LPWSTR lpMachine,
IN LPWSTR lpOldMachine,
IN LPWSTR lpDcName,
IN LPWSTR lpPassword,
IN ULONG fControl,
IN ULONG AccountOptions,
IN BOOL fIsNt4Dc
)
/*++
Routine Description:
Manages the creation/deletion, and manipulation of the machine account
Arguments:
lpMachine -- Name of the current machine /
name of the new machine for rename
lpOldMachine -- Old machine name for rename
lpDcName -- Name of a DC in the domain
lpPassword -- Password to use for machine object. If NULL, use the default.
fControl -- Whether to create/delete/rename
AccountOptions -- Options to determine the particular behavior of the account creation
fIsNt4Dc -- TRUE if the DC is NT4 or earlier
Returns:
NERR_Success -- Success
ERROR_INVALID_PASSWORD -- The machine account could not have a random password set
on it, so it must resort to using the default password
--*/
{
NET_API_STATUS NetStatus = NERR_Success;
PPOLICY_PRIMARY_DOMAIN_INFO pPolicy;
PPOLICY_DNS_DOMAIN_INFO pDns;
NetStatus = NetpGetLsaPrimaryDomain( NULL, lpDcName, &pPolicy, &pDns, NULL );
if ( NetStatus == NERR_Success )
{
NetStatus = NetpManageMachineAccountWithSid( lpMachine, lpOldMachine,
lpDcName, lpPassword,
pPolicy->Sid, fControl,
AccountOptions,
fIsNt4Dc );
LsaFreeMemory( pPolicy );
LsaFreeMemory( pDns );
}
return( NetStatus );
}
NET_API_STATUS
NET_API_FUNCTION
NetpUnJoinDomain(
IN PPOLICY_PRIMARY_DOMAIN_INFO pPolicyPDI,
IN LPWSTR lpAccount,
IN LPWSTR lpPassword,
IN DWORD fJoinOpts
)
{
NET_API_STATUS NetStatus = NERR_Success;
NET_JOIN_STATE JoinState = { 0 };
WCHAR szMachineNameBuf[MAX_COMPUTERNAME_LENGTH + 1];
LPWSTR szMachineName=szMachineNameBuf;
DWORD dwJoinAction=0;
POLICY_PRIMARY_DOMAIN_INFO PolicyPDI;
NT_PRODUCT_TYPE ProductType;
PolicyPDI = *pPolicyPDI;
PolicyPDI.Sid = NULL;
NetSetuppOpenLog();
NetpLog(("NetpUnJoinDomain: unjoin from '%wZ' using '%ws' creds, options: 0x%lx\n", &pPolicyPDI->Name, lpAccount, fJoinOpts));
NetpLogBuildInformation();
//
// check to see if the machine being joined is a DC
// if it is, we cannot let this join another domain/workgroup.
//
if (!RtlGetNtProductType(&ProductType))
{
NetStatus = GetLastError();
}
else if (ProductType == NtProductLanManNt)
{
NetStatus = NERR_SetupDomainController;
NetpLog(( "NetpUnJoinDomain: the specified machine is a domain controller.\n"));
}
if (NetStatus == NERR_Success)
{
//
// get the computer name
//
NetStatus = NetpGetComputerNameAllocIfReqd(
&szMachineName, MAX_COMPUTERNAME_LENGTH+1);
NetpLog(( "NetpUnJoinDomain: status of getting computer name: 0x%lx\n", NetStatus ));
if (NetStatus == NERR_Success)
{
dwJoinAction =
NJA_SetNetlogonState |
NJA_SetTimeSvcUnjoin |
NJA_SetAutoenrolSvcUnjoin |
NJA_SetPolicyDomainInfo |
NJA_RemoveFromLocalGroups |
NJA_DeleteMachinePassword |
NJA_RemoveDnsRegistrations |
NJA_RollbackOnFailure;
if ( FLAG_ON( fJoinOpts, NETSETUP_ACCT_DELETE ) )
{
dwJoinAction |= NJA_DeleteAccount;
}
JoinState.szDomainName = pPolicyPDI->Name.Buffer;
JoinState.pPolicyPDI = &PolicyPDI;
JoinState.uiNetlogonStartType = NETSETUP_SVC_MANUAL;
JoinState.uiNetlogonState = NETSETUP_SVC_STOPPED;
NetStatus = NetpApplyJoinState(&JoinState, dwJoinAction,
szMachineName,
lpAccount, lpPassword, NULL);
}
}
NetpLog(( "NetpUnJoinDomain: status: 0x%lx\n", NetStatus));
NetSetuppCloseLog();
if (szMachineName != szMachineNameBuf)
{
NetApiBufferFree(szMachineName);
}
return NetStatus;
}
NET_API_STATUS
NET_API_FUNCTION
NetpControlServices(
IN DWORD SvcOpts,
IN DWORD Services
)
/*++
Routine Description:
Controls the state of the netlogon service
Arguments:
SvcOpts -- What to do to the service, such as start/stop or
enable/disable
Services -- Which services to control
Returns:
NERR_Success -- Success
--*/
{
NET_API_STATUS NetStatus = NERR_Success;
SC_HANDLE hScMgr=NULL, hSvc=NULL;
DWORD i, OpenMode=0;
PWSTR ppwszServices[] = {
SERVICE_NETLOGON
};
//
// Open the service control manager
//
hScMgr = OpenSCManager( NULL,
SERVICES_ACTIVE_DATABASE,
GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE );
if ( hScMgr == NULL )
{
NetStatus = GetLastError();
NetpLog(( "NetpControlServices: open SCManager failed: 0x%lx\n", NetStatus ));
}
else
{
i = 0;
while ( Services != 0 )
{
if ( FLAG_ON( Services, 0x00000001 ) )
{
if ( i > sizeof( ppwszServices ) / sizeof( PWSTR ) )
{
NetStatus = ERROR_INVALID_PARAMETER;
ASSERT( FALSE && "Invalid Service" );
break;
}
OpenMode = 0;
//
// Set the open mode
//
if( FLAG_ON( SvcOpts, NETSETUP_SVC_STOPPED ) )
{
OpenMode = SERVICE_STOP | SERVICE_ENUMERATE_DEPENDENTS |
SERVICE_QUERY_STATUS | SERVICE_CHANGE_CONFIG;
} else if( FLAG_ON( SvcOpts, NETSETUP_SVC_STARTED ) )
{
OpenMode = SERVICE_START;
}
if ( FLAG_ON( SvcOpts, NETSETUP_SVC_ENABLED |
NETSETUP_SVC_DISABLED |
NETSETUP_SVC_MANUAL ) )
{
OpenMode |= SERVICE_CHANGE_CONFIG | SERVICE_QUERY_CONFIG;
}
if ( FLAG_ON( SvcOpts, NETSETUP_SVC_STOPPED ) )
{
NetStatus = NetpStopService( ppwszServices[ i ], hScMgr );
if ( NetStatus != NERR_Success )
{
break;
}
}
//
// Open the service
//
hSvc = OpenService( hScMgr, ppwszServices[i], OpenMode );
if ( hSvc == NULL )
{
NetStatus = GetLastError();
NetpLog(( "NetpControlServices: open service '%ws' failed: 0x%lx\n", ppwszServices[i], NetStatus ));
}
else
{
if ( FLAG_ON( SvcOpts, NETSETUP_SVC_ENABLED |
NETSETUP_SVC_DISABLED |
NETSETUP_SVC_MANUAL ) )
{
DWORD StartControl = 0;
if ( FLAG_ON( SvcOpts, NETSETUP_SVC_ENABLED ) )
{
StartControl = SERVICE_AUTO_START;
}
else if ( FLAG_ON( SvcOpts, NETSETUP_SVC_DISABLED ) )
{
StartControl = SERVICE_DISABLED;
}
else if ( FLAG_ON( SvcOpts, NETSETUP_SVC_MANUAL ) )
{
StartControl = SERVICE_DEMAND_START;
}
if ( ChangeServiceConfig( hSvc, SERVICE_NO_CHANGE,
StartControl,
SERVICE_NO_CHANGE,
NULL, NULL, 0, NULL, NULL, NULL,
NULL ) == FALSE )
{
NetStatus = GetLastError();
NetpLog(( "NetpControlServices: configuring service '%ws' [ 0x%lx ] failed: 0x%lx\n",
ppwszServices[i], StartControl, NetStatus ));
}
}
if ( FLAG_ON( SvcOpts, NETSETUP_SVC_STARTED ) )
{
//
// See about changing its state
//
if ( StartService( hSvc, 0, NULL ) == FALSE )
{
NetStatus = GetLastError();
if ( NetStatus == ERROR_SERVICE_ALREADY_RUNNING )
{
NetStatus = NERR_Success;
}
else
{
NetpLog(( "NetpControlServices: start service '%ws' failed: 0x%lx\n", ppwszServices[i], NetStatus ));
}
}
}
CloseServiceHandle( hSvc );
}
}
i++;
Services >>= 1;
}
CloseServiceHandle( hScMgr );
}
return(NetStatus);
}
NET_API_STATUS
NET_API_FUNCTION
NetpChangeMachineName(
IN LPWSTR lpCurrentMachine,
IN LPWSTR lpNewHostName,
IN LPWSTR lpDomain,
IN LPWSTR lpAccount,
IN LPWSTR lpPassword,
IN DWORD fOptions
)
/*++
Routine Description:
Handles the machine rename case
Arguments:
lpCurrentMachine -- The current Netbios machine name
lpNewHostName -- The new (untruncated) machine name (may be longer than 15 chars)
lpDomain -- Domain the machine is a member of
lpAccount -- Account to use for machine account manipulation
lpPassword -- Password to use for machine account manipulation.
The password has been encoded and the first WCHAR
of the password buffer is the seed
fOptions -- Whether to mess with the machine account or not
Returns:
NERR_Success -- Success
--*/
{
NET_API_STATUS NetStatus = NERR_Success;
PWSTR DomainControllerName = NULL;
ULONG DcFlags = 0;
ULONG cLen;
BOOLEAN IpcConnect = FALSE;
UCHAR Seed;
UNICODE_STRING EncodedPassword;
BOOL fIsOemCompatible=FALSE;
WCHAR lpNewMachine[MAX_COMPUTERNAME_LENGTH + 1];
PDOMAIN_CONTROLLER_INFO DcInfo = NULL;
PPOLICY_PRIMARY_DOMAIN_INFO pPolicyLocalPDI = NULL;
PPOLICY_DNS_DOMAIN_INFO pPolicyLocalDns = NULL;
LPWSTR DnsHostName = NULL;
NT_PRODUCT_TYPE NtProductType;
NetSetuppOpenLog();
if ( lpPassword )
{
if ( wcslen( lpPassword ) < 1 )
{
return( ERROR_INVALID_PARAMETER );
}
Seed = ( UCHAR )*lpPassword;
RtlInitUnicodeString( &EncodedPassword, lpPassword + 1 );
}
else
{
RtlZeroMemory( &EncodedPassword, sizeof( UNICODE_STRING ) );
Seed = 0;
}
NetpLog(( "NetpChangeMachineName: from '%ws' to '%ws' using '%ws' [0x%lx]\n",
GetStrPtr(lpCurrentMachine),
GetStrPtr(lpNewHostName),
GetStrPtr(lpAccount), fOptions));
//
// Only do this if we have create/manage account specified
//
if ( FLAG_ON(fOptions, NETSETUP_ACCT_CREATE) )
{
//
// Find a DC in that domain
//
NetStatus = NetpDsGetDcName( NULL,
lpDomain,
lpCurrentMachine,
FLAG_ON( fOptions, NETSETUP_ACCT_CREATE ) ?
NETSETUPP_DSGETDC_FLAGS :
NETSETUP_DSGETDC_FLAGS_ACCOUNT_EXISTS,
&DcFlags,
&DomainControllerName,
&DcInfo );
//
// If this is a DC, ensure we are using it.
// This is done to avoid cases when we modify our computer
// object on some other downlevel DC which doesn't update
// the server object in the config container. Besides, we
// don't really know how this DC would behave itself after
// its computer object gets updated on incoming replication
// change from some other DC.
//
if ( NetStatus == NERR_Success &&
RtlGetNtProductType(&NtProductType) &&
NtProductType == NtProductLanManNt ) { // DC
//
// If we got Netbios name,
// compare it with the one we have
//
if ( (DcInfo->Flags & DS_DNS_DOMAIN_FLAG) == 0 ) {
if ( I_NetNameCompare(NULL,
lpCurrentMachine,
DcInfo->DomainControllerName+2, // skip '\\'
NAMETYPE_COMPUTER, 0) != 0 ) {
NetpLog(( "NetpChangeMachineName: Got other Netbios DC '%ws' than local machine '%ws'\n",
DcInfo->DomainControllerName+2,
lpCurrentMachine ));
NetStatus = ERROR_NO_SUCH_DOMAIN;
}
//
// Otherwise, get the local DNS host name and
// compare it with the one from DC locator
//
} else {
WCHAR LocalDnsHostName[DNS_MAX_NAME_BUFFER_LENGTH];
ULONG LocalDnsHostNameLen = DNS_MAX_NAME_BUFFER_LENGTH;
if ( GetComputerNameExW(ComputerNameDnsFullyQualified,
LocalDnsHostName,
&LocalDnsHostNameLen) ) {
if ( !DnsNameCompare_W(DcInfo->DomainControllerName+2,
LocalDnsHostName) ) {
NetpLog(( "NetpChangeMachineName: Got other DNS DC '%ws' than local machine '%ws'\n",
DcInfo->DomainControllerName+2,
LocalDnsHostName ));
NetStatus = ERROR_NO_SUCH_DOMAIN;
}
} else {
NetStatus = GetLastError();
}
}
}
//
// Get the computer name from the host name (truncate as needed)
//
if ( NetStatus == NERR_Success ) {
ULONG Size = MAX_COMPUTERNAME_LENGTH + 1;
BOOL Result;
Result = DnsHostnameToComputerNameW( lpNewHostName,
lpNewMachine,
&Size );
if ( Result != TRUE ) {
NetStatus = GetLastError();
NetpLog(( "NetpChangeMachineName: DnsHostnameToComputerNameW failed: 0x%lx\n", NetStatus ));
}
}
if ( NetStatus == NERR_Success )
{
RtlRunDecodeUnicodeString( Seed, &EncodedPassword );
NetStatus = NetpManageIPCConnect( DomainControllerName,
lpAccount, EncodedPassword.Buffer,
NETSETUPP_CONNECT_IPC );
RtlRunEncodeUnicodeString( &Seed, &EncodedPassword );
NetpLog(( "NetpChangeMachineName: status of connecting to dc '%ws': 0x%lx\n", DomainControllerName, NetStatus ));
if ( NetStatus == NERR_Success )
{
IpcConnect = TRUE;
//
// REVIEW kahrent 03-March-00
// See review comment above with the same date
//
//$ kumarp 02-June-1999
// the following requires admin access to the dc so that the
// right regkeys could be read to decide the default locale.
// We need to get a remoted API to get default locale
// on the dc.
//
// make sure that the machine name is OEM codepage compatible with
// the default codepage being used on the dc. We determine this
// by translating the machine name to oem string using the
// local code page and the dc code page and then binary comparing
// the resultant strings. if the strings are different, we
// will fail the rename.
//
// Note that this is not a limitation of join apis. netlogon uses
// oem translated netbios machine name internally. the translation
// is different if the code page on the dc and the local machine are
// not compatible. This causes lack of access to net resources.
//
/*
NetStatus = NetpVerifyStrOemCompatibleOnMachine(
DomainControllerName, lpNewMachine);
NetpLog(( "NetpChangeMachineName: status of verifying OEM compatibility of computer name: 0x%lx\n", NetStatus ));
*/
}
}
if ( NetStatus == NERR_Success )
{
NetStatus = NetpManageMachineAccount( lpNewMachine, lpCurrentMachine,
DomainControllerName,
NULL, NETSETUPP_RENAME, 0,
TRUE ); // NT4 DC; but doesn't really matter for rename
}
//
// Set DnsHostName and SPN attributes
//
// First get the local DNS domain name to be used
// in case primary DNS suffix defaults to DNS domain name.
//
if ( NetStatus == NERR_Success ) {
NetStatus = NetpGetLsaPrimaryDomain( NULL,
NULL,
&pPolicyLocalPDI,
&pPolicyLocalDns,
NULL );
}
//
// Determine DnsHostName of this machine
//
if ( NetStatus == NERR_Success &&
(DcFlags & DS_DS_FLAG) != 0 &&
pPolicyLocalDns != NULL ) {
NetStatus = NetpGetDnsHostName( lpNewHostName,
&pPolicyLocalDns->DnsDomainName,
&DnsHostName );
}
//
// Now set DnsHostName and SPNs.
//
// (Comments in NetpJoinDomain where we call
// NetpSetDnsHostNameAndSpn all aply here, too)
//
if ( NetStatus == NERR_Success &&
DnsHostName != NULL &&
lpAccount != NULL && lpPassword != NULL ) {
//
// Tell netlogon that it should avoid
// setting DnsHostName and SPN until the reboot
//
NetpAvoidNetlogonSpnSet( TRUE );
RtlRunDecodeUnicodeString( Seed, &EncodedPassword );
NetStatus = NetpSetDnsHostNameAndSpn( DcInfo,
lpAccount,
EncodedPassword.Buffer,
lpNewMachine,
DnsHostName );
RtlRunEncodeUnicodeString( &Seed, &EncodedPassword );
NetpLog(( "NetpChangeMachineName: status of setting DnsHostName and SPN: 0x%lx\n", NetStatus ));
//
// If the problem was that values were invalid,
// return a distinctive error indicating where that happened
//
if ( NetStatus == ERROR_INVALID_PARAMETER ) {
NetStatus = ERROR_DS_COULDNT_UPDATE_SPNS;
}
//
// On error, take back what we told Netlogon
//
if ( NetStatus != NO_ERROR ) {
NetpAvoidNetlogonSpnSet( FALSE );
}
}
}
if ( IpcConnect )
{
RtlRunDecodeUnicodeString( Seed, &EncodedPassword );
NetpManageIPCConnect( DomainControllerName, lpAccount,
EncodedPassword.Buffer, NETSETUPP_DISCONNECT_IPC );
RtlRunEncodeUnicodeString( &Seed, &EncodedPassword );
}
//
// Free up memory
//
NetApiBufferFree( DomainControllerName );
NetApiBufferFree( DcInfo );
NetApiBufferFree( DnsHostName );
if ( pPolicyLocalPDI != NULL ) {
LsaFreeMemory( pPolicyLocalPDI );
}
if ( pPolicyLocalDns != NULL ) {
LsaFreeMemory( pPolicyLocalDns );
}
NetSetuppCloseLog( );
return( NetStatus );
}
NET_API_STATUS
NET_API_FUNCTION
NetpDsGetDcName(
IN LPWSTR ComputerName, OPTIONAL
IN LPWSTR DomainName,
IN LPWSTR MachineName, OPTIONAL
IN ULONG Flags,
OUT PULONG DcFlags,
OUT PWSTR *DcName,
OUT PDOMAIN_CONTROLLER_INFO *DcInfo OPTIONAL
)
/*++
Routine Description:
Find a dc in the specified domain using the following steps:
- get the machine account name
- find a writable dc that has this machine account
- if we cannot find such dc, find any writable dc
Arguments:
ComputerName -- name of the remote server to process this function.
typically this is the name of the preferred dc.
DomainName -- Domain for which we want to find a dc
MachineName -- current machine name
Flags -- flags to be passed to DsGetDcName* functions
DcFlags -- flags returned by DsGetDcName* functions
DcName -- name of the DC found
DcInfo -- info about the DC found
Returns:
NERR_Success -- Success
--*/
{
NET_API_STATUS NetStatus = NERR_Success;
PDOMAIN_CONTROLLER_INFO pDCInfo = NULL;
WCHAR wszMachine[MAX_COMPUTERNAME_LENGTH + 2];
PWSTR pwszMachine = wszMachine;
ULONG Len;
NetpLog(( "NetpDsGetDcName: trying to find DC in domain '%ws', flags: 0x%lx\n", DomainName, Flags ));
//
// generate the machine account name
//
if ( MachineName == NULL )
{
// note: NetpGetComputerNameAllocIfReqd allocates 1 extra
// char for the $
NetStatus = NetpGetComputerNameAllocIfReqd(&pwszMachine,
MAX_COMPUTERNAME_LENGTH+1);
}
else
{
Len = wcslen( MachineName );
if ( Len > MAX_COMPUTERNAME_LENGTH )
{
NetStatus = NetApiBufferAllocate( ( Len + 2 ) * sizeof(WCHAR),
( PBYTE * )&pwszMachine );
}
if ( NetStatus == NERR_Success )
{
wcscpy(pwszMachine, MachineName);
}
}
if ( NetStatus == NERR_Success )
{
wcscat(pwszMachine, L"$");
}
MachineName = pwszMachine;
//
// Now that we have the machine account name, see if we can
// find a dc that has this account
//
if ( NetStatus == NERR_Success )
{
NetStatus = DsGetDcNameWithAccountW( ComputerName, MachineName,
UF_WORKSTATION_TRUST_ACCOUNT |
UF_SERVER_TRUST_ACCOUNT,
DomainName,
NULL, NULL, Flags, &pDCInfo );
if ( NetStatus == ERROR_NO_SUCH_USER )
{
NetpLog(( "NetpDsGetDcName: failed to find a DC having account '%ws': 0x%lx\n",
MachineName, NetStatus ));
//
// we didnt find a dc with that account.
// try to find any writable dc in that domain
//
NetStatus = DsGetDcName( ComputerName, DomainName,
NULL, NULL, Flags, &pDCInfo );
}
//
// on success, set the out parameters
//
if ( NetStatus == NERR_Success )
{
*DcFlags = pDCInfo->Flags;
NetStatus = NetpDuplicateString(pDCInfo->DomainControllerName,
-1, DcName);
if ( DcInfo )
{
*DcInfo = pDCInfo;
}
else
{
NetApiBufferFree( pDCInfo );
}
}
}
if ( NetStatus == NERR_Success )
{
NetpLog(( "NetpDsGetDcName: found DC '%ws' in the specified domain\n",
*DcName));
}
else
{
NetpLog(( "NetpDsGetDcName: failed to find a DC in the specified domain: 0x%lx\n", NetStatus));
}
//
// Free any memory we may have allocated
//
if ( pwszMachine != wszMachine )
{
NetApiBufferFree( pwszMachine );
}
return( NetStatus );
}
NET_API_STATUS
NET_API_FUNCTION
NetpStopService(
IN LPWSTR Service,
IN SC_HANDLE SCManager
)
/*++
Routine Description:
Stops the specified service and all of its depenendencies
Arguments:
Service -- Name of the service to stop
SCManager -- Open handle to the Service Controller
Returns:
NERR_Success -- Success
--*/
{
NET_API_STATUS NetStatus = NERR_Success;
SERVICE_STATUS SvcStatus;
SC_HANDLE Svc;
LPENUM_SERVICE_STATUS DependentServices = NULL;
ULONG DependSvcSize = 0, DependSvcCount = 0, i;
//
// Open the service
//
Svc = OpenService( SCManager,
Service,
SERVICE_STOP | SERVICE_ENUMERATE_DEPENDENTS | SERVICE_QUERY_STATUS );
if ( Svc == NULL ) {
NetStatus = GetLastError();
NetpLog(( "Failed to open SCManager\n", NetStatus ));
} else {
//
// Enumerate all of the dependent services first
//
if(EnumDependentServices( Svc,
SERVICE_ACTIVE,
NULL,
0,
&DependSvcSize,
&DependSvcCount ) == FALSE ) {
NetStatus = GetLastError();
}
if ( NetStatus == ERROR_MORE_DATA ) {
NetStatus = NetApiBufferAllocate( DependSvcSize,
(PBYTE *)&DependentServices );
if ( NetStatus == NERR_Success ) {
if( EnumDependentServices( Svc,
SERVICE_ACTIVE,
DependentServices,
DependSvcSize,
&DependSvcSize,
&DependSvcCount ) == FALSE ) {
NetStatus = GetLastError();
} else {
for ( i = 0; i < DependSvcCount; i++) {
NetStatus = NetpStopService( DependentServices[i].lpServiceName,
SCManager );
if ( NetStatus != NERR_Success ) {
break;
}
}
}
NetApiBufferFree( DependentServices );
}
}
if ( NetStatus == NERR_Success ) {
if ( ControlService( Svc,
SERVICE_CONTROL_STOP,
&SvcStatus ) == FALSE ) {
NetStatus = GetLastError();
//
// It's not an error if the service wasn't running
//
if ( NetStatus == ERROR_SERVICE_NOT_ACTIVE ) {
NetStatus = ERROR_SUCCESS;
} else {
NetpLog(( "Stop service on '%ws' failed with 0x%lx\n",
Service, NetStatus ));
}
} else {
NetStatus = ERROR_SUCCESS;
//
// Wait for the service to stop
//
while ( TRUE ) {
if( QueryServiceStatus( Svc, &SvcStatus ) == FALSE ) {
NetStatus = GetLastError();
}
if ( NetStatus != ERROR_SUCCESS ||
SvcStatus.dwCurrentState == SERVICE_STOPPED) {
break;
}
Sleep( 1000 );
}
}
} else {
NetpLog(( "Failed to enumerate dependentServices for '%ws': 0x%lx\n",
Service, NetStatus ));
}
}
return( NetStatus );
}
NET_API_STATUS
NET_API_FUNCTION
NetpValidateName(
IN LPWSTR lpMachine,
IN LPWSTR lpName,
IN LPWSTR lpAccount, OPTIONAL
IN LPWSTR lpPassword, OPTIONAL
IN NETSETUP_NAME_TYPE NameType
)
/*++
Routine Description:
Ensures that the given name is valid for a name of that type
Arguments:
lpMachine -- Name of the machine being run on
lpName -- Name to validate
NameType -- Type of the name to validate
Returns:
NERR_Success -- Name is valid
ERROR_INVALID_PARAMETER -- A bad parameter was given
NERR_InvalidComputer -- The name format given is bad
ERROR_DUP_NAME -- The name is invalid for this type
--*/
{
NET_API_STATUS NetStatus = NERR_Success, ReturnStatus;
PWSTR LocalComputerName = NULL;
ULONG ValidateNameType;
BOOLEAN NBNameFailed = FALSE;
BOOLEAN NonRfcDnsName = FALSE;
PSTR NameTypeString;
WCHAR szMachineNameBuf[MAX_COMPUTERNAME_LENGTH + 1];
LPWSTR szMachineName=szMachineNameBuf;
NetSetuppOpenLog();
NetpLog(( "NetpValidateName: checking to see if '%ws' is valid as type %d name\n", GetStrPtr(lpName), (UINT) NameType ));
if ((lpName == NULL) || (NameType == NetSetupUnknown))
{
NetStatus = ERROR_INVALID_PARAMETER;
}
//
// Name valid as a Netbios or DNS name
//
if ( NameType != NetSetupDnsMachine )
{
switch ( NameType )
{
case NetSetupMachine:
ValidateNameType = NAMETYPE_COMPUTER;
ReturnStatus = NERR_InvalidComputer;
NameTypeString = "machine";
break;
case NetSetupWorkgroup:
ValidateNameType = NAMETYPE_WORKGROUP;
ReturnStatus = NERR_InvalidWorkgroupName;
NameTypeString = "workgroup";
break;
case NetSetupDomain:
case NetSetupNonExistentDomain:
ValidateNameType = NAMETYPE_DOMAIN;
ReturnStatus = ERROR_INVALID_DOMAINNAME;
NameTypeString = "domain";
break;
default:
ReturnStatus = ERROR_INVALID_PARAMETER;
break;
}
NetStatus = I_NetNameValidate( NULL, lpName, ValidateNameType,
LM2X_COMPATIBLE );
if ( NetStatus == NERR_Success &&
NameType == NetSetupMachine &&
wcslen( lpName ) > CNLEN )
{
NetStatus = NERR_InvalidComputer;
}
if ( NetStatus != NERR_Success )
{
NetpLog(( "NetpValidateName: '%ws' is not a valid NetBIOS %s name: 0x%lx\n", lpName, NameTypeString, NetStatus ));
//
// If we are checking for a domain name, we'll automaticly
// pass it on to the Dns name validation. This is because
// the name has to only be valid for one name type
//
NetStatus = ReturnStatus;
if ( NameType == NetSetupDomain ||
NameType == NetSetupNonExistentDomain )
{
NBNameFailed = TRUE;
NetStatus = NERR_Success;
}
}
}
if ( NetStatus == NERR_Success )
{
NetStatus = DnsValidateDnsName_W( lpName );
if ( NetStatus != NERR_Success )
{
switch ( NameType )
{
case NetSetupMachine:
case NetSetupWorkgroup:
NetStatus = NERR_Success;
break;
case NetSetupDomain:
case NetSetupNonExistentDomain:
/*
the following if stmt fixes bug #382695.
this bug was postponed therefore I am commenting out
the fix. Uncomment this after NT5 RTM
if (NetStatus == ERROR_INVALID_NAME)
{
NetStatus = ERROR_INVALID_DOMAINNAME;
}
*/
break;
case NetSetupDnsMachine:
default:
break;
}
if ( NetStatus != NERR_Success )
{
NetpLog(( "NetpValidateName: '%ws' is not a valid Dns %s name: 0x%lx\n", lpName, NameType == NetSetupMachine ? "machine" : "domain", NetStatus ));
//
// If the the NetBIOS domain name validation succeeded but
// the Dns failed, consider it success.
//
if ( !NBNameFailed &&
( NameType == NetSetupDomain ||
NameType == NetSetupNonExistentDomain ) )
{
NetStatus = NERR_Success;
}
if (NetStatus == DNS_ERROR_NON_RFC_NAME)
{
NonRfcDnsName = TRUE;
NetStatus = NERR_Success;
}
}
}
}
//
// Name unique from computer name
//
if ( NetStatus == NERR_Success && NameType == NetSetupWorkgroup )
{
if ( lpMachine == NULL )
{
NetStatus = NetpGetNewMachineName( &LocalComputerName );
if ( NetStatus == NERR_Success )
{
lpMachine = LocalComputerName;
}
}
if ( NetStatus == NERR_Success && lpMachine )
{
if ( _wcsicmp( lpName, lpMachine ) == 0 )
{
//
// Not legal
//
NetpLog(( "NetpValidateName: Workgroup same as computer name\n" ));
NetStatus = NERR_InvalidWorkgroupName;
}
else
{
NetStatus = NetpCheckNetBiosNameNotInUse( lpName, TRUE, FALSE );
NetpLog(( "NetpCheckNetBiosNameNotInUse for '%ws' [ Workgroup as MACHINE] returned 0x%lx\n",
lpName, NetStatus ));
//$ REVIEW kumarp 25-May-1999
// this is absolutely the wrong thing to do!!
// the caller has to decide whether to treat
// NERR_NetworkError as error or not. The code
// below hides a fatal error.
//
// Handle the wkgrp name during setup before
// networking is installed case.
//
if ( NetStatus == NERR_NetworkError )
{
NetStatus = NERR_Success;
}
}
}
}
//
// Name not in use as a netbios name
//
// Do this by adding the <lpName>[00] name as a Netbios unique name.
//
if ( NetStatus == NERR_Success &&
( NameType == NetSetupMachine ||
NameType == NetSetupNonExistentDomain ) )
{
NetStatus = NetpCheckNetBiosNameNotInUse(
lpName, TRUE, (BOOLEAN) ( NameType == NetSetupMachine ));
if ((NetStatus == NERR_Success) &&
(NameType == NetSetupNonExistentDomain))
{
//
// make sure that the domain name is not the same as the local
// computer name
//
NetStatus = NetpGetComputerNameAllocIfReqd(
&szMachineName, MAX_COMPUTERNAME_LENGTH+1);
if (NetStatus == NERR_Success)
{
if (!_wcsicmp(szMachineName, lpName))
{
NetStatus = ERROR_DUP_NAME;
}
}
}
NetpLog(( "NetpCheckNetBiosNameNotInUse for '%ws' [MACHINE] returned 0x%lx\n",
lpName, NetStatus ));
}
//
// The idea behind the following block of code was to ensure
// that if this was to be a new domain name or a new machine name,
// we should check to see if the <lpName>[1C] name is registered or not.
// If the name is indeed registered, then this cannot be a valid
// machine/new domain name. Unfortunately, it doesn't quite work.
// The problem is that registering the name as a unique name causes
// problems down the road (WINS??) when we actually try to register
// this as a group name. So, the only thing we can do is registry
// it as a group name, but that doesn't do us any good.
// So, the net result is that this call is useless.
//
#if 0
if ( NetStatus == NERR_Success &&
( NameType == NetSetupMachine ||
NameType == NetSetupNonExistentDomain ) )
{
NetStatus = NetpCheckNetBiosNameNotInUse( lpName, FALSE, FALSE );
NetpLog(( "NetpCheckNetBiosNameNotInUse for '%ws' [MACHINE/DOMAIN] returned 0x%lx\n",
lpName, NetStatus ));
}
#endif
//
// Next, the valid domain name
//
if ( NetStatus == NERR_Success && NameType == NetSetupDomain )
{
if ( _wcsicmp( lpName, L"BUILTIN" ) == 0 )
{
NetStatus = NERR_InvalidComputer;
}
else
{
NetStatus = NetpCheckDomainNameIsValid( lpName, lpAccount,
lpPassword, TRUE );
}
NetpLog(( "NetpCheckDomainNameIsValid [ Exists ] for '%ws' returned 0x%lx\n",
lpName, NetStatus ));
}
//
// Then, the invalid domain name
//
if ( NetStatus == NERR_Success && NameType == NetSetupNonExistentDomain )
{
if ( _wcsicmp( lpName, L"BUILTIN" ) == 0 )
{
NetStatus = NERR_InvalidComputer;
}
else
{
NetStatus = NetpCheckDomainNameIsValid( lpName, lpAccount,
lpPassword, FALSE );
}
NetpLog(( "NetpCheckDomainNameIsValid [ NON-Existant ]for '%ws' returned 0x%lx\n", lpName, NetStatus ));
//$ REVIEW kumarp 01-October-1999
//
// the if stmt below should be in an outer scope.
// currently it only affects the NetSetupNonExistentDomain case.
//
// we have verified that a domain with this name does not exist,
// restore the non-rfc error code if we got it earlier.
//
if ((NetStatus == NERR_Success) && NonRfcDnsName)
{
NetpLog(( "NetpValidateName: a domain with the specified non-RFC compliant name does not exist\n" ));
NetStatus = DNS_ERROR_NON_RFC_NAME;
}
}
if ( NetStatus == NERR_Success )
{
NetpLog(( "NetpValidateName: name '%ws' is valid for type %lu\n",
lpName, NameType ));
}
NetApiBufferFree( LocalComputerName );
if (szMachineName != szMachineNameBuf)
{
NetApiBufferFree(szMachineName);
}
NetSetuppCloseLog( );
return( NetStatus );
}
NET_API_STATUS
NET_API_FUNCTION
NetpGetJoinInformation(
IN LPWSTR lpServer OPTIONAL,
OUT LPWSTR *lpNameBuffer,
OUT PNETSETUP_JOIN_STATUS BufferType
)
/*++
Routine Description:
Gets information on the state of the workstation. The information
obtainable is whether the machine is joined to a workgroup or a domain,
and optionally, the name of that workgroup/domain.
Arguments:
lpServer -- ignored, set to NULL
lpNameBuffer -- Where the domain/workgroup name is returned.
memory allocated for this must be freed by the caller
by calling NetApiBufferFree
BufferType -- Whether the machine is joined to a workgroup or a domain
Returns:
NERR_Success -- Name is valid
ERROR_INVALID_PARAMETER -- A bad parameter was given
ERROR_NOT_ENOUGH_MEMORY -- A memory allocation failed
--*/
{
NET_API_STATUS NetStatus = NERR_Success;
PPOLICY_PRIMARY_DOMAIN_INFO pPolicyPDI=NULL;
PPOLICY_DNS_DOMAIN_INFO pPolicyDns=NULL;
//
// Check the parameters
//
if ( ( lpNameBuffer == NULL ) || ( BufferType == NULL ) )
{
return( ERROR_INVALID_PARAMETER );
}
//
// Get the current domain information
//
NetStatus = NetpGetLsaPrimaryDomain( NULL, NULL, &pPolicyPDI,
&pPolicyDns, NULL );
if ( NetStatus == NERR_Success )
{
if ( pPolicyPDI->Sid == NULL )
{
if ( pPolicyPDI->Name.Length == 0 )
{
*BufferType = NetSetupUnjoined;
}
else
{
*BufferType = NetSetupWorkgroupName;
}
}
else
{
if ( pPolicyPDI->Name.Length == 0 )
{
*BufferType = NetSetupUnjoined;
}
else
{
*BufferType = NetSetupDomainName;
}
}
//
// Copy the string
//
if ( *BufferType == NetSetupUnjoined || pPolicyPDI->Name.Length == 0 )
{
*lpNameBuffer = NULL;
}
else
{
NetStatus = NetpDuplicateString(pPolicyPDI->Name.Buffer,
pPolicyPDI->Name.Length / sizeof(WCHAR),
lpNameBuffer);
}
LsaFreeMemory( pPolicyPDI );
LsaFreeMemory( pPolicyDns );
}
return( NetStatus );
}
NET_API_STATUS
NET_API_FUNCTION
NetpDoDomainJoin(
IN LPWSTR lpServer,
IN LPWSTR lpDomain,
IN LPWSTR lpMachineAccountOU, OPTIONAL
IN LPWSTR lpAccount,
IN LPWSTR lpPassword,
IN DWORD fOptions
)
/*++
Routine Description:
Joins the machine to the domain.
Arguments:
lpServer -- Name of the machine being run on
lpDomain -- Domain to join
lpMachineAccountOU -- Optional OU under which to create the machine account
lpAccount -- Account to use for join
lpPassword -- Password matching the account. The password has been encoded and the
first WCHAR of the password buffer is the seed
fOptions -- Options to use when joining the domain
Returns:
NERR_Success -- Success
ERROR_INVALID_PARAMETER -- A bad parameter was given
--*/
{
NET_API_STATUS NetStatus = NERR_Success;
LPTSTR ComputerName = NULL;
NetSetuppOpenLog();
NetpLog(( "NetpDoDomainJoin\n" ));
//
// Check the parameters we can
//
if (lpDomain == NULL )
{
NetStatus = ERROR_INVALID_PARAMETER;
}
#if(_WIN32_WINNT < 0x0500)
if ( lpMachineAccountOU != NULL )
{
NetStatus = ERROR_INVALID_PARAMETER;
}
#endif
//
// If we have no machine name, get the current name
//
if ((NetStatus == NERR_Success) && (lpServer == NULL))
{
NetStatus = NetpGetNewMachineName( &ComputerName );
if ( NetStatus == NERR_Success )
{
lpServer = ComputerName;
}
}
//
// Then, see about the join...
//
if ( NetStatus == NERR_Success )
{
if ( FLAG_ON( fOptions, NETSETUP_JOIN_DOMAIN ) )
{
//
// A domain join
//
NetStatus = NetpMachineValidToJoin( lpServer, TRUE );
if ((NERR_SetupAlreadyJoined == NetStatus) &&
(FLAG_ON(fOptions, NETSETUP_IGNORE_JOIN) ||
FLAG_ON(fOptions, NETSETUP_DOMAIN_JOIN_IF_JOINED)))
{
NetStatus = NERR_Success;
}
if ( NetStatus == NERR_Success )
{
NetStatus = NetpJoinDomain( lpServer, lpDomain, lpMachineAccountOU,
lpAccount, lpPassword, fOptions );
}
}
else
{
//
// Doing a workgroup join
//
NetStatus = NetpMachineValidToJoin( lpServer, FALSE );
if ( NetStatus == NERR_Success )
{
NetStatus = NetpJoinWorkgroup( lpServer, lpDomain );
}
}
}
NetApiBufferFree( ComputerName );
NetpLog(( "NetpDoDomainJoin: status: 0x%lx\n", NetStatus ));
NetSetuppCloseLog( );
return( NetStatus );
}
NET_API_STATUS
NET_API_FUNCTION
NetpSetMachineAccountPassword(
IN LPWSTR lpDc,
IN LPWSTR lpDomain,
IN LPWSTR lpMachine,
IN LPWSTR lpPassword
)
/*++
Routine Description:
Sets the password on the machine account object
Arguments:
lpDcName -- Name of a DC in the domain
lpDomain -- Name of the domain
lpMachine -- Current name of the machine
lpPassword -- Password to use for machine object.
Returns:
NERR_Success -- Success
--*/
{
NET_API_STATUS NetStatus = NERR_Success;
LPWSTR lpMachAcc = NULL;
USER_INFO_1 *CurrentUI1;
//
// Build the machine account name
//
NetStatus = NetpGetMachineAccountName(lpMachine, &lpMachAcc);
//
// Now, either create or delete it
//
if ( NetStatus == NERR_Success )
{
//
// Get the current info
//
NetStatus = NetUserGetInfo( lpDc, lpMachAcc, 1, (PBYTE*) &CurrentUI1 );
if ( NetStatus == NERR_Success )
{
CurrentUI1->usri1_password = lpPassword;
//$ REVIEW kumarp 17-May-1999
// why??
//
if (!FLAG_ON( CurrentUI1->usri1_flags, UF_WORKSTATION_TRUST_ACCOUNT))
{
CurrentUI1->usri1_flags = UF_WORKSTATION_TRUST_ACCOUNT | UF_SCRIPT;
}
NetStatus = NetUserSetInfo( lpDc, lpMachAcc, 1, (PBYTE) CurrentUI1, NULL );
if ( NetStatus != NERR_Success )
{
NetpLog(( "NetpSetMachineAccountPassword: NetUserSetInfo on '%ws' '%ws' failed: 0x%lx\n", lpDc, lpMachAcc, NetStatus ));
}
NetApiBufferFree( CurrentUI1 );
}
else
{
NetpLog(( "NetpSetMachineAccountPassword: NetUserGetInfo on '%ws' '%ws' failed: 0x%lx\n", lpDc, lpMachAcc, NetStatus ));
}
NetApiBufferFree( lpMachAcc );
}
return( NetStatus );
}
#define COMPUTERNAME_ROOT \
L"System\\CurrentControlSet\\Control\\ComputerName\\ComputerName"
#define NEW_COMPUTERNAME_VALUE_NAME L"ComputerName"
NET_API_STATUS
NET_API_FUNCTION
NetpGetNewMachineName(
OUT PWSTR *NewMachineName
)
/*++
Routine Description:
This function reads the new Machine name parameter from the registry.
This values is what the computer name will be after the next reboot.
The returned buffer must be freed via NetApiBufferFree
Arguments:
NewMachineName -- Where the new machine name is returned
Returns:
NERR_Success -- Success
ERROR_NOT_ENOUGH_MEMORY -- A memory allocation failed
--*/
{
NET_API_STATUS NetStatus = NERR_Success;
HKEY ComputerNameRootKey;
ULONG Length = 0;
//
// Open the registry key
//
NetStatus = RegOpenKeyEx( HKEY_LOCAL_MACHINE, COMPUTERNAME_ROOT,
0, KEY_READ, &ComputerNameRootKey);
if ( NetStatus == ERROR_SUCCESS )
{
DWORD ValueType;
//
// Determine the size we need
//
NetStatus = RegQueryValueEx( ComputerNameRootKey,
NEW_COMPUTERNAME_VALUE_NAME,
0, &ValueType, NULL, &Length );
if ( NetStatus == NERR_Success )
{
//
// Allocate the buffer and re-read it.
//
NetStatus = NetApiBufferAllocate( Length,
( LPVOID * )NewMachineName );
if ( NetStatus == NERR_Success )
{
NetStatus = RegQueryValueEx( ComputerNameRootKey,
NEW_COMPUTERNAME_VALUE_NAME,
0, &ValueType,
( PBYTE )*NewMachineName, &Length );
}
}
RegCloseKey( ComputerNameRootKey );
}
return( NetStatus );
}
BOOL
IsNonEmptyUnicodeStr( IN UNICODE_STRING* ps )
{
return ps && ps->Length && ps->Buffer && *(ps->Buffer);
}
NET_API_STATUS
NET_API_FUNCTION
NetpValidateJoinStateAndActions(
IN NET_JOIN_STATE* pJoinState,
IN DWORD dwJoinAction
)
{
NET_API_STATUS NetStatus = NERR_Success;
GUID guidNull = { 0 };
/*
//
// validate POLICY_DNS_DOMAIN_INFO
//
if (dwJoinAction & NJA_SetPolicyDomainInfo)
{
if (!(IsNonEmptyUnicodeStr(&pJoinState->PolicyDDI.Name) &&
IsNonEmptyUnicodeStr(&pJoinState->PolicyDDI.DnsDomainName) &&
IsNonEmptyUnicodeStr(&pJoinState->PolicyDDI.DnsForestName) &&
memcmp((PVOID) &guidNull,
(PVOID) &pJoinState->PolicyDDI.DomainGuid,
sizeof(GUID)) &&
&pJoinState->PolicyDDI.Sid))
{
NetStatus = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
}
*/
if (dwJoinAction & NJA_SetNetlogonState)
{
if (!(((pJoinState->uiNetlogonStartType == NETSETUP_SVC_ENABLED) ||
(pJoinState->uiNetlogonStartType == NETSETUP_SVC_MANUAL)) &&
((pJoinState->uiNetlogonState == NETSETUP_SVC_STOPPED) ||
(pJoinState->uiNetlogonState == NETSETUP_SVC_STARTED))))
{
NetStatus = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
}
Cleanup:
return NetStatus;
}
NET_API_STATUS
NET_API_FUNCTION
NetpApplyJoinState(
IN NET_JOIN_STATE* pJoinState,
IN DWORD dwJoinAction,
IN LPWSTR szMachineName, OPTIONAL
IN LPWSTR szUser, OPTIONAL
IN LPWSTR szUserPassword, OPTIONAL
IN LPWSTR szPreferredDc OPTIONAL
)
{
NET_API_STATUS NetStatus = NERR_Success, NetStatus2;
NET_API_STATUS RetStatus = NERR_Success;
PWSTR szDcName = NULL;
PPOLICY_PRIMARY_DOMAIN_INFO pDcPolicyPDI = NULL, pLocalPolicyPDI = NULL;
PPOLICY_DNS_DOMAIN_INFO pDcPolicyDns = NULL, pLocalPolicyDns = NULL;
BOOL fIpcConnected = FALSE;
LSA_HANDLE hLsaLocal = NULL, hLsaDc = NULL;
WCHAR szMachinePasswordBuf[ PWLEN + 1];
ULONG ulIpcConnectFlags = NETSETUPP_CONNECT_IPC;
ULONG ulDcFlags=0, ulGetDcFlags = 0;
UNICODE_STRING sUserPassword;
UCHAR Seed;
BOOL fIsNt4Dc=FALSE;
BOOL fRandomPwdPreferred=TRUE;
PDOMAIN_CONTROLLER_INFO pDcInfo = NULL;
PWSTR szDnsDomainName=NULL;
NET_JOIN_STATE RollbackState = { 0 };
DWORD dwRollbackAction=0;
BOOL fIgnoreErrors=FALSE;
BOOL fSaveRollbackInfo=FALSE;
PWSTR szCurrentMachinePassword = NULL;
fIgnoreErrors = FLAG_ON ( dwJoinAction, NJA_IgnoreErrors );
fSaveRollbackInfo = FLAG_ON ( dwJoinAction, NJA_RollbackOnFailure );
NetpLog(( "NetpApplyJoinState: actions: 0x%lx\n", dwJoinAction ));
if ( szUserPassword )
{
if ( wcslen( szUserPassword ) < 1 )
{
return ERROR_INVALID_PARAMETER;
}
Seed = ( UCHAR )*szUserPassword;
RtlInitUnicodeString( &sUserPassword, szUserPassword + 1 );
}
else
{
RtlZeroMemory( &sUserPassword, sizeof( UNICODE_STRING ) );
Seed = 0;
}
NetStatus = NetpValidateJoinStateAndActions(pJoinState, dwJoinAction);
if ( NetStatus != NERR_Success )
{
return NetStatus;
}
//
// connect to the DC only if required by at least on action
//
if ( FLAG_ON( dwJoinAction, NJA_NeedDc ) )
{
ulGetDcFlags = FLAG_ON( dwJoinAction, NJA_CreateAccount ) ?
NETSETUPP_DSGETDC_FLAGS : NETSETUP_DSGETDC_FLAGS_ACCOUNT_EXISTS;
//
// Find a DC in the domain using these steps:
// - find a writable dc that has this machine account
// - if we cannot find such dc, find any writable dc
//
NetStatus = NetpDsGetDcName( szPreferredDc,
(LPWSTR) pJoinState->szDomainName,
szMachineName, ulGetDcFlags,
&ulDcFlags, &szDcName, &pDcInfo );
//
// First establish connection with the dc we found
//
if ( NetStatus == NERR_Success )
{
fIsNt4Dc = !FLAG_ON( ulDcFlags, DS_DS_FLAG);
if ( !FLAG_ON( dwJoinAction, NJA_CreateAccount ) )
{
ulIpcConnectFlags |= NETSETUPP_NULL_SESSION_IPC;
}
RtlRunDecodeUnicodeString( Seed, &sUserPassword );
NetStatus = NetpManageIPCConnect( szDcName,
szUser, sUserPassword.Buffer,
ulIpcConnectFlags );
RtlRunEncodeUnicodeString( &Seed, &sUserPassword );
if ( NetStatus == NERR_Success )
{
fIpcConnected = TRUE;
}
NetpLog(( "NetpApplyJoinState: status of connecting to dc '%ws': 0x%lx\n", szDcName, NetStatus ));
}
}
if (NetStatus != NERR_Success)
{
RetStatus = NetStatus;
}
//
// stop netlogon service if specified
//
if ( FLAG_ON ( dwJoinAction, NJA_SetNetlogonState ) &&
( pJoinState->uiNetlogonState == NETSETUP_SVC_STOPPED ) &&
( fIgnoreErrors || ( NetStatus == NERR_Success ) ) )
{
NetStatus = NetpControlServices( pJoinState->uiNetlogonStartType |
NETSETUP_SVC_STOPPED,
NETSETUPP_SVC_NETLOGON );
if (fSaveRollbackInfo && (NetStatus == NERR_Success))
{
RollbackState.uiNetlogonState = NETSETUP_SVC_STARTED;
RollbackState.uiNetlogonStartType = NETSETUP_SVC_ENABLED;
dwRollbackAction |= NJA_SetNetlogonState;
}
NetpLog(( "NetpApplyJoinState: status of stopping and setting start type of Netlogon to %ld: 0x%lx\n", pJoinState->uiNetlogonStartType, NetStatus ));
}
if (NetStatus != NERR_Success)
{
RetStatus = NetStatus;
}
//
// config w32time service if specified
//
if ( FLAG_ON ( dwJoinAction, NJA_SetTimeSvcJoin ) &&
( fIgnoreErrors || ( NetStatus == NERR_Success ) ) )
{
NetStatus = NetpUpdateW32timeConfig( "W32TimeVerifyJoinConfig" );
if (fSaveRollbackInfo && (NetStatus == NERR_Success))
{
dwRollbackAction |= NJA_SetTimeSvcUnjoin;
}
}
if (NetStatus != NERR_Success)
{
RetStatus = NetStatus;
}
if (fIgnoreErrors || (NetStatus == NERR_Success))
{
//
// read our old primary domain info
//
NetStatus = NetpGetLsaPrimaryDomain( NULL, NULL,
&pLocalPolicyPDI,
&pLocalPolicyDns,
&hLsaLocal );
if (fSaveRollbackInfo && (NetStatus == NERR_Success))
{
RollbackState.pPolicyPDI = pLocalPolicyPDI;
RollbackState.pPolicyDDI = pLocalPolicyDns;
}
}
if (NetStatus != NERR_Success)
{
RetStatus = NetStatus;
}
if ( FLAG_ON ( dwJoinAction, NJA_GetPolicyDomainInfo ) && fIpcConnected &&
(fIgnoreErrors || (NetStatus == NERR_Success)))
{
//
// get the lsa domain info on the DC
//
NetStatus = NetpGetLsaPrimaryDomain(NULL, szDcName, &pDcPolicyPDI,
&pDcPolicyDns, &hLsaDc);
if (NetStatus == NERR_Success)
{
pJoinState->pPolicyPDI = pDcPolicyPDI;
pJoinState->pPolicyDDI = pDcPolicyDns;
}
}
if (NetStatus != NERR_Success)
{
RetStatus = NetStatus;
}
if ((fIgnoreErrors || (NetStatus == NERR_Success)) &&
pJoinState->pPolicyDDI &&
pJoinState->pPolicyDDI->DnsDomainName.Buffer)
{
//
// make a copy of of DnsDomainName in the form of sz
//
NetStatus = NetpDuplicateString(
pJoinState->pPolicyDDI->DnsDomainName.Buffer,
pJoinState->pPolicyDDI->DnsDomainName.Length / sizeof(WCHAR),
&szDnsDomainName);
}
if (NetStatus != NERR_Success)
{
RetStatus = NetStatus;
}
if ( FLAG_ON ( dwJoinAction, NJA_GenMachinePassword ) &&
( fIgnoreErrors || ( NetStatus == NERR_Success ) ) )
{
fRandomPwdPreferred = FLAG_ON ( dwJoinAction, NJA_RandomPwdPreferred );
//
// Generate the password to use on the machine account.
// This can either be the default password
// (the 1st 14 characters of the machine name, lower cased),
// or a randomly generated password.
//
NetStatus = NetpGeneratePassword( szMachineName,
fRandomPwdPreferred,
szDcName, fIsNt4Dc,
szMachinePasswordBuf );
if (NetStatus == NERR_Success)
{
pJoinState->szMachinePassword = szMachinePasswordBuf;
}
}
if (NetStatus != NERR_Success)
{
RetStatus = NetStatus;
}
if (fSaveRollbackInfo && (NetStatus == NERR_Success) &&
FLAG_ON (dwJoinAction, (NJA_SetMachinePassword |
NJA_DeleteMachinePassword)))
{
NetStatus = NetpReadCurrentSecret( NULL, &szCurrentMachinePassword,
&hLsaLocal );
if (NetStatus == NERR_Success)
{
RollbackState.szMachinePassword = szCurrentMachinePassword;
}
else if (NetStatus == ERROR_FILE_NOT_FOUND)
{
NetpLog(( "NetpApplyJoinState: machine secret not found. join state seems to be inconsistent. this case is correctly handled by the code.\n" ));
dwRollbackAction |=
NJA_GenMachinePassword | NJA_RandomPwdPreferred;
NetStatus = NERR_Success;
}
}
if (NetStatus != NERR_Success)
{
RetStatus = NetStatus;
}
//
// set the local machine secret, create if not already present
//
if ( FLAG_ON ( dwJoinAction, NJA_SetMachinePassword ) &&
( fIgnoreErrors || ( NetStatus == NERR_Success ) ) )
{
// this also opens the local LSA policy handle
NetStatus = NetpManageMachineSecret(
NULL, szMachineName,
(LPWSTR) pJoinState->szMachinePassword,
NETSETUPP_CREATE, FALSE, &hLsaLocal );
if (fSaveRollbackInfo && (NetStatus == NERR_Success))
{
dwRollbackAction |= NJA_SetMachinePassword;
}
}
if (NetStatus != NERR_Success)
{
RetStatus = NetStatus;
}
//
// delete the local machine secret
//
if ( FLAG_ON ( dwJoinAction, NJA_DeleteMachinePassword ) &&
( fIgnoreErrors || ( NetStatus == NERR_Success ) ) )
{
// this also opens the local LSA policy handle
NetStatus = NetpManageMachineSecret(
NULL, szMachineName,
(LPWSTR) pJoinState->szMachinePassword,
NETSETUPP_DELETE, FALSE, &hLsaLocal );
if (fSaveRollbackInfo && (NetStatus == NERR_Success))
{
dwRollbackAction |= NJA_SetMachinePassword;
}
}
if (NetStatus != NERR_Success)
{
RetStatus = NetStatus;
}
//
// kahrent 10-09-99 (adding comment)
//
// After this point we will be doing changes on the DC.
// However, if we are not successful at this point, fail
// over and rollback all the changes we've done locally.
//
if ((NetStatus != NERR_Success) && !fIgnoreErrors)
{
goto Cleanup;
}
//
// create the machine account on the dc if specified
//
if ( FLAG_ON ( dwJoinAction, NJA_CreateAccount ) )
{
NetStatus = NetpManageMachineAccountWithSid(
szMachineName, NULL, szDcName,
(LPWSTR) pJoinState->szMachinePassword,
pJoinState->pPolicyPDI->Sid, NETSETUPP_CREATE, 0, fIsNt4Dc );
NetpLog(( "NetpApplyJoinState: status of creating account: 0x%lx\n", NetStatus ));
if (fSaveRollbackInfo && (NetStatus == NERR_Success))
{
dwRollbackAction |= NJA_DeleteAccount;
}
}
else if ( FLAG_ON ( dwJoinAction, NJA_DeleteAccount ) )
{
//
// delete the machine account on the dc if specified
//
if (fIsNt4Dc)
{
NetpLog(( "NetpApplyJoinState: account not disabled since we are talking to NT4 dc\n" ));
}
else
{
NetStatus = NetpManageMachineAccountWithSid(
szMachineName, NULL, szDcName,
(LPWSTR) pJoinState->szMachinePassword,
pLocalPolicyPDI->Sid, NETSETUPP_DELETE, 0, fIsNt4Dc );
NetpLog(( "NetpApplyJoinState: status of disabling account: 0x%lx\n", NetStatus ));
if (fSaveRollbackInfo && (NetStatus == NERR_Success))
{
dwRollbackAction |= NJA_CreateAccount;
}
}
}
if (NetStatus != NERR_Success)
{
RetStatus = NetStatus;
}
if ( FLAG_ON ( dwJoinAction, NJA_ValidateMachineAccount ) && fIpcConnected &&
(fIgnoreErrors || (NetStatus == NERR_Success)))
{
NetStatus = NetpValidateMachineAccount(
szDcName, (LPWSTR) pJoinState->szDomainName,
szMachineName, (LPWSTR) pJoinState->szMachinePassword );
NetpLog(( "NetpApplyJoinState: w9x: status of validating account: 0x%lx\n", NetStatus ));
}
if (NetStatus != NERR_Success)
{
RetStatus = NetStatus;
}
if ( FLAG_ON ( dwJoinAction, NJA_SetMachinePassword ) && fIpcConnected &&
(fIgnoreErrors || (NetStatus == NERR_Success)))
{
NetStatus = NetpSetMachineAccountPassword(
szDcName, (LPWSTR) pJoinState->szDomainName,
szMachineName, (LPWSTR) pJoinState->szMachinePassword );
NetpLog(( "NetpApplyJoinState: status of setting machine password: 0x%lx\n", NetStatus ));
}
if (NetStatus != NERR_Success)
{
RetStatus = NetStatus;
}
if ( FLAG_ON ( dwJoinAction, NJA_UpdateNetlogonCache ) && fIpcConnected &&
(fIgnoreErrors || (NetStatus == NERR_Success)))
{
NetStatus = NetpSetNetlogonDomainCache( szDcName );
NetpLog(( "NetpApplyJoinState: status of setting netlogon cache: 0x%lx\n", NetStatus ));
}
if (NetStatus != NERR_Success)
{
RetStatus = NetStatus;
}
//
// Set the primary domain info from the Dc to the client
//
if ( FLAG_ON ( dwJoinAction, NJA_SetPolicyDomainInfo ) &&
pJoinState->pPolicyPDI &&
( fIgnoreErrors || ( NetStatus == NERR_Success ) ) )
{
NetStatus = NetpSetLsaPrimaryDomain(hLsaLocal,
pJoinState->pPolicyPDI->Name.Buffer,
pJoinState->pPolicyPDI->Sid,
pJoinState->pPolicyDDI,
NULL);
NetpLog(( "NetpApplyJoinState: status of setting LSA pri. domain: 0x%lx\n", NetStatus ));
if (fSaveRollbackInfo && (NetStatus == NERR_Success))
{
dwRollbackAction |= NJA_SetPolicyDomainInfo;
}
}
//
// Set the new DNS computer name, as needed
//
if ( FLAG_ON(dwJoinAction, NJA_SetPolicyDomainInfo) &&
(fIgnoreErrors || (NetStatus == NERR_Success)) ) {
//
// If we have a new name, set it if it's different from the old one
//
if ( pJoinState->pPolicyDDI != NULL ) {
if ( pLocalPolicyDns == NULL ||
RtlCompareUnicodeString( &pLocalPolicyDns->DnsDomainName,
&pJoinState->pPolicyDDI->DnsDomainName,
TRUE ) != 0 ) {
NetStatus = NetpSetDnsComputerNameAsRequired( pJoinState->pPolicyDDI->DnsDomainName.Buffer );
NetpLog(( "NetpApplyJoinState: status of setting ComputerNamePhysicalDnsDomain to '%wZ': 0x%lx\n",
&pJoinState->pPolicyDDI->DnsDomainName,
NetStatus ));
}
//
// If we don't have a new name (must be an NT4 domain/DC),
// clear the old name, if any.
//
// Note, if this is an NT5 domain but we simply don't have
// an NT5 DC at this time, netlogon will eventually discover
// an NT5 DC and netlogon will set the new name then.
//
} else if ( pLocalPolicyDns != NULL ) {
NetStatus = NetpSetDnsComputerNameAsRequired( L"\0" );
NetpLog(( "NetpApplyJoinState: status of clearing ComputerNamePhysicalDnsDomain: 0x%lx\n",
NetStatus ));
}
}
if (NetStatus != NERR_Success)
{
RetStatus = NetStatus;
}
//
// add to local group memberships
//
if ( FLAG_ON ( dwJoinAction, NJA_AddToLocalGroups ) &&
( fIgnoreErrors || ( NetStatus == NERR_Success ) ) )
{
NetStatus = NetpManageLocalGroups( pJoinState->pPolicyPDI->Sid, NETSETUPP_CREATE );
if (fSaveRollbackInfo && (NetStatus == NERR_Success))
{
dwRollbackAction |= NJA_RemoveFromLocalGroups;
}
NetpLog(( "NetpApplyJoinState: status of adding to local groups: 0x%lx\n", NetStatus ));
}
if (NetStatus != NERR_Success)
{
RetStatus = NetStatus;
}
//
// remove from our local group memberships
//
if ( FLAG_ON ( dwJoinAction, NJA_RemoveFromLocalGroups ) &&
( fIgnoreErrors || ( NetStatus == NERR_Success ) ) )
{
NetStatus = NetpManageLocalGroups( pLocalPolicyPDI->Sid, NETSETUPP_DELETE );
if (fSaveRollbackInfo && (NetStatus == NERR_Success))
{
dwRollbackAction |= NJA_AddToLocalGroups;
}
NetpLog(( "NetpApplyJoinState: status of removing from local groups: 0x%lx\n", NetStatus ));
}
if (NetStatus != NERR_Success)
{
RetStatus = NetStatus;
}
//
// start netlogon service if specified
//
if ( FLAG_ON ( dwJoinAction, NJA_SetNetlogonState ) &&
( pJoinState->uiNetlogonState == NETSETUP_SVC_STARTED ) &&
( fIgnoreErrors || ( NetStatus == NERR_Success ) ) )
{
NetStatus = NetpControlServices( pJoinState->uiNetlogonStartType |
NETSETUP_SVC_STARTED,
NETSETUPP_SVC_NETLOGON );
if (fSaveRollbackInfo && (NetStatus == NERR_Success))
{
RollbackState.uiNetlogonState = NETSETUP_SVC_STOPPED;
RollbackState.uiNetlogonStartType = NETSETUP_SVC_MANUAL;
dwRollbackAction |= NJA_SetNetlogonState;
}
NetpLog(( "NetpApplyJoinState: status of starting and setting start type of Netlogon to %ld: 0x%lx\n", pJoinState->uiNetlogonStartType, NetStatus ));
}
if (NetStatus != NERR_Success)
{
RetStatus = NetStatus;
}
//
// config w32time service if specified
//
if ( FLAG_ON ( dwJoinAction, NJA_SetTimeSvcUnjoin ) &&
( fIgnoreErrors || ( NetStatus == NERR_Success ) ) )
{
NetStatus = NetpUpdateW32timeConfig( "W32TimeVerifyUnjoinConfig" );
if (fSaveRollbackInfo && (NetStatus == NERR_Success))
{
dwRollbackAction |= NJA_SetTimeSvcJoin;
}
}
//
// Config AutoEnrol service if specified.
//
// On join, do this after enabling the account in
// the DS because Autoenrol needs the account
// enabled to have the DS access. Also, do this
// after deleting the local secret to ensure we
// are running as local admin.
//
if ( FLAG_ON ( dwJoinAction, NJA_SetAutoenrolSvcJoin ) &&
( fIgnoreErrors || ( NetStatus == NERR_Success ) ) )
{
//
// Ignore the failure since it's not critical
// but remember if we need to do anything on rollback
//
NET_API_STATUS TmpNetStatus = NetpUpdateAutoenrolConfig( FALSE );
if (fSaveRollbackInfo && (TmpNetStatus == NERR_Success))
{
dwRollbackAction |= NJA_SetAutoenrolSvcUnjoin;
}
}
if ( FLAG_ON ( dwJoinAction, NJA_SetAutoenrolSvcUnjoin ) &&
( fIgnoreErrors || ( NetStatus == NERR_Success ) ) )
{
//
// Ignore the failure since it's not critical
// but remember if we need to do anything on rollback
//
NET_API_STATUS TmpNetStatus = NetpUpdateAutoenrolConfig( TRUE );
if (fSaveRollbackInfo && (TmpNetStatus == NERR_Success))
{
dwRollbackAction |= NJA_SetAutoenrolSvcJoin;
}
}
if (NetStatus != NERR_Success)
{
RetStatus = NetStatus;
}
//
// Save off the name of the initial domain controller we contacted
// if we successfully joined the domain
//
if ( FLAG_ON ( dwJoinAction, NJA_RecordDcInfo ) && fIpcConnected &&
(fIgnoreErrors || (NetStatus == NERR_Success)))
{
//
// A failure here is NON-FATAL, so we ignore the error code
//
NetStatus2 = NetpStoreIntialDcRecord( pDcInfo );
if ( NetStatus2 != NERR_Success )
{
NetpLog(( "NetpApplyJoinState: NON FATAL: failed to store the initial Dc record for '%ws': 0x%lx\n", szDcName, NetStatus ));
}
}
if (NetStatus != NERR_Success)
{
RetStatus = NetStatus;
}
//
// Remove DNS registrations
//
if ( FLAG_ON ( dwJoinAction, NJA_RemoveDnsRegistrations ) &&
( fIgnoreErrors || ( NetStatus == NERR_Success ) ) )
{
NetStatus2 = NetpRemoveDnsRegistrations();
NetpLog(( "NetpApplyJoinState: NON FATAL: status of removing DNS registrations: 0x%lx\n", NetStatus2 ));
}
Cleanup:
if (fSaveRollbackInfo && (NetStatus != NERR_Success))
{
// time to rollback. ignore all errors during rollback
NetpLog(( "NetpApplyJoinState: initiating a rollback due to earlier errors\n"));
dwRollbackAction |= NJA_IgnoreErrors;
NetStatus2 = NetpApplyJoinState(&RollbackState, dwRollbackAction,
szMachineName, szUser, szUserPassword,
szDcName ? szDcName : szPreferredDc);
}
LsaFreeMemory( pDcPolicyPDI );
LsaFreeMemory( pDcPolicyDns );
LsaFreeMemory( pLocalPolicyPDI );
LsaFreeMemory( pLocalPolicyDns );
if ( hLsaLocal != NULL )
{
LsaClose( hLsaLocal );
}
if ( hLsaDc != NULL )
{
LsaClose( hLsaDc );
}
//
// Now, we'll no longer need our session to our dc
//
if ( fIpcConnected )
{
RtlRunDecodeUnicodeString( Seed, &sUserPassword );
NetStatus2 = NetpManageIPCConnect( szDcName, szUser,
sUserPassword.Buffer,
NETSETUPP_DISCONNECT_IPC );
RtlRunEncodeUnicodeString( &Seed, &sUserPassword );
NetpLog(( "NetpApplyJoinState: status of disconnecting from '%ws': 0x%lx\n", szDcName, NetStatus2));
}
// Note: NetApiBufferFree checks for NULL
NetApiBufferFree( pDcInfo );
NetApiBufferFree( szDcName );
NetApiBufferFree( szDnsDomainName );
NetApiBufferFree( szCurrentMachinePassword );
if (RetStatus != NERR_Success)
{
NetStatus = RetStatus;
}
return( NetStatus );
}
NET_API_STATUS
NET_API_FUNCTION
NetpForceMachinePasswordChange(
IN LPWSTR szDomainName
)
{
NET_API_STATUS NetStatus = NERR_Success;
LPBYTE pNetlogonInfo=NULL;
NetpLog(( "NetpForceMachinePasswordChange: on '%ws'\n",
GetStrPtr(szDomainName)));
NetStatus = I_NetLogonControl2( NULL,
NETLOGON_CONTROL_CHANGE_PASSWORD,
1, (LPBYTE) &szDomainName,
(LPBYTE *) &pNetlogonInfo );
if (NetStatus == NERR_Success)
{
NetApiBufferFree(pNetlogonInfo);
}
NetpLog(( "NetpForceMachinePasswordChange: status: 0x%lx\n", NetStatus));
return NetStatus;
}
NET_API_STATUS
NET_API_FUNCTION
NetpUpgradePreNT5JoinInfo( VOID )
{
NET_API_STATUS NetStatus =NERR_Success;
WCHAR szMachineNameBuf[MAX_COMPUTERNAME_LENGTH + 1];
LPWSTR szMachineName=szMachineNameBuf;
LPWSTR szDomainName=NULL;
NETSETUP_JOIN_STATUS JoinStatus;
NET_JOIN_STATE JoinState = { 0 };
DWORD dwJoinAction=0;
NetSetuppOpenLog();
NetpLog(( "NetpUpgradePreNT5JoinInfo: upgrading join info\n" ));
//
// make sure we are joined to a domain
//
NetStatus = NetpGetJoinInformation(NULL, &szDomainName, &JoinStatus);
if (NetStatus == NERR_Success)
{
if (JoinStatus == NetSetupDomainName)
{
//
// get the computer name
//
NetStatus = NetpGetComputerNameAllocIfReqd(
&szMachineName, MAX_COMPUTERNAME_LENGTH+1);
NetpLog(( "NetpUpgradePreNT5JoinInfo: status of getting computer name: 0x%lx\n", NetStatus ));
}
else
{
//
// machine is not joined to a domain,
// no need to upgrade anything
//
NetStatus = NERR_SetupNotJoined;
goto Cleanup;
}
}
if (NetStatus == NERR_Success)
{
NetpLog(( "NetpUpgradePreNT5JoinInfo: upgrading join info for '%ws' in domain '%ws'\n", szMachineName, szDomainName));
dwJoinAction =
NJA_UpdateNetlogonCache |
NJA_GetPolicyDomainInfo |
NJA_SetPolicyDomainInfo |
NJA_SetNetlogonState |
NJA_RecordDcInfo |
NJA_IgnoreErrors;
JoinState.szDomainName = szDomainName;
JoinState.uiNetlogonStartType = NETSETUP_SVC_ENABLED;
JoinState.uiNetlogonState = NETSETUP_SVC_STARTED;
NetStatus = NetpApplyJoinState(&JoinState, dwJoinAction,
szMachineName, NULL, NULL, NULL);
NetpLog(( "NetpUpgradePreNT5JoinInfo: status of NetpApplyJoinState: 0x%lx\n", NetStatus ));
//
// ignore earlier error, if any, and try to reset password
//
NetStatus = NetpWaitForNetlogonSc(szDomainName);
if (NetStatus == NERR_Success)
{
NetStatus = NetpForceMachinePasswordChange( szDomainName );
}
else
{
NetpLog(( "NetpUpgradePreNT5JoinInfo: netlogon did not establish secure channel, machine password not updated. This is not a fatal error. netlogon will retry updating the password later.\n" ));
}
}
Cleanup:
NetApiBufferFree(szDomainName);
if (szMachineName != szMachineNameBuf)
{
NetApiBufferFree(szMachineName);
}
NetpLog(( "NetpUpgradePreNT5JoinInfo: status: 0x%lx\n", NetStatus ));
NetSetuppCloseLog( );
return NetStatus;
}