Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2723 lines
74 KiB

  1. /*++
  2. Copyright (c) 1998-1998 Microsoft Corporation
  3. Module Name:
  4. join.c
  5. Abstract:
  6. This module contains the worker routines for the NetJoin APIs
  7. implemented in the Workstation service.
  8. Author:
  9. Mac McLain (macm) 06-Jan-1998
  10. Revision History:
  11. --*/
  12. #include "wsutil.h"
  13. #include "wsconfig.h"
  14. #include <lmerrlog.h>
  15. #include <lmaccess.h>
  16. #define __LMJOIN_H__
  17. #include <netsetup.h>
  18. #include <icanon.h>
  19. #include <crypt.h>
  20. #include <rc4.h>
  21. #include <md5.h>
  22. #if(_WIN32_WINNT >= 0x0500)
  23. #include <dnsapi.h>
  24. #include <ntdsapi.h>
  25. #include <dsgetdc.h>
  26. #include <dsgetdcp.h>
  27. #include <winldap.h>
  28. #include <ntldap.h>
  29. #endif
  30. #if(_WIN32_WINNT < 0x0500)
  31. #include <winreg.h>
  32. #endif
  33. #define COMPUTERNAME_ROOT \
  34. L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\ComputerName"
  35. #define NEW_COMPUTERNAME_VALUE_NAME L"ComputerName"
  36. NET_API_STATUS
  37. JoinpDecryptPasswordWithKey(
  38. IN handle_t RpcBindingHandle,
  39. IN PJOINPR_ENCRYPTED_USER_PASSWORD EncryptedPassword,
  40. IN BOOL EncodePassword,
  41. OUT LPWSTR *EncodedPassword
  42. )
  43. /*++
  44. Routine Description:
  45. Decrypts a password encrypted with the user session key.
  46. Arguments:
  47. RpcBindingHandle - Rpc Binding handle describing the session key to use.
  48. EncryptedPassword - Encrypted password to decrypt.
  49. EncodePassword - If TRUE, the returned password will be encoded
  50. and the first WCHAR of the password buffer will be the seed
  51. EncodedPassword - Returns the (optionally Encoded) password.
  52. Buffer should be freed using NetpMemoryFree.
  53. Return Value:
  54. --*/
  55. {
  56. NET_API_STATUS NetStatus;
  57. NTSTATUS Status;
  58. USER_SESSION_KEY UserSessionKey;
  59. RC4_KEYSTRUCT Rc4Key;
  60. MD5_CTX Md5Context;
  61. UNICODE_STRING EncodedPasswordU;
  62. LPWSTR PasswordPart;
  63. UCHAR Seed = 0;
  64. PJOINPR_USER_PASSWORD Password = (PJOINPR_USER_PASSWORD) EncryptedPassword;
  65. //
  66. // Handle the trivial case
  67. //
  68. *EncodedPassword = NULL;
  69. if ( EncryptedPassword == NULL ) {
  70. return NO_ERROR;
  71. }
  72. //
  73. // Get the session key
  74. //
  75. Status = RtlGetUserSessionKeyServer(
  76. (RPC_BINDING_HANDLE)RpcBindingHandle,
  77. &UserSessionKey );
  78. if (!NT_SUCCESS(Status)) {
  79. return NetpNtStatusToApiStatus( Status );
  80. }
  81. //
  82. // The UserSessionKey is the same for the life of the session. RC4'ing multiple
  83. // strings with a single key is weak (if you crack one you've cracked them all).
  84. // So compute a key that's unique for this particular encryption.
  85. //
  86. //
  87. MD5Init(&Md5Context);
  88. MD5Update( &Md5Context, (LPBYTE)&UserSessionKey, sizeof(UserSessionKey) );
  89. MD5Update( &Md5Context, Password->Obfuscator, sizeof(Password->Obfuscator) );
  90. MD5Final( &Md5Context );
  91. rc4_key( &Rc4Key, MD5DIGESTLEN, Md5Context.digest );
  92. //
  93. // Decrypt the Buffer
  94. //
  95. rc4( &Rc4Key, sizeof(Password->Buffer)+sizeof(Password->Length), (LPBYTE) Password->Buffer );
  96. //
  97. // Check that the length is valid. If it isn't bail here.
  98. //
  99. if (Password->Length > JOIN_MAX_PASSWORD_LENGTH * sizeof(WCHAR)) {
  100. return ERROR_INVALID_PASSWORD;
  101. }
  102. //
  103. // Return the password to the caller.
  104. //
  105. *EncodedPassword = NetpMemoryAllocate( Password->Length + sizeof(WCHAR) + sizeof(WCHAR) );
  106. if ( *EncodedPassword == NULL ) {
  107. return ERROR_NOT_ENOUGH_MEMORY;
  108. }
  109. //
  110. // Copy the password into the buffer
  111. //
  112. // If we are to encode the password, reserve
  113. // the first character for the seed
  114. //
  115. if ( EncodePassword ) {
  116. PasswordPart = ( *EncodedPassword ) + 1;
  117. } else {
  118. PasswordPart = ( *EncodedPassword );
  119. }
  120. RtlCopyMemory( PasswordPart,
  121. ((PCHAR) Password->Buffer) +
  122. (JOIN_MAX_PASSWORD_LENGTH * sizeof(WCHAR)) -
  123. Password->Length,
  124. Password->Length );
  125. PasswordPart[Password->Length/sizeof(WCHAR)] = L'\0';
  126. RtlZeroMemory( ((PCHAR) Password->Buffer) +
  127. (JOIN_MAX_PASSWORD_LENGTH * sizeof(WCHAR)) -
  128. Password->Length,
  129. Password->Length );
  130. //
  131. // Run Encode it so we can pass it around the process with impunity
  132. //
  133. if ( EncodePassword ) {
  134. RtlInitUnicodeString( &EncodedPasswordU, PasswordPart );
  135. RtlRunEncodeUnicodeString( &Seed, &EncodedPasswordU );
  136. *( PWCHAR )( *EncodedPassword ) = ( WCHAR )Seed;
  137. }
  138. return NO_ERROR;
  139. }
  140. NET_API_STATUS
  141. NET_API_FUNCTION
  142. NetrJoinDomain2(
  143. IN handle_t RpcBindingHandle,
  144. IN LPWSTR lpServer OPTIONAL,
  145. IN LPWSTR lpDomain,
  146. IN LPWSTR lpMachineAccountOU,
  147. IN LPWSTR lpAccount OPTIONAL,
  148. IN PJOINPR_ENCRYPTED_USER_PASSWORD EncryptedPassword OPTIONAL,
  149. IN DWORD fOptions
  150. )
  151. /*++
  152. Routine Description:
  153. Joins the machine to the domain.
  154. Arguments:
  155. lpServer -- Name of the machine being run on
  156. lpDomain -- Domain to join
  157. lpMachineAccountOU -- Optional name of the OU under which to create the machine account
  158. lpAccount -- Account to use for join
  159. EncryptedPassword - Encrypted password for lpAccount.
  160. fOptions -- Options to use when joining the domain
  161. Returns:
  162. NERR_Success -- Success
  163. ERROR_INVALID_PARAMETER -- A bad parameter was given
  164. --*/
  165. {
  166. NET_API_STATUS NetStatus = NERR_Success;
  167. LPTSTR ComputerName = NULL;
  168. LPWSTR EncodedPassword = NULL;
  169. //
  170. // Check the parameters we can
  171. //
  172. if (lpDomain == NULL ) {
  173. NetStatus = ERROR_INVALID_PARAMETER;
  174. }
  175. //
  176. // Decrypt the password.
  177. //
  178. if ( NetStatus == NERR_Success ) {
  179. NetStatus = JoinpDecryptPasswordWithKey(
  180. RpcBindingHandle,
  181. EncryptedPassword,
  182. TRUE, // encode the password
  183. &EncodedPassword );
  184. }
  185. //
  186. // Get the current machine name, so we are sure we always have it in flat format
  187. //
  188. if ( NetStatus == NERR_Success ) {
  189. NetStatus = NetpGetComputerName( &ComputerName );
  190. if ( NetStatus == NERR_Success ) {
  191. lpServer = ComputerName;
  192. }
  193. }
  194. //
  195. // Do the impersonation
  196. //
  197. if ( NetStatus == NERR_Success ) {
  198. NetStatus = WsImpersonateClient();
  199. }
  200. //
  201. // Then, see about the join...
  202. //
  203. if ( NetStatus == NERR_Success ) {
  204. NetStatus = NetpDoDomainJoin( lpServer, lpDomain, lpMachineAccountOU, lpAccount,
  205. EncodedPassword, fOptions );
  206. //
  207. // Revert back to ourselves
  208. //
  209. WsRevertToSelf();
  210. }
  211. //
  212. // Write event log stating that we successfully joined the domain/workgroup
  213. //
  214. if ( NetStatus == NERR_Success ) {
  215. LPWSTR StringArray[2];
  216. if ( fOptions & NETSETUP_JOIN_DOMAIN ) {
  217. StringArray[0] = L"domain";
  218. } else {
  219. StringArray[0] = L"workgroup";
  220. }
  221. StringArray[1] = lpDomain;
  222. WsLogEvent( NELOG_Joined_Domain,
  223. EVENTLOG_INFORMATION_TYPE,
  224. 2,
  225. StringArray,
  226. NERR_Success );
  227. }
  228. //
  229. // Free the memory for the machine name if we need to
  230. //
  231. if ( ComputerName != NULL ) {
  232. NetApiBufferFree( ComputerName );
  233. }
  234. if ( EncodedPassword != NULL ) {
  235. NetpMemoryFree( EncodedPassword );
  236. }
  237. return( NetStatus );
  238. }
  239. NET_API_STATUS
  240. NET_API_FUNCTION
  241. NetrUnjoinDomain2(
  242. IN handle_t RpcBindingHandle,
  243. IN LPWSTR lpServer OPTIONAL,
  244. IN LPWSTR lpAccount OPTIONAL,
  245. IN PJOINPR_ENCRYPTED_USER_PASSWORD EncryptedPassword OPTIONAL,
  246. IN DWORD fJoinOptions
  247. )
  248. /*++
  249. Routine Description:
  250. Unjoins from the joined domain
  251. Arguments:
  252. lpServer -- Name of the machine being run on
  253. lpAccount -- Account to use for unjoining
  254. lpPassword -- Password matching the account. The password is encoded. The first WCHAR is
  255. the seed
  256. fOptions -- Options to use when unjoining the domain
  257. Returns:
  258. NERR_Success -- Name is valid
  259. NERR_SetupNotJoined -- This machine was not joined to a domain
  260. NERR_SetupDomainController -- This machine is a domain controller and
  261. cannot be unjoined
  262. NERR_InternalError -- Can't determine product type
  263. --*/
  264. {
  265. NET_API_STATUS NetStatus = NERR_Success;
  266. PPOLICY_PRIMARY_DOMAIN_INFO pPolicyPDI;
  267. PPOLICY_DNS_DOMAIN_INFO pPolicyDNS;
  268. NT_PRODUCT_TYPE ProductType;
  269. LPWSTR EncodedPassword = NULL;
  270. //
  271. // Decrypt the password.
  272. //
  273. NetStatus = JoinpDecryptPasswordWithKey(
  274. RpcBindingHandle,
  275. EncryptedPassword,
  276. TRUE, // encode the password
  277. &EncodedPassword );
  278. if ( NetStatus != NO_ERROR ) {
  279. return NetStatus;
  280. }
  281. //
  282. // Do the impersonation
  283. //
  284. NetStatus = WsImpersonateClient();
  285. //
  286. // First, get the primary domain info... We'll need it later
  287. //
  288. if ( NetStatus == NERR_Success ) {
  289. NetStatus = NetpGetLsaPrimaryDomain( NULL,
  290. NULL,
  291. &pPolicyPDI,
  292. &pPolicyDNS,
  293. NULL );
  294. if ( NetStatus == NERR_Success ) {
  295. if ( !IS_CLIENT_JOINED(pPolicyPDI) ) {
  296. NetStatus = NERR_SetupNotJoined;
  297. } else {
  298. //
  299. // See if it's a DC...
  300. //
  301. if ( RtlGetNtProductType( &ProductType ) == FALSE ) {
  302. NetStatus = NERR_InternalError;
  303. } else {
  304. if ( ProductType == NtProductLanManNt ) {
  305. NetStatus = NERR_SetupDomainController;
  306. }
  307. }
  308. }
  309. //
  310. // Ok, now if all that worked, we'll go ahead and do the removal
  311. //
  312. if ( NetStatus == NERR_Success ) {
  313. NetStatus = NetpUnJoinDomain( pPolicyPDI, lpAccount, EncodedPassword,
  314. fJoinOptions );
  315. }
  316. LsaFreeMemory( pPolicyPDI );
  317. LsaFreeMemory( pPolicyDNS );
  318. }
  319. //
  320. // Revert back to ourselves
  321. //
  322. WsRevertToSelf();
  323. }
  324. if ( EncodedPassword != NULL ) {
  325. NetpMemoryFree( EncodedPassword );
  326. }
  327. return(NetStatus);
  328. }
  329. NET_API_STATUS
  330. NET_API_FUNCTION
  331. NetrValidateName2(
  332. IN handle_t RpcBindingHandle,
  333. IN LPWSTR lpMachine,
  334. IN LPWSTR lpName,
  335. IN LPWSTR lpAccount OPTIONAL,
  336. IN PJOINPR_ENCRYPTED_USER_PASSWORD EncryptedPassword OPTIONAL,
  337. IN NETSETUP_NAME_TYPE NameType
  338. )
  339. /*++
  340. Routine Description:
  341. Ensures that the given name is valid for a name of that type
  342. Arguments:
  343. lpMachine -- Name of the machine being run on
  344. lpName -- Name to validate
  345. lpAccount -- Account to use for name validation
  346. lpPassword -- Password matching the account. The password is encoded. The first WCHAR is
  347. the seed
  348. NameType -- Type of the name to validate
  349. Returns:
  350. NERR_Success -- Name is valid
  351. ERROR_INVALID_PARAMETER -- A bad parameter was given
  352. NERR_InvalidComputer -- The name format given is bad
  353. ERROR_DUP_NAME -- The name is invalid for this type
  354. --*/
  355. {
  356. NET_API_STATUS NetStatus = NERR_Success;
  357. UNICODE_STRING EncodedPasswordU;
  358. UCHAR Seed;
  359. LPWSTR EncodedPassword = NULL;
  360. //
  361. // Decrypt the password.
  362. //
  363. NetStatus = JoinpDecryptPasswordWithKey(
  364. RpcBindingHandle,
  365. EncryptedPassword,
  366. TRUE, // encode the password
  367. &EncodedPassword );
  368. if ( NetStatus != NO_ERROR ) {
  369. return NetStatus;
  370. }
  371. if ( EncodedPassword ) {
  372. Seed = *( PUCHAR )EncodedPassword;
  373. RtlInitUnicodeString( &EncodedPasswordU, EncodedPassword + 1 );
  374. } else {
  375. RtlZeroMemory( &EncodedPasswordU, sizeof( UNICODE_STRING ) );
  376. }
  377. //
  378. // Do the impersonation
  379. //
  380. NetStatus = WsImpersonateClient();
  381. if ( NetStatus == NERR_Success ) {
  382. RtlRunDecodeUnicodeString( Seed, &EncodedPasswordU );
  383. NetStatus = NetpValidateName( lpMachine,
  384. lpName,
  385. lpAccount,
  386. EncodedPasswordU.Buffer,
  387. NameType );
  388. RtlRunEncodeUnicodeString( &Seed, &EncodedPasswordU );
  389. //
  390. // Revert back to ourselves
  391. //
  392. WsRevertToSelf();
  393. }
  394. if ( EncodedPassword != NULL ) {
  395. NetpMemoryFree( EncodedPassword );
  396. }
  397. return( NetStatus );
  398. }
  399. NET_API_STATUS
  400. NET_API_FUNCTION
  401. NetrGetJoinInformation(
  402. IN LPWSTR lpServer OPTIONAL,
  403. OUT LPWSTR *lpNameBuffer,
  404. OUT PNETSETUP_JOIN_STATUS BufferType
  405. )
  406. /*++
  407. Routine Description:
  408. Gets information on the state of the workstation. The information
  409. obtainable is whether the machine is joined to a workgroup or a domain,
  410. and optionally, the name of that workgroup/domain.
  411. Arguments:
  412. lpNameBuffer -- Where the domain/workgroup name is returned.
  413. lpNameBufferSize -- Size of the passed in buffer, in WCHARs. If 0, the
  414. workgroup/domain name is not returned.
  415. BufferType -- Whether the machine is joined to a workgroup or a domain
  416. Returns:
  417. NERR_Success -- Name is valid
  418. ERROR_INVALID_PARAMETER -- A bad parameter was given
  419. ERROR_NOT_ENOUGH_MEMORY -- A memory allocation failed
  420. --*/
  421. {
  422. NET_API_STATUS NetStatus = NERR_Success;
  423. //
  424. // Check the parameters
  425. //
  426. if ( lpNameBuffer == NULL ) {
  427. return( ERROR_INVALID_PARAMETER );
  428. }
  429. //
  430. // Do the impersonation
  431. //
  432. NetStatus = WsImpersonateClient();
  433. if ( NetStatus == NERR_Success ) {
  434. NetStatus = NetpGetJoinInformation( lpServer,
  435. lpNameBuffer,
  436. BufferType );
  437. //
  438. // Revert back to ourselves
  439. //
  440. WsRevertToSelf();
  441. }
  442. return( NetStatus );
  443. }
  444. NET_API_STATUS
  445. NET_API_FUNCTION
  446. NetrRenameMachineInDomain2(
  447. IN handle_t RpcBindingHandle,
  448. IN LPWSTR lpServer OPTIONAL,
  449. IN LPWSTR lpNewMachineName OPTIONAL,
  450. IN LPWSTR lpAccount OPTIONAL,
  451. IN PJOINPR_ENCRYPTED_USER_PASSWORD EncryptedPassword OPTIONAL,
  452. IN DWORD fRenameOptions
  453. )
  454. /*++
  455. Routine Description:
  456. Renames a machine currently joined to a domain.
  457. Arguments:
  458. lpServer -- Name of the machine being run on
  459. lpNewMachineName -- New name for this machine. If the name is specified, it is used
  460. for the new machine name. If it is not specified, it is assumed that SetComputerName
  461. has already been invoked, and that name will be used.
  462. lpAccount -- Account to use for the rename
  463. lpPassword -- Password matching the account. The password has been encoded. The first
  464. WCHAR of the string is the seed.
  465. fOptions -- Options to use for the rename
  466. Returns:
  467. NERR_Success -- Success
  468. ERROR_INVALID_PARAMETER -- A bad parameter was given
  469. --*/
  470. {
  471. NET_API_STATUS NetStatus = NERR_Success;
  472. PPOLICY_PRIMARY_DOMAIN_INFO pPolicyPDI;
  473. PPOLICY_DNS_DOMAIN_INFO pPolicyDNS;
  474. LPTSTR ComputerName = NULL;
  475. LPTSTR NewComputerName = NULL;
  476. LPTSTR DomainName = NULL;
  477. HKEY ComputerNameRootKey;
  478. ULONG Length;
  479. LPWSTR EncodedPassword = NULL;
  480. //
  481. // Get the current machine name
  482. //
  483. NetStatus = NetpGetComputerName( &ComputerName );
  484. if ( NetStatus == NERR_Success ) {
  485. lpServer = ComputerName;
  486. }
  487. //
  488. // Decrypt the password.
  489. //
  490. if ( NetStatus == NERR_Success ) {
  491. NetStatus = JoinpDecryptPasswordWithKey(
  492. RpcBindingHandle,
  493. EncryptedPassword,
  494. TRUE, // encode the password
  495. &EncodedPassword );
  496. }
  497. //
  498. // Get the new machine name if it isn't specified
  499. //
  500. if ( NetStatus == NERR_Success && lpNewMachineName == NULL ) {
  501. NetStatus = NetpGetNewMachineName( &NewComputerName );
  502. lpNewMachineName = NewComputerName;
  503. }
  504. //
  505. // Get the current domain information
  506. //
  507. if ( NetStatus == NERR_Success ) {
  508. NetStatus = NetpGetLsaPrimaryDomain( NULL,
  509. NULL,
  510. &pPolicyPDI,
  511. &pPolicyDNS,
  512. NULL );
  513. if ( NetStatus == NERR_Success ) {
  514. NetStatus = NetApiBufferAllocate( pPolicyPDI->Name.Length + sizeof( WCHAR ),
  515. ( LPVOID * )&DomainName );
  516. if ( NetStatus == NERR_Success ) {
  517. RtlCopyMemory( DomainName, pPolicyPDI->Name.Buffer, pPolicyPDI->Name.Length );
  518. DomainName[ pPolicyPDI->Name.Length / sizeof( WCHAR ) ] = UNICODE_NULL;
  519. }
  520. LsaFreeMemory( pPolicyPDI );
  521. LsaFreeMemory( pPolicyDNS );
  522. }
  523. }
  524. //
  525. // Do the impersonation
  526. //
  527. if ( NetStatus == NERR_Success ) {
  528. NetStatus = WsImpersonateClient();
  529. if ( NetStatus == NERR_Success ) {
  530. //
  531. // A machine rename
  532. //
  533. NetStatus = NetpMachineValidToJoin( lpNewMachineName, TRUE );
  534. if ( NetStatus == NERR_SetupAlreadyJoined ||
  535. NetStatus == NERR_SetupDomainController ) { // Allow DC rename
  536. NetStatus = NetpChangeMachineName( lpServer,
  537. lpNewMachineName,
  538. DomainName,
  539. lpAccount,
  540. EncodedPassword,
  541. fRenameOptions );
  542. if ( NetStatus == NERR_Success && lpNewMachineName ) {
  543. if ( SetComputerNameEx( ComputerNamePhysicalDnsHostname,
  544. lpNewMachineName ) == FALSE ) {
  545. NetStatus = GetLastError();
  546. }
  547. }
  548. } else if ( NetStatus == NERR_Success ) {
  549. NetStatus = NERR_SetupNotJoined;
  550. }
  551. //
  552. // Revert back to ourselves
  553. //
  554. WsRevertToSelf();
  555. }
  556. }
  557. //
  558. // Free the memory for the machine name(s) if we need to
  559. //
  560. if ( ComputerName != NULL ) {
  561. NetApiBufferFree( ComputerName );
  562. }
  563. if ( NewComputerName != NULL ) {
  564. NetApiBufferFree( NewComputerName );
  565. }
  566. if ( DomainName != NULL ) {
  567. NetApiBufferFree( DomainName );
  568. }
  569. if ( EncodedPassword != NULL ) {
  570. NetpMemoryFree( EncodedPassword );
  571. }
  572. return( NetStatus );
  573. }
  574. NET_API_STATUS
  575. NET_API_FUNCTION
  576. NetrGetJoinableOUs2(
  577. IN handle_t RpcBindingHandle,
  578. IN LPWSTR lpServer OPTIONAL,
  579. IN LPWSTR lpDomain,
  580. IN LPWSTR lpAccount OPTIONAL,
  581. IN PJOINPR_ENCRYPTED_USER_PASSWORD EncryptedPassword OPTIONAL,
  582. OUT DWORD *OUCount,
  583. OUT LPWSTR **OUs
  584. )
  585. /*++
  586. Routine Description:
  587. Renames a machine currently joined to a domain.
  588. Arguments:
  589. lpServer -- Name of the machine being run on
  590. lpDomain -- Domain to join
  591. lpAccount -- Account to use for join
  592. lpPassword -- Password matching the account. The password has been encoded and the first
  593. WCHAR of the name is the seed
  594. MachineAccountOUs -- Where the information is returned.
  595. Returns:
  596. NERR_Success -- Success
  597. ERROR_INVALID_PARAMETER -- A bad parameter was given
  598. --*/
  599. {
  600. NET_API_STATUS NetStatus = NERR_Success;
  601. LPWSTR EncodedPassword = NULL;
  602. NetStatus = WsImpersonateClient();
  603. //
  604. // Decrypt the password.
  605. //
  606. if ( NetStatus == NERR_Success ) {
  607. NetStatus = JoinpDecryptPasswordWithKey(
  608. RpcBindingHandle,
  609. EncryptedPassword,
  610. TRUE, // encode the password
  611. &EncodedPassword );
  612. }
  613. if ( NetStatus == NERR_Success ) {
  614. //
  615. // Read the current information
  616. //
  617. NetStatus = NetpGetListOfJoinableOUs( lpDomain,
  618. lpAccount,
  619. EncodedPassword,
  620. OUCount,
  621. OUs );
  622. }
  623. //
  624. // Revert back to ourselves
  625. //
  626. WsRevertToSelf();
  627. if ( EncodedPassword != NULL ) {
  628. NetpMemoryFree( EncodedPassword );
  629. }
  630. return( NetStatus );
  631. }
  632. NET_API_STATUS
  633. NET_API_FUNCTION
  634. NetrJoinDomain(
  635. IN LPWSTR lpServer OPTIONAL,
  636. IN LPWSTR lpDomain,
  637. IN LPWSTR lpMachineAccountOU,
  638. IN LPWSTR lpAccount OPTIONAL,
  639. IN LPWSTR lpPassword OPTIONAL,
  640. IN DWORD fOptions
  641. )
  642. /*++
  643. Routine Description:
  644. Joins the machine to the domain.
  645. Arguments:
  646. lpServer -- Name of the machine being run on
  647. lpDomain -- Domain to join
  648. lpMachineAccountOU -- Optional name of the OU under which to create the machine account
  649. lpAccount -- Account to use for join
  650. lpPassword -- Password matching the account. The password is encoded. The first WCHAR
  651. is the seed.
  652. fOptions -- Options to use when joining the domain
  653. Returns:
  654. NERR_Success -- Success
  655. ERROR_INVALID_PARAMETER -- A bad parameter was given
  656. --*/
  657. {
  658. //
  659. // This version that takes a clear text password isn't supported.
  660. //
  661. return ERROR_NOT_SUPPORTED;
  662. }
  663. NET_API_STATUS
  664. NET_API_FUNCTION
  665. NetrUnjoinDomain(
  666. IN LPWSTR lpServer OPTIONAL,
  667. IN LPWSTR lpAccount OPTIONAL,
  668. IN LPWSTR lpPassword OPTIONAL,
  669. IN DWORD fJoinOptions
  670. )
  671. /*++
  672. Routine Description:
  673. Unjoins from the joined domain
  674. Arguments:
  675. lpServer -- Name of the machine being run on
  676. lpAccount -- Account to use for unjoining
  677. lpPassword -- Password matching the account. The password is encoded. The first WCHAR is
  678. the seed
  679. fOptions -- Options to use when unjoining the domain
  680. Returns:
  681. NERR_Success -- Name is valid
  682. NERR_SetupNotJoined -- This machine was not joined to a domain
  683. NERR_SetupDomainController -- This machine is a domain controller and
  684. cannot be unjoined
  685. NERR_InternalError -- Can't determine product type
  686. --*/
  687. {
  688. //
  689. // This version that takes a clear text password isn't supported.
  690. //
  691. return ERROR_NOT_SUPPORTED;
  692. }
  693. NET_API_STATUS
  694. NET_API_FUNCTION
  695. NetrValidateName(
  696. IN LPWSTR lpMachine,
  697. IN LPWSTR lpName,
  698. IN LPWSTR lpAccount OPTIONAL,
  699. IN LPWSTR lpPassword OPTIONAL,
  700. IN NETSETUP_NAME_TYPE NameType
  701. )
  702. /*++
  703. Routine Description:
  704. Ensures that the given name is valid for a name of that type
  705. Arguments:
  706. lpMachine -- Name of the machine being run on
  707. lpName -- Name to validate
  708. lpAccount -- Account to use for name validation
  709. lpPassword -- Password matching the account. The password is encoded. The first WCHAR is
  710. the seed
  711. NameType -- Type of the name to validate
  712. Returns:
  713. NERR_Success -- Name is valid
  714. ERROR_INVALID_PARAMETER -- A bad parameter was given
  715. NERR_InvalidComputer -- The name format given is bad
  716. ERROR_DUP_NAME -- The name is invalid for this type
  717. --*/
  718. {
  719. //
  720. // This version that takes a clear text password isn't supported.
  721. //
  722. return ERROR_NOT_SUPPORTED;
  723. }
  724. NET_API_STATUS
  725. NET_API_FUNCTION
  726. NetrRenameMachineInDomain(
  727. IN LPWSTR lpServer OPTIONAL,
  728. IN LPWSTR lpNewMachineName OPTIONAL,
  729. IN LPWSTR lpAccount OPTIONAL,
  730. IN LPWSTR lpPassword OPTIONAL,
  731. IN DWORD fRenameOptions
  732. )
  733. /*++
  734. Routine Description:
  735. Renames a machine currently joined to a domain.
  736. Arguments:
  737. lpServer -- Name of the machine being run on
  738. lpNewMachineName -- New name for this machine. If the name is specified, it is used
  739. for the new machine name. If it is not specified, it is assumed that SetComputerName
  740. has already been invoked, and that name will be used.
  741. lpAccount -- Account to use for the rename
  742. lpPassword -- Password matching the account. The password has been encoded. The first
  743. WCHAR of the string is the seed.
  744. fOptions -- Options to use for the rename
  745. Returns:
  746. NERR_Success -- Success
  747. ERROR_INVALID_PARAMETER -- A bad parameter was given
  748. --*/
  749. {
  750. //
  751. // This version that takes a clear text password isn't supported.
  752. //
  753. return ERROR_NOT_SUPPORTED;
  754. }
  755. NET_API_STATUS
  756. NET_API_FUNCTION
  757. NetrGetJoinableOUs(
  758. IN LPWSTR lpServer OPTIONAL,
  759. IN LPWSTR lpDomain,
  760. IN LPWSTR lpAccount OPTIONAL,
  761. IN LPWSTR lpPassword OPTIONAL,
  762. OUT DWORD *OUCount,
  763. OUT LPWSTR **OUs
  764. )
  765. /*++
  766. Routine Description:
  767. Renames a machine currently joined to a domain.
  768. Arguments:
  769. lpServer -- Name of the machine being run on
  770. lpDomain -- Domain to join
  771. lpAccount -- Account to use for join
  772. lpPassword -- Password matching the account. The password has been encoded and the first
  773. WCHAR of the name is the seed
  774. MachineAccountOUs -- Where the information is returned.
  775. Returns:
  776. NERR_Success -- Success
  777. ERROR_INVALID_PARAMETER -- A bad parameter was given
  778. --*/
  779. {
  780. //
  781. // This version that takes a clear text password isn't supported.
  782. //
  783. return ERROR_NOT_SUPPORTED;
  784. }
  785. //
  786. // Computer rename preparation APIs
  787. //
  788. NET_API_STATUS
  789. NetpSetPrimarySamAccountName(
  790. IN LPWSTR DomainController,
  791. IN LPWSTR CurrentSamAccountName,
  792. IN LPWSTR NewSamAccountName,
  793. IN LPWSTR DomainAccountName,
  794. IN LPWSTR DomainAccountPassword
  795. )
  796. /*++
  797. Routine Description:
  798. Sets primary SAM account name and teh display name on the
  799. computer object in the DS.
  800. Arguments:
  801. DomainController -- DC name where to modify the computer object.
  802. CurrentSamAccountName -- The current value of SAM account name.
  803. NewSamAccountName -- The new value of SAM account name to be set.
  804. DomainAccount -- Domain account to use for accessing the machine
  805. account object in the DS. May be NULL in which case the
  806. credentials of the user executing this routine are used.
  807. DomainAccountPassword -- Password matching the domain account.
  808. May be NULL in which case the credentials of the user executing
  809. this routine are used.
  810. Note:
  811. This routine uses NetUserSetInfo, downlevel SAM based API.
  812. NetUserSetInfo has an advantage such that it updates the DN of the
  813. computer object to correspond to the new SAM account name. Also,
  814. for a DC's computer object, it follows the serverReferenceBL attribute
  815. link and updates the DN of the server object in the Config container.
  816. The server object in the Config container, in its turn, has a reference
  817. to the computer object (serverReference attrib) -- that reference also
  818. gets updated as the result of NetUserSetInfo call. Updating the two
  819. objects through ldap (instead of NetuserSetInfo) currently can't be done
  820. as one transaction, so we use NetUserSetInfo to do all this for us. We
  821. may reconsider the use of ldap once transactional ldap (i.e. several
  822. ldap operations performed as one transaction) becomes available.
  823. Returns:
  824. NO_ERROR -- Success
  825. Otherwise, error returned by NetUserSetInfo.
  826. --*/
  827. {
  828. NET_API_STATUS NetStatus = NO_ERROR;
  829. NET_API_STATUS TmpNetStatus = NO_ERROR;
  830. USER_INFO_0 NetUI0;
  831. PUSER_INFO_10 usri10 = NULL;
  832. BOOLEAN Connected = FALSE;
  833. //
  834. // Connect to the DC
  835. //
  836. NetStatus = NetpManageIPCConnect( DomainController,
  837. DomainAccountName,
  838. DomainAccountPassword,
  839. NETSETUPP_CONNECT_IPC );
  840. if ( NetStatus != NO_ERROR ) {
  841. NetpLog(( "NetpSetPrimarySamAccountName: NetpManageIPCConnect failed to connect to %ws: 0x%lx\n",
  842. DomainController,
  843. NetStatus ));
  844. goto Cleanup;
  845. }
  846. Connected = TRUE;
  847. //
  848. // Set the SAM account name
  849. //
  850. NetUI0.usri0_name = NewSamAccountName;
  851. NetStatus = NetUserSetInfo( DomainController,
  852. CurrentSamAccountName,
  853. 0,
  854. (PBYTE)&NetUI0,
  855. NULL );
  856. if ( NetStatus != NO_ERROR ) {
  857. NetpLog(( "NetpSetPrimarySamAccountName: NetUserSetInfo failed on %ws: 0x%lx\n",
  858. DomainController,
  859. NetStatus ));
  860. goto Cleanup;
  861. }
  862. //
  863. // Update the display name as well.
  864. // Ignore error as this is not critical.
  865. //
  866. // First get the current display name
  867. //
  868. TmpNetStatus = NetUserGetInfo( DomainController,
  869. NewSamAccountName,
  870. 10,
  871. (PBYTE *)&usri10 );
  872. if ( TmpNetStatus != NERR_Success ) {
  873. NetpLog(( "NetpSetPrimarySamAccountName: failed to get display name on %ws (ignored) 0x%lx\n",
  874. DomainController,
  875. TmpNetStatus ));
  876. //
  877. // If the display name exists and is
  878. // different from the new one, update it
  879. //
  880. } else if ( usri10->usri10_full_name != NULL &&
  881. _wcsicmp(usri10->usri10_full_name, NewSamAccountName) != 0 ) {
  882. USER_INFO_1011 usri1011;
  883. usri1011.usri1011_full_name = NewSamAccountName; // new name
  884. TmpNetStatus = NetUserSetInfo( DomainController,
  885. NewSamAccountName,
  886. 1011,
  887. (PBYTE)&usri1011,
  888. NULL );
  889. if ( TmpNetStatus != NERR_Success ) {
  890. NetpLog(( "NetpSetPrimarySamAccountName: failed to update display name on %ws (ignored) 0x%lx\n",
  891. DomainController,
  892. TmpNetStatus ));
  893. }
  894. }
  895. Cleanup:
  896. if ( usri10 != NULL ) {
  897. NetApiBufferFree( usri10 );
  898. }
  899. if ( Connected ) {
  900. TmpNetStatus = NetpManageIPCConnect( DomainController,
  901. DomainAccountName,
  902. DomainAccountPassword,
  903. NETSETUPP_DISCONNECT_IPC );
  904. if ( TmpNetStatus != NO_ERROR ) {
  905. NetpLog(( "NetpSetPrimarySamAccountName: NetpManageIPCConnect failed to disconnect from %ws: 0x%lx\n",
  906. DomainController,
  907. TmpNetStatus ));
  908. }
  909. }
  910. return NetStatus;
  911. }
  912. #define NET_ADD_ALTERNATE_COMPUTER_NAME 1
  913. #define NET_DEL_ALTERNATE_COMPUTER_NAME 2
  914. #define NET_SET_PRIMARY_COMPUTER_NAME 3
  915. NET_API_STATUS
  916. NET_API_FUNCTION
  917. NetpManageAltComputerName(
  918. IN handle_t RpcBindingHandle,
  919. IN LPWSTR AlternateName,
  920. IN ULONG Action,
  921. IN LPWSTR DomainAccount OPTIONAL,
  922. IN PJOINPR_ENCRYPTED_USER_PASSWORD EncryptedPassword OPTIONAL,
  923. IN ULONG Reserved
  924. )
  925. /*++
  926. Routine Description:
  927. Manages the alternate names for the specified server.
  928. Arguments:
  929. AlternateName -- The name to add.
  930. Action -- Specifies action to take on the name:
  931. NET_ADD_ALTERNATE_COMPUTER_NAME - Add the alternate name.
  932. NET_DEL_ALTERNATE_COMPUTER_NAME - Delete the alternate name.
  933. NET_SET_PRIMARY_COMPUTER_NAME - Set the alternate name as
  934. the primary computer name.
  935. DomainAccount -- Domain account to use for accessing the
  936. machine account object for the specified server in the AD.
  937. Not used if the server is not joined to a domain. May be
  938. NULL in which case the credentials of the user executing
  939. this routine are used.
  940. DomainAccountPassword -- Password matching the domain account.
  941. Not used if the server is not joined to a domain. May be
  942. NULL in which case the credentials of the user executing
  943. this routine are used.
  944. Reserved -- Reserved for future use. If some flags are specified
  945. that are not supported, they will be ignored if
  946. NET_IGNORE_UNSUPPORTED_FLAGS is set, otherwise this routine
  947. will fail with ERROR_INVALID_FLAGS.
  948. Note:
  949. The process that calls this routine must have administrator
  950. privileges on the local computer to perform the local computer
  951. name modifications. The access check is performed by the local
  952. information APIs.
  953. Returns:
  954. NO_ERROR -- Success
  955. ERROR_NOT_SUPPORTED -- The specified server does not support this
  956. functionality.
  957. ERROR_INVALID_FLAGS - The Flags parameter is incorrect.
  958. --*/
  959. {
  960. NET_API_STATUS NetStatus = NO_ERROR;
  961. NTSTATUS Status = STATUS_SUCCESS;
  962. ULONG LdapStatus = LDAP_SUCCESS;
  963. NT_PRODUCT_TYPE NtProductType;
  964. LSA_HANDLE LocalPolicyHandle = NULL;
  965. PPOLICY_DNS_DOMAIN_INFO LocalPolicyDns = NULL;
  966. ULONG PrimaryNameSize = DNS_MAX_NAME_BUFFER_LENGTH * sizeof(WCHAR);
  967. WCHAR NewNetbiosMachineName[MAX_COMPUTERNAME_LENGTH + 1];
  968. LPWSTR ComputerName = NULL;
  969. LPWSTR MachineAccountName = NULL;
  970. LPWSTR NewMachineAccountName = NULL;
  971. LPWSTR AccountUserName = NULL;
  972. LPWSTR AccountDomainName = NULL;
  973. LPWSTR DomainAccountPassword = NULL;
  974. LPWSTR MachineAccountNameToCrack = NULL; // not allocated
  975. LPWSTR NameToCrack = NULL;
  976. LPWSTR PrimaryName = NULL;
  977. BOOLEAN ClientImpersonated = FALSE;
  978. BOOLEAN NameModifiedLocally = FALSE;
  979. BOOLEAN PrimarySamAccountNameSet = FALSE;
  980. BOOLEAN ToldNetlogonToAvoidDnsHostNameUpdate = FALSE;
  981. BOOLEAN StopedNetlogon = FALSE;
  982. SERVICE_STATUS NetlogonServiceStatus;
  983. LPQUERY_SERVICE_CONFIG NetlogonServiceConfig = NULL;
  984. RPC_AUTH_IDENTITY_HANDLE AuthId = 0;
  985. HANDLE hDs = NULL;
  986. PLDAP LdapHandle = NULL;
  987. LONG LdapOption;
  988. OBJECT_ATTRIBUTES OA;
  989. PDS_NAME_RESULTW CrackedName = NULL;
  990. PDOMAIN_CONTROLLER_INFOW DcInfo = NULL;
  991. PWSTR AlternateNameValues[2];
  992. PWSTR DnsHostNameValues[2];
  993. PWSTR PrimaryNameValues[2];
  994. LDAPModW DnsHostNameMod;
  995. LDAPModW PrimaryNameMod;
  996. LDAPModW AlternateDnsHostNameMod;
  997. LDAPModW *ModList[4] = {NULL};
  998. ULONG ModCount = 0;
  999. SEC_WINNT_AUTH_IDENTITY AuthIdent = {0};
  1000. //
  1001. // Ldap modify server control
  1002. //
  1003. // An LDAP modify request will normally fail if it attempts
  1004. // to add an attribute that already exists, or if it attempts
  1005. // to delete an attribute that does not exist. With this control,
  1006. // as long as the attribute to be added has the same value as
  1007. // the existing attribute, then the modify will succeed. With
  1008. // this control, deletion of an attribute that doesn't exist
  1009. // will also succeed.
  1010. //
  1011. LDAPControlW ModifyControl =
  1012. {
  1013. LDAP_SERVER_PERMISSIVE_MODIFY_OID_W,
  1014. {
  1015. 0, NULL
  1016. },
  1017. FALSE
  1018. };
  1019. PLDAPControlW ModifyControlArray[2] =
  1020. {
  1021. &ModifyControl,
  1022. NULL
  1023. };
  1024. //
  1025. // Initialize the log file
  1026. //
  1027. NetSetuppOpenLog();
  1028. NetpLog(( "NetpManageAltComputerName called:\n" ));
  1029. NetpLog(( " AlternateName = %ws\n", AlternateName ));
  1030. NetpLog(( " DomainAccount = %ws\n", DomainAccount ));
  1031. NetpLog(( " Action = 0x%lx\n", Action ));
  1032. NetpLog(( " Flags = 0x%lx\n", Reserved ));
  1033. //
  1034. // This API is supported on DCs and servers only
  1035. //
  1036. if ( !RtlGetNtProductType( &NtProductType ) ) {
  1037. NtProductType = NtProductWinNt;
  1038. }
  1039. if ( NtProductType != NtProductServer &&
  1040. NtProductType != NtProductLanManNt ) {
  1041. NetpLog(( "NetpManageAltComputerName: Not supported on wksta: %lu\n",
  1042. NtProductType ));
  1043. NetStatus = ERROR_NOT_SUPPORTED;
  1044. goto Cleanup;
  1045. }
  1046. //
  1047. // Validate the Flags
  1048. //
  1049. // If some flags are passed which we don't understand
  1050. // and we are not told to ignore them, error out.
  1051. //
  1052. if ( Reserved != 0 &&
  1053. (Reserved & NET_IGNORE_UNSUPPORTED_FLAGS) == 0 ) {
  1054. NetpLog(( "NetpManageAltComputerName: Invalid Flags passed\n" ));
  1055. NetStatus = ERROR_INVALID_FLAGS;
  1056. goto Cleanup;
  1057. }
  1058. //
  1059. // Validate the alternate name passed
  1060. //
  1061. NetStatus = DnsValidateName_W( AlternateName, DnsNameHostnameFull );
  1062. if ( NetStatus != NO_ERROR && NetStatus != DNS_ERROR_NON_RFC_NAME ) {
  1063. NetpLog(( "NetpManageAltComputerName: DnsValidateName failed: 0x%lx\n",
  1064. NetStatus ));
  1065. goto Cleanup;
  1066. }
  1067. //
  1068. // Decrypt the domain account password
  1069. //
  1070. NetStatus = JoinpDecryptPasswordWithKey(
  1071. RpcBindingHandle,
  1072. EncryptedPassword,
  1073. FALSE, // don't encode the password
  1074. &DomainAccountPassword );
  1075. if ( NetStatus != NO_ERROR ) {
  1076. NetpLog(( "NetpManageAltComputerName: JoinpDecryptPasswordWithKey failed: 0x%lx\n",
  1077. NetStatus ));
  1078. goto Cleanup;
  1079. }
  1080. //
  1081. // If there is no domain account passed,
  1082. // ignore the domain account password (if any)
  1083. //
  1084. if ( DomainAccount == NULL &&
  1085. DomainAccountPassword != NULL ) {
  1086. NetpMemoryFree( DomainAccountPassword );
  1087. DomainAccountPassword = NULL;
  1088. }
  1089. //
  1090. // Separate the domain account into
  1091. // the user and domain parts for later use
  1092. //
  1093. if ( DomainAccount != NULL ) {
  1094. NetStatus = NetpSeparateUserAndDomain( DomainAccount,
  1095. &AccountUserName,
  1096. &AccountDomainName );
  1097. }
  1098. if ( NetStatus != NERR_Success ) {
  1099. NetpLog(( "NetpGetComputerObjectDn: Cannot NetpSeparateUserAndDomain 0x%lx\n",
  1100. NetStatus ));
  1101. goto Cleanup;
  1102. }
  1103. //
  1104. // Get the current Netbios machine name
  1105. //
  1106. NetStatus = NetpGetComputerName( &ComputerName );
  1107. if ( NetStatus != NO_ERROR ) {
  1108. NetpLog(( "NetpManageAltComputerName: NetpGetComputerName failed: 0x%lx\n",
  1109. NetStatus ));
  1110. goto Cleanup;
  1111. }
  1112. //
  1113. // Get SAM machine account name from the Netbios machine name
  1114. //
  1115. NetStatus = NetpGetMachineAccountName( ComputerName, &MachineAccountName );
  1116. if ( NetStatus != NO_ERROR ) {
  1117. NetpLog(( "NetpManageAltComputerName: NetpGetMachineAccountName failed: 0x%lx\n",
  1118. NetStatus ));
  1119. goto Cleanup;
  1120. }
  1121. //
  1122. // Get the current primary DNS host name
  1123. //
  1124. PrimaryName = LocalAlloc( LMEM_ZEROINIT, PrimaryNameSize );
  1125. if ( PrimaryName == NULL ) {
  1126. NetpLog(( "NetpManageAltComputerName: LocalAlloc for PrimaryName failed\n" ));
  1127. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1128. goto Cleanup;
  1129. }
  1130. NetStatus = EnumerateLocalComputerNamesW(
  1131. PrimaryComputerName, // type of name
  1132. 0, // reserved
  1133. PrimaryName,
  1134. &PrimaryNameSize );
  1135. if ( NetStatus != NO_ERROR ) {
  1136. NetpLog(( "NetpManageAltComputerName: EnumerateLocalComputerNamesW failed with Size 0x%lx: 0x%lx\n",
  1137. PrimaryNameSize,
  1138. NetStatus ));
  1139. goto Cleanup;
  1140. }
  1141. //
  1142. // If we are to rename the machine,
  1143. // get the new machine account name from
  1144. // the DNS name passed
  1145. //
  1146. if ( Action == NET_SET_PRIMARY_COMPUTER_NAME ) {
  1147. ULONG Size = MAX_COMPUTERNAME_LENGTH + 1;
  1148. if ( !DnsHostnameToComputerNameW(AlternateName,
  1149. NewNetbiosMachineName,
  1150. &Size) ) {
  1151. NetStatus = GetLastError();
  1152. NetpLog(( "NetpManageAltComputerName: DnsHostnameToComputerNameW failed: 0x%lx\n",
  1153. NetStatus ));
  1154. goto Cleanup;
  1155. }
  1156. //
  1157. // Get the new SAM machine account name from the new Netbios machine name
  1158. //
  1159. NetStatus = NetpGetMachineAccountName( NewNetbiosMachineName, &NewMachineAccountName );
  1160. if ( NetStatus != NO_ERROR ) {
  1161. NetpLog(( "NetpManageAltComputerName: NetpGetMachineAccountName (2) failed: 0x%lx\n",
  1162. NetStatus ));
  1163. goto Cleanup;
  1164. }
  1165. }
  1166. //
  1167. // Open the local LSA policy
  1168. //
  1169. InitializeObjectAttributes( &OA, NULL, 0, NULL, NULL );
  1170. Status = LsaOpenPolicy( NULL,
  1171. &OA,
  1172. MAXIMUM_ALLOWED,
  1173. &LocalPolicyHandle );
  1174. if ( !NT_SUCCESS(Status) ) {
  1175. NetpLog(( "NetpManageAltComputerName: LsaOpenPolicy failed: 0x%lx\n",
  1176. Status ));
  1177. NetStatus = NetpNtStatusToApiStatus( Status );
  1178. goto Cleanup;
  1179. }
  1180. //
  1181. // Get the current domain information from LSA
  1182. //
  1183. Status = LsaQueryInformationPolicy( LocalPolicyHandle,
  1184. PolicyDnsDomainInformation,
  1185. (PVOID *) &LocalPolicyDns );
  1186. if ( !NT_SUCCESS(Status) ) {
  1187. NetpLog(( "NetpManageAltComputerName: LsaQueryInformationPolicy failed: 0x%lx\n",
  1188. Status ));
  1189. NetStatus = NetpNtStatusToApiStatus( Status );
  1190. goto Cleanup;
  1191. }
  1192. //
  1193. // Do the local opperation for the specified alternate name
  1194. //
  1195. // Impersonate the caller. The local API will perform the
  1196. // access check on the caller on our behalf.
  1197. //
  1198. NetStatus = WsImpersonateClient();
  1199. if ( NetStatus != NO_ERROR ) {
  1200. NetpLog(( "NetpManageAltComputerName: WsImpersonateClient failed: 0x%lx\n",
  1201. NetStatus ));
  1202. goto Cleanup;
  1203. }
  1204. ClientImpersonated = TRUE;
  1205. //
  1206. // Do local operations
  1207. //
  1208. if ( Action == NET_ADD_ALTERNATE_COMPUTER_NAME ) {
  1209. NetStatus = AddLocalAlternateComputerName( AlternateName,
  1210. 0 ); // reserved
  1211. if ( NetStatus != NO_ERROR ) {
  1212. NetpLog(( "NetpManageAltComputerName: AddLocalAlternateComputerName failed 0x%lx\n",
  1213. NetStatus ));
  1214. goto Cleanup;
  1215. }
  1216. } else if ( Action == NET_DEL_ALTERNATE_COMPUTER_NAME ) {
  1217. NetStatus = RemoveLocalAlternateComputerName( AlternateName,
  1218. 0 ); // reserved
  1219. if ( NetStatus != NO_ERROR ) {
  1220. NetpLog(( "NetpManageAltComputerName: RemoveLocalAlternateComputerName failed 0x%lx\n",
  1221. NetStatus ));
  1222. goto Cleanup;
  1223. }
  1224. } else if ( Action == NET_SET_PRIMARY_COMPUTER_NAME ) {
  1225. NetStatus = SetLocalPrimaryComputerName( AlternateName,
  1226. 0 ); // reserved
  1227. if ( NetStatus != NO_ERROR ) {
  1228. NetpLog(( "NetpManageAltComputerName: SetLocalPrimaryComputerName failed 0x%lx\n",
  1229. NetStatus ));
  1230. goto Cleanup;
  1231. }
  1232. }
  1233. NameModifiedLocally = TRUE;
  1234. //
  1235. // We are done with local operations; we are going
  1236. // to do remote operations on the DC next.
  1237. //
  1238. // If the user credentials are supplied, revert
  1239. // the impersonation -- we will access the remote
  1240. // server with explicit credentials supplied.
  1241. // Otherwise, access the DC while running in the
  1242. // user context.
  1243. //
  1244. if ( DomainAccount != NULL ) {
  1245. WsRevertToSelf();
  1246. ClientImpersonated = FALSE;
  1247. }
  1248. //
  1249. // If this machine is not joined to an AD domain,
  1250. // there is nothing to do on the DC.
  1251. //
  1252. if ( LocalPolicyDns->Sid == NULL ||
  1253. LocalPolicyDns->DnsDomainName.Length == 0 ) {
  1254. NetStatus = NO_ERROR;
  1255. goto Cleanup;
  1256. }
  1257. //
  1258. // Discover a DC for the domain of this machine
  1259. // to modify the computer object in the DS.
  1260. //
  1261. NetStatus = DsGetDcNameWithAccountW(
  1262. NULL,
  1263. MachineAccountName,
  1264. UF_WORKSTATION_TRUST_ACCOUNT | UF_SERVER_TRUST_ACCOUNT,
  1265. NULL,
  1266. NULL,
  1267. NULL,
  1268. DS_DIRECTORY_SERVICE_REQUIRED | DS_RETURN_DNS_NAME,
  1269. &DcInfo );
  1270. if ( NetStatus != NO_ERROR ) {
  1271. NetpLog(( "NetpManageAltComputerName: DsGetDcNameWithAccountW failed 0x%lx\n",
  1272. NetStatus ));
  1273. goto Cleanup;
  1274. }
  1275. //
  1276. // If this machine is a DC, verify that we got it.
  1277. // We do this because we want to avoid inconsistent
  1278. // state where the local name stored in registry
  1279. // is different from name stored locally in the DS.
  1280. //
  1281. if ( NtProductType == NtProductLanManNt &&
  1282. !DnsNameCompare_W(PrimaryName, DcInfo->DomainControllerName+2) ) {
  1283. NetpLog(( "NetpManageAltComputerName: Got different DC '%ws' than local DC '%ws'\n",
  1284. DcInfo->DomainControllerName+2,
  1285. PrimaryName ));
  1286. NetStatus = ERROR_NO_SUCH_DOMAIN;
  1287. goto Cleanup;
  1288. }
  1289. //
  1290. // We've got a DC. Bind to the DS to get the DN
  1291. // for our machine account and do a LDAP connect
  1292. // to modify our machine account given the DN
  1293. //
  1294. NetStatus = DsMakePasswordCredentials( AccountUserName,
  1295. AccountDomainName,
  1296. DomainAccountPassword,
  1297. &AuthId );
  1298. if ( NetStatus != NERR_Success ) {
  1299. NetpLog(( "NetpManageAltComputerName: DsMakePasswordCredentials failed 0x%lx\n",
  1300. NetStatus ));
  1301. goto Cleanup;
  1302. }
  1303. //
  1304. // Bind to the DS on the DC.
  1305. //
  1306. NetStatus = DsBindWithCredW( DcInfo->DomainControllerName,
  1307. NULL,
  1308. AuthId,
  1309. &hDs );
  1310. if ( NetStatus != NO_ERROR ) {
  1311. NetpLog(( "NetpManageAltComputerName: DsBindWithCredW failed to '%ws': 0x%lx\n",
  1312. DcInfo->DomainControllerName,
  1313. NetStatus ));
  1314. goto Cleanup ;
  1315. }
  1316. //
  1317. // Open an LDAP connection to the DC and set useful options
  1318. //
  1319. LdapHandle = ldap_initW( DcInfo->DomainControllerName+2,
  1320. LDAP_PORT );
  1321. if ( LdapHandle == NULL ) {
  1322. LdapStatus = LdapGetLastError();
  1323. NetpLog(( "NetpManageAltComputerName: ldap_init to %ws failed: %lu\n",
  1324. DcInfo->DomainControllerName+2,
  1325. LdapStatus ));
  1326. NetStatus = LdapMapErrorToWin32( LdapStatus );
  1327. goto Cleanup;
  1328. }
  1329. //
  1330. // Tell LDAP to avoid chasing referals
  1331. //
  1332. LdapOption = PtrToLong( LDAP_OPT_OFF );
  1333. LdapStatus = ldap_set_optionW( LdapHandle,
  1334. LDAP_OPT_REFERRALS,
  1335. &LdapOption );
  1336. if ( LdapStatus != LDAP_SUCCESS ) {
  1337. NetpLog(( "NetpManageAltComputerName: ldap_set_option LDAP_OPT_REFERRALS failed on %ws: %ld: %s\n",
  1338. DcInfo->DomainControllerName+2,
  1339. LdapStatus,
  1340. ldap_err2stringA(LdapStatus) ));
  1341. NetStatus = LdapMapErrorToWin32( LdapStatus );
  1342. goto Cleanup;
  1343. }
  1344. //
  1345. // Tell LDAP we are passing an explicit DC name
  1346. // to avoid the DC discovery
  1347. //
  1348. LdapOption = PtrToLong( LDAP_OPT_ON );
  1349. LdapStatus = ldap_set_optionW( LdapHandle,
  1350. LDAP_OPT_AREC_EXCLUSIVE,
  1351. &LdapOption );
  1352. if ( LdapStatus != LDAP_SUCCESS ) {
  1353. NetpLog(( "NetpManageAltComputerName: ldap_set_option LDAP_OPT_AREC_EXCLUSIVE failed on %ws: %ld: %s\n",
  1354. DcInfo->DomainControllerName+2,
  1355. LdapStatus,
  1356. ldap_err2stringA(LdapStatus) ));
  1357. NetStatus = LdapMapErrorToWin32( LdapStatus );
  1358. goto Cleanup;
  1359. }
  1360. //
  1361. // Bind to the LDAP server
  1362. //
  1363. if ( AccountUserName != NULL ) {
  1364. AuthIdent.User = AccountUserName;
  1365. AuthIdent.UserLength = wcslen( AccountUserName );
  1366. }
  1367. if ( AccountDomainName != NULL ) {
  1368. AuthIdent.Domain = AccountDomainName;
  1369. AuthIdent.DomainLength = wcslen( AccountDomainName );
  1370. }
  1371. if ( DomainAccountPassword != NULL ) {
  1372. AuthIdent.Password = DomainAccountPassword;
  1373. AuthIdent.PasswordLength = wcslen( DomainAccountPassword );
  1374. }
  1375. AuthIdent.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
  1376. LdapStatus = ldap_bind_sW( LdapHandle,
  1377. NULL,
  1378. (PWSTR) &AuthIdent,
  1379. LDAP_AUTH_NEGOTIATE );
  1380. if ( LdapStatus != LDAP_SUCCESS ) {
  1381. NetpLog(( "NetpManageAltComputerName: ldap_bind failed on %ws: %ld: %s\n",
  1382. DcInfo->DomainControllerName+2,
  1383. LdapStatus,
  1384. ldap_err2stringA(LdapStatus) ));
  1385. NetStatus = LdapMapErrorToWin32( LdapStatus );
  1386. goto Cleanup;
  1387. }
  1388. //
  1389. // Ok, now that we have all prerequsites satisfied,
  1390. // do the operations that may require rollback if
  1391. // they fail.
  1392. //
  1393. // Set the primary SAM account name by doing NetSetUser.
  1394. // Note that this will also rename the DN of the computer
  1395. // object (and its display name) and the DN of the server
  1396. // object linked to from the computer object if this is a DC.
  1397. //
  1398. if ( Action == NET_SET_PRIMARY_COMPUTER_NAME ) {
  1399. NetStatus = NetpSetPrimarySamAccountName(
  1400. DcInfo->DomainControllerName,
  1401. MachineAccountName,
  1402. NewMachineAccountName,
  1403. DomainAccount,
  1404. DomainAccountPassword );
  1405. if ( NetStatus != NO_ERROR ) {
  1406. NetpLog(( "NetpManageAltComputerName: NetpSetPrimarySamAccountName failed on %ws: 0x%lx\n",
  1407. DcInfo->DomainControllerName,
  1408. NetStatus ));
  1409. goto Cleanup;
  1410. }
  1411. PrimarySamAccountNameSet = TRUE;
  1412. //
  1413. // We need to crack the new machine account
  1414. // name which we just set
  1415. //
  1416. MachineAccountNameToCrack = NewMachineAccountName;
  1417. //
  1418. // If we are not changing the primary name,
  1419. // the name to crack is the current machine name.
  1420. //
  1421. } else {
  1422. MachineAccountNameToCrack = MachineAccountName;
  1423. }
  1424. //
  1425. // Now get the DN for our machine account object
  1426. // in the DS. Do this after setting the SAM account
  1427. // name as it changes the DN.
  1428. //
  1429. // Form the NT4 account name 'domain\account' to crack
  1430. // it into the DN.
  1431. //
  1432. NameToCrack = LocalAlloc( LMEM_ZEROINIT,
  1433. LocalPolicyDns->Name.Length + // Netbios domain name
  1434. (1 + // backslash
  1435. wcslen(MachineAccountNameToCrack) + // SAM account name
  1436. 1) * sizeof(WCHAR) ); // NULL terminator
  1437. if ( NameToCrack == NULL ) {
  1438. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1439. goto Cleanup;
  1440. }
  1441. RtlCopyMemory( NameToCrack,
  1442. LocalPolicyDns->Name.Buffer,
  1443. LocalPolicyDns->Name.Length );
  1444. wcscat( NameToCrack, L"\\" );
  1445. wcscat( NameToCrack, MachineAccountNameToCrack );
  1446. //
  1447. // Crack the account name into a DN
  1448. //
  1449. NetStatus = DsCrackNamesW( hDs,
  1450. 0,
  1451. DS_NT4_ACCOUNT_NAME,
  1452. DS_FQDN_1779_NAME,
  1453. 1,
  1454. &NameToCrack,
  1455. &CrackedName );
  1456. if ( NetStatus != NO_ERROR ) {
  1457. NetpLog(( "NetpManageAltComputerName: DsCrackNames failed on '%ws' for %ws: 0x%lx\n",
  1458. DcInfo->DomainControllerName,
  1459. NameToCrack,
  1460. NetStatus ));
  1461. goto Cleanup;
  1462. }
  1463. //
  1464. // Check for consistency
  1465. //
  1466. if ( CrackedName->rItems[0].status != DS_NAME_NO_ERROR ) {
  1467. NetpLog(( "NetpManageAltComputerName: CrackNames failed for %ws: substatus 0x%lx\n",
  1468. NameToCrack,
  1469. CrackedName->rItems[0].status ));
  1470. NetStatus = NetpCrackNamesStatus2Win32Error( CrackedName->rItems[0].status );
  1471. goto Cleanup;
  1472. }
  1473. if ( CrackedName->cItems > 1 ) {
  1474. NetStatus = ERROR_DS_NAME_ERROR_NOT_UNIQUE;
  1475. NetpLog(( "NetpManageAltComputerName: Cracked Name %ws is not unique on %ws: %lu\n",
  1476. NameToCrack,
  1477. DcInfo->DomainControllerName,
  1478. CrackedName->cItems ));
  1479. goto Cleanup;
  1480. }
  1481. //
  1482. // Ok, we have our machine account DN. Proceed with modifying
  1483. // our machine account object in the DS.
  1484. //
  1485. // If we are seting new DnsHostName, we have to stop netlogon
  1486. // and tell it not to update this attribute before the
  1487. // reboot.
  1488. //
  1489. if ( Action == NET_SET_PRIMARY_COMPUTER_NAME ) {
  1490. //
  1491. // First get the current status of netlogon so that
  1492. // we can roll back properly on failure
  1493. //
  1494. NetStatus = NetpQueryService( SERVICE_NETLOGON,
  1495. &NetlogonServiceStatus,
  1496. &NetlogonServiceConfig );
  1497. if ( NetStatus != NO_ERROR ) {
  1498. NetpLog(( "NetpManageAltComputerName: NetpQueryService failed 0x%lx\n",
  1499. NetStatus ));
  1500. goto Cleanup;
  1501. }
  1502. //
  1503. // Stop netlogon if it's running
  1504. //
  1505. if ( NetlogonServiceStatus.dwCurrentState != SERVICE_STOPPED ) {
  1506. NetStatus = NetpControlServices( NETSETUP_SVC_STOPPED,
  1507. NETSETUPP_SVC_NETLOGON );
  1508. if ( NetStatus != NO_ERROR ) {
  1509. NetpLog(( "NetpManageAltComputerName: NetpControlServices failed 0x%lx\n",
  1510. NetStatus ));
  1511. goto Cleanup;
  1512. }
  1513. StopedNetlogon = TRUE;
  1514. }
  1515. //
  1516. // Tell netlogon not to update DnsHostName until reboot
  1517. // in case the user decides to start netlogon before reboot
  1518. // for some reason
  1519. //
  1520. NetpAvoidNetlogonSpnSet( TRUE );
  1521. ToldNetlogonToAvoidDnsHostNameUpdate = TRUE;
  1522. }
  1523. //
  1524. // Prepare attributes that need to be set in the DS
  1525. //
  1526. // If we are seting a primary name, we need to set
  1527. // DnsHostName attribute. Also, we need to add the
  1528. // current primary computer name to the additional
  1529. // DNS host name list.
  1530. //
  1531. if ( Action == NET_SET_PRIMARY_COMPUTER_NAME ) {
  1532. DnsHostNameValues[0] = AlternateName;
  1533. DnsHostNameValues[1] = NULL;
  1534. DnsHostNameMod.mod_type = L"DnsHostName";
  1535. DnsHostNameMod.mod_values = DnsHostNameValues;
  1536. DnsHostNameMod.mod_op = LDAP_MOD_REPLACE;
  1537. ModList[ModCount++] = &DnsHostNameMod;
  1538. //
  1539. // Add the current primary to additional list
  1540. //
  1541. PrimaryNameValues[0] = PrimaryName;
  1542. PrimaryNameValues[1] = NULL;
  1543. PrimaryNameMod.mod_type = L"msDS-AdditionalDnsHostName";
  1544. PrimaryNameMod.mod_values = PrimaryNameValues;
  1545. PrimaryNameMod.mod_op = LDAP_MOD_ADD;
  1546. ModList[ModCount++] = &PrimaryNameMod;
  1547. }
  1548. //
  1549. // Prepare the additional DNS host name modification.
  1550. //
  1551. // Note that we don't need to manipulate the additional
  1552. // SAM account name as it will be added/deleted by the DS
  1553. // itself as the result of the AdditionalDnsHostName update.
  1554. //
  1555. AlternateNameValues[0] = AlternateName;
  1556. AlternateNameValues[1] = NULL;
  1557. AlternateDnsHostNameMod.mod_type = L"msDS-AdditionalDnsHostName";
  1558. AlternateDnsHostNameMod.mod_values = AlternateNameValues;
  1559. //
  1560. // If we are additing an alternate name, the operation
  1561. // is add. Otherwise, we are deleting the alternate
  1562. // name or setting the alternate name as primary: in
  1563. // both cases the alternate name should be deleted
  1564. // from the additional name attribute list.
  1565. //
  1566. if ( Action == NET_ADD_ALTERNATE_COMPUTER_NAME ) {
  1567. AlternateDnsHostNameMod.mod_op = LDAP_MOD_ADD;
  1568. } else {
  1569. AlternateDnsHostNameMod.mod_op = LDAP_MOD_DELETE;
  1570. }
  1571. ModList[ModCount++] = &AlternateDnsHostNameMod;
  1572. //
  1573. // Write the modifications
  1574. //
  1575. LdapStatus = ldap_modify_ext_sW( LdapHandle,
  1576. CrackedName->rItems[0].pName, // DN of account
  1577. ModList,
  1578. ModifyControlArray, // server controls
  1579. NULL ); // no client controls
  1580. if ( LdapStatus != LDAP_SUCCESS ) {
  1581. NetpLog(( "NetpManageAltComputerName: ldap_modify_ext_s failed on %ws: %ld: %s\n",
  1582. DcInfo->DomainControllerName+2,
  1583. LdapStatus,
  1584. ldap_err2stringA(LdapStatus) ));
  1585. NetStatus = LdapMapErrorToWin32( LdapStatus );
  1586. goto Cleanup;
  1587. }
  1588. Cleanup:
  1589. //
  1590. // Revert impersonation
  1591. //
  1592. if ( ClientImpersonated ) {
  1593. WsRevertToSelf();
  1594. }
  1595. //
  1596. // On error, revert the changes. Do this after reverting
  1597. // impersonation to have as much (LocalSystem) access
  1598. // as one possibly can.
  1599. //
  1600. // Note that we don't need to revert ldap modifications
  1601. // because they were made as the last step.
  1602. //
  1603. if ( NetStatus != NO_ERROR && NameModifiedLocally ) {
  1604. NET_API_STATUS TmpNetStatus = NO_ERROR;
  1605. //
  1606. // If we added an alternate name, remove it
  1607. //
  1608. if ( Action == NET_ADD_ALTERNATE_COMPUTER_NAME ) {
  1609. TmpNetStatus = RemoveLocalAlternateComputerName( AlternateName,
  1610. 0 ); // reserved
  1611. if ( TmpNetStatus != NO_ERROR ) {
  1612. NetpLog(( "NetpManageAltComputerName: (rollback) RemoveLocalAlternateComputerName failed 0x%lx\n",
  1613. TmpNetStatus ));
  1614. }
  1615. //
  1616. // If we removed an alternate name, add it
  1617. //
  1618. } else if ( Action == NET_DEL_ALTERNATE_COMPUTER_NAME ) {
  1619. TmpNetStatus = AddLocalAlternateComputerName( AlternateName,
  1620. 0 ); // reserved
  1621. if ( TmpNetStatus != NO_ERROR ) {
  1622. NetpLog(( "NetpManageAltComputerName: (rollback) AddLocalAlternateComputerName failed 0x%lx\n",
  1623. TmpNetStatus ));
  1624. }
  1625. //
  1626. // If we set a new primary name, reset it to the previous value
  1627. //
  1628. } else if ( Action == NET_SET_PRIMARY_COMPUTER_NAME ) {
  1629. TmpNetStatus = SetLocalPrimaryComputerName( PrimaryName,
  1630. 0 ); // reserved
  1631. if ( TmpNetStatus != NO_ERROR ) {
  1632. NetpLog(( "NetpManageAltComputerName: (rollback) SetLocalPrimaryComputerName failed 0x%lx\n",
  1633. TmpNetStatus ));
  1634. }
  1635. }
  1636. }
  1637. if ( NetStatus != NO_ERROR && PrimarySamAccountNameSet ) {
  1638. NET_API_STATUS TmpNetStatus = NO_ERROR;
  1639. TmpNetStatus = NetpSetPrimarySamAccountName(
  1640. DcInfo->DomainControllerName,
  1641. NewMachineAccountName, // the changed name
  1642. MachineAccountName, // old name to restore
  1643. DomainAccount,
  1644. DomainAccountPassword );
  1645. if ( TmpNetStatus != NO_ERROR) {
  1646. NetpLog(( "NetpManageAltComputerName: NetpSetPrimarySamAccountName (rollback) failed on %ws: 0x%lx\n",
  1647. DcInfo->DomainControllerName,
  1648. TmpNetStatus ));
  1649. }
  1650. }
  1651. //
  1652. // On error, take back what we told netlogon
  1653. // w.r.t. the DnsHostName update
  1654. //
  1655. if ( NetStatus != NO_ERROR && ToldNetlogonToAvoidDnsHostNameUpdate ) {
  1656. NetpAvoidNetlogonSpnSet( FALSE );
  1657. }
  1658. //
  1659. // On error, restart netlogon if we stoped it
  1660. //
  1661. if ( NetStatus != NO_ERROR && StopedNetlogon ) {
  1662. NET_API_STATUS TmpNetStatus = NO_ERROR;
  1663. TmpNetStatus = NetpControlServices( NETSETUP_SVC_STARTED,
  1664. NETSETUPP_SVC_NETLOGON );
  1665. if ( TmpNetStatus != NO_ERROR ) {
  1666. NetpLog(( "NetpManageAltComputerName: (rollback) Failed starting netlogon: 0x%lx\n",
  1667. TmpNetStatus ));
  1668. }
  1669. }
  1670. //
  1671. // Close the log file
  1672. //
  1673. NetSetuppCloseLog();
  1674. //
  1675. // Finally free the memory
  1676. //
  1677. if ( DomainAccountPassword != NULL ) {
  1678. NetpMemoryFree( DomainAccountPassword );
  1679. }
  1680. if ( AccountUserName != NULL ) {
  1681. NetApiBufferFree( AccountUserName );
  1682. }
  1683. if ( AccountDomainName != NULL ) {
  1684. NetApiBufferFree( AccountDomainName );
  1685. }
  1686. if ( ComputerName != NULL ) {
  1687. NetApiBufferFree( ComputerName );
  1688. }
  1689. if ( MachineAccountName != NULL ) {
  1690. NetApiBufferFree( MachineAccountName );
  1691. }
  1692. if ( NewMachineAccountName != NULL ) {
  1693. NetApiBufferFree( NewMachineAccountName );
  1694. }
  1695. if ( PrimaryName != NULL ) {
  1696. LocalFree( PrimaryName );
  1697. }
  1698. if ( NameToCrack != NULL ) {
  1699. LocalFree( NameToCrack );
  1700. }
  1701. if ( NetlogonServiceConfig != NULL ) {
  1702. LocalFree( NetlogonServiceConfig );
  1703. }
  1704. if ( LocalPolicyDns != NULL ) {
  1705. LsaFreeMemory( LocalPolicyDns );
  1706. }
  1707. if ( LocalPolicyHandle != NULL ) {
  1708. LsaClose( LocalPolicyHandle );
  1709. }
  1710. if ( DcInfo != NULL ) {
  1711. NetApiBufferFree( DcInfo );
  1712. }
  1713. if ( AuthId ) {
  1714. DsFreePasswordCredentials( AuthId );
  1715. }
  1716. if ( CrackedName ) {
  1717. DsFreeNameResultW( CrackedName );
  1718. }
  1719. if ( hDs ) {
  1720. DsUnBind( &hDs );
  1721. }
  1722. if ( LdapHandle != NULL ) {
  1723. ldap_unbind_s( LdapHandle );
  1724. }
  1725. return NetStatus;
  1726. }
  1727. NET_API_STATUS
  1728. NET_API_FUNCTION
  1729. NetrAddAlternateComputerName(
  1730. IN handle_t RpcBindingHandle,
  1731. IN LPWSTR ServerName OPTIONAL,
  1732. IN LPWSTR AlternateName,
  1733. IN LPWSTR DomainAccount OPTIONAL,
  1734. IN PJOINPR_ENCRYPTED_USER_PASSWORD EncryptedPassword OPTIONAL,
  1735. IN ULONG Reserved
  1736. )
  1737. /*++
  1738. Routine Description:
  1739. Adds an alternate name for the specified server.
  1740. Arguments:
  1741. ServerName -- Name of server on which to execute this function.
  1742. AlternateName -- The name to add.
  1743. DomainAccount -- Domain account to use for accessing the
  1744. machine account object for the specified server in the AD.
  1745. Not used if the server is not joined to a domain. May be
  1746. NULL in which case the credentials of the user executing
  1747. this routine are used.
  1748. DomainAccountPassword -- Password matching the domain account.
  1749. Not used if the server is not joined to a domain. May be
  1750. NULL in which case the credentials of the user executing
  1751. this routine are used.
  1752. Reserved -- Reserved for future use. If some flags are specified
  1753. that are not supported, they will be ignored if
  1754. NET_IGNORE_UNSUPPORTED_FLAGS is set, otherwise this routine
  1755. will fail with ERROR_INVALID_FLAGS.
  1756. Note:
  1757. The process that calls this routine must have administrator
  1758. privileges on the server computer.
  1759. Returns:
  1760. NO_ERROR -- Success
  1761. ERROR_NOT_SUPPORTED -- The specified server does not support this
  1762. functionality.
  1763. ERROR_INVALID_FLAGS - The Flags parameter is incorrect.
  1764. --*/
  1765. {
  1766. //
  1767. // Call the common routine
  1768. //
  1769. return NetpManageAltComputerName(
  1770. RpcBindingHandle,
  1771. AlternateName,
  1772. NET_ADD_ALTERNATE_COMPUTER_NAME,
  1773. DomainAccount,
  1774. EncryptedPassword,
  1775. Reserved );
  1776. }
  1777. NET_API_STATUS
  1778. NET_API_FUNCTION
  1779. NetrRemoveAlternateComputerName(
  1780. IN handle_t RpcBindingHandle,
  1781. IN LPWSTR ServerName OPTIONAL,
  1782. IN LPWSTR AlternateName,
  1783. IN LPWSTR DomainAccount OPTIONAL,
  1784. IN PJOINPR_ENCRYPTED_USER_PASSWORD EncryptedPassword OPTIONAL,
  1785. IN ULONG Reserved
  1786. )
  1787. /*++
  1788. Routine Description:
  1789. Deletes an alternate name for the specified server.
  1790. Arguments:
  1791. ServerName -- Name of server on which to execute this function.
  1792. AlternateName -- The name to delete.
  1793. DomainAccount -- Domain account to use for accessing the
  1794. machine account object for the specified server in the AD.
  1795. Not used if the server is not joined to a domain. May be
  1796. NULL in which case the credentials of the user executing
  1797. this routine are used.
  1798. DomainAccountPassword -- Password matching the domain account.
  1799. Not used if the server is not joined to a domain. May be
  1800. NULL in which case the credentials of the user executing
  1801. this routine are used.
  1802. Reserved -- Reserved for future use. If some flags are specified
  1803. that are not supported, they will be ignored if
  1804. NET_IGNORE_UNSUPPORTED_FLAGS is set, otherwise this routine
  1805. will fail with ERROR_INVALID_FLAGS.
  1806. Note:
  1807. The process that calls this routine must have administrator
  1808. privileges on the server computer.
  1809. Returns:
  1810. NO_ERROR -- Success
  1811. ERROR_NOT_SUPPORTED -- The specified server does not support this
  1812. functionality.
  1813. ERROR_INVALID_FLAGS - The Flags parameter is incorrect.
  1814. --*/
  1815. {
  1816. //
  1817. // Call the common routine
  1818. //
  1819. return NetpManageAltComputerName(
  1820. RpcBindingHandle,
  1821. AlternateName,
  1822. NET_DEL_ALTERNATE_COMPUTER_NAME,
  1823. DomainAccount,
  1824. EncryptedPassword,
  1825. Reserved );
  1826. }
  1827. NET_API_STATUS
  1828. NET_API_FUNCTION
  1829. NetrSetPrimaryComputerName(
  1830. IN handle_t RpcBindingHandle,
  1831. IN LPWSTR ServerName OPTIONAL,
  1832. IN LPWSTR PrimaryName,
  1833. IN LPWSTR DomainAccount OPTIONAL,
  1834. IN PJOINPR_ENCRYPTED_USER_PASSWORD EncryptedPassword OPTIONAL,
  1835. IN ULONG Reserved
  1836. )
  1837. /*++
  1838. Routine Description:
  1839. Sets the primary computer name for the specified server.
  1840. Arguments:
  1841. ServerName -- Name of server on which to execute this function.
  1842. PrimaryName -- The primary computer name to set.
  1843. DomainAccount -- Domain account to use for accessing the
  1844. machine account object for the specified server in the AD.
  1845. Not used if the server is not joined to a domain. May be
  1846. NULL in which case the credentials of the user executing
  1847. this routine are used.
  1848. DomainAccountPassword -- Password matching the domain account.
  1849. Not used if the server is not joined to a domain. May be
  1850. NULL in which case the credentials of the user executing
  1851. this routine are used.
  1852. Reserved -- Reserved for future use. If some flags are specified
  1853. that are not supported, they will be ignored if
  1854. NET_IGNORE_UNSUPPORTED_FLAGS is set, otherwise this routine
  1855. will fail with ERROR_INVALID_FLAGS.
  1856. Note:
  1857. The process that calls this routine must have administrator
  1858. privileges on the server computer.
  1859. Returns:
  1860. NO_ERROR -- Success
  1861. ERROR_NOT_SUPPORTED -- The specified server does not support this
  1862. functionality.
  1863. ERROR_INVALID_FLAGS - The Flags parameter is incorrect.
  1864. --*/
  1865. {
  1866. //
  1867. // Call the common routine
  1868. //
  1869. return NetpManageAltComputerName(
  1870. RpcBindingHandle,
  1871. PrimaryName,
  1872. NET_SET_PRIMARY_COMPUTER_NAME,
  1873. DomainAccount,
  1874. EncryptedPassword,
  1875. Reserved );
  1876. }
  1877. NET_API_STATUS
  1878. NET_API_FUNCTION
  1879. NetrEnumerateComputerNames(
  1880. IN LPWSTR ServerName OPTIONAL,
  1881. IN NET_COMPUTER_NAME_TYPE NameType,
  1882. IN ULONG Reserved,
  1883. OUT PNET_COMPUTER_NAME_ARRAY *ComputerNames
  1884. )
  1885. /*++
  1886. Routine Description:
  1887. Enumerates computer names for the specified server.
  1888. Arguments:
  1889. ServerName -- Name of server on which to execute this function.
  1890. NameType -- The type of the name queried.
  1891. Reserved -- Reserved for future use. If some flags are specified
  1892. that are not supported, they will be ignored if
  1893. NET_IGNORE_UNSUPPORTED_FLAGS is set, otherwise this routine
  1894. will fail with ERROR_INVALID_FLAGS.
  1895. ComputerNames - Returns the computer names structure.
  1896. Returns:
  1897. NO_ERROR -- Success
  1898. ERROR_NOT_SUPPORTED -- The specified server does not support this
  1899. functionality.
  1900. ERROR_INVALID_FLAGS - The Flags parameter is incorrect.
  1901. --*/
  1902. {
  1903. NET_API_STATUS NetStatus = NO_ERROR;
  1904. NT_PRODUCT_TYPE NtProductType;
  1905. BOOL ClientImpersonated = FALSE;
  1906. ULONG Size = 0;
  1907. ULONG Index = 0;
  1908. ULONG EntryCount = 0;
  1909. LPWSTR LocalNames = NULL;
  1910. PNET_COMPUTER_NAME_ARRAY LocalArray = NULL;
  1911. LPTSTR_ARRAY TStrArray;
  1912. //
  1913. // Initialize the log file
  1914. //
  1915. NetSetuppOpenLog();
  1916. NetpLog(( "NetrEnumerateComputerNames called: NameType = 0x%lx, Flags = 0x%lx\n",
  1917. NameType, Reserved ));
  1918. //
  1919. // This API is supported on DCs and servers only
  1920. //
  1921. if ( !RtlGetNtProductType( &NtProductType ) ) {
  1922. NtProductType = NtProductWinNt;
  1923. }
  1924. if ( NtProductType != NtProductServer &&
  1925. NtProductType != NtProductLanManNt ) {
  1926. NetpLog(( "NetrEnumerateComputerNames: Not supported on wksta: %lu\n",
  1927. NtProductType ));
  1928. NetStatus = ERROR_NOT_SUPPORTED;
  1929. goto Cleanup;
  1930. }
  1931. //
  1932. // Validate the Flags
  1933. //
  1934. // If some flags are passed which we don't understand
  1935. // and we are not told to ignore them, error out.
  1936. //
  1937. if ( Reserved != 0 &&
  1938. (Reserved & NET_IGNORE_UNSUPPORTED_FLAGS) == 0 ) {
  1939. NetpLog(( "NetrEnumerateComputerNames: Invalid Flags passed\n" ));
  1940. NetStatus = ERROR_INVALID_FLAGS;
  1941. goto Cleanup;
  1942. }
  1943. //
  1944. // Validate the name type
  1945. //
  1946. if ( NameType >= NetComputerNameTypeMax ) {
  1947. NetpLog(( "NetrEnumerateComputerNames: Invalid name type passed %lu\n",
  1948. NameType ));
  1949. NetStatus = ERROR_INVALID_PARAMETER;
  1950. goto Cleanup;
  1951. }
  1952. //
  1953. // Impersonate the caller. The local API will perform the
  1954. // access check on the caller on our behalf.
  1955. //
  1956. NetStatus = WsImpersonateClient();
  1957. if ( NetStatus != NO_ERROR ) {
  1958. NetpLog(( "NetrEnumerateComputerNames: WsImpersonateClient failed: 0x%lx\n",
  1959. NetStatus ));
  1960. goto Cleanup;
  1961. }
  1962. ClientImpersonated = TRUE;
  1963. //
  1964. // Get the size of the local data
  1965. //
  1966. NetStatus = EnumerateLocalComputerNamesW(
  1967. NameType,
  1968. 0, // reserved
  1969. LocalNames,
  1970. &Size ); // in characters, Null included
  1971. //
  1972. // Allocate memory for local names
  1973. //
  1974. if ( NetStatus != NO_ERROR ) {
  1975. if ( NetStatus == ERROR_MORE_DATA ) {
  1976. NetStatus = NetApiBufferAllocate( Size * sizeof(WCHAR), &LocalNames );
  1977. if ( NetStatus != NO_ERROR ) {
  1978. goto Cleanup;
  1979. }
  1980. } else {
  1981. NetpLog(( "NetrEnumerateComputerNames: EnumerateLocalComputerNamesW failed 0x%lx\n",
  1982. NetStatus ));
  1983. goto Cleanup;
  1984. }
  1985. }
  1986. //
  1987. // Get the names
  1988. //
  1989. NetStatus = EnumerateLocalComputerNamesW(
  1990. NameType,
  1991. 0, // reserved
  1992. LocalNames,
  1993. &Size );
  1994. if ( NetStatus != NO_ERROR ) {
  1995. NetpLog(( "NetrEnumerateComputerNames: EnumerateLocalComputerNamesW (2) failed 0x%lx\n",
  1996. NetStatus ));
  1997. goto Cleanup;
  1998. }
  1999. //
  2000. // Determine the length of the returned information
  2001. //
  2002. Size = sizeof( NET_COMPUTER_NAME_ARRAY );
  2003. TStrArray = LocalNames;
  2004. while ( !NetpIsTStrArrayEmpty(TStrArray) ) {
  2005. Size += sizeof(UNICODE_STRING) + (wcslen(TStrArray) + 1) * sizeof(WCHAR);
  2006. EntryCount++;
  2007. TStrArray = NetpNextTStrArrayEntry( TStrArray );
  2008. }
  2009. //
  2010. // Allocate the return buffer.
  2011. //
  2012. NetStatus = NetApiBufferAllocate( Size, &LocalArray );
  2013. if ( NetStatus != NO_ERROR ) {
  2014. goto Cleanup;
  2015. }
  2016. LocalArray->EntryCount = EntryCount;
  2017. //
  2018. // If there are names to return, copy them
  2019. // to the return buffer
  2020. //
  2021. if ( EntryCount == 0 ) {
  2022. LocalArray->ComputerNames = NULL;
  2023. } else {
  2024. PUNICODE_STRING Strings;
  2025. LPBYTE Where;
  2026. Strings = (PUNICODE_STRING) (LocalArray + 1);
  2027. LocalArray->ComputerNames = Strings;
  2028. Where = (LPBYTE) &Strings[EntryCount];
  2029. //
  2030. // Loop copying the names into the return buffer.
  2031. //
  2032. Index = 0;
  2033. TStrArray = LocalNames;
  2034. while ( !NetpIsTStrArrayEmpty(TStrArray) ) {
  2035. Strings[Index].Buffer = (LPWSTR) Where;
  2036. Strings[Index].Length = wcslen(TStrArray) * sizeof(WCHAR);
  2037. Strings[Index].MaximumLength = Strings[Index].Length + sizeof(WCHAR);
  2038. RtlCopyMemory( Where, TStrArray, Strings[Index].MaximumLength );
  2039. Where += Strings[Index].MaximumLength;
  2040. Index++;
  2041. TStrArray = NetpNextTStrArrayEntry( TStrArray );
  2042. }
  2043. }
  2044. NetStatus = NO_ERROR;
  2045. Cleanup:
  2046. //
  2047. // Revert impersonation
  2048. //
  2049. if ( ClientImpersonated ) {
  2050. WsRevertToSelf();
  2051. }
  2052. //
  2053. // Return names on success or clean up on error
  2054. //
  2055. if ( NetStatus == NO_ERROR ) {
  2056. *ComputerNames = LocalArray;
  2057. //
  2058. // Be verbose
  2059. //
  2060. if ( LocalArray->EntryCount > 0 ) {
  2061. NetpLog(( "NetrEnumerateComputerNames: Returning names:" ));
  2062. for ( Index = 0; Index < LocalArray->EntryCount; Index++ ) {
  2063. NetpLog(( " %wZ", &(LocalArray->ComputerNames[Index]) ));
  2064. }
  2065. NetpLog(( "\n" ));
  2066. } else {
  2067. NetpLog(( "NetrEnumerateComputerNames: No names returned\n" ));
  2068. }
  2069. } else {
  2070. if ( LocalArray != NULL ) {
  2071. NetApiBufferFree( LocalArray );
  2072. }
  2073. NetpLog(( "NetrEnumerateComputerNames: Failed 0x%lx\n", NetStatus ));
  2074. }
  2075. if ( LocalNames != NULL ) {
  2076. NetApiBufferFree( LocalNames );
  2077. }
  2078. //
  2079. // Close the log file
  2080. //
  2081. NetSetuppCloseLog();
  2082. return NetStatus;
  2083. }