Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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