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.

9020 lines
255 KiB

  1. /*++
  2. Copyright (c) 1998 - 1998 Microsoft Corporation
  3. Module Name:
  4. ldapjoin.c
  5. Abstract:
  6. NetJoin support functions for accessing the DS via LDAP, validating names, and handling LSA
  7. functionality
  8. Author:
  9. Mac McLain (MacM) 27-Jan-1998 Name validation code based on ui\common\lmobj\lmobj code
  10. by ThomasPa
  11. Environment:
  12. User mode only.
  13. Revision History:
  14. --*/
  15. // Netlib uses DsGetDcName AND is linked into netapi32 where DsGetDcName is
  16. // implemented. So define that we aren't importing the API.
  17. #define _DSGETDCAPI_
  18. #include <netsetp.h>
  19. #include <lmaccess.h>
  20. #include <wincrypt.h>
  21. #define WKSTA_NETLOGON
  22. #define NETSETUP_JOIN
  23. #include <confname.h>
  24. #include <winldap.h>
  25. #include <nb30.h>
  26. #include <msgrutil.h>
  27. #include <lmaccess.h>
  28. #include <lmuse.h>
  29. #include <lmwksta.h>
  30. #include <stdio.h>
  31. #include <ntddbrow.h>
  32. #include <netlibnt.h>
  33. #include <ntddnfs.h>
  34. #include <remboot.h>
  35. #include <dns.h>
  36. #include <ntsam.h>
  37. #include <rpc.h>
  38. #include <ntdsapi.h>
  39. #include <netlogon.h>
  40. #include <logonp.h>
  41. #include <wchar.h>
  42. #include <icanon.h> // NetpNameCanonicalize
  43. #include <tstring.h> // STRLEN
  44. #include <autoenr.h> // Autoenrol routine
  45. #include "joinp.h"
  46. #define NETSETUPP_WINLOGON_PATH L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\"
  47. #define NETSETUPP_WINLOGON_CAD L"DisableCAD"
  48. #define NETSETUPP_ALL_FILTER L"(ObjectClass=*)"
  49. #define NETSETUPP_OU_FILTER L"(ObjectClass=OrganizationalUnit)"
  50. #define NETSETUPP_RETURNED_ATTR L"AllowedChildClassesEffective"
  51. #define NETSETUPP_DN_ATTR L"DistinguishedName"
  52. #define NETSETUPP_WELL_KNOWN L"WellKnownObjects"
  53. #define NETSETUPP_COMPUTER_OBJECT L"Computer"
  54. #define NETSETUPP_OBJ_PREFIX L"CN="
  55. #define NETSETUPP_ACCNT_TYPE_ENABLED L"4096"
  56. #define NETSETUPP_ACCNT_TYPE_DISABLED L"4098"
  57. //
  58. // DNS registration removal function prototype
  59. //
  60. typedef DWORD (APIENTRY *DNS_REGISTRATION_REMOVAL_FN) ( VOID );
  61. //
  62. // Locally defined macros
  63. //
  64. #define clearncb(x) memset((char *)x,'\0',sizeof(NCB))
  65. NTSTATUS
  66. NetpGetLsaHandle(
  67. IN LPWSTR lpServer, OPTIONAL
  68. IN LSA_HANDLE PolicyHandle, OPTIONAL
  69. OUT PLSA_HANDLE pPolicyHandle
  70. )
  71. /*++
  72. Routine Description:
  73. Either returns the given LSA handle if it's valid, or opens a new one
  74. Arguments:
  75. lpServer -- server name : NULL == local policy
  76. PolicyHandle -- Potentially open policy handle
  77. pPolicyHandle -- Open policy handle returned here
  78. Returns:
  79. STATUS_SUCCESS -- Success
  80. --*/
  81. {
  82. NTSTATUS Status = STATUS_SUCCESS;
  83. OBJECT_ATTRIBUTES OA;
  84. UNICODE_STRING Server, *pServer = NULL;
  85. if ( PolicyHandle == NULL )
  86. {
  87. if ( lpServer != NULL )
  88. {
  89. RtlInitUnicodeString( &Server, lpServer );
  90. pServer = &Server;
  91. }
  92. //
  93. // Open the local policy
  94. //
  95. InitializeObjectAttributes( &OA, NULL, 0, NULL, NULL );
  96. Status = LsaOpenPolicy( pServer, &OA, MAXIMUM_ALLOWED, pPolicyHandle );
  97. if ( !NT_SUCCESS( Status ) )
  98. {
  99. NetpLog(( "NetpGetLsaHandle: LsaOpenPolicy on %ws failed: 0x%lx\n",
  100. GetStrPtr(lpServer), Status ));
  101. }
  102. }
  103. else
  104. {
  105. *pPolicyHandle = PolicyHandle;
  106. }
  107. return( Status );
  108. }
  109. VOID
  110. NetpSetLsaHandle(
  111. IN LSA_HANDLE PassedHandle,
  112. IN LSA_HANDLE OpenHandle,
  113. OUT PLSA_HANDLE pReturnedHandle
  114. )
  115. /*++
  116. Routine Description:
  117. Either closes the opened handle or returns it
  118. Arguments:
  119. PassedHandle -- Handle passed to the original API call
  120. OpenHandle -- Handle returned from NetpGetLsaHandle
  121. pReturnedHandle -- handle is passed back to the caller if requested
  122. Returns:
  123. VOID
  124. --*/
  125. {
  126. if ( pReturnedHandle == NULL )
  127. {
  128. if ((PassedHandle == NULL) && (OpenHandle != NULL))
  129. {
  130. LsaClose( OpenHandle );
  131. }
  132. }
  133. else
  134. {
  135. *pReturnedHandle = OpenHandle;
  136. }
  137. }
  138. NET_API_STATUS
  139. NET_API_FUNCTION
  140. NetpSetLsaPrimaryDomain(
  141. IN LSA_HANDLE PolicyHandle, OPTIONAL
  142. IN LPWSTR lpDomain,
  143. IN PSID pDomainSid, OPTIONAL
  144. IN PPOLICY_DNS_DOMAIN_INFO pPolicyDns, OPTIONAL
  145. OUT PLSA_HANDLE pPolicyHandle OPTIONAL
  146. )
  147. /*++
  148. Routine Description:
  149. Sets the primary domain in the local LSA policy
  150. Arguments:
  151. PolicyHandle -- Handle to the open policy
  152. lpDomain -- Name of the domain to join
  153. pDomainSid -- Primary domain sid to be set
  154. pPolicyDns -- DNS domain info
  155. pPolicyHandle -- handle returned if non-null
  156. Returns:
  157. NERR_Success -- Success
  158. --*/
  159. {
  160. NTSTATUS Status = STATUS_SUCCESS;
  161. LSA_HANDLE LocalPolicy = NULL;
  162. POLICY_PRIMARY_DOMAIN_INFO PolicyPDI;
  163. Status = NetpGetLsaHandle( NULL, PolicyHandle, &LocalPolicy );
  164. //
  165. // Now, build the primary domain info, and set it
  166. //
  167. if ( NT_SUCCESS( Status ) )
  168. {
  169. RtlInitUnicodeString( &(PolicyPDI.Name), lpDomain );
  170. PolicyPDI.Sid = pDomainSid;
  171. Status = LsaSetInformationPolicy( LocalPolicy,
  172. PolicyPrimaryDomainInformation,
  173. ( PVOID )&PolicyPDI );
  174. if ( NT_SUCCESS( Status ) && pPolicyDns )
  175. {
  176. Status = LsaSetInformationPolicy( LocalPolicy,
  177. PolicyDnsDomainInformation,
  178. ( PVOID )pPolicyDns );
  179. }
  180. }
  181. NetpSetLsaHandle( PolicyHandle, LocalPolicy, pPolicyHandle );
  182. NetpLog(( "NetpSetLsaPrimaryDomain: for '%ws' status: 0x%lx\n", GetStrPtr(lpDomain), Status ));
  183. return( RtlNtStatusToDosError( Status ) );
  184. }
  185. NET_API_STATUS
  186. NET_API_FUNCTION
  187. NetpGetLsaPrimaryDomain(
  188. IN LSA_HANDLE PolicyHandle, OPTIONAL
  189. IN LPWSTR lpServer, OPTIONAL
  190. OUT PPOLICY_PRIMARY_DOMAIN_INFO *ppPolicyPDI,
  191. OUT PPOLICY_DNS_DOMAIN_INFO *ppPolicyDns,
  192. OUT PLSA_HANDLE pPolicyHandle OPTIONAL
  193. )
  194. /*++
  195. Routine Description:
  196. Gets the primary domain info in the local LSA policy
  197. Arguments:
  198. PolicyHandle -- Handle to the open policy. If NULL, a new handle is
  199. opened.
  200. lpServer -- Optional server name on which to read the policy
  201. ppPolicyPDI -- Primary domain policy returned here
  202. ppPolicyDNS -- Dns domain information is returned here if it exists
  203. pPolicyHandle -- Optional. Policy handle returned here if not null
  204. Returns:
  205. NERR_Success -- Success
  206. --*/
  207. {
  208. NTSTATUS Status = STATUS_SUCCESS;
  209. LSA_HANDLE LocalPolicy = NULL;
  210. UNICODE_STRING Server, *pServer = NULL;
  211. //
  212. // Initialization
  213. //
  214. *ppPolicyPDI = NULL;
  215. *ppPolicyDns = NULL;
  216. if ( lpServer != NULL )
  217. {
  218. RtlInitUnicodeString( &Server, lpServer );
  219. pServer = &Server;
  220. }
  221. Status = NetpGetLsaHandle( lpServer, PolicyHandle, &LocalPolicy );
  222. //
  223. // Now, get the primary domain info
  224. //
  225. if ( NT_SUCCESS( Status ) )
  226. {
  227. Status = LsaQueryInformationPolicy( LocalPolicy,
  228. PolicyDnsDomainInformation,
  229. ( PVOID * )ppPolicyDns );
  230. if ( Status == RPC_NT_PROCNUM_OUT_OF_RANGE )
  231. {
  232. Status = STATUS_SUCCESS;
  233. *ppPolicyDns = NULL;
  234. }
  235. if ( NT_SUCCESS( Status ) )
  236. {
  237. Status = LsaQueryInformationPolicy( LocalPolicy,
  238. PolicyPrimaryDomainInformation,
  239. (PVOID *)ppPolicyPDI);
  240. if ( !NT_SUCCESS( Status ) && (*ppPolicyDns) != NULL )
  241. {
  242. LsaFreeMemory( *ppPolicyDns );
  243. *ppPolicyDns = NULL;
  244. }
  245. }
  246. }
  247. NetpSetLsaHandle( PolicyHandle, LocalPolicy, pPolicyHandle );
  248. NetpLog(( "NetpGetLsaPrimaryDomain: status: 0x%lx\n", Status ));
  249. return( RtlNtStatusToDosError( Status ) );
  250. }
  251. NET_API_STATUS
  252. NET_API_FUNCTION
  253. NetpGetLsaDcRole(
  254. IN LPWSTR lpMachine,
  255. OUT BOOL *pfIsDC
  256. )
  257. /*++
  258. Routine Description:
  259. Gets the role of the DC in the domain
  260. Arguments:
  261. lpMachine -- Machine to connect to
  262. pfIsDC -- If TRUE, this is a DC.
  263. Returns:
  264. NERR_Success -- Success
  265. --*/
  266. {
  267. NTSTATUS Status = STATUS_SUCCESS;
  268. PBYTE pBuff;
  269. LSA_HANDLE hPolicy;
  270. Status = NetpGetLsaHandle( lpMachine, NULL, &hPolicy );
  271. //
  272. // Now, get the server role info
  273. //
  274. if ( NT_SUCCESS( Status ) ) {
  275. Status = LsaQueryInformationPolicy( hPolicy,
  276. PolicyLsaServerRoleInformation,
  277. &pBuff);
  278. if ( *(PPOLICY_LSA_SERVER_ROLE)pBuff == PolicyServerRoleBackup ||
  279. *(PPOLICY_LSA_SERVER_ROLE)pBuff == PolicyServerRolePrimary ) {
  280. *pfIsDC = TRUE;
  281. } else {
  282. *pfIsDC = FALSE;
  283. }
  284. LsaFreeMemory( pBuff );
  285. LsaClose( hPolicy );
  286. }
  287. if ( !NT_SUCCESS( Status ) ) {
  288. NetpLog(( "NetpGetLsaDcRole failed with 0x%lx\n", Status ));
  289. }
  290. return( RtlNtStatusToDosError( Status ) );
  291. }
  292. NTSTATUS
  293. NetpLsaOpenSecret(
  294. IN LSA_HANDLE hLsa,
  295. IN PUNICODE_STRING pusSecretName,
  296. IN ACCESS_MASK DesiredAccess,
  297. OUT PLSA_HANDLE phSecret
  298. )
  299. /*++
  300. Routine Description:
  301. Open the specified LSA secret as self.
  302. LsaQuerySecret fails for a network client whent the client is not
  303. trusted (see lsa\server\dbsecret.c). This causes remote join
  304. operation to fail. To get around this, this function temporarily
  305. un-impersonates, opens the secrets and impersonates again.
  306. Thus the open secret occurrs in LocalSystem context.
  307. $ REVIEW kumarp 15-July-1999
  308. This is obviously not a good design. This should be changed post NT5.
  309. Arguments:
  310. same as those for LsaOpenSecret
  311. Returns:
  312. NTSTATUS, see help for LsaOpenSecret
  313. --*/
  314. {
  315. NTSTATUS Status = STATUS_UNSUCCESSFUL;
  316. HANDLE hToken=NULL;
  317. __try
  318. {
  319. if (OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE,
  320. TRUE, &hToken))
  321. {
  322. if (SetThreadToken(NULL, NULL))
  323. {
  324. Status = LsaOpenSecret(hLsa, pusSecretName,
  325. DesiredAccess, phSecret);
  326. }
  327. }
  328. }
  329. __finally
  330. {
  331. if (hToken)
  332. {
  333. SetThreadToken(NULL, hToken);
  334. }
  335. }
  336. NetpLog(( "NetpLsaOpenSecret: status: 0x%lx\n", Status ));
  337. return Status;
  338. }
  339. //$ REVIEW kumarp 13-May-1999
  340. // the fDelete param is used as uint but is declared as BOOL
  341. NET_API_STATUS
  342. NET_API_FUNCTION
  343. NetpManageMachineSecret(
  344. IN LSA_HANDLE PolicyHandle, OPTIONAL
  345. IN LPWSTR lpMachine,
  346. IN LPWSTR lpPassword,
  347. IN BOOL fDelete,
  348. IN BOOL UseDefaultForOldPwd,
  349. OUT PLSA_HANDLE pPolicyHandle OPTIONAL
  350. )
  351. /*++
  352. Routine Description:
  353. Create/delete the machine secret
  354. Arguments:
  355. PolicyHandle -- Optional open handle to the policy
  356. lpMachine -- Machine to add/delete the secret for
  357. lpPassword -- Machine password to use.
  358. fDelete -- if TRUE, the secret is removed
  359. UseDefaultForOldPwd - if TRUE, the default password should be set
  360. for the old password value. Used only if
  361. secret is created.
  362. pPolicyHandle -- If present, the opened policy handle is returned here
  363. Returns:
  364. NERR_Success -- Success
  365. --*/
  366. {
  367. NTSTATUS Status = STATUS_SUCCESS;
  368. LSA_HANDLE LocalPolicy = NULL, SecretHandle = NULL;
  369. UNICODE_STRING Key, Data, *CurrentValue = NULL;
  370. BOOLEAN SecretCreated = FALSE;
  371. WCHAR MachinePasswordBuffer[PWLEN + 1];
  372. UNICODE_STRING MachinePassword;
  373. BOOLEAN FreeCurrentValue = FALSE;
  374. if( fDelete == FALSE )
  375. {
  376. ASSERT( lpPassword );
  377. }
  378. Status = NetpGetLsaHandle( NULL, PolicyHandle, &LocalPolicy );
  379. //
  380. // open/create the secret
  381. //
  382. if ( NT_SUCCESS( Status ) )
  383. {
  384. RtlInitUnicodeString( &Key, L"$MACHINE.ACC" );
  385. RtlInitUnicodeString( &Data, lpPassword );
  386. Status = NetpLsaOpenSecret( LocalPolicy, &Key,
  387. fDelete == NETSETUPP_CREATE ?
  388. SECRET_SET_VALUE | SECRET_QUERY_VALUE : DELETE,
  389. &SecretHandle );
  390. if ( Status == STATUS_OBJECT_NAME_NOT_FOUND )
  391. {
  392. if ( fDelete )
  393. {
  394. Status = STATUS_SUCCESS;
  395. }
  396. else
  397. {
  398. Status = LsaCreateSecret( LocalPolicy, &Key,
  399. SECRET_SET_VALUE, &SecretHandle );
  400. if ( NT_SUCCESS( Status ) )
  401. {
  402. SecretCreated = TRUE;
  403. }
  404. }
  405. }
  406. if ( !NT_SUCCESS( Status ) )
  407. {
  408. NetpLog(( "NetpManageMachineSecret: Open/Create secret failed: 0x%lx\n", Status ));
  409. }
  410. if ( NT_SUCCESS( Status ) )
  411. {
  412. if ( fDelete == NETSETUPP_CREATE )
  413. {
  414. //
  415. // First, read the current value, so we can save it as the old value
  416. //
  417. if ( !UseDefaultForOldPwd ) {
  418. if ( SecretCreated )
  419. {
  420. CurrentValue = &Data;
  421. }
  422. else
  423. {
  424. Status = LsaQuerySecret( SecretHandle, &CurrentValue,
  425. NULL, NULL, NULL );
  426. FreeCurrentValue = TRUE;
  427. }
  428. //
  429. // If we are to use the default value for old password,
  430. // generate the default value
  431. //
  432. } else {
  433. NetpGenerateDefaultPassword(lpMachine, MachinePasswordBuffer);
  434. RtlInitUnicodeString( &MachinePassword, MachinePasswordBuffer );
  435. CurrentValue = &MachinePassword;
  436. }
  437. if ( NT_SUCCESS( Status ) )
  438. {
  439. //
  440. // Now, store both the new password and the old
  441. //
  442. Status = LsaSetSecret( SecretHandle, &Data, CurrentValue );
  443. if ( FreeCurrentValue )
  444. {
  445. LsaFreeMemory( CurrentValue );
  446. }
  447. }
  448. }
  449. else
  450. {
  451. //
  452. // No secret handle means we failed earlier in
  453. // some intermediate state. That's ok, just press on.
  454. //
  455. if ( SecretHandle != NULL )
  456. {
  457. Status = LsaDelete( SecretHandle );
  458. if ( NT_SUCCESS( Status ) )
  459. {
  460. SecretHandle = NULL;
  461. }
  462. }
  463. }
  464. }
  465. if ( SecretHandle )
  466. {
  467. LsaClose( SecretHandle );
  468. }
  469. }
  470. NetpSetLsaHandle( PolicyHandle, LocalPolicy, pPolicyHandle );
  471. if ( !NT_SUCCESS( Status ) )
  472. {
  473. NetpLog(( "NetpManageMachineSecret: '%s' operation failed: 0x%lx\n",
  474. fDelete == NETSETUPP_CREATE ? "CREATE" : "DELETE", Status ));
  475. }
  476. return( RtlNtStatusToDosError( Status ) );
  477. }
  478. NET_API_STATUS
  479. NET_API_FUNCTION
  480. NetpReadCurrentSecret(
  481. IN LSA_HANDLE PolicyHandle, OPTIONAL
  482. OUT LPWSTR *lpCurrentSecret,
  483. OUT PLSA_HANDLE pPolicyHandle OPTIONAL
  484. )
  485. /*++
  486. Routine Description:
  487. Reads the value of the current secret
  488. Arguments:
  489. PolicyHandle -- Optional open handle to the policy
  490. lpCurrentSecret -- Where the current secret is returned
  491. pPolicyHandle -- If present, the opened policy handle is returned here
  492. Returns:
  493. NERR_Success -- Success
  494. --*/
  495. {
  496. NTSTATUS Status = STATUS_SUCCESS;
  497. LSA_HANDLE LocalPolicy = NULL, SecretHandle;
  498. UNICODE_STRING Secret, *Data;
  499. Status = NetpGetLsaHandle( NULL, PolicyHandle, &LocalPolicy );
  500. //
  501. // Now, read the secret
  502. //
  503. if ( NT_SUCCESS( Status ) ) {
  504. RtlInitUnicodeString( &Secret, L"$MACHINE.ACC" );
  505. // Status = LsaRetrievePrivateData( LocalPolicy, &Key, &Data );
  506. Status = NetpLsaOpenSecret( LocalPolicy,
  507. &Secret,
  508. SECRET_QUERY_VALUE,
  509. &SecretHandle );
  510. if ( NT_SUCCESS(Status) ) {
  511. Status = LsaQuerySecret( SecretHandle,
  512. &Data,
  513. NULL,
  514. NULL,
  515. NULL );
  516. LsaClose( SecretHandle );
  517. }
  518. if ( NT_SUCCESS( Status ) ) {
  519. if( NetApiBufferAllocate( Data->Length + sizeof( WCHAR ),
  520. ( PBYTE * )lpCurrentSecret ) != NERR_Success ) {
  521. Status = STATUS_INSUFFICIENT_RESOURCES;
  522. } else {
  523. RtlCopyMemory( ( PVOID )*lpCurrentSecret,
  524. Data->Buffer,
  525. Data->Length );
  526. ( *lpCurrentSecret )[ Data->Length / sizeof( WCHAR ) ] = UNICODE_NULL;
  527. }
  528. }
  529. }
  530. NetpSetLsaHandle( PolicyHandle, LocalPolicy, pPolicyHandle );
  531. if ( !NT_SUCCESS( Status ) ) {
  532. NetpLog(( "NetpReadCurrentSecret: failed: 0x%lx\n", Status ));
  533. }
  534. return( RtlNtStatusToDosError( Status ) );
  535. }
  536. NET_API_STATUS
  537. NET_API_FUNCTION
  538. NetpSetNetlogonDomainCache(
  539. IN LPWSTR lpDc
  540. )
  541. /*++
  542. Routine Description:
  543. Initializes NetLogons trusted domain cache, using the trusted
  544. domain list on the DC.
  545. Arguments:
  546. lpDc -- Name of a DC in the domain
  547. The caller should already have an valid connection to IPC$
  548. Returns:
  549. NERR_Success -- Success
  550. --*/
  551. {
  552. DWORD dwErr = ERROR_SUCCESS;
  553. PDS_DOMAIN_TRUSTSW TrustedDomains=NULL;
  554. ULONG TrustedDomainCount=0;
  555. //
  556. // Get the trusted domain list from the DC.
  557. //
  558. dwErr = DsEnumerateDomainTrustsW( lpDc, DS_DOMAIN_VALID_FLAGS,
  559. &TrustedDomains, &TrustedDomainCount );
  560. //
  561. // If the server does not support returning all trust types
  562. // (i.e. the server is an NT4 machine) ask for only those
  563. // which it can return.
  564. //
  565. if ( dwErr == ERROR_NOT_SUPPORTED )
  566. {
  567. dwErr = DsEnumerateDomainTrustsW(
  568. lpDc,
  569. DS_DOMAIN_PRIMARY | DS_DOMAIN_DIRECT_OUTBOUND,
  570. &TrustedDomains,
  571. &TrustedDomainCount );
  572. if ( dwErr == ERROR_NOT_SUPPORTED )
  573. {
  574. //
  575. // looks like the DC is running NT3.51. In this case, we do not want
  576. // to fail the join operation because we could not write
  577. // the netlogon cache. reset the error code.
  578. //
  579. // see bug "359684 Win2k workstation unable to join NT3.51Domain"
  580. //
  581. NetpLog(( "NetpSetNetlogonDomainCache: DsEnumerateDomainTrustsW failed with ERROR_NOT_SUPPORTED, this looks like a NT3.51 DC. The failure was ignored. dc list not written to netlogon cache.\n"));
  582. dwErr = ERROR_SUCCESS;
  583. }
  584. }
  585. if ( ( dwErr == NO_ERROR ) && TrustedDomainCount )
  586. {
  587. //
  588. // Write the trusted domain list to a file where netlogon will find it.
  589. //
  590. dwErr = NlWriteFileForestTrustList (
  591. NL_FOREST_BINARY_LOG_FILE_JOIN,
  592. TrustedDomains,
  593. TrustedDomainCount );
  594. }
  595. else
  596. {
  597. NetpLog(( "NetpSetNetlogonDomainCache: NlWriteFileForestTrustList failed: 0x%lx\n", dwErr ));
  598. }
  599. //
  600. // Disable the no Ctrl-Alt-Del from the winlogon side of things
  601. //
  602. if ( dwErr == ERROR_SUCCESS ) {
  603. HKEY hWinlogon;
  604. dwErr = RegOpenKey( HKEY_LOCAL_MACHINE,
  605. NETSETUPP_WINLOGON_PATH, &hWinlogon );
  606. if ( dwErr == ERROR_SUCCESS ) {
  607. DWORD Value;
  608. Value = 0;
  609. dwErr = RegSetValueEx( hWinlogon, NETSETUPP_WINLOGON_CAD, 0,
  610. REG_DWORD, (PBYTE)&Value, sizeof( ULONG ) );
  611. RegCloseKey( hWinlogon );
  612. }
  613. //
  614. // Failing to set this never causes failure
  615. //
  616. if ( dwErr != ERROR_SUCCESS ) {
  617. NetpLog(( "Setting Winlogon DisableCAD failed with %lu\n", dwErr ));
  618. dwErr = ERROR_SUCCESS;
  619. }
  620. }
  621. //
  622. // Free locally used resources
  623. //
  624. if ( TrustedDomains != NULL ) {
  625. NetApiBufferFree( TrustedDomains );
  626. }
  627. return dwErr;
  628. }
  629. /*++
  630. Routine Description:
  631. Is this a terminal-server-application-server?
  632. Arguments:
  633. Args - none
  634. Return Value:
  635. TRUE or FALSE
  636. --*/
  637. BOOL IsAppServer(void)
  638. {
  639. OSVERSIONINFOEX osVersionInfo;
  640. DWORDLONG dwlConditionMask = 0;
  641. BOOL fIsWTS;
  642. osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
  643. fIsWTS = GetVersionEx((OSVERSIONINFO *)&osVersionInfo) &&
  644. (osVersionInfo.wSuiteMask & VER_SUITE_TERMINAL) &&
  645. !(osVersionInfo.wSuiteMask & VER_SUITE_SINGLEUSERTS);
  646. return fIsWTS;
  647. }
  648. NET_API_STATUS
  649. NET_API_FUNCTION
  650. NetpManageLocalGroups(
  651. IN PSID pDomainSid,
  652. IN BOOL fDelete
  653. )
  654. /*++
  655. Routine Description:
  656. Performs SAM account handling to either add or remove the DomainAdmins,
  657. etc groups from the local groups.
  658. Arguments:
  659. pDomainSid -- SID of the domain being joined/left
  660. fDelete -- Whether to add or remove the admin alias
  661. Returns:
  662. NERR_Success -- Success
  663. --*/
  664. {
  665. NET_API_STATUS NetStatus = NERR_Success;
  666. //
  667. // Keep these in synch with the rids and Sids below
  668. //
  669. ULONG LocalRids[] =
  670. {
  671. DOMAIN_ALIAS_RID_ADMINS,
  672. DOMAIN_ALIAS_RID_USERS,
  673. DOMAIN_ALIAS_RID_REMOTE_DESKTOP_USERS // this LAST item is only used on TS-App-Servers
  674. };
  675. PWSTR ppwszLocalGroups[ sizeof( LocalRids ) / sizeof( ULONG )] =
  676. {
  677. NULL,
  678. NULL,
  679. NULL
  680. };
  681. ULONG Rids[] =
  682. {
  683. DOMAIN_GROUP_RID_ADMINS,
  684. DOMAIN_GROUP_RID_USERS,
  685. DOMAIN_GROUP_RID_USERS // this LAST item is only used on TS-App-Servers
  686. };
  687. BOOLEAN GroupMustExist[ sizeof( LocalRids ) / sizeof( ULONG )] =
  688. {
  689. TRUE,
  690. TRUE,
  691. TRUE,
  692. };
  693. static SID_IDENTIFIER_AUTHORITY BultinAuth = SECURITY_NT_AUTHORITY;
  694. DWORD Sids[sizeof( SID )/sizeof( DWORD ) + SID_MAX_SUB_AUTHORITIES][sizeof(Rids) / sizeof(ULONG)];
  695. DWORD cDSidSize, *pLastSub, i, j;
  696. PUCHAR pSubAuthCnt;
  697. PWSTR LocalGroupName = NULL;
  698. PWCHAR DomainName = NULL;
  699. ULONG Size, DomainSize;
  700. SID_NAME_USE SNE;
  701. ULONG numOfGroups;
  702. cDSidSize = RtlLengthSid( pDomainSid );
  703. // number of groups to process
  704. numOfGroups = sizeof(Rids) / sizeof(ULONG);
  705. // if we are NOT on an app server, we ignore the last entry
  706. if ( ! IsAppServer() )
  707. {
  708. numOfGroups -= 1;
  709. }
  710. for ( i = 0 ; i < numOfGroups && NetStatus == NERR_Success; i++)
  711. {
  712. Size = 0;
  713. DomainSize = 0;
  714. if ( DomainName != NULL ) {
  715. NetApiBufferFree( DomainName );
  716. DomainName = NULL;
  717. }
  718. //
  719. // Get the name of the local group first...
  720. //
  721. RtlInitializeSid( ( PSID )Sids[ i ], &BultinAuth, 2 );
  722. *(RtlSubAuthoritySid(( PSID )Sids[ i ], 0)) = SECURITY_BUILTIN_DOMAIN_RID;
  723. *(RtlSubAuthoritySid(( PSID )Sids[ i ], 1)) = LocalRids[ i ];
  724. LookupAccountSidW( NULL, ( PSID )Sids[ i ], NULL, &Size,
  725. DomainName, &DomainSize, &SNE );
  726. if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
  727. {
  728. NetStatus = NetApiBufferAllocate( Size * sizeof(WCHAR),
  729. &LocalGroupName );
  730. if ( NetStatus == NERR_Success ) {
  731. NetStatus = NetApiBufferAllocate( DomainSize * sizeof(WCHAR),
  732. &DomainName );
  733. }
  734. if ( NetStatus == NERR_Success )
  735. {
  736. if ( !LookupAccountSid( NULL, ( PSID )Sids[ i ], LocalGroupName,
  737. &Size, DomainName, &DomainSize, &SNE ) )
  738. {
  739. NetStatus = GetLastError();
  740. if ( NetStatus == ERROR_NONE_MAPPED && GroupMustExist[ i ] == FALSE )
  741. {
  742. NetStatus = NERR_Success;
  743. continue;
  744. }
  745. else
  746. {
  747. #ifdef NETSETUP_VERBOSE_LOGGING
  748. UNICODE_STRING DisplaySid;
  749. NTSTATUS Status2;
  750. RtlZeroMemory( &DisplaySid, sizeof( UNICODE_STRING ) );
  751. Status2 = RtlConvertSidToUnicodeString( &DisplaySid,
  752. ( PSID )Sids[ i ], TRUE );
  753. if ( NT_SUCCESS( Status2 ) )
  754. {
  755. NetpLog(( "LookupAccounSid on %wZ failed with %lu\n",
  756. &DisplaySid,
  757. NetStatus ));
  758. RtlFreeUnicodeString(&DisplaySid);
  759. }
  760. else
  761. {
  762. NetpLog(( "LookupAccounSid on <undisplayable sid> "
  763. "[Rid 0x%lx] failed with %lu\n",
  764. NetStatus ));
  765. }
  766. #endif
  767. }
  768. }
  769. else
  770. {
  771. ppwszLocalGroups[ i ] = LocalGroupName;
  772. }
  773. }
  774. else
  775. {
  776. break;
  777. }
  778. }
  779. RtlCopyMemory( (PBYTE)Sids[i], pDomainSid, cDSidSize );
  780. //
  781. // Now, add the new domain relative rid
  782. //
  783. pSubAuthCnt = GetSidSubAuthorityCount( (PSID)Sids[i] );
  784. (*pSubAuthCnt)++;
  785. pLastSub = GetSidSubAuthority( (PSID)Sids[i], (*pSubAuthCnt) - 1 );
  786. *pLastSub = Rids[i];
  787. if ( fDelete == NETSETUPP_CREATE)
  788. {
  789. NetStatus = NetLocalGroupAddMember( NULL,
  790. ppwszLocalGroups[i],
  791. (PSID)Sids[i] );
  792. if ( NetStatus == ERROR_MEMBER_IN_ALIAS )
  793. {
  794. NetStatus = NERR_Success;
  795. }
  796. }
  797. else
  798. {
  799. NetStatus = NetLocalGroupDelMember( NULL,
  800. ppwszLocalGroups[i],
  801. (PSID)Sids[i] );
  802. if ( NetStatus == ERROR_MEMBER_NOT_IN_ALIAS )
  803. {
  804. NetStatus = NERR_Success;
  805. }
  806. }
  807. }
  808. //
  809. // If something failed, try to restore what was deleted
  810. //
  811. if ( NetStatus != NERR_Success )
  812. {
  813. for ( j = 0; j < i; j++ ) {
  814. if ( fDelete == NETSETUPP_DELETE)
  815. {
  816. NetLocalGroupAddMember( NULL,
  817. ppwszLocalGroups[j],
  818. (PSID)Sids[j] );
  819. }
  820. else
  821. {
  822. NetLocalGroupDelMember( NULL,
  823. ppwszLocalGroups[j],
  824. (PSID)Sids[j] );
  825. }
  826. }
  827. }
  828. if ( DomainName != NULL ) {
  829. NetApiBufferFree( DomainName );
  830. }
  831. for ( i = 0; i < numOfGroups ; i++ )
  832. {
  833. if ( ppwszLocalGroups[ i ] )
  834. {
  835. NetApiBufferFree( ppwszLocalGroups[ i ] );
  836. }
  837. }
  838. if ( NetStatus != NERR_Success )
  839. {
  840. NetpLog(( "NetpManageLocalGroups failed with %lu\n", NetStatus ));
  841. }
  842. return( NetStatus );
  843. }
  844. NET_API_STATUS
  845. NET_API_FUNCTION
  846. NetpHandleJoinedStateInfo(
  847. IN LSA_HANDLE PolicyHandle, OPTIONAL
  848. IN PNETSETUP_SAVED_JOIN_STATE SavedState,
  849. IN BOOLEAN Save,
  850. OUT PLSA_HANDLE ReturnedPolicyHandle OPTIONAL
  851. )
  852. /*++
  853. Routine Description:
  854. Saves or restores the join state info.
  855. Arguments:
  856. PolicyHandle -- Local LSA policy handle.
  857. If not supplied, this fn. opens it
  858. SavedState -- join state info
  859. This includes:
  860. - machine account secret value
  861. - primary domain info
  862. - dns domain info
  863. Save -- TRUE == save state, FALSE == restore state
  864. ReturnedPolicyHandle -- local LSA handle returned in this
  865. Returns:
  866. NERR_Success -- Success
  867. --*/
  868. {
  869. NTSTATUS Status = STATUS_SUCCESS;
  870. LSA_HANDLE LocalPolicy = NULL, SecretHandle;
  871. UNICODE_STRING Secret;
  872. if ( Save )
  873. {
  874. RtlZeroMemory( SavedState, sizeof( NETSETUP_SAVED_JOIN_STATE ) );
  875. }
  876. //
  877. // get handle to local LSA policy
  878. //
  879. Status = NetpGetLsaHandle( NULL, PolicyHandle, &LocalPolicy );
  880. if ( NT_SUCCESS( Status ) )
  881. {
  882. //
  883. // First, read the machine account secret
  884. //
  885. RtlInitUnicodeString( &Secret, L"$MACHINE.ACC" );
  886. Status = NetpLsaOpenSecret( LocalPolicy,
  887. &Secret,
  888. SECRET_QUERY_VALUE | SECRET_SET_VALUE,
  889. &SecretHandle );
  890. if ( NT_SUCCESS( Status ) )
  891. {
  892. if ( Save )
  893. {
  894. SavedState->MachineSecret = TRUE;
  895. Status = LsaQuerySecret( SecretHandle,
  896. &( SavedState->CurrentValue ),
  897. NULL,
  898. &( SavedState->PreviousValue ),
  899. NULL );
  900. }
  901. else
  902. {
  903. if ( SavedState ->MachineSecret )
  904. {
  905. Status = LsaSetSecret( SecretHandle,
  906. SavedState->CurrentValue,
  907. SavedState->PreviousValue );
  908. }
  909. }
  910. LsaClose( SecretHandle );
  911. }
  912. //
  913. // If machine secret is not present, it is not an error.
  914. //
  915. if ( Status == STATUS_OBJECT_NAME_NOT_FOUND )
  916. {
  917. if ( Save )
  918. {
  919. SavedState->MachineSecret = FALSE;
  920. }
  921. Status = STATUS_SUCCESS;
  922. }
  923. //
  924. // Now, save/restore the policy information
  925. //
  926. if ( NT_SUCCESS( Status ) )
  927. {
  928. if ( Save )
  929. {
  930. Status = NetpGetLsaPrimaryDomain( LocalPolicy, NULL,
  931. &( SavedState->PrimaryDomainInfo ),
  932. &( SavedState->DnsDomainInfo ),
  933. NULL );
  934. }
  935. else
  936. {
  937. Status = LsaSetInformationPolicy( LocalPolicy,
  938. PolicyPrimaryDomainInformation,
  939. SavedState->PrimaryDomainInfo );
  940. if ( NT_SUCCESS( Status ) )
  941. {
  942. Status = LsaSetInformationPolicy( LocalPolicy,
  943. PolicyDnsDomainInformation,
  944. SavedState->DnsDomainInfo );
  945. }
  946. }
  947. }
  948. }
  949. NetpSetLsaHandle( PolicyHandle, LocalPolicy, ReturnedPolicyHandle );
  950. if ( !NT_SUCCESS( Status ) )
  951. {
  952. NetpLog((
  953. "NetpHandleJoinedStateInfo: '%s' operation failed: 0x%lx\n",
  954. Save ? "Save" : "Restore", Status ));
  955. }
  956. return( RtlNtStatusToDosError( Status ) );
  957. }
  958. NET_API_STATUS
  959. MsgFmtNcbName(
  960. OUT PCHAR DestBuf,
  961. IN LPTSTR Name,
  962. IN DWORD Type)
  963. /*++
  964. Routine Description:
  965. FmtNcbName - format a name NCB-style
  966. Given a name, a name type, and a destination address, this
  967. function copies the name and the type to the destination in
  968. the format used in the name fields of a Network Control
  969. Block.
  970. SIDE EFFECTS
  971. Modifies 16 bytes starting at the destination address.
  972. Arguments:
  973. DestBuf - Pointer to the destination buffer.
  974. Name - Unicode NUL-terminated name string
  975. Type - Name type number (0, 3, 5, or 32) (3=NON_FWD, 5=FWD)
  976. Return Value:
  977. NERR_Success - The operation was successful
  978. Translated Return Code from the Rtl Translate routine.
  979. --*/
  980. {
  981. DWORD i; // Counter
  982. NTSTATUS ntStatus;
  983. NET_API_STATUS status;
  984. OEM_STRING ansiString;
  985. UNICODE_STRING unicodeString;
  986. PCHAR pAnsiString;
  987. //
  988. // Force the name to be upper case.
  989. //
  990. status = NetpNameCanonicalize(
  991. NULL,
  992. Name,
  993. Name,
  994. STRSIZE(Name),
  995. NAMETYPE_MESSAGEDEST,
  996. 0);
  997. if (status != NERR_Success) {
  998. return(status);
  999. }
  1000. //
  1001. // Convert the unicode name string into an ansi string - using the
  1002. // current locale.
  1003. //
  1004. #ifdef UNICODE
  1005. unicodeString.Length = (USHORT)(STRLEN(Name)*sizeof(WCHAR));
  1006. unicodeString.MaximumLength = (USHORT)((STRLEN(Name)+1) * sizeof(WCHAR));
  1007. unicodeString.Buffer = Name;
  1008. ntStatus = RtlUnicodeStringToOemString(
  1009. &ansiString,
  1010. &unicodeString,
  1011. TRUE); // Allocate the ansiString Buffer.
  1012. if (!NT_SUCCESS(ntStatus))
  1013. {
  1014. NetpLog(( "FmtNcbName: RtlUnicodeStringToOemString failed 0x%lx\n",
  1015. ntStatus ));
  1016. return NetpNtStatusToApiStatus(ntStatus);
  1017. }
  1018. pAnsiString = ansiString.Buffer;
  1019. *(pAnsiString+ansiString.Length) = '\0';
  1020. #else
  1021. UNUSED(ntStatus);
  1022. UNUSED(unicodeString);
  1023. UNUSED(ansiString);
  1024. pAnsiString = Name;
  1025. #endif // UNICODE
  1026. //
  1027. // copy each character until a NUL is reached, or until NCBNAMSZ-1
  1028. // characters have been copied.
  1029. //
  1030. for (i=0; i < NCBNAMSZ - 1; ++i) {
  1031. if (*pAnsiString == '\0') {
  1032. break;
  1033. }
  1034. //
  1035. // Copy the Name
  1036. //
  1037. *DestBuf++ = *pAnsiString++;
  1038. }
  1039. //
  1040. // Free the buffer that RtlUnicodeStringToOemString created for us.
  1041. // NOTE: only the ansiString.Buffer portion is free'd.
  1042. //
  1043. #ifdef UNICODE
  1044. RtlFreeOemString( &ansiString);
  1045. #endif // UNICODE
  1046. //
  1047. // Pad the name field with spaces
  1048. //
  1049. for(; i < NCBNAMSZ - 1; ++i) {
  1050. *DestBuf++ = ' ';
  1051. }
  1052. //
  1053. // Set the name type.
  1054. //
  1055. NetpAssert( Type!=5 ); // 5 is not valid for NT.
  1056. *DestBuf = (CHAR) Type; // Set name type
  1057. return(NERR_Success);
  1058. }
  1059. NET_API_STATUS
  1060. NET_API_FUNCTION
  1061. NetpCheckNetBiosNameNotInUse(
  1062. IN LPWSTR pszName,
  1063. IN BOOLEAN MachineName,
  1064. IN BOOLEAN Unique
  1065. )
  1066. {
  1067. NCB ncb;
  1068. LANA_ENUM lanaBuffer;
  1069. unsigned char i;
  1070. unsigned char nbStatus;
  1071. NET_API_STATUS NetStatus = NERR_Success;
  1072. WCHAR szMachineNameBuf[MAX_COMPUTERNAME_LENGTH + 1];
  1073. LPWSTR szMachineName=szMachineNameBuf;
  1074. //
  1075. // Find the number of networks by sending an enum request via Netbios.
  1076. //
  1077. clearncb(&ncb);
  1078. ncb.ncb_command = NCBENUM; // Enumerate LANA nums (wait)
  1079. ncb.ncb_buffer = (PUCHAR)&lanaBuffer;
  1080. ncb.ncb_length = sizeof(LANA_ENUM);
  1081. nbStatus = Netbios (&ncb);
  1082. if (nbStatus != NRC_GOODRET)
  1083. {
  1084. NetStatus = NetpNetBiosStatusToApiStatus( nbStatus );
  1085. goto Cleanup;
  1086. }
  1087. clearncb(&ncb);
  1088. NetStatus = MsgFmtNcbName( (char *)ncb.ncb_name, pszName,
  1089. MachineName ? 0 : 0x1c );
  1090. if ( NetStatus != NERR_Success )
  1091. {
  1092. goto Cleanup;
  1093. }
  1094. //
  1095. // Move the Adapter Numbers (lana) into the array that will contain them.
  1096. //
  1097. for ( i = 0; i < lanaBuffer.length && NetStatus == NERR_Success; i++ )
  1098. {
  1099. NetpNetBiosReset( lanaBuffer.lana[i] );
  1100. if ( Unique )
  1101. {
  1102. ncb.ncb_command = NCBADDNAME;
  1103. }
  1104. else
  1105. {
  1106. ncb.ncb_command = NCBADDGRNAME;
  1107. }
  1108. ncb.ncb_lana_num = lanaBuffer.lana[i];
  1109. nbStatus = Netbios( &ncb );
  1110. switch ( nbStatus )
  1111. {
  1112. case NRC_DUPNAME:
  1113. // NRC_DUPNAME ==
  1114. // "A duplicate name existed in the local name table"
  1115. //
  1116. // In this case, we need to check if the name being checked
  1117. // is the same as the local computer name. If so,
  1118. // the name is expected to be in the local table therefore
  1119. // we convert this errcode to a success code
  1120. //
  1121. NetStatus = NetpGetComputerNameAllocIfReqd(
  1122. &szMachineName, MAX_COMPUTERNAME_LENGTH+1);
  1123. if (NetStatus == NERR_Success)
  1124. {
  1125. if (!_wcsicmp(szMachineName, pszName))
  1126. {
  1127. NetStatus = NERR_Success;
  1128. }
  1129. else
  1130. {
  1131. NetStatus = ERROR_DUP_NAME;
  1132. }
  1133. }
  1134. break;
  1135. case NRC_INUSE:
  1136. NetStatus = ERROR_DUP_NAME;
  1137. break;
  1138. case NRC_GOODRET:
  1139. // Delete the name
  1140. ncb.ncb_command = NCBDELNAME;
  1141. ncb.ncb_lana_num = lanaBuffer.lana[i];
  1142. // Not much we can do if this fails.
  1143. Netbios( &ncb );
  1144. // fall through
  1145. default:
  1146. NetStatus = NetpNetBiosStatusToApiStatus( nbStatus );
  1147. break;
  1148. }
  1149. }
  1150. Cleanup:
  1151. if ( NetStatus != NERR_Success )
  1152. {
  1153. NetpLog(( "NetpCheckNetBiosNameNotInUse: for '%ws' returned: 0x%lx\n",
  1154. pszName, NetStatus ));
  1155. }
  1156. if (szMachineName != szMachineNameBuf)
  1157. {
  1158. NetApiBufferFree(szMachineName);
  1159. }
  1160. return( NetStatus );
  1161. }
  1162. NET_API_STATUS
  1163. NET_API_FUNCTION
  1164. NetpIsValidDomainName(
  1165. IN LPWSTR lpName,
  1166. IN LPWSTR lpServer,
  1167. IN LPWSTR lpAccount,
  1168. IN LPWSTR lpPassword
  1169. )
  1170. /*++
  1171. Routine Description:
  1172. Determines if a name is a DC name or not. Copied from
  1173. ui\net\common\src\lmboj\lmobj\lmodom.cxx
  1174. Arguments:
  1175. lpName -- Name to check
  1176. lpServer -- Name of a server within that domain
  1177. Returns:
  1178. NERR_Success -- Success
  1179. ERROR_DUP_NAME -- The domain name is in use
  1180. --*/
  1181. {
  1182. NET_API_STATUS NetStatus = NERR_Success;
  1183. PWKSTA_INFO_100 pWKI100 = NULL;
  1184. BOOL fIsDC;
  1185. POLICY_LSA_SERVER_ROLE Role;
  1186. NetStatus = NetpManageIPCConnect( lpServer, lpAccount,
  1187. lpPassword,
  1188. NETSETUPP_CONNECT_IPC | NETSETUPP_NULL_SESSION_IPC );
  1189. if ( NetStatus == NERR_Success ) {
  1190. //
  1191. // Now, get the info from the server
  1192. //
  1193. NetStatus = NetWkstaGetInfo( lpServer, 100, (LPBYTE *)&pWKI100 );
  1194. if ( NetStatus == NERR_Success ) {
  1195. if (_wcsicmp( lpName, pWKI100->wki100_langroup ) == 0 ) {
  1196. //
  1197. // Ok, it's a match... Determine the domain role.
  1198. //
  1199. NetStatus = NetpGetLsaDcRole( lpServer, &fIsDC );
  1200. if ( ( NetStatus == NERR_Success ) && ( fIsDC == FALSE ) )
  1201. {
  1202. NetStatus = NERR_DCNotFound;
  1203. }
  1204. }
  1205. }
  1206. NetpManageIPCConnect( lpServer, lpAccount,
  1207. lpPassword, NETSETUPP_DISCONNECT_IPC );
  1208. }
  1209. if ( NetStatus != NERR_Success ) {
  1210. NetpLog((
  1211. "NetpIsValidDomainName for %ws returned 0x%lx\n",
  1212. lpName, NetStatus ));
  1213. }
  1214. return( NetStatus );
  1215. }
  1216. NET_API_STATUS
  1217. NET_API_FUNCTION
  1218. NetpCheckDomainNameIsValid(
  1219. IN LPWSTR lpName,
  1220. IN LPWSTR lpAccount,
  1221. IN LPWSTR lpPassword,
  1222. IN BOOL fShouldExist
  1223. )
  1224. /*++
  1225. Routine Description:
  1226. Checks to see if the given name is in use by a domain
  1227. Arguments:
  1228. lpName -- Name to check
  1229. Returns:
  1230. NERR_Success -- The domain is found and valid
  1231. ERROR_NO_SUCH_DOMAIN -- Domain name not found
  1232. --*/
  1233. {
  1234. NET_API_STATUS NetStatus;
  1235. PBYTE pbDC;
  1236. DWORD cDCs, i, j;
  1237. PUNICODE_STRING pDCList;
  1238. LPWSTR pwszDomain;
  1239. #if(_WIN32_WINNT >= 0x0500)
  1240. PDOMAIN_CONTROLLER_INFO pDCInfo = NULL;
  1241. #else
  1242. PBYTE pDCInfo = NULL;
  1243. #endif
  1244. UNREFERENCED_PARAMETER( lpAccount );
  1245. UNREFERENCED_PARAMETER( lpPassword );
  1246. //
  1247. // Start with NetGetAnyDCName
  1248. //
  1249. #if(_WIN32_WINNT >= 0x0500)
  1250. NetStatus = DsGetDcName( NULL, lpName, NULL, NULL,
  1251. DS_FORCE_REDISCOVERY, &pDCInfo );
  1252. #else
  1253. NetStatus = NetGetAnyDCName( NULL,
  1254. ( LPCWSTR )lpName,
  1255. &pDCInfo );
  1256. #endif
  1257. if ( NetStatus != NERR_Success ) {
  1258. if ( NetStatus == ERROR_NO_SUCH_USER ) {
  1259. NetStatus = NERR_Success;
  1260. }
  1261. } else {
  1262. NetApiBufferFree( pDCInfo );
  1263. }
  1264. //
  1265. // Map our error codes so we only return success if we validated the
  1266. // domain name
  1267. //
  1268. if ( fShouldExist ) {
  1269. if ( NetStatus == NERR_Success || NetStatus == ERROR_NO_LOGON_SERVERS ) {
  1270. NetStatus = NERR_Success;
  1271. } else {
  1272. NetStatus = ERROR_NO_SUCH_DOMAIN;
  1273. }
  1274. } else {
  1275. if ( NetStatus == NERR_Success || NetStatus == ERROR_NO_LOGON_SERVERS ) {
  1276. NetStatus = ERROR_DUP_NAME;
  1277. } else if ( NetStatus == NERR_DCNotFound || NetStatus == ERROR_NO_SUCH_DOMAIN ) {
  1278. NetStatus = NERR_Success;
  1279. }
  1280. }
  1281. if ( NetStatus != NERR_Success ) {
  1282. NetpLog(( "NetpCheckDomainNameIsValid for %ws returned 0x%lx\n",
  1283. lpName, NetStatus ));
  1284. }
  1285. return( NetStatus );
  1286. }
  1287. NET_API_STATUS
  1288. NET_API_FUNCTION
  1289. NetpManageIPCConnect(
  1290. IN LPWSTR lpServer,
  1291. IN LPWSTR lpAccount,
  1292. IN LPWSTR lpPassword,
  1293. IN ULONG fOptions
  1294. )
  1295. /*++
  1296. Routine Description:
  1297. Manages the connections to the servers IPC share
  1298. Arguments:
  1299. lpServer -- Server to connect to
  1300. lpAccount -- Account to use
  1301. lpPassword -- Password to use. The password has been NOT been encoded
  1302. fOptions -- Flags to determine operation/connect/disconnect
  1303. Returns:
  1304. NERR_Success -- The domain is found and valid
  1305. --*/
  1306. {
  1307. NET_API_STATUS NetStatus;
  1308. #if(_WIN32_WINNT >= 0x0500)
  1309. WCHAR wszPath[2 + DNS_MAX_NAME_LENGTH + 1 + NNLEN + 1];
  1310. #else
  1311. WCHAR wszPath[2 + 256 + 1 + NNLEN + 1];
  1312. #endif
  1313. PWSTR pwszPath = wszPath;
  1314. USE_INFO_2 NetUI2;
  1315. PWSTR pwszUser, pwszDomain, pwszReset;
  1316. DWORD BadParm = 0;
  1317. DWORD ForceLevel = USE_NOFORCE;
  1318. //
  1319. // Build the path...
  1320. //
  1321. if (*lpServer != L'\\') {
  1322. wcscpy(wszPath, L"\\\\");
  1323. pwszPath += 2;
  1324. }
  1325. if ( FLAG_ON( fOptions, NETSETUPP_USE_LOTS_FORCE ) )
  1326. {
  1327. ASSERT( FLAG_ON(fOptions, NETSETUPP_DISCONNECT_IPC ) );
  1328. ForceLevel = USE_LOTS_OF_FORCE;
  1329. }
  1330. swprintf( pwszPath, L"%ws\\IPC$", lpServer );
  1331. pwszPath = wszPath;
  1332. if ( FLAG_ON( fOptions, NETSETUPP_DISCONNECT_IPC ) )
  1333. {
  1334. NetStatus = NetUseDel( NULL, pwszPath, ForceLevel );
  1335. if ( NetStatus != NERR_Success )
  1336. {
  1337. NetpKdPrint(( PREFIX_NETJOIN "NetUseDel on %ws failed with %d\n", pwszPath, NetStatus ));
  1338. NetpLog(( "NetUseDel on %ws failed with %d\n", pwszPath, NetStatus ));
  1339. if ( (NetStatus != NERR_UseNotFound)
  1340. && (ForceLevel != USE_LOTS_OF_FORCE) )
  1341. {
  1342. NetStatus = NetUseDel( NULL, pwszPath, USE_LOTS_OF_FORCE );
  1343. if ( NetStatus != NERR_Success )
  1344. {
  1345. ASSERT( NetStatus == NERR_Success );
  1346. NetpKdPrint(( PREFIX_NETJOIN "NetUseDel with force on %ws failed with %d\n",
  1347. pwszPath, NetStatus ));
  1348. NetpLog(( "NetUseDel with force on %ws failed with %d\n",
  1349. pwszPath, NetStatus ));
  1350. }
  1351. }
  1352. }
  1353. }
  1354. else
  1355. {
  1356. if ( lpAccount != NULL )
  1357. {
  1358. pwszReset = wcschr( lpAccount, L'\\' );
  1359. if (pwszReset != NULL)
  1360. {
  1361. pwszUser = pwszReset + 1;
  1362. pwszDomain = lpAccount;
  1363. *pwszReset = UNICODE_NULL;
  1364. }
  1365. else
  1366. {
  1367. pwszUser = lpAccount;
  1368. //
  1369. // First, assume it's a UPN, so we pass in an empty string
  1370. //
  1371. pwszDomain = L"";
  1372. }
  1373. }
  1374. else
  1375. {
  1376. pwszUser = NULL;
  1377. pwszDomain = NULL;
  1378. pwszReset = NULL;
  1379. }
  1380. RtlZeroMemory(&NetUI2, sizeof(USE_INFO_2) );
  1381. NetUI2.ui2_local = NULL;
  1382. NetUI2.ui2_remote = pwszPath;
  1383. NetUI2.ui2_asg_type = USE_IPC;
  1384. NetUI2.ui2_username = pwszUser;
  1385. NetUI2.ui2_domainname = pwszDomain;
  1386. NetUI2.ui2_password = lpPassword;
  1387. NetStatus = NetUseAdd( NULL, 2, (PBYTE)&NetUI2, &BadParm );
  1388. if ( NetStatus == ERROR_LOGON_FAILURE )
  1389. {
  1390. //
  1391. // If we passed in an empty domain name, try it again with a NULL one
  1392. //
  1393. if ( pwszReset == NULL && pwszUser != NULL )
  1394. {
  1395. NetUI2.ui2_domainname = NULL;
  1396. NetStatus = NetUseAdd( NULL, 2, (PBYTE)&NetUI2, &BadParm );
  1397. }
  1398. }
  1399. if ( NetStatus != NERR_Success )
  1400. {
  1401. NetpKdPrint((PREFIX_NETJOIN "NetUseAdd to %ws returned %lu\n", pwszPath, NetStatus ));
  1402. NetpLog(( "NetUseAdd to %ws returned %lu\n", pwszPath, NetStatus ));
  1403. if ( NetStatus == ERROR_INVALID_PARAMETER && BadParm != 0 )
  1404. {
  1405. NetpLog(( "NetUseAdd bad parameter is %lu\n", BadParm ));
  1406. }
  1407. }
  1408. if ( pwszReset != NULL )
  1409. {
  1410. *pwszReset = L'\\';
  1411. }
  1412. if ( ( NetStatus == ERROR_NOLOGON_WORKSTATION_TRUST_ACCOUNT ||
  1413. NetStatus == ERROR_NOLOGON_SERVER_TRUST_ACCOUNT ||
  1414. NetStatus == ERROR_SESSION_CREDENTIAL_CONFLICT ||
  1415. NetStatus == ERROR_ACCESS_DENIED ||
  1416. NetStatus == ERROR_LOGON_FAILURE ) &&
  1417. FLAG_ON( fOptions, NETSETUPP_NULL_SESSION_IPC ) )
  1418. {
  1419. NetpLog(( "Trying add to %ws using NULL Session\n", pwszPath ));
  1420. //
  1421. // Try it again with the null session
  1422. //
  1423. NetUI2.ui2_username = L"";
  1424. NetUI2.ui2_domainname = L"";
  1425. NetUI2.ui2_password = L"";
  1426. NetStatus = NetUseAdd( NULL, 2, (PBYTE)&NetUI2, NULL );
  1427. if ( NetStatus != NERR_Success ) {
  1428. NetpLog(( "NullSession NetUseAdd to %ws returned %lu\n",
  1429. pwszPath, NetStatus ));
  1430. }
  1431. }
  1432. }
  1433. return( NetStatus );
  1434. }
  1435. NET_API_STATUS
  1436. NetpBrowserCheckDomain(
  1437. IN LPWSTR NewDomainName
  1438. )
  1439. /*++
  1440. Routine Description:
  1441. Tell the browser to check a domain/workgroup name
  1442. Arguments:
  1443. NewDomainName - new name of the domain.
  1444. Return Value:
  1445. Status of the operation.
  1446. --*/
  1447. {
  1448. NET_API_STATUS NetStatus;
  1449. NTSTATUS Status;
  1450. UNICODE_STRING DeviceName;
  1451. IO_STATUS_BLOCK IoStatusBlock;
  1452. OBJECT_ATTRIBUTES ObjectAttributes;
  1453. HANDLE BrowserHandle = NULL;
  1454. LPBYTE Where;
  1455. DWORD BytesReturned;
  1456. UCHAR PacketBuffer[sizeof(LMDR_REQUEST_PACKET)+2*(DNLEN+1)*sizeof(WCHAR)];
  1457. PLMDR_REQUEST_PACKET RequestPacket = (PLMDR_REQUEST_PACKET)PacketBuffer;
  1458. //
  1459. // Open the browser driver.
  1460. //
  1461. //
  1462. // Open the browser device.
  1463. //
  1464. RtlInitUnicodeString(&DeviceName, DD_BROWSER_DEVICE_NAME_U);
  1465. InitializeObjectAttributes(
  1466. &ObjectAttributes,
  1467. &DeviceName,
  1468. OBJ_CASE_INSENSITIVE,
  1469. NULL,
  1470. NULL
  1471. );
  1472. Status = NtOpenFile(
  1473. &BrowserHandle,
  1474. SYNCHRONIZE,
  1475. &ObjectAttributes,
  1476. &IoStatusBlock,
  1477. 0,
  1478. 0
  1479. );
  1480. if (NT_SUCCESS(Status)) {
  1481. Status = IoStatusBlock.Status;
  1482. }
  1483. if (!NT_SUCCESS(Status)) {
  1484. NetStatus = NetpNtStatusToApiStatus( Status );
  1485. goto Cleanup;
  1486. }
  1487. //
  1488. // Build the request packet.
  1489. //
  1490. RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION_DOM;
  1491. RtlInitUnicodeString( &RequestPacket->TransportName, NULL );
  1492. RequestPacket->Parameters.DomainRename.ValidateOnly = TRUE;
  1493. RtlInitUnicodeString( &RequestPacket->EmulatedDomainName, NULL );
  1494. //
  1495. // Copy the new domain name into the packet.
  1496. //
  1497. Where = (LPBYTE) RequestPacket->Parameters.DomainRename.DomainName;
  1498. RequestPacket->Parameters.DomainRename.DomainNameLength = wcslen( NewDomainName ) * sizeof(WCHAR);
  1499. wcscpy( (LPWSTR)Where, NewDomainName );
  1500. Where += RequestPacket->Parameters.DomainRename.DomainNameLength + sizeof(WCHAR);
  1501. //
  1502. // Send the request to the Datagram Receiver device driver.
  1503. //
  1504. if ( !DeviceIoControl(
  1505. BrowserHandle,
  1506. IOCTL_LMDR_RENAME_DOMAIN,
  1507. RequestPacket,
  1508. (DWORD)(Where - (LPBYTE)RequestPacket),
  1509. NULL,
  1510. 0,
  1511. &BytesReturned,
  1512. NULL )) {
  1513. NetStatus = GetLastError();
  1514. goto Cleanup;
  1515. }
  1516. NetStatus = NO_ERROR;
  1517. Cleanup:
  1518. if ( BrowserHandle != NULL ) {
  1519. NtClose( BrowserHandle );
  1520. }
  1521. return NetStatus;
  1522. }
  1523. NET_API_STATUS
  1524. NET_API_FUNCTION
  1525. NetpCreateAuthIdentForCreds(
  1526. IN PWSTR Account,
  1527. IN PWSTR Password,
  1528. OUT SEC_WINNT_AUTH_IDENTITY *AuthIdent
  1529. )
  1530. /*++
  1531. Routine Description:
  1532. Internal routine to create an AuthIdent structure for the given creditentials
  1533. Arguments:
  1534. Account - Account name
  1535. Password - Password for the account
  1536. AuthIdent - AuthIdentity struct to fill in
  1537. Returns:
  1538. ERROR_SUCCESS - Success
  1539. ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed.
  1540. --*/
  1541. {
  1542. NET_API_STATUS NetStatus = NERR_Success;
  1543. PWSTR UserCredentialString = NULL;
  1544. PWSTR szUser=NULL;
  1545. PWSTR szDomain=NULL;
  1546. RtlZeroMemory( AuthIdent, sizeof( SEC_WINNT_AUTH_IDENTITY ) );
  1547. //
  1548. // If there are no creds, just return
  1549. //
  1550. if ( Account == NULL )
  1551. {
  1552. return NERR_Success;
  1553. }
  1554. NetStatus = NetpSeparateUserAndDomain(Account, &szUser, &szDomain);
  1555. if ( NetStatus == NERR_Success )
  1556. {
  1557. if ( szUser )
  1558. {
  1559. AuthIdent->User = szUser;
  1560. AuthIdent->UserLength = wcslen( szUser );
  1561. }
  1562. if ( szDomain )
  1563. {
  1564. AuthIdent->Domain = szDomain;
  1565. AuthIdent->DomainLength = wcslen( szDomain );
  1566. }
  1567. if ( Password )
  1568. {
  1569. AuthIdent->Password = Password;
  1570. AuthIdent->PasswordLength = wcslen( Password );
  1571. }
  1572. AuthIdent->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
  1573. }
  1574. return( NetStatus );
  1575. }
  1576. NET_API_STATUS
  1577. NET_API_FUNCTION
  1578. NetpGetSeparatedSubstrings(
  1579. IN LPCWSTR szString,
  1580. IN WCHAR chSeparator,
  1581. OUT LPWSTR* pszS1,
  1582. OUT LPWSTR* pszS2
  1583. )
  1584. {
  1585. NET_API_STATUS NetStatus = ERROR_FILE_NOT_FOUND;
  1586. LPWSTR szT=NULL;
  1587. LPWSTR szS1=NULL;
  1588. LPWSTR szS2=NULL;
  1589. *pszS1 = NULL;
  1590. *pszS2 = NULL;
  1591. if (szString && wcschr( szString, chSeparator ))
  1592. {
  1593. NetStatus = NetpDuplicateString(szString, -1, &szS1);
  1594. if ( NetStatus == NERR_Success )
  1595. {
  1596. szT = wcschr( szS1, chSeparator );
  1597. *szT = UNICODE_NULL;
  1598. szT++;
  1599. NetStatus = NetpDuplicateString(szT, -1, &szS2);
  1600. if (NetStatus == NERR_Success)
  1601. {
  1602. *pszS1 = szS1;
  1603. *pszS2 = szS2;
  1604. }
  1605. }
  1606. }
  1607. if (NetStatus != NERR_Success)
  1608. {
  1609. NetApiBufferFree(szS1);
  1610. NetApiBufferFree(szS2);
  1611. }
  1612. return NetStatus;
  1613. }
  1614. NET_API_STATUS
  1615. NET_API_FUNCTION
  1616. NetpSeparateUserAndDomain(
  1617. IN LPCWSTR szUserAndDomain,
  1618. OUT LPWSTR* pszUser,
  1619. OUT LPWSTR* pszDomain
  1620. )
  1621. {
  1622. NET_API_STATUS NetStatus = NERR_Success;
  1623. *pszUser = NULL;
  1624. *pszDomain = NULL;
  1625. //
  1626. // check for domain\user format
  1627. //
  1628. NetStatus = NetpGetSeparatedSubstrings(szUserAndDomain, L'\\',
  1629. pszDomain, pszUser);
  1630. if (NetStatus == ERROR_FILE_NOT_FOUND)
  1631. {
  1632. //
  1633. // check for user@domain format
  1634. //
  1635. //NetStatus = NetpGetSeparatedSubstrings(szUserAndDomain, L'@',
  1636. // pszUser, pszDomain);
  1637. //if (NetStatus == ERROR_FILE_NOT_FOUND)
  1638. //{
  1639. //
  1640. // domain not specified, szUserAndDomain specifies a user
  1641. // (may be in the UPN format)
  1642. //
  1643. NetStatus = NetpDuplicateString(szUserAndDomain, -1, pszUser);
  1644. //}
  1645. }
  1646. return NetStatus;
  1647. }
  1648. VOID
  1649. NET_API_FUNCTION
  1650. NetpFreeAuthIdentForCreds(
  1651. IN PSEC_WINNT_AUTH_IDENTITY AuthIdent
  1652. )
  1653. /*++
  1654. Routine Description:
  1655. Free the authident structure allocated above
  1656. Arguments:
  1657. AuthIdent - AuthIdentity struct to free
  1658. Returns:
  1659. VOID
  1660. --*/
  1661. {
  1662. if ( AuthIdent )
  1663. {
  1664. NetApiBufferFree( AuthIdent->User );
  1665. NetApiBufferFree( AuthIdent->Domain );
  1666. }
  1667. }
  1668. NET_API_STATUS
  1669. NET_API_FUNCTION
  1670. NetpLdapUnbind(
  1671. IN PLDAP Ldap
  1672. )
  1673. /*++
  1674. Routine Description:
  1675. Unbinds a current ldap connection
  1676. Arguments:
  1677. Ldap -- Connection to be severed
  1678. Returns:
  1679. NERR_Success -- Success
  1680. --*/
  1681. {
  1682. NET_API_STATUS NetStatus = NERR_Success;
  1683. if ( Ldap != NULL ) {
  1684. NetStatus = LdapMapErrorToWin32( ldap_unbind( Ldap ) );
  1685. }
  1686. NetpLog(( "ldap_unbind status: 0x%lx\n", NetStatus ));
  1687. return( NetStatus );
  1688. }
  1689. NET_API_STATUS
  1690. NET_API_FUNCTION
  1691. NetpLdapBind(
  1692. IN LPWSTR szUncDcName,
  1693. IN LPWSTR szUser,
  1694. IN LPWSTR szPassword,
  1695. OUT PLDAP *pLdap
  1696. )
  1697. /*++
  1698. Routine Description:
  1699. Binds to the named server using the given credentials
  1700. Arguments:
  1701. szUncDcName -- DC to connect to
  1702. szUser -- User name to bind with
  1703. szPassword -- Password to use for bind
  1704. pLdap -- Where the connection handle is returned
  1705. Returns:
  1706. NERR_Success -- Success
  1707. --*/
  1708. {
  1709. NET_API_STATUS NetStatus = NERR_Success;
  1710. SEC_WINNT_AUTH_IDENTITY AuthId, *pAuthId = NULL;
  1711. LONG LdapOption;
  1712. ULONG LdapStatus = LDAP_SUCCESS;
  1713. //
  1714. // Initialization
  1715. //
  1716. *pLdap = NULL;
  1717. if ( szUser ) {
  1718. NetStatus = NetpCreateAuthIdentForCreds( szUser, szPassword, &AuthId );
  1719. pAuthId = &AuthId;
  1720. }
  1721. if ( NetStatus == NERR_Success ) {
  1722. //
  1723. // Open an LDAP connection to the DC and set useful options
  1724. //
  1725. *pLdap = ldap_initW( szUncDcName + 2, LDAP_PORT );
  1726. if ( *pLdap ) {
  1727. //
  1728. // Tell LDAP we are passing an explicit DC name
  1729. // to avoid the DC discovery
  1730. //
  1731. LdapOption = PtrToLong( LDAP_OPT_ON );
  1732. LdapStatus = ldap_set_optionW( *pLdap,
  1733. LDAP_OPT_AREC_EXCLUSIVE,
  1734. &LdapOption );
  1735. if ( LdapStatus != LDAP_SUCCESS ) {
  1736. NetpLog(( "NetpLdapBind: ldap_set_option LDAP_OPT_AREC_EXCLUSIVE failed on %ws: %ld: %s\n",
  1737. szUncDcName,
  1738. LdapStatus,
  1739. ldap_err2stringA(LdapStatus) ));
  1740. NetStatus = LdapMapErrorToWin32( LdapStatus );
  1741. //
  1742. // Do the bind
  1743. //
  1744. } else {
  1745. LdapStatus = ldap_bind_sW( *pLdap,
  1746. NULL,
  1747. (PWSTR) pAuthId,
  1748. LDAP_AUTH_NEGOTIATE );
  1749. if ( LdapStatus != LDAP_SUCCESS ) {
  1750. NetpLog(( "NetpLdapBind: ldap_bind failed on %ws: %ld: %s\n",
  1751. szUncDcName,
  1752. LdapStatus,
  1753. ldap_err2stringA(LdapStatus) ));
  1754. NetStatus = LdapMapErrorToWin32( LdapStatus );
  1755. }
  1756. }
  1757. if ( NetStatus != NERR_Success ) {
  1758. NetpLdapUnbind( *pLdap );
  1759. *pLdap = NULL;
  1760. }
  1761. } else {
  1762. LdapStatus = LdapGetLastError();
  1763. NetpLog(( "NetpLdapBind: ldap_init to %ws failed: %lu\n",
  1764. szUncDcName,
  1765. LdapStatus ));
  1766. NetStatus = LdapMapErrorToWin32( LdapStatus );
  1767. }
  1768. NetpFreeAuthIdentForCreds( pAuthId );
  1769. }
  1770. return( NetStatus );
  1771. }
  1772. NET_API_STATUS
  1773. NET_API_FUNCTION
  1774. NetpGetNCRoot(
  1775. IN PLDAP Ldap,
  1776. OUT LPWSTR *NCRoot,
  1777. OUT PBOOLEAN SupportsPageable
  1778. )
  1779. /*++
  1780. Routine Description:
  1781. This routine determines the DS root for the given domain and determines whether this
  1782. server supports pageable searches
  1783. Arguments:
  1784. Ldap -- Connection to the server
  1785. NCRoot -- Where the root is returned. Must be freed via NetApiBufferFree
  1786. SupportsPageable -- if TRUE, this server supports pageable searches
  1787. Returns:
  1788. NERR_Success -- Success
  1789. --*/
  1790. {
  1791. NET_API_STATUS NetStatus = NERR_Success;
  1792. PWSTR Attribs[3] = {
  1793. L"defaultNamingContext",
  1794. L"supportedControl",
  1795. NULL
  1796. };
  1797. PWSTR *Values = NULL;
  1798. LDAPMessage *Message=NULL, *Entry;
  1799. ULONG Items, i;
  1800. NetStatus = LdapMapErrorToWin32( ldap_search_s( Ldap, NULL, LDAP_SCOPE_BASE,
  1801. NETSETUPP_ALL_FILTER, Attribs, 0, &Message ) );
  1802. if ( NetStatus == NERR_Success ) {
  1803. Entry = ldap_first_entry( Ldap, Message );
  1804. if ( Entry ) {
  1805. //
  1806. // Now, we'll have to get the values
  1807. //
  1808. Values = ldap_get_values( Ldap, Entry, Attribs[ 0 ] );
  1809. if ( Values ) {
  1810. NetStatus = NetpDuplicateString(Values[ 0 ], -1, NCRoot);
  1811. ldap_value_free( Values );
  1812. } else {
  1813. NetStatus = LdapMapErrorToWin32( Ldap->ld_errno );
  1814. }
  1815. //
  1816. // Now, see if we have the right control bits to do pageable stuff
  1817. //
  1818. if ( NetStatus == NERR_Success ) {
  1819. Values = ldap_get_values( Ldap, Entry, Attribs[ 1 ] );
  1820. if ( Values ) {
  1821. Items = ldap_count_values( Values );
  1822. for ( i = 0; i < Items ; i++ ) {
  1823. if ( _wcsicmp( Values[ i ], LDAP_PAGED_RESULT_OID_STRING_W ) == 0 ) {
  1824. *SupportsPageable = TRUE;
  1825. break;
  1826. }
  1827. }
  1828. ldap_value_free( Values );
  1829. } else {
  1830. NetStatus = LdapMapErrorToWin32( Ldap->ld_errno );
  1831. }
  1832. }
  1833. } else {
  1834. NetStatus = LdapMapErrorToWin32( Ldap->ld_errno );
  1835. }
  1836. }
  1837. if ( NetStatus != NERR_Success ) {
  1838. NetpLog(( "Failed to find the root NC: %lu\n", NetStatus ));
  1839. }
  1840. //Cleanup:
  1841. if (Message)
  1842. {
  1843. ldap_msgfree( Message );
  1844. }
  1845. return( NetStatus );
  1846. }
  1847. NET_API_STATUS
  1848. NET_API_FUNCTION
  1849. NetpGetDefaultJoinableOu(
  1850. IN LPWSTR Root,
  1851. IN PLDAP Ldap,
  1852. OUT PWSTR *DefaultOu
  1853. )
  1854. /*++
  1855. Routine Description:
  1856. This routine searches for all the OUs under the given domain root under which the bound
  1857. user has the rights to create a computer object
  1858. This routine does pageable searches
  1859. Arguments:
  1860. Root -- Root NC path
  1861. Ldap -- Connection to the server
  1862. DefaultOu - Where the default joinable ou is returned. NULL if no default joinable ou was
  1863. found
  1864. Returns:
  1865. NERR_Success -- Success
  1866. --*/
  1867. {
  1868. NET_API_STATUS NetStatus = NERR_Success;
  1869. PWSTR Attribs[] = {
  1870. NETSETUPP_WELL_KNOWN,
  1871. NULL
  1872. };
  1873. ULONG Count, Status, i, StringLength;
  1874. PWSTR *WKOs = NULL, *Classes = NULL;
  1875. LDAPMessage *Message = NULL, *Entry, *Message2 = NULL, *Entry2;
  1876. PWSTR ParseString, End, DN;
  1877. BOOLEAN MatchFound = FALSE;
  1878. *DefaultOu = NULL;
  1879. NetpLog(( "Default OU search\n" ));
  1880. //
  1881. // Ok, first, read the list of WellKnownObjects off of the root
  1882. //
  1883. Status = ldap_search_s( Ldap,
  1884. Root,
  1885. LDAP_SCOPE_BASE,
  1886. NETSETUPP_ALL_FILTER,
  1887. Attribs,
  1888. 0,
  1889. &Message );
  1890. if ( Message ) {
  1891. Entry = ldap_first_entry( Ldap, Message );
  1892. while ( Status == LDAP_SUCCESS && Entry ) {
  1893. //
  1894. // Read the list of objects that the current user is allowed to
  1895. // create under this OU and make sure that we can create a computer
  1896. // object
  1897. //
  1898. WKOs = ldap_get_values( Ldap, Entry, Attribs[ 0 ] );
  1899. if ( WKOs ) {
  1900. i = 0;
  1901. while ( WKOs[ i ] ) {
  1902. if ( !toupper( WKOs[ i ][ 0 ] ) == L'B' ) {
  1903. NetpLog(( "Unexpected object string %ws\n",
  1904. WKOs[ i ] ));
  1905. i++;
  1906. continue;
  1907. }
  1908. ParseString = WKOs[ i ] + 2;
  1909. StringLength = wcstoul( ParseString, &End, 10 );
  1910. ParseString = End + 1; // Skip over the ':'
  1911. if ( _wcsnicmp( ParseString,
  1912. L"AA312825768811D1ADED00C04FD8D5CD",
  1913. StringLength ) == 0 ) {
  1914. MatchFound = TRUE;
  1915. ParseString += StringLength + 1;
  1916. //
  1917. // Now, see if it is accessible or not
  1918. //
  1919. Attribs[ 0 ] = NETSETUPP_RETURNED_ATTR;
  1920. Status = ldap_search_s( Ldap,
  1921. Root,
  1922. LDAP_SCOPE_BASE,
  1923. NETSETUPP_ALL_FILTER,
  1924. Attribs,
  1925. 0,
  1926. &Message2 );
  1927. if ( Message2 ) {
  1928. Entry2 = ldap_first_entry( Ldap, Message2 );
  1929. while ( Status == LDAP_SUCCESS && Entry2 ) {
  1930. //
  1931. // Read the list of objects that the current user is allowed to
  1932. // create under this OU and make sure that we can create a computer
  1933. // object
  1934. //
  1935. Classes = ldap_get_values( Ldap, Entry2, Attribs[ 0 ] );
  1936. if ( Classes ) {
  1937. i = 0;
  1938. while ( Classes[ i ] ) {
  1939. if ( _wcsicmp( Classes[ i ],
  1940. NETSETUPP_COMPUTER_OBJECT ) == 0 ) {
  1941. DN = ldap_get_dn( Ldap, Entry2 );
  1942. NetStatus = NetpDuplicateString(DN, -1,
  1943. DefaultOu);
  1944. break;
  1945. }
  1946. i++;
  1947. }
  1948. ldap_value_free( Classes );
  1949. }
  1950. Entry2 = ldap_next_entry( Ldap, Entry2 );
  1951. }
  1952. Status = Ldap->ld_errno;
  1953. ldap_msgfree( Message2 );
  1954. }
  1955. if ( NetStatus != NERR_Success || MatchFound ) {
  1956. break;
  1957. }
  1958. }
  1959. i++;
  1960. }
  1961. ldap_value_free( WKOs );
  1962. }
  1963. Entry = ldap_next_entry( Ldap, Entry );
  1964. }
  1965. Status = Ldap->ld_errno;
  1966. ldap_msgfree( Message );
  1967. }
  1968. if ( NetStatus == NERR_Success ) {
  1969. NetStatus = LdapMapErrorToWin32( Status );
  1970. }
  1971. return( NetStatus );
  1972. }
  1973. NET_API_STATUS
  1974. NET_API_FUNCTION
  1975. NetpGetListOfJoinableOUsPaged(
  1976. IN LPWSTR Root,
  1977. IN PLDAP Ldap,
  1978. OUT PULONG OUCount,
  1979. OUT PWSTR **OUs
  1980. )
  1981. /*++
  1982. Routine Description:
  1983. This routine searches for all the OUs under the given domain root under which the bound
  1984. user has the rights to create a computer object
  1985. This routine does pageable searches
  1986. Arguments:
  1987. Root -- Root NC path
  1988. Ldap -- Connection to the server
  1989. OUCount -- Where the count of strings is returned
  1990. OUs -- Where the list of OUs is returned
  1991. Returns:
  1992. NERR_Success -- Success
  1993. --*/
  1994. {
  1995. NET_API_STATUS NetStatus = NERR_Success;
  1996. PLDAPSearch SearchHandle = NULL;
  1997. PWSTR Attribs[] = {
  1998. NETSETUPP_RETURNED_ATTR,
  1999. NULL
  2000. };
  2001. ULONG Count, i;
  2002. ULONG Status = LDAP_SUCCESS;
  2003. PWSTR *Classes = NULL;
  2004. LDAPMessage *Message = NULL, *Entry;
  2005. PWSTR DN;
  2006. PWSTR *DnList = NULL, *NewList = NULL;
  2007. ULONG CurrentIndex = 0, ListCount = 0;
  2008. PWSTR DefaultOu = NULL;
  2009. NetpLog(( "PAGED OU search\n" ));
  2010. //
  2011. // Initialize the pageable search
  2012. //
  2013. SearchHandle = ldap_search_init_pageW( Ldap,
  2014. Root,
  2015. LDAP_SCOPE_SUBTREE,
  2016. NETSETUPP_OU_FILTER,
  2017. Attribs,
  2018. FALSE,
  2019. NULL,
  2020. NULL,
  2021. 0,
  2022. 2000,
  2023. NULL );
  2024. if ( SearchHandle == NULL ) {
  2025. NetStatus = LdapMapErrorToWin32( LdapGetLastError( ) );
  2026. } else {
  2027. while ( NetStatus == NERR_Success ) {
  2028. Count = 0;
  2029. //
  2030. // Get the next page
  2031. //
  2032. Status = ldap_get_next_page_s( Ldap,
  2033. SearchHandle,
  2034. NULL,
  2035. 100,
  2036. &Count,
  2037. &Message );
  2038. if ( Message ) {
  2039. //
  2040. // Process all of the entries
  2041. //
  2042. Entry = ldap_first_entry( Ldap, Message );
  2043. while ( Status == LDAP_SUCCESS && Entry ) {
  2044. //
  2045. // Read the list of classes that the current user is allowed to
  2046. // create under this OU and make sure that we can create a computer
  2047. // object
  2048. //
  2049. Classes = ldap_get_values( Ldap, Entry, Attribs[ 0 ] );
  2050. if ( Classes ) {
  2051. i = 0;
  2052. while ( Classes[ i ] ) {
  2053. if ( _wcsicmp( Classes[ i ], NETSETUPP_COMPUTER_OBJECT ) == 0 ) {
  2054. DN = ldap_get_dn( Ldap, Entry );
  2055. NetpKdPrint(( PREFIX_NETJOIN "DN = %ws\n", DN ));
  2056. //
  2057. // We'll allocate the return list in blocks of 10 to cut
  2058. // down on the number of allocations
  2059. //
  2060. if ( CurrentIndex >= ListCount ) {
  2061. if ( NetApiBufferAllocate( ( ListCount + 10 ) * sizeof( PWSTR ),
  2062. ( PVOID * )&NewList ) != NERR_Success ) {
  2063. Status = LDAP_NO_MEMORY;
  2064. } else {
  2065. RtlCopyMemory( NewList,
  2066. DnList,
  2067. ListCount * sizeof( PWSTR ) );
  2068. ListCount += 10;
  2069. DnList = NewList;
  2070. }
  2071. }
  2072. //
  2073. // Copy the string
  2074. //
  2075. if ( Status == LDAP_SUCCESS ) {
  2076. if (NERR_Success ==
  2077. NetpDuplicateString(DN, -1,
  2078. &NewList[CurrentIndex]))
  2079. {
  2080. CurrentIndex++;
  2081. }
  2082. else
  2083. {
  2084. Status = LDAP_NO_MEMORY;
  2085. }
  2086. }
  2087. ldap_memfree( DN );
  2088. break;
  2089. }
  2090. i++;
  2091. }
  2092. ldap_value_free( Classes );
  2093. }
  2094. Entry = ldap_next_entry( Ldap, Entry );
  2095. }
  2096. Status = Ldap->ld_errno;
  2097. ldap_msgfree( Message );
  2098. Message = NULL;
  2099. }
  2100. if ( Status == LDAP_NO_RESULTS_RETURNED ) {
  2101. Status = LDAP_SUCCESS;
  2102. break;
  2103. }
  2104. }
  2105. ldap_search_abandon_page( Ldap,
  2106. SearchHandle );
  2107. NetStatus = LdapMapErrorToWin32( Status );
  2108. }
  2109. //
  2110. // Check the computers container
  2111. //
  2112. if ( NetStatus == NERR_Success ) {
  2113. NetStatus = NetpGetDefaultJoinableOu( Root,
  2114. Ldap,
  2115. &DefaultOu );
  2116. if ( NetStatus == NERR_Success && DefaultOu ) {
  2117. //
  2118. // We'll allocate the return list in blocks of 10 to cut
  2119. // down on the number of allocations
  2120. //
  2121. if ( CurrentIndex >= ListCount ) {
  2122. if ( NetApiBufferAllocate( ( ListCount + 10 ) * sizeof( PWSTR ),
  2123. ( PVOID * )&NewList ) != NERR_Success ) {
  2124. Status = LDAP_NO_MEMORY;
  2125. } else {
  2126. RtlCopyMemory( NewList,
  2127. DnList,
  2128. ListCount * sizeof( PWSTR ) );
  2129. ListCount += 10;
  2130. DnList = NewList;
  2131. }
  2132. }
  2133. //
  2134. // Copy the string
  2135. //
  2136. if ( Status == LDAP_SUCCESS ) {
  2137. if (NERR_Success ==
  2138. NetpDuplicateString(DefaultOu, -1, &NewList[CurrentIndex]))
  2139. {
  2140. CurrentIndex++;
  2141. }
  2142. else
  2143. {
  2144. Status = LDAP_NO_MEMORY;
  2145. }
  2146. }
  2147. NetApiBufferFree( DefaultOu );
  2148. }
  2149. }
  2150. //
  2151. // If there was an error, free everyting
  2152. //
  2153. if ( NetStatus != NERR_Success ) {
  2154. for ( i = 0; i < ListCount; i++ ) {
  2155. NetApiBufferFree( DnList[ i ] );
  2156. }
  2157. NetApiBufferFree( DnList );
  2158. } else {
  2159. *OUs = DnList;
  2160. *OUCount = CurrentIndex;
  2161. }
  2162. if ( NetStatus == NERR_Success ) {
  2163. NetpLog(( "Found %lu OUs\n", *OUs ));
  2164. } else {
  2165. NetpLog(( "Failed to obtain the list of joinable OUs: %lu\n",
  2166. NetStatus ));
  2167. }
  2168. return( NetStatus );
  2169. }
  2170. NET_API_STATUS
  2171. NET_API_FUNCTION
  2172. NetpGetListOfJoinableOUsNonPaged(
  2173. IN LPWSTR Root,
  2174. IN PLDAP Ldap,
  2175. OUT PULONG OUCount,
  2176. OUT PWSTR **OUs
  2177. )
  2178. /*++
  2179. Routine Description:
  2180. This routine searches for all the OUs under the given domain root under which the bound
  2181. user has the rights to create a computer object
  2182. This routine does not use pageable searchs and will return only the max_search count
  2183. of entries
  2184. Arguments:
  2185. Root -- Root NC path
  2186. Ldap -- Connection to the server
  2187. OUCount -- Where the count of strings is returned
  2188. OUs -- Where the list of OUs is returned
  2189. Returns:
  2190. NERR_Success -- Success
  2191. --*/
  2192. {
  2193. NET_API_STATUS NetStatus = NERR_Success;
  2194. PWSTR Attribs[] = {
  2195. NETSETUPP_RETURNED_ATTR,
  2196. NULL
  2197. };
  2198. ULONG Count, Status, i;
  2199. PWSTR *Classes = NULL;
  2200. LDAPMessage *Message = NULL, *Entry;
  2201. PWSTR DN;
  2202. PWSTR *DnList = NULL, *NewList = NULL;
  2203. ULONG CurrentIndex = 0, ListCount = 0;
  2204. PWSTR DefaultOu = NULL;
  2205. NetpLog(( "Normal OU search\n" ));
  2206. Status = ldap_search_s( Ldap,
  2207. Root,
  2208. LDAP_SCOPE_SUBTREE,
  2209. NETSETUPP_OU_FILTER,
  2210. Attribs,
  2211. 0,
  2212. &Message );
  2213. if ( Message ) {
  2214. Entry = ldap_first_entry( Ldap, Message );
  2215. while ( Status == LDAP_SUCCESS && Entry ) {
  2216. //
  2217. // Read the list of classes that the current user is allowed to
  2218. // create under this OU and make sure that we can create a computer
  2219. // object
  2220. //
  2221. Classes = ldap_get_values( Ldap, Entry, Attribs[ 0 ] );
  2222. if ( Classes ) {
  2223. i = 0;
  2224. while ( Classes[ i ] ) {
  2225. if ( _wcsicmp( Classes[ i ], NETSETUPP_COMPUTER_OBJECT ) == 0 ) {
  2226. DN = ldap_get_dn( Ldap, Entry );
  2227. //
  2228. // We'll allocate the return list in blocks of 10 to cut
  2229. // down on the number of allocations
  2230. //
  2231. if ( CurrentIndex >= ListCount ) {
  2232. if ( NetApiBufferAllocate( ( ListCount + 10 ) * sizeof( PWSTR ),
  2233. ( PVOID * )&NewList ) != NERR_Success ) {
  2234. Status = LDAP_NO_MEMORY;
  2235. } else {
  2236. RtlCopyMemory( NewList,
  2237. DnList,
  2238. ListCount * sizeof( PWSTR ) );
  2239. ListCount += 10;
  2240. DnList = NewList;
  2241. }
  2242. }
  2243. //
  2244. // Copy the string
  2245. //
  2246. if ( Status == LDAP_SUCCESS ) {
  2247. if (NERR_Success ==
  2248. NetpDuplicateString(DN, -1, &NewList[CurrentIndex]))
  2249. {
  2250. CurrentIndex++;
  2251. }
  2252. else
  2253. {
  2254. Status = LDAP_NO_MEMORY;
  2255. }
  2256. }
  2257. ldap_memfree( DN );
  2258. break;
  2259. }
  2260. i++;
  2261. }
  2262. ldap_value_free( Classes );
  2263. }
  2264. Entry = ldap_next_entry( Ldap, Entry );
  2265. }
  2266. Status = Ldap->ld_errno;
  2267. ldap_msgfree( Message );
  2268. }
  2269. NetStatus = LdapMapErrorToWin32( Status );
  2270. //
  2271. // Check the computers container
  2272. //
  2273. if ( NetStatus == NERR_Success ) {
  2274. NetStatus = NetpGetDefaultJoinableOu( Root,
  2275. Ldap,
  2276. &DefaultOu );
  2277. if ( NetStatus == NERR_Success && DefaultOu ) {
  2278. //
  2279. // We'll allocate the return list in blocks of 10 to cut
  2280. // down on the number of allocations
  2281. //
  2282. if ( CurrentIndex >= ListCount ) {
  2283. if ( NetApiBufferAllocate( ( ListCount + 10 ) * sizeof( PWSTR ),
  2284. ( PVOID * )&NewList ) != NERR_Success ) {
  2285. Status = LDAP_NO_MEMORY;
  2286. } else {
  2287. RtlCopyMemory( NewList,
  2288. DnList,
  2289. ListCount * sizeof( PWSTR ) );
  2290. ListCount += 10;
  2291. DnList = NewList;
  2292. }
  2293. }
  2294. //
  2295. // Copy the string
  2296. //
  2297. if ( Status == LDAP_SUCCESS ) {
  2298. if (NERR_Success ==
  2299. NetpDuplicateString(DefaultOu, -1, &NewList[CurrentIndex]))
  2300. {
  2301. CurrentIndex++;
  2302. }
  2303. else
  2304. {
  2305. Status = LDAP_NO_MEMORY;
  2306. }
  2307. }
  2308. NetApiBufferFree( DefaultOu );
  2309. }
  2310. }
  2311. //
  2312. // If there was an error, free everyting
  2313. //
  2314. if ( NetStatus != NERR_Success ) {
  2315. for ( i = 0; i < ListCount; i++ ) {
  2316. NetApiBufferFree( DnList[ i ] );
  2317. }
  2318. NetApiBufferFree( DnList );
  2319. } else {
  2320. *OUs = DnList;
  2321. *OUCount = CurrentIndex;
  2322. }
  2323. if ( NetStatus == NERR_Success ) {
  2324. NetpLog(( "Found %lu OUs\n", *OUs ));
  2325. } else {
  2326. NetpLog(( "Failed to obtain the list of joinable OUs: %lu\n",
  2327. NetStatus ));
  2328. }
  2329. return( NetStatus );
  2330. }
  2331. NET_API_STATUS
  2332. NET_API_FUNCTION
  2333. NetpGetListOfJoinableOUs(
  2334. IN LPWSTR Domain,
  2335. IN LPWSTR Account,
  2336. IN LPWSTR Password,
  2337. OUT PULONG Count,
  2338. OUT PWSTR **OUs
  2339. )
  2340. /*++
  2341. Routine Description:
  2342. This routine searches for all the OUs under the given domain root under which the bound
  2343. user has the rights to create a computer object
  2344. Arguments:
  2345. Domain -- Domain under which to find all of the OUs under which a computer object can be
  2346. created
  2347. Account -- Account to use for the LDAP bind
  2348. Password -- Password to used for the bind. The password is encoded. The first WCHAR of the
  2349. password is the seed.
  2350. OUCount -- Where the count of strings is returned
  2351. OUs -- Where the list of OUs is returned
  2352. Returns:
  2353. NERR_Success -- Success
  2354. NERR_DefaultJoinRequired -- The servers for this domain do not support the DS so the computer
  2355. account can only be created under the default container (for NT4, this is the SAM account
  2356. database)
  2357. --*/
  2358. {
  2359. NET_API_STATUS NetStatus = NERR_Success;
  2360. PWSTR DomainControllerName = NULL;
  2361. ULONG DcFlags = 0;
  2362. PLDAP Ldap = NULL;
  2363. PWSTR NCRoot;
  2364. BOOLEAN Pageable = FALSE;
  2365. UCHAR Seed;
  2366. UNICODE_STRING EncodedPassword;
  2367. NetSetuppOpenLog();
  2368. if ( Password ) {
  2369. if ( wcslen( Password ) < 1 ) {
  2370. return( ERROR_INVALID_PARAMETER );
  2371. }
  2372. Seed = ( UCHAR )*Password;
  2373. RtlInitUnicodeString( &EncodedPassword, Password + 1 );
  2374. } else {
  2375. RtlZeroMemory( &EncodedPassword, sizeof( UNICODE_STRING ) );
  2376. Seed = 0;
  2377. }
  2378. //
  2379. // First, find a DC in the destination domain
  2380. //
  2381. NetStatus = NetpDsGetDcName( NULL,
  2382. Domain,
  2383. NULL,
  2384. NETSETUPP_DSGETDC_FLAGS,
  2385. &DcFlags,
  2386. &DomainControllerName
  2387. ,NULL
  2388. );
  2389. if ( NetStatus == NERR_Success ) {
  2390. //
  2391. // Try and bind to the server
  2392. //
  2393. RtlRunDecodeUnicodeString( Seed, &EncodedPassword );
  2394. NetStatus = NetpLdapBind( DomainControllerName,
  2395. Account,
  2396. EncodedPassword.Buffer,
  2397. &Ldap );
  2398. RtlRunEncodeUnicodeString( &Seed, &EncodedPassword );
  2399. if ( NetStatus == NERR_Success ) {
  2400. //
  2401. // Get the X500 domain name
  2402. //
  2403. NetStatus = NetpGetNCRoot( Ldap,
  2404. &NCRoot,
  2405. &Pageable );
  2406. if ( NetStatus == NERR_Success ) {
  2407. //
  2408. // Get the list of OUs
  2409. //
  2410. if ( Pageable ) {
  2411. NetStatus = NetpGetListOfJoinableOUsPaged( NCRoot,
  2412. Ldap,
  2413. Count,
  2414. OUs );
  2415. } else {
  2416. NetStatus = NetpGetListOfJoinableOUsNonPaged( NCRoot,
  2417. Ldap,
  2418. Count,
  2419. OUs );
  2420. }
  2421. NetApiBufferFree( NCRoot );
  2422. }
  2423. NetpLdapUnbind( Ldap );
  2424. } else if ( NetStatus == ERROR_BAD_NET_RESP ) {
  2425. NetStatus = NERR_DefaultJoinRequired;
  2426. }
  2427. NetApiBufferFree( DomainControllerName );
  2428. }
  2429. if ( NetStatus != NERR_Success ) {
  2430. NetpLog(( "NetpGetListOfJoinableOUs failed with %lu\n",
  2431. NetStatus ));
  2432. }
  2433. NetSetuppCloseLog( );
  2434. return( NetStatus );
  2435. }
  2436. NET_API_STATUS
  2437. NetpGetDnsHostName(
  2438. IN LPWSTR PassedHostName OPTIONAL,
  2439. IN PUNICODE_STRING DnsDomainName,
  2440. OUT LPWSTR *DnsHostName
  2441. )
  2442. /*++
  2443. Routine Description:
  2444. This routine determines the value of DnsHostName attribute to be set on the
  2445. computer object in the DS. DnsHostName is <HostName.PrimaryDnsSuffix>.
  2446. Here HostName is a computer name which may be different from the Netbios name;
  2447. Netbios name is at most 15 characters of HostName. PrimaryDnsSuffix can be
  2448. set through Policy or through the UI or can be defaulted to the DNS name of the
  2449. domain being joined; policy setting takes preference.
  2450. This routine determines *new* values for HostName and PrimaryDnsSuffix which
  2451. will be applied after the reboot. Thus DnsHostName will have the correct value
  2452. after the machine reboots.
  2453. Arguments:
  2454. PassedHostName - The host name of this machine (can be longer than 15 chars).
  2455. If NULL, the host name is read from the registry.
  2456. DnsDomainName - DNS name of the domain being joined
  2457. DnsHostname - Returns the value of DnsHostName. Must be freed by calling
  2458. NetApiBufferFree.
  2459. Returns:
  2460. NO_ERROR - Success
  2461. ERROR_NOT_ENOUGH_MEMORY - There was not enough memory to read the data from
  2462. registry
  2463. ERROR_INVALID_COMPUTERNAME - It was not possible to determine DnsHostName from
  2464. the registry
  2465. --*/
  2466. {
  2467. LONG RegStatus;
  2468. HKEY Key = NULL;
  2469. DWORD Type;
  2470. NET_API_STATUS NetStatus;
  2471. PWSTR HostName = NULL;
  2472. PWSTR PrimaryDnsSuffix = NULL;
  2473. LPWSTR LocalDnsHostName;
  2474. DWORD Size = 0;
  2475. //
  2476. // First detemine HostName.
  2477. //
  2478. // If it's passed, use it
  2479. //
  2480. if ( PassedHostName != NULL ) {
  2481. HostName = PassedHostName;
  2482. //
  2483. // Otherwise, read it from teh registry
  2484. //
  2485. } else {
  2486. RegStatus = RegOpenKeyExW( HKEY_LOCAL_MACHINE,
  2487. L"System\\CurrentControlSet\\Services\\Tcpip\\Parameters",
  2488. 0,
  2489. KEY_QUERY_VALUE,
  2490. &Key );
  2491. //
  2492. // Not having host name is critical -- error out in such case
  2493. //
  2494. if ( RegStatus != ERROR_SUCCESS ) {
  2495. NetpLog(( "NetpGetDnsHostName: Cannot open TCPIP parameters: 0x%lx\n", RegStatus ));
  2496. } else {
  2497. //
  2498. // First try to read the new value
  2499. //
  2500. RegStatus = RegQueryValueExW( Key,
  2501. L"NV Hostname",
  2502. 0,
  2503. &Type,
  2504. NULL,
  2505. &Size );
  2506. if ( RegStatus == ERROR_SUCCESS && Size != 0 ) {
  2507. HostName = LocalAlloc( 0, Size );
  2508. if ( HostName == NULL ) {
  2509. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2510. goto Cleanup;
  2511. }
  2512. RegStatus = RegQueryValueExW( Key,
  2513. L"NV Hostname",
  2514. 0,
  2515. &Type,
  2516. (PUCHAR) HostName,
  2517. &Size );
  2518. if ( RegStatus != ERROR_SUCCESS ) {
  2519. NetpLog(( "NetpGetDnsHostName: Cannot read NV Hostname: 0x%lx\n", RegStatus ));
  2520. NetStatus = ERROR_INVALID_COMPUTERNAME;
  2521. goto Cleanup;
  2522. } else {
  2523. NetpLog(( "NetpGetDnsHostName: Read NV Hostname: %ws\n", HostName ));
  2524. }
  2525. }
  2526. //
  2527. // If the new value does not exist for some reason,
  2528. // try to read the currently active one
  2529. //
  2530. if ( HostName == NULL ) {
  2531. RegStatus = RegQueryValueExW( Key,
  2532. L"Hostname",
  2533. 0,
  2534. &Type,
  2535. NULL,
  2536. &Size );
  2537. if ( RegStatus == ERROR_SUCCESS && Size != 0 ) {
  2538. HostName = LocalAlloc( 0, Size );
  2539. if ( HostName == NULL ) {
  2540. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2541. goto Cleanup;
  2542. }
  2543. RegStatus = RegQueryValueExW( Key,
  2544. L"Hostname",
  2545. 0,
  2546. &Type,
  2547. (PUCHAR) HostName,
  2548. &Size );
  2549. if ( RegStatus != ERROR_SUCCESS ) {
  2550. NetpLog(( "NetpGetDnsHostName: Cannot read Hostname: 0x%lx\n", RegStatus ));
  2551. NetStatus = ERROR_INVALID_COMPUTERNAME;
  2552. goto Cleanup;
  2553. } else {
  2554. NetpLog(( "NetpGetDnsHostName: Read Hostname: %ws\n", HostName ));
  2555. }
  2556. }
  2557. }
  2558. }
  2559. }
  2560. //
  2561. // If we couldn't get HostName, something's really bad
  2562. //
  2563. if ( HostName == NULL ) {
  2564. NetpLog(( "NetpGetDnsHostName: Could not get Hostname\n" ));
  2565. NetStatus = ERROR_INVALID_COMPUTERNAME;
  2566. goto Cleanup;
  2567. }
  2568. if ( Key != NULL ) {
  2569. RegCloseKey( Key );
  2570. Key = NULL;
  2571. }
  2572. //
  2573. // Second read primary DNS suffix of this machine.
  2574. //
  2575. // Try the suffix that comes down through policy first
  2576. //
  2577. RegStatus = RegOpenKeyExW( HKEY_LOCAL_MACHINE,
  2578. L"Software\\Policies\\Microsoft\\System\\DNSclient",
  2579. 0,
  2580. KEY_QUERY_VALUE,
  2581. &Key );
  2582. if ( RegStatus == 0 ) {
  2583. //
  2584. // Read only the new value; if it doesn't exist the
  2585. // current value will be deleted after the reboot
  2586. //
  2587. RegStatus = RegQueryValueExW( Key,
  2588. L"NV PrimaryDnsSuffix",
  2589. 0,
  2590. &Type,
  2591. NULL,
  2592. &Size );
  2593. if ( RegStatus == ERROR_SUCCESS && Size != 0 ) {
  2594. PrimaryDnsSuffix = LocalAlloc( 0, Size );
  2595. if ( PrimaryDnsSuffix == NULL ) {
  2596. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2597. goto Cleanup;
  2598. }
  2599. RegStatus = RegQueryValueExW( Key,
  2600. L"NV PrimaryDnsSuffix",
  2601. 0,
  2602. &Type,
  2603. (PUCHAR) PrimaryDnsSuffix,
  2604. &Size );
  2605. if ( RegStatus != ERROR_SUCCESS ) {
  2606. NetpLog(( "NetpGetDnsHostName: Cannot read NV PrimaryDnsSuffix: 0x%lx\n", RegStatus ));
  2607. NetStatus = ERROR_INVALID_COMPUTERNAME;
  2608. goto Cleanup;
  2609. } else {
  2610. NetpLog(( "NetpGetDnsHostName: Read NV PrimaryDnsSuffix: %ws\n", PrimaryDnsSuffix ));
  2611. }
  2612. }
  2613. }
  2614. //
  2615. // If there is no policy setting for PrimaryDnsSuffix,
  2616. // get it from the TCPIP setting
  2617. //
  2618. if ( Key != NULL ) {
  2619. RegCloseKey( Key );
  2620. Key = NULL;
  2621. }
  2622. if ( PrimaryDnsSuffix == NULL ) {
  2623. RegStatus = RegOpenKeyExW( HKEY_LOCAL_MACHINE,
  2624. L"System\\CurrentControlSet\\Services\\Tcpip\\Parameters",
  2625. 0,
  2626. KEY_QUERY_VALUE,
  2627. &Key );
  2628. if ( RegStatus == ERROR_SUCCESS ) {
  2629. ULONG SyncValue;
  2630. Size = sizeof( ULONG );
  2631. RegStatus = RegQueryValueEx( Key,
  2632. L"SyncDomainWithMembership",
  2633. 0,
  2634. &Type,
  2635. (PUCHAR)&SyncValue,
  2636. &Size );
  2637. //
  2638. // If we are not to sync DNS suffix with the name of the
  2639. // domain that we join, get the configured suffix
  2640. //
  2641. if ( RegStatus == ERROR_SUCCESS && SyncValue == 0 ) {
  2642. //
  2643. // Read the new value
  2644. //
  2645. RegStatus = RegQueryValueExW( Key,
  2646. L"NV Domain",
  2647. 0,
  2648. &Type,
  2649. NULL,
  2650. &Size );
  2651. if ( RegStatus == ERROR_SUCCESS && Size != 0 ) {
  2652. PrimaryDnsSuffix = LocalAlloc( 0, Size );
  2653. if ( PrimaryDnsSuffix == NULL ) {
  2654. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2655. goto Cleanup;
  2656. }
  2657. RegStatus = RegQueryValueExW( Key,
  2658. L"NV Domain",
  2659. 0,
  2660. &Type,
  2661. (PUCHAR) PrimaryDnsSuffix,
  2662. &Size );
  2663. if ( RegStatus != ERROR_SUCCESS ) {
  2664. NetpLog(( "NetpGetDnsHostName: Cannot read NV Domain: 0x%lx\n", RegStatus ));
  2665. NetStatus = ERROR_INVALID_COMPUTERNAME;
  2666. goto Cleanup;
  2667. } else {
  2668. NetpLog(( "NetpGetDnsHostName: Read NV Domain: %ws\n", PrimaryDnsSuffix ));
  2669. }
  2670. }
  2671. //
  2672. // If the new value does not exist for some reason,
  2673. // read the currently active one
  2674. //
  2675. if ( PrimaryDnsSuffix == NULL ) {
  2676. RegStatus = RegQueryValueExW( Key,
  2677. L"Domain",
  2678. 0,
  2679. &Type,
  2680. NULL,
  2681. &Size );
  2682. if ( RegStatus == ERROR_SUCCESS && Size != 0 ) {
  2683. PrimaryDnsSuffix = LocalAlloc( 0, Size );
  2684. if ( PrimaryDnsSuffix == NULL ) {
  2685. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2686. goto Cleanup;
  2687. }
  2688. RegStatus = RegQueryValueExW( Key,
  2689. L"Domain",
  2690. 0,
  2691. &Type,
  2692. (PUCHAR) PrimaryDnsSuffix,
  2693. &Size );
  2694. if ( RegStatus != ERROR_SUCCESS ) {
  2695. NetpLog(( "NetpGetDnsHostName: Cannot read Domain: 0x%lx\n", RegStatus ));
  2696. NetStatus = ERROR_INVALID_COMPUTERNAME;
  2697. goto Cleanup;
  2698. } else {
  2699. NetpLog(( "NetpGetDnsHostName: Read Domain: %ws\n", PrimaryDnsSuffix ));
  2700. }
  2701. }
  2702. }
  2703. }
  2704. }
  2705. }
  2706. //
  2707. // If we still have no PrimaryDnsSuffix, use DNS name of the domain we join
  2708. //
  2709. if ( PrimaryDnsSuffix == NULL ) {
  2710. NetpLog(( "NetpGetDnsHostName: PrimaryDnsSuffix defaulted to DNS domain name: %wZ\n", DnsDomainName ));
  2711. PrimaryDnsSuffix = LocalAlloc( 0, DnsDomainName->Length + sizeof(WCHAR) );
  2712. if ( PrimaryDnsSuffix == NULL ) {
  2713. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2714. goto Cleanup;
  2715. }
  2716. RtlCopyMemory( PrimaryDnsSuffix,
  2717. DnsDomainName->Buffer,
  2718. DnsDomainName->Length );
  2719. PrimaryDnsSuffix[ (DnsDomainName->Length)/sizeof(WCHAR) ] = UNICODE_NULL;
  2720. }
  2721. //
  2722. // Now we have Hostname and Primary DNS suffix.
  2723. // Connect them with . to form DnsHostName.
  2724. //
  2725. NetStatus = NetApiBufferAllocate(
  2726. (wcslen(HostName) + 1 + wcslen(PrimaryDnsSuffix) + 1) * sizeof(WCHAR),
  2727. &LocalDnsHostName );
  2728. if ( NetStatus != NO_ERROR ) {
  2729. goto Cleanup;
  2730. }
  2731. wcscpy( LocalDnsHostName, HostName );
  2732. wcscat( LocalDnsHostName, L"." );
  2733. wcscat( LocalDnsHostName, PrimaryDnsSuffix );
  2734. //
  2735. // If we are here, it's a success
  2736. //
  2737. *DnsHostName = LocalDnsHostName;
  2738. NetStatus = NO_ERROR;
  2739. Cleanup:
  2740. if ( Key != NULL ) {
  2741. RegCloseKey( Key );
  2742. }
  2743. if ( HostName != NULL && HostName != PassedHostName ) {
  2744. LocalFree( HostName );
  2745. }
  2746. if ( PrimaryDnsSuffix != NULL ) {
  2747. LocalFree( PrimaryDnsSuffix );
  2748. }
  2749. return NetStatus;
  2750. }
  2751. VOID
  2752. NetpRemoveDuplicateStrings(
  2753. IN PWCHAR *Source,
  2754. IN OUT PWCHAR *Target
  2755. )
  2756. /*++
  2757. Routine Description:
  2758. This routine accepts two pointer arrays and removes those entries
  2759. from the target array which point to strings that are identical to
  2760. one of the strings pointed to by the entries in the source array.
  2761. On return, the target array entries which precede the NULL terminator
  2762. will point to strings which are different from any of the strings
  2763. pointed to by the source array elements.
  2764. Arguments:
  2765. Source -- The NULL terminated array of pointes to source strings.
  2766. For example:
  2767. Source[0] = L"abc";
  2768. Source[1] = L"def";
  2769. Source[2] = NULL;
  2770. Target -- The NULL terminated array of pointes to target strings.
  2771. For example:
  2772. Target[0] = L"abc";
  2773. Target[1] = L"ghi";
  2774. Target[2] = L"def";
  2775. Target[3] = NULL;
  2776. On return, the Target array will be, for our example:
  2777. Target[0] = L"ghi";
  2778. Target[1] = NULL;
  2779. Target[2] = L"def";
  2780. Target[3] = NULL;
  2781. Note that, on return, the target array has size of 1 and
  2782. contains only one valid pointer.
  2783. Returns:
  2784. VOID
  2785. --*/
  2786. {
  2787. PWCHAR *TargetPtr, *TargetNextPtr, *SourcePtr;
  2788. BOOL KeepEntry;
  2789. //
  2790. // Sanity check
  2791. //
  2792. if ( Source == NULL || *Source == NULL ||
  2793. Target == NULL || *Target == NULL ) {
  2794. return;
  2795. }
  2796. //
  2797. // Loop through the target and compare with the source
  2798. //
  2799. for ( TargetPtr = TargetNextPtr = Target;
  2800. *TargetNextPtr != NULL;
  2801. TargetNextPtr++ ) {
  2802. KeepEntry = TRUE;
  2803. for ( SourcePtr = Source; *SourcePtr != NULL; SourcePtr++ ) {
  2804. if ( _wcsicmp( *SourcePtr, *TargetNextPtr ) == 0 ) {
  2805. KeepEntry = FALSE;
  2806. break;
  2807. }
  2808. }
  2809. if ( KeepEntry ) {
  2810. *TargetPtr = *TargetNextPtr;
  2811. TargetPtr ++;
  2812. }
  2813. }
  2814. //
  2815. // Terminate the target array
  2816. //
  2817. *TargetPtr = NULL;
  2818. return;
  2819. }
  2820. DWORD
  2821. NetpCrackNamesStatus2Win32Error(
  2822. DWORD dwStatus
  2823. )
  2824. {
  2825. switch (dwStatus) {
  2826. case DS_NAME_ERROR_RESOLVING:
  2827. return ERROR_DS_NAME_ERROR_RESOLVING;
  2828. case DS_NAME_ERROR_NOT_FOUND:
  2829. return ERROR_DS_NAME_ERROR_NOT_FOUND;
  2830. case DS_NAME_ERROR_NOT_UNIQUE:
  2831. return ERROR_DS_NAME_ERROR_NOT_UNIQUE;
  2832. case DS_NAME_ERROR_NO_MAPPING:
  2833. return ERROR_DS_NAME_ERROR_NO_MAPPING;
  2834. case DS_NAME_ERROR_DOMAIN_ONLY:
  2835. return ERROR_DS_NAME_ERROR_DOMAIN_ONLY;
  2836. case DS_NAME_ERROR_NO_SYNTACTICAL_MAPPING:
  2837. return ERROR_DS_NAME_ERROR_NO_SYNTACTICAL_MAPPING;
  2838. }
  2839. return ERROR_FILE_NOT_FOUND;
  2840. }
  2841. //
  2842. // Machine account attributes in the DS
  2843. //
  2844. #define NETSETUPP_OBJECTCLASS L"objectClass"
  2845. #define NETSETUPP_SAMACCOUNTNAME L"SamAccountName"
  2846. #define NETSETUPP_DNSHOSTNAME L"DnsHostName"
  2847. #define NETSETUPP_SERVICEPRINCIPALNAME L"ServicePrincipalName"
  2848. #define NETSETUPP_USERACCOUNTCONTROL L"userAccountControl"
  2849. #define NETSETUPP_UNICODEPWD L"unicodePwd"
  2850. #define NETSETUPP_ORGANIZATIONALUNIT L"OrganizationalUnit"
  2851. #define NETSETUPP_HOST_SPN_PREFIX L"HOST/"
  2852. #define NETSETUPP_COMP_OBJ_ATTR_COUNT 6
  2853. #define NETSETUPP_MULTIVAL_ATTRIB 0x01
  2854. #define NETSETUPP_COMPUTER_CONTAINER_GUID_IN_B32_FORM L"B:32:" GUID_COMPUTRS_CONTAINER_W L":"
  2855. typedef struct _NETSETUPP_MACH_ACC_ATTRIBUTE {
  2856. PWSTR AttribType; // Type of the attribute
  2857. ULONG AttribFlags; // Attribute flags
  2858. PWSTR *AttribValues; // Values of the attribute
  2859. } NETSETUPP_MACH_ACC_ATTRIBUTE, *PNETSETUPP_MACH_ACC_ATTRIBUTE;
  2860. NET_API_STATUS
  2861. NET_API_FUNCTION
  2862. NetpGetComputerObjectDn(
  2863. IN PDOMAIN_CONTROLLER_INFO DcInfo,
  2864. IN LPWSTR Account,
  2865. IN LPWSTR Password,
  2866. IN PLDAP Ldap,
  2867. IN LPWSTR ComputerName,
  2868. IN LPWSTR OU OPTIONAL,
  2869. OUT LPWSTR *ComputerObjectDn
  2870. )
  2871. /*++
  2872. Routine Description:
  2873. Get the DN for the computer account in the specified OU.
  2874. The algorithm is as follows.
  2875. First try to get the DN of the pre-existing account (if any)
  2876. by cracking the account name into a DN. If that succeeds, verify
  2877. that the passed OU (if any) matches the cracked DN. If the OU
  2878. matches, return success, otherwise return error (ERROR_FILE_EXISTS).
  2879. If no OU is not passed, simply return the cracked DN.
  2880. If the account does not exist, verify that the passed OU
  2881. (if any) exists. If so, build the DN from the computer name and
  2882. the OU and return it. If no OU is passed, get the default computer
  2883. container name (by reading the WellKnownObjects attribute) and build
  2884. the DN using the computer name and the default computer container DN.
  2885. Arguments:
  2886. DcInfo - Domain controller on which to create the object
  2887. Account - Account to use for the LDAP bind
  2888. Password - Password to used for the bind
  2889. Ldap - Ldap binding to the DC
  2890. ComputerName - Name of the computer being joined
  2891. OU - OU under which to create the object.
  2892. The name must be a fully qualified name
  2893. e.g.: "ou=test,dc=ntdev,dc=microsoft,dc=com"
  2894. NULL indicates to use the default computer container
  2895. ComputerObjectDn - Returns the DN of the computer object.
  2896. The retuned buffer must be freed using NetApiBufferFree
  2897. Returns:
  2898. NO_ERROR -- Success
  2899. ERROR_DS_NAME_ERROR_NOT_UNIQUE -- One of names being cracked
  2900. (the Netbios domain name or the pre-existing account name
  2901. or the root DN) is not unique.
  2902. ERROR_FILE_EXISTS -- The OU passed does not match the cracked DN
  2903. of the pre-existing account.
  2904. ERROR_FILE_NOT_FOUND -- The specified OU does not exist or
  2905. Could not get/read the WellKnownObjects attribute or
  2906. Could not get the default computer container name from the
  2907. WellKnownObjects attribute.
  2908. ERROR_NOT_ENOUGH_MEMORY -- Could not allocated memory required.
  2909. One of the errors returned by DsCrackNames.
  2910. (see NetpCrackNamesStatus2Win32Error())
  2911. --*/
  2912. {
  2913. NET_API_STATUS NetStatus = NO_ERROR;
  2914. ULONG LdapStatus;
  2915. HANDLE hDs = NULL;
  2916. PWCHAR AccountUserName = NULL;
  2917. PWCHAR AccountDomainName = NULL;
  2918. LPWSTR NetbiosDomainNameWithBackslash = NULL;
  2919. PWCHAR ComputerContainerDn = NULL;
  2920. PWCHAR NameToCrack = NULL;
  2921. RPC_AUTH_IDENTITY_HANDLE AuthId = 0;
  2922. PDS_NAME_RESULTW CrackedName = NULL;
  2923. PWCHAR WellKnownObjectsAttr[2];
  2924. PWSTR *WellKnownObjectValues = NULL;
  2925. LDAPMessage *LdapMessage = NULL, *LdapEntry = NULL;
  2926. LPWSTR LocalComputerObjectDn = NULL;
  2927. ULONG Index;
  2928. //
  2929. // First check whether the account already exists for the computer
  2930. //
  2931. // If account is passed, prepare the corresponding credentials.
  2932. // Otherwise, use the default creds of the user running this routine.
  2933. //
  2934. if ( Account != NULL ) {
  2935. NetStatus = NetpSeparateUserAndDomain( Account, &AccountUserName, &AccountDomainName );
  2936. if ( NetStatus != NERR_Success ) {
  2937. NetpLog(( "NetpGetComputerObjectDn: Cannot NetpSeparateUserAndDomain 0x%lx\n", NetStatus ));
  2938. goto Cleanup;
  2939. }
  2940. NetStatus = DsMakePasswordCredentials( AccountUserName,
  2941. AccountDomainName,
  2942. Password,
  2943. &AuthId);
  2944. if ( NetStatus != NERR_Success ) {
  2945. NetpLog(( "NetpGetComputerObjectDn: Cannot DsMakePasswordCredentials 0x%lx\n", NetStatus ));
  2946. goto Cleanup;
  2947. }
  2948. }
  2949. //
  2950. // Bind to the DS on the DC.
  2951. //
  2952. NetStatus = DsBindWithCredW( DcInfo->DomainControllerName, NULL, AuthId, &hDs);
  2953. if ( NetStatus != NO_ERROR ) {
  2954. NetpLog(( "NetpGetComputerObjectDn: Unable to bind to DS on '%ws': 0x%lx\n",
  2955. DcInfo->DomainControllerName, NetStatus ));
  2956. goto Cleanup ;
  2957. }
  2958. //
  2959. // Attempt to crack the account name into a DN.
  2960. //
  2961. // We need to have the Netbios domain name to
  2962. // form an NT4 style account name since DsCrackNames
  2963. // doesn't accept DNS domain names for cracking accounts.
  2964. // So, if we have a DNS domain name, we need to crack it
  2965. // into a Netbios domain name first.
  2966. //
  2967. if ( (DcInfo->Flags & DS_DNS_DOMAIN_FLAG) == 0 ) {
  2968. NetbiosDomainNameWithBackslash = LocalAlloc( 0, (wcslen(DcInfo->DomainName) + 2) * sizeof(WCHAR) );
  2969. if ( NetbiosDomainNameWithBackslash == NULL ) {
  2970. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2971. goto Cleanup;
  2972. }
  2973. swprintf( NetbiosDomainNameWithBackslash, L"%ws\\", DcInfo->DomainName );
  2974. } else {
  2975. NameToCrack = LocalAlloc( 0, (wcslen(DcInfo->DomainName) + 1 + 1) * sizeof(WCHAR) );
  2976. if ( NameToCrack == NULL ) {
  2977. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2978. goto Cleanup;
  2979. }
  2980. swprintf( NameToCrack, L"%ws/", DcInfo->DomainName );
  2981. //
  2982. // Be verbose
  2983. //
  2984. NetpLog(( "NetpGetComputerObjectDn: Cracking DNS domain name %ws into Netbios on %ws\n",
  2985. NameToCrack,
  2986. DcInfo->DomainControllerName ));
  2987. if ( CrackedName != NULL ) {
  2988. DsFreeNameResultW( CrackedName );
  2989. CrackedName = NULL;
  2990. }
  2991. //
  2992. // Crack the DNS domain name into a Netbios domain name
  2993. //
  2994. NetStatus = DsCrackNamesW( hDs,
  2995. 0,
  2996. DS_CANONICAL_NAME,
  2997. DS_NT4_ACCOUNT_NAME,
  2998. 1,
  2999. &NameToCrack,
  3000. &CrackedName );
  3001. if ( NetStatus != NO_ERROR ) {
  3002. NetpLog(( "NetpGetComputerObjectDn: CrackNames failed for %ws: 0x%lx\n",
  3003. NameToCrack,
  3004. NetStatus ));
  3005. goto Cleanup ;
  3006. }
  3007. //
  3008. // Check for consistency
  3009. //
  3010. if ( CrackedName->cItems != 1 ) {
  3011. NetStatus = ERROR_DS_NAME_ERROR_NOT_UNIQUE;
  3012. NetpLog(( "NetpGetComputerObjectDn: Cracked Name %ws is not unique: %lu\n",
  3013. NameToCrack,
  3014. CrackedName->cItems ));
  3015. goto Cleanup ;
  3016. }
  3017. if ( CrackedName->rItems[0].status != DS_NAME_NO_ERROR ) {
  3018. NetpLog(( "NetpGetComputerObjectDn: CrackNames failed for %ws: substatus 0x%lx\n",
  3019. NameToCrack,
  3020. CrackedName->rItems[0].status ));
  3021. NetStatus = NetpCrackNamesStatus2Win32Error( CrackedName->rItems[0].status );
  3022. goto Cleanup ;
  3023. }
  3024. //
  3025. // Be verbose
  3026. //
  3027. NetpLog(( "NetpGetComputerObjectDn: Crack results: \tname = %ws\n",
  3028. CrackedName->rItems[0].pName ));
  3029. //
  3030. // We've got the Netbios domain name
  3031. // (the cracked name already includes the trailing backslash)
  3032. //
  3033. NetbiosDomainNameWithBackslash = LocalAlloc( 0, (wcslen(CrackedName->rItems[0].pName) + 1) * sizeof(WCHAR) );
  3034. if ( NetbiosDomainNameWithBackslash == NULL ) {
  3035. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  3036. goto Cleanup;
  3037. }
  3038. wcscpy( NetbiosDomainNameWithBackslash, CrackedName->rItems[0].pName );
  3039. }
  3040. //
  3041. // Form the NT4 account name given the Netbios domain name
  3042. //
  3043. if ( NameToCrack != NULL ) {
  3044. LocalFree( NameToCrack );
  3045. NameToCrack = NULL;
  3046. }
  3047. NameToCrack = LocalAlloc( 0,
  3048. (wcslen(NetbiosDomainNameWithBackslash) + wcslen(ComputerName) + 1 + 1) * sizeof(WCHAR) );
  3049. if ( NameToCrack == NULL ) {
  3050. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  3051. goto Cleanup;
  3052. }
  3053. swprintf( NameToCrack, L"%ws%ws$", NetbiosDomainNameWithBackslash, ComputerName );
  3054. //
  3055. // Crack the account name into a DN
  3056. //
  3057. if ( CrackedName != NULL ) {
  3058. DsFreeNameResultW( CrackedName );
  3059. CrackedName = NULL;
  3060. }
  3061. //
  3062. // Be verbose
  3063. //
  3064. NetpLog(( "NetpGetComputerObjectDn: Cracking account name %ws on %ws\n",
  3065. NameToCrack,
  3066. DcInfo->DomainControllerName ));
  3067. NetStatus = DsCrackNamesW( hDs,
  3068. 0,
  3069. DS_NT4_ACCOUNT_NAME,
  3070. DS_FQDN_1779_NAME,
  3071. 1,
  3072. &NameToCrack,
  3073. &CrackedName );
  3074. if ( NetStatus != NO_ERROR ) {
  3075. NetpLog(( "NetpGetComputerObjectDn: CrackNames failed for %ws: 0x%lx\n",
  3076. NameToCrack,
  3077. NetStatus ));
  3078. goto Cleanup ;
  3079. }
  3080. //
  3081. // Check for consistency
  3082. //
  3083. if ( CrackedName->cItems > 1 ) {
  3084. NetStatus = ERROR_DS_NAME_ERROR_NOT_UNIQUE;
  3085. NetpLog(( "NetpGetComputerObjectDn: Cracked Name %ws is not unique: %lu\n",
  3086. NameToCrack,
  3087. CrackedName->cItems ));
  3088. goto Cleanup ;
  3089. }
  3090. //
  3091. // If the account alredy exists, verify that the passed OU (if any)
  3092. // matches that of the account DN
  3093. //
  3094. if ( CrackedName->rItems[0].status == DS_NAME_NO_ERROR ) {
  3095. ULONG DnSize;
  3096. NetpLog(( "NetpGetComputerObjectDn: Crack results: \t(Account already exists) DN = %ws\n",
  3097. CrackedName->rItems[0].pName ));
  3098. DnSize = ( wcslen(CrackedName->rItems[0].pName) + 1 ) * sizeof(WCHAR);
  3099. //
  3100. // Allocate storage for the computer object DN
  3101. //
  3102. NetStatus = NetApiBufferAllocate( DnSize, &LocalComputerObjectDn );
  3103. if ( NetStatus != NO_ERROR ) {
  3104. goto Cleanup;
  3105. }
  3106. //
  3107. // If the OU is passed, verify that it matches the cracked name
  3108. //
  3109. if ( OU != NULL ) {
  3110. ULONG DnSizeFromOu;
  3111. DnSizeFromOu = ( wcslen(NETSETUPP_OBJ_PREFIX) +
  3112. wcslen(ComputerName) + 1 + wcslen(OU) + 1 ) * sizeof(WCHAR);
  3113. if ( DnSizeFromOu != DnSize ) {
  3114. NetpLog(( "NetpGetComputerObjectDn: Passed OU doesn't match in size cracked DN: %lu %lu\n",
  3115. DnSizeFromOu,
  3116. DnSize ));
  3117. NetStatus = ERROR_FILE_EXISTS;
  3118. goto Cleanup;
  3119. }
  3120. swprintf( LocalComputerObjectDn, L"%ws%ws,%ws", NETSETUPP_OBJ_PREFIX, ComputerName, OU );
  3121. if ( _wcsicmp(LocalComputerObjectDn, CrackedName->rItems[0].pName) != 0 ) {
  3122. NetpLog(( "NetpGetComputerObjectDn: Passed OU doesn't match cracked DN: %ws %ws\n",
  3123. LocalComputerObjectDn,
  3124. CrackedName->rItems[0].pName ));
  3125. NetStatus = ERROR_FILE_EXISTS;
  3126. goto Cleanup;
  3127. }
  3128. //
  3129. // Otherwise, just use the cracked name
  3130. //
  3131. } else {
  3132. wcscpy( LocalComputerObjectDn, CrackedName->rItems[0].pName );
  3133. }
  3134. //
  3135. // We've got the computer object DN from the existing account
  3136. //
  3137. NetStatus = NO_ERROR;
  3138. goto Cleanup;
  3139. }
  3140. //
  3141. // Be verbose
  3142. //
  3143. NetpLog(( "NetpGetComputerObjectDn: Crack results: \tAccount does not exist\n" ));
  3144. //
  3145. // At this point, we know that the account does not exist
  3146. // If OU is passed, simply verify it
  3147. //
  3148. if ( OU != NULL ) {
  3149. LdapStatus = ldap_compare_s( Ldap,
  3150. OU,
  3151. NETSETUPP_OBJECTCLASS,
  3152. NETSETUPP_ORGANIZATIONALUNIT );
  3153. if ( LdapStatus == LDAP_COMPARE_FALSE ) {
  3154. NetStatus = ERROR_FILE_NOT_FOUND;
  3155. NetpLog(( "NetpGetComputerObjectDn: Specified path '%ws' is not an OU\n", OU ));
  3156. goto Cleanup;
  3157. } else if ( LdapStatus != LDAP_COMPARE_TRUE ) {
  3158. NetStatus = LdapMapErrorToWin32( LdapStatus );
  3159. NetpLog(( "NetpGetComputerObjectDn: ldap_compare_s failed: 0x%lx 0x%lx\n",
  3160. LdapStatus, NetStatus ));
  3161. goto Cleanup;
  3162. }
  3163. //
  3164. // OU has been verified.
  3165. // Allocate the computer object DN.
  3166. //
  3167. NetStatus = NetApiBufferAllocate(
  3168. ( wcslen(NETSETUPP_OBJ_PREFIX) +
  3169. wcslen(ComputerName) + 1 + wcslen(OU) + 1 ) * sizeof(WCHAR),
  3170. &LocalComputerObjectDn );
  3171. if ( NetStatus != NO_ERROR ) {
  3172. goto Cleanup;
  3173. }
  3174. //
  3175. // We've got the computer object DN from the OU passed
  3176. //
  3177. swprintf( LocalComputerObjectDn, L"%ws%ws,%ws", NETSETUPP_OBJ_PREFIX, ComputerName, OU );
  3178. NetpLog(( "NetpGetComputerObjectDn: Got DN %ws from the passed OU\n", LocalComputerObjectDn ));
  3179. NetStatus = NO_ERROR;
  3180. goto Cleanup;
  3181. }
  3182. //
  3183. // At this point, the account does not exist
  3184. // and no OU was specified. So get the default
  3185. // computer container DN.
  3186. //
  3187. if ( CrackedName != NULL ) {
  3188. DsFreeNameResultW( CrackedName );
  3189. CrackedName = NULL;
  3190. }
  3191. //
  3192. // Be verbose
  3193. //
  3194. NetpLog(( "NetpGetComputerObjectDn: Cracking Netbios domain name %ws into root DN on %ws\n",
  3195. NetbiosDomainNameWithBackslash,
  3196. DcInfo->DomainControllerName ));
  3197. NetStatus = DsCrackNamesW( hDs,
  3198. 0,
  3199. DS_NT4_ACCOUNT_NAME,
  3200. DS_FQDN_1779_NAME,
  3201. 1,
  3202. &NetbiosDomainNameWithBackslash,
  3203. &CrackedName );
  3204. if ( NetStatus != NO_ERROR ) {
  3205. NetpLog(( "NetpGetComputerObjectDn: CrackNames failed for %ws: 0x%lx\n",
  3206. NetbiosDomainNameWithBackslash,
  3207. NetStatus ));
  3208. goto Cleanup ;
  3209. }
  3210. //
  3211. // Check for consistency
  3212. //
  3213. if ( CrackedName->cItems != 1 ) {
  3214. NetStatus = ERROR_DS_NAME_ERROR_NOT_UNIQUE;
  3215. NetpLog(( "NetpGetComputerObjectDn: Cracked Name %ws is not unique: %lu\n",
  3216. NetbiosDomainNameWithBackslash,
  3217. CrackedName->cItems ));
  3218. goto Cleanup ;
  3219. }
  3220. if ( CrackedName->rItems[0].status != DS_NAME_NO_ERROR ) {
  3221. NetpLog(( "NetpGetComputerObjectDn: CrackNames failed for %ws: substatus 0x%lx\n",
  3222. NetbiosDomainNameWithBackslash,
  3223. CrackedName->rItems[0].status ));
  3224. NetStatus = NetpCrackNamesStatus2Win32Error( CrackedName->rItems[0].status );
  3225. goto Cleanup ;
  3226. }
  3227. //
  3228. // Be verbose
  3229. //
  3230. NetpLog(( "NetpGetComputerObjectDn: Crack results: \tname = %ws\n",
  3231. CrackedName->rItems[0].pName ));
  3232. //
  3233. // Now get the computer container DN given the root DN.
  3234. // The DN of the computer container is part of the wellKnownObjects
  3235. // attribute in the root of the domain. So, look it up.
  3236. //
  3237. WellKnownObjectsAttr[0] = L"wellKnownObjects";
  3238. WellKnownObjectsAttr[1] = NULL;
  3239. LdapStatus = ldap_search_s( Ldap,
  3240. CrackedName->rItems[0].pName, // Root DN
  3241. LDAP_SCOPE_BASE,
  3242. L"objectclass=*",
  3243. WellKnownObjectsAttr,
  3244. 0,
  3245. &LdapMessage);
  3246. if ( LdapStatus != LDAP_SUCCESS ) {
  3247. NetStatus = LdapMapErrorToWin32( LdapStatus );
  3248. NetpLog(( "NetpGetComputerObjectDn: ldap_search_s failed 0x%lx 0x%lx\n",
  3249. LdapStatus,
  3250. NetStatus ));
  3251. goto Cleanup;
  3252. }
  3253. if ( ldap_count_entries(Ldap, LdapMessage) == 0 ) {
  3254. NetStatus = ERROR_FILE_NOT_FOUND;
  3255. NetpLog(( "NetpGetComputerObjectDn: ldap_search_s returned no entries\n" ));
  3256. goto Cleanup;
  3257. }
  3258. LdapEntry = ldap_first_entry( Ldap, LdapMessage );
  3259. if ( LdapEntry == NULL ) {
  3260. NetStatus = ERROR_FILE_NOT_FOUND;
  3261. NetpLog(( "NetpGetComputerObjectDn: ldap_first_entry returned NULL\n" ));
  3262. goto Cleanup;
  3263. }
  3264. WellKnownObjectValues = ldap_get_valuesW( Ldap,
  3265. LdapEntry,
  3266. L"wellKnownObjects" );
  3267. if ( WellKnownObjectValues == NULL ) {
  3268. NetStatus = ERROR_FILE_NOT_FOUND;
  3269. NetpLog(( "NetpGetComputerObjectDn: ldap_get_valuesW returned NULL\n" ));
  3270. goto Cleanup;
  3271. }
  3272. //
  3273. // Lookup the default computer container
  3274. //
  3275. for ( Index = 0; WellKnownObjectValues[Index] != NULL; Index++ ) {
  3276. //
  3277. // The structure of this particular field is:
  3278. // L"B:32:GUID:DN" where GUID is AA312825768811D1ADED00C04FD8D5CD
  3279. //
  3280. if ( _wcsnicmp( WellKnownObjectValues[Index],
  3281. NETSETUPP_COMPUTER_CONTAINER_GUID_IN_B32_FORM,
  3282. wcslen(NETSETUPP_COMPUTER_CONTAINER_GUID_IN_B32_FORM) ) == 0 ) {
  3283. ComputerContainerDn = WellKnownObjectValues[Index] +
  3284. wcslen(NETSETUPP_COMPUTER_CONTAINER_GUID_IN_B32_FORM);
  3285. break;
  3286. }
  3287. }
  3288. //
  3289. // If we couldn't get the computer container DN, error out
  3290. //
  3291. if ( ComputerContainerDn == NULL || *ComputerContainerDn == L'\0' ) {
  3292. NetpLog(( "NetpGetComputerObjectDn: Couldn't get computer container DN\n" ));
  3293. NetStatus = ERROR_FILE_NOT_FOUND;
  3294. goto Cleanup;
  3295. }
  3296. //
  3297. // Allocate the computer object DN
  3298. //
  3299. NetStatus = NetApiBufferAllocate(
  3300. ( wcslen(NETSETUPP_OBJ_PREFIX) +
  3301. wcslen(ComputerName) + 1 + wcslen(ComputerContainerDn) + 1 ) * sizeof(WCHAR),
  3302. &LocalComputerObjectDn );
  3303. if ( NetStatus != NO_ERROR ) {
  3304. goto Cleanup;
  3305. }
  3306. //
  3307. // We've got the computer object DN from the default computer container
  3308. //
  3309. swprintf( LocalComputerObjectDn, L"%ws%ws,%ws", NETSETUPP_OBJ_PREFIX, ComputerName, ComputerContainerDn );
  3310. NetpLog(( "NetpGetComputerObjectDn: Got DN %ws from the default computer container\n", LocalComputerObjectDn ));
  3311. NetStatus = NO_ERROR;
  3312. //
  3313. // Free locally used resources
  3314. //
  3315. Cleanup:
  3316. if ( hDs ) {
  3317. DsUnBind( &hDs );
  3318. }
  3319. if ( CrackedName ) {
  3320. DsFreeNameResultW( CrackedName );
  3321. }
  3322. if ( AuthId ) {
  3323. DsFreePasswordCredentials( AuthId );
  3324. }
  3325. if ( WellKnownObjectValues != NULL ) {
  3326. ldap_value_free( WellKnownObjectValues );
  3327. }
  3328. if ( NameToCrack != NULL ) {
  3329. LocalFree( NameToCrack );
  3330. }
  3331. if ( AccountUserName != NULL ) {
  3332. NetApiBufferFree( AccountUserName );
  3333. }
  3334. if ( AccountDomainName != NULL ) {
  3335. NetApiBufferFree( AccountDomainName );
  3336. }
  3337. if ( NetbiosDomainNameWithBackslash != NULL ) {
  3338. LocalFree( NetbiosDomainNameWithBackslash );
  3339. }
  3340. if ( LdapMessage != NULL ) {
  3341. ldap_msgfree( LdapMessage );
  3342. }
  3343. if ( NetStatus == NO_ERROR ) {
  3344. *ComputerObjectDn = LocalComputerObjectDn;
  3345. } else if ( LocalComputerObjectDn != NULL ) {
  3346. NetApiBufferFree( LocalComputerObjectDn );
  3347. }
  3348. return NetStatus;
  3349. }
  3350. NET_API_STATUS
  3351. NET_API_FUNCTION
  3352. NetpModifyComputerObjectInDs(
  3353. IN LPWSTR DC,
  3354. IN PLDAP Ldap,
  3355. IN LPWSTR ComputerName,
  3356. IN LPWSTR ComputerObjectDn,
  3357. IN ULONG NumberOfAttributes,
  3358. IN OUT PNETSETUPP_MACH_ACC_ATTRIBUTE Attrib
  3359. )
  3360. /*++
  3361. Routine Description:
  3362. Create a computer account in the specified OU.
  3363. Arguments:
  3364. DC -- Domain controller on which to create the object
  3365. Ldap -- Ldap binding to the DC
  3366. ComputerName -- Name of the computer being joined
  3367. ComputerObjectDn -- DN of computer object being modified
  3368. NumberOfAttributes -- Number of attributes passed
  3369. Attrib -- List of attribute structures. The list may
  3370. be modified on return so that only those entries
  3371. that were not already set in the DS will be preserved.
  3372. NOTE: If the machine password (unicodePwd) is passed as one of the attributes,
  3373. it must be the last entry in the attribute list because this order is assumed
  3374. by the fail-over code below.
  3375. Returns:
  3376. NERR_Success -- Success
  3377. --*/
  3378. {
  3379. NET_API_STATUS NetStatus = NERR_Success;
  3380. ULONG LdapStatus;
  3381. PWSTR *AttribTypesList = NULL;
  3382. LDAPMod *ModList = NULL;
  3383. PLDAPMod *Mods = NULL;
  3384. LDAPMessage *Message = NULL, *Entry;
  3385. ULONG Index;
  3386. ULONG ModIndex = 0;
  3387. BOOL NewAccount = FALSE;
  3388. PWSTR SamAccountName = NULL;
  3389. USER_INFO_1 *CurrentUI1 = NULL;
  3390. //
  3391. // Allocate storage for the attribute list and the modifications block
  3392. //
  3393. NetStatus = NetApiBufferAllocate( (NumberOfAttributes+1)*sizeof(PWSTR),
  3394. (PVOID *) &AttribTypesList );
  3395. if ( NetStatus != NO_ERROR ) {
  3396. goto Cleanup;
  3397. }
  3398. NetStatus = NetApiBufferAllocate( NumberOfAttributes * sizeof(LDAPMod),
  3399. (PVOID *) &ModList );
  3400. if ( NetStatus != NO_ERROR ) {
  3401. goto Cleanup;
  3402. }
  3403. NetStatus = NetApiBufferAllocate( (NumberOfAttributes+1)*sizeof(PLDAPMod),
  3404. (PVOID *) &Mods );
  3405. if ( NetStatus != NO_ERROR ) {
  3406. goto Cleanup;
  3407. }
  3408. //
  3409. // Build modification list given the list of attributes
  3410. //
  3411. NetpLog(( "NetpModifyComputerObjectInDs: Initial attribute values:\n" ));
  3412. for ( Index = 0; Index < NumberOfAttributes; Index++ ) {
  3413. ModList[Index].mod_op = LDAP_MOD_ADD; // Set to add. We may adjust this below.
  3414. ModList[Index].mod_type = Attrib[Index].AttribType;
  3415. ModList[Index].mod_values = Attrib[Index].AttribValues;
  3416. //
  3417. // Be verbose - output all values of each attribute
  3418. //
  3419. NetpLog(( "\t\t%ws =", Attrib[Index].AttribType ));
  3420. //
  3421. // Don't leak sensitive info!
  3422. //
  3423. if ( _wcsicmp( Attrib[Index].AttribType, NETSETUPP_UNICODEPWD ) == 0 ) {
  3424. NetpLog(( " <SomePassword>" ));
  3425. } else {
  3426. PWSTR *CurrentValues;
  3427. for ( CurrentValues = Attrib[Index].AttribValues; *CurrentValues != NULL; CurrentValues++ ) {
  3428. NetpLog(( " %ws", *CurrentValues ));
  3429. }
  3430. }
  3431. NetpLog(( "\n" ));
  3432. }
  3433. //
  3434. // Now check which attribute values are already set in the DS
  3435. //
  3436. for ( Index = 0; Index < NumberOfAttributes; Index++ ) {
  3437. AttribTypesList[Index] = Attrib[Index].AttribType;
  3438. }
  3439. AttribTypesList[Index] = NULL; // Terminate the list
  3440. LdapStatus = ldap_search_s( Ldap,
  3441. ComputerObjectDn,
  3442. LDAP_SCOPE_BASE,
  3443. NULL,
  3444. AttribTypesList,
  3445. 0,
  3446. &Message );
  3447. //
  3448. // If the computer object does not exist,
  3449. // we need to add all attributes
  3450. //
  3451. if ( LdapStatus == LDAP_NO_SUCH_OBJECT ) {
  3452. NetpLog(( "NetpModifyComputerObjectInDs: Computer Object does not exist in OU\n" ));
  3453. NewAccount = TRUE;
  3454. for ( ModIndex = 0; ModIndex < NumberOfAttributes; ModIndex++ ) {
  3455. Mods[ModIndex] = &ModList[ModIndex];
  3456. }
  3457. //
  3458. // Terminate the modification list
  3459. //
  3460. Mods[ModIndex] = NULL;
  3461. //
  3462. // Otherwise see which attribute values need modification
  3463. //
  3464. } else if ( LdapStatus == LDAP_SUCCESS ) {
  3465. NetpLog(( "NetpModifyComputerObjectInDs: Computer Object already exists in OU:\n" ));
  3466. //
  3467. // Get the first entry (there should be only one)
  3468. //
  3469. Entry = ldap_first_entry( Ldap, Message );
  3470. //
  3471. // Loop through the attributes and weed out those values
  3472. // which are already set.
  3473. //
  3474. for ( Index = 0; Index < NumberOfAttributes; Index++ ) {
  3475. PWSTR *AttribValueRet = NULL;
  3476. //
  3477. // Be verbose - output the values returned for each type
  3478. //
  3479. NetpLog(( "\t\t%ws =", Attrib[Index].AttribType ));
  3480. AttribValueRet = ldap_get_values( Ldap, Entry, Attrib[Index].AttribType );
  3481. if ( AttribValueRet != NULL ) {
  3482. //
  3483. // Don't leak sensitive info!
  3484. //
  3485. if ( _wcsicmp( Attrib[Index].AttribType, NETSETUPP_UNICODEPWD ) == 0 ) {
  3486. NetpLog(( " <SomePassword>" ));
  3487. } else {
  3488. PWSTR *CurrentValueRet;
  3489. for ( CurrentValueRet = AttribValueRet; *CurrentValueRet != NULL; CurrentValueRet++ ) {
  3490. NetpLog(( " %ws", *CurrentValueRet ));
  3491. }
  3492. }
  3493. //
  3494. // Remove those values from the modification which are alredy set
  3495. //
  3496. NetpRemoveDuplicateStrings( AttribValueRet, Attrib[Index].AttribValues );
  3497. ldap_value_free( AttribValueRet );
  3498. //
  3499. // If this is a single valued attribute, we need to
  3500. // replace (not add) its value since it already exists in the DS
  3501. //
  3502. if ( (Attrib[Index].AttribFlags & NETSETUPP_MULTIVAL_ATTRIB) == 0 ) {
  3503. ModList[Index].mod_op = LDAP_MOD_REPLACE;
  3504. }
  3505. }
  3506. NetpLog(( "\n" ));
  3507. //
  3508. // If there are any attribute values which are
  3509. // not already set, add them to the modification.
  3510. //
  3511. if ( *(Attrib[Index].AttribValues) != NULL ) {
  3512. Mods[ModIndex] = &ModList[Index];
  3513. ModIndex ++;
  3514. }
  3515. }
  3516. //
  3517. // Terminate the modification list
  3518. //
  3519. Mods[ModIndex] = NULL;
  3520. //
  3521. // Otherwise, error out
  3522. //
  3523. } else {
  3524. NetStatus = LdapMapErrorToWin32( LdapStatus );
  3525. NetpLog(( "NetpModifyComputerObjectInDs: ldap_search_s failed: 0x%lx 0x%lx\n",
  3526. LdapStatus, NetStatus ));
  3527. goto Cleanup;
  3528. }
  3529. //
  3530. // If there are no modifications to do
  3531. // we are done.
  3532. //
  3533. if ( ModIndex == 0 ) {
  3534. NetpLog(( "NetpModifyComputerObjectInDs: There are _NO_ modifications to do\n" ));
  3535. NetStatus = NERR_Success;
  3536. goto Cleanup;
  3537. }
  3538. //
  3539. // Be verbose - output the attribute values to be set
  3540. //
  3541. NetpLog(( "NetpModifyComputerObjectInDs: Attribute values to set:\n" ));
  3542. for ( Index = 0; Mods[Index] != NULL; Index++ ) {
  3543. NetpLog(( "\t\t%ws =", (*(Mods[Index])).mod_type ));
  3544. //
  3545. // Don't leak sensitive info!
  3546. //
  3547. if ( _wcsicmp( (*(Mods[Index])).mod_type, NETSETUPP_UNICODEPWD ) == 0 ) {
  3548. NetpLog(( " <SomePassword>" ));
  3549. } else {
  3550. ULONG ValIndex;
  3551. for ( ValIndex = 0; ((*(Mods[Index])).mod_values)[ValIndex] != NULL; ValIndex++ ) {
  3552. NetpLog(( " %ws", ((*(Mods[Index])).mod_values)[ValIndex] ));
  3553. }
  3554. }
  3555. NetpLog(( "\n" ));
  3556. }
  3557. //
  3558. // Now, add the missing attributes
  3559. //
  3560. if ( NewAccount ) {
  3561. LdapStatus = ldap_add_s( Ldap, ComputerObjectDn, Mods );
  3562. } else {
  3563. LdapStatus = ldap_modify_s( Ldap, ComputerObjectDn, Mods );
  3564. }
  3565. //
  3566. // If we tried to create against a server that doesn't support 128 bit encryption,
  3567. // we get LDAP_UNWILLING_TO_PERFORM back from the server. So, we'll create the
  3568. // account again without the password, and then reset it using NetApi.
  3569. //
  3570. if ( LdapStatus == LDAP_UNWILLING_TO_PERFORM ) {
  3571. LPWSTR MachinePassword;
  3572. NetpLog(( "NetpModifyComputerObjectInDs: ldap_modify_s (1) returned"
  3573. " LDAP_UNWILLING_TO_PERFORM. Trying to recover.\n" ));
  3574. //
  3575. // If we didn't try to set a password, there is no legitimate reason
  3576. // for the add to fail.
  3577. //
  3578. if ( _wcsicmp( (*(Mods[ModIndex-1])).mod_type, NETSETUPP_UNICODEPWD ) != 0 ) {
  3579. NetpLog(( "NetpModifyComputerObjectInDs: ldap_modify_s retuned LDAP_UNWILLING_TO_PERFORM"
  3580. " but password was not being set\n" ));
  3581. NetStatus = LdapMapErrorToWin32( LdapStatus );
  3582. goto Cleanup;
  3583. }
  3584. MachinePassword = *((*(Mods[ModIndex-1])).mod_values);
  3585. //
  3586. // If there are other entries besides the password, retry to add them
  3587. //
  3588. if ( ModIndex > 1 ) {
  3589. Mods[ModIndex-1] = NULL;
  3590. if ( NewAccount ) {
  3591. LdapStatus = ldap_add_s( Ldap, ComputerObjectDn, Mods );
  3592. } else{
  3593. LdapStatus = ldap_modify_s( Ldap, ComputerObjectDn, Mods );
  3594. }
  3595. if ( LdapStatus != LDAP_SUCCESS ) {
  3596. NetStatus = LdapMapErrorToWin32( LdapStatus );
  3597. NetpLog(( "NetpModifyComputerObjectInDs: ldap_modify_s (2) failed: 0x%lx 0x%lx\n",
  3598. LdapStatus, NetStatus ));
  3599. goto Cleanup;
  3600. }
  3601. }
  3602. //
  3603. // Reset the password
  3604. //
  3605. NetStatus = NetpGetMachineAccountName( ComputerName, &SamAccountName );
  3606. if ( NetStatus != NO_ERROR ) {
  3607. goto Cleanup;
  3608. }
  3609. NetStatus = NetUserGetInfo( DC, SamAccountName, 1, ( PBYTE * )&CurrentUI1 );
  3610. if ( NetStatus == NERR_Success ) {
  3611. CurrentUI1->usri1_password = MachinePassword;
  3612. if ( !FLAG_ON( CurrentUI1->usri1_flags, UF_WORKSTATION_TRUST_ACCOUNT ) ) {
  3613. CurrentUI1->usri1_flags = UF_WORKSTATION_TRUST_ACCOUNT | UF_SCRIPT;
  3614. }
  3615. NetStatus = NetUserSetInfo( DC, SamAccountName, 1, ( PBYTE )CurrentUI1, NULL );
  3616. if ( NetStatus != NERR_Success ) {
  3617. NetpLog(( "NetpModifyComputerObjectInDs: NetUserSetInfo failed on '%ws' for '%ws': 0x%lx."
  3618. " Deleting the account.\n",
  3619. DC, SamAccountName, NetStatus ));
  3620. }
  3621. } else {
  3622. NetpLog(( "NetpModifyComputerObjectInDs: NetUserGetInfo failed on '%ws' for '%ws': 0x%lx."
  3623. " Deleting the account.\n",
  3624. DC, SamAccountName, NetStatus ));
  3625. }
  3626. //
  3627. // Delete the user if it fails
  3628. //
  3629. if ( NetStatus != NERR_Success ) {
  3630. LdapStatus = ldap_delete_s( Ldap, ComputerObjectDn );
  3631. if ( LdapStatus != LDAP_SUCCESS ) {
  3632. NetpLog(( "NetpModifyComputerObjectInDs: Failed to delete '%ws': 0x%lx 0x%lx\n",
  3633. ComputerObjectDn, LdapStatus, LdapMapErrorToWin32( LdapStatus ) ));
  3634. }
  3635. goto Cleanup;
  3636. }
  3637. } else if ( LdapStatus != LDAP_SUCCESS ) {
  3638. //
  3639. // Return the error code the user understands
  3640. //
  3641. if ( LdapStatus == LDAP_ALREADY_EXISTS ) {
  3642. NetStatus = NERR_UserExists;
  3643. } else {
  3644. NetStatus = LdapMapErrorToWin32( LdapStatus );
  3645. }
  3646. NetpLog(( "NetpModifyComputerObjectInDs: ldap_modify_s (1) failed: 0x%lx 0x%lx\n",
  3647. LdapStatus, NetStatus ));
  3648. goto Cleanup;
  3649. }
  3650. //
  3651. // Toggle the account type property. See comment in
  3652. // NetpSetMachineAccountPasswordAndTypeEx() for an
  3653. // explanation of why this is needed. (Search for USN).
  3654. //
  3655. Mods[0] = NULL;
  3656. for ( Index = 0; Index < NumberOfAttributes; Index++ ) {
  3657. if ( _wcsicmp( ModList[Index].mod_type, NETSETUPP_USERACCOUNTCONTROL ) == 0 ) {
  3658. Mods[0] = &ModList[Index];
  3659. //
  3660. // If this is a single valued attribute, we need to
  3661. // replace (not add) its value since it already exists in the DS
  3662. //
  3663. if ( (Attrib[Index].AttribFlags & NETSETUPP_MULTIVAL_ATTRIB) == 0 ) {
  3664. ModList[Index].mod_op = LDAP_MOD_REPLACE;
  3665. }
  3666. break;
  3667. }
  3668. }
  3669. Mods[1] = NULL;
  3670. if ( Mods[0] != NULL ) {
  3671. //
  3672. // Disable the account
  3673. //
  3674. *(Mods[0]->mod_values) = NETSETUPP_ACCNT_TYPE_DISABLED;
  3675. LdapStatus = ldap_modify_s( Ldap, ComputerObjectDn, Mods );
  3676. if ( LdapStatus != LDAP_SUCCESS ) {
  3677. NetStatus = LdapMapErrorToWin32( LdapStatus );
  3678. NetpLog(( "NetpModifyComputerObjectInDs: set UserAccountControl (1) on '%ws' failed: 0x%lx 0x%lx\n",
  3679. ComputerObjectDn, LdapStatus, NetStatus ));
  3680. goto Cleanup;
  3681. }
  3682. //
  3683. // Re-enable the account
  3684. //
  3685. *(Mods[0]->mod_values) = NETSETUPP_ACCNT_TYPE_ENABLED;
  3686. LdapStatus = ldap_modify_s( Ldap, ComputerObjectDn, Mods );
  3687. if ( LdapStatus != LDAP_SUCCESS ) {
  3688. NetStatus = LdapMapErrorToWin32( LdapStatus );
  3689. NetpLog(( "NetpModifyComputerObjectInDs: set UserAccountControl (2) on '%ws' failed: 0x%lx 0x%lx\n",
  3690. ComputerObjectDn, LdapStatus, NetStatus ));
  3691. goto Cleanup;
  3692. }
  3693. NetpLog(( "NetpModifyComputerObjectInDs: Toggled UserAccountControl successfully\n" ));
  3694. }
  3695. //
  3696. // If we've made up to this point, it's success!
  3697. //
  3698. NetStatus = NERR_Success;
  3699. //
  3700. // REVIEW: On error, consider using ldap_get_option to retrieve
  3701. // a human readable string describing the error that happend.
  3702. // Use LDAP_OPT_SERVER_ERROR as the option value. Return the
  3703. // string to the caller who may want to expose it to the user.
  3704. //
  3705. Cleanup:
  3706. if ( AttribTypesList != NULL ) {
  3707. NetApiBufferFree( AttribTypesList );
  3708. }
  3709. if ( ModList != NULL ) {
  3710. NetApiBufferFree( ModList );
  3711. }
  3712. if ( Mods != NULL ) {
  3713. NetApiBufferFree( Mods );
  3714. }
  3715. if ( Message != NULL ) {
  3716. ldap_msgfree( Message );
  3717. }
  3718. if ( SamAccountName != NULL ) {
  3719. NetApiBufferFree( SamAccountName );
  3720. }
  3721. if ( CurrentUI1 != NULL ) {
  3722. NetApiBufferFree( CurrentUI1 );
  3723. }
  3724. return NetStatus;
  3725. }
  3726. NET_API_STATUS
  3727. NET_API_FUNCTION
  3728. NetpCreateComputerObjectInDs(
  3729. IN PDOMAIN_CONTROLLER_INFO DcInfo,
  3730. IN LPWSTR Account OPTIONAL,
  3731. IN LPWSTR Password OPTIONAL,
  3732. IN LPWSTR ComputerName,
  3733. IN LPWSTR MachinePassword OPTIONAL,
  3734. IN LPWSTR DnsHostName OPTIONAL,
  3735. IN LPWSTR OU OPTIONAL
  3736. )
  3737. /*++
  3738. Routine Description:
  3739. Create a computer account in the specified OU.
  3740. Arguments:
  3741. DcInfo -- Domain controller on which to create the object
  3742. Account -- Account to use for the LDAP and DS binds.
  3743. If NULL, the default creds of the current user
  3744. context are used.
  3745. Password -- Password to used for the binds. Ignored if
  3746. Account is NULL.
  3747. ComputerName -- (Netbios) Name of the computer being joined
  3748. MachinePassword -- Password to set on the machine object
  3749. DnsHostName -- DNS host name of the computer being joined
  3750. OU -- OU under which to create the object.
  3751. The name must be a fully qualified name
  3752. e.g.: "ou=test,dc=ntdev,dc=microsoft,dc=com"
  3753. Returns:
  3754. NERR_Success -- Success
  3755. --*/
  3756. {
  3757. NET_API_STATUS NetStatus;
  3758. PLDAP Ldap = NULL;
  3759. PWSTR ComputerObjectDn = NULL;
  3760. PWSTR SamAccountName = NULL;
  3761. PWSTR DnsSpn = NULL;
  3762. PWSTR NetbiosSpn = NULL;
  3763. ULONG AttribCount;
  3764. PWSTR ClassValues[ 2 ];
  3765. PWSTR AccntNameValues[ 2 ];
  3766. PWSTR DnsHostNameValues[ 2 ];
  3767. PWSTR SpnValues[ 3 ];
  3768. PWSTR PasswordValues[ 2 ];
  3769. PWSTR AccntTypeValues[ 2 ];
  3770. NETSETUPP_MACH_ACC_ATTRIBUTE Attributes[NETSETUPP_COMP_OBJ_ATTR_COUNT];
  3771. USER_INFO_1 *CurrentUI1 = NULL;
  3772. //
  3773. // Validate parameters
  3774. //
  3775. if ( DcInfo == NULL ) {
  3776. NetpLog(( "NetpCreateComputerObjectInDs: No DcInfo passed\n" ));
  3777. NetStatus = ERROR_INVALID_PARAMETER;
  3778. goto Cleanup;
  3779. }
  3780. if ( ComputerName == NULL ) {
  3781. NetpLog(( "NetpCreateComputerObjectInDs: No ComputerName passed\n" ));
  3782. NetStatus = ERROR_INVALID_PARAMETER;
  3783. goto Cleanup;
  3784. }
  3785. //
  3786. // Verify that the DC runs DS
  3787. //
  3788. if ( (DcInfo->Flags & DS_DS_FLAG) == 0 ||
  3789. (DcInfo->Flags & DS_WRITABLE_FLAG) == 0 ) {
  3790. NetpLog(( "NetpCreateComputerObjectInDs: DC passed '%ws' doesn't have writable DS 0x%lx\n",
  3791. DcInfo->DomainControllerName,
  3792. DcInfo->Flags ));
  3793. NetStatus = ERROR_NOT_SUPPORTED;
  3794. goto Cleanup;
  3795. }
  3796. //
  3797. // First, try to bind to the server
  3798. //
  3799. NetStatus = NetpLdapBind( DcInfo->DomainControllerName, Account, Password, &Ldap );
  3800. if ( NetStatus != NO_ERROR ) {
  3801. NetpLog(( "NetpCreateComputerObjectInDs: NetpLdapBind failed: 0x%lx\n", NetStatus ));
  3802. goto Cleanup;
  3803. }
  3804. //
  3805. // Next get the computer object DN
  3806. //
  3807. NetStatus = NetpGetComputerObjectDn( DcInfo,
  3808. Account,
  3809. Password,
  3810. Ldap,
  3811. ComputerName,
  3812. OU,
  3813. &ComputerObjectDn );
  3814. if ( NetStatus != NO_ERROR ) {
  3815. NetpLog(( "NetpCreateComputerObjectInDs: NetpGetComputerObjectDn failed: 0x%lx\n", NetStatus ));
  3816. //
  3817. // Return meaningful error
  3818. //
  3819. if ( NetStatus == ERROR_FILE_EXISTS ) {
  3820. NetStatus = NERR_UserExists;
  3821. }
  3822. goto Cleanup;
  3823. }
  3824. //
  3825. // Get SAM account name
  3826. //
  3827. NetStatus = NetpGetMachineAccountName( ComputerName, &SamAccountName );
  3828. if ( NetStatus != NO_ERROR ) {
  3829. goto Cleanup;
  3830. }
  3831. //
  3832. // Build SPN values
  3833. //
  3834. if ( DnsHostName != NULL ) {
  3835. DnsSpn = LocalAlloc( 0, (wcslen(NETSETUPP_HOST_SPN_PREFIX) + wcslen(DnsHostName) + 1) * sizeof(WCHAR) );
  3836. if ( DnsSpn == NULL ) {
  3837. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  3838. goto Cleanup;
  3839. }
  3840. swprintf( DnsSpn, L"%ws%ws", NETSETUPP_HOST_SPN_PREFIX, DnsHostName );
  3841. NetbiosSpn = LocalAlloc( 0, (wcslen(NETSETUPP_HOST_SPN_PREFIX) + wcslen(ComputerName) + 1) * sizeof(WCHAR) );
  3842. if ( Netbios == NULL ) {
  3843. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  3844. goto Cleanup;
  3845. }
  3846. swprintf( NetbiosSpn, L"%ws%ws", NETSETUPP_HOST_SPN_PREFIX, ComputerName );
  3847. }
  3848. //
  3849. // Prepare the list of attributes that need to be set in the DS
  3850. //
  3851. // Always keep unicodePwd as the last entry because this order is
  3852. // assumed by the API called below.
  3853. //
  3854. AttribCount = 0;
  3855. Attributes[AttribCount].AttribType = NETSETUPP_OBJECTCLASS; //
  3856. Attributes[AttribCount].AttribFlags = NETSETUPP_MULTIVAL_ATTRIB; //
  3857. Attributes[AttribCount].AttribValues = ClassValues; // ObjectClass
  3858. ClassValues[ 0 ] = NETSETUPP_COMPUTER_OBJECT; //
  3859. ClassValues[ 1 ] = NULL; //
  3860. AttribCount ++;
  3861. Attributes[AttribCount].AttribType = NETSETUPP_SAMACCOUNTNAME; //
  3862. Attributes[AttribCount].AttribFlags = 0; //
  3863. Attributes[AttribCount].AttribValues = AccntNameValues; // SamAccountName
  3864. AccntNameValues[ 0 ] = SamAccountName; //
  3865. AccntNameValues[ 1 ] = NULL; //
  3866. AttribCount ++;
  3867. Attributes[AttribCount].AttribType = NETSETUPP_USERACCOUNTCONTROL; //
  3868. Attributes[AttribCount].AttribFlags = 0; //
  3869. Attributes[AttribCount].AttribValues = AccntTypeValues; // userAccountControl
  3870. AccntTypeValues[ 0 ] = NETSETUPP_ACCNT_TYPE_ENABLED; //
  3871. AccntTypeValues[ 1 ] = NULL; //
  3872. AttribCount ++;
  3873. if ( DnsHostName != NULL ) {
  3874. Attributes[AttribCount].AttribType = NETSETUPP_DNSHOSTNAME; //
  3875. Attributes[AttribCount].AttribFlags = 0; //
  3876. Attributes[AttribCount].AttribValues = DnsHostNameValues; // DnsHostName
  3877. DnsHostNameValues[ 0 ] = DnsHostName; //
  3878. DnsHostNameValues[ 1 ] = NULL; //
  3879. AttribCount ++;
  3880. Attributes[AttribCount].AttribType = NETSETUPP_SERVICEPRINCIPALNAME; //
  3881. Attributes[AttribCount].AttribFlags = NETSETUPP_MULTIVAL_ATTRIB; //
  3882. Attributes[AttribCount].AttribValues = SpnValues; // ServicePrincipalName
  3883. SpnValues[ 0 ] = DnsSpn; //
  3884. SpnValues[ 1 ] = NetbiosSpn; //
  3885. SpnValues[ 2 ] = NULL; //
  3886. AttribCount ++;
  3887. }
  3888. //
  3889. // The following attribute is the machine password. We avoid
  3890. // updating it though ldap because it is hard to ensure that
  3891. // the ldap session uses the 128-bit encryption required by
  3892. // SAM on the DC for password updates.
  3893. //
  3894. // To enforce the encryption, we would need to set an option
  3895. // LDAP_OPT_ENCRYPT via a ldap_set_option call following ldap_open
  3896. // before calling ldap_bind_s. However, there is no guarantee that
  3897. // the established connection will use 128 bit encryption; it may
  3898. // use 56 bit encryption if either side does not support strong
  3899. // encryption. We could, in principle, find out the resulting encryption
  3900. // strength using some QueryContextAttribute call, but it's just too much
  3901. // trouble. So, we will just create the account without the password and
  3902. // we will then update the password using good old Net/SAM API.
  3903. //
  3904. #if 0
  3905. Attributes[AttribCount].AttribType = NETSETUPP_UNICODEPWD; //
  3906. Attributes[AttribCount].AttribFlags = 0; //
  3907. Attributes[AttribCount].AttribValues = PasswordValues; // unicodePwd
  3908. PasswordValues[ 0 ] = MachinePassword; //
  3909. PasswordValues[ 1 ] = NULL; //
  3910. AttribCount ++;
  3911. #endif
  3912. //
  3913. // Modify the computer object given the list of attributes
  3914. //
  3915. NetStatus = NetpModifyComputerObjectInDs( DcInfo->DomainControllerName,
  3916. Ldap,
  3917. ComputerName,
  3918. ComputerObjectDn,
  3919. AttribCount,
  3920. Attributes );
  3921. if ( NetStatus != NO_ERROR ) {
  3922. NetpLog(( "NetpCreateComputerObjectInDs: NetpModifyComputerObjectInDs failed: 0x%lx\n", NetStatus ));
  3923. goto Cleanup;
  3924. }
  3925. //
  3926. // Now set the password using good old Net/SAM API.
  3927. // First get the current account info.
  3928. //
  3929. NetStatus = NetUserGetInfo( DcInfo->DomainControllerName,
  3930. SamAccountName,
  3931. 1,
  3932. ( PBYTE * )&CurrentUI1 );
  3933. //
  3934. // Update the password and reset it
  3935. //
  3936. if ( NetStatus == NO_ERROR ) {
  3937. CurrentUI1->usri1_password = MachinePassword;
  3938. if ( !FLAG_ON( CurrentUI1->usri1_flags, UF_WORKSTATION_TRUST_ACCOUNT ) ) {
  3939. CurrentUI1->usri1_flags = UF_WORKSTATION_TRUST_ACCOUNT | UF_SCRIPT;
  3940. }
  3941. NetStatus = NetUserSetInfo( DcInfo->DomainControllerName,
  3942. SamAccountName,
  3943. 1,
  3944. ( PBYTE )CurrentUI1,
  3945. NULL );
  3946. if ( NetStatus != NERR_Success ) {
  3947. NetpLog(( "NetpCreateComputerObjectInDs: NetUserSetInfo failed on '%ws' for '%ws': 0x%lx."
  3948. " Deleting the account.\n",
  3949. DcInfo->DomainControllerName,
  3950. SamAccountName,
  3951. NetStatus ));
  3952. }
  3953. } else {
  3954. NetpLog(( "NetpCreateComputerObjectInDs: NetUserGetInfo failed on '%ws' for '%ws': 0x%lx."
  3955. " Deleting the account.\n",
  3956. DcInfo->DomainControllerName,
  3957. SamAccountName,
  3958. NetStatus ));
  3959. }
  3960. //
  3961. // Delete the account if we couldn't set the password.
  3962. // Ignore the failure if we cannot delete the account for some reason.
  3963. //
  3964. if ( NetStatus != NO_ERROR ) {
  3965. ULONG LdapStatus;
  3966. LdapStatus = ldap_delete_s( Ldap, ComputerObjectDn );
  3967. if ( LdapStatus != LDAP_SUCCESS ) {
  3968. NetpLog(( "NetpCreateComputerObjectInDs: Failed to delete '%ws': 0x%lx 0x%lx\n",
  3969. ComputerObjectDn, LdapStatus, LdapMapErrorToWin32( LdapStatus ) ));
  3970. }
  3971. }
  3972. //
  3973. // Tell Netlogon that it should avoid setting
  3974. // DnsHostName and SPN until the reboot
  3975. //
  3976. if ( NetStatus == NO_ERROR && DnsHostName != NULL ) {
  3977. NetpAvoidNetlogonSpnSet( TRUE );
  3978. }
  3979. Cleanup:
  3980. if ( Ldap != NULL ) {
  3981. NetpLdapUnbind( Ldap );
  3982. }
  3983. if ( ComputerObjectDn != NULL ) {
  3984. NetApiBufferFree( ComputerObjectDn );
  3985. }
  3986. if ( SamAccountName != NULL ) {
  3987. NetApiBufferFree( SamAccountName );
  3988. }
  3989. if ( DnsSpn != NULL ) {
  3990. LocalFree( DnsSpn );
  3991. }
  3992. if ( NetbiosSpn != NULL ) {
  3993. LocalFree( NetbiosSpn );
  3994. }
  3995. if ( CurrentUI1 != NULL ) {
  3996. NetApiBufferFree( CurrentUI1 );
  3997. }
  3998. return NetStatus;
  3999. }
  4000. NET_API_STATUS
  4001. NET_API_FUNCTION
  4002. NetpSetDnsHostNameAndSpn(
  4003. IN PDOMAIN_CONTROLLER_INFO DcInfo,
  4004. IN LPWSTR Account,
  4005. IN LPWSTR Password,
  4006. IN LPWSTR ComputerName,
  4007. IN LPWSTR DnsHostName
  4008. )
  4009. /*++
  4010. Routine Description:
  4011. Set DnsHostName and HOST SPN (ServicePrincipalName) attributes on the
  4012. computer object in the DS.
  4013. Arguments:
  4014. DcInfo -- Domain controller on which to create the object
  4015. Account -- Account to use for the LDAP bind
  4016. Password -- Password to used for the bind
  4017. ComputerName -- Name of the computer being joined
  4018. DnsHostName -- DNS host name of the machine
  4019. Returns:
  4020. NERR_Success -- Success
  4021. --*/
  4022. {
  4023. NET_API_STATUS NetStatus;
  4024. HANDLE hToken = NULL;
  4025. PLDAP Ldap = NULL;
  4026. PWSTR ComputerObjectDn = NULL;
  4027. PWSTR DnsHostNameValues[ 2 ];
  4028. PWSTR SpnValues[ 3 ] = {NULL};
  4029. NETSETUPP_MACH_ACC_ATTRIBUTE Attributes[ 2 ];
  4030. //
  4031. // REVIEW: Kerberos has a bug such that if this server is joined remotely
  4032. // and the impersonated client connected to this server using NTLM (as is
  4033. // the case if this server is not a member of a domain before the join),
  4034. // explicit credentials supplied to ldap_bind or DsBindWithCredW will not
  4035. // work (AcquireCredentialsHandle call will fail). To get around this, we
  4036. // temporarily un-impersonates, bind to the DC, and then impersonate again
  4037. // at the end of this routine.
  4038. //
  4039. if ( OpenThreadToken( GetCurrentThread(),
  4040. TOKEN_IMPERSONATE,
  4041. TRUE,
  4042. &hToken ) ) {
  4043. if ( RevertToSelf() == 0 ) {
  4044. NetpLog(( "NetpSetDnsHostNameAndSpn: RevertToSelf failed: 0x%lx\n",
  4045. GetLastError() ));
  4046. }
  4047. } else {
  4048. NetpLog(( "NetpSetDnsHostNameAndSpn: OpenThreadToken failed: 0x%lx\n",
  4049. GetLastError() ));
  4050. }
  4051. //
  4052. // Bind to the DC
  4053. //
  4054. NetStatus = NetpLdapBind( DcInfo->DomainControllerName, Account, Password, &Ldap );
  4055. if ( NetStatus != NO_ERROR ) {
  4056. NetpLog(( "NetpSetDnsHostNameAndSpn: NetpLdapBind failed: 0x%lx\n", NetStatus ));
  4057. goto Cleanup;
  4058. }
  4059. //
  4060. // Next get the computer object DN
  4061. //
  4062. NetStatus = NetpGetComputerObjectDn( DcInfo,
  4063. Account,
  4064. Password,
  4065. Ldap,
  4066. ComputerName,
  4067. NULL, // Default computer container
  4068. &ComputerObjectDn );
  4069. if ( NetStatus != NO_ERROR ) {
  4070. NetpLog(( "NetpSetDnsHostNameAndSpn: NetpGetComputerObjectDn failed: 0x%lx\n", NetStatus ));
  4071. //
  4072. // Return meaningful error
  4073. //
  4074. if ( NetStatus == ERROR_FILE_EXISTS ) {
  4075. NetStatus = NERR_UserExists;
  4076. }
  4077. goto Cleanup;
  4078. }
  4079. //
  4080. // Build DnsHostName values
  4081. //
  4082. DnsHostNameValues[ 0 ] = DnsHostName;
  4083. DnsHostNameValues[ 1 ] = NULL;
  4084. //
  4085. // Build SPN values
  4086. //
  4087. SpnValues[0] = LocalAlloc( 0,
  4088. (wcslen(NETSETUPP_HOST_SPN_PREFIX) + wcslen(DnsHostName) + 1) * sizeof(WCHAR) );
  4089. if ( SpnValues[0] == NULL ) {
  4090. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  4091. goto Cleanup;
  4092. }
  4093. swprintf( SpnValues[0], L"%ws%ws", NETSETUPP_HOST_SPN_PREFIX, DnsHostName );
  4094. SpnValues[1] = LocalAlloc( 0,
  4095. (wcslen(NETSETUPP_HOST_SPN_PREFIX) + wcslen(ComputerName) + 1) * sizeof(WCHAR) );
  4096. if ( SpnValues[1] == NULL ) {
  4097. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  4098. goto Cleanup;
  4099. }
  4100. swprintf( SpnValues[1], L"%ws%ws", NETSETUPP_HOST_SPN_PREFIX, ComputerName );
  4101. SpnValues[2] = NULL;
  4102. //
  4103. // Prepare the list of attributes that need to be set in the DS
  4104. //
  4105. Attributes[0].AttribType = NETSETUPP_DNSHOSTNAME; //
  4106. Attributes[0].AttribFlags = 0; // DnsHostName
  4107. Attributes[0].AttribValues = DnsHostNameValues; //
  4108. Attributes[1].AttribType = NETSETUPP_SERVICEPRINCIPALNAME; //
  4109. Attributes[1].AttribFlags = NETSETUPP_MULTIVAL_ATTRIB; // ServicePrincipalName
  4110. Attributes[1].AttribValues = SpnValues; //
  4111. //
  4112. // Modify the computer object given the list of attributes
  4113. //
  4114. NetStatus = NetpModifyComputerObjectInDs( DcInfo->DomainControllerName,
  4115. Ldap,
  4116. ComputerName,
  4117. ComputerObjectDn,
  4118. 2,
  4119. Attributes );
  4120. //
  4121. // Tell Netlogon that it should avoid setting
  4122. // DnsHostName and SPN until the reboot
  4123. //
  4124. if ( NetStatus == NO_ERROR ) {
  4125. NetpAvoidNetlogonSpnSet( TRUE );
  4126. }
  4127. Cleanup:
  4128. if ( Ldap != NULL ) {
  4129. NetpLdapUnbind( Ldap );
  4130. }
  4131. //
  4132. // REVIEW: Revert the impersonation
  4133. //
  4134. if ( hToken != NULL ) {
  4135. if ( SetThreadToken( NULL, hToken ) == 0 ) {
  4136. NetpLog(( "NetpSetDnsHostNameAndSpn: SetThreadToken failed: 0x%lx\n",
  4137. GetLastError() ));
  4138. }
  4139. CloseHandle( hToken );
  4140. }
  4141. //
  4142. // Free locally allocated memory
  4143. //
  4144. if ( ComputerObjectDn != NULL ) {
  4145. NetApiBufferFree( ComputerObjectDn );
  4146. }
  4147. if ( SpnValues[0] != NULL ) {
  4148. LocalFree( SpnValues[0] );
  4149. }
  4150. if ( SpnValues[1] != NULL ) {
  4151. LocalFree( SpnValues[1] );
  4152. }
  4153. return NetStatus;
  4154. }
  4155. NET_API_STATUS
  4156. NET_API_FUNCTION
  4157. NetpCreateComputerObjectInOU(
  4158. IN LPWSTR DC,
  4159. IN LPWSTR OU,
  4160. IN LPWSTR ComputerName,
  4161. IN LPWSTR Account,
  4162. IN LPWSTR Password,
  4163. IN LPWSTR MachinePassword
  4164. )
  4165. /*++
  4166. Routine Description:
  4167. Create a computer account in the specified OU.
  4168. Arguments:
  4169. DC -- Domain controller on which to create the object
  4170. OU -- OU under which to create the object.
  4171. The name must be a fully qualified name
  4172. e.g.: "ou=test,dc=ntdev,dc=microsoft,dc=com"
  4173. ComputerName -- Name of the computer being joined
  4174. Account -- Account to use for the LDAP bind
  4175. Password -- Password to used for the bind
  4176. MachinePassword -- Password to set on the machine object
  4177. Returns:
  4178. NERR_Success -- Success
  4179. --*/
  4180. {
  4181. NET_API_STATUS NetStatus = NERR_Success;
  4182. ULONG LdapStatus;
  4183. PWSTR ObjectName = NULL, SamAccountName = NULL;
  4184. PLDAP Ldap = NULL;
  4185. PWSTR ClassValues[ 2 ];
  4186. PWSTR AccntNameValues[ 2 ];
  4187. PWSTR PasswordValues[ 2 ];
  4188. PWSTR AccntTypeValues[ 2 ];
  4189. PWSTR AttribTypes[NETSETUPP_COMP_OBJ_ATTR_COUNT];
  4190. ULONG AttribFlags[NETSETUPP_COMP_OBJ_ATTR_COUNT];
  4191. PWSTR *AttribValues[NETSETUPP_COMP_OBJ_ATTR_COUNT];
  4192. LDAPMod ModList[NETSETUPP_COMP_OBJ_ATTR_COUNT];
  4193. PLDAPMod Mods[NETSETUPP_COMP_OBJ_ATTR_COUNT + 1];
  4194. LDAPMessage *Message = NULL, *Entry;
  4195. USER_INFO_1 *CurrentUI1;
  4196. ULONG Index;
  4197. ULONG ModIndex = 0;
  4198. BOOL NewAccount = FALSE;
  4199. //
  4200. // First, try to bind to the server
  4201. //
  4202. NetStatus = NetpLdapBind( DC, Account, Password, &Ldap );
  4203. if ( NetStatus != NO_ERROR ) {
  4204. NetpLog(( "NetpCreateComputerObjectInOU: NetpLdapBind failed: 0x%lx\n", NetStatus ));
  4205. goto Cleanup;
  4206. }
  4207. //
  4208. // Next verify that the OU exists
  4209. //
  4210. LdapStatus = ldap_compare_s( Ldap,
  4211. OU,
  4212. NETSETUPP_OBJECTCLASS,
  4213. NETSETUPP_ORGANIZATIONALUNIT );
  4214. if ( LdapStatus == LDAP_COMPARE_FALSE ) {
  4215. NetStatus = ERROR_FILE_NOT_FOUND;
  4216. NetpLog(( "NetpCreateComputerObjectInOU: Specified path '%ws' is not an OU\n", OU ));
  4217. goto Cleanup;
  4218. } else if ( LdapStatus != LDAP_COMPARE_TRUE ) {
  4219. NetStatus = LdapMapErrorToWin32( LdapStatus );
  4220. NetpLog(( "NetpCreateComputerObjectInOU: ldap_compare_s failed: 0x%lx 0x%lx\n",
  4221. LdapStatus, NetStatus ));
  4222. goto Cleanup;
  4223. }
  4224. //
  4225. // Allocate the object name
  4226. //
  4227. NetStatus = NetApiBufferAllocate(
  4228. sizeof( NETSETUPP_OBJ_PREFIX ) + ( wcslen( OU ) + wcslen( ComputerName ) + 1 ) * sizeof( WCHAR ),
  4229. ( PVOID * ) &ObjectName );
  4230. if ( NetStatus != NO_ERROR ) {
  4231. goto Cleanup;
  4232. }
  4233. swprintf( ObjectName, L"%ws%ws,%ws", NETSETUPP_OBJ_PREFIX, ComputerName, OU );
  4234. NetStatus = NetpGetMachineAccountName(ComputerName, &SamAccountName);
  4235. if ( NetStatus != NO_ERROR ) {
  4236. goto Cleanup;
  4237. }
  4238. //
  4239. // Build attribute values. Always keep unicodePwd as the last entry
  4240. // because this order is assumed by the fail-over code below.
  4241. //
  4242. AttribTypes[ 0 ] = NETSETUPP_OBJECTCLASS; //
  4243. AttribFlags[ 0 ] = NETSETUPP_MULTIVAL_ATTRIB; //
  4244. AttribValues[ 0 ] = ClassValues; // ObjectClass
  4245. ClassValues[ 0 ] = NETSETUPP_COMPUTER_OBJECT; //
  4246. ClassValues[ 1 ] = NULL; //
  4247. AttribTypes[ 1 ] = NETSETUPP_SAMACCOUNTNAME; //
  4248. AttribFlags[ 1 ] = 0; //
  4249. AttribValues[ 1 ] = AccntNameValues; // SamAccountName
  4250. AccntNameValues[ 0 ] = SamAccountName; //
  4251. AccntNameValues[ 1 ] = NULL; //
  4252. AttribTypes[ 2 ] = NETSETUPP_USERACCOUNTCONTROL; //
  4253. AttribFlags[ 2 ] = 0; //
  4254. AttribValues[ 2 ] = AccntTypeValues; // userAccountControl
  4255. AccntTypeValues[ 0 ] = NETSETUPP_ACCNT_TYPE_ENABLED; //
  4256. AccntTypeValues[ 1 ] = NULL; //
  4257. AttribTypes[ 3 ] = NETSETUPP_UNICODEPWD; //
  4258. AttribFlags[ 3 ] = 0; //
  4259. AttribValues[ 3 ] = PasswordValues; // unicodePwd
  4260. PasswordValues[ 0 ] = MachinePassword; //
  4261. PasswordValues[ 1 ] = NULL; //
  4262. //
  4263. // Build modification list given the list of attributes
  4264. //
  4265. NetpLog(( "NetpCreateComputerObjectInOU: Initial attribute values:\n" ));
  4266. for ( Index = 0; Index < NETSETUPP_COMP_OBJ_ATTR_COUNT; Index++ ) {
  4267. ModList[Index].mod_op = LDAP_MOD_ADD; // Set to add. We may adjust this below.
  4268. ModList[Index].mod_type = AttribTypes[Index];
  4269. ModList[Index].mod_values = AttribValues[Index];
  4270. //
  4271. // Be verbose - output all values of each attribute
  4272. //
  4273. NetpLog(( "\t\t%ws =", AttribTypes[Index] ));
  4274. //
  4275. // Don't leak sensitive info!
  4276. //
  4277. if ( _wcsicmp( AttribTypes[Index], NETSETUPP_UNICODEPWD ) == 0 ) {
  4278. NetpLog(( " <SomePassword>" ));
  4279. } else {
  4280. ULONG ValIndex;
  4281. for ( ValIndex = 0; AttribValues[Index][ValIndex] != NULL; ValIndex++ ) {
  4282. NetpLog(( " %ws", AttribValues[Index][ValIndex] ));
  4283. }
  4284. }
  4285. NetpLog(( "\n" ));
  4286. }
  4287. //
  4288. // Now check which attribute values are already set in the DS
  4289. //
  4290. LdapStatus = ldap_search_s( Ldap,
  4291. ObjectName,
  4292. LDAP_SCOPE_BASE,
  4293. NULL,
  4294. AttribTypes,
  4295. 0,
  4296. &Message );
  4297. //
  4298. // If the computer object does not exist,
  4299. // we need to add all attributes
  4300. //
  4301. if ( LdapStatus == LDAP_NO_SUCH_OBJECT ) {
  4302. NetpLog(( "NetpCreateComputerObjectInOU: Computer Object does not exist in OU\n" ));
  4303. NewAccount = TRUE;
  4304. for ( ModIndex = 0; ModIndex < NETSETUPP_COMP_OBJ_ATTR_COUNT; ModIndex++ ) {
  4305. Mods[ModIndex] = &ModList[ModIndex];
  4306. }
  4307. //
  4308. // Terminate the modification list
  4309. //
  4310. Mods[ModIndex] = NULL;
  4311. //
  4312. // Otherwise see which attribute values need modification
  4313. //
  4314. } else if ( LdapStatus == LDAP_SUCCESS ) {
  4315. NetpLog(( "NetpCreateComputerObjectInOU: Computer Object already exists in OU:\n" ));
  4316. //
  4317. // Get the first entry (there should be only one)
  4318. //
  4319. Entry = ldap_first_entry( Ldap, Message );
  4320. //
  4321. // Loop through the attributes and weed out those values
  4322. // which are already set.
  4323. //
  4324. for ( Index = 0; Index < NETSETUPP_COMP_OBJ_ATTR_COUNT; Index++ ) {
  4325. PWSTR *AttribValueRet = NULL;
  4326. //
  4327. // Be verbose - output the values returned for each type
  4328. //
  4329. NetpLog(( "\t\t%ws =", AttribTypes[Index] ));
  4330. AttribValueRet = ldap_get_values( Ldap, Entry, AttribTypes[Index] );
  4331. if ( AttribValueRet != NULL ) {
  4332. //
  4333. // Don't leak sensitive info!
  4334. //
  4335. if ( _wcsicmp( AttribTypes[Index], NETSETUPP_UNICODEPWD ) == 0 ) {
  4336. NetpLog(( " <SomePassword>" ));
  4337. } else {
  4338. ULONG ValIndex;
  4339. for ( ValIndex = 0; AttribValueRet[ValIndex] != NULL; ValIndex++ ) {
  4340. NetpLog(( " %ws", AttribValueRet[ValIndex] ));
  4341. }
  4342. }
  4343. //
  4344. // Remove those values from the modification which are alredy set
  4345. //
  4346. NetpRemoveDuplicateStrings( AttribValueRet, AttribValues[Index] );
  4347. ldap_value_free( AttribValueRet );
  4348. //
  4349. // If this is a single valued attribute, we need to
  4350. // replace (not add) its value since it already exists in the DS
  4351. //
  4352. if ( (AttribFlags[Index] & NETSETUPP_MULTIVAL_ATTRIB) == 0 ) {
  4353. ModList[Index].mod_op = LDAP_MOD_REPLACE;
  4354. }
  4355. }
  4356. NetpLog(( "\n" ));
  4357. //
  4358. // If there are any attribute values which are
  4359. // not already set, add them to the modification.
  4360. //
  4361. if ( *AttribValues[Index] != NULL ) {
  4362. Mods[ModIndex] = &ModList[Index];
  4363. ModIndex ++;
  4364. }
  4365. }
  4366. //
  4367. // Terminate the modification list
  4368. //
  4369. Mods[ModIndex] = NULL;
  4370. //
  4371. // Otherwise, error out
  4372. //
  4373. } else {
  4374. NetStatus = LdapMapErrorToWin32( LdapStatus );
  4375. NetpLog(( "NetpCreateComputerObjectInOU: ldap_search_s failed: 0x%lx 0x%lx\n",
  4376. LdapStatus, NetStatus ));
  4377. goto Cleanup;
  4378. }
  4379. //
  4380. // If there are no modifications to do
  4381. // we are done.
  4382. //
  4383. if ( ModIndex == 0 ) {
  4384. NetpLog(( "NetpCreateComputerObjectInOU: There are _NO_ modifications to do\n" ));
  4385. NetStatus = NERR_Success;
  4386. goto Cleanup;
  4387. }
  4388. //
  4389. // Be verbose - output the attribute values to be set
  4390. //
  4391. NetpLog(( "NetpCreateComputerObjectInOU: Attribute values to set:\n" ));
  4392. for ( Index = 0; Mods[Index] != NULL; Index++ ) {
  4393. NetpLog(( "\t\t%ws =", (*(Mods[Index])).mod_type ));
  4394. //
  4395. // Don't leak sensitive info!
  4396. //
  4397. if ( _wcsicmp( (*(Mods[Index])).mod_type, NETSETUPP_UNICODEPWD ) == 0 ) {
  4398. NetpLog(( " <SomePassword>" ));
  4399. } else {
  4400. ULONG ValIndex;
  4401. for ( ValIndex = 0; ((*(Mods[Index])).mod_values)[ValIndex] != NULL; ValIndex++ ) {
  4402. NetpLog(( " %ws", ((*(Mods[Index])).mod_values)[ValIndex] ));
  4403. }
  4404. }
  4405. NetpLog(( "\n" ));
  4406. }
  4407. //
  4408. // Now, add the missing attributes
  4409. //
  4410. if ( NewAccount ) {
  4411. LdapStatus = ldap_add_s( Ldap, ObjectName, Mods );
  4412. } else {
  4413. LdapStatus = ldap_modify_s( Ldap, ObjectName, Mods );
  4414. }
  4415. //
  4416. // If we tried to create against a server that doesn't support 128 bit encryption,
  4417. // we get LDAP_UNWILLING_TO_PERFORM back from the server. So, we'll create the
  4418. // account again without the password, and then reset it using NetApi.
  4419. //
  4420. if ( LdapStatus == LDAP_UNWILLING_TO_PERFORM ) {
  4421. NetpLog(( "NetpCreateComputerObjectInOU: ldap_modify_s (1) returned"
  4422. " LDAP_UNWILLING_TO_PERFORM. Trying to recover.\n" ));
  4423. //
  4424. // If we didn't try to set a password, there is no legitimate reason
  4425. // for the add to fail.
  4426. //
  4427. if ( _wcsicmp( (*(Mods[ModIndex-1])).mod_type, NETSETUPP_UNICODEPWD ) != 0 ) {
  4428. NetpLog(( "NetpCreateComputerObjectInOU: ldap_modify_s retuned LDAP_UNWILLING_TO_PERFORM"
  4429. " but password was not being set\n" ));
  4430. NetStatus = LdapMapErrorToWin32( LdapStatus );
  4431. goto Cleanup;
  4432. }
  4433. //
  4434. // If there are other entries besides the password, retry to add them
  4435. //
  4436. if ( ModIndex > 1 ) {
  4437. Mods[ModIndex-1] = NULL;
  4438. if ( NewAccount ) {
  4439. LdapStatus = ldap_add_s( Ldap, ObjectName, Mods );
  4440. } else{
  4441. LdapStatus = ldap_modify_s( Ldap, ObjectName, Mods );
  4442. }
  4443. if ( LdapStatus != LDAP_SUCCESS ) {
  4444. NetStatus = LdapMapErrorToWin32( LdapStatus );
  4445. NetpLog(( "NetpCreateComputerObjectInOU: ldap_modify_s (2) failed: 0x%lx 0x%lx\n",
  4446. LdapStatus, NetStatus ));
  4447. goto Cleanup;
  4448. }
  4449. }
  4450. //
  4451. // Reset the password
  4452. //
  4453. NetStatus = NetUserGetInfo( DC, SamAccountName, 1, ( PBYTE * )&CurrentUI1 );
  4454. if ( NetStatus == NERR_Success ) {
  4455. CurrentUI1->usri1_password = MachinePassword;
  4456. if ( !FLAG_ON( CurrentUI1->usri1_flags, UF_WORKSTATION_TRUST_ACCOUNT ) ) {
  4457. CurrentUI1->usri1_flags = UF_WORKSTATION_TRUST_ACCOUNT | UF_SCRIPT;
  4458. }
  4459. NetStatus = NetUserSetInfo( DC, SamAccountName, 1, ( PBYTE )CurrentUI1, NULL );
  4460. if ( NetStatus != NERR_Success ) {
  4461. NetpLog(( "NetpCreateComputerObjectInOU: NetUserSetInfo failed on '%ws' for '%ws': 0x%lx."
  4462. " Deleting the account.\n",
  4463. DC, SamAccountName, NetStatus ));
  4464. }
  4465. NetApiBufferFree( CurrentUI1 );
  4466. } else {
  4467. NetpLog(( "NetpCreateComputerObjectInOU: NetUserGetInfo failed on '%ws' for '%ws': 0x%lx.",
  4468. " Deleting the account.\n",
  4469. DC, SamAccountName, NetStatus ));
  4470. }
  4471. //
  4472. // Delete the user if it fails
  4473. //
  4474. if ( NetStatus != NERR_Success ) {
  4475. LdapStatus = ldap_delete_s( Ldap, ObjectName );
  4476. if ( LdapStatus != LDAP_SUCCESS ) {
  4477. NetpLog(( "NetpCreateComputerObjectInOU: Failed to delete '%ws': 0x%lx 0x%lx\n",
  4478. ObjectName, LdapStatus, LdapMapErrorToWin32( LdapStatus ) ));
  4479. }
  4480. goto Cleanup;
  4481. }
  4482. } else if ( LdapStatus != LDAP_SUCCESS ) {
  4483. NetStatus = LdapMapErrorToWin32( LdapStatus );
  4484. NetpLog(( "NetpCreateComputerObjectInOU: ldap_modify_s (1) failed: 0x%lx 0x%lx\n",
  4485. LdapStatus, NetStatus ));
  4486. goto Cleanup;
  4487. }
  4488. //
  4489. // Toggle the account type property. See comment in
  4490. // NetpSetMachineAccountPasswordAndTypeEx() for an
  4491. // explanation of why this is needed. (Search for USN).
  4492. //
  4493. Mods[0] = NULL;
  4494. for ( Index = 0; Index < NETSETUPP_COMP_OBJ_ATTR_COUNT; Index++ ) {
  4495. if ( _wcsicmp( ModList[Index].mod_type, NETSETUPP_USERACCOUNTCONTROL ) == 0 ) {
  4496. Mods[0] = &ModList[Index];
  4497. break;
  4498. }
  4499. }
  4500. Mods[1] = NULL;
  4501. if ( Mods[0] != NULL ) {
  4502. //
  4503. // Disable the account
  4504. //
  4505. AccntTypeValues[0] = NETSETUPP_ACCNT_TYPE_DISABLED;
  4506. LdapStatus = ldap_modify_s( Ldap, ObjectName, Mods );
  4507. if ( LdapStatus != LDAP_SUCCESS ) {
  4508. NetStatus = LdapMapErrorToWin32( LdapStatus );
  4509. NetpLog(( "NetpCreateComputerObjectInOU: set UserAccountControl (1) on '%ws' failed: 0x%lx 0x%lx\n",
  4510. ObjectName, LdapStatus, NetStatus ));
  4511. goto Cleanup;
  4512. }
  4513. //
  4514. // Re-enable the account
  4515. //
  4516. AccntTypeValues[0] = NETSETUPP_ACCNT_TYPE_ENABLED;
  4517. LdapStatus = ldap_modify_s( Ldap, ObjectName, Mods );
  4518. if ( LdapStatus != LDAP_SUCCESS ) {
  4519. NetStatus = LdapMapErrorToWin32( LdapStatus );
  4520. NetpLog(( "NetpCreateComputerObjectInOU: set UserAccountControl (2) on '%ws' failed: 0x%lx 0x%lx\n",
  4521. ObjectName, LdapStatus, NetStatus ));
  4522. goto Cleanup;
  4523. }
  4524. } else {
  4525. NetpLog(( "NetpCreateComputerObjectInOU: UserAccountControl was not in the list\n" ));
  4526. }
  4527. //
  4528. // If we've made up to this point, it's success!
  4529. //
  4530. NetStatus = NERR_Success;
  4531. Cleanup:
  4532. if ( Ldap != NULL ) {
  4533. NetpLdapUnbind( Ldap );
  4534. }
  4535. if ( ObjectName != NULL ) {
  4536. NetApiBufferFree( ObjectName );
  4537. }
  4538. if ( SamAccountName != NULL ) {
  4539. NetApiBufferFree( SamAccountName );
  4540. }
  4541. if ( Message != NULL ) {
  4542. ldap_msgfree( Message );
  4543. }
  4544. return( NetStatus );
  4545. }
  4546. NET_API_STATUS
  4547. NET_API_FUNCTION
  4548. NetpDeleteComputerObjectInOU(
  4549. IN LPWSTR DC,
  4550. IN LPWSTR OU,
  4551. IN LPWSTR ComputerName,
  4552. IN LPWSTR Account,
  4553. IN LPWSTR Password
  4554. )
  4555. /*++
  4556. Routine Description:
  4557. This routine will actually create a computer account in the specified OU.
  4558. Arguments:
  4559. DC -- Domain controller on which to create the object
  4560. OU -- OU under which to create the object
  4561. ComputerName -- Name of the computer being joined
  4562. Account -- Account to use for the LDAP bind
  4563. Password -- Password to used for the bind
  4564. Returns:
  4565. NERR_Success -- Success
  4566. --*/
  4567. {
  4568. NET_API_STATUS NetStatus = NERR_Success;
  4569. PWSTR ObjectName = NULL, SamAccountName = NULL;
  4570. PLDAP Ldap = NULL;
  4571. ULONG Len;
  4572. Len = wcslen( ComputerName );
  4573. NetStatus = NetApiBufferAllocate( sizeof( NETSETUPP_OBJ_PREFIX ) + ( wcslen( OU ) + Len + 1 ) * sizeof( WCHAR ),
  4574. ( PVOID * ) &ObjectName );
  4575. if ( NetStatus == NERR_Success ) {
  4576. swprintf( ObjectName, L"%ws%ws,%ws", NETSETUPP_OBJ_PREFIX, ComputerName, OU );
  4577. NetStatus = NetApiBufferAllocate( ( Len + 2 ) * sizeof( WCHAR ),
  4578. ( PVOID * )&SamAccountName );
  4579. if ( NetStatus == NERR_Success ) {
  4580. swprintf( SamAccountName, L"%ws$", ComputerName );
  4581. }
  4582. }
  4583. if ( NetStatus == NERR_Success ) {
  4584. //
  4585. // Try and bind to the server
  4586. //
  4587. NetStatus = NetpLdapBind( DC,
  4588. Account,
  4589. Password,
  4590. &Ldap );
  4591. if ( NetStatus == NERR_Success ) {
  4592. //
  4593. // Now, do the delete..
  4594. //
  4595. NetStatus = LdapMapErrorToWin32( ldap_delete_s( Ldap, ObjectName ) );
  4596. NetpLdapUnbind( Ldap );
  4597. }
  4598. }
  4599. if ( NetStatus != NERR_Success ) {
  4600. NetpLog(( "NetpCreateComputerObjectInOU failed with %lu\n",
  4601. NetStatus ));
  4602. }
  4603. NetApiBufferFree( ObjectName );
  4604. NetApiBufferFree( SamAccountName );
  4605. if ( NetStatus != NERR_Success ) {
  4606. NetpLog(( "NetpDeleteComputerObjectInOU failed with %lu\n",
  4607. NetStatus ));
  4608. }
  4609. return( NetStatus );
  4610. }
  4611. #if defined(REMOTE_BOOT)
  4612. NET_API_STATUS
  4613. NetpGetRemoteBootMachinePassword(
  4614. OUT LPWSTR Password
  4615. )
  4616. /*++
  4617. Routine Description:
  4618. Determine if this is a remote boot client, and if so return
  4619. the machine account password.
  4620. This information is obtained via an IOCTL to the redirector.
  4621. Arguments:
  4622. Password - returns the password. Should be at least PWLEN WCHARs long.
  4623. Return Value:
  4624. NERR_Success if the password is found.
  4625. An error if this is not a remote boot machine.
  4626. --*/
  4627. {
  4628. NET_API_STATUS NetStatus;
  4629. NTSTATUS Status;
  4630. UNICODE_STRING DeviceName;
  4631. IO_STATUS_BLOCK IoStatusBlock;
  4632. OBJECT_ATTRIBUTES ObjectAttributes;
  4633. HANDLE RedirHandle = NULL;
  4634. UCHAR PacketBuffer[sizeof(ULONG)+64];
  4635. PLMMR_RB_CHECK_FOR_NEW_PASSWORD RequestPacket = (PLMMR_RB_CHECK_FOR_NEW_PASSWORD)PacketBuffer;
  4636. //
  4637. // Open the redirector device.
  4638. //
  4639. RtlInitUnicodeString(&DeviceName, DD_NFS_DEVICE_NAME_U);
  4640. InitializeObjectAttributes(
  4641. &ObjectAttributes,
  4642. &DeviceName,
  4643. OBJ_CASE_INSENSITIVE,
  4644. NULL,
  4645. NULL
  4646. );
  4647. Status = NtOpenFile(
  4648. &RedirHandle,
  4649. SYNCHRONIZE,
  4650. &ObjectAttributes,
  4651. &IoStatusBlock,
  4652. 0,
  4653. 0
  4654. );
  4655. if (NT_SUCCESS(Status)) {
  4656. Status = IoStatusBlock.Status;
  4657. }
  4658. if (!NT_SUCCESS(Status)) {
  4659. NetpLog(( "Could not open redirector device %lx\n",
  4660. Status ));
  4661. NetStatus = NetpNtStatusToApiStatus( Status );
  4662. goto Cleanup;
  4663. }
  4664. //
  4665. // Send the request to the redir.
  4666. //
  4667. Status = NtFsControlFile(
  4668. RedirHandle,
  4669. NULL,
  4670. NULL,
  4671. NULL,
  4672. &IoStatusBlock,
  4673. FSCTL_LMMR_RB_CHECK_FOR_NEW_PASSWORD,
  4674. NULL, // no input buffer
  4675. 0,
  4676. PacketBuffer,
  4677. sizeof(PacketBuffer));
  4678. if (NT_SUCCESS(Status)) {
  4679. Status = IoStatusBlock.Status;
  4680. }
  4681. //
  4682. // We expect this to work on a disked machine, since we need the password
  4683. // to join.
  4684. //
  4685. if ( !NT_SUCCESS( Status ) )
  4686. {
  4687. NetpLog(( "Could not open FSCTL_LMMR_RB_CHECK_FOR_NEW_PASSWORD %lx\n",
  4688. Status ));
  4689. NetStatus = NetpNtStatusToApiStatus( Status );
  4690. goto Cleanup;
  4691. }
  4692. //
  4693. // Copy the result back to the caller's buffer.
  4694. //
  4695. RtlCopyMemory(Password, RequestPacket->Data, RequestPacket->Length);
  4696. Password[RequestPacket->Length / 2] = L'\0';
  4697. NetStatus = NO_ERROR;
  4698. Cleanup:
  4699. if ( RedirHandle != NULL ) {
  4700. NtClose( RedirHandle );
  4701. }
  4702. return NetStatus;
  4703. }
  4704. #endif // REMOTE_BOOT
  4705. NET_API_STATUS
  4706. NET_API_FUNCTION
  4707. NetpSetMachineAccountPasswordAndType(
  4708. IN LPWSTR lpDcName,
  4709. IN PSID DomainSid,
  4710. IN LPWSTR lpAccountName,
  4711. IN LPWSTR lpPassword
  4712. )
  4713. {
  4714. return( NetpSetMachineAccountPasswordAndTypeEx(
  4715. lpDcName,
  4716. DomainSid,
  4717. lpAccountName,
  4718. lpPassword,
  4719. 0,
  4720. TRUE
  4721. ) );
  4722. }
  4723. NET_API_STATUS
  4724. NET_API_FUNCTION
  4725. NetpSetMachineAccountPasswordAndTypeEx(
  4726. IN LPWSTR lpDcName,
  4727. IN PSID DomainSid,
  4728. IN LPWSTR lpAccountName,
  4729. IN OUT OPTIONAL LPWSTR lpPassword,
  4730. IN OPTIONAL UCHAR AccountState,
  4731. IN BOOL fIsNt4Dc
  4732. )
  4733. /*++
  4734. Routine Description:
  4735. Due to a few strange reasons, we cannot use the supported, documented Net apis for
  4736. managing the machine account, so we have to use the undocumented Sam apis. This routine
  4737. will set the password and account type on an account that alread exists.
  4738. Arguments:
  4739. lpDcName - Name of the DC on which the account lives
  4740. DomainSid - Sid of the domain on which the account lives
  4741. lpAccountName - Name of the account
  4742. lpPassword - Password to be set on the account.
  4743. This function gets a strong password to begin with.
  4744. If the dc refuses to accept this password, this fn
  4745. can weaken the password by making it shorter.
  4746. The caller of this function should check if the length
  4747. of the supplied password was changed.
  4748. This function should preferably return a BOOL to
  4749. indicate this.
  4750. AccountState - if specified, the account will be set to this state.
  4751. possible values:
  4752. ACCOUNT_STATE_ENABLED, ACCOUNT_STATE_DISABLED
  4753. fIsNt4Dc - TRUE if the DC is NT4 or earlier.
  4754. Return Value:
  4755. NERR_Success -- Success
  4756. --*/
  4757. {
  4758. NET_API_STATUS NetStatus=NERR_Success;
  4759. NTSTATUS Status = STATUS_SUCCESS;
  4760. UNICODE_STRING DcName, AccountName;
  4761. OBJECT_ATTRIBUTES ObjectAttributes;
  4762. SAM_HANDLE SamHandle = NULL, DomainHandle = NULL, AccountHandle = NULL;
  4763. ULONG UserRid;
  4764. PULONG RidList = NULL;
  4765. PSID_NAME_USE NameUseList = NULL;
  4766. PUSER_CONTROL_INFORMATION UserAccountControl = NULL;
  4767. USER_SET_PASSWORD_INFORMATION PasswordInfo;
  4768. ULONG OldUserInfo;
  4769. BOOL fAccountControlModified = FALSE;
  4770. LPWSTR lpSamAccountName=lpAccountName;
  4771. ULONG AccountNameLen=0;
  4772. AccountNameLen = wcslen( lpAccountName );
  4773. //
  4774. // if caller has not passed in sam-account name,
  4775. // generate it from machine name ==> append $ at the end
  4776. //
  4777. if (lpAccountName[AccountNameLen-1] != L'$')
  4778. {
  4779. NetStatus = NetpGetMachineAccountName(lpAccountName,
  4780. &lpSamAccountName);
  4781. if (NetStatus != NERR_Success)
  4782. {
  4783. Status = STATUS_INSUFFICIENT_RESOURCES;
  4784. goto SetPasswordError;
  4785. }
  4786. }
  4787. RtlInitUnicodeString( &DcName, lpDcName );
  4788. RtlZeroMemory( &ObjectAttributes, sizeof( OBJECT_ATTRIBUTES ) );
  4789. Status = SamConnect( &DcName,
  4790. &SamHandle,
  4791. SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN,
  4792. &ObjectAttributes );
  4793. if ( !NT_SUCCESS( Status ) ) {
  4794. NetpLog(( "SamConnect to %wZ failed with 0x%lx\n", &DcName, Status ));
  4795. goto SetPasswordError;
  4796. }
  4797. //
  4798. // Open the domain
  4799. //
  4800. Status = SamOpenDomain( SamHandle,
  4801. DOMAIN_LOOKUP,
  4802. DomainSid,
  4803. &DomainHandle );
  4804. if ( !NT_SUCCESS( Status ) ) {
  4805. #ifdef NETSETUP_VERBOSE_LOGGING
  4806. UNICODE_STRING DisplaySid;
  4807. NTSTATUS Status2;
  4808. RtlZeroMemory( &DisplaySid, sizeof( UNICODE_STRING ) );
  4809. Status2 = RtlConvertSidToUnicodeString( &DisplaySid, DomainSid, TRUE );
  4810. if ( NT_SUCCESS( Status2 ) ) {
  4811. NetpLog(( "SamOpenDomain on %wZ failed with 0x%lx\n",
  4812. &DisplaySid, Status ));
  4813. RtlFreeUnicodeString(&DisplaySid);
  4814. } else {
  4815. NetpLog(( "SamOpenDomain on <undisplayable sid> failed with 0x%lx\n",
  4816. Status ));
  4817. }
  4818. #endif
  4819. goto SetPasswordError;
  4820. }
  4821. //
  4822. // Get the RID of the user account
  4823. //
  4824. RtlInitUnicodeString( &AccountName, lpSamAccountName );
  4825. Status = SamLookupNamesInDomain( DomainHandle,
  4826. 1,
  4827. &AccountName,
  4828. &RidList,
  4829. &NameUseList );
  4830. if ( !NT_SUCCESS( Status ) ) {
  4831. NetpLog(( "SamLookupNamesInDomain on %wZ failed with 0x%lx\n",
  4832. &AccountName, Status ));
  4833. goto SetPasswordError;
  4834. }
  4835. UserRid = RidList[ 0 ];
  4836. SamFreeMemory( RidList );
  4837. SamFreeMemory( NameUseList );
  4838. //
  4839. // Finally, open the user account
  4840. //
  4841. Status = SamOpenUser( DomainHandle,
  4842. USER_FORCE_PASSWORD_CHANGE | USER_READ_ACCOUNT | USER_WRITE_ACCOUNT,
  4843. UserRid,
  4844. &AccountHandle );
  4845. if ( !NT_SUCCESS( Status ) ) {
  4846. Status = SamOpenUser( DomainHandle,
  4847. USER_FORCE_PASSWORD_CHANGE | USER_READ_ACCOUNT,
  4848. UserRid,
  4849. &AccountHandle );
  4850. if ( !NT_SUCCESS( Status ) ) {
  4851. NetpLog(( "SamOpenUser on %lu failed with 0x%lx\n",
  4852. UserRid,
  4853. Status ));
  4854. goto SetPasswordError;
  4855. }
  4856. }
  4857. //
  4858. // Now, read the current user account type and see if it needs to be modified
  4859. //
  4860. Status = SamQueryInformationUser( AccountHandle,
  4861. UserControlInformation,
  4862. ( PVOID * )&UserAccountControl );
  4863. if ( !NT_SUCCESS( Status ) ) {
  4864. NetpLog(( "SamQueryInformationUser for UserControlInformation "
  4865. "failed with 0x%lx\n", Status ));
  4866. goto SetPasswordError;
  4867. }
  4868. OldUserInfo = UserAccountControl->UserAccountControl;
  4869. if ( !FLAG_ON( UserAccountControl->UserAccountControl, USER_WORKSTATION_TRUST_ACCOUNT ) ) {
  4870. fAccountControlModified = TRUE;
  4871. UserAccountControl->UserAccountControl |= USER_WORKSTATION_TRUST_ACCOUNT;
  4872. UserAccountControl->UserAccountControl &= ~( USER_INTERDOMAIN_TRUST_ACCOUNT |
  4873. USER_SERVER_TRUST_ACCOUNT );
  4874. }
  4875. //
  4876. // Determine if the account control changes. If the account is being enabled,
  4877. // we want to perform the following sequence of operations for NT5: enable, disable,
  4878. // and enable again. This is needed to increase the USN (Universal Sequence
  4879. // Number) of this attribute so that the enabled value will win if the DS
  4880. // replication resolves colliding changes, as the following example shows.
  4881. // Suppose we have two DCs in the domain we join, A abd B. Suppose the account
  4882. // is currently disabled on A (because the user unjoined using that DC),
  4883. // but it is still enabled on B (because the replication hasn't happened yet).
  4884. // Suppose the user performs now joining to the domain. Then we have discovered
  4885. // B and so we proceed with setting up the changes to the existing account. If
  4886. // we don't toggle the account control attribute, then the USN of this attribute
  4887. // will not change on B (since attribute's value doesn't change) while it was
  4888. // incremented on A as the result of unjoin. At the replication time the data
  4889. // from A will rule and the account will be incorrectly marked as diabled.
  4890. //
  4891. // NOTE: This design may fail for the case of unjoining a domain that has
  4892. // three (or more) DCs, A, B, and C if the following sequence of operations
  4893. // happens. Suppose that the account is originally enabled on all DCs (state [1]
  4894. // in the bellow diagram). Then the user unjoins using DC A (state [2]). Then the
  4895. // user joins using B where the account is still enabled (state [3]). Then the user
  4896. // unjoins using C where the account is still enabled (state [4]). The final
  4897. // operation is unjoin, so the user expects that his account is disabled. We've
  4898. // assumed here that for some reason no replication was happening when these
  4899. // operations were performed. Then at the replication time the value from B will
  4900. // win (because of the additional toggling performed at the join time). But the
  4901. // account state on B is Enabled, so the final result will be that the account is
  4902. // enabled on all DCs which is not what the user expects.
  4903. //
  4904. // A B C
  4905. // Enabled [1] Enabled [1] Enabled [1]
  4906. // Disabled [2] Enabled (no-op)+Disabled (1 op) Disabled [4]
  4907. // Enabled [3]
  4908. //
  4909. if ( AccountState != ACCOUNT_STATE_IGNORE ) {
  4910. if ( ( AccountState == ACCOUNT_STATE_ENABLED ) &&
  4911. ( (OldUserInfo & USER_ACCOUNT_DISABLED) || !fIsNt4Dc ) ) {
  4912. fAccountControlModified = TRUE;
  4913. UserAccountControl->UserAccountControl &= ~USER_ACCOUNT_DISABLED;
  4914. }
  4915. if ( ( AccountState == ACCOUNT_STATE_DISABLED ) &&
  4916. !( OldUserInfo & USER_ACCOUNT_DISABLED ) ) {
  4917. fAccountControlModified = TRUE;
  4918. UserAccountControl->UserAccountControl |= USER_ACCOUNT_DISABLED;
  4919. }
  4920. }
  4921. if ( fAccountControlModified == FALSE ) {
  4922. SamFreeMemory( UserAccountControl );
  4923. UserAccountControl = NULL;
  4924. }
  4925. //
  4926. // First, set the account type if required
  4927. //
  4928. if ( UserAccountControl ) {
  4929. Status = SamSetInformationUser( AccountHandle,
  4930. UserControlInformation,
  4931. ( PVOID )UserAccountControl );
  4932. if ( !NT_SUCCESS( Status ) ) {
  4933. NetpLog(( "SamSetInformationUser for UserControlInformation "
  4934. "failed with 0x%lx\n", Status ));
  4935. goto SetPasswordError;
  4936. //
  4937. // If we are enabling the account, disable and re-enable it to
  4938. // make the two additional account state toggles.
  4939. //
  4940. } else if ( AccountState == ACCOUNT_STATE_ENABLED ) {
  4941. UserAccountControl->UserAccountControl |= USER_ACCOUNT_DISABLED;
  4942. Status = SamSetInformationUser( AccountHandle,
  4943. UserControlInformation,
  4944. ( PVOID )UserAccountControl );
  4945. if ( !NT_SUCCESS(Status) ) {
  4946. NetpLog(( "SamSetInformationUser (second) for UserControlInformation "
  4947. "failed with 0x%lx\n", Status ));
  4948. goto SetPasswordError;
  4949. }
  4950. UserAccountControl->UserAccountControl &= ~USER_ACCOUNT_DISABLED;
  4951. Status = SamSetInformationUser( AccountHandle,
  4952. UserControlInformation,
  4953. ( PVOID )UserAccountControl );
  4954. if ( !NT_SUCCESS(Status) ) {
  4955. NetpLog(( "SamSetInformationUser (third) for UserControlInformation "
  4956. "failed with 0x%lx\n", Status ));
  4957. goto SetPasswordError;
  4958. }
  4959. }
  4960. }
  4961. //
  4962. // If requested, set the password on the account
  4963. //
  4964. if ( lpPassword != NULL )
  4965. {
  4966. RtlInitUnicodeString( &PasswordInfo.Password, lpPassword );
  4967. PasswordInfo.PasswordExpired = FALSE;
  4968. //
  4969. // Ok, then, set the password on the account
  4970. //
  4971. // The caller has passed in a strong password, try that first
  4972. // NT5 dcs will always accept a strong password.
  4973. //
  4974. Status = SamSetInformationUser( AccountHandle,
  4975. UserSetPasswordInformation,
  4976. ( PVOID )&PasswordInfo );
  4977. if ( !NT_SUCCESS( Status ) )
  4978. {
  4979. if ( (Status == STATUS_PASSWORD_RESTRICTION) &&
  4980. !NetpIsDefaultPassword( lpAccountName, lpPassword ))
  4981. {
  4982. NetpLog(( "NetpSetMachineAccountPasswordAndTypeEx: STATUS_PASSWORD_RESTRICTION error setting password. retrying...\n" ));
  4983. //
  4984. // SAM did not accpet a long password, try LM20_PWLEN
  4985. //
  4986. // This is probably because the dc is NT4 dc.
  4987. // NT4 dcs will not accept a password longer than LM20_PWLEN
  4988. //
  4989. lpPassword[LM20_PWLEN] = UNICODE_NULL;
  4990. RtlInitUnicodeString( &PasswordInfo.Password, lpPassword );
  4991. Status = SamSetInformationUser( AccountHandle,
  4992. UserSetPasswordInformation,
  4993. ( PVOID )&PasswordInfo );
  4994. if ( Status == STATUS_PASSWORD_RESTRICTION )
  4995. {
  4996. NetpLog(( "NetpSetMachineAccountPasswordAndTypeEx: STATUS_PASSWORD_RESTRICTION error setting password. retrying...\n" ));
  4997. //
  4998. // SAM did not accpet a LM20_PWLEN password, try shorter one
  4999. //
  5000. // SAM uses RtlUpcaseUnicodeStringToOemString internally.
  5001. // In this process it is possible that in the worst case,
  5002. // n unicode char password will get mapped to 2*n dbcs
  5003. // char password. This will make it exceed LM20_PWLEN.
  5004. // To guard against this worst case, try a password
  5005. // with LM20_PWLEN/2 length
  5006. //
  5007. // One might say that LM20_PWLEN/2 length password
  5008. // is not really secure. I agree, but it is definitely
  5009. // better than the default password which we will have
  5010. // to fall back to otherwise.
  5011. //
  5012. lpPassword[LM20_PWLEN/2] = UNICODE_NULL;
  5013. RtlInitUnicodeString( &PasswordInfo.Password, lpPassword );
  5014. Status = SamSetInformationUser( AccountHandle,
  5015. UserSetPasswordInformation,
  5016. ( PVOID )&PasswordInfo );
  5017. if ( Status == STATUS_PASSWORD_RESTRICTION )
  5018. {
  5019. //
  5020. // SAM did not accpet a short pwd, try default pwd
  5021. //
  5022. NetpLog(( "NetpSetMachineAccountPasswordAndTypeEx: STATUS_PASSWORD_RESTRICTION error setting password. retrying...\n" ));
  5023. NetpGenerateDefaultPassword(lpAccountName, lpPassword);
  5024. RtlInitUnicodeString( &PasswordInfo.Password, lpPassword );
  5025. Status = SamSetInformationUser( AccountHandle,
  5026. UserSetPasswordInformation,
  5027. ( PVOID )&PasswordInfo );
  5028. }
  5029. }
  5030. }
  5031. if ( NT_SUCCESS( Status ) )
  5032. {
  5033. NetpLog(( "NetpGenerateDefaultPassword: successfully set password\n" ));
  5034. }
  5035. else
  5036. {
  5037. NetpLog(( "NetpSetMachineAccountPasswordAndTypeEx: SamSetInformationUser for UserSetPasswordInformation failed: 0x%lx\n", Status ));
  5038. //
  5039. // Make sure we try to restore the account control
  5040. //
  5041. if ( UserAccountControl )
  5042. {
  5043. NTSTATUS Status2;
  5044. UserAccountControl->UserAccountControl = OldUserInfo;
  5045. Status2 = SamSetInformationUser( AccountHandle,
  5046. UserControlInformation,
  5047. ( PVOID )UserAccountControl );
  5048. if ( !NT_SUCCESS( Status2 ) )
  5049. {
  5050. NetpLog(( "SamSetInformationUser for UserControlInformation (RESTORE) failed with 0x%lx\n", Status2 ));
  5051. }
  5052. }
  5053. goto SetPasswordError;
  5054. }
  5055. }
  5056. }
  5057. SetPasswordError:
  5058. if ( lpSamAccountName != lpAccountName )
  5059. {
  5060. NetApiBufferFree( lpSamAccountName );
  5061. }
  5062. if ( AccountHandle ) {
  5063. SamCloseHandle( AccountHandle );
  5064. }
  5065. if ( DomainHandle ) {
  5066. SamCloseHandle( DomainHandle );
  5067. }
  5068. if ( SamHandle ) {
  5069. SamCloseHandle( SamHandle );
  5070. }
  5071. NetStatus = RtlNtStatusToDosError( Status );
  5072. SamFreeMemory( UserAccountControl );
  5073. return( NetStatus );
  5074. }
  5075. NET_API_STATUS
  5076. NET_API_FUNCTION
  5077. NetpRemoveDnsRegistrations (
  5078. VOID
  5079. )
  5080. /*++
  5081. Routine Description:
  5082. This function removes DNS registration entries via an entrypoint defined
  5083. in DHCPCSVC.DLL called DhcpRemoveDNSRegistrations
  5084. Return Value:
  5085. NERR_Success -- Success
  5086. --*/
  5087. {
  5088. HMODULE hModule = NULL;
  5089. DNS_REGISTRATION_REMOVAL_FN pfn = NULL;
  5090. hModule = LoadLibraryW( L"dhcpcsvc.dll" );
  5091. if ( hModule != NULL )
  5092. {
  5093. pfn = (DNS_REGISTRATION_REMOVAL_FN)GetProcAddress(
  5094. hModule,
  5095. "DhcpRemoveDNSRegistrations"
  5096. );
  5097. if ( pfn != NULL )
  5098. {
  5099. ( *pfn )();
  5100. }
  5101. FreeLibrary( hModule );
  5102. }
  5103. return( NERR_Success );
  5104. }
  5105. //
  5106. // Helper functions
  5107. //
  5108. LPWSTR
  5109. GetStrPtr(IN LPWSTR szString OPTIONAL)
  5110. {
  5111. return szString ? szString : L"(NULL)";
  5112. }
  5113. NET_API_STATUS
  5114. NET_API_FUNCTION
  5115. NetpDuplicateString(IN LPCWSTR szSrc,
  5116. IN LONG cchSrc,
  5117. OUT LPWSTR* pszDst)
  5118. {
  5119. NET_API_STATUS NetStatus;
  5120. if (cchSrc < 0)
  5121. {
  5122. cchSrc = wcslen(szSrc);
  5123. }
  5124. ++cchSrc;
  5125. NetStatus = NetApiBufferAllocate(cchSrc * sizeof( WCHAR ),
  5126. pszDst);
  5127. if ( NetStatus == NERR_Success )
  5128. {
  5129. wcsncpy(*pszDst, szSrc, cchSrc);
  5130. }
  5131. return NetStatus;
  5132. }
  5133. NET_API_STATUS
  5134. NET_API_FUNCTION
  5135. NetpConcatStrings(IN LPCWSTR szSrc1,
  5136. IN LONG cchSrc1,
  5137. IN LPCWSTR szSrc2,
  5138. IN LONG cchSrc2,
  5139. OUT LPWSTR* pszDst)
  5140. {
  5141. NET_API_STATUS NetStatus;
  5142. if (cchSrc1 < 0)
  5143. {
  5144. cchSrc1 = wcslen(szSrc1);
  5145. }
  5146. if (cchSrc2 < 0)
  5147. {
  5148. cchSrc2 = wcslen(szSrc2);
  5149. }
  5150. NetStatus = NetApiBufferAllocate((cchSrc1 + cchSrc2 + 1) * sizeof( WCHAR ),
  5151. pszDst);
  5152. if ( NetStatus == NERR_Success )
  5153. {
  5154. wcsncpy(*pszDst, szSrc1, cchSrc1);
  5155. wcsncpy(*pszDst + cchSrc1, szSrc2, cchSrc2+1);
  5156. }
  5157. return NetStatus;
  5158. }
  5159. NET_API_STATUS
  5160. NET_API_FUNCTION
  5161. NetpConcatStrings3(IN LPCWSTR szSrc1,
  5162. IN LONG cchSrc1,
  5163. IN LPCWSTR szSrc2,
  5164. IN LONG cchSrc2,
  5165. IN LPCWSTR szSrc3,
  5166. IN LONG cchSrc3,
  5167. OUT LPWSTR* pszDst)
  5168. {
  5169. NET_API_STATUS NetStatus;
  5170. if (cchSrc1 < 0)
  5171. {
  5172. cchSrc1 = wcslen(szSrc1);
  5173. }
  5174. if (cchSrc2 < 0)
  5175. {
  5176. cchSrc2 = wcslen(szSrc2);
  5177. }
  5178. if (cchSrc3 < 0)
  5179. {
  5180. cchSrc3 = wcslen(szSrc3);
  5181. }
  5182. NetStatus = NetApiBufferAllocate((cchSrc1 + cchSrc2 + cchSrc3 + 1) *
  5183. sizeof( WCHAR ), pszDst);
  5184. if ( NetStatus == NERR_Success )
  5185. {
  5186. wcsncpy(*pszDst, szSrc1, cchSrc1);
  5187. wcsncpy(*pszDst + cchSrc1, szSrc2, cchSrc2);
  5188. wcsncpy(*pszDst + cchSrc1 + cchSrc2, szSrc3, cchSrc3+1);
  5189. }
  5190. return NetStatus;
  5191. }
  5192. NET_API_STATUS
  5193. NET_API_FUNCTION
  5194. NetpGetMachineAccountName(
  5195. IN LPCWSTR szMachineName,
  5196. OUT LPWSTR* pszMachineAccountName
  5197. )
  5198. /*++
  5199. Routine Description:
  5200. Get machine account name from machine name.
  5201. Arguments:
  5202. szMachineName -- name of a computer
  5203. pszMachineAccountName -- receives the name of computer account
  5204. Returns:
  5205. NERR_Success -- Success
  5206. Notes:
  5207. Caller must free the allocated memory using NetApiBufferFree.
  5208. --*/
  5209. {
  5210. NET_API_STATUS NetStatus;
  5211. ULONG ulLen;
  5212. LPWSTR szMachineAccountName;
  5213. ulLen = wcslen(szMachineName);
  5214. NetStatus = NetApiBufferAllocate( (ulLen + 2) * sizeof(WCHAR),
  5215. (PBYTE *) &szMachineAccountName );
  5216. if ( NetStatus == NERR_Success )
  5217. {
  5218. wcscpy(szMachineAccountName, szMachineName);
  5219. _wcsupr(szMachineAccountName);
  5220. szMachineAccountName[ulLen] = L'$';
  5221. szMachineAccountName[ulLen+1] = UNICODE_NULL;
  5222. *pszMachineAccountName = szMachineAccountName;
  5223. }
  5224. return NetStatus;
  5225. }
  5226. NET_API_STATUS
  5227. NET_API_FUNCTION
  5228. NetpGeneratePassword(
  5229. IN LPCWSTR szMachine,
  5230. IN BOOL fRandomPwdPreferred,
  5231. IN LPCWSTR szDcName,
  5232. IN BOOL fIsNt4Dc,
  5233. OUT LPWSTR szPassword
  5234. )
  5235. /*++
  5236. Routine Description:
  5237. Arguments:
  5238. szMachine -- name of a computer
  5239. szPassword -- receives the generated password this buffer must be
  5240. atleast PWLEN+1 char long.
  5241. Returns:
  5242. NERR_Success -- Success
  5243. --*/
  5244. {
  5245. NET_API_STATUS NetStatus = NERR_Success;
  5246. BOOL fUseDefaultPwd = FALSE;
  5247. // The default password is used if we are joining an NT4 DC
  5248. // that has RefusePasswordChange set. This is determined by
  5249. // remotely reading the appropriate netlogon regval.
  5250. // If the key cannot be read, it is assumed that the value is not set
  5251. //
  5252. if ( fIsNt4Dc )
  5253. {
  5254. //
  5255. // we are joining an NT4 domain, see if RefusePasswordChange is set
  5256. //
  5257. NetStatus = NetpGetNt4RefusePasswordChangeStatus( szDcName,
  5258. &fUseDefaultPwd );
  5259. }
  5260. if ( NetStatus == NERR_Success )
  5261. {
  5262. //
  5263. // if we are explicitly asked to use a default password, generate one
  5264. //
  5265. if ( fUseDefaultPwd )
  5266. {
  5267. NetpGenerateDefaultPassword(szMachine, szPassword);
  5268. }
  5269. //
  5270. // otherwise if the caller prefers a random password, generate one
  5271. //
  5272. else if ( fRandomPwdPreferred )
  5273. {
  5274. NetStatus = NetpGenerateRandomPassword(szPassword);
  5275. }
  5276. #if defined(REMOTE_BOOT)
  5277. //
  5278. // If it's a remote boot machine, then this will return the
  5279. // current machine account password, so use that.
  5280. //
  5281. else if (NERR_Success ==
  5282. NetpGetRemoteBootMachinePassword(szPassword))
  5283. {
  5284. // do nothing since the above already generated the password
  5285. }
  5286. #endif
  5287. else
  5288. {
  5289. //
  5290. // if none of the above apply,
  5291. // we end up generating a default password
  5292. //
  5293. NetpGenerateDefaultPassword(szMachine, szPassword);
  5294. NetStatus = NERR_Success;
  5295. }
  5296. }
  5297. return NetStatus;
  5298. }
  5299. void
  5300. NetpGenerateDefaultPassword(
  5301. IN LPCWSTR szMachine,
  5302. OUT LPWSTR szPassword
  5303. )
  5304. /*++
  5305. Routine Description:
  5306. Generate the default password from machine name.
  5307. This is simply the first 14 characters of the machine name lower cased.
  5308. Arguments:
  5309. szMachine -- name of a computer
  5310. szPassword -- receives the generated password
  5311. Returns:
  5312. NERR_Success -- Success
  5313. --*/
  5314. {
  5315. wcsncpy( szPassword, szMachine, LM20_PWLEN );
  5316. szPassword[LM20_PWLEN] = UNICODE_NULL;
  5317. _wcslwr( szPassword );
  5318. }
  5319. BOOL
  5320. NetpIsDefaultPassword(
  5321. IN LPCWSTR szMachine,
  5322. IN LPWSTR szPassword
  5323. )
  5324. /*++
  5325. Routine Description:
  5326. Determine if szPassword is the default password for szMachine
  5327. Arguments:
  5328. szMachine -- name of a computer
  5329. szPassword -- machine password
  5330. Returns:
  5331. TRUE if szPassword is the default password,
  5332. FALSE otherwise
  5333. --*/
  5334. {
  5335. WCHAR szPassword2[LM20_PWLEN+1];
  5336. NetpGenerateDefaultPassword(szMachine, szPassword2);
  5337. return (wcscmp(szPassword, szPassword2) == 0);
  5338. }
  5339. NET_API_STATUS
  5340. NET_API_FUNCTION
  5341. NetpGenerateRandomPassword(
  5342. OUT LPWSTR szPassword
  5343. )
  5344. {
  5345. NET_API_STATUS NetStatus=NERR_Success;
  5346. ULONG Length, i;
  5347. BYTE n;
  5348. HCRYPTPROV CryptProvider = 0;
  5349. LPWSTR szPwd=szPassword;
  5350. BOOL fStatus;
  5351. #define PWD_CHAR_MIN 32 // ' ' space
  5352. #define PWD_CHAR_MAX 122 // 'z'
  5353. //
  5354. // there is a reason behind this number
  5355. //
  5356. Length = 120;
  5357. szPassword[Length] = UNICODE_NULL;
  5358. //
  5359. // Generate a random password.
  5360. //
  5361. // the password is made of english printable chars. when w2k client
  5362. // joins NT4 dc. SAM on the dc calls RRtlUpcaseUnicodeStringToOemString
  5363. // the password length will remain unchanged. If we do not do this,
  5364. // the dc returns STATUS_PASSWORD_RESTRICTION and we have to
  5365. // fall back to default password.
  5366. //
  5367. if ( CryptAcquireContext( &CryptProvider, NULL, NULL,
  5368. PROV_RSA_FULL, CRYPT_VERIFYCONTEXT ) )
  5369. {
  5370. for ( i = 0; i < Length; i++, szPwd++ )
  5371. {
  5372. //
  5373. // the method we use here is not very efficient.
  5374. // This does not matter much in the context of NetJoin apis
  5375. // but it should not be used where perf is a criterion
  5376. //
  5377. while ( ( fStatus = CryptGenRandom( CryptProvider, sizeof(BYTE),
  5378. (LPBYTE) &n ) ) &&
  5379. ( ( n < PWD_CHAR_MIN ) || ( n > PWD_CHAR_MAX ) ) )
  5380. {
  5381. // try till we get a non-zero random number
  5382. }
  5383. if ( fStatus )
  5384. {
  5385. *szPwd = (WCHAR) n;
  5386. }
  5387. else
  5388. {
  5389. NetStatus = GetLastError();
  5390. break;
  5391. }
  5392. }
  5393. CryptReleaseContext( CryptProvider, 0 );
  5394. }
  5395. else
  5396. {
  5397. NetStatus = GetLastError();
  5398. }
  5399. if ( NetStatus != NERR_Success )
  5400. {
  5401. NetpLog(( "NetpGenerateRandomPassword: failed: 0x%lx\n", NetStatus ));
  5402. }
  5403. return NetStatus;
  5404. }
  5405. NET_API_STATUS
  5406. NET_API_FUNCTION
  5407. NetpStoreIntialDcRecord(
  5408. IN PDOMAIN_CONTROLLER_INFO DcInfo
  5409. )
  5410. /*++
  5411. Routine Description:
  5412. This function will cache the name of the domain controller on which we successfully
  5413. created/modified the machine account, so that the auth packages will know which dc to
  5414. try first
  5415. Arguments:
  5416. lpDcName - Name of the DC on which the account was created/modified
  5417. CreateNetlogonStoppedKey - If TRUE, a volatile key will be created
  5418. in the Netlogon registry section. The presence of this key
  5419. will instruct the client side of DsGetDcName( ) and the MSV1
  5420. package not to wait on netlogon to start.
  5421. Return Value:
  5422. NERR_Success -- Success
  5423. --*/
  5424. {
  5425. NET_API_STATUS NetStatus = NERR_Success;
  5426. HKEY hNetLogon, hJoinKey = NULL;
  5427. ULONG Disp;
  5428. NetStatus = RegOpenKey( HKEY_LOCAL_MACHINE,
  5429. NETSETUPP_NETLOGON_JD_PATH,
  5430. &hNetLogon );
  5431. if ( NetStatus == NERR_Success ) {
  5432. NetStatus = RegCreateKeyEx( hNetLogon,
  5433. NETSETUPP_NETLOGON_JD,
  5434. 0,
  5435. NULL,
  5436. REG_OPTION_NON_VOLATILE,
  5437. KEY_WRITE,
  5438. NULL,
  5439. &hJoinKey,
  5440. &Disp );
  5441. //
  5442. // Now, start creating all of the values. Ignore any failures, and don't write out
  5443. // NULL values
  5444. //
  5445. if ( NetStatus == NERR_Success ) {
  5446. PWSTR String = DcInfo->DomainControllerName;
  5447. //
  5448. // DomainControllerName
  5449. //
  5450. if ( String ) {
  5451. NetStatus = RegSetValueEx( hJoinKey,
  5452. NETSETUPP_NETLOGON_JD_DC,
  5453. 0,
  5454. REG_SZ,
  5455. ( const PBYTE )String,
  5456. ( wcslen( String ) + 1 ) * sizeof( WCHAR ) );
  5457. if ( NetStatus != NERR_Success ) {
  5458. NetpLog(( "Set of value %ws to %ws failed with %lu\n",
  5459. NETSETUPP_NETLOGON_JD_DC, String, NetStatus ));
  5460. }
  5461. }
  5462. //
  5463. // DomainControllerAddress
  5464. //
  5465. String = DcInfo->DomainControllerAddress;
  5466. if ( String ) {
  5467. NetStatus = RegSetValueEx( hJoinKey,
  5468. NETSETUPP_NETLOGON_JD_DCA,
  5469. 0,
  5470. REG_SZ,
  5471. ( const PBYTE )String,
  5472. ( wcslen( String ) + 1 ) * sizeof( WCHAR ) );
  5473. if ( NetStatus != NERR_Success ) {
  5474. NetpLog(( "Set of value %ws to %ws failed with %lu\n",
  5475. NETSETUPP_NETLOGON_JD_DCA, String, NetStatus ));
  5476. }
  5477. }
  5478. //
  5479. // DomainControllerType
  5480. //
  5481. NetStatus = RegSetValueEx( hJoinKey,
  5482. NETSETUPP_NETLOGON_JD_DCAT,
  5483. 0,
  5484. REG_DWORD,
  5485. ( const PBYTE )&DcInfo->DomainControllerAddressType,
  5486. sizeof( ULONG ) );
  5487. if ( NetStatus != NERR_Success ) {
  5488. NetpLog(( "Set of value %ws to %lu failed with %lu\n",
  5489. NETSETUPP_NETLOGON_JD_DCAT,
  5490. DcInfo->DomainControllerAddressType, NetStatus ));
  5491. }
  5492. //
  5493. // DomainControllerType
  5494. //
  5495. NetStatus = RegSetValueEx( hJoinKey,
  5496. NETSETUPP_NETLOGON_JD_DG,
  5497. 0,
  5498. REG_BINARY,
  5499. ( const PBYTE )&DcInfo->DomainGuid,
  5500. sizeof( GUID ) );
  5501. if ( NetStatus != NERR_Success ) {
  5502. NetpLog(( "Set of value %ws failed with %lu\n",
  5503. NETSETUPP_NETLOGON_JD_DG, NetStatus ));
  5504. }
  5505. //
  5506. // DomainName
  5507. //
  5508. String = DcInfo->DomainName;
  5509. if ( String ) {
  5510. NetStatus = RegSetValueEx( hJoinKey,
  5511. NETSETUPP_NETLOGON_JD_DN,
  5512. 0,
  5513. REG_SZ,
  5514. ( const PBYTE )String,
  5515. ( wcslen( String ) + 1 ) * sizeof( WCHAR ) );
  5516. if ( NetStatus != NERR_Success ) {
  5517. NetpLog(( "Set of value %ws to %ws failed with %lu\n",
  5518. NETSETUPP_NETLOGON_JD_DN, String, NetStatus ));
  5519. }
  5520. }
  5521. //
  5522. // DnsForestName
  5523. //
  5524. String = DcInfo->DnsForestName;
  5525. if ( String ) {
  5526. NetStatus = RegSetValueEx( hJoinKey,
  5527. NETSETUPP_NETLOGON_JD_DFN,
  5528. 0,
  5529. REG_SZ,
  5530. ( const PBYTE )String,
  5531. ( wcslen( String ) + 1 ) * sizeof( WCHAR ) );
  5532. if ( NetStatus != NERR_Success ) {
  5533. NetpLog(( "Set of value %ws to %ws failed with %lu\n",
  5534. NETSETUPP_NETLOGON_JD_DFN, String, NetStatus ));
  5535. }
  5536. }
  5537. //
  5538. // Flags
  5539. //
  5540. NetStatus = RegSetValueEx( hJoinKey,
  5541. NETSETUPP_NETLOGON_JD_F,
  5542. 0,
  5543. REG_DWORD,
  5544. ( const PBYTE )&DcInfo->Flags,
  5545. sizeof( ULONG ) );
  5546. if ( NetStatus != NERR_Success ) {
  5547. NetpLog(( "Set of value %ws to %lu failed with %lu\n",
  5548. NETSETUPP_NETLOGON_JD_F, DcInfo->Flags, NetStatus ));
  5549. }
  5550. //
  5551. // DcSiteName
  5552. //
  5553. String = DcInfo->DcSiteName;
  5554. if ( String ) {
  5555. NetStatus = RegSetValueEx( hJoinKey,
  5556. NETSETUPP_NETLOGON_JD_DSN,
  5557. 0,
  5558. REG_SZ,
  5559. ( const PBYTE )String,
  5560. ( wcslen( String ) + 1 ) * sizeof( WCHAR ) );
  5561. if ( NetStatus != NERR_Success ) {
  5562. NetpLog(( "Set of value %ws to %ws failed with %lu\n",
  5563. NETSETUPP_NETLOGON_JD_DSN, String, NetStatus ));
  5564. }
  5565. }
  5566. //
  5567. // DcSiteName
  5568. //
  5569. String = DcInfo->ClientSiteName;
  5570. if ( String ) {
  5571. NetStatus = RegSetValueEx( hJoinKey,
  5572. NETSETUPP_NETLOGON_JD_CSN,
  5573. 0,
  5574. REG_SZ,
  5575. ( const PBYTE )String,
  5576. ( wcslen( String ) + 1 ) * sizeof( WCHAR ) );
  5577. if ( NetStatus != NERR_Success ) {
  5578. NetpLog(( "Set of value %ws to %ws failed with %lu\n",
  5579. NETSETUPP_NETLOGON_JD_CSN, String, NetStatus ));
  5580. }
  5581. }
  5582. RegCloseKey( hJoinKey );
  5583. }
  5584. RegCloseKey( hNetLogon );
  5585. }
  5586. return( NetStatus );
  5587. }
  5588. VOID
  5589. NetpAvoidNetlogonSpnSet(
  5590. BOOL AvoidSet
  5591. )
  5592. /*++
  5593. Routine Description:
  5594. This function will write into Netlogon reg key to instruct Netlogon
  5595. not to register DnsHostName and SPNs. This is needed because
  5596. Netlogon could otherwise set incorrect values based on the old computer
  5597. name. The registry key that this function writes is volatile so that
  5598. Netlogon will notice it before the reboot but it will not exist after
  5599. the reboot when Netlogon will have the new computer name.
  5600. Arguments:
  5601. AvoidSet - If TRUE, this routine will inform netlogon to not write SPNs
  5602. Otherwise, it will delete the reg key which we may have set previously.
  5603. Return Value:
  5604. None
  5605. --*/
  5606. {
  5607. NET_API_STATUS NetStatus = NERR_Success;
  5608. HKEY hNetLogon = NULL;
  5609. HKEY hNetLogonAvoidSpnSet = NULL;
  5610. ULONG Disp;
  5611. NetStatus = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  5612. NETSETUPP_NETLOGON_JD_PATH,
  5613. 0,
  5614. KEY_ALL_ACCESS,
  5615. &hNetLogon );
  5616. if ( NetStatus == NERR_Success ) {
  5617. //
  5618. // If we are to avoid SPN setting by netlogon,
  5619. // write the appropriate reg key to inform Netlogon accordingly
  5620. //
  5621. if ( AvoidSet ) {
  5622. NetStatus = RegCreateKeyEx( hNetLogon,
  5623. NETSETUPP_NETLOGON_AVOID_SPN,
  5624. 0,
  5625. NULL,
  5626. REG_OPTION_VOLATILE,
  5627. KEY_WRITE,
  5628. NULL,
  5629. &hNetLogonAvoidSpnSet,
  5630. &Disp );
  5631. if ( NetStatus == NERR_Success ) {
  5632. RegCloseKey( hNetLogonAvoidSpnSet );
  5633. }
  5634. //
  5635. // Otherwise, delete the reg key which we may have set previously.
  5636. //
  5637. } else {
  5638. RegDeleteKey( hNetLogon,
  5639. NETSETUPP_NETLOGON_AVOID_SPN );
  5640. }
  5641. RegCloseKey( hNetLogon );
  5642. }
  5643. }
  5644. LONG cNetsetupLogRefCount=0;
  5645. LONG LogWriterCount=0;
  5646. HANDLE hDebugLog = NULL;
  5647. BOOL LogFileInitialized = FALSE;
  5648. BOOL LogShutdownInProgress = FALSE;
  5649. DWORD NetpOpenLogThreadId = 0;
  5650. void
  5651. NetSetuppOpenLog()
  5652. {
  5653. //
  5654. // Increment the ref count atomically
  5655. //
  5656. LONG LocalLogRefCount = InterlockedIncrement( &cNetsetupLogRefCount );
  5657. //
  5658. // If we are the first thread to access the log,
  5659. // initialize the log and open it
  5660. //
  5661. if ( LocalLogRefCount == 1 ) {
  5662. //
  5663. // It's possible that the log hasn't been
  5664. // shut down yet by the last thread leaving
  5665. // the log, so wait until it is.
  5666. //
  5667. while ( LogFileInitialized || LogShutdownInProgress ) {
  5668. Sleep(100);
  5669. }
  5670. NetpInitializeLogFile();
  5671. NetpOpenLogThreadId = GetCurrentThreadId();
  5672. //
  5673. // Now open the log and mark the start of the output
  5674. //
  5675. hDebugLog = NetpOpenDebugFile( L"NetSetup", FALSE );
  5676. LogFileInitialized = TRUE;
  5677. NetpLog(("-----------------------------------------------------------------\n" ));
  5678. //
  5679. // If we are not the first one, it's possible
  5680. // that the log hasn't been initilized by the
  5681. // first thread yet, so wait until it is
  5682. //
  5683. } else {
  5684. while ( !LogFileInitialized ) {
  5685. Sleep(100);
  5686. }
  5687. }
  5688. }
  5689. void
  5690. NetSetuppCloseLog()
  5691. {
  5692. LONG LocalLogRefCount;
  5693. //
  5694. // We can walk into this routine only if
  5695. // we previously initialized the log
  5696. //
  5697. ASSERT( cNetsetupLogRefCount > 0 );
  5698. ASSERT( LogFileInitialized == TRUE );
  5699. //
  5700. // Decrement the ref count atomically
  5701. //
  5702. LocalLogRefCount = InterlockedDecrement( &cNetsetupLogRefCount );
  5703. //
  5704. // If we are the last thread to close the log,
  5705. // shut the log down
  5706. //
  5707. if ( LocalLogRefCount == 0 ) {
  5708. //
  5709. // Inform writers we are shutting the log down
  5710. //
  5711. LogShutdownInProgress = TRUE;
  5712. //
  5713. // Wait until all writers leave the log
  5714. //
  5715. while ( LogWriterCount > 0 ) {
  5716. Sleep(100);
  5717. }
  5718. //
  5719. // Close the log
  5720. //
  5721. NetpCloseDebugFile( hDebugLog );
  5722. hDebugLog = NULL;
  5723. NetpOpenLogThreadId = 0;
  5724. NetpShutdownLogFile();
  5725. LogFileInitialized = FALSE;
  5726. //
  5727. // We are done with log shutdown
  5728. //
  5729. LogShutdownInProgress = FALSE;
  5730. }
  5731. }
  5732. void
  5733. NetpLogPrintHelper(
  5734. IN LPCSTR Format,
  5735. ...)
  5736. {
  5737. va_list arglist;
  5738. //
  5739. // Don't go any further if the file isn't ready
  5740. //
  5741. if ( !LogFileInitialized || LogShutdownInProgress ) {
  5742. return;
  5743. }
  5744. //
  5745. // Increment the number of log writers to prevent
  5746. // the log from shutting down behind our back
  5747. //
  5748. InterlockedIncrement( &LogWriterCount );
  5749. //
  5750. // Now that we have our writer reference,
  5751. // it's safe to write into the log if
  5752. // it is ready at this point
  5753. //
  5754. if ( LogFileInitialized && !LogShutdownInProgress ) {
  5755. va_start(arglist, Format);
  5756. NetpLogPrintRoutineVEx(hDebugLog, &NetpOpenLogThreadId, (LPSTR) Format, arglist);
  5757. va_end(arglist);
  5758. }
  5759. //
  5760. // Remove our reference
  5761. //
  5762. InterlockedDecrement( &LogWriterCount );
  5763. }
  5764. NET_API_STATUS
  5765. NET_API_FUNCTION
  5766. NetpWaitForNetlogonSc(
  5767. IN LPCWSTR szDomainName
  5768. )
  5769. {
  5770. NET_API_STATUS NetStatus = NERR_Success;
  5771. NTSTATUS NlSubStatus=STATUS_SUCCESS;
  5772. LPBYTE pNetlogonInfo=NULL;
  5773. UINT cAttempts=0;
  5774. BOOLEAN fScSetup=FALSE;
  5775. PNETLOGON_INFO_2 pNetlogonInfo2;
  5776. #define NL_SC_WAIT_INTERVAL 2000
  5777. #define NL_SC_WAIT_NUM_ATTEMPTS 60
  5778. NetpLog(( "NetpWaitForNetlogonSc: waiting for netlogon secure channel setup...\n"));
  5779. while (!fScSetup && (cAttempts < NL_SC_WAIT_NUM_ATTEMPTS))
  5780. {
  5781. cAttempts++;
  5782. NetStatus = I_NetLogonControl2( NULL, NETLOGON_CONTROL_TC_QUERY,
  5783. 2, (LPBYTE) &szDomainName,
  5784. (LPBYTE *) &pNetlogonInfo );
  5785. if (NetStatus == NERR_Success)
  5786. {
  5787. pNetlogonInfo2 = (PNETLOGON_INFO_2) pNetlogonInfo;
  5788. NlSubStatus = pNetlogonInfo2->netlog2_tc_connection_status;
  5789. fScSetup = NlSubStatus == NERR_Success;
  5790. NetApiBufferFree(pNetlogonInfo);
  5791. }
  5792. if (!fScSetup)
  5793. {
  5794. Sleep(NL_SC_WAIT_INTERVAL);
  5795. }
  5796. }
  5797. NetpLog(( "NetpWaitForNetlogonSc: status: 0x%lx, sub-status: 0x%lx\n",
  5798. NetStatus, NlSubStatus));
  5799. return NetStatus;
  5800. }
  5801. NET_API_STATUS
  5802. NET_API_FUNCTION
  5803. NetpDsSetSPN2(
  5804. IN LPCWSTR szComputerName,
  5805. IN LPCWSTR szDnsDomainName,
  5806. IN LPCWSTR szUncDcName,
  5807. IN LPCWSTR szUser,
  5808. IN LPCWSTR szUserPassword
  5809. )
  5810. /*++
  5811. Routine Description:
  5812. Arguments:
  5813. Return Value:
  5814. ignored - this is a thread pool worker function.
  5815. --*/
  5816. {
  5817. DWORD NetStatus=NERR_Success;
  5818. ULONG LdapStatus;
  5819. LPWSTR Spn = NULL;
  5820. LPWSTR SpnNetbios = NULL;
  5821. LPWSTR SamName = NULL;
  5822. LPWSTR SamAccountName = NULL;
  5823. HANDLE hDs = NULL;
  5824. LDAP *hLdap = NULL;
  5825. PDS_NAME_RESULTW CrackedName = NULL;
  5826. LPWSTR DnOfAccount;
  5827. LONG LdapOption;
  5828. LPWSTR DnsHostName = NULL;
  5829. LPWSTR DnsHostNameValues[2];
  5830. LDAPModW DnsHostNameAttr;
  5831. LDAPModW *Mods[2];
  5832. RPC_AUTH_IDENTITY_HANDLE AuthId = 0;
  5833. WCHAR szComputerNetbiosName[MAX_COMPUTERNAME_LENGTH+1];
  5834. LPWSTR szUserName=NULL;
  5835. LPWSTR szDomainName=NULL;
  5836. LPWSTR szNetbiosDomainName=NULL;
  5837. LPWSTR szT;
  5838. ULONG uLen;
  5839. static WCHAR c_szHostPrefix[] = L"HOST/";
  5840. NetStatus = NetpSeparateUserAndDomain(szUser, &szUserName, &szDomainName);
  5841. if (NetStatus == NERR_Success)
  5842. {
  5843. NetStatus = DsMakePasswordCredentials(szUserName, szDomainName,
  5844. szUserPassword, &AuthId);
  5845. if (NetStatus != NERR_Success)
  5846. {
  5847. goto Cleanup;
  5848. }
  5849. }
  5850. //
  5851. // first build various names
  5852. //
  5853. //
  5854. // build Netbios domain name
  5855. //
  5856. uLen = wcslen(szDnsDomainName);
  5857. NetStatus = NetpDuplicateString(szDnsDomainName, uLen, &szNetbiosDomainName);
  5858. if (NetStatus == NERR_Success)
  5859. {
  5860. if (uLen > MAX_COMPUTERNAME_LENGTH)
  5861. {
  5862. szNetbiosDomainName[MAX_COMPUTERNAME_LENGTH] = UNICODE_NULL;
  5863. }
  5864. szT = wcschr(szNetbiosDomainName, L'.');
  5865. if (szT)
  5866. {
  5867. *szT = UNICODE_NULL;
  5868. }
  5869. }
  5870. //
  5871. // build SamAccountName
  5872. //
  5873. NetStatus = NetpGetMachineAccountName(szComputerName,
  5874. &SamAccountName);
  5875. if (NetStatus == NERR_Success)
  5876. {
  5877. //
  5878. // build SamName
  5879. //
  5880. NetStatus = NetpConcatStrings3(szNetbiosDomainName, -1,
  5881. L"\\", 1,
  5882. SamAccountName, -1,
  5883. &SamName);
  5884. if (NetStatus == NERR_Success)
  5885. {
  5886. wcsncpy(szComputerNetbiosName,
  5887. szComputerName, MAX_COMPUTERNAME_LENGTH);
  5888. szComputerNetbiosName[MAX_COMPUTERNAME_LENGTH] = UNICODE_NULL;
  5889. //
  5890. // build DnsHostName
  5891. //
  5892. NetStatus = NetpConcatStrings3( szComputerNetbiosName,
  5893. -1,
  5894. L".", 1,
  5895. szDnsDomainName, -1,
  5896. &DnsHostName );
  5897. if (NetStatus == NERR_Success)
  5898. {
  5899. //
  5900. // build SPN
  5901. //
  5902. NetStatus = NetpConcatStrings(c_szHostPrefix,
  5903. (sizeof( c_szHostPrefix )/
  5904. sizeof(WCHAR) ) - 1,
  5905. DnsHostName, -1,
  5906. &Spn);
  5907. if (NetStatus == NERR_Success)
  5908. {
  5909. //
  5910. // build NebBios SPN
  5911. //
  5912. NetStatus = NetpConcatStrings(c_szHostPrefix,
  5913. (sizeof( c_szHostPrefix )/
  5914. sizeof(WCHAR) ) - 1,
  5915. szComputerNetbiosName, -1,
  5916. &SpnNetbios);
  5917. }
  5918. }
  5919. }
  5920. }
  5921. if (NetStatus != NERR_Success)
  5922. {
  5923. goto Cleanup;
  5924. }
  5925. //
  5926. // Bind to the DS on the DC.
  5927. //
  5928. NetStatus = DsBindWithCredW(szUncDcName, NULL, AuthId, &hDs);
  5929. if ( NetStatus != NO_ERROR )
  5930. {
  5931. NetpLog(("NetpDsSetSPN: Unable to bind to DS on '%ws': 0x%lx\n",
  5932. szUncDcName, NetStatus ));
  5933. goto Cleanup ;
  5934. }
  5935. //
  5936. // Crack the sam account name into a DN:
  5937. //
  5938. NetStatus = DsCrackNamesW( hDs, 0, DS_NT4_ACCOUNT_NAME, DS_FQDN_1779_NAME,
  5939. 1, &SamName, &CrackedName );
  5940. if ( ( NetStatus != NO_ERROR ) ||
  5941. ( CrackedName->cItems != 1 ) )
  5942. {
  5943. NetpLog(( "NetpDsSetSPN: CrackNames failed on '%ws' for '%ws': 0x%lx\n",
  5944. szUncDcName, SamName, NetStatus ));
  5945. goto Cleanup ;
  5946. }
  5947. if ( CrackedName->rItems[ 0 ].status != 0 )
  5948. {
  5949. //$ kumarp 15-July-1999
  5950. //
  5951. // we dont change NetStatus here thus treat this failure as success
  5952. // and end up not setting the DnsHostName/SPN
  5953. //
  5954. NetpLog(( "NetpDsSetSPN: CrackNames failed on %ws for %ws: substatus 0x%lx\n",
  5955. szUncDcName, SamName, CrackedName->rItems[ 0 ].status ));
  5956. goto Cleanup ;
  5957. }
  5958. DnOfAccount = CrackedName->rItems[0].pName;
  5959. NetStatus = NetpLdapBind( (LPWSTR) szUncDcName,
  5960. (LPWSTR) szUser,
  5961. (LPWSTR) szUserPassword, &hLdap );
  5962. if (NetStatus == NERR_Success)
  5963. {
  5964. //
  5965. // Write the DNS host name
  5966. //
  5967. DnsHostNameValues[0] = DnsHostName;
  5968. DnsHostNameValues[1] = NULL;
  5969. DnsHostNameAttr.mod_op = LDAP_MOD_REPLACE;
  5970. DnsHostNameAttr.mod_type = L"DnsHostName";
  5971. DnsHostNameAttr.mod_values = DnsHostNameValues;
  5972. Mods[0] = &DnsHostNameAttr;
  5973. Mods[1] = NULL;
  5974. NetpLog (("NetpDsSetSPN: Setting DnsHostName '%ws' on '%ws'\n", DnsHostName, DnOfAccount));
  5975. LdapStatus = ldap_modify_sW( hLdap, DnOfAccount, Mods );
  5976. if ( LdapStatus != LDAP_SUCCESS )
  5977. {
  5978. NetStatus = LdapMapErrorToWin32(LdapStatus);
  5979. NetpLog(( "NetpDsSetSPN: ldap_modify_s failed on '%ws' for '%ws': '%s' : 0x%lx\n",
  5980. szUncDcName, SamName, ldap_err2stringA( LdapStatus ),
  5981. NetStatus));
  5982. goto Cleanup;
  5983. }
  5984. }
  5985. if (NetStatus == NERR_Success)
  5986. {
  5987. //
  5988. // Write the SPN.
  5989. //
  5990. NetpLog (("NetpDsSetSPN: Setting SPN '%ws' on '%ws'\n", Spn, DnOfAccount));
  5991. NetStatus = DsWriteAccountSpn( hDs, DS_SPN_ADD_SPN_OP,
  5992. DnOfAccount, 1, &Spn );
  5993. if (NetStatus == NERR_Success)
  5994. {
  5995. NetpLog (("NetpDsSetSPN: Setting SPN '%ws' on '%ws'\n", SpnNetbios, DnOfAccount));
  5996. NetStatus = DsWriteAccountSpn( hDs, DS_SPN_ADD_SPN_OP,
  5997. DnOfAccount, 1, &SpnNetbios );
  5998. }
  5999. if ( NetStatus != NO_ERROR )
  6000. {
  6001. NetpLog(( "NetpDsSetSPN: DsWriteAccountSpn failed on '%ws' for '%ws': 0x%lx\n", szUncDcName, SamName, NetStatus ));
  6002. }
  6003. }
  6004. Cleanup:
  6005. if ( hDs )
  6006. {
  6007. DsUnBind( &hDs );
  6008. }
  6009. if ( CrackedName )
  6010. {
  6011. DsFreeNameResultW( CrackedName );
  6012. }
  6013. if ( hLdap != NULL )
  6014. {
  6015. ldap_unbind_s( hLdap );
  6016. }
  6017. if ( AuthId )
  6018. {
  6019. DsFreePasswordCredentials( AuthId );
  6020. }
  6021. NetApiBufferFree( Spn );
  6022. NetApiBufferFree( SpnNetbios );
  6023. NetApiBufferFree( SamAccountName );
  6024. NetApiBufferFree( SamName );
  6025. NetApiBufferFree( szNetbiosDomainName );
  6026. NetApiBufferFree( szDomainName );
  6027. NetApiBufferFree( szUserName );
  6028. return NetStatus ;
  6029. }
  6030. NET_API_STATUS
  6031. NET_API_FUNCTION
  6032. NetpGetDefaultLcidOnMachine(
  6033. IN LPCWSTR szMachine,
  6034. OUT LCID* plcidMachine
  6035. )
  6036. {
  6037. NET_API_STATUS NetStatus = NERR_Success;
  6038. HKEY hkeyRemoteMachine, hkeyLanguage;
  6039. WCHAR szLocale[16];
  6040. DWORD dwLocaleSize=0;
  6041. DWORD dwType;
  6042. static WCHAR c_szRegKeySystemLanguage[] =
  6043. L"System\\CurrentControlSet\\Control\\Nls\\Locale";
  6044. static WCHAR c_szRegValDefault[] = L"(Default)";
  6045. //
  6046. // Connect to the remote registry
  6047. //
  6048. if ( NetStatus == NERR_Success )
  6049. {
  6050. NetStatus = RegConnectRegistry( szMachine,
  6051. HKEY_LOCAL_MACHINE,
  6052. &hkeyRemoteMachine );
  6053. //
  6054. // Now, open the system language key
  6055. //
  6056. if ( NetStatus == NERR_Success )
  6057. {
  6058. NetStatus = RegOpenKeyEx( hkeyRemoteMachine,
  6059. c_szRegKeySystemLanguage,
  6060. 0, KEY_READ, &hkeyLanguage);
  6061. //
  6062. // get default locale
  6063. //
  6064. if ( NetStatus == NERR_Success )
  6065. {
  6066. dwLocaleSize = sizeof( szLocale );
  6067. NetStatus = RegQueryValueEx( hkeyLanguage,
  6068. c_szRegValDefault,
  6069. NULL, &dwType,
  6070. (LPBYTE) szLocale,
  6071. &dwLocaleSize );
  6072. if ( NetStatus == NERR_Success)
  6073. {
  6074. if ((dwType == REG_SZ) &&
  6075. (swscanf(szLocale, L"%lx", plcidMachine) != 1))
  6076. {
  6077. //$ REVIEW kumarp 29-May-1999
  6078. // better errorcode?
  6079. NetStatus = ERROR_INVALID_PARAMETER;
  6080. }
  6081. }
  6082. RegCloseKey( hkeyLanguage );
  6083. }
  6084. RegCloseKey( hkeyRemoteMachine );
  6085. }
  6086. }
  6087. return NetStatus;
  6088. }
  6089. NET_API_STATUS
  6090. NET_API_FUNCTION
  6091. NetpVerifyStrOemCompatibleInLocale(
  6092. IN LPCWSTR szString,
  6093. IN LCID lcidRemote
  6094. )
  6095. {
  6096. NET_API_STATUS NetStatus = NERR_Success;
  6097. NTSTATUS NtStatus=STATUS_SUCCESS;
  6098. OEM_STRING osLocal = { 0 };
  6099. OEM_STRING osRemote = { 0 };
  6100. UNICODE_STRING sString;
  6101. LCID lcidLocal;
  6102. lcidLocal = GetThreadLocale();
  6103. RtlInitUnicodeString(&sString, szString);
  6104. NtStatus = RtlUnicodeStringToOemString(&osLocal, &sString, TRUE);
  6105. __try
  6106. {
  6107. if (NtStatus == STATUS_SUCCESS)
  6108. {
  6109. if (SetThreadLocale(lcidRemote))
  6110. {
  6111. NtStatus = RtlUnicodeStringToOemString(&osRemote,
  6112. &sString, TRUE);
  6113. if (NtStatus == STATUS_SUCCESS)
  6114. {
  6115. if (!RtlEqualMemory(osLocal.Buffer, osRemote.Buffer,
  6116. osLocal.Length))
  6117. {
  6118. NetStatus = NERR_NameUsesIncompatibleCodePage;
  6119. }
  6120. }
  6121. else
  6122. {
  6123. NetStatus = RtlNtStatusToDosError(NtStatus);
  6124. }
  6125. }
  6126. else
  6127. {
  6128. NetStatus = GetLastError();
  6129. }
  6130. }
  6131. else
  6132. {
  6133. NetStatus = RtlNtStatusToDosError(NtStatus);
  6134. }
  6135. }
  6136. __finally
  6137. {
  6138. if (!SetThreadLocale(lcidLocal))
  6139. {
  6140. NetStatus = GetLastError();
  6141. }
  6142. // RtlFreeOemString checks for NULL Buffer
  6143. RtlFreeOemString(&osLocal);
  6144. RtlFreeOemString(&osRemote);
  6145. }
  6146. return NetStatus;
  6147. }
  6148. NET_API_STATUS
  6149. NET_API_FUNCTION
  6150. NetpVerifyStrOemCompatibleOnMachine(
  6151. IN LPCWSTR szRemoteMachine,
  6152. IN LPCWSTR szString
  6153. )
  6154. {
  6155. NET_API_STATUS NetStatus = NERR_Success;
  6156. LCID lcidRemoteMachine;
  6157. NetStatus = NetpGetDefaultLcidOnMachine(szRemoteMachine,
  6158. &lcidRemoteMachine);
  6159. if (NetStatus == NERR_Success)
  6160. {
  6161. NetStatus = NetpVerifyStrOemCompatibleInLocale(szString,
  6162. lcidRemoteMachine);
  6163. }
  6164. return NetStatus;
  6165. }
  6166. #define NETP_NETLOGON_PATH L"System\\CurrentControlSet\\services\\Netlogon\\parameters\\"
  6167. #define NETP_NETLOGON_RPC L"RefusePasswordChange"
  6168. NET_API_STATUS
  6169. NET_API_FUNCTION
  6170. NetpGetNt4RefusePasswordChangeStatus(
  6171. IN LPCWSTR Nt4Dc,
  6172. OUT BOOL* RefusePasswordChangeSet
  6173. )
  6174. /*++
  6175. Routine Description:
  6176. Read the regkey NETP_NETLOGON_PATH\NETP_NETLOGON_RPC on Nt4Dc.
  6177. Return the value read in the out parameter.
  6178. Arguments:
  6179. Nt4Dc -- name of machine to read reg. from
  6180. RefusePasswordChangeSet -- value returned
  6181. Returns:
  6182. NERR_Success -- Success
  6183. --*/
  6184. {
  6185. NET_API_STATUS NetStatus = NERR_Success;
  6186. PWSTR FullComputerName = NULL;
  6187. HKEY NetlogonRootKey, DcKey;
  6188. ULONG Length, Type;
  6189. DWORD Value;
  6190. *RefusePasswordChangeSet = FALSE;
  6191. //
  6192. // Build the full computer name if necessary
  6193. //
  6194. if ( *Nt4Dc != L'\\' )
  6195. {
  6196. NetStatus = NetApiBufferAllocate( ( wcslen( Nt4Dc ) + 3 ) * sizeof( WCHAR ),
  6197. ( LPVOID * )&FullComputerName );
  6198. if ( NetStatus == NERR_Success )
  6199. {
  6200. swprintf( FullComputerName, L"\\\\%ws", Nt4Dc );
  6201. }
  6202. }
  6203. else
  6204. {
  6205. FullComputerName = (LPWSTR) Nt4Dc;
  6206. }
  6207. NetpLog(( "NetpGetNt4RefusePasswordChangeStatus: trying to read from '%ws'\n", FullComputerName));
  6208. //
  6209. // Connect to the remote registry
  6210. //
  6211. if ( NetStatus == NERR_Success )
  6212. {
  6213. NetStatus = RegConnectRegistry( FullComputerName,
  6214. HKEY_LOCAL_MACHINE,
  6215. &DcKey );
  6216. //
  6217. // Now, open the netlogon parameters section
  6218. //
  6219. if ( NetStatus == NERR_Success )
  6220. {
  6221. NetStatus = RegOpenKeyEx( DcKey,
  6222. NETP_NETLOGON_PATH,
  6223. 0,
  6224. KEY_READ,
  6225. &NetlogonRootKey);
  6226. //
  6227. // Now, see if the key actually exists...
  6228. //
  6229. if ( NetStatus == NERR_Success )
  6230. {
  6231. Length = sizeof( Value );
  6232. NetStatus = RegQueryValueEx( NetlogonRootKey,
  6233. NETP_NETLOGON_RPC,
  6234. NULL,
  6235. &Type,
  6236. ( LPBYTE )&Value,
  6237. &Length );
  6238. if ( NetStatus == NERR_Success)
  6239. {
  6240. NetpLog(( "NetpGetNt4RefusePasswordChangeStatus: RefusePasswordChange == %d\n", Value));
  6241. if ( Value != 0 )
  6242. {
  6243. *RefusePasswordChangeSet = TRUE;
  6244. }
  6245. }
  6246. RegCloseKey( NetlogonRootKey );
  6247. }
  6248. RegCloseKey( DcKey );
  6249. }
  6250. }
  6251. if ( FullComputerName != Nt4Dc )
  6252. {
  6253. NetApiBufferFree( FullComputerName );
  6254. }
  6255. //
  6256. // If anything went wrong, ignore it...
  6257. //
  6258. if ( NetStatus != NERR_Success )
  6259. {
  6260. NetpLog(( "NetpGetNt4RefusePasswordChangeStatus: failed but ignored the failure: 0x%lx\n", NetStatus ));
  6261. NetStatus = NERR_Success;
  6262. }
  6263. return( NetStatus );
  6264. }
  6265. NET_API_STATUS
  6266. NET_API_FUNCTION
  6267. NetpGetComputerNameAllocIfReqd(
  6268. OUT LPWSTR* ppwszMachine,
  6269. IN UINT cLen
  6270. )
  6271. /*++
  6272. Routine Description:
  6273. Get name of the computer on which this runs. Alloc a buffer
  6274. if the name is longer than cLen.
  6275. Arguments:
  6276. ppwszMachine -- pointer to buffer. this receives a buffer if allocated.
  6277. cLen -- length of buffer pointed to by *ppwszMachine.
  6278. If the computer name to be returned is longer than this
  6279. a new buffer is allocated.
  6280. Returns:
  6281. NERR_Success -- Success
  6282. --*/
  6283. {
  6284. NET_API_STATUS NetStatus=NERR_Success;
  6285. if ( GetComputerName( *ppwszMachine, &cLen ) == FALSE )
  6286. {
  6287. NetStatus = GetLastError();
  6288. if ( (NetStatus == ERROR_INSUFFICIENT_BUFFER) ||
  6289. (NetStatus == ERROR_BUFFER_OVERFLOW) )
  6290. {
  6291. // allocate an extra char for the append-$ case
  6292. NetStatus = NetApiBufferAllocate( (cLen + 1 + 1) * sizeof(WCHAR),
  6293. (PBYTE *) ppwszMachine );
  6294. if ( NetStatus == NERR_Success )
  6295. {
  6296. if ( GetComputerName( *ppwszMachine, &cLen ) == FALSE )
  6297. {
  6298. NetStatus = GetLastError();
  6299. }
  6300. }
  6301. }
  6302. }
  6303. return NetStatus;
  6304. }
  6305. // ======================================================================
  6306. //
  6307. // Note: all code below this has been added as helper code for
  6308. // NetpSetComputerAccountPassword. this function is used by
  6309. // netdom.exe to fix a dc that was rendered unusable because
  6310. // of a ds restore resulting into 2+ password mismatch on
  6311. // machine account.
  6312. //
  6313. // This entire code is temporary and should be removed and
  6314. // rewritten post w2k.
  6315. //
  6316. static
  6317. NET_API_STATUS
  6318. NET_API_FUNCTION
  6319. NetpEncodePassword(
  6320. IN LPWSTR lpPassword,
  6321. IN OUT PUCHAR Seed,
  6322. OUT LPWSTR *EncodedPassword,
  6323. OUT PULONG EncodedPasswordLength
  6324. )
  6325. {
  6326. NET_API_STATUS status = NERR_Success;
  6327. UNICODE_STRING EncodedPasswordU;
  6328. PWSTR PasswordPart;
  6329. ULONG PwdLen;
  6330. *EncodedPassword = NULL;
  6331. *EncodedPasswordLength = 0;
  6332. if ( lpPassword ) {
  6333. PwdLen = wcslen( ( LPWSTR )lpPassword ) * sizeof( WCHAR );
  6334. PwdLen += sizeof( WCHAR ) + sizeof( WCHAR );
  6335. status = NetApiBufferAllocate( PwdLen,
  6336. ( PVOID * )EncodedPassword );
  6337. if ( status == NERR_Success ) {
  6338. //
  6339. // We'll put the encode byte as the first character in the string
  6340. //
  6341. PasswordPart = ( *EncodedPassword ) + 1;
  6342. wcscpy( PasswordPart, ( LPWSTR )lpPassword );
  6343. RtlInitUnicodeString( &EncodedPasswordU, PasswordPart );
  6344. *Seed = 0;
  6345. RtlRunEncodeUnicodeString( Seed, &EncodedPasswordU );
  6346. *( PWCHAR )( *EncodedPassword ) = ( WCHAR )*Seed;
  6347. //
  6348. // Encode the old password as well...
  6349. //
  6350. RtlInitUnicodeString( &EncodedPasswordU, lpPassword );
  6351. RtlRunEncodeUnicodeString( Seed, &EncodedPasswordU );
  6352. *EncodedPasswordLength = PwdLen;
  6353. }
  6354. }
  6355. return( status );
  6356. }
  6357. NTSTATUS
  6358. NetpLsaOpenSecret2(
  6359. IN LSA_HANDLE hLsa,
  6360. IN PUNICODE_STRING pusSecretName,
  6361. IN ACCESS_MASK DesiredAccess,
  6362. OUT PLSA_HANDLE phSecret
  6363. )
  6364. {
  6365. NTSTATUS Status = STATUS_UNSUCCESSFUL;
  6366. HANDLE hToken=NULL;
  6367. __try
  6368. {
  6369. if (OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE,
  6370. TRUE, &hToken))
  6371. {
  6372. if (SetThreadToken(NULL, NULL))
  6373. {
  6374. Status = STATUS_SUCCESS;
  6375. }
  6376. }
  6377. else
  6378. {
  6379. if (GetLastError() == ERROR_NO_TOKEN)
  6380. {
  6381. Status = STATUS_SUCCESS;
  6382. }
  6383. }
  6384. if ( NT_SUCCESS(Status) )
  6385. {
  6386. Status = LsaOpenSecret(hLsa, pusSecretName,
  6387. DesiredAccess, phSecret);
  6388. }
  6389. }
  6390. __finally
  6391. {
  6392. if (hToken)
  6393. {
  6394. SetThreadToken(NULL, hToken);
  6395. }
  6396. }
  6397. NetpLog(( "NetpLsaOpenSecret: status: 0x%lx\n", Status ));
  6398. return Status;
  6399. }
  6400. NET_API_STATUS
  6401. NET_API_FUNCTION
  6402. NetpManageMachineSecret2(
  6403. IN LSA_HANDLE PolicyHandle, OPTIONAL
  6404. IN LPWSTR lpMachine,
  6405. IN LPWSTR lpPassword,
  6406. IN BOOL fDelete,
  6407. OUT PLSA_HANDLE pPolicyHandle OPTIONAL
  6408. )
  6409. {
  6410. NTSTATUS Status = STATUS_SUCCESS;
  6411. LSA_HANDLE LocalPolicy = NULL, SecretHandle = NULL;
  6412. UNICODE_STRING Key, Data, *CurrentValue = NULL;
  6413. BOOLEAN SecretCreated = FALSE;
  6414. if( fDelete == FALSE )
  6415. {
  6416. ASSERT( lpPassword );
  6417. }
  6418. UNREFERENCED_PARAMETER( lpMachine );
  6419. Status = NetpGetLsaHandle( NULL, PolicyHandle, &LocalPolicy );
  6420. //
  6421. // open/create the secret
  6422. //
  6423. if ( NT_SUCCESS( Status ) )
  6424. {
  6425. RtlInitUnicodeString( &Key, L"$MACHINE.ACC" );
  6426. RtlInitUnicodeString( &Data, lpPassword );
  6427. Status = NetpLsaOpenSecret2( LocalPolicy, &Key,
  6428. fDelete == NETSETUPP_CREATE ?
  6429. SECRET_SET_VALUE | SECRET_QUERY_VALUE : DELETE,
  6430. &SecretHandle );
  6431. if ( Status == STATUS_OBJECT_NAME_NOT_FOUND )
  6432. {
  6433. if ( fDelete )
  6434. {
  6435. Status = STATUS_SUCCESS;
  6436. }
  6437. else
  6438. {
  6439. Status = LsaCreateSecret( LocalPolicy, &Key,
  6440. SECRET_SET_VALUE, &SecretHandle );
  6441. if ( NT_SUCCESS( Status ) )
  6442. {
  6443. SecretCreated = TRUE;
  6444. }
  6445. }
  6446. }
  6447. if ( !NT_SUCCESS( Status ) )
  6448. {
  6449. NetpLog(( "NetpManageMachineSecret: Open/Create secret failed: 0x%lx\n", Status ));
  6450. }
  6451. if ( NT_SUCCESS( Status ) )
  6452. {
  6453. if ( fDelete == NETSETUPP_CREATE )
  6454. {
  6455. //
  6456. // First, read the current value, so we can save it as the old value
  6457. //
  6458. if ( SecretCreated )
  6459. {
  6460. CurrentValue = &Data;
  6461. }
  6462. else
  6463. {
  6464. Status = LsaQuerySecret( SecretHandle, &CurrentValue,
  6465. NULL, NULL, NULL );
  6466. }
  6467. if ( NT_SUCCESS( Status ) )
  6468. {
  6469. //
  6470. // Now, store both the new password and the old
  6471. //
  6472. Status = LsaSetSecret( SecretHandle, &Data, CurrentValue );
  6473. if ( !SecretCreated )
  6474. {
  6475. LsaFreeMemory( CurrentValue );
  6476. }
  6477. }
  6478. }
  6479. else
  6480. {
  6481. //
  6482. // No secret handle means we failed earlier in
  6483. // some intermediate state. That's ok, just press on.
  6484. //
  6485. if ( SecretHandle != NULL )
  6486. {
  6487. Status = LsaDelete( SecretHandle );
  6488. if ( NT_SUCCESS( Status ) )
  6489. {
  6490. SecretHandle = NULL;
  6491. }
  6492. }
  6493. }
  6494. }
  6495. if ( SecretHandle )
  6496. {
  6497. LsaClose( SecretHandle );
  6498. }
  6499. }
  6500. NetpSetLsaHandle( PolicyHandle, LocalPolicy, pPolicyHandle );
  6501. if ( !NT_SUCCESS( Status ) )
  6502. {
  6503. NetpLog(( "NetpManageMachineSecret: '%s' operation failed: 0x%lx\n",
  6504. fDelete == NETSETUPP_CREATE ? "CREATE" : "DELETE", Status ));
  6505. }
  6506. return( RtlNtStatusToDosError( Status ) );
  6507. }
  6508. NET_API_STATUS
  6509. NET_API_FUNCTION
  6510. NetpSetMachineAccountPasswordAndTypeEx2(
  6511. IN LPWSTR lpDcName,
  6512. IN PSID DomainSid,
  6513. IN LPWSTR lpAccountName,
  6514. IN OUT OPTIONAL LPWSTR lpPassword,
  6515. IN OPTIONAL UCHAR AccountState
  6516. )
  6517. {
  6518. NET_API_STATUS NetStatus=NERR_Success;
  6519. NTSTATUS Status = STATUS_SUCCESS;
  6520. UNICODE_STRING DcName, AccountName;
  6521. OBJECT_ATTRIBUTES ObjectAttributes;
  6522. SAM_HANDLE SamHandle = NULL, DomainHandle = NULL, AccountHandle = NULL;
  6523. ULONG UserRid;
  6524. PULONG RidList = NULL;
  6525. PSID_NAME_USE NameUseList = NULL;
  6526. PUSER_CONTROL_INFORMATION UserAccountControl = NULL;
  6527. USER_SET_PASSWORD_INFORMATION PasswordInfo;
  6528. ULONG OldUserInfo;
  6529. BOOL fAccountControlModified = FALSE;
  6530. LPWSTR lpSamAccountName=lpAccountName;
  6531. ULONG AccountNameLen=0;
  6532. AccountNameLen = wcslen( lpAccountName );
  6533. //
  6534. // if caller has not passed in sam-account name,
  6535. // generate it from machine name ==> append $ at the end
  6536. //
  6537. if (lpAccountName[AccountNameLen-1] != L'$')
  6538. {
  6539. NetStatus = NetpGetMachineAccountName(lpAccountName,
  6540. &lpSamAccountName);
  6541. if (NetStatus != NERR_Success)
  6542. {
  6543. Status = STATUS_INSUFFICIENT_RESOURCES;
  6544. goto SetPasswordError;
  6545. }
  6546. }
  6547. RtlInitUnicodeString( &DcName, lpDcName );
  6548. RtlZeroMemory( &ObjectAttributes, sizeof( OBJECT_ATTRIBUTES ) );
  6549. Status = SamConnect( &DcName,
  6550. &SamHandle,
  6551. SAM_SERVER_CONNECT | SAM_SERVER_LOOKUP_DOMAIN,
  6552. &ObjectAttributes );
  6553. if ( !NT_SUCCESS( Status ) ) {
  6554. NetpLog(( "SamConnect to %wZ failed with 0x%lx\n", &DcName, Status ));
  6555. goto SetPasswordError;
  6556. }
  6557. //
  6558. // Open the domain
  6559. //
  6560. Status = SamOpenDomain( SamHandle,
  6561. DOMAIN_LOOKUP,
  6562. DomainSid,
  6563. &DomainHandle );
  6564. if ( !NT_SUCCESS( Status ) ) {
  6565. #ifdef NETSETUP_VERBOSE_LOGGING
  6566. UNICODE_STRING DisplaySid;
  6567. NTSTATUS Status2;
  6568. RtlZeroMemory( &DisplaySid, sizeof( UNICODE_STRING ) );
  6569. Status2 = RtlConvertSidToUnicodeString( &DisplaySid, DomainSid, TRUE );
  6570. if ( NT_SUCCESS( Status2 ) ) {
  6571. NetpLog(( "SamOpenDomain on %wZ failed with 0x%lx\n",
  6572. &DisplaySid, Status ));
  6573. RtlFreeUnicodeString(&DisplaySid);
  6574. } else {
  6575. NetpLog(( "SamOpenDomain on <undisplayable sid> failed with 0x%lx\n",
  6576. Status ));
  6577. }
  6578. #endif
  6579. goto SetPasswordError;
  6580. }
  6581. //
  6582. // Get the RID of the user account
  6583. //
  6584. RtlInitUnicodeString( &AccountName, lpSamAccountName );
  6585. Status = SamLookupNamesInDomain( DomainHandle,
  6586. 1,
  6587. &AccountName,
  6588. &RidList,
  6589. &NameUseList );
  6590. if ( !NT_SUCCESS( Status ) ) {
  6591. NetpLog(( "SamLookupNamesInDomain on %wZ failed with 0x%lx\n",
  6592. &AccountName, Status ));
  6593. goto SetPasswordError;
  6594. }
  6595. UserRid = RidList[ 0 ];
  6596. SamFreeMemory( RidList );
  6597. SamFreeMemory( NameUseList );
  6598. //
  6599. // Finally, open the user account
  6600. //
  6601. Status = SamOpenUser( DomainHandle,
  6602. USER_FORCE_PASSWORD_CHANGE | USER_READ_ACCOUNT | USER_WRITE_ACCOUNT,
  6603. UserRid,
  6604. &AccountHandle );
  6605. if ( !NT_SUCCESS( Status ) ) {
  6606. Status = SamOpenUser( DomainHandle,
  6607. USER_FORCE_PASSWORD_CHANGE | USER_READ_ACCOUNT,
  6608. UserRid,
  6609. &AccountHandle );
  6610. if ( !NT_SUCCESS( Status ) ) {
  6611. NetpLog(( "SamOpenUser on %lu failed with 0x%lx\n",
  6612. UserRid,
  6613. Status ));
  6614. goto SetPasswordError;
  6615. }
  6616. }
  6617. //
  6618. // Now, read the current user account type and see if it needs to be modified
  6619. //
  6620. Status = SamQueryInformationUser( AccountHandle,
  6621. UserControlInformation,
  6622. ( PVOID * )&UserAccountControl );
  6623. if ( !NT_SUCCESS( Status ) ) {
  6624. NetpLog(( "SamQueryInformationUser for UserControlInformation "
  6625. "failed with 0x%lx\n", Status ));
  6626. goto SetPasswordError;
  6627. }
  6628. OldUserInfo = UserAccountControl->UserAccountControl;
  6629. //
  6630. // Determine if the account control changes. If the account is being enabled,
  6631. // we want to perform the following sequence of operations: enable, disable,
  6632. // and enable again. This is needed to increase the USN (Universal Sequence
  6633. // Number) of this attribute so that the enabled value will win if the DS
  6634. // replication resolves colliding changes, as the following example shows.
  6635. // Suppose we have two DCs in the domain we join, A abd B. Suppose the account
  6636. // is currently disabled on A (because the user unjoined using that DC),
  6637. // but it is still enabled on B (because the replication hasn't happened yet).
  6638. // Suppose the user performs now joining to the domain. Then we have discovered
  6639. // B and so we proceed with setting up the changes to the existing account. If
  6640. // we don't toggle the account control attribute, then the USN of this attribute
  6641. // will not change on B (since attribute's value doesn't change) while it was
  6642. // incremented on A as the result of unjoin. At the replication time the data
  6643. // from A will rule and the account will be incorrectly marked as diabled.
  6644. //
  6645. // NOTE: This design may fail for the case of unjoining a domain that has
  6646. // three (or more) DCs, A, B, and C if the following sequence of operations
  6647. // happens. Suppose that the account is originally enabled on all DCs (state [1]
  6648. // in the bellow diagram). Then the user unjoins using DC A (state [2]). Then the
  6649. // user joins using B where the account is still enabled (state [3]). Then the user
  6650. // unjoins using C where the account is still enabled (state [4]). The final
  6651. // operation is unjoin, so the user expects that his account is disabled. We've
  6652. // assumed here that for some reason no replication was happening when these
  6653. // operations were performed. Then at the replication time the value from B will
  6654. // win (because of the additional toggling performed at the join time). But the
  6655. // account state on B is Enabled, so the final result will be that the account is
  6656. // enabled on all DCs which is not what the user expects.
  6657. //
  6658. // A B C
  6659. // Enabled [1] Enabled [1] Enabled [1]
  6660. // Disabled [2] Enabled (no-op)+Disabled (1 op) Disabled [4]
  6661. // Enabled [3]
  6662. //
  6663. if ( AccountState != ACCOUNT_STATE_IGNORE ) {
  6664. if ( AccountState == ACCOUNT_STATE_ENABLED ) {
  6665. fAccountControlModified = TRUE;
  6666. UserAccountControl->UserAccountControl &= ~USER_ACCOUNT_DISABLED;
  6667. }
  6668. if ( ( AccountState == ACCOUNT_STATE_DISABLED ) &&
  6669. !( OldUserInfo & USER_ACCOUNT_DISABLED ) ) {
  6670. fAccountControlModified = TRUE;
  6671. UserAccountControl->UserAccountControl |= USER_ACCOUNT_DISABLED;
  6672. }
  6673. }
  6674. if ( fAccountControlModified == FALSE ) {
  6675. SamFreeMemory( UserAccountControl );
  6676. UserAccountControl = NULL;
  6677. }
  6678. //
  6679. // First, set the account type if required
  6680. //
  6681. if ( UserAccountControl ) {
  6682. Status = SamSetInformationUser( AccountHandle,
  6683. UserControlInformation,
  6684. ( PVOID )UserAccountControl );
  6685. if ( !NT_SUCCESS( Status ) ) {
  6686. NetpLog(( "SamSetInformationUser for UserControlInformation "
  6687. "failed with 0x%lx\n", Status ));
  6688. goto SetPasswordError;
  6689. //
  6690. // If we are enabling the account, disable and re-enable it to
  6691. // make the two additional account state toggles.
  6692. //
  6693. } else if ( AccountState == ACCOUNT_STATE_ENABLED ) {
  6694. UserAccountControl->UserAccountControl |= USER_ACCOUNT_DISABLED;
  6695. Status = SamSetInformationUser( AccountHandle,
  6696. UserControlInformation,
  6697. ( PVOID )UserAccountControl );
  6698. if ( !NT_SUCCESS(Status) ) {
  6699. NetpLog(( "SamSetInformationUser (second) for UserControlInformation "
  6700. "failed with 0x%lx\n", Status ));
  6701. goto SetPasswordError;
  6702. }
  6703. UserAccountControl->UserAccountControl &= ~USER_ACCOUNT_DISABLED;
  6704. Status = SamSetInformationUser( AccountHandle,
  6705. UserControlInformation,
  6706. ( PVOID )UserAccountControl );
  6707. if ( !NT_SUCCESS(Status) ) {
  6708. NetpLog(( "SamSetInformationUser (third) for UserControlInformation "
  6709. "failed with 0x%lx\n", Status ));
  6710. goto SetPasswordError;
  6711. }
  6712. }
  6713. }
  6714. //
  6715. // If requested, set the password on the account
  6716. //
  6717. if ( lpPassword != NULL )
  6718. {
  6719. RtlInitUnicodeString( &PasswordInfo.Password, lpPassword );
  6720. PasswordInfo.PasswordExpired = FALSE;
  6721. //
  6722. // Ok, then, set the password on the account
  6723. //
  6724. // The caller has passed in a strong password, try that first
  6725. // NT5 dcs will always accept a strong password.
  6726. //
  6727. Status = SamSetInformationUser( AccountHandle,
  6728. UserSetPasswordInformation,
  6729. ( PVOID )&PasswordInfo );
  6730. if ( !NT_SUCCESS( Status ) )
  6731. {
  6732. if ( (Status == STATUS_PASSWORD_RESTRICTION) &&
  6733. !NetpIsDefaultPassword( lpAccountName, lpPassword ))
  6734. {
  6735. NetpLog(( "NetpSetMachineAccountPasswordAndTypeEx: STATUS_PASSWORD_RESTRICTION error setting password. retrying...\n" ));
  6736. //
  6737. // SAM did not accpet a long password, try LM20_PWLEN
  6738. //
  6739. // This is probably because the dc is NT4 dc.
  6740. // NT4 dcs will not accept a password longer than LM20_PWLEN
  6741. //
  6742. lpPassword[LM20_PWLEN] = UNICODE_NULL;
  6743. RtlInitUnicodeString( &PasswordInfo.Password, lpPassword );
  6744. Status = SamSetInformationUser( AccountHandle,
  6745. UserSetPasswordInformation,
  6746. ( PVOID )&PasswordInfo );
  6747. if ( Status == STATUS_PASSWORD_RESTRICTION )
  6748. {
  6749. NetpLog(( "NetpSetMachineAccountPasswordAndTypeEx: STATUS_PASSWORD_RESTRICTION error setting password. retrying...\n" ));
  6750. //
  6751. // SAM did not accpet a LM20_PWLEN password, try shorter one
  6752. //
  6753. // SAM uses RtlUpcaseUnicodeStringToOemString internally.
  6754. // In this process it is possible that in the worst case,
  6755. // n unicode char password will get mapped to 2*n dbcs
  6756. // char password. This will make it exceed LM20_PWLEN.
  6757. // To guard against this worst case, try a password
  6758. // with LM20_PWLEN/2 length
  6759. //
  6760. // One might say that LM20_PWLEN/2 length password
  6761. // is not really secure. I agree, but it is definitely
  6762. // better than the default password which we will have
  6763. // to fall back to otherwise.
  6764. //
  6765. lpPassword[LM20_PWLEN/2] = UNICODE_NULL;
  6766. RtlInitUnicodeString( &PasswordInfo.Password, lpPassword );
  6767. Status = SamSetInformationUser( AccountHandle,
  6768. UserSetPasswordInformation,
  6769. ( PVOID )&PasswordInfo );
  6770. if ( Status == STATUS_PASSWORD_RESTRICTION )
  6771. {
  6772. //
  6773. // SAM did not accpet a short pwd, try default pwd
  6774. //
  6775. NetpLog(( "NetpSetMachineAccountPasswordAndTypeEx: STATUS_PASSWORD_RESTRICTION error setting password. retrying...\n" ));
  6776. NetpGenerateDefaultPassword(lpAccountName, lpPassword);
  6777. RtlInitUnicodeString( &PasswordInfo.Password, lpPassword );
  6778. Status = SamSetInformationUser( AccountHandle,
  6779. UserSetPasswordInformation,
  6780. ( PVOID )&PasswordInfo );
  6781. }
  6782. }
  6783. }
  6784. if ( NT_SUCCESS( Status ) )
  6785. {
  6786. NetpLog(( "NetpGenerateDefaultPassword: successfully set password\n" ));
  6787. }
  6788. else
  6789. {
  6790. NetpLog(( "NetpSetMachineAccountPasswordAndTypeEx: SamSetInformationUser for UserSetPasswordInformation failed: 0x%lx\n", Status ));
  6791. //
  6792. // Make sure we try to restore the account control
  6793. //
  6794. if ( UserAccountControl )
  6795. {
  6796. NTSTATUS Status2;
  6797. UserAccountControl->UserAccountControl = OldUserInfo;
  6798. Status2 = SamSetInformationUser( AccountHandle,
  6799. UserControlInformation,
  6800. ( PVOID )UserAccountControl );
  6801. if ( !NT_SUCCESS( Status2 ) )
  6802. {
  6803. NetpLog(( "SamSetInformationUser for UserControlInformation (RESTORE) failed with 0x%lx\n", Status2 ));
  6804. }
  6805. }
  6806. goto SetPasswordError;
  6807. }
  6808. }
  6809. }
  6810. SetPasswordError:
  6811. if ( lpSamAccountName != lpAccountName )
  6812. {
  6813. NetApiBufferFree( lpSamAccountName );
  6814. }
  6815. if ( AccountHandle ) {
  6816. SamCloseHandle( AccountHandle );
  6817. }
  6818. if ( DomainHandle ) {
  6819. SamCloseHandle( DomainHandle );
  6820. }
  6821. if ( SamHandle ) {
  6822. SamCloseHandle( SamHandle );
  6823. }
  6824. NetStatus = RtlNtStatusToDosError( Status );
  6825. SamFreeMemory( UserAccountControl );
  6826. return( NetStatus );
  6827. }
  6828. NET_API_STATUS
  6829. NET_API_FUNCTION
  6830. NetpSetComputerAccountPassword(
  6831. IN PWSTR szMachine,
  6832. IN PWSTR szDomainController,
  6833. IN PWSTR szUser,
  6834. IN PWSTR szUserPassword,
  6835. IN PVOID Reserved
  6836. )
  6837. {
  6838. NET_API_STATUS NetStatus=NERR_Success;
  6839. NET_API_STATUS NetStatus2=NERR_Success;
  6840. BOOL fIpcConnected = FALSE;
  6841. BYTE bSeed;
  6842. PPOLICY_PRIMARY_DOMAIN_INFO pPolicyPDI = NULL;
  6843. PPOLICY_DNS_DOMAIN_INFO pPolicyDns = NULL;
  6844. LSA_HANDLE hLsa = NULL, hDC = NULL;
  6845. WCHAR szMachinePassword[ PWLEN + 1];
  6846. WCHAR szMachineNameBuf[MAX_COMPUTERNAME_LENGTH + 1];
  6847. PWSTR szMachineName=szMachineNameBuf;
  6848. NetSetuppOpenLog();
  6849. NetpLog(( "NetpSetComputerAccountPassword: for '%ws' on '%ws' using '%ws' creds\n", GetStrPtr(szMachine), GetStrPtr(szDomainController), GetStrPtr(szUser) ));
  6850. if ( ( szDomainController == NULL ) ||
  6851. ( szUser == NULL ) ||
  6852. ( szUserPassword == NULL ) )
  6853. {
  6854. NetStatus = ERROR_INVALID_PARAMETER;
  6855. goto Cleanup;
  6856. }
  6857. if ( szMachine == NULL )
  6858. {
  6859. NetStatus = NetpGetComputerNameAllocIfReqd(&szMachineName,
  6860. MAX_COMPUTERNAME_LENGTH);
  6861. }
  6862. else
  6863. {
  6864. szMachineName = szMachine;
  6865. }
  6866. NetStatus = NetpManageIPCConnect( szDomainController,
  6867. szUser, szUserPassword,
  6868. NETSETUPP_CONNECT_IPC );
  6869. RtlZeroMemory( szUserPassword, wcslen( szUserPassword ) * sizeof(WCHAR) );
  6870. NetpLog(( "NetpSetComputerAccountPassword: status of connecting to dc '%ws': 0x%lx\n", szDomainController, NetStatus ));
  6871. //
  6872. // get the lsa domain info on the DC
  6873. //
  6874. if ( NetStatus == NERR_Success )
  6875. {
  6876. fIpcConnected = TRUE;
  6877. NetStatus = NetpGetLsaPrimaryDomain(NULL, szDomainController,
  6878. &pPolicyPDI, &pPolicyDns, &hDC);
  6879. }
  6880. if (NetStatus == NERR_Success)
  6881. {
  6882. //
  6883. // Generate the password to use on the machine account.
  6884. //
  6885. NetStatus = NetpGeneratePassword( szMachineName,
  6886. TRUE, // fRandomPwdPreferred
  6887. szDomainController,
  6888. FALSE, // fIsNt4Dc
  6889. szMachinePassword );
  6890. NetpLog(( "NetpSetComputerAccountPassword: status of generating machine password: 0x%lx\n", NetStatus ));
  6891. }
  6892. if (NetStatus == NERR_Success)
  6893. {
  6894. NetStatus = NetpSetMachineAccountPasswordAndTypeEx2(
  6895. szDomainController, pPolicyPDI->Sid,
  6896. szMachineName, szMachinePassword,
  6897. ACCOUNT_STATE_IGNORE
  6898. );
  6899. NetpLog(( "NetpSetComputerAccountPassword: status of setting machine password on %ws: 0x%lx\n", GetStrPtr(szDomainController), NetStatus ));
  6900. }
  6901. if (NetStatus == NERR_Success)
  6902. {
  6903. // if we are not creating the machine account,
  6904. // just set the password
  6905. NetStatus = NetpSetMachineAccountPasswordAndTypeEx2(
  6906. szMachineName, pPolicyPDI->Sid,
  6907. szMachineName, szMachinePassword,
  6908. ACCOUNT_STATE_IGNORE
  6909. );
  6910. NetpLog(( "NetpSetComputerAccountPassword: status of setting machine password on %ws: 0x%lx\n", GetStrPtr(szMachineName), NetStatus ));
  6911. }
  6912. //
  6913. // set the local machine secret
  6914. //
  6915. if ( NetStatus == NERR_Success )
  6916. {
  6917. NetStatus = NetpGetLsaHandle( NULL, NULL, &hLsa );
  6918. if ( NetStatus == NERR_Success )
  6919. {
  6920. NetStatus = NetpManageMachineSecret2( NULL, szMachineName,
  6921. szMachinePassword,
  6922. NETSETUPP_CREATE, &hLsa );
  6923. LsaClose( hLsa );
  6924. }
  6925. NetpLog(( "NetpSetComputerAccountPassword: status of setting local secret: 0x%lx\n", NetStatus ));
  6926. }
  6927. //
  6928. // Now, we no longer need our session to our dc
  6929. //
  6930. if ( fIpcConnected )
  6931. {
  6932. //RtlRunDecodeUnicodeString( bSeed, &usEncodedPassword );
  6933. NetStatus2 = NetpManageIPCConnect( szDomainController, szUser,
  6934. //usEncodedPassword.Buffer,
  6935. NULL,
  6936. NETSETUPP_DISCONNECT_IPC );
  6937. //RtlRunEncodeUnicodeString( &bSeed, &usEncodedPassword );
  6938. NetpLog(( "NetpJoinDomain: status of disconnecting from '%ws': 0x%lx\n", szDomainController, NetStatus2));
  6939. }
  6940. Cleanup:
  6941. if ( (szMachineName != szMachine) &&
  6942. (szMachineName != szMachineNameBuf) )
  6943. {
  6944. NetApiBufferFree( szMachineName );
  6945. }
  6946. NetpLog(( "NetpSetComputerAccountPassword: status: 0x%lx\n", NetStatus ));
  6947. NetSetuppCloseLog();
  6948. return NetStatus;
  6949. }
  6950. NET_API_STATUS
  6951. NET_API_FUNCTION
  6952. NetpUpdateW32timeConfig(
  6953. IN PCSTR szW32timeJoinConfigFuncName
  6954. )
  6955. /*++
  6956. Routine Description:
  6957. Call entry point in w32time service so that it can update
  6958. its internal info after a domain join/unjoin
  6959. Arguments:
  6960. szW32timeJoinConfigFuncName - name of entry point to call
  6961. (must be W32TimeVerifyJoinConfig or W32TimeVerifyUnjoinConfig)
  6962. Return Value:
  6963. NERR_Success -- on Success
  6964. otherwise win32 error codes returned by LoadLibrary, GetProcAddress
  6965. Notes:
  6966. --*/
  6967. {
  6968. NET_API_STATUS NetStatus = NERR_Success;
  6969. HANDLE hDll = NULL;
  6970. typedef VOID (*PW32TimeUpdateJoinConfig)( VOID );
  6971. PW32TimeUpdateJoinConfig pfnW32timeUpdateJoinConfig = NULL;
  6972. //
  6973. // Call into the time service to allow it initialize itself properly
  6974. //
  6975. hDll = LoadLibraryA( "w32Time" );
  6976. if ( hDll != NULL )
  6977. {
  6978. pfnW32timeUpdateJoinConfig =
  6979. (PW32TimeUpdateJoinConfig) GetProcAddress(hDll,
  6980. szW32timeJoinConfigFuncName);
  6981. if ( pfnW32timeUpdateJoinConfig != NULL )
  6982. {
  6983. pfnW32timeUpdateJoinConfig();
  6984. }
  6985. else
  6986. {
  6987. NetStatus = GetLastError();
  6988. NetpLog(( "NetpUpdateW32timeConfig: Failed to get proc address for %s: 0x%lx\n", szW32timeJoinConfigFuncName, NetStatus ));
  6989. }
  6990. }
  6991. else
  6992. {
  6993. NetStatus = GetLastError();
  6994. NetpLog(( "NetpUpdateW32timeConfig: Failed to load w32time: 0x%lx\n", NetStatus ));
  6995. }
  6996. if ( hDll != NULL )
  6997. {
  6998. FreeLibrary( hDll );
  6999. }
  7000. NetpLog(( "NetpUpdateW32timeConfig: 0x%lx\n", NetStatus ));
  7001. return NetStatus;
  7002. }
  7003. NET_API_STATUS
  7004. NET_API_FUNCTION
  7005. NetpUpdateAutoenrolConfig(
  7006. IN BOOL UnjoinDomain
  7007. )
  7008. /*++
  7009. Routine Description:
  7010. Call entry point in pautoenr service so that it can update
  7011. its internal info after a domain join/unjoin
  7012. Arguments:
  7013. UnjoinDomain - If TRUE, we are unjoining from a domain.
  7014. Otherwise, we are roling back from unsuccessful domain
  7015. unjoin.
  7016. Return Value:
  7017. NERR_Success -- on Success
  7018. otherwise win32 error codes returned by LoadLibrary, GetProcAddress
  7019. Notes:
  7020. --*/
  7021. {
  7022. NET_API_STATUS NetStatus = NERR_Success;
  7023. HANDLE hDll = NULL;
  7024. ULONG Flags = 0;
  7025. typedef BOOL (*PCertAutoRemove)( DWORD );
  7026. PCertAutoRemove pfnCertAutoRemove = NULL;
  7027. //
  7028. // Prepare the flags to pass to autoenrol routine
  7029. //
  7030. if ( UnjoinDomain ) {
  7031. Flags = CERT_AUTO_REMOVE_COMMIT;
  7032. } else {
  7033. Flags = CERT_AUTO_REMOVE_ROLL_BACK;
  7034. }
  7035. //
  7036. // Call into the time service to allow it initialize itself properly
  7037. //
  7038. hDll = LoadLibraryA( "pautoenr" );
  7039. if ( hDll != NULL ) {
  7040. pfnCertAutoRemove =
  7041. (PCertAutoRemove) GetProcAddress( hDll, "CertAutoRemove" );
  7042. if ( pfnCertAutoRemove != NULL ) {
  7043. if ( !pfnCertAutoRemove(Flags) ) {
  7044. NetStatus = GetLastError();
  7045. NetpLog(( "NetpUpdateAutoenrolConfig: CertAutoRemove failed 0x%lx\n",
  7046. NetStatus ));
  7047. }
  7048. } else {
  7049. NetStatus = GetLastError();
  7050. NetpLog(( "NetpUpdateAutoenrolConfig: Failed to get CertAutoRemove proc address 0x%lx\n",
  7051. NetStatus ));
  7052. }
  7053. } else {
  7054. NetStatus = GetLastError();
  7055. NetpLog(( "NetpUpdateAutoenrolConfig: Failed to load w32time: 0x%lx\n", NetStatus ));
  7056. }
  7057. if ( hDll != NULL ) {
  7058. FreeLibrary( hDll );
  7059. }
  7060. return NetStatus;
  7061. }