/*++ 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 #include #include #include #include #include #include #include #include #include #include #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(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, &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; BOOLEAN AutoenrolConfigUpdated = 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; } // // The password must be specified // if ( lpPassword == NULL ) { NetpLog(( "NetpJoinDomain: NULL machine password is passed. Error out.\n" )); NetStatus = ERROR_PASSWORD_RESTRICTION; goto Cleanup; } // // Switch the passwords // lpMachinePassword = lpPassword; lpPassword = NULL; MachinePasswordLen = wcslen(lpMachinePassword); if ( MachinePasswordLen < 1 ) { NetpLog(( "NetpJoinDomain: zero length machine password is passed. Error out.\n" )); 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(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, FALSE, // Don't use the GP value as we are changing domains &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( &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( 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( 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( NULL, &pPolicyLocalPDI, &pPolicyLocalDns, &hLsa ); } // // 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; } } // // If we are already joined, clean up the AutoEnrol data // (which is a list of GUID for DCs stored in registry). // Note that this is temp code. The AutoEntrol itself // should do the cleanup on service start. It should write // the name of the domain the GUIDs are for and clean up // if the domain name is different from the primary // domain name. This is Windows Bug 668489. // After this bug is fixed, all code using NetpUpdateAutoenrolConfig() // should be removed. // if ( NetStatus == NERR_Success && IS_CLIENT_JOINED(pPolicyLocalPDI) ) { NET_API_STATUS NetStatusTmp = NetpUpdateAutoenrolConfig( TRUE ); // // Ignore failure // if ( NetStatusTmp == NERR_Success ) { AutoenrolConfigUpdated = TRUE; } else { NetpLog(( "NetpJoinDomain: Failed to clean up AutoEnrol data: 0x%lx. Ignored.\n", NetStatusTmp )); } } // // Set the primary domain info from the Dc to the client // if ( NetStatus == NERR_Success ) { NetStatus = NetpSetLsaPrimaryDomain(pPolicyPDI->Name.Buffer, pPolicyPDI->Sid, pPolicyDns, &hLsa); 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( pPolicyLocalPDI->Name.Buffer, NULL, NULL, &hLsa ); 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( &SavedState, FALSE, &hLsa ); NetpLog(( "NetpJoinDomain: rollback: status of restoring secret: 0x%lx\n", NetStatus2)); } else { NetStatus2 = NetpManageMachineSecret( lpMachine, NULL, NETSETUPP_DELETE, FALSE, &hLsa ); NetpLog(( "NetpJoinDomain: rollback: status of deleting secret: 0x%lx\n", NetStatus2)); } } // // Reset AutoEnrol data // if ( AutoenrolConfigUpdated ) { NetStatus2 = NetpUpdateAutoenrolConfig( FALSE ); if ( NetStatus2 != NERR_Success ) { NetpLog(( "NetpJoinDomain: rollback: failed to reset AutoEnrol data: 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( 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(); // // Validate flags. If there is a flag set that we don't // recognize and we are not asked to ignore it, the flags // are invalid. // if ( (fJoinOpts & ~NETSETUP_VALID_UNJOIN_FLAGS) != 0 ) { if ( (fJoinOpts & NETSETUP_IGNORE_UNSUPPORTED_FLAGS) != 0 ) { NetpLog(( "NetpUnJoinDomain: Ignoring unrecognized flags: 0x%lx\n", (fJoinOpts & ~NETSETUP_VALID_UNJOIN_FLAGS) )); } else { NetpLog(( "NetpUnJoinDomain: Flags are invalid: 0x%lx\n", fJoinOpts )); NetStatus = ERROR_INVALID_FLAGS; } } // // check to see if the machine being joined is a DC // if it is, we cannot let this join another domain/workgroup. // if ( NetStatus == NERR_Success ) { 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; BOOL NameChanged = 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; 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; } NetSetuppOpenLog(); 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(); NetpLog(( "NetpChangeMachineName: GetComputerNameExW failed 0x%lx\n", NetStatus )); } } } // // 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 if ( NetStatus == NERR_Success ) { NameChanged = TRUE; } } // // 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, &pPolicyLocalPDI, &pPolicyLocalDns, NULL ); } // // Determine DnsHostName of this machine // if ( NetStatus == NERR_Success && (DcFlags & DS_DS_FLAG) != 0 && pPolicyLocalDns != NULL ) { NetStatus = NetpGetDnsHostName( lpNewHostName, &pPolicyLocalDns->DnsDomainName, TRUE, // Use the GP value &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 ); } } } // // Rollback on error to set DnsHostName and SPN // if ( NetStatus != NO_ERROR && NameChanged ) { NET_API_STATUS NetStatusTmp = NetpManageMachineAccount( lpCurrentMachine, lpNewMachine, // Names switched DomainControllerName, NULL, NETSETUPP_RENAME, 0, TRUE ); // NT4 DC; but doesn't really matter for rename if ( NetStatusTmp != NERR_Success ) { NetpLog(( "NetpChangeMachineName: Failed to rollback the machine name 0x%lx\n", NetStatusTmp )); } else { NetpLog(( "NetpChangeMachineName: Rolled back the machine name successfully\n" )); } } 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; ULONG LoopCount = 0; // // 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 ) { // // Try in a loop for a max of 20 sec to stop the service // special casing some netlogon condition // for ( LoopCount = 0; LoopCount < 20; LoopCount++ ) { if ( ControlService( Svc, SERVICE_CONTROL_STOP, &SvcStatus ) == FALSE ) { NetStatus = GetLastError(); // // Special case ERROR_INVALID_SERVICE_CONTROL if // we are stopping netlogon. This error means // that netlogon is in the process of starting // and hasn't yet registered with the service // controller. This may happen, in particular, // if we restarted netlogon on roll back on an // error to unjoin (perhaps because the user creds // supplied were not valid to delete the machine account). // If the unjoin is now retried immediately (this time // without asking to delete the account), we are stopping // netlogon here that may not have started yet. // if ( NetStatus == ERROR_INVALID_SERVICE_CONTROL && _wcsicmp(Service, SERVICE_NETLOGON) == 0 ) { // // Just sleep a second and retry // Sleep( 1000 ); // // It's not an error if the service wasn't running // } else if ( NetStatus == ERROR_SERVICE_NOT_ACTIVE ) { NetStatus = ERROR_SUCCESS; break; } else { NetpLog(( "Stop service '%ws' failed with 0x%lx\n", Service, NetStatus )); break; } } else { NetStatus = ERROR_SUCCESS; break; } } if ( LoopCount == 20 ) { NetpLog(( "NetpStopService: Failed to retry to stop service %ws 0x%lx\n", Service, NetStatus )); } // // Wait for max of 1 min for the service to stop // if ( NetStatus == ERROR_SUCCESS ) { for ( LoopCount = 0; LoopCount < 60; LoopCount++ ) { if( QueryServiceStatus( Svc, &SvcStatus ) == FALSE ) { NetStatus = GetLastError(); } if ( NetStatus != ERROR_SUCCESS || SvcStatus.dwCurrentState == SERVICE_STOPPED) { break; } Sleep( 1000 ); } if ( LoopCount == 60 ) { NetpLog(( "NetpStopService: Failed to wait until service %ws stops 0x%lx\n", Service, NetStatus )); } } } else { NetpLog(( "Failed to enumerate dependentServices for '%ws': 0x%lx\n", Service, NetStatus )); } CloseServiceHandle( Svc ); } 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) { NetpLog(( "NetpValidateName: invalid null name passed\n" )); NetSetuppCloseLog( ); return 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: NetpLog(( "NetpValidateName: invalid name type %lu\n", NameType )); NetSetuppCloseLog( ); return ERROR_INVALID_PARAMETER; } 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 [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 [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, &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 lpMachine, IN LPWSTR lpPassword ) /*++ Routine Description: Sets the password on the machine account object Arguments: lpDcName -- Name of a DC in 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_1003 UserInfo1003 = {NULL}; // // Build the machine account name // NetStatus = NetpGetMachineAccountName(lpMachine, &lpMachAcc); // // Now, either create or delete it // if ( NetStatus == NERR_Success ) { UserInfo1003.usri1003_password = lpPassword; NetStatus = NetUserSetInfo( lpDc, lpMachAcc, 1003, (PBYTE) &UserInfo1003, NULL ); if ( NetStatus != NERR_Success ) { NetpLog(( "NetpSetMachineAccountPassword: NetUserSetInfo (level 1003) on '%ws' for '%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; } // // Remove DNS registrations. // Do this before changing this machine identification // locally and in the domain because DNS deregistration // may be secure // if ( FLAG_ON ( dwJoinAction, NJA_RemoveDnsRegistrations ) && ( fIgnoreErrors || ( NetStatus == NERR_Success ) ) ) { NetStatus2 = NetpUpdateDnsRegistrations( FALSE ); NetpLog(( "NetpApplyJoinState: NON FATAL: status of removing DNS registrations: 0x%lx\n", NetStatus2 )); if (fSaveRollbackInfo && (NetStatus2 == NERR_Success)) { dwRollbackAction |= NJA_AddDnsRegistrations; } } if (fIgnoreErrors || (NetStatus == NERR_Success)) { // // read our old primary domain info // NetStatus = NetpGetLsaPrimaryDomain( 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(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( &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( 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( 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, 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(pJoinState->pPolicyPDI->Name.Buffer, pJoinState->pPolicyPDI->Sid, pJoinState->pPolicyDDI, &hLsaLocal); 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; } // // Add DNS registrations. // Do this after the machine account is set locally // and in teh domain because DNS upodates may be secure. // if ( FLAG_ON ( dwJoinAction, NJA_AddDnsRegistrations ) && ( fIgnoreErrors || ( NetStatus == NERR_Success ) ) ) { NetStatus2 = NetpUpdateDnsRegistrations( TRUE ); NetpLog(( "NetpApplyJoinState: NON FATAL: status of adding DNS registrations: 0x%lx\n", NetStatus2 )); if (fSaveRollbackInfo && (NetStatus2 == NERR_Success)) { dwRollbackAction |= NJA_RemoveDnsRegistrations; } } 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; }