Leaked source code of windows server 2003
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.

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