2724 lines
74 KiB
C
2724 lines
74 KiB
C
/*++
|
||
|
||
Copyright (c) 1998-1998 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
join.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the worker routines for the NetJoin APIs
|
||
implemented in the Workstation service.
|
||
|
||
Author:
|
||
|
||
Mac McLain (macm) 06-Jan-1998
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
#include "wsutil.h"
|
||
#include "wsconfig.h"
|
||
#include <lmerrlog.h>
|
||
#include <lmaccess.h>
|
||
|
||
#define __LMJOIN_H__
|
||
#include <netsetup.h>
|
||
#include <icanon.h>
|
||
#include <crypt.h>
|
||
#include <rc4.h>
|
||
#include <md5.h>
|
||
#if(_WIN32_WINNT >= 0x0500)
|
||
#include <dnsapi.h>
|
||
#include <ntdsapi.h>
|
||
#include <dsgetdc.h>
|
||
#include <dsgetdcp.h>
|
||
#include <winldap.h>
|
||
#include <ntldap.h>
|
||
#endif
|
||
|
||
#if(_WIN32_WINNT < 0x0500)
|
||
#include <winreg.h>
|
||
#endif
|
||
|
||
#define COMPUTERNAME_ROOT \
|
||
L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\ComputerName"
|
||
#define NEW_COMPUTERNAME_VALUE_NAME L"ComputerName"
|
||
|
||
|
||
|
||
|
||
NET_API_STATUS
|
||
JoinpDecryptPasswordWithKey(
|
||
IN handle_t RpcBindingHandle,
|
||
IN PJOINPR_ENCRYPTED_USER_PASSWORD EncryptedPassword,
|
||
IN BOOL EncodePassword,
|
||
OUT LPWSTR *EncodedPassword
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Decrypts a password encrypted with the user session key.
|
||
|
||
Arguments:
|
||
|
||
RpcBindingHandle - Rpc Binding handle describing the session key to use.
|
||
|
||
EncryptedPassword - Encrypted password to decrypt.
|
||
|
||
EncodePassword - If TRUE, the returned password will be encoded
|
||
and the first WCHAR of the password buffer will be the seed
|
||
|
||
EncodedPassword - Returns the (optionally Encoded) password.
|
||
Buffer should be freed using NetpMemoryFree.
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
{
|
||
NET_API_STATUS NetStatus;
|
||
NTSTATUS Status;
|
||
USER_SESSION_KEY UserSessionKey;
|
||
RC4_KEYSTRUCT Rc4Key;
|
||
MD5_CTX Md5Context;
|
||
|
||
UNICODE_STRING EncodedPasswordU;
|
||
LPWSTR PasswordPart;
|
||
UCHAR Seed = 0;
|
||
|
||
PJOINPR_USER_PASSWORD Password = (PJOINPR_USER_PASSWORD) EncryptedPassword;
|
||
|
||
//
|
||
// Handle the trivial case
|
||
//
|
||
|
||
*EncodedPassword = NULL;
|
||
if ( EncryptedPassword == NULL ) {
|
||
return NO_ERROR;
|
||
}
|
||
|
||
//
|
||
// Get the session key
|
||
//
|
||
|
||
|
||
Status = RtlGetUserSessionKeyServer(
|
||
(RPC_BINDING_HANDLE)RpcBindingHandle,
|
||
&UserSessionKey );
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
return NetpNtStatusToApiStatus( Status );
|
||
}
|
||
|
||
//
|
||
// The UserSessionKey is the same for the life of the session. RC4'ing multiple
|
||
// strings with a single key is weak (if you crack one you've cracked them all).
|
||
// So compute a key that's unique for this particular encryption.
|
||
//
|
||
//
|
||
|
||
MD5Init(&Md5Context);
|
||
|
||
MD5Update( &Md5Context, (LPBYTE)&UserSessionKey, sizeof(UserSessionKey) );
|
||
MD5Update( &Md5Context, Password->Obfuscator, sizeof(Password->Obfuscator) );
|
||
|
||
MD5Final( &Md5Context );
|
||
|
||
rc4_key( &Rc4Key, MD5DIGESTLEN, Md5Context.digest );
|
||
|
||
|
||
//
|
||
// Decrypt the Buffer
|
||
//
|
||
|
||
rc4( &Rc4Key, sizeof(Password->Buffer)+sizeof(Password->Length), (LPBYTE) Password->Buffer );
|
||
|
||
//
|
||
// Check that the length is valid. If it isn't bail here.
|
||
//
|
||
|
||
if (Password->Length > JOIN_MAX_PASSWORD_LENGTH * sizeof(WCHAR)) {
|
||
return ERROR_INVALID_PASSWORD;
|
||
}
|
||
|
||
//
|
||
// Return the password to the caller.
|
||
//
|
||
|
||
*EncodedPassword = NetpMemoryAllocate( Password->Length + sizeof(WCHAR) + sizeof(WCHAR) );
|
||
|
||
if ( *EncodedPassword == NULL ) {
|
||
return ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
|
||
//
|
||
// Copy the password into the buffer
|
||
//
|
||
// If we are to encode the password, reserve
|
||
// the first character for the seed
|
||
//
|
||
|
||
if ( EncodePassword ) {
|
||
PasswordPart = ( *EncodedPassword ) + 1;
|
||
} else {
|
||
PasswordPart = ( *EncodedPassword );
|
||
}
|
||
|
||
RtlCopyMemory( PasswordPart,
|
||
((PCHAR) Password->Buffer) +
|
||
(JOIN_MAX_PASSWORD_LENGTH * sizeof(WCHAR)) -
|
||
Password->Length,
|
||
Password->Length );
|
||
|
||
PasswordPart[Password->Length/sizeof(WCHAR)] = L'\0';
|
||
|
||
RtlZeroMemory( ((PCHAR) Password->Buffer) +
|
||
(JOIN_MAX_PASSWORD_LENGTH * sizeof(WCHAR)) -
|
||
Password->Length,
|
||
Password->Length );
|
||
|
||
|
||
//
|
||
// Run Encode it so we can pass it around the process with impunity
|
||
//
|
||
|
||
if ( EncodePassword ) {
|
||
RtlInitUnicodeString( &EncodedPasswordU, PasswordPart );
|
||
|
||
RtlRunEncodeUnicodeString( &Seed, &EncodedPasswordU );
|
||
|
||
*( PWCHAR )( *EncodedPassword ) = ( WCHAR )Seed;
|
||
}
|
||
|
||
return NO_ERROR;
|
||
}
|
||
|
||
NET_API_STATUS
|
||
NET_API_FUNCTION
|
||
NetrJoinDomain2(
|
||
IN handle_t RpcBindingHandle,
|
||
IN LPWSTR lpServer OPTIONAL,
|
||
IN LPWSTR lpDomain,
|
||
IN LPWSTR lpMachineAccountOU,
|
||
IN LPWSTR lpAccount OPTIONAL,
|
||
IN PJOINPR_ENCRYPTED_USER_PASSWORD EncryptedPassword OPTIONAL,
|
||
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 name of the OU under which to create the machine account
|
||
lpAccount -- Account to use for join
|
||
EncryptedPassword - Encrypted password for lpAccount.
|
||
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;
|
||
LPWSTR EncodedPassword = NULL;
|
||
|
||
//
|
||
// Check the parameters we can
|
||
//
|
||
if (lpDomain == NULL ) {
|
||
|
||
NetStatus = ERROR_INVALID_PARAMETER;
|
||
|
||
}
|
||
|
||
//
|
||
// Decrypt the password.
|
||
//
|
||
|
||
if ( NetStatus == NERR_Success ) {
|
||
NetStatus = JoinpDecryptPasswordWithKey(
|
||
RpcBindingHandle,
|
||
EncryptedPassword,
|
||
TRUE, // encode the password
|
||
&EncodedPassword );
|
||
}
|
||
|
||
//
|
||
// Get the current machine name, so we are sure we always have it in flat format
|
||
//
|
||
if ( NetStatus == NERR_Success ) {
|
||
|
||
NetStatus = NetpGetComputerName( &ComputerName );
|
||
|
||
if ( NetStatus == NERR_Success ) {
|
||
|
||
lpServer = ComputerName;
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Do the impersonation
|
||
//
|
||
if ( NetStatus == NERR_Success ) {
|
||
|
||
NetStatus = WsImpersonateClient();
|
||
}
|
||
|
||
//
|
||
// Then, see about the join...
|
||
//
|
||
if ( NetStatus == NERR_Success ) {
|
||
|
||
NetStatus = NetpDoDomainJoin( lpServer, lpDomain, lpMachineAccountOU, lpAccount,
|
||
EncodedPassword, fOptions );
|
||
|
||
//
|
||
// Revert back to ourselves
|
||
//
|
||
WsRevertToSelf();
|
||
}
|
||
|
||
//
|
||
// Write event log stating that we successfully joined the domain/workgroup
|
||
//
|
||
if ( NetStatus == NERR_Success ) {
|
||
LPWSTR StringArray[2];
|
||
|
||
if ( fOptions & NETSETUP_JOIN_DOMAIN ) {
|
||
StringArray[0] = L"domain";
|
||
} else {
|
||
StringArray[0] = L"workgroup";
|
||
}
|
||
StringArray[1] = lpDomain;
|
||
|
||
WsLogEvent( NELOG_Joined_Domain,
|
||
EVENTLOG_INFORMATION_TYPE,
|
||
2,
|
||
StringArray,
|
||
NERR_Success );
|
||
}
|
||
|
||
//
|
||
// Free the memory for the machine name if we need to
|
||
//
|
||
if ( ComputerName != NULL ) {
|
||
|
||
NetApiBufferFree( ComputerName );
|
||
}
|
||
|
||
if ( EncodedPassword != NULL ) {
|
||
NetpMemoryFree( EncodedPassword );
|
||
}
|
||
|
||
return( NetStatus );
|
||
}
|
||
|
||
|
||
NET_API_STATUS
|
||
NET_API_FUNCTION
|
||
NetrUnjoinDomain2(
|
||
IN handle_t RpcBindingHandle,
|
||
IN LPWSTR lpServer OPTIONAL,
|
||
IN LPWSTR lpAccount OPTIONAL,
|
||
IN PJOINPR_ENCRYPTED_USER_PASSWORD EncryptedPassword OPTIONAL,
|
||
IN DWORD fJoinOptions
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Unjoins from the joined domain
|
||
|
||
Arguments:
|
||
|
||
lpServer -- Name of the machine being run on
|
||
lpAccount -- Account to use for unjoining
|
||
lpPassword -- Password matching the account. The password is encoded. The first WCHAR is
|
||
the seed
|
||
fOptions -- Options to use when unjoining the domain
|
||
|
||
Returns:
|
||
|
||
NERR_Success -- Name is valid
|
||
NERR_SetupNotJoined -- This machine was not joined to a domain
|
||
NERR_SetupDomainController -- This machine is a domain controller and
|
||
cannot be unjoined
|
||
NERR_InternalError -- Can't determine product type
|
||
|
||
--*/
|
||
{
|
||
NET_API_STATUS NetStatus = NERR_Success;
|
||
PPOLICY_PRIMARY_DOMAIN_INFO pPolicyPDI;
|
||
PPOLICY_DNS_DOMAIN_INFO pPolicyDNS;
|
||
NT_PRODUCT_TYPE ProductType;
|
||
LPWSTR EncodedPassword = NULL;
|
||
|
||
//
|
||
// Decrypt the password.
|
||
//
|
||
|
||
NetStatus = JoinpDecryptPasswordWithKey(
|
||
RpcBindingHandle,
|
||
EncryptedPassword,
|
||
TRUE, // encode the password
|
||
&EncodedPassword );
|
||
|
||
if ( NetStatus != NO_ERROR ) {
|
||
return NetStatus;
|
||
}
|
||
|
||
//
|
||
// Do the impersonation
|
||
//
|
||
NetStatus = WsImpersonateClient();
|
||
|
||
//
|
||
// First, get the primary domain info... We'll need it later
|
||
//
|
||
if ( NetStatus == NERR_Success ) {
|
||
|
||
NetStatus = NetpGetLsaPrimaryDomain( NULL,
|
||
NULL,
|
||
&pPolicyPDI,
|
||
&pPolicyDNS,
|
||
NULL );
|
||
|
||
if ( NetStatus == NERR_Success ) {
|
||
|
||
if ( !IS_CLIENT_JOINED(pPolicyPDI) ) {
|
||
|
||
NetStatus = NERR_SetupNotJoined;
|
||
|
||
} else {
|
||
|
||
//
|
||
// See if it's a DC...
|
||
//
|
||
if ( RtlGetNtProductType( &ProductType ) == FALSE ) {
|
||
|
||
NetStatus = NERR_InternalError;
|
||
|
||
} else {
|
||
|
||
if ( ProductType == NtProductLanManNt ) {
|
||
|
||
NetStatus = NERR_SetupDomainController;
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
//
|
||
// Ok, now if all that worked, we'll go ahead and do the removal
|
||
//
|
||
if ( NetStatus == NERR_Success ) {
|
||
|
||
NetStatus = NetpUnJoinDomain( pPolicyPDI, lpAccount, EncodedPassword,
|
||
fJoinOptions );
|
||
|
||
|
||
}
|
||
|
||
LsaFreeMemory( pPolicyPDI );
|
||
LsaFreeMemory( pPolicyDNS );
|
||
}
|
||
|
||
//
|
||
// Revert back to ourselves
|
||
//
|
||
WsRevertToSelf();
|
||
}
|
||
|
||
if ( EncodedPassword != NULL ) {
|
||
NetpMemoryFree( EncodedPassword );
|
||
}
|
||
|
||
return(NetStatus);
|
||
}
|
||
|
||
NET_API_STATUS
|
||
NET_API_FUNCTION
|
||
NetrValidateName2(
|
||
IN handle_t RpcBindingHandle,
|
||
IN LPWSTR lpMachine,
|
||
IN LPWSTR lpName,
|
||
IN LPWSTR lpAccount OPTIONAL,
|
||
IN PJOINPR_ENCRYPTED_USER_PASSWORD EncryptedPassword 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
|
||
lpAccount -- Account to use for name validation
|
||
lpPassword -- Password matching the account. The password is encoded. The first WCHAR is
|
||
the seed
|
||
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;
|
||
UNICODE_STRING EncodedPasswordU;
|
||
UCHAR Seed;
|
||
LPWSTR EncodedPassword = NULL;
|
||
|
||
//
|
||
// Decrypt the password.
|
||
//
|
||
|
||
NetStatus = JoinpDecryptPasswordWithKey(
|
||
RpcBindingHandle,
|
||
EncryptedPassword,
|
||
TRUE, // encode the password
|
||
&EncodedPassword );
|
||
|
||
if ( NetStatus != NO_ERROR ) {
|
||
return NetStatus;
|
||
}
|
||
|
||
if ( EncodedPassword ) {
|
||
|
||
Seed = *( PUCHAR )EncodedPassword;
|
||
RtlInitUnicodeString( &EncodedPasswordU, EncodedPassword + 1 );
|
||
|
||
} else {
|
||
|
||
RtlZeroMemory( &EncodedPasswordU, sizeof( UNICODE_STRING ) );
|
||
}
|
||
|
||
//
|
||
// Do the impersonation
|
||
//
|
||
NetStatus = WsImpersonateClient();
|
||
|
||
if ( NetStatus == NERR_Success ) {
|
||
|
||
|
||
RtlRunDecodeUnicodeString( Seed, &EncodedPasswordU );
|
||
NetStatus = NetpValidateName( lpMachine,
|
||
lpName,
|
||
lpAccount,
|
||
EncodedPasswordU.Buffer,
|
||
NameType );
|
||
RtlRunEncodeUnicodeString( &Seed, &EncodedPasswordU );
|
||
|
||
//
|
||
// Revert back to ourselves
|
||
//
|
||
WsRevertToSelf();
|
||
}
|
||
|
||
if ( EncodedPassword != NULL ) {
|
||
NetpMemoryFree( EncodedPassword );
|
||
}
|
||
|
||
return( NetStatus );
|
||
}
|
||
|
||
|
||
|
||
NET_API_STATUS
|
||
NET_API_FUNCTION
|
||
NetrGetJoinInformation(
|
||
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:
|
||
|
||
lpNameBuffer -- Where the domain/workgroup name is returned.
|
||
lpNameBufferSize -- Size of the passed in buffer, in WCHARs. If 0, the
|
||
workgroup/domain name is not returned.
|
||
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;
|
||
|
||
//
|
||
// Check the parameters
|
||
//
|
||
if ( lpNameBuffer == NULL ) {
|
||
|
||
return( ERROR_INVALID_PARAMETER );
|
||
|
||
}
|
||
|
||
//
|
||
// Do the impersonation
|
||
//
|
||
NetStatus = WsImpersonateClient();
|
||
|
||
if ( NetStatus == NERR_Success ) {
|
||
|
||
NetStatus = NetpGetJoinInformation( lpServer,
|
||
lpNameBuffer,
|
||
BufferType );
|
||
|
||
//
|
||
// Revert back to ourselves
|
||
//
|
||
WsRevertToSelf();
|
||
}
|
||
|
||
return( NetStatus );
|
||
}
|
||
|
||
NET_API_STATUS
|
||
NET_API_FUNCTION
|
||
NetrRenameMachineInDomain2(
|
||
IN handle_t RpcBindingHandle,
|
||
IN LPWSTR lpServer OPTIONAL,
|
||
IN LPWSTR lpNewMachineName OPTIONAL,
|
||
IN LPWSTR lpAccount OPTIONAL,
|
||
IN PJOINPR_ENCRYPTED_USER_PASSWORD EncryptedPassword OPTIONAL,
|
||
IN DWORD fRenameOptions
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Renames a machine currently joined to a domain.
|
||
|
||
Arguments:
|
||
|
||
lpServer -- Name of the machine being run on
|
||
|
||
lpNewMachineName -- New name for this machine. If the name is specified, it is used
|
||
for the new machine name. If it is not specified, it is assumed that SetComputerName
|
||
has already been invoked, and that name will be used.
|
||
|
||
lpAccount -- Account to use for the rename
|
||
|
||
lpPassword -- Password matching the account. The password has been encoded. The first
|
||
WCHAR of the string is the seed.
|
||
|
||
fOptions -- Options to use for the rename
|
||
|
||
Returns:
|
||
|
||
NERR_Success -- Success
|
||
|
||
ERROR_INVALID_PARAMETER -- A bad parameter was given
|
||
|
||
--*/
|
||
{
|
||
NET_API_STATUS NetStatus = NERR_Success;
|
||
PPOLICY_PRIMARY_DOMAIN_INFO pPolicyPDI;
|
||
PPOLICY_DNS_DOMAIN_INFO pPolicyDNS;
|
||
LPTSTR ComputerName = NULL;
|
||
LPTSTR NewComputerName = NULL;
|
||
LPTSTR DomainName = NULL;
|
||
HKEY ComputerNameRootKey;
|
||
ULONG Length;
|
||
LPWSTR EncodedPassword = NULL;
|
||
|
||
//
|
||
// Get the current machine name
|
||
//
|
||
NetStatus = NetpGetComputerName( &ComputerName );
|
||
|
||
if ( NetStatus == NERR_Success ) {
|
||
|
||
lpServer = ComputerName;
|
||
}
|
||
|
||
//
|
||
// Decrypt the password.
|
||
//
|
||
|
||
if ( NetStatus == NERR_Success ) {
|
||
NetStatus = JoinpDecryptPasswordWithKey(
|
||
RpcBindingHandle,
|
||
EncryptedPassword,
|
||
TRUE, // encode the password
|
||
&EncodedPassword );
|
||
}
|
||
|
||
|
||
//
|
||
// Get the new machine name if it isn't specified
|
||
//
|
||
if ( NetStatus == NERR_Success && lpNewMachineName == NULL ) {
|
||
|
||
NetStatus = NetpGetNewMachineName( &NewComputerName );
|
||
lpNewMachineName = NewComputerName;
|
||
}
|
||
|
||
//
|
||
// Get the current domain information
|
||
//
|
||
if ( NetStatus == NERR_Success ) {
|
||
|
||
NetStatus = NetpGetLsaPrimaryDomain( NULL,
|
||
NULL,
|
||
&pPolicyPDI,
|
||
&pPolicyDNS,
|
||
NULL );
|
||
|
||
if ( NetStatus == NERR_Success ) {
|
||
|
||
NetStatus = NetApiBufferAllocate( pPolicyPDI->Name.Length + sizeof( WCHAR ),
|
||
( LPVOID * )&DomainName );
|
||
|
||
if ( NetStatus == NERR_Success ) {
|
||
|
||
RtlCopyMemory( DomainName, pPolicyPDI->Name.Buffer, pPolicyPDI->Name.Length );
|
||
DomainName[ pPolicyPDI->Name.Length / sizeof( WCHAR ) ] = UNICODE_NULL;
|
||
}
|
||
|
||
LsaFreeMemory( pPolicyPDI );
|
||
LsaFreeMemory( pPolicyDNS );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Do the impersonation
|
||
//
|
||
|
||
if ( NetStatus == NERR_Success ) {
|
||
|
||
NetStatus = WsImpersonateClient();
|
||
|
||
if ( NetStatus == NERR_Success ) {
|
||
|
||
//
|
||
// A machine rename
|
||
//
|
||
NetStatus = NetpMachineValidToJoin( lpNewMachineName, TRUE );
|
||
|
||
if ( NetStatus == NERR_SetupAlreadyJoined ||
|
||
NetStatus == NERR_SetupDomainController ) { // Allow DC rename
|
||
|
||
NetStatus = NetpChangeMachineName( lpServer,
|
||
lpNewMachineName,
|
||
DomainName,
|
||
lpAccount,
|
||
EncodedPassword,
|
||
fRenameOptions );
|
||
|
||
if ( NetStatus == NERR_Success && lpNewMachineName ) {
|
||
|
||
if ( SetComputerNameEx( ComputerNamePhysicalDnsHostname,
|
||
lpNewMachineName ) == FALSE ) {
|
||
|
||
NetStatus = GetLastError();
|
||
}
|
||
}
|
||
|
||
} else if ( NetStatus == NERR_Success ) {
|
||
|
||
NetStatus = NERR_SetupNotJoined;
|
||
}
|
||
//
|
||
// Revert back to ourselves
|
||
//
|
||
WsRevertToSelf();
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Free the memory for the machine name(s) if we need to
|
||
//
|
||
if ( ComputerName != NULL ) {
|
||
|
||
NetApiBufferFree( ComputerName );
|
||
}
|
||
|
||
if ( NewComputerName != NULL ) {
|
||
|
||
NetApiBufferFree( NewComputerName );
|
||
}
|
||
|
||
if ( DomainName != NULL ) {
|
||
|
||
NetApiBufferFree( DomainName );
|
||
}
|
||
|
||
if ( EncodedPassword != NULL ) {
|
||
NetpMemoryFree( EncodedPassword );
|
||
}
|
||
|
||
return( NetStatus );
|
||
}
|
||
|
||
NET_API_STATUS
|
||
NET_API_FUNCTION
|
||
NetrGetJoinableOUs2(
|
||
IN handle_t RpcBindingHandle,
|
||
IN LPWSTR lpServer OPTIONAL,
|
||
IN LPWSTR lpDomain,
|
||
IN LPWSTR lpAccount OPTIONAL,
|
||
IN PJOINPR_ENCRYPTED_USER_PASSWORD EncryptedPassword OPTIONAL,
|
||
OUT DWORD *OUCount,
|
||
OUT LPWSTR **OUs
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Renames a machine currently joined to a domain.
|
||
|
||
Arguments:
|
||
|
||
lpServer -- Name of the machine being run on
|
||
lpDomain -- Domain to join
|
||
lpAccount -- Account to use for join
|
||
lpPassword -- Password matching the account. The password has been encoded and the first
|
||
WCHAR of the name is the seed
|
||
MachineAccountOUs -- Where the information is returned.
|
||
|
||
Returns:
|
||
|
||
NERR_Success -- Success
|
||
|
||
ERROR_INVALID_PARAMETER -- A bad parameter was given
|
||
|
||
--*/
|
||
{
|
||
NET_API_STATUS NetStatus = NERR_Success;
|
||
LPWSTR EncodedPassword = NULL;
|
||
|
||
NetStatus = WsImpersonateClient();
|
||
|
||
//
|
||
// Decrypt the password.
|
||
//
|
||
|
||
if ( NetStatus == NERR_Success ) {
|
||
NetStatus = JoinpDecryptPasswordWithKey(
|
||
RpcBindingHandle,
|
||
EncryptedPassword,
|
||
TRUE, // encode the password
|
||
&EncodedPassword );
|
||
}
|
||
|
||
if ( NetStatus == NERR_Success ) {
|
||
|
||
//
|
||
// Read the current information
|
||
//
|
||
NetStatus = NetpGetListOfJoinableOUs( lpDomain,
|
||
lpAccount,
|
||
EncodedPassword,
|
||
OUCount,
|
||
OUs );
|
||
}
|
||
|
||
//
|
||
// Revert back to ourselves
|
||
//
|
||
WsRevertToSelf();
|
||
|
||
if ( EncodedPassword != NULL ) {
|
||
NetpMemoryFree( EncodedPassword );
|
||
}
|
||
|
||
return( NetStatus );
|
||
}
|
||
|
||
|
||
NET_API_STATUS
|
||
NET_API_FUNCTION
|
||
NetrJoinDomain(
|
||
IN LPWSTR lpServer OPTIONAL,
|
||
IN LPWSTR lpDomain,
|
||
IN LPWSTR lpMachineAccountOU,
|
||
IN LPWSTR lpAccount OPTIONAL,
|
||
IN LPWSTR lpPassword OPTIONAL,
|
||
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 name of the OU under which to create the machine account
|
||
lpAccount -- Account to use for join
|
||
lpPassword -- Password matching the account. The password is encoded. The first WCHAR
|
||
is the seed.
|
||
fOptions -- Options to use when joining the domain
|
||
|
||
Returns:
|
||
|
||
NERR_Success -- Success
|
||
|
||
ERROR_INVALID_PARAMETER -- A bad parameter was given
|
||
|
||
--*/
|
||
{
|
||
|
||
//
|
||
// This version that takes a clear text password isn't supported.
|
||
//
|
||
|
||
return ERROR_NOT_SUPPORTED;
|
||
}
|
||
|
||
|
||
|
||
NET_API_STATUS
|
||
NET_API_FUNCTION
|
||
NetrUnjoinDomain(
|
||
IN LPWSTR lpServer OPTIONAL,
|
||
IN LPWSTR lpAccount OPTIONAL,
|
||
IN LPWSTR lpPassword OPTIONAL,
|
||
IN DWORD fJoinOptions
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Unjoins from the joined domain
|
||
|
||
Arguments:
|
||
|
||
lpServer -- Name of the machine being run on
|
||
lpAccount -- Account to use for unjoining
|
||
lpPassword -- Password matching the account. The password is encoded. The first WCHAR is
|
||
the seed
|
||
fOptions -- Options to use when unjoining the domain
|
||
|
||
Returns:
|
||
|
||
NERR_Success -- Name is valid
|
||
NERR_SetupNotJoined -- This machine was not joined to a domain
|
||
NERR_SetupDomainController -- This machine is a domain controller and
|
||
cannot be unjoined
|
||
NERR_InternalError -- Can't determine product type
|
||
|
||
--*/
|
||
{
|
||
|
||
//
|
||
// This version that takes a clear text password isn't supported.
|
||
//
|
||
|
||
return ERROR_NOT_SUPPORTED;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
NET_API_STATUS
|
||
NET_API_FUNCTION
|
||
NetrValidateName(
|
||
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
|
||
lpAccount -- Account to use for name validation
|
||
lpPassword -- Password matching the account. The password is encoded. The first WCHAR is
|
||
the seed
|
||
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
|
||
|
||
--*/
|
||
{
|
||
|
||
//
|
||
// This version that takes a clear text password isn't supported.
|
||
//
|
||
|
||
return ERROR_NOT_SUPPORTED;
|
||
}
|
||
|
||
|
||
NET_API_STATUS
|
||
NET_API_FUNCTION
|
||
NetrRenameMachineInDomain(
|
||
IN LPWSTR lpServer OPTIONAL,
|
||
IN LPWSTR lpNewMachineName OPTIONAL,
|
||
IN LPWSTR lpAccount OPTIONAL,
|
||
IN LPWSTR lpPassword OPTIONAL,
|
||
IN DWORD fRenameOptions
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Renames a machine currently joined to a domain.
|
||
|
||
Arguments:
|
||
|
||
lpServer -- Name of the machine being run on
|
||
|
||
lpNewMachineName -- New name for this machine. If the name is specified, it is used
|
||
for the new machine name. If it is not specified, it is assumed that SetComputerName
|
||
has already been invoked, and that name will be used.
|
||
|
||
lpAccount -- Account to use for the rename
|
||
|
||
lpPassword -- Password matching the account. The password has been encoded. The first
|
||
WCHAR of the string is the seed.
|
||
|
||
fOptions -- Options to use for the rename
|
||
|
||
Returns:
|
||
|
||
NERR_Success -- Success
|
||
|
||
ERROR_INVALID_PARAMETER -- A bad parameter was given
|
||
|
||
--*/
|
||
{
|
||
|
||
//
|
||
// This version that takes a clear text password isn't supported.
|
||
//
|
||
|
||
return ERROR_NOT_SUPPORTED;
|
||
}
|
||
|
||
NET_API_STATUS
|
||
NET_API_FUNCTION
|
||
NetrGetJoinableOUs(
|
||
IN LPWSTR lpServer OPTIONAL,
|
||
IN LPWSTR lpDomain,
|
||
IN LPWSTR lpAccount OPTIONAL,
|
||
IN LPWSTR lpPassword OPTIONAL,
|
||
OUT DWORD *OUCount,
|
||
OUT LPWSTR **OUs
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Renames a machine currently joined to a domain.
|
||
|
||
Arguments:
|
||
|
||
lpServer -- Name of the machine being run on
|
||
lpDomain -- Domain to join
|
||
lpAccount -- Account to use for join
|
||
lpPassword -- Password matching the account. The password has been encoded and the first
|
||
WCHAR of the name is the seed
|
||
MachineAccountOUs -- Where the information is returned.
|
||
|
||
Returns:
|
||
|
||
NERR_Success -- Success
|
||
|
||
ERROR_INVALID_PARAMETER -- A bad parameter was given
|
||
|
||
--*/
|
||
{
|
||
|
||
//
|
||
// This version that takes a clear text password isn't supported.
|
||
//
|
||
|
||
return ERROR_NOT_SUPPORTED;
|
||
}
|
||
|
||
|
||
//
|
||
// Computer rename preparation APIs
|
||
//
|
||
|
||
NET_API_STATUS
|
||
NetpSetPrimarySamAccountName(
|
||
IN LPWSTR DomainController,
|
||
IN LPWSTR CurrentSamAccountName,
|
||
IN LPWSTR NewSamAccountName,
|
||
IN LPWSTR DomainAccountName,
|
||
IN LPWSTR DomainAccountPassword
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Sets primary SAM account name and teh display name on the
|
||
computer object in the DS.
|
||
|
||
Arguments:
|
||
|
||
DomainController -- DC name where to modify the computer object.
|
||
|
||
CurrentSamAccountName -- The current value of SAM account name.
|
||
|
||
NewSamAccountName -- The new value of SAM account name to be set.
|
||
|
||
DomainAccount -- Domain account to use for accessing the machine
|
||
account object in the DS. May be NULL in which case the
|
||
credentials of the user executing this routine are used.
|
||
|
||
DomainAccountPassword -- Password matching the domain account.
|
||
May be NULL in which case the credentials of the user executing
|
||
this routine are used.
|
||
|
||
Note:
|
||
|
||
This routine uses NetUserSetInfo, downlevel SAM based API.
|
||
NetUserSetInfo has an advantage such that it updates the DN of the
|
||
computer object to correspond to the new SAM account name. Also,
|
||
for a DC's computer object, it follows the serverReferenceBL attribute
|
||
link and updates the DN of the server object in the Config container.
|
||
The server object in the Config container, in its turn, has a reference
|
||
to the computer object (serverReference attrib) -- that reference also
|
||
gets updated as the result of NetUserSetInfo call. Updating the two
|
||
objects through ldap (instead of NetuserSetInfo) currently can't be done
|
||
as one transaction, so we use NetUserSetInfo to do all this for us. We
|
||
may reconsider the use of ldap once transactional ldap (i.e. several
|
||
ldap operations performed as one transaction) becomes available.
|
||
|
||
Returns:
|
||
|
||
NO_ERROR -- Success
|
||
|
||
Otherwise, error returned by NetUserSetInfo.
|
||
|
||
--*/
|
||
{
|
||
NET_API_STATUS NetStatus = NO_ERROR;
|
||
NET_API_STATUS TmpNetStatus = NO_ERROR;
|
||
USER_INFO_0 NetUI0;
|
||
PUSER_INFO_10 usri10 = NULL;
|
||
BOOLEAN Connected = FALSE;
|
||
|
||
//
|
||
// Connect to the DC
|
||
//
|
||
|
||
NetStatus = NetpManageIPCConnect( DomainController,
|
||
DomainAccountName,
|
||
DomainAccountPassword,
|
||
NETSETUPP_CONNECT_IPC );
|
||
|
||
if ( NetStatus != NO_ERROR ) {
|
||
NetpLog(( "NetpSetPrimarySamAccountName: NetpManageIPCConnect failed to connect to %ws: 0x%lx\n",
|
||
DomainController,
|
||
NetStatus ));
|
||
goto Cleanup;
|
||
}
|
||
|
||
Connected = TRUE;
|
||
|
||
//
|
||
// Set the SAM account name
|
||
//
|
||
|
||
NetUI0.usri0_name = NewSamAccountName;
|
||
NetStatus = NetUserSetInfo( DomainController,
|
||
CurrentSamAccountName,
|
||
0,
|
||
(PBYTE)&NetUI0,
|
||
NULL );
|
||
|
||
if ( NetStatus != NO_ERROR ) {
|
||
NetpLog(( "NetpSetPrimarySamAccountName: NetUserSetInfo failed on %ws: 0x%lx\n",
|
||
DomainController,
|
||
NetStatus ));
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Update the display name as well.
|
||
// Ignore error as this is not critical.
|
||
//
|
||
// First get the current display name
|
||
//
|
||
|
||
TmpNetStatus = NetUserGetInfo( DomainController,
|
||
NewSamAccountName,
|
||
10,
|
||
(PBYTE *)&usri10 );
|
||
|
||
if ( TmpNetStatus != NERR_Success ) {
|
||
NetpLog(( "NetpSetPrimarySamAccountName: failed to get display name on %ws (ignored) 0x%lx\n",
|
||
DomainController,
|
||
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, NewSamAccountName) != 0 ) {
|
||
|
||
USER_INFO_1011 usri1011;
|
||
|
||
usri1011.usri1011_full_name = NewSamAccountName; // new name
|
||
TmpNetStatus = NetUserSetInfo( DomainController,
|
||
NewSamAccountName,
|
||
1011,
|
||
(PBYTE)&usri1011,
|
||
NULL );
|
||
|
||
if ( TmpNetStatus != NERR_Success ) {
|
||
NetpLog(( "NetpSetPrimarySamAccountName: failed to update display name on %ws (ignored) 0x%lx\n",
|
||
DomainController,
|
||
TmpNetStatus ));
|
||
}
|
||
}
|
||
|
||
Cleanup:
|
||
|
||
if ( usri10 != NULL ) {
|
||
NetApiBufferFree( usri10 );
|
||
}
|
||
|
||
if ( Connected ) {
|
||
TmpNetStatus = NetpManageIPCConnect( DomainController,
|
||
DomainAccountName,
|
||
DomainAccountPassword,
|
||
NETSETUPP_DISCONNECT_IPC );
|
||
if ( TmpNetStatus != NO_ERROR ) {
|
||
NetpLog(( "NetpSetPrimarySamAccountName: NetpManageIPCConnect failed to disconnect from %ws: 0x%lx\n",
|
||
DomainController,
|
||
TmpNetStatus ));
|
||
}
|
||
}
|
||
|
||
return NetStatus;
|
||
}
|
||
|
||
#define NET_ADD_ALTERNATE_COMPUTER_NAME 1
|
||
#define NET_DEL_ALTERNATE_COMPUTER_NAME 2
|
||
#define NET_SET_PRIMARY_COMPUTER_NAME 3
|
||
|
||
NET_API_STATUS
|
||
NET_API_FUNCTION
|
||
NetpManageAltComputerName(
|
||
IN handle_t RpcBindingHandle,
|
||
IN LPWSTR AlternateName,
|
||
IN ULONG Action,
|
||
IN LPWSTR DomainAccount OPTIONAL,
|
||
IN PJOINPR_ENCRYPTED_USER_PASSWORD EncryptedPassword OPTIONAL,
|
||
IN ULONG Reserved
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Manages the alternate names for the specified server.
|
||
|
||
Arguments:
|
||
|
||
AlternateName -- The name to add.
|
||
|
||
Action -- Specifies action to take on the name:
|
||
|
||
NET_ADD_ALTERNATE_COMPUTER_NAME - Add the alternate name.
|
||
NET_DEL_ALTERNATE_COMPUTER_NAME - Delete the alternate name.
|
||
NET_SET_PRIMARY_COMPUTER_NAME - Set the alternate name as
|
||
the primary computer name.
|
||
|
||
DomainAccount -- Domain account to use for accessing the
|
||
machine account object for the specified server in the AD.
|
||
Not used if the server is not joined to a domain. May be
|
||
NULL in which case the credentials of the user executing
|
||
this routine are used.
|
||
|
||
DomainAccountPassword -- Password matching the domain account.
|
||
Not used if the server is not joined to a domain. May be
|
||
NULL in which case the credentials of the user executing
|
||
this routine are used.
|
||
|
||
Reserved -- Reserved for future use. If some flags are specified
|
||
that are not supported, they will be ignored if
|
||
NET_IGNORE_UNSUPPORTED_FLAGS is set, otherwise this routine
|
||
will fail with ERROR_INVALID_FLAGS.
|
||
|
||
Note:
|
||
|
||
The process that calls this routine must have administrator
|
||
privileges on the local computer to perform the local computer
|
||
name modifications. The access check is performed by the local
|
||
information APIs.
|
||
|
||
Returns:
|
||
|
||
NO_ERROR -- Success
|
||
|
||
ERROR_NOT_SUPPORTED -- The specified server does not support this
|
||
functionality.
|
||
|
||
ERROR_INVALID_FLAGS - The Flags parameter is incorrect.
|
||
|
||
--*/
|
||
{
|
||
NET_API_STATUS NetStatus = NO_ERROR;
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
ULONG LdapStatus = LDAP_SUCCESS;
|
||
NT_PRODUCT_TYPE NtProductType;
|
||
LSA_HANDLE LocalPolicyHandle = NULL;
|
||
PPOLICY_DNS_DOMAIN_INFO LocalPolicyDns = NULL;
|
||
|
||
|
||
ULONG PrimaryNameSize = DNS_MAX_NAME_BUFFER_LENGTH * sizeof(WCHAR);
|
||
WCHAR NewNetbiosMachineName[MAX_COMPUTERNAME_LENGTH + 1];
|
||
LPWSTR ComputerName = NULL;
|
||
LPWSTR MachineAccountName = NULL;
|
||
LPWSTR NewMachineAccountName = NULL;
|
||
LPWSTR AccountUserName = NULL;
|
||
LPWSTR AccountDomainName = NULL;
|
||
LPWSTR DomainAccountPassword = NULL;
|
||
LPWSTR MachineAccountNameToCrack = NULL; // not allocated
|
||
LPWSTR NameToCrack = NULL;
|
||
LPWSTR PrimaryName = NULL;
|
||
BOOLEAN ClientImpersonated = FALSE;
|
||
BOOLEAN NameModifiedLocally = FALSE;
|
||
BOOLEAN PrimarySamAccountNameSet = FALSE;
|
||
BOOLEAN ToldNetlogonToAvoidDnsHostNameUpdate = FALSE;
|
||
BOOLEAN StopedNetlogon = FALSE;
|
||
|
||
SERVICE_STATUS NetlogonServiceStatus;
|
||
LPQUERY_SERVICE_CONFIG NetlogonServiceConfig = NULL;
|
||
|
||
|
||
RPC_AUTH_IDENTITY_HANDLE AuthId = 0;
|
||
HANDLE hDs = NULL;
|
||
PLDAP LdapHandle = NULL;
|
||
LONG LdapOption;
|
||
OBJECT_ATTRIBUTES OA;
|
||
PDS_NAME_RESULTW CrackedName = NULL;
|
||
|
||
PDOMAIN_CONTROLLER_INFOW DcInfo = NULL;
|
||
PWSTR AlternateNameValues[2];
|
||
PWSTR DnsHostNameValues[2];
|
||
PWSTR PrimaryNameValues[2];
|
||
LDAPModW DnsHostNameMod;
|
||
LDAPModW PrimaryNameMod;
|
||
LDAPModW AlternateDnsHostNameMod;
|
||
LDAPModW *ModList[4] = {NULL};
|
||
ULONG ModCount = 0;
|
||
|
||
SEC_WINNT_AUTH_IDENTITY AuthIdent = {0};
|
||
|
||
//
|
||
// Ldap modify server control
|
||
//
|
||
// An LDAP modify request will normally fail if it attempts
|
||
// to add an attribute that already exists, or if it attempts
|
||
// to delete an attribute that does not exist. With this control,
|
||
// as long as the attribute to be added has the same value as
|
||
// the existing attribute, then the modify will succeed. With
|
||
// this control, deletion of an attribute that doesn't exist
|
||
// will also succeed.
|
||
//
|
||
|
||
LDAPControlW ModifyControl =
|
||
{
|
||
LDAP_SERVER_PERMISSIVE_MODIFY_OID_W,
|
||
{
|
||
0, NULL
|
||
},
|
||
FALSE
|
||
};
|
||
|
||
PLDAPControlW ModifyControlArray[2] =
|
||
{
|
||
&ModifyControl,
|
||
NULL
|
||
};
|
||
|
||
//
|
||
// Initialize the log file
|
||
//
|
||
|
||
NetSetuppOpenLog();
|
||
NetpLog(( "NetpManageAltComputerName called:\n" ));
|
||
NetpLog(( " AlternateName = %ws\n", AlternateName ));
|
||
NetpLog(( " DomainAccount = %ws\n", DomainAccount ));
|
||
NetpLog(( " Action = 0x%lx\n", Action ));
|
||
NetpLog(( " Flags = 0x%lx\n", Reserved ));
|
||
|
||
//
|
||
// This API is supported on DCs and servers only
|
||
//
|
||
|
||
if ( !RtlGetNtProductType( &NtProductType ) ) {
|
||
NtProductType = NtProductWinNt;
|
||
}
|
||
|
||
if ( NtProductType != NtProductServer &&
|
||
NtProductType != NtProductLanManNt ) {
|
||
|
||
NetpLog(( "NetpManageAltComputerName: Not supported on wksta: %lu\n",
|
||
NtProductType ));
|
||
NetStatus = ERROR_NOT_SUPPORTED;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Validate the Flags
|
||
//
|
||
// If some flags are passed which we don't understand
|
||
// and we are not told to ignore them, error out.
|
||
//
|
||
|
||
if ( Reserved != 0 &&
|
||
(Reserved & NET_IGNORE_UNSUPPORTED_FLAGS) == 0 ) {
|
||
NetpLog(( "NetpManageAltComputerName: Invalid Flags passed\n" ));
|
||
NetStatus = ERROR_INVALID_FLAGS;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Validate the alternate name passed
|
||
//
|
||
|
||
NetStatus = DnsValidateName_W( AlternateName, DnsNameHostnameFull );
|
||
|
||
if ( NetStatus != NO_ERROR && NetStatus != DNS_ERROR_NON_RFC_NAME ) {
|
||
NetpLog(( "NetpManageAltComputerName: DnsValidateName failed: 0x%lx\n",
|
||
NetStatus ));
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Decrypt the domain account password
|
||
//
|
||
|
||
NetStatus = JoinpDecryptPasswordWithKey(
|
||
RpcBindingHandle,
|
||
EncryptedPassword,
|
||
FALSE, // don't encode the password
|
||
&DomainAccountPassword );
|
||
|
||
if ( NetStatus != NO_ERROR ) {
|
||
NetpLog(( "NetpManageAltComputerName: JoinpDecryptPasswordWithKey failed: 0x%lx\n",
|
||
NetStatus ));
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// If there is no domain account passed,
|
||
// ignore the domain account password (if any)
|
||
//
|
||
|
||
if ( DomainAccount == NULL &&
|
||
DomainAccountPassword != NULL ) {
|
||
|
||
NetpMemoryFree( DomainAccountPassword );
|
||
DomainAccountPassword = NULL;
|
||
}
|
||
|
||
//
|
||
// Separate the domain account into
|
||
// the user and domain parts for later use
|
||
//
|
||
|
||
if ( DomainAccount != NULL ) {
|
||
NetStatus = NetpSeparateUserAndDomain( DomainAccount,
|
||
&AccountUserName,
|
||
&AccountDomainName );
|
||
}
|
||
|
||
if ( NetStatus != NERR_Success ) {
|
||
NetpLog(( "NetpGetComputerObjectDn: Cannot NetpSeparateUserAndDomain 0x%lx\n",
|
||
NetStatus ));
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Get the current Netbios machine name
|
||
//
|
||
|
||
NetStatus = NetpGetComputerName( &ComputerName );
|
||
|
||
if ( NetStatus != NO_ERROR ) {
|
||
NetpLog(( "NetpManageAltComputerName: NetpGetComputerName failed: 0x%lx\n",
|
||
NetStatus ));
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Get SAM machine account name from the Netbios machine name
|
||
//
|
||
|
||
NetStatus = NetpGetMachineAccountName( ComputerName, &MachineAccountName );
|
||
if ( NetStatus != NO_ERROR ) {
|
||
NetpLog(( "NetpManageAltComputerName: NetpGetMachineAccountName failed: 0x%lx\n",
|
||
NetStatus ));
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Get the current primary DNS host name
|
||
//
|
||
|
||
PrimaryName = LocalAlloc( LMEM_ZEROINIT, PrimaryNameSize );
|
||
|
||
if ( PrimaryName == NULL ) {
|
||
NetpLog(( "NetpManageAltComputerName: LocalAlloc for PrimaryName failed\n" ));
|
||
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
|
||
goto Cleanup;
|
||
}
|
||
|
||
NetStatus = EnumerateLocalComputerNamesW(
|
||
PrimaryComputerName, // type of name
|
||
0, // reserved
|
||
PrimaryName,
|
||
&PrimaryNameSize );
|
||
|
||
if ( NetStatus != NO_ERROR ) {
|
||
NetpLog(( "NetpManageAltComputerName: EnumerateLocalComputerNamesW failed with Size 0x%lx: 0x%lx\n",
|
||
PrimaryNameSize,
|
||
NetStatus ));
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// If we are to rename the machine,
|
||
// get the new machine account name from
|
||
// the DNS name passed
|
||
//
|
||
|
||
if ( Action == NET_SET_PRIMARY_COMPUTER_NAME ) {
|
||
ULONG Size = MAX_COMPUTERNAME_LENGTH + 1;
|
||
|
||
if ( !DnsHostnameToComputerNameW(AlternateName,
|
||
NewNetbiosMachineName,
|
||
&Size) ) {
|
||
NetStatus = GetLastError();
|
||
NetpLog(( "NetpManageAltComputerName: DnsHostnameToComputerNameW failed: 0x%lx\n",
|
||
NetStatus ));
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Get the new SAM machine account name from the new Netbios machine name
|
||
//
|
||
NetStatus = NetpGetMachineAccountName( NewNetbiosMachineName, &NewMachineAccountName );
|
||
if ( NetStatus != NO_ERROR ) {
|
||
NetpLog(( "NetpManageAltComputerName: NetpGetMachineAccountName (2) failed: 0x%lx\n",
|
||
NetStatus ));
|
||
goto Cleanup;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Open the local LSA policy
|
||
//
|
||
|
||
InitializeObjectAttributes( &OA, NULL, 0, NULL, NULL );
|
||
|
||
Status = LsaOpenPolicy( NULL,
|
||
&OA,
|
||
MAXIMUM_ALLOWED,
|
||
&LocalPolicyHandle );
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
NetpLog(( "NetpManageAltComputerName: LsaOpenPolicy failed: 0x%lx\n",
|
||
Status ));
|
||
NetStatus = NetpNtStatusToApiStatus( Status );
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Get the current domain information from LSA
|
||
//
|
||
|
||
Status = LsaQueryInformationPolicy( LocalPolicyHandle,
|
||
PolicyDnsDomainInformation,
|
||
(PVOID *) &LocalPolicyDns );
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
NetpLog(( "NetpManageAltComputerName: LsaQueryInformationPolicy failed: 0x%lx\n",
|
||
Status ));
|
||
NetStatus = NetpNtStatusToApiStatus( Status );
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Do the local opperation for the specified alternate name
|
||
//
|
||
// Impersonate the caller. The local API will perform the
|
||
// access check on the caller on our behalf.
|
||
//
|
||
|
||
NetStatus = WsImpersonateClient();
|
||
|
||
if ( NetStatus != NO_ERROR ) {
|
||
NetpLog(( "NetpManageAltComputerName: WsImpersonateClient failed: 0x%lx\n",
|
||
NetStatus ));
|
||
goto Cleanup;
|
||
}
|
||
|
||
ClientImpersonated = TRUE;
|
||
|
||
//
|
||
// Do local operations
|
||
//
|
||
|
||
if ( Action == NET_ADD_ALTERNATE_COMPUTER_NAME ) {
|
||
|
||
NetStatus = AddLocalAlternateComputerName( AlternateName,
|
||
0 ); // reserved
|
||
|
||
if ( NetStatus != NO_ERROR ) {
|
||
NetpLog(( "NetpManageAltComputerName: AddLocalAlternateComputerName failed 0x%lx\n",
|
||
NetStatus ));
|
||
goto Cleanup;
|
||
}
|
||
|
||
} else if ( Action == NET_DEL_ALTERNATE_COMPUTER_NAME ) {
|
||
|
||
NetStatus = RemoveLocalAlternateComputerName( AlternateName,
|
||
0 ); // reserved
|
||
|
||
if ( NetStatus != NO_ERROR ) {
|
||
NetpLog(( "NetpManageAltComputerName: RemoveLocalAlternateComputerName failed 0x%lx\n",
|
||
NetStatus ));
|
||
goto Cleanup;
|
||
}
|
||
|
||
} else if ( Action == NET_SET_PRIMARY_COMPUTER_NAME ) {
|
||
|
||
NetStatus = SetLocalPrimaryComputerName( AlternateName,
|
||
0 ); // reserved
|
||
|
||
if ( NetStatus != NO_ERROR ) {
|
||
NetpLog(( "NetpManageAltComputerName: SetLocalPrimaryComputerName failed 0x%lx\n",
|
||
NetStatus ));
|
||
goto Cleanup;
|
||
}
|
||
}
|
||
|
||
NameModifiedLocally = TRUE;
|
||
|
||
//
|
||
// We are done with local operations; we are going
|
||
// to do remote operations on the DC next.
|
||
//
|
||
// If the user credentials are supplied, revert
|
||
// the impersonation -- we will access the remote
|
||
// server with explicit credentials supplied.
|
||
// Otherwise, access the DC while running in the
|
||
// user context.
|
||
//
|
||
|
||
if ( DomainAccount != NULL ) {
|
||
WsRevertToSelf();
|
||
ClientImpersonated = FALSE;
|
||
}
|
||
|
||
//
|
||
// If this machine is not joined to an AD domain,
|
||
// there is nothing to do on the DC.
|
||
//
|
||
|
||
if ( LocalPolicyDns->Sid == NULL ||
|
||
LocalPolicyDns->DnsDomainName.Length == 0 ) {
|
||
|
||
NetStatus = NO_ERROR;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Discover a DC for the domain of this machine
|
||
// to modify the computer object in the DS.
|
||
//
|
||
|
||
NetStatus = DsGetDcNameWithAccountW(
|
||
NULL,
|
||
MachineAccountName,
|
||
UF_WORKSTATION_TRUST_ACCOUNT | UF_SERVER_TRUST_ACCOUNT,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
DS_DIRECTORY_SERVICE_REQUIRED | DS_RETURN_DNS_NAME,
|
||
&DcInfo );
|
||
|
||
if ( NetStatus != NO_ERROR ) {
|
||
NetpLog(( "NetpManageAltComputerName: DsGetDcNameWithAccountW failed 0x%lx\n",
|
||
NetStatus ));
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// If this machine is a DC, verify that we got it.
|
||
// We do this because we want to avoid inconsistent
|
||
// state where the local name stored in registry
|
||
// is different from name stored locally in the DS.
|
||
//
|
||
|
||
if ( NtProductType == NtProductLanManNt &&
|
||
!DnsNameCompare_W(PrimaryName, DcInfo->DomainControllerName+2) ) {
|
||
|
||
NetpLog(( "NetpManageAltComputerName: Got different DC '%ws' than local DC '%ws'\n",
|
||
DcInfo->DomainControllerName+2,
|
||
PrimaryName ));
|
||
NetStatus = ERROR_NO_SUCH_DOMAIN;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// We've got a DC. Bind to the DS to get the DN
|
||
// for our machine account and do a LDAP connect
|
||
// to modify our machine account given the DN
|
||
//
|
||
|
||
NetStatus = DsMakePasswordCredentials( AccountUserName,
|
||
AccountDomainName,
|
||
DomainAccountPassword,
|
||
&AuthId );
|
||
if ( NetStatus != NERR_Success ) {
|
||
NetpLog(( "NetpManageAltComputerName: DsMakePasswordCredentials failed 0x%lx\n",
|
||
NetStatus ));
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Bind to the DS on the DC.
|
||
//
|
||
|
||
NetStatus = DsBindWithCredW( DcInfo->DomainControllerName,
|
||
NULL,
|
||
AuthId,
|
||
&hDs );
|
||
|
||
if ( NetStatus != NO_ERROR ) {
|
||
NetpLog(( "NetpManageAltComputerName: DsBindWithCredW failed to '%ws': 0x%lx\n",
|
||
DcInfo->DomainControllerName,
|
||
NetStatus ));
|
||
goto Cleanup ;
|
||
}
|
||
|
||
//
|
||
// Open an LDAP connection to the DC and set useful options
|
||
//
|
||
|
||
LdapHandle = ldap_initW( DcInfo->DomainControllerName+2,
|
||
LDAP_PORT );
|
||
|
||
if ( LdapHandle == NULL ) {
|
||
LdapStatus = LdapGetLastError();
|
||
NetpLog(( "NetpManageAltComputerName: ldap_init to %ws failed: %lu\n",
|
||
DcInfo->DomainControllerName+2,
|
||
LdapStatus ));
|
||
NetStatus = LdapMapErrorToWin32( LdapStatus );
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Tell LDAP to avoid chasing referals
|
||
//
|
||
|
||
LdapOption = PtrToLong( LDAP_OPT_OFF );
|
||
LdapStatus = ldap_set_optionW( LdapHandle,
|
||
LDAP_OPT_REFERRALS,
|
||
&LdapOption );
|
||
|
||
if ( LdapStatus != LDAP_SUCCESS ) {
|
||
NetpLog(( "NetpManageAltComputerName: ldap_set_option LDAP_OPT_REFERRALS failed on %ws: %ld: %s\n",
|
||
DcInfo->DomainControllerName+2,
|
||
LdapStatus,
|
||
ldap_err2stringA(LdapStatus) ));
|
||
NetStatus = LdapMapErrorToWin32( LdapStatus );
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Tell LDAP we are passing an explicit DC name
|
||
// to avoid the DC discovery
|
||
//
|
||
|
||
LdapOption = PtrToLong( LDAP_OPT_ON );
|
||
LdapStatus = ldap_set_optionW( LdapHandle,
|
||
LDAP_OPT_AREC_EXCLUSIVE,
|
||
&LdapOption );
|
||
|
||
if ( LdapStatus != LDAP_SUCCESS ) {
|
||
NetpLog(( "NetpManageAltComputerName: ldap_set_option LDAP_OPT_AREC_EXCLUSIVE failed on %ws: %ld: %s\n",
|
||
DcInfo->DomainControllerName+2,
|
||
LdapStatus,
|
||
ldap_err2stringA(LdapStatus) ));
|
||
NetStatus = LdapMapErrorToWin32( LdapStatus );
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Bind to the LDAP server
|
||
//
|
||
|
||
if ( AccountUserName != NULL ) {
|
||
AuthIdent.User = AccountUserName;
|
||
AuthIdent.UserLength = wcslen( AccountUserName );
|
||
}
|
||
|
||
if ( AccountDomainName != NULL ) {
|
||
AuthIdent.Domain = AccountDomainName;
|
||
AuthIdent.DomainLength = wcslen( AccountDomainName );
|
||
}
|
||
|
||
if ( DomainAccountPassword != NULL ) {
|
||
AuthIdent.Password = DomainAccountPassword;
|
||
AuthIdent.PasswordLength = wcslen( DomainAccountPassword );
|
||
}
|
||
|
||
AuthIdent.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
|
||
|
||
LdapStatus = ldap_bind_sW( LdapHandle,
|
||
NULL,
|
||
(PWSTR) &AuthIdent,
|
||
LDAP_AUTH_NEGOTIATE );
|
||
|
||
if ( LdapStatus != LDAP_SUCCESS ) {
|
||
NetpLog(( "NetpManageAltComputerName: ldap_bind failed on %ws: %ld: %s\n",
|
||
DcInfo->DomainControllerName+2,
|
||
LdapStatus,
|
||
ldap_err2stringA(LdapStatus) ));
|
||
NetStatus = LdapMapErrorToWin32( LdapStatus );
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Ok, now that we have all prerequsites satisfied,
|
||
// do the operations that may require rollback if
|
||
// they fail.
|
||
//
|
||
// Set the primary SAM account name by doing NetSetUser.
|
||
// Note that this will also rename the DN of the computer
|
||
// object (and its display name) and the DN of the server
|
||
// object linked to from the computer object if this is a DC.
|
||
//
|
||
|
||
if ( Action == NET_SET_PRIMARY_COMPUTER_NAME ) {
|
||
NetStatus = NetpSetPrimarySamAccountName(
|
||
DcInfo->DomainControllerName,
|
||
MachineAccountName,
|
||
NewMachineAccountName,
|
||
DomainAccount,
|
||
DomainAccountPassword );
|
||
|
||
if ( NetStatus != NO_ERROR ) {
|
||
NetpLog(( "NetpManageAltComputerName: NetpSetPrimarySamAccountName failed on %ws: 0x%lx\n",
|
||
DcInfo->DomainControllerName,
|
||
NetStatus ));
|
||
goto Cleanup;
|
||
}
|
||
PrimarySamAccountNameSet = TRUE;
|
||
|
||
//
|
||
// We need to crack the new machine account
|
||
// name which we just set
|
||
//
|
||
MachineAccountNameToCrack = NewMachineAccountName;
|
||
|
||
//
|
||
// If we are not changing the primary name,
|
||
// the name to crack is the current machine name.
|
||
//
|
||
|
||
} else {
|
||
MachineAccountNameToCrack = MachineAccountName;
|
||
}
|
||
|
||
//
|
||
// Now get the DN for our machine account object
|
||
// in the DS. Do this after setting the SAM account
|
||
// name as it changes the DN.
|
||
//
|
||
// Form the NT4 account name 'domain\account' to crack
|
||
// it into the DN.
|
||
//
|
||
|
||
NameToCrack = LocalAlloc( LMEM_ZEROINIT,
|
||
LocalPolicyDns->Name.Length + // Netbios domain name
|
||
(1 + // backslash
|
||
wcslen(MachineAccountNameToCrack) + // SAM account name
|
||
1) * sizeof(WCHAR) ); // NULL terminator
|
||
|
||
if ( NameToCrack == NULL ) {
|
||
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
|
||
goto Cleanup;
|
||
}
|
||
|
||
RtlCopyMemory( NameToCrack,
|
||
LocalPolicyDns->Name.Buffer,
|
||
LocalPolicyDns->Name.Length );
|
||
|
||
wcscat( NameToCrack, L"\\" );
|
||
wcscat( NameToCrack, MachineAccountNameToCrack );
|
||
|
||
//
|
||
// Crack the account name into a DN
|
||
//
|
||
|
||
NetStatus = DsCrackNamesW( hDs,
|
||
0,
|
||
DS_NT4_ACCOUNT_NAME,
|
||
DS_FQDN_1779_NAME,
|
||
1,
|
||
&NameToCrack,
|
||
&CrackedName );
|
||
|
||
if ( NetStatus != NO_ERROR ) {
|
||
NetpLog(( "NetpManageAltComputerName: DsCrackNames failed on '%ws' for %ws: 0x%lx\n",
|
||
DcInfo->DomainControllerName,
|
||
NameToCrack,
|
||
NetStatus ));
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Check for consistency
|
||
//
|
||
|
||
if ( CrackedName->rItems[0].status != DS_NAME_NO_ERROR ) {
|
||
NetpLog(( "NetpManageAltComputerName: CrackNames failed for %ws: substatus 0x%lx\n",
|
||
NameToCrack,
|
||
CrackedName->rItems[0].status ));
|
||
NetStatus = NetpCrackNamesStatus2Win32Error( CrackedName->rItems[0].status );
|
||
goto Cleanup;
|
||
}
|
||
|
||
if ( CrackedName->cItems > 1 ) {
|
||
NetStatus = ERROR_DS_NAME_ERROR_NOT_UNIQUE;
|
||
NetpLog(( "NetpManageAltComputerName: Cracked Name %ws is not unique on %ws: %lu\n",
|
||
NameToCrack,
|
||
DcInfo->DomainControllerName,
|
||
CrackedName->cItems ));
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Ok, we have our machine account DN. Proceed with modifying
|
||
// our machine account object in the DS.
|
||
//
|
||
// If we are seting new DnsHostName, we have to stop netlogon
|
||
// and tell it not to update this attribute before the
|
||
// reboot.
|
||
//
|
||
|
||
if ( Action == NET_SET_PRIMARY_COMPUTER_NAME ) {
|
||
|
||
//
|
||
// First get the current status of netlogon so that
|
||
// we can roll back properly on failure
|
||
//
|
||
NetStatus = NetpQueryService( SERVICE_NETLOGON,
|
||
&NetlogonServiceStatus,
|
||
&NetlogonServiceConfig );
|
||
|
||
if ( NetStatus != NO_ERROR ) {
|
||
NetpLog(( "NetpManageAltComputerName: NetpQueryService failed 0x%lx\n",
|
||
NetStatus ));
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Stop netlogon if it's running
|
||
//
|
||
if ( NetlogonServiceStatus.dwCurrentState != SERVICE_STOPPED ) {
|
||
NetStatus = NetpControlServices( NETSETUP_SVC_STOPPED,
|
||
NETSETUPP_SVC_NETLOGON );
|
||
if ( NetStatus != NO_ERROR ) {
|
||
NetpLog(( "NetpManageAltComputerName: NetpControlServices failed 0x%lx\n",
|
||
NetStatus ));
|
||
goto Cleanup;
|
||
}
|
||
StopedNetlogon = TRUE;
|
||
}
|
||
|
||
//
|
||
// Tell netlogon not to update DnsHostName until reboot
|
||
// in case the user decides to start netlogon before reboot
|
||
// for some reason
|
||
//
|
||
NetpAvoidNetlogonSpnSet( TRUE );
|
||
ToldNetlogonToAvoidDnsHostNameUpdate = TRUE;
|
||
}
|
||
|
||
|
||
//
|
||
// Prepare attributes that need to be set in the DS
|
||
//
|
||
// If we are seting a primary name, we need to set
|
||
// DnsHostName attribute. Also, we need to add the
|
||
// current primary computer name to the additional
|
||
// DNS host name list.
|
||
//
|
||
|
||
if ( Action == NET_SET_PRIMARY_COMPUTER_NAME ) {
|
||
DnsHostNameValues[0] = AlternateName;
|
||
DnsHostNameValues[1] = NULL;
|
||
|
||
DnsHostNameMod.mod_type = L"DnsHostName";
|
||
DnsHostNameMod.mod_values = DnsHostNameValues;
|
||
DnsHostNameMod.mod_op = LDAP_MOD_REPLACE;
|
||
|
||
ModList[ModCount++] = &DnsHostNameMod;
|
||
|
||
//
|
||
// Add the current primary to additional list
|
||
//
|
||
PrimaryNameValues[0] = PrimaryName;
|
||
PrimaryNameValues[1] = NULL;
|
||
|
||
PrimaryNameMod.mod_type = L"msDS-AdditionalDnsHostName";
|
||
PrimaryNameMod.mod_values = PrimaryNameValues;
|
||
PrimaryNameMod.mod_op = LDAP_MOD_ADD;
|
||
|
||
ModList[ModCount++] = &PrimaryNameMod;
|
||
}
|
||
|
||
//
|
||
// Prepare the additional DNS host name modification.
|
||
//
|
||
// Note that we don't need to manipulate the additional
|
||
// SAM account name as it will be added/deleted by the DS
|
||
// itself as the result of the AdditionalDnsHostName update.
|
||
//
|
||
|
||
AlternateNameValues[0] = AlternateName;
|
||
AlternateNameValues[1] = NULL;
|
||
|
||
AlternateDnsHostNameMod.mod_type = L"msDS-AdditionalDnsHostName";
|
||
AlternateDnsHostNameMod.mod_values = AlternateNameValues;
|
||
|
||
//
|
||
// If we are additing an alternate name, the operation
|
||
// is add. Otherwise, we are deleting the alternate
|
||
// name or setting the alternate name as primary: in
|
||
// both cases the alternate name should be deleted
|
||
// from the additional name attribute list.
|
||
//
|
||
|
||
if ( Action == NET_ADD_ALTERNATE_COMPUTER_NAME ) {
|
||
AlternateDnsHostNameMod.mod_op = LDAP_MOD_ADD;
|
||
} else {
|
||
AlternateDnsHostNameMod.mod_op = LDAP_MOD_DELETE;
|
||
}
|
||
|
||
ModList[ModCount++] = &AlternateDnsHostNameMod;
|
||
|
||
//
|
||
// Write the modifications
|
||
//
|
||
|
||
LdapStatus = ldap_modify_ext_sW( LdapHandle,
|
||
CrackedName->rItems[0].pName, // DN of account
|
||
ModList,
|
||
ModifyControlArray, // server controls
|
||
NULL ); // no client controls
|
||
|
||
if ( LdapStatus != LDAP_SUCCESS ) {
|
||
NetpLog(( "NetpManageAltComputerName: ldap_modify_ext_s failed on %ws: %ld: %s\n",
|
||
DcInfo->DomainControllerName+2,
|
||
LdapStatus,
|
||
ldap_err2stringA(LdapStatus) ));
|
||
NetStatus = LdapMapErrorToWin32( LdapStatus );
|
||
goto Cleanup;
|
||
}
|
||
|
||
Cleanup:
|
||
|
||
//
|
||
// Revert impersonation
|
||
//
|
||
|
||
if ( ClientImpersonated ) {
|
||
WsRevertToSelf();
|
||
}
|
||
|
||
//
|
||
// On error, revert the changes. Do this after reverting
|
||
// impersonation to have as much (LocalSystem) access
|
||
// as one possibly can.
|
||
//
|
||
// Note that we don't need to revert ldap modifications
|
||
// because they were made as the last step.
|
||
//
|
||
|
||
if ( NetStatus != NO_ERROR && NameModifiedLocally ) {
|
||
NET_API_STATUS TmpNetStatus = NO_ERROR;
|
||
|
||
//
|
||
// If we added an alternate name, remove it
|
||
//
|
||
if ( Action == NET_ADD_ALTERNATE_COMPUTER_NAME ) {
|
||
|
||
TmpNetStatus = RemoveLocalAlternateComputerName( AlternateName,
|
||
0 ); // reserved
|
||
if ( TmpNetStatus != NO_ERROR ) {
|
||
NetpLog(( "NetpManageAltComputerName: (rollback) RemoveLocalAlternateComputerName failed 0x%lx\n",
|
||
TmpNetStatus ));
|
||
}
|
||
|
||
//
|
||
// If we removed an alternate name, add it
|
||
//
|
||
} else if ( Action == NET_DEL_ALTERNATE_COMPUTER_NAME ) {
|
||
|
||
TmpNetStatus = AddLocalAlternateComputerName( AlternateName,
|
||
0 ); // reserved
|
||
if ( TmpNetStatus != NO_ERROR ) {
|
||
NetpLog(( "NetpManageAltComputerName: (rollback) AddLocalAlternateComputerName failed 0x%lx\n",
|
||
TmpNetStatus ));
|
||
}
|
||
|
||
//
|
||
// If we set a new primary name, reset it to the previous value
|
||
//
|
||
} else if ( Action == NET_SET_PRIMARY_COMPUTER_NAME ) {
|
||
|
||
TmpNetStatus = SetLocalPrimaryComputerName( PrimaryName,
|
||
0 ); // reserved
|
||
|
||
if ( TmpNetStatus != NO_ERROR ) {
|
||
NetpLog(( "NetpManageAltComputerName: (rollback) SetLocalPrimaryComputerName failed 0x%lx\n",
|
||
TmpNetStatus ));
|
||
}
|
||
}
|
||
}
|
||
|
||
if ( NetStatus != NO_ERROR && PrimarySamAccountNameSet ) {
|
||
NET_API_STATUS TmpNetStatus = NO_ERROR;
|
||
|
||
TmpNetStatus = NetpSetPrimarySamAccountName(
|
||
DcInfo->DomainControllerName,
|
||
NewMachineAccountName, // the changed name
|
||
MachineAccountName, // old name to restore
|
||
DomainAccount,
|
||
DomainAccountPassword );
|
||
|
||
if ( TmpNetStatus != NO_ERROR) {
|
||
NetpLog(( "NetpManageAltComputerName: NetpSetPrimarySamAccountName (rollback) failed on %ws: 0x%lx\n",
|
||
DcInfo->DomainControllerName,
|
||
TmpNetStatus ));
|
||
}
|
||
}
|
||
|
||
//
|
||
// On error, take back what we told netlogon
|
||
// w.r.t. the DnsHostName update
|
||
//
|
||
|
||
if ( NetStatus != NO_ERROR && ToldNetlogonToAvoidDnsHostNameUpdate ) {
|
||
NetpAvoidNetlogonSpnSet( FALSE );
|
||
}
|
||
|
||
//
|
||
// On error, restart netlogon if we stoped it
|
||
//
|
||
|
||
if ( NetStatus != NO_ERROR && StopedNetlogon ) {
|
||
NET_API_STATUS TmpNetStatus = NO_ERROR;
|
||
TmpNetStatus = NetpControlServices( NETSETUP_SVC_STARTED,
|
||
NETSETUPP_SVC_NETLOGON );
|
||
|
||
if ( TmpNetStatus != NO_ERROR ) {
|
||
NetpLog(( "NetpManageAltComputerName: (rollback) Failed starting netlogon: 0x%lx\n",
|
||
TmpNetStatus ));
|
||
}
|
||
}
|
||
|
||
//
|
||
// Close the log file
|
||
//
|
||
|
||
NetSetuppCloseLog();
|
||
|
||
//
|
||
// Finally free the memory
|
||
//
|
||
|
||
if ( DomainAccountPassword != NULL ) {
|
||
NetpMemoryFree( DomainAccountPassword );
|
||
}
|
||
|
||
if ( AccountUserName != NULL ) {
|
||
NetApiBufferFree( AccountUserName );
|
||
}
|
||
|
||
if ( AccountDomainName != NULL ) {
|
||
NetApiBufferFree( AccountDomainName );
|
||
}
|
||
|
||
if ( ComputerName != NULL ) {
|
||
NetApiBufferFree( ComputerName );
|
||
}
|
||
|
||
if ( MachineAccountName != NULL ) {
|
||
NetApiBufferFree( MachineAccountName );
|
||
}
|
||
|
||
if ( NewMachineAccountName != NULL ) {
|
||
NetApiBufferFree( NewMachineAccountName );
|
||
}
|
||
|
||
if ( PrimaryName != NULL ) {
|
||
LocalFree( PrimaryName );
|
||
}
|
||
|
||
if ( NameToCrack != NULL ) {
|
||
LocalFree( NameToCrack );
|
||
}
|
||
|
||
if ( NetlogonServiceConfig != NULL ) {
|
||
LocalFree( NetlogonServiceConfig );
|
||
}
|
||
|
||
if ( LocalPolicyDns != NULL ) {
|
||
LsaFreeMemory( LocalPolicyDns );
|
||
}
|
||
|
||
if ( LocalPolicyHandle != NULL ) {
|
||
LsaClose( LocalPolicyHandle );
|
||
}
|
||
|
||
if ( DcInfo != NULL ) {
|
||
NetApiBufferFree( DcInfo );
|
||
}
|
||
|
||
if ( AuthId ) {
|
||
DsFreePasswordCredentials( AuthId );
|
||
}
|
||
|
||
if ( CrackedName ) {
|
||
DsFreeNameResultW( CrackedName );
|
||
}
|
||
|
||
if ( hDs ) {
|
||
DsUnBind( &hDs );
|
||
}
|
||
|
||
if ( LdapHandle != NULL ) {
|
||
ldap_unbind_s( LdapHandle );
|
||
}
|
||
|
||
return NetStatus;
|
||
}
|
||
|
||
NET_API_STATUS
|
||
NET_API_FUNCTION
|
||
NetrAddAlternateComputerName(
|
||
IN handle_t RpcBindingHandle,
|
||
IN LPWSTR ServerName OPTIONAL,
|
||
IN LPWSTR AlternateName,
|
||
IN LPWSTR DomainAccount OPTIONAL,
|
||
IN PJOINPR_ENCRYPTED_USER_PASSWORD EncryptedPassword OPTIONAL,
|
||
IN ULONG Reserved
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Adds an alternate name for the specified server.
|
||
|
||
Arguments:
|
||
|
||
ServerName -- Name of server on which to execute this function.
|
||
|
||
AlternateName -- The name to add.
|
||
|
||
DomainAccount -- Domain account to use for accessing the
|
||
machine account object for the specified server in the AD.
|
||
Not used if the server is not joined to a domain. May be
|
||
NULL in which case the credentials of the user executing
|
||
this routine are used.
|
||
|
||
DomainAccountPassword -- Password matching the domain account.
|
||
Not used if the server is not joined to a domain. May be
|
||
NULL in which case the credentials of the user executing
|
||
this routine are used.
|
||
|
||
Reserved -- Reserved for future use. If some flags are specified
|
||
that are not supported, they will be ignored if
|
||
NET_IGNORE_UNSUPPORTED_FLAGS is set, otherwise this routine
|
||
will fail with ERROR_INVALID_FLAGS.
|
||
|
||
Note:
|
||
|
||
The process that calls this routine must have administrator
|
||
privileges on the server computer.
|
||
|
||
Returns:
|
||
|
||
NO_ERROR -- Success
|
||
|
||
ERROR_NOT_SUPPORTED -- The specified server does not support this
|
||
functionality.
|
||
|
||
ERROR_INVALID_FLAGS - The Flags parameter is incorrect.
|
||
|
||
--*/
|
||
{
|
||
//
|
||
// Call the common routine
|
||
//
|
||
|
||
return NetpManageAltComputerName(
|
||
RpcBindingHandle,
|
||
AlternateName,
|
||
NET_ADD_ALTERNATE_COMPUTER_NAME,
|
||
DomainAccount,
|
||
EncryptedPassword,
|
||
Reserved );
|
||
}
|
||
|
||
NET_API_STATUS
|
||
NET_API_FUNCTION
|
||
NetrRemoveAlternateComputerName(
|
||
IN handle_t RpcBindingHandle,
|
||
IN LPWSTR ServerName OPTIONAL,
|
||
IN LPWSTR AlternateName,
|
||
IN LPWSTR DomainAccount OPTIONAL,
|
||
IN PJOINPR_ENCRYPTED_USER_PASSWORD EncryptedPassword OPTIONAL,
|
||
IN ULONG Reserved
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Deletes an alternate name for the specified server.
|
||
|
||
Arguments:
|
||
|
||
ServerName -- Name of server on which to execute this function.
|
||
|
||
AlternateName -- The name to delete.
|
||
|
||
DomainAccount -- Domain account to use for accessing the
|
||
machine account object for the specified server in the AD.
|
||
Not used if the server is not joined to a domain. May be
|
||
NULL in which case the credentials of the user executing
|
||
this routine are used.
|
||
|
||
DomainAccountPassword -- Password matching the domain account.
|
||
Not used if the server is not joined to a domain. May be
|
||
NULL in which case the credentials of the user executing
|
||
this routine are used.
|
||
|
||
Reserved -- Reserved for future use. If some flags are specified
|
||
that are not supported, they will be ignored if
|
||
NET_IGNORE_UNSUPPORTED_FLAGS is set, otherwise this routine
|
||
will fail with ERROR_INVALID_FLAGS.
|
||
|
||
Note:
|
||
|
||
The process that calls this routine must have administrator
|
||
privileges on the server computer.
|
||
|
||
Returns:
|
||
|
||
NO_ERROR -- Success
|
||
|
||
ERROR_NOT_SUPPORTED -- The specified server does not support this
|
||
functionality.
|
||
|
||
ERROR_INVALID_FLAGS - The Flags parameter is incorrect.
|
||
|
||
--*/
|
||
{
|
||
//
|
||
// Call the common routine
|
||
//
|
||
|
||
return NetpManageAltComputerName(
|
||
RpcBindingHandle,
|
||
AlternateName,
|
||
NET_DEL_ALTERNATE_COMPUTER_NAME,
|
||
DomainAccount,
|
||
EncryptedPassword,
|
||
Reserved );
|
||
}
|
||
|
||
NET_API_STATUS
|
||
NET_API_FUNCTION
|
||
NetrSetPrimaryComputerName(
|
||
IN handle_t RpcBindingHandle,
|
||
IN LPWSTR ServerName OPTIONAL,
|
||
IN LPWSTR PrimaryName,
|
||
IN LPWSTR DomainAccount OPTIONAL,
|
||
IN PJOINPR_ENCRYPTED_USER_PASSWORD EncryptedPassword OPTIONAL,
|
||
IN ULONG Reserved
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Sets the primary computer name for the specified server.
|
||
|
||
Arguments:
|
||
|
||
ServerName -- Name of server on which to execute this function.
|
||
|
||
PrimaryName -- The primary computer name to set.
|
||
|
||
DomainAccount -- Domain account to use for accessing the
|
||
machine account object for the specified server in the AD.
|
||
Not used if the server is not joined to a domain. May be
|
||
NULL in which case the credentials of the user executing
|
||
this routine are used.
|
||
|
||
DomainAccountPassword -- Password matching the domain account.
|
||
Not used if the server is not joined to a domain. May be
|
||
NULL in which case the credentials of the user executing
|
||
this routine are used.
|
||
|
||
Reserved -- Reserved for future use. If some flags are specified
|
||
that are not supported, they will be ignored if
|
||
NET_IGNORE_UNSUPPORTED_FLAGS is set, otherwise this routine
|
||
will fail with ERROR_INVALID_FLAGS.
|
||
|
||
Note:
|
||
|
||
The process that calls this routine must have administrator
|
||
privileges on the server computer.
|
||
|
||
Returns:
|
||
|
||
NO_ERROR -- Success
|
||
|
||
ERROR_NOT_SUPPORTED -- The specified server does not support this
|
||
functionality.
|
||
|
||
ERROR_INVALID_FLAGS - The Flags parameter is incorrect.
|
||
|
||
--*/
|
||
{
|
||
//
|
||
// Call the common routine
|
||
//
|
||
|
||
return NetpManageAltComputerName(
|
||
RpcBindingHandle,
|
||
PrimaryName,
|
||
NET_SET_PRIMARY_COMPUTER_NAME,
|
||
DomainAccount,
|
||
EncryptedPassword,
|
||
Reserved );
|
||
}
|
||
|
||
NET_API_STATUS
|
||
NET_API_FUNCTION
|
||
NetrEnumerateComputerNames(
|
||
IN LPWSTR ServerName OPTIONAL,
|
||
IN NET_COMPUTER_NAME_TYPE NameType,
|
||
IN ULONG Reserved,
|
||
OUT PNET_COMPUTER_NAME_ARRAY *ComputerNames
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Enumerates computer names for the specified server.
|
||
|
||
Arguments:
|
||
|
||
ServerName -- Name of server on which to execute this function.
|
||
|
||
NameType -- The type of the name queried.
|
||
|
||
Reserved -- Reserved for future use. If some flags are specified
|
||
that are not supported, they will be ignored if
|
||
NET_IGNORE_UNSUPPORTED_FLAGS is set, otherwise this routine
|
||
will fail with ERROR_INVALID_FLAGS.
|
||
|
||
ComputerNames - Returns the computer names structure.
|
||
|
||
Returns:
|
||
|
||
NO_ERROR -- Success
|
||
|
||
ERROR_NOT_SUPPORTED -- The specified server does not support this
|
||
functionality.
|
||
|
||
ERROR_INVALID_FLAGS - The Flags parameter is incorrect.
|
||
|
||
--*/
|
||
{
|
||
NET_API_STATUS NetStatus = NO_ERROR;
|
||
NT_PRODUCT_TYPE NtProductType;
|
||
BOOL ClientImpersonated = FALSE;
|
||
ULONG Size = 0;
|
||
ULONG Index = 0;
|
||
ULONG EntryCount = 0;
|
||
|
||
LPWSTR LocalNames = NULL;
|
||
PNET_COMPUTER_NAME_ARRAY LocalArray = NULL;
|
||
LPTSTR_ARRAY TStrArray;
|
||
|
||
//
|
||
// Initialize the log file
|
||
//
|
||
|
||
NetSetuppOpenLog();
|
||
NetpLog(( "NetrEnumerateComputerNames called: NameType = 0x%lx, Flags = 0x%lx\n",
|
||
NameType, Reserved ));
|
||
|
||
//
|
||
// This API is supported on DCs and servers only
|
||
//
|
||
|
||
if ( !RtlGetNtProductType( &NtProductType ) ) {
|
||
NtProductType = NtProductWinNt;
|
||
}
|
||
|
||
if ( NtProductType != NtProductServer &&
|
||
NtProductType != NtProductLanManNt ) {
|
||
|
||
NetpLog(( "NetrEnumerateComputerNames: Not supported on wksta: %lu\n",
|
||
NtProductType ));
|
||
NetStatus = ERROR_NOT_SUPPORTED;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Validate the Flags
|
||
//
|
||
// If some flags are passed which we don't understand
|
||
// and we are not told to ignore them, error out.
|
||
//
|
||
|
||
if ( Reserved != 0 &&
|
||
(Reserved & NET_IGNORE_UNSUPPORTED_FLAGS) == 0 ) {
|
||
NetpLog(( "NetrEnumerateComputerNames: Invalid Flags passed\n" ));
|
||
NetStatus = ERROR_INVALID_FLAGS;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Validate the name type
|
||
//
|
||
|
||
if ( NameType >= NetComputerNameTypeMax ) {
|
||
NetpLog(( "NetrEnumerateComputerNames: Invalid name type passed %lu\n",
|
||
NameType ));
|
||
NetStatus = ERROR_INVALID_PARAMETER;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Impersonate the caller. The local API will perform the
|
||
// access check on the caller on our behalf.
|
||
//
|
||
|
||
NetStatus = WsImpersonateClient();
|
||
|
||
if ( NetStatus != NO_ERROR ) {
|
||
NetpLog(( "NetrEnumerateComputerNames: WsImpersonateClient failed: 0x%lx\n",
|
||
NetStatus ));
|
||
goto Cleanup;
|
||
}
|
||
|
||
ClientImpersonated = TRUE;
|
||
|
||
//
|
||
// Get the size of the local data
|
||
//
|
||
|
||
NetStatus = EnumerateLocalComputerNamesW(
|
||
NameType,
|
||
0, // reserved
|
||
LocalNames,
|
||
&Size ); // in characters, Null included
|
||
|
||
//
|
||
// Allocate memory for local names
|
||
//
|
||
|
||
if ( NetStatus != NO_ERROR ) {
|
||
if ( NetStatus == ERROR_MORE_DATA ) {
|
||
NetStatus = NetApiBufferAllocate( Size * sizeof(WCHAR), &LocalNames );
|
||
if ( NetStatus != NO_ERROR ) {
|
||
goto Cleanup;
|
||
}
|
||
} else {
|
||
NetpLog(( "NetrEnumerateComputerNames: EnumerateLocalComputerNamesW failed 0x%lx\n",
|
||
NetStatus ));
|
||
goto Cleanup;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Get the names
|
||
//
|
||
|
||
NetStatus = EnumerateLocalComputerNamesW(
|
||
NameType,
|
||
0, // reserved
|
||
LocalNames,
|
||
&Size );
|
||
|
||
if ( NetStatus != NO_ERROR ) {
|
||
NetpLog(( "NetrEnumerateComputerNames: EnumerateLocalComputerNamesW (2) failed 0x%lx\n",
|
||
NetStatus ));
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Determine the length of the returned information
|
||
//
|
||
|
||
Size = sizeof( NET_COMPUTER_NAME_ARRAY );
|
||
|
||
TStrArray = LocalNames;
|
||
while ( !NetpIsTStrArrayEmpty(TStrArray) ) {
|
||
|
||
Size += sizeof(UNICODE_STRING) + (wcslen(TStrArray) + 1) * sizeof(WCHAR);
|
||
EntryCount++;
|
||
TStrArray = NetpNextTStrArrayEntry( TStrArray );
|
||
}
|
||
|
||
//
|
||
// Allocate the return buffer.
|
||
//
|
||
|
||
NetStatus = NetApiBufferAllocate( Size, &LocalArray );
|
||
|
||
if ( NetStatus != NO_ERROR ) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
LocalArray->EntryCount = EntryCount;
|
||
|
||
//
|
||
// If there are names to return, copy them
|
||
// to the return buffer
|
||
//
|
||
|
||
if ( EntryCount == 0 ) {
|
||
LocalArray->ComputerNames = NULL;
|
||
} else {
|
||
PUNICODE_STRING Strings;
|
||
LPBYTE Where;
|
||
|
||
Strings = (PUNICODE_STRING) (LocalArray + 1);
|
||
LocalArray->ComputerNames = Strings;
|
||
Where = (LPBYTE) &Strings[EntryCount];
|
||
|
||
//
|
||
// Loop copying the names into the return buffer.
|
||
//
|
||
|
||
Index = 0;
|
||
TStrArray = LocalNames;
|
||
while ( !NetpIsTStrArrayEmpty(TStrArray) ) {
|
||
|
||
Strings[Index].Buffer = (LPWSTR) Where;
|
||
Strings[Index].Length = wcslen(TStrArray) * sizeof(WCHAR);
|
||
Strings[Index].MaximumLength = Strings[Index].Length + sizeof(WCHAR);
|
||
|
||
RtlCopyMemory( Where, TStrArray, Strings[Index].MaximumLength );
|
||
|
||
Where += Strings[Index].MaximumLength;
|
||
Index++;
|
||
TStrArray = NetpNextTStrArrayEntry( TStrArray );
|
||
}
|
||
}
|
||
|
||
NetStatus = NO_ERROR;
|
||
|
||
Cleanup:
|
||
|
||
//
|
||
// Revert impersonation
|
||
//
|
||
|
||
if ( ClientImpersonated ) {
|
||
WsRevertToSelf();
|
||
}
|
||
|
||
//
|
||
// Return names on success or clean up on error
|
||
//
|
||
|
||
if ( NetStatus == NO_ERROR ) {
|
||
|
||
*ComputerNames = LocalArray;
|
||
|
||
//
|
||
// Be verbose
|
||
//
|
||
if ( LocalArray->EntryCount > 0 ) {
|
||
NetpLog(( "NetrEnumerateComputerNames: Returning names:" ));
|
||
for ( Index = 0; Index < LocalArray->EntryCount; Index++ ) {
|
||
NetpLog(( " %wZ", &(LocalArray->ComputerNames[Index]) ));
|
||
}
|
||
NetpLog(( "\n" ));
|
||
} else {
|
||
NetpLog(( "NetrEnumerateComputerNames: No names returned\n" ));
|
||
}
|
||
|
||
} else {
|
||
if ( LocalArray != NULL ) {
|
||
NetApiBufferFree( LocalArray );
|
||
}
|
||
NetpLog(( "NetrEnumerateComputerNames: Failed 0x%lx\n", NetStatus ));
|
||
}
|
||
|
||
if ( LocalNames != NULL ) {
|
||
NetApiBufferFree( LocalNames );
|
||
}
|
||
|
||
//
|
||
// Close the log file
|
||
//
|
||
|
||
NetSetuppCloseLog();
|
||
|
||
return NetStatus;
|
||
}
|
||
|