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