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.

2376 lines
70 KiB

  1. /*++
  2. Microsoft Windows
  3. Copyright (C) Microsoft Corporation, 1998 - 2001
  4. Module Name:
  5. query.c
  6. Abstract:
  7. Handles the various functions for the QUERY command
  8. --*/
  9. #include "pch.h"
  10. #pragma hdrstop
  11. #include <netdom.h>
  12. #define VERIFY_QUERY_ONLY 0xFFFFFFFF
  13. typedef enum _ND5_ACCOUNT_TYPE {
  14. TypeWorkstation,
  15. TypeServer,
  16. TypeDomainController,
  17. TypePDC,
  18. TypeUnknown
  19. } ND5_ACCOUNT_TYPE;
  20. typedef enum _ND5_ACCOUNT_OPERATION {
  21. OperationDisplay,
  22. OperationVerify,
  23. OperationReset
  24. } ND5_ACCOUNT_OPERATION;
  25. typedef struct _ND5_TRANS_TREE_NODE {
  26. PDS_DOMAIN_TRUSTS DomainInfo;
  27. ULONG ListIndex;
  28. ULONG Children;
  29. struct _ND5_TRANS_TREE_NODE *ChildList;
  30. struct _ND5_TRANS_TREE_NODE *Parent;
  31. } ND5_TRANS_TREE_NODE, *PND5_TRANS_TREE_NODE;
  32. VOID
  33. NetDompFreeBuiltTrustInfo(
  34. IN PTRUSTED_DOMAIN_INFORMATION_EX TDInfoEx,
  35. IN ULONG Count
  36. )
  37. {
  38. ULONG i;
  39. for ( i = 0; i < Count; i++ ) {
  40. NetApiBufferFree( TDInfoEx[ i ].Name.Buffer );
  41. }
  42. NetApiBufferFree( TDInfoEx );
  43. }
  44. VOID
  45. NetDompDumpTrustInfo(
  46. IN PWSTR Domain,
  47. IN PTRUSTED_DOMAIN_INFORMATION_EX TrustInfo
  48. )
  49. /*++
  50. Routine Description:
  51. This function will display the specified trusted domain info
  52. Arguments:
  53. Domain - Domain to be dumped
  54. TrustInfo - Trust info the domain
  55. Return Value:
  56. VOID
  57. --*/
  58. {
  59. ULONG Message, Type;
  60. //
  61. // Display the direction & name
  62. //
  63. Type = TrustInfo->TrustDirection & TRUST_DIRECTION_BIDIRECTIONAL;
  64. switch ( Type ) {
  65. case TRUST_DIRECTION_BIDIRECTIONAL:
  66. Message = MSG_TRUST_BOTH_ARROW;
  67. break;
  68. case TRUST_DIRECTION_INBOUND:
  69. Message = MSG_TRUST_IN_ARROW;
  70. break;
  71. case TRUST_DIRECTION_OUTBOUND:
  72. Message = MSG_TRUST_OUT_ARROW;
  73. break;
  74. }
  75. NetDompDisplayMessage( Message,
  76. TrustInfo->Name.Buffer );
  77. //
  78. // Then, the type
  79. //
  80. switch ( TrustInfo->TrustType ) {
  81. case TRUST_TYPE_DOWNLEVEL:
  82. case TRUST_TYPE_UPLEVEL:
  83. Message = MSG_TRUST_TYPE_WINDOWS;
  84. break;
  85. case TRUST_TYPE_MIT:
  86. Message = MSG_TRUST_TYPE_MIT;
  87. break;
  88. default:
  89. Message = MSG_TRUST_TYPE_OTHER;
  90. break;
  91. }
  92. NetDompDisplayMessage( Message );
  93. printf( "\n" );
  94. }
  95. //+----------------------------------------------------------------------------
  96. //
  97. // Function: GetTrustInfo
  98. //
  99. // Synopsis: Reads the trust info from the local TDO for the named domain.
  100. //
  101. //-----------------------------------------------------------------------------
  102. DWORD
  103. GetTrustInfo(PWSTR pwzDomain,
  104. PND5_TRUST_INFO pLocalInfo,
  105. PND5_TRUST_INFO pTrustInfo,
  106. DWORD * pdwVerifyErr)
  107. {
  108. DWORD Win32Err = ERROR_SUCCESS;
  109. NTSTATUS Status = STATUS_SUCCESS;
  110. PTRUSTED_DOMAIN_INFORMATION_EX pTDIEx = NULL;
  111. UNICODE_STRING usDomainName;
  112. LSA_HANDLE hTrust;
  113. *pdwVerifyErr = ERROR_ACCESS_DENIED;
  114. RtlInitUnicodeString(&usDomainName, pwzDomain);
  115. Status = LsaOpenTrustedDomainByName(pLocalInfo->LsaHandle,
  116. &usDomainName,
  117. TRUSTED_READ,
  118. &hTrust);
  119. if (!NT_SUCCESS(Status))
  120. {
  121. *pdwVerifyErr = LsaNtStatusToWinError(Status);
  122. return *pdwVerifyErr;
  123. }
  124. Status = LsaQueryInfoTrustedDomain(hTrust,
  125. TrustedDomainInformationEx,
  126. (PVOID*)&pTDIEx);
  127. if (!NT_SUCCESS(Status))
  128. {
  129. *pdwVerifyErr = LsaNtStatusToWinError(Status);
  130. return *pdwVerifyErr;
  131. }
  132. pTrustInfo->TrustHandle = hTrust;
  133. pTrustInfo->DomainName = &pTDIEx->Name;
  134. pTrustInfo->FlatName = &pTDIEx->FlatName;
  135. pTrustInfo->Sid = pTDIEx->Sid;
  136. pTrustInfo->BlobToFree = pTDIEx;
  137. if (pTDIEx->TrustType >= TRUST_TYPE_MIT)
  138. {
  139. pTrustInfo->Uplevel = FALSE;
  140. pTrustInfo->Flags = NETDOM_TRUST_TYPE_MIT;
  141. *pdwVerifyErr = ERROR_SUCCESS;
  142. }
  143. else
  144. {
  145. PDOMAIN_CONTROLLER_INFO pDcInfo = NULL;
  146. PPOLICY_DNS_DOMAIN_INFO pPolicyDDI = NULL;
  147. OBJECT_ATTRIBUTES OA;
  148. UNICODE_STRING ServerU, DomainNameU;
  149. // Get a DC name for the domain.
  150. //
  151. Win32Err = DsGetDcName(NULL,
  152. pwzDomain,
  153. NULL,
  154. NULL,
  155. DS_DIRECTORY_SERVICE_PREFERRED,
  156. &pDcInfo );
  157. if (ERROR_SUCCESS != Win32Err)
  158. {
  159. pTrustInfo->Flags = NETDOM_TRUST_FLAG_DOMAIN_NOT_FOUND;
  160. *pdwVerifyErr = Win32Err;
  161. return ERROR_SUCCESS;
  162. }
  163. // Save off the DC name.
  164. //
  165. Win32Err = NetApiBufferAllocate((wcslen(pDcInfo->DomainControllerName) + 1) * sizeof(WCHAR),
  166. (PVOID*)&(pTrustInfo->Server));
  167. if (ERROR_SUCCESS != Win32Err)
  168. {
  169. NetApiBufferFree(pDcInfo);
  170. return Win32Err;
  171. }
  172. wcscpy(pTrustInfo->Server, pDcInfo->DomainControllerName);
  173. NetApiBufferFree(pDcInfo);
  174. Win32Err = NetpManageIPCConnect(pTrustInfo->Server,
  175. L"",
  176. L"",
  177. NETSETUPP_NULL_SESSION_IPC);
  178. if (ERROR_SUCCESS == Win32Err)
  179. {
  180. pTrustInfo->Connected = TRUE;
  181. }
  182. RtlInitUnicodeString(&ServerU, pTrustInfo->Server);
  183. InitializeObjectAttributes( &OA, NULL, 0, NULL, NULL );
  184. Status = LsaOpenPolicy(&ServerU,
  185. &OA,
  186. POLICY_VIEW_LOCAL_INFORMATION | POLICY_LOOKUP_NAMES,
  187. &(pTrustInfo->LsaHandle));
  188. if (NT_SUCCESS(Status))
  189. {
  190. // Find out if this is an uplevel or downlevel domain.
  191. //
  192. Status = LsaQueryInformationPolicy(pTrustInfo->LsaHandle,
  193. PolicyDnsDomainInformation,
  194. (PVOID *)&pPolicyDDI);
  195. if (NT_SUCCESS(Status))
  196. {
  197. LsaFreeMemory(pPolicyDDI);
  198. pTrustInfo->Uplevel = TRUE;
  199. pTrustInfo->fWasDownlevel = FALSE;
  200. }
  201. else
  202. {
  203. if (RPC_NT_PROCNUM_OUT_OF_RANGE == Status)
  204. {
  205. pTrustInfo->Uplevel = pTrustInfo->fWasDownlevel = FALSE;
  206. Status = STATUS_SUCCESS;
  207. }
  208. }
  209. }
  210. if (ERROR_NO_SUCH_DOMAIN == (*pdwVerifyErr = RtlNtStatusToDosError(Status)) ||
  211. RPC_S_SERVER_UNAVAILABLE == *pdwVerifyErr)
  212. {
  213. pTrustInfo->Flags = NETDOM_TRUST_FLAG_DOMAIN_NOT_FOUND;
  214. }
  215. if (*pdwVerifyErr != ERROR_SUCCESS && pTrustInfo->Connected)
  216. {
  217. NetpManageIPCConnect(pTrustInfo->Server,
  218. NULL,
  219. NULL,
  220. NETSETUPP_DISCONNECT_IPC);
  221. pTrustInfo->Connected = FALSE;
  222. }
  223. }
  224. return S_OK;
  225. }
  226. DWORD
  227. NetDompQueryDirectTrust(
  228. IN PWSTR Domain,
  229. IN PND5_TRUST_INFO TrustInfo
  230. )
  231. /*++
  232. Routine Description:
  233. This function will get the trustinfo for the specified domain
  234. Arguments:
  235. Domain - Domain to get the trust info for
  236. TrustInfo - Trust info to be obtained
  237. Return Value:
  238. ERROR_SUCCESS - The function succeeded
  239. --*/
  240. {
  241. DWORD Win32Err = ERROR_SUCCESS;
  242. NTSTATUS Status = STATUS_SUCCESS;
  243. LSA_ENUMERATION_HANDLE EnumerationContext = 0;
  244. PTRUSTED_DOMAIN_INFORMATION_EX TDInfoEx = NULL, TempTDIEx = NULL;
  245. PLSA_TRUST_INFORMATION TDInfo = NULL;
  246. ULONG Count, i, TotalCount = 0, UserCount, j;
  247. BOOL DisplayHeader = TRUE;
  248. LPUSER_INFO_0 UserList = NULL;
  249. ULONG ResumeHandle = 0;
  250. PWSTR Lop, FullServer = NULL;
  251. //
  252. // Handle the uplevel case differently
  253. //
  254. if ( TrustInfo->Uplevel ) {
  255. do {
  256. Status = LsaEnumerateTrustedDomainsEx( TrustInfo->LsaHandle,
  257. &EnumerationContext,
  258. (PVOID*)&TDInfoEx,
  259. 0x1000,
  260. &Count );
  261. if ( NT_SUCCESS( Status ) || Status == STATUS_NO_MORE_ENTRIES ) {
  262. if ( DisplayHeader ) {
  263. NetDompDisplayMessage( MSG_TRUST_DIRECT_HEADER );
  264. DisplayHeader = FALSE;
  265. }
  266. for ( i = 0; i < Count; i++ ) {
  267. NetDompDumpTrustInfo( TrustInfo->DomainName->Buffer,
  268. &TDInfoEx[ i ] );
  269. }
  270. }
  271. LsaFreeMemory( TDInfoEx );
  272. TDInfoEx = NULL;
  273. } while ( Status == STATUS_MORE_ENTRIES );
  274. } else {
  275. //
  276. // We'll have to do this the old fashioned way. That means that we'll enumerate all of
  277. // the trust directly, save them off in a list, and then go through and enumerate all
  278. // of the interdomain trust accounts and merge those into the list.
  279. //
  280. do {
  281. Status = LsaEnumerateTrustedDomains( TrustInfo->LsaHandle,
  282. &EnumerationContext,
  283. (PVOID*)&TDInfo,
  284. 0x1000,
  285. &Count );
  286. if ( NT_SUCCESS( Status ) || Status == STATUS_NO_MORE_ENTRIES ) {
  287. Win32Err = NetApiBufferAllocate( ( Count + TotalCount ) *
  288. sizeof( TRUSTED_DOMAIN_INFORMATION_EX ),
  289. (PVOID*)&TempTDIEx );
  290. if ( Win32Err != ERROR_SUCCESS ) {
  291. Status = STATUS_INSUFFICIENT_RESOURCES;
  292. break;
  293. }
  294. RtlZeroMemory( TempTDIEx, ( Count + TotalCount ) *
  295. sizeof( TRUSTED_DOMAIN_INFORMATION_EX ) );
  296. RtlCopyMemory( TempTDIEx,
  297. TDInfoEx,
  298. TotalCount * sizeof( TRUSTED_DOMAIN_INFORMATION_EX ) );
  299. for ( i = 0; i < Count; i++ ) {
  300. TempTDIEx[ TotalCount + i ].TrustType = TRUST_TYPE_DOWNLEVEL;
  301. TempTDIEx[ TotalCount + i ].TrustDirection = TRUST_DIRECTION_OUTBOUND;
  302. Win32Err = NetApiBufferAllocate( TDInfo[ i ].Name.MaximumLength,
  303. (PVOID*)&( TempTDIEx[ TotalCount + i ].Name.Buffer ) );
  304. if ( Win32Err != ERROR_SUCCESS ) {
  305. Status = STATUS_INSUFFICIENT_RESOURCES;
  306. break;
  307. }
  308. RtlCopyMemory( TempTDIEx[ TotalCount + i ].Name.Buffer,
  309. TDInfo[ i ].Name.Buffer,
  310. TDInfo[ i ].Name.MaximumLength );
  311. TempTDIEx[ TotalCount + i ].Name.Length = TDInfo[ i ].Name.Length;
  312. TempTDIEx[ TotalCount + i ].Name.MaximumLength =
  313. TDInfo[ i ].Name.MaximumLength;
  314. }
  315. if ( NT_SUCCESS( Status ) ) {
  316. NetApiBufferFree( TDInfoEx );
  317. TDInfoEx = TempTDIEx;
  318. TotalCount += Count;
  319. } else {
  320. for ( j = 0; j < i; j++ ) {
  321. NetApiBufferFree( TempTDIEx[ TotalCount + j ].Name.Buffer );
  322. TempTDIEx[ TotalCount + j ].Name.Buffer = NULL;
  323. }
  324. NetApiBufferFree( TempTDIEx );
  325. }
  326. }
  327. } while ( Status == STATUS_MORE_ENTRIES );
  328. //
  329. // Now, let's add in the user accounts
  330. //
  331. if ( NT_SUCCESS( Status ) ) {
  332. if ( TrustInfo->Server && *( TrustInfo->Server ) != L'\\' ) {
  333. Win32Err = NetApiBufferAllocate( ( wcslen( TrustInfo->Server ) + 3 ) * sizeof( WCHAR ),
  334. ( PVOID * )&FullServer );
  335. if ( Win32Err == ERROR_SUCCESS ) {
  336. swprintf( FullServer, L"\\\\%ws", TrustInfo->Server );
  337. }
  338. } else {
  339. FullServer = TrustInfo->Server;
  340. }
  341. if ( Win32Err == ERROR_SUCCESS ) {
  342. do {
  343. Win32Err = NetUserEnum( FullServer,
  344. 0,
  345. FILTER_INTERDOMAIN_TRUST_ACCOUNT,
  346. ( LPBYTE * )&UserList,
  347. MAX_PREFERRED_LENGTH,
  348. &Count,
  349. &UserCount,
  350. &ResumeHandle );
  351. if ( Win32Err == ERROR_SUCCESS || Win32Err == ERROR_MORE_DATA ) {
  352. for ( i = 0; i < Count; i++ ) {
  353. Lop = wcsrchr( UserList[ i ].usri0_name, L'$' );
  354. if ( Lop ) {
  355. *Lop = UNICODE_NULL;
  356. }
  357. for ( j = 0; j < TotalCount; j++ ) {
  358. if ( _wcsicmp( UserList[ i ].usri0_name,
  359. TDInfoEx[ j ].Name.Buffer ) == 0 ) {
  360. TDInfoEx[ j ].TrustDirection |= TRUST_DIRECTION_INBOUND;
  361. break;
  362. }
  363. }
  364. //
  365. // If it wasn't found, add it...
  366. //
  367. if ( j == TotalCount ) {
  368. Win32Err = NetApiBufferAllocate( ( 1 + TotalCount ) *
  369. sizeof( TRUSTED_DOMAIN_INFORMATION_EX ),
  370. (PVOID*)&TempTDIEx );
  371. if ( Win32Err != ERROR_SUCCESS ) {
  372. Status = STATUS_INSUFFICIENT_RESOURCES;
  373. break;
  374. }
  375. RtlZeroMemory( TempTDIEx,
  376. ( 1 + TotalCount ) *
  377. sizeof( TRUSTED_DOMAIN_INFORMATION_EX ) );
  378. RtlCopyMemory( TempTDIEx,
  379. TDInfoEx,
  380. TotalCount * sizeof( TRUSTED_DOMAIN_INFORMATION_EX ) );
  381. TempTDIEx[ TotalCount ].TrustType = TRUST_TYPE_DOWNLEVEL;
  382. TempTDIEx[ TotalCount ].TrustDirection = TRUST_DIRECTION_INBOUND;
  383. Win32Err = NetApiBufferAllocate(
  384. ( wcslen( UserList[ i ].usri0_name ) + 1 ) *
  385. sizeof( WCHAR ) ,
  386. (PVOID*)&( TempTDIEx[ TotalCount ].Name.Buffer ) );
  387. if ( Win32Err != ERROR_SUCCESS ) {
  388. Status = STATUS_INSUFFICIENT_RESOURCES;
  389. break;
  390. }
  391. wcscpy( TempTDIEx[ TotalCount ].Name.Buffer,
  392. UserList[ i ].usri0_name );
  393. RtlInitUnicodeString( &TempTDIEx[ TotalCount ].Name,
  394. TempTDIEx[ TotalCount ].Name.Buffer );
  395. if ( NT_SUCCESS( Status ) ) {
  396. NetApiBufferFree( TDInfoEx );
  397. TDInfoEx = TempTDIEx;
  398. TotalCount++;
  399. } else {
  400. NetApiBufferFree( TempTDIEx );
  401. }
  402. }
  403. if ( Lop ) {
  404. *Lop = L'$';
  405. }
  406. }
  407. NetApiBufferFree( UserList );
  408. }
  409. } while ( Win32Err == ERROR_MORE_DATA );
  410. if( FullServer != TrustInfo->Server )
  411. NetApiBufferFree( FullServer );
  412. }
  413. //
  414. // If everything worked, then dump them all
  415. //
  416. if ( Win32Err == ERROR_SUCCESS ) {
  417. if ( TotalCount > 0 ) {
  418. NetDompDisplayMessage( MSG_TRUST_DIRECT_HEADER );
  419. }
  420. for ( i = 0; i < TotalCount; i++ ) {
  421. NetDompDumpTrustInfo( TrustInfo->DomainName->Buffer,
  422. &TDInfoEx[ i ] );
  423. }
  424. }
  425. NetDompFreeBuiltTrustInfo( TDInfoEx, TotalCount );
  426. } else {
  427. Win32Err = RtlNtStatusToDosError( Status );
  428. }
  429. }
  430. if ( Status == STATUS_NO_MORE_ENTRIES ) {
  431. Status = STATUS_SUCCESS;
  432. }
  433. if ( Win32Err == ERROR_SUCCESS ) {
  434. Win32Err = RtlNtStatusToDosError( Status );
  435. }
  436. return( Win32Err );
  437. }
  438. DWORD
  439. NetDompFindChildrenForNode(
  440. IN PWSTR LocalDomain,
  441. IN ULONG DomainCount,
  442. IN PDS_DOMAIN_TRUSTS DomainList,
  443. IN OUT PND5_TRANS_TREE_NODE TreeNode,
  444. IN OUT PND5_TRANS_TREE_NODE *DomainNode
  445. )
  446. /*++
  447. Routine Description:
  448. This recursive function will find all of the children for a given node in the trust list
  449. Arguments:
  450. LocalDomain - Domain to find the children for
  451. DomainCount - Number of domains in the list
  452. DomainList - List of domains
  453. TreeNode - Tree to insert into
  454. DomainNode - Pointer to the LocalDomain's node, if encountered
  455. Return Value:
  456. ERROR_SUCCESS - The function succeeded
  457. ERROR_INVALID_PARAMETER - No server, workstation or machine was specified
  458. --*/
  459. {
  460. DWORD Win32Err = ERROR_SUCCESS;
  461. ULONG i, Count = 0;
  462. BOOL HandleDirect = FALSE;
  463. //
  464. // See how many
  465. //
  466. for ( i = 0; i < DomainCount; i++ ) {
  467. if ( DomainList[ i ].ParentIndex == TreeNode->ListIndex &&
  468. FLAG_ON( DomainList[ i ].Flags, DS_DOMAIN_IN_FOREST ) &&
  469. !FLAG_ON( DomainList[ i ].Flags, DS_DOMAIN_TREE_ROOT)) {
  470. Count++;
  471. }
  472. }
  473. //
  474. // If we have the current node, then make sure we get the direct trusts as well
  475. //
  476. if ( ( TreeNode->DomainInfo->DnsDomainName &&
  477. _wcsicmp( LocalDomain, TreeNode->DomainInfo->DnsDomainName ) == 0 ) ||
  478. _wcsicmp( LocalDomain, TreeNode->DomainInfo->NetbiosDomainName ) == 0 ) {
  479. HandleDirect = TRUE;
  480. *DomainNode = TreeNode;
  481. for ( i = 0; i < DomainCount; i++ ) {
  482. if ( FLAG_ON( DomainList[ i ].Flags, (DS_DOMAIN_DIRECT_OUTBOUND | DS_DOMAIN_DIRECT_INBOUND) ) &&
  483. !FLAG_ON( DomainList[ i ].Flags, DS_DOMAIN_IN_FOREST ) &&
  484. DomainList[ i ].ParentIndex == 0 ) {
  485. Count++;
  486. }
  487. }
  488. }
  489. //
  490. // Add 'em to the list
  491. //
  492. if ( Count ) {
  493. Win32Err = NetApiBufferAllocate( Count * sizeof( ND5_TRANS_TREE_NODE ),
  494. (PVOID*)&( TreeNode->ChildList ) );
  495. if ( Win32Err == ERROR_SUCCESS ) {
  496. RtlZeroMemory( TreeNode->ChildList, Count * sizeof( ND5_TRANS_TREE_NODE ) );
  497. for ( i = 0; i < DomainCount && Win32Err == ERROR_SUCCESS; i++ ) {
  498. if ( DomainList[ i ].ParentIndex == TreeNode->ListIndex &&
  499. FLAG_ON( DomainList[ i ].Flags, DS_DOMAIN_IN_FOREST ) &&
  500. !FLAG_ON( DomainList[ i ].Flags, DS_DOMAIN_TREE_ROOT) ) {
  501. TreeNode->ChildList[ TreeNode->Children ].DomainInfo = &DomainList[ i ];
  502. TreeNode->ChildList[ TreeNode->Children ].ListIndex = i;
  503. TreeNode->ChildList[ TreeNode->Children ].Parent = TreeNode;
  504. Win32Err = NetDompFindChildrenForNode( LocalDomain,
  505. DomainCount,
  506. DomainList,
  507. &TreeNode->ChildList[ TreeNode->Children ],
  508. DomainNode );
  509. TreeNode->Children++;
  510. DomainList[ i ].ParentIndex = 0xFFFFFFFF;
  511. }
  512. }
  513. //
  514. // Now, the other local entries
  515. //
  516. if ( Win32Err == ERROR_SUCCESS && HandleDirect ) {
  517. for ( i = 0; i < DomainCount; i++ ) {
  518. if ( FLAG_ON( DomainList[ i ].Flags, (DS_DOMAIN_DIRECT_OUTBOUND | DS_DOMAIN_DIRECT_INBOUND) ) &&
  519. !FLAG_ON( DomainList[ i ].Flags, DS_DOMAIN_IN_FOREST ) &&
  520. DomainList[ i ].ParentIndex == 0 ) {
  521. TreeNode->ChildList[ TreeNode->Children ].DomainInfo = &DomainList[ i ];
  522. TreeNode->ChildList[ TreeNode->Children ].ListIndex = i;
  523. TreeNode->Children++;
  524. DomainList[ i ].ParentIndex = 0xFFFFFFFF;
  525. }
  526. }
  527. }
  528. }
  529. }
  530. return( Win32Err );
  531. }
  532. DWORD
  533. NetDompBuildTransTrustTree(
  534. IN PWSTR LocalDomain,
  535. IN ULONG DomainCount,
  536. IN PDS_DOMAIN_TRUSTS DomainList,
  537. OUT PND5_TRANS_TREE_NODE *TreeRoot,
  538. OUT PND5_TRANS_TREE_NODE *CurrentDomainNode
  539. )
  540. /*++
  541. Routine Description:
  542. This function will build the transative trust tree for the given trust list
  543. Arguments:
  544. LocalDomain - Current domain
  545. DomainCount - Number of domains in the list
  546. DomainList - List of domains
  547. TreeRoot - Tree root
  548. CurrentDomainNode - Pointer to the LocalDomain's node
  549. Return Value:
  550. ERROR_SUCCESS - The function succeeded
  551. ERROR_INVALID_PARAMETER - No server, workstation or machine was specified
  552. --*/
  553. {
  554. DWORD Win32Err = ERROR_SUCCESS;
  555. PND5_TRANS_TREE_NODE Root = NULL, Temp = NULL, DomainNode = NULL;
  556. PDS_DOMAIN_TRUSTS TDRoot = NULL;
  557. ULONG i, Index;
  558. //
  559. // First, find the tree root.
  560. //
  561. for ( i = 0; i < DomainCount; i++ ) {
  562. if ( DomainList[ i ].ParentIndex == 0 &&
  563. FLAG_ON( DomainList[ i ].Flags, DS_DOMAIN_TREE_ROOT ) ) {
  564. TDRoot = &DomainList[ i ];
  565. Index = i;
  566. break;
  567. }
  568. }
  569. if ( TDRoot == NULL ) {
  570. //
  571. // Find ourselves, and make us the root
  572. //
  573. for ( i = 0; i < DomainCount; i++ ) {
  574. if ( ( DomainList[ i ].DnsDomainName &&
  575. _wcsicmp( LocalDomain, DomainList[ i ].DnsDomainName ) == 0 ) ||
  576. _wcsicmp( LocalDomain, DomainList[ i ].NetbiosDomainName ) == 0 ) {
  577. TDRoot = &DomainList[ i ];
  578. Index = i;
  579. break;
  580. }
  581. }
  582. }
  583. //
  584. // If we still don't have one, bail...
  585. //
  586. if ( TDRoot == NULL) {
  587. Win32Err = ERROR_INVALID_DOMAIN_STATE;
  588. goto BuildTransExit;
  589. }
  590. Win32Err = NetApiBufferAllocate( sizeof( ND5_TRANS_TREE_NODE ), (PVOID*)&Root );
  591. if ( Win32Err != ERROR_SUCCESS ) {
  592. goto BuildTransExit;
  593. }
  594. RtlZeroMemory( Root, sizeof( ND5_TRANS_TREE_NODE ) );
  595. Root->DomainInfo = TDRoot;
  596. Root->ListIndex = Index;
  597. TDRoot->ParentIndex = 0xFFFFFFFF;
  598. Win32Err = NetDompFindChildrenForNode( LocalDomain,
  599. DomainCount,
  600. DomainList,
  601. Root,
  602. &DomainNode );
  603. BuildTransExit:
  604. if ( Win32Err == ERROR_SUCCESS ) {
  605. *TreeRoot = Root;
  606. *CurrentDomainNode = DomainNode;
  607. }
  608. return( Win32Err );
  609. }
  610. DWORD
  611. NetDompGetTrustDirection(
  612. IN PND5_TRUST_INFO TrustingInfo,
  613. IN PND5_TRUST_INFO TrustedInfo,
  614. IN OUT PDWORD Direction
  615. )
  616. /*++
  617. Routine Description:
  618. This function will get the direction of the trust between the 2 specified domains
  619. Arguments:
  620. TrustingInfo - Domain #1
  621. TrustedInfo - Domain #2
  622. Direction - Where the trust direction is returned
  623. Return Value:
  624. ERROR_SUCCESS - The function succeeded
  625. --*/
  626. {
  627. DWORD Win32Err = ERROR_SUCCESS;
  628. NTSTATUS Status;
  629. LSA_HANDLE TrustedDomain;
  630. PTRUSTED_DOMAIN_INFORMATION_EX TDIEx = NULL;
  631. PUSER_INFO_1 UI1 = NULL;
  632. WCHAR AccountName[ UNLEN + 1 ];
  633. if ( TrustingInfo->Uplevel ) {
  634. Status = LsaQueryTrustedDomainInfoByName( TrustingInfo->LsaHandle,
  635. TrustedInfo->DomainName,
  636. TrustedDomainInformationEx,
  637. (PVOID*)&TDIEx );
  638. if (STATUS_OBJECT_NAME_NOT_FOUND == Status && TrustedInfo->Uplevel)
  639. {
  640. // Pre-existing TDOs for domains upgraded from NT4 to NT5 will continue to
  641. // have a flat name.
  642. //
  643. TrustedInfo->fWasDownlevel = TRUE;
  644. Status = LsaQueryTrustedDomainInfoByName( TrustingInfo->LsaHandle,
  645. TrustedInfo->FlatName,
  646. TrustedDomainInformationEx,
  647. (PVOID*)&TDIEx );
  648. }
  649. if ( NT_SUCCESS( Status ) ) {
  650. DBG_VERBOSE(("Trust to domain %ws has direction %d\n", TrustedInfo->DomainName->Buffer,
  651. TDIEx->TrustDirection));
  652. *Direction = TDIEx->TrustDirection;
  653. LsaFreeMemory( TDIEx );
  654. }
  655. Win32Err = RtlNtStatusToDosError( Status );
  656. } else {
  657. *Direction = 0;
  658. Status = LsaOpenTrustedDomain( TrustingInfo->LsaHandle,
  659. TrustedInfo->Sid,
  660. MAXIMUM_ALLOWED,
  661. &TrustedDomain );
  662. if ( Status != STATUS_OBJECT_NAME_NOT_FOUND ) {
  663. *Direction = TRUST_DIRECTION_OUTBOUND;
  664. }
  665. if ( NT_SUCCESS( Status ) ) {
  666. LsaClose( TrustedDomain );
  667. }
  668. if ( TrustedInfo->FlatName->Length > DNLEN * sizeof( WCHAR ) ) {
  669. Win32Err = ERROR_INVALID_DOMAINNAME;
  670. } else {
  671. //
  672. // Build the account name...
  673. //
  674. swprintf( AccountName, L"%ws$", TrustedInfo->FlatName->Buffer );
  675. Win32Err = NetUserGetInfo( TrustingInfo->Server,
  676. AccountName,
  677. 1,
  678. ( LPBYTE * )&UI1 );
  679. if ( Win32Err != ERROR_NO_SUCH_USER &&
  680. Win32Err != NERR_UserNotFound ) {
  681. *Direction |= TRUST_DIRECTION_INBOUND;
  682. }
  683. if ( Win32Err == ERROR_SUCCESS ) {
  684. NetApiBufferFree( UI1 );
  685. }
  686. if ( Win32Err == ERROR_NO_SUCH_USER ||
  687. Win32Err == NERR_UserNotFound ) {
  688. Win32Err = ERROR_SUCCESS;
  689. }
  690. }
  691. }
  692. return( Win32Err );
  693. }
  694. /*++
  695. DWORD
  696. NetDompFindChildNode(
  697. IN PUNICODE_STRING ChildToFind,
  698. IN PND5_TRANS_TREE_NODE Current,
  699. IN PND5_TRANS_TREE_NODE Skip,
  700. IN ULONG Display,
  701. BOOL IncludeParent
  702. )
  703. Routine Description:
  704. This function will find the child of the current node
  705. Arguments:
  706. ChildToFind - Child domain to find
  707. Current - Where we are in the tree
  708. Skip - Node to not process if we are coming from our parent
  709. Display - Resource id of string to display
  710. IncludeParent - If TRUE, work up the tree as well as down
  711. Return Value:
  712. ERROR_SUCCESS - The function succeeded
  713. ERROR_INVALID_PARAMETER - No server, workstation or machine was specified
  714. {
  715. DWORD Win32Err = ERROR_NOT_FOUND;
  716. ULONG i;
  717. UNICODE_STRING CurrentDomain;
  718. BOOL Found = FALSE;
  719. if ( !Current ) {
  720. return( Win32Err );
  721. }
  722. for ( i = 0; i < Current->Children && !Found && Win32Err == ERROR_NOT_FOUND; i++ ) {
  723. RtlInitUnicodeString( &CurrentDomain,
  724. Current->ChildList[ i ].DomainInfo->DnsDomainName ?
  725. Current->ChildList[ i ].DomainInfo->DnsDomainName :
  726. Current->ChildList[ i ].DomainInfo->NetbiosDomainName );
  727. if ( RtlCompareUnicodeString( &CurrentDomain,
  728. ChildToFind,
  729. TRUE ) == 0 ) {
  730. Found = TRUE;
  731. break;
  732. } else {
  733. if ( Skip != &Current->ChildList[ i ] ) {
  734. Win32Err = NetDompFindChildNode( ChildToFind,
  735. &Current->ChildList[ i ],
  736. NULL,
  737. Display,
  738. FALSE );
  739. if ( Win32Err == ERROR_SUCCESS ) {
  740. break;
  741. }
  742. }
  743. }
  744. }
  745. if ( Win32Err == ERROR_NOT_FOUND && IncludeParent ) {
  746. if ( Current->Parent && !Found ) {
  747. RtlInitUnicodeString( &CurrentDomain,
  748. Current->Parent->DomainInfo->DnsDomainName ?
  749. Current->Parent->DomainInfo->DnsDomainName :
  750. Current->Parent->DomainInfo->NetbiosDomainName );
  751. if ( RtlCompareUnicodeString( &CurrentDomain,
  752. ChildToFind,
  753. TRUE ) == 0 ) {
  754. Found = TRUE;
  755. }
  756. }
  757. if ( !Found ) {
  758. Win32Err = NetDompFindChildNode( ChildToFind,
  759. Current->Parent,
  760. Current,
  761. Display,
  762. TRUE );
  763. }
  764. }
  765. if ( Win32Err == ERROR_SUCCESS && Display ) {
  766. NetDompDisplayMessage( Display,
  767. CurrentDomain.Buffer );
  768. }
  769. if ( Found ) {
  770. Win32Err = ERROR_SUCCESS;
  771. }
  772. return( Win32Err );
  773. }
  774. --*/
  775. DWORD
  776. NetDompDisplayTransTrustStatus(
  777. IN PND5_TRUST_INFO TrustInfo,
  778. IN PWSTR DomainName,
  779. //IN PND5_TRANS_TREE_NODE CurrentDomain,
  780. IN DWORD Direction,
  781. IN DWORD TrustStatus
  782. )
  783. /*++
  784. Routine Description:
  785. This function will display the status for a trust
  786. Arguments:
  787. TrustInfo - Trust info to display the status for
  788. DomainName - Name of the domain (if TrustInfo isn't available)
  789. CurrentDomain - Current domain node pointer
  790. Direction - Direction of the trust
  791. TrustStatus - Status code from verifying the trust
  792. Return Value:
  793. ERROR_SUCCESS - The function succeeded
  794. --*/
  795. {
  796. DWORD Win32Err = ERROR_SUCCESS;
  797. ULONG Message, Type;
  798. //
  799. // Display the direction & name
  800. //
  801. Type = Direction & TRUST_DIRECTION_BIDIRECTIONAL;
  802. switch ( Type ) {
  803. case 0:
  804. Message = MSG_TRUST_TRANS_NO_ARROW;
  805. break;
  806. case TRUST_DIRECTION_BIDIRECTIONAL:
  807. Message = MSG_TRUST_TRANS_BOTH_ARROW;
  808. break;
  809. case TRUST_DIRECTION_INBOUND:
  810. Message = MSG_TRUST_TRANS_IN_ARROW;
  811. break;
  812. case TRUST_DIRECTION_OUTBOUND:
  813. Message = MSG_TRUST_TRANS_OUT_ARROW;
  814. break;
  815. }
  816. NetDompDisplayMessage( Message, TrustInfo ? TrustInfo->DomainName->Buffer : DomainName );
  817. //
  818. // Then, the type
  819. //
  820. if (TrustInfo && TrustInfo->Flags & NETDOM_TRUST_TYPE_INDIRECT)
  821. {
  822. Message = MSG_TRUST_TYPE_INDIRECT;
  823. }
  824. else
  825. {
  826. if (TrustInfo && TrustInfo->Flags & NETDOM_TRUST_TYPE_MIT)
  827. {
  828. Message = MSG_TRUST_TYPE_MIT;
  829. }
  830. else
  831. {
  832. Message = MSG_TRUST_TYPE_WINDOWS;
  833. }
  834. }
  835. NetDompDisplayMessage( Message );
  836. //
  837. // Finally, the status.
  838. //
  839. if (TrustInfo && TrustInfo->Flags & NETDOM_TRUST_FLAG_DOMAIN_NOT_FOUND)
  840. {
  841. TrustStatus = ERROR_NO_SUCH_DOMAIN;
  842. }
  843. switch ( TrustStatus ) {
  844. case ERROR_SUCCESS:
  845. NetDompDisplayMessage( MSG_TRUST_VERIFIED );
  846. break;
  847. case ERROR_NO_SUCH_DOMAIN:
  848. NetDompDisplayMessage( MSG_TRUST_NO_DOMAIN );
  849. break;
  850. case ERROR_ACCESS_DENIED:
  851. NetDompDisplayMessage( MSG_TRUST_ACCESS_DENIED );
  852. break;
  853. case VERIFY_QUERY_ONLY:
  854. printf( "\n" );
  855. break;
  856. default:
  857. NetDompDisplayMessage( MSG_TRUST_BROKEN );
  858. break;
  859. }
  860. /* this doesn't work.
  861. if ( TrustInfo ) {
  862. Win32Err = NetDompFindChildNode( TrustInfo->DomainName,
  863. CurrentDomain,
  864. NULL,
  865. MSG_TRUST_VIA,
  866. TRUE );
  867. } */
  868. return( Win32Err );
  869. }
  870. DWORD
  871. NetDompQueryTrust(
  872. IN PWSTR Domain,
  873. IN PND5_AUTH_INFO AuthInfo,
  874. IN PWSTR pwzServer,
  875. IN BOOL Direct,
  876. IN BOOL Verify
  877. )
  878. /*++
  879. Routine Description:
  880. This function will get the list of trusts for a domain
  881. Arguments:
  882. Domain - Domain to get the trust for
  883. AuthInfo - Username and password to use to connect to the machine
  884. pwzServer - Server specified on command line, if any
  885. Direct - if TRUE, get only the DIRECTLY trusted domains
  886. Verify - If TRUE, verify that the trusts are valid
  887. Return Value:
  888. ERROR_SUCCESS - The function succeeded
  889. ERROR_INVALID_PARAMETER - No server, workstation or machine was specified
  890. --*/
  891. {
  892. DWORD Win32Err = ERROR_SUCCESS, VerifyErr;
  893. ND5_TRUST_INFO TrustInfo, OtherInfo;
  894. ULONG Count = 0, i;
  895. PDS_DOMAIN_TRUSTS rgTrustedDomains = NULL;
  896. ULONG Message, Type, Direction;
  897. //PND5_TRANS_TREE_NODE TreeRoot = NULL, CurrentDomainNode;
  898. PWSTR CurrentDomain;
  899. RtlZeroMemory( &TrustInfo, sizeof( ND5_TRUST_INFO ) );
  900. Win32Err = NetDompTrustGetDomInfo( Domain,
  901. pwzServer,
  902. AuthInfo,
  903. &TrustInfo,
  904. FALSE,
  905. FALSE,
  906. FALSE);
  907. if ( Win32Err == ERROR_SUCCESS ) {
  908. if ( Direct || !TrustInfo.Uplevel ) {
  909. Win32Err = NetDompQueryDirectTrust( Domain,
  910. &TrustInfo );
  911. } else {
  912. Win32Err = DsEnumerateDomainTrusts(TrustInfo.Server,
  913. DS_DOMAIN_IN_FOREST | DS_DOMAIN_DIRECT_OUTBOUND | DS_DOMAIN_DIRECT_INBOUND,
  914. &rgTrustedDomains,
  915. &Count);
  916. if ( Win32Err == ERROR_SUCCESS ) {
  917. if ( Count ) {
  918. NetDompDisplayMessage((Verify) ? MSG_TRUST_TRANS_HEADER_VERIFY :
  919. MSG_TRUST_TRANS_HEADER);
  920. }
  921. /* this doesn't work.
  922. Win32Err = NetDompBuildTransTrustTree( Domain,
  923. Count,
  924. rgTrustedDomains,
  925. &TreeRoot,
  926. &CurrentDomainNode ); */
  927. if ( Win32Err == ERROR_SUCCESS ) {
  928. for ( i = 0; i < Count; i++ ) {
  929. //
  930. // Make sure we aren't connecting to ourselves...
  931. //
  932. CurrentDomain = rgTrustedDomains[ i ].DnsDomainName ?
  933. rgTrustedDomains[ i ].DnsDomainName :
  934. rgTrustedDomains[ i ].NetbiosDomainName;
  935. if ( !_wcsicmp( CurrentDomain, TrustInfo.DomainName->Buffer ) ) {
  936. continue;
  937. }
  938. RtlZeroMemory(&OtherInfo, sizeof(ND5_TRUST_INFO));
  939. if (rgTrustedDomains[i].Flags & DS_DOMAIN_DIRECT_OUTBOUND ||
  940. rgTrustedDomains[i].Flags & DS_DOMAIN_DIRECT_INBOUND)
  941. {
  942. // There is a direct trust to the domain, therefore a TDO
  943. // exists, so read the domain data locally.
  944. //
  945. Win32Err = GetTrustInfo(CurrentDomain,
  946. &TrustInfo,
  947. &OtherInfo,
  948. &VerifyErr);
  949. if (ERROR_SUCCESS == Win32Err)
  950. {
  951. VerifyErr = NetDompGetTrustDirection(&TrustInfo,
  952. &OtherInfo,
  953. &Direction);
  954. }
  955. else
  956. {
  957. Direction = 0;
  958. }
  959. }
  960. else
  961. {
  962. Win32Err = NetDompTrustGetDomInfo(CurrentDomain,
  963. NULL,
  964. AuthInfo,
  965. &OtherInfo,
  966. FALSE,
  967. FALSE, TRUE);
  968. VerifyErr = Win32Err;
  969. OtherInfo.Flags |= NETDOM_TRUST_TYPE_INDIRECT;
  970. //
  971. // If the trust is indirect, it must be a forest trust.
  972. // Enterprise trusts always have a bi-di path.
  973. //
  974. Direction = TRUST_DIRECTION_BIDIRECTIONAL;
  975. }
  976. if (ERROR_SUCCESS == VerifyErr)
  977. {
  978. if (Verify
  979. && !(NETDOM_TRUST_TYPE_MIT & OtherInfo.Flags)
  980. && (DS_DOMAIN_DIRECT_OUTBOUND & rgTrustedDomains[i].Flags))
  981. {
  982. // Verify only direct, outbound, non-MIT trusts.
  983. // Can't verify incoming without creds to the other
  984. // domain.
  985. //
  986. VerifyErr = NetDompVerifyTrust(&TrustInfo,
  987. &OtherInfo,
  988. FALSE);
  989. }
  990. else
  991. {
  992. VerifyErr = VERIFY_QUERY_ONLY;
  993. }
  994. NetDompDisplayTransTrustStatus( &OtherInfo,
  995. NULL,
  996. //CurrentDomainNode,
  997. Direction,
  998. VerifyErr );
  999. NetDompFreeDomInfo( &OtherInfo );
  1000. } else {
  1001. if ( !Verify ) {
  1002. VerifyErr = VERIFY_QUERY_ONLY;
  1003. }
  1004. NetDompDisplayTransTrustStatus( NULL,
  1005. rgTrustedDomains[ i ].DnsDomainName ?
  1006. rgTrustedDomains[ i ].DnsDomainName :
  1007. rgTrustedDomains[ i ].NetbiosDomainName,
  1008. //CurrentDomainNode,
  1009. Direction,
  1010. VerifyErr );
  1011. }
  1012. }
  1013. }
  1014. NetApiBufferFree( rgTrustedDomains );
  1015. }
  1016. }
  1017. NetDompFreeDomInfo( &TrustInfo );
  1018. }
  1019. return( Win32Err );
  1020. }
  1021. DWORD
  1022. NetDompQueryDisplayOus(
  1023. IN PWSTR Domain,
  1024. IN PND5_AUTH_INFO AuthInfo
  1025. )
  1026. /*++
  1027. Routine Description:
  1028. This function will list the OUs under which the specified user can create a computer object
  1029. Arguments:
  1030. Domain - Domain to connect to
  1031. AuthInfo - Username and password to connect to the domain with
  1032. Return Value:
  1033. ERROR_SUCCESS - The function succeeded
  1034. --*/
  1035. {
  1036. DWORD Win32Err = ERROR_SUCCESS;
  1037. PWSTR *OuList;
  1038. ULONG OuCount = 0, i;
  1039. //
  1040. // Get the list and display it
  1041. //
  1042. LOG_VERBOSE(( MSG_VERBOSE_DETERMINE_OU ));
  1043. Win32Err = NetGetJoinableOUs( NULL,
  1044. Domain,
  1045. AuthInfo->User,
  1046. AuthInfo->Password,
  1047. &OuCount,
  1048. &OuList );
  1049. if ( Win32Err == ERROR_SUCCESS ) {
  1050. NetDompDisplayMessage( MSG_OU_LIST );
  1051. for ( i = 0; i < OuCount; i++ ) {
  1052. DisplayOutput( OuList[ i ] );
  1053. }
  1054. NetApiBufferFree( OuList );
  1055. }
  1056. return( Win32Err );
  1057. }
  1058. DWORD
  1059. NetDompQueryFsmo(
  1060. IN PWSTR Domain,
  1061. IN PND5_AUTH_INFO AuthInfo
  1062. )
  1063. /*++
  1064. Routine Description:
  1065. This function will list the machines holding the various FSMO roles
  1066. Arguments:
  1067. Domain - Domain to connect to
  1068. AuthInfo - Username and password to connect to the domain with
  1069. Return Value:
  1070. ERROR_SUCCESS - The function succeeded
  1071. --*/
  1072. {
  1073. DWORD Win32Err = ERROR_SUCCESS;
  1074. PWSTR User = NULL, Separator = NULL, pwzDomain = NULL, FsmoServer = NULL, ServerPath;
  1075. PDOMAIN_CONTROLLER_INFO DcInfo = NULL;
  1076. HANDLE DsHandle = NULL;
  1077. RPC_AUTH_IDENTITY_HANDLE AuthHandle = NULL;
  1078. PDS_NAME_RESULT DsRoles = NULL;
  1079. PLDAP Ldap = NULL;
  1080. ULONG i;
  1081. ULONG DisplayMap[ ] = {
  1082. MSG_FSMO_SCHEMA,
  1083. MSG_FSMO_DOMAIN,
  1084. MSG_FSMO_PDC,
  1085. MSG_FSMO_RID,
  1086. MSG_FSMO_INFRASTRUCTURE
  1087. };
  1088. //
  1089. // Find a domain controller
  1090. //
  1091. LOG_VERBOSE(( MSG_VERBOSE_FIND_DC, Domain ));
  1092. Win32Err = DsGetDcName( NULL,
  1093. Domain,
  1094. NULL,
  1095. NULL,
  1096. DS_DIRECTORY_SERVICE_REQUIRED,
  1097. &DcInfo );
  1098. if ( Win32Err == ERROR_SUCCESS ) {
  1099. if ( AuthInfo->User ) {
  1100. Separator = wcschr( AuthInfo->User, L'\\' );
  1101. if ( Separator ) {
  1102. *Separator = UNICODE_NULL;
  1103. User = Separator + 1;
  1104. if (!*User) {
  1105. return ERROR_INVALID_PARAMETER;
  1106. }
  1107. pwzDomain = AuthInfo->User;
  1108. } else {
  1109. User = AuthInfo->User;
  1110. pwzDomain = Domain;
  1111. }
  1112. }
  1113. Win32Err = DsMakePasswordCredentials( User,
  1114. pwzDomain,
  1115. AuthInfo->Password,
  1116. &AuthHandle );
  1117. // NTRAID#NTBUG9-738640-2002/12/17-shasan Allow AuthHandle to be NULL for case when user is already logged on as admin
  1118. if ( Win32Err == ERROR_SUCCESS ) {
  1119. Win32Err = DsBindWithCred( DcInfo->DomainControllerName,
  1120. NULL,
  1121. AuthHandle,
  1122. &DsHandle );
  1123. DsFreePasswordCredentials( AuthHandle );
  1124. }
  1125. //
  1126. // Now, start getting the info
  1127. //
  1128. if ( Win32Err == ERROR_SUCCESS ) {
  1129. Win32Err = DsListRoles( DsHandle,
  1130. &DsRoles );
  1131. if ( Win32Err == ERROR_SUCCESS ) {
  1132. ASSERT( sizeof( DisplayMap ) / sizeof( ULONG ) == DsRoles->cItems );
  1133. for ( i = 0; i < sizeof( DisplayMap ) / sizeof( ULONG ); i++ ) {
  1134. ULONG Type = 0;
  1135. //
  1136. // Skip items that may not exist
  1137. //
  1138. if ( DsRoles->rItems[ i ].status != DS_NAME_NO_ERROR ) {
  1139. continue;
  1140. }
  1141. ServerPath = wcschr( DsRoles->rItems[ i ].pName, L',' );
  1142. if ( ServerPath ) {
  1143. ServerPath++;
  1144. } else {
  1145. ServerPath = DsRoles->rItems[ i ].pName;
  1146. }
  1147. if ( !Ldap ) {
  1148. Win32Err = NetDompLdapBind( DcInfo->DomainControllerName + 2,
  1149. User == AuthInfo->User ? NULL : AuthInfo->User,
  1150. User,
  1151. AuthInfo->Password,
  1152. LDAP_AUTH_SSPI,
  1153. &Ldap );
  1154. }
  1155. if ( Win32Err == ERROR_SUCCESS ) {
  1156. Win32Err = NetDompLdapReadOneAttribute( Ldap,
  1157. ServerPath,
  1158. L"dNSHostName",
  1159. &FsmoServer );
  1160. if ( Win32Err == ERROR_SUCCESS ) {
  1161. NetDompDisplayMessage( DisplayMap[ i ], FsmoServer );
  1162. NetApiBufferFree( FsmoServer );
  1163. }
  1164. }
  1165. }
  1166. }
  1167. }
  1168. }
  1169. NetDompLdapUnbind(Ldap);
  1170. if ( DsHandle ) {
  1171. DsUnBind( &DsHandle );
  1172. }
  1173. if ( DsRoles ) {
  1174. DsFreeNameResult( DsRoles );
  1175. }
  1176. if ( Separator ) {
  1177. *Separator = L'\\';
  1178. }
  1179. NetApiBufferFree( DcInfo );
  1180. return( Win32Err );
  1181. }
  1182. DWORD
  1183. NetDompDisplayMachineByType(
  1184. IN PWSTR AccountName,
  1185. IN PND5_AUTH_INFO AuthInfo,
  1186. IN ND5_ACCOUNT_TYPE DesiredType,
  1187. IN ND5_ACCOUNT_TYPE KnownType,
  1188. IN BOOL DisplayOnError
  1189. )
  1190. /*++
  1191. Routine Description:
  1192. This function display machines of the specified type that are joined to the domain
  1193. Arguments:
  1194. AccountName - Name of the machine to get the info from
  1195. AuthInfo - Username and password to connect to the domain with
  1196. DesiredType - Type of machine to get
  1197. KnownType - Whether the machine type is known or not
  1198. DisplayOnError - If TRUE, display a message if an error is encountered
  1199. Return Value:
  1200. ERROR_SUCCESS - The function succeeded
  1201. ERROR_UNSUPPORTED_TYPE - An unknown type was encountered
  1202. --*/
  1203. {
  1204. DWORD Win32Err = ERROR_SUCCESS;
  1205. PSERVER_INFO_101 SrvInfo = NULL;
  1206. PWSTR AccountChar;
  1207. AccountChar = wcsrchr( AccountName, L'$' );
  1208. if ( AccountChar ) {
  1209. *AccountChar = UNICODE_NULL;
  1210. }
  1211. //
  1212. // See if we have to get the type or not
  1213. //
  1214. if ( KnownType == TypeUnknown ) {
  1215. Win32Err = NetpManageIPCConnect( AccountName,
  1216. AuthInfo->User,
  1217. AuthInfo->Password,
  1218. NETSETUPP_CONNECT_IPC );
  1219. if ( Win32Err == ERROR_SUCCESS ) {
  1220. Win32Err = NetServerGetInfo( AccountName,
  1221. 101,
  1222. ( LPBYTE * )&SrvInfo );
  1223. NetpManageIPCConnect( AccountName,
  1224. AuthInfo->User,
  1225. AuthInfo->Password,
  1226. NETSETUPP_DISCONNECT_IPC );
  1227. }
  1228. if ( Win32Err == ERROR_SUCCESS ) {
  1229. if ( FLAG_ON( SrvInfo->sv101_type, SV_TYPE_DOMAIN_BAKCTRL ) ) {
  1230. KnownType = TypeDomainController;
  1231. } else if ( FLAG_ON( SrvInfo->sv101_type, SV_TYPE_DOMAIN_CTRL ) ) {
  1232. if ( DesiredType == TypeDomainController ) {
  1233. KnownType = TypeDomainController;
  1234. } else {
  1235. KnownType = TypePDC;
  1236. }
  1237. } else if ( FLAG_ON( SrvInfo->sv101_type, SV_TYPE_WORKSTATION ) ) {
  1238. KnownType = TypeWorkstation;
  1239. } else {
  1240. Win32Err = ERROR_UNSUPPORTED_TYPE;
  1241. }
  1242. } else {
  1243. LOG_VERBOSE(( MSG_VERBOSE_FAIL_MACH_TYPE, AccountName ));
  1244. ERROR_VERBOSE(( Win32Err ));
  1245. if ( DisplayOnError ) {
  1246. KnownType = DesiredType;
  1247. }
  1248. }
  1249. }
  1250. if ( KnownType == DesiredType && ( Win32Err == ERROR_SUCCESS || DisplayOnError ) ) {
  1251. if ( Win32Err != ERROR_SUCCESS ) {
  1252. NetDompDisplayMessage( MSG_WKSTA_OR_SERVER, AccountName );
  1253. Win32Err = ERROR_SUCCESS;
  1254. } else {
  1255. DisplayOutput( AccountName );
  1256. }
  1257. }
  1258. return( Win32Err );
  1259. }
  1260. DWORD
  1261. NetDompQueryMachines(
  1262. IN ND5_ACCOUNT_OPERATION Operation,
  1263. IN PWSTR Domain,
  1264. IN PND5_AUTH_INFO AuthInfo,
  1265. IN PWSTR pwzServer,
  1266. IN ND5_ACCOUNT_TYPE AccountType,
  1267. IN ULONG MessageId
  1268. )
  1269. /*++
  1270. Routine Description:
  1271. This function will list the machines in a domian
  1272. Arguments:
  1273. Operation - Whether to display/verify/reset the machines
  1274. Domain - Domain to connect to
  1275. AuthInfo - Username and password to connect to the domain with
  1276. pwzServer - Optional server name specified on command line, must be NULL for PDC operation.
  1277. AccountType - Type of accounts to display
  1278. MessageId - Resource ID of string to display
  1279. Return Value:
  1280. ERROR_SUCCESS - The function succeeded
  1281. --*/
  1282. {
  1283. DWORD Win32Err = ERROR_SUCCESS, Win32Err2;
  1284. PWSTR pwzUncServer = NULL, Lop, pwzUser = NULL, pwzDomain = NULL;
  1285. BOOL Connected = FALSE, fDsDcInfoAllocated = FALSE, fFreeServer = FALSE;
  1286. ULONG Type = 0;
  1287. PDOMAIN_CONTROLLER_INFO DcInfo = NULL;
  1288. ULONG AccountTypeMap[] = {
  1289. FILTER_WORKSTATION_TRUST_ACCOUNT,
  1290. FILTER_WORKSTATION_TRUST_ACCOUNT,
  1291. FILTER_SERVER_TRUST_ACCOUNT,
  1292. FILTER_SERVER_TRUST_ACCOUNT,
  1293. FILTER_WORKSTATION_TRUST_ACCOUNT
  1294. };
  1295. LPUSER_INFO_0 UserList = NULL;
  1296. ULONG ResumeHandle = 0, Count = 0, TotalCount = 0, i;
  1297. ULONG DsGetDcOptions = DS_DIRECTORY_SERVICE_PREFERRED;
  1298. PDS_DOMAIN_CONTROLLER_INFO_1 pDsDcInfo;
  1299. if ( AccountType == TypeUnknown ) {
  1300. return( ERROR_INVALID_PARAMETER );
  1301. }
  1302. if (!pwzServer)
  1303. {
  1304. if ( AccountType == TypePDC ) {
  1305. DsGetDcOptions |= DS_PDC_REQUIRED;
  1306. }
  1307. LOG_VERBOSE(( MSG_VERBOSE_FIND_DC, Domain ));
  1308. Win32Err = DsGetDcName( NULL,
  1309. Domain,
  1310. NULL,
  1311. NULL,
  1312. DsGetDcOptions,
  1313. &DcInfo );
  1314. if (ERROR_SUCCESS != Win32Err)
  1315. {
  1316. return Win32Err;
  1317. }
  1318. if (AccountType == TypePDC)
  1319. {
  1320. NetDompDisplayMessage( MessageId );
  1321. NetDompDisplayMachineByType( DcInfo->DomainControllerName + 2,
  1322. AuthInfo,
  1323. TypePDC,
  1324. TypePDC,
  1325. TRUE );
  1326. goto QueryMachinesExit;
  1327. }
  1328. pwzUncServer = DcInfo->DomainControllerName;
  1329. }
  1330. else
  1331. {
  1332. // Server supplied on the command line. See if it has the needed backslashes.
  1333. //
  1334. if (L'\\' == *pwzServer)
  1335. {
  1336. if (wcslen(pwzServer) < 3 || L'\\' != pwzServer[1])
  1337. {
  1338. return ERROR_INVALID_PARAMETER;
  1339. }
  1340. pwzUncServer = pwzServer;
  1341. }
  1342. else
  1343. {
  1344. Win32Err = NetApiBufferAllocate((wcslen(pwzServer) + 3) * sizeof(WCHAR),
  1345. (PVOID*)&pwzUncServer);
  1346. if (ERROR_SUCCESS != Win32Err)
  1347. {
  1348. return Win32Err;
  1349. }
  1350. wsprintf(pwzUncServer, L"\\\\%s", pwzServer);
  1351. fFreeServer = TRUE;
  1352. }
  1353. }
  1354. LOG_VERBOSE(( MSG_VERBOSE_ESTABLISH_SESSION, pwzUncServer ));
  1355. Win32Err = NetpManageIPCConnect( pwzUncServer,
  1356. AuthInfo->User,
  1357. AuthInfo->Password,
  1358. NETSETUPP_CONNECT_IPC );
  1359. if ( Win32Err == ERROR_SUCCESS ) {
  1360. Connected = TRUE;
  1361. }
  1362. else {
  1363. goto QueryMachinesExit;
  1364. }
  1365. NetDompDisplayMessage( MessageId );
  1366. //
  1367. // Now, do the enumeration
  1368. //
  1369. if (TypeDomainController == AccountType) {
  1370. HANDLE hDS;
  1371. RPC_AUTH_IDENTITY_HANDLE hID;
  1372. if (AuthInfo->User) {
  1373. pwzUser = wcschr(AuthInfo->User, L'\\');
  1374. if (pwzUser) {
  1375. //
  1376. // backslash found, replace with NULL and point to next char.
  1377. //
  1378. *pwzUser = UNICODE_NULL;
  1379. pwzUser++;
  1380. if (!*pwzUser) {
  1381. return ERROR_INVALID_PARAMETER;
  1382. }
  1383. pwzDomain = AuthInfo->User;
  1384. }
  1385. else {
  1386. pwzUser = AuthInfo->User;
  1387. pwzDomain = Domain;
  1388. }
  1389. }
  1390. Win32Err = DsMakePasswordCredentials( pwzUser,
  1391. pwzDomain,
  1392. AuthInfo->Password,
  1393. &hID);
  1394. if ( Win32Err != ERROR_SUCCESS ) {
  1395. goto QueryMachinesExit;
  1396. }
  1397. Win32Err = DsBindWithCred(pwzUncServer, NULL, hID, &hDS);
  1398. DsFreePasswordCredentials(hID);
  1399. if ( Win32Err == ERROR_SUCCESS ) {
  1400. Win32Err = DsGetDomainControllerInfo(hDS, Domain, 1, &Count, (PVOID*)&pDsDcInfo);
  1401. DsUnBind(&hDS);
  1402. if ( Win32Err != ERROR_SUCCESS ) {
  1403. goto QueryMachinesExit;
  1404. }
  1405. fDsDcInfoAllocated = TRUE;
  1406. for ( i = 0; i < Count; i++ ) {
  1407. switch ( Operation ) {
  1408. case OperationDisplay:
  1409. //
  1410. // Ignore errors from this function
  1411. //
  1412. NetDompDisplayMachineByType( pDsDcInfo[ i ].NetbiosName,
  1413. AuthInfo,
  1414. TypeDomainController,
  1415. TypeDomainController,
  1416. TRUE );
  1417. break;
  1418. case OperationVerify:
  1419. Win32Err2 = NetDompVerifyServerSC( Domain,
  1420. pDsDcInfo[ i ].NetbiosName,
  1421. AuthInfo,
  1422. MSG_QUERY_VERIFY_OK,
  1423. 0 );
  1424. if ( Win32Err2 != ERROR_SUCCESS ) {
  1425. NetDompDisplayMessageAndError( MSG_QUERY_VERIFY_BAD,
  1426. Win32Err2,
  1427. pDsDcInfo[ i ].NetbiosName );
  1428. }
  1429. break;
  1430. case OperationReset:
  1431. Win32Err2 = NetDompResetServerSC( Domain,
  1432. pDsDcInfo[ i ].NetbiosName,
  1433. NULL,
  1434. AuthInfo,
  1435. MSG_QUERY_VERIFY_OK,
  1436. 0 );
  1437. if ( Win32Err2 != ERROR_SUCCESS ) {
  1438. NetDompDisplayMessageAndError( MSG_QUERY_VERIFY_BAD,
  1439. Win32Err2,
  1440. pDsDcInfo[ i ].NetbiosName );
  1441. }
  1442. break;
  1443. default:
  1444. Win32Err2 = ERROR_INVALID_PARAMETER;
  1445. break;
  1446. }
  1447. }
  1448. goto QueryMachinesExit;
  1449. }
  1450. else {
  1451. // DsBind will return EPT_S_NOT_REGISTERED if a downlevel DC is targetted.
  1452. // If so, fall through to the NetUserEnum code.
  1453. //
  1454. if (EPT_S_NOT_REGISTERED != Win32Err) {
  1455. goto QueryMachinesExit;
  1456. }
  1457. }
  1458. }
  1459. do {
  1460. Win32Err = NetUserEnum( pwzUncServer,
  1461. 0,
  1462. AccountTypeMap[ AccountType ],
  1463. ( LPBYTE * )&UserList,
  1464. MAX_PREFERRED_LENGTH,
  1465. &Count,
  1466. &TotalCount,
  1467. &ResumeHandle );
  1468. if ( Win32Err == ERROR_SUCCESS || Win32Err == ERROR_MORE_DATA ) {
  1469. for ( i = 0; i < Count; i++ ) {
  1470. switch ( Operation ) {
  1471. case OperationDisplay:
  1472. //
  1473. // Ignore errors from this function
  1474. //
  1475. NetDompDisplayMachineByType( UserList[ i ].usri0_name,
  1476. AuthInfo,
  1477. AccountType,
  1478. TypeUnknown,
  1479. TRUE );
  1480. break;
  1481. case OperationVerify:
  1482. Lop = wcsrchr( UserList[ i ].usri0_name, L'$' );
  1483. if ( Lop ) {
  1484. *Lop = UNICODE_NULL;
  1485. }
  1486. Win32Err2 = NetDompVerifyServerSC( Domain,
  1487. UserList[ i ].usri0_name,
  1488. AuthInfo,
  1489. MSG_QUERY_VERIFY_OK,
  1490. 0 );
  1491. if ( Win32Err2 != ERROR_SUCCESS ) {
  1492. NetDompDisplayMessageAndError( MSG_QUERY_VERIFY_BAD,
  1493. Win32Err2,
  1494. UserList[ i ].usri0_name );
  1495. }
  1496. if ( Lop ) {
  1497. *Lop = L'$';
  1498. }
  1499. break;
  1500. case OperationReset:
  1501. Lop = wcsrchr( UserList[ i ].usri0_name, L'$' );
  1502. if ( Lop ) {
  1503. *Lop = UNICODE_NULL;
  1504. }
  1505. Win32Err2 = NetDompResetServerSC( Domain,
  1506. UserList[ i ].usri0_name,
  1507. NULL,
  1508. AuthInfo,
  1509. MSG_QUERY_VERIFY_OK,
  1510. 0 );
  1511. if ( Win32Err2 != ERROR_SUCCESS ) {
  1512. NetDompDisplayMessageAndError( MSG_QUERY_VERIFY_BAD,
  1513. Win32Err2,
  1514. UserList[ i ].usri0_name );
  1515. }
  1516. if ( Lop ) {
  1517. *Lop = L'$';
  1518. }
  1519. break;
  1520. default:
  1521. Win32Err2 = ERROR_INVALID_PARAMETER;
  1522. break;
  1523. }
  1524. }
  1525. NetApiBufferFree( UserList );
  1526. }
  1527. } while ( Win32Err == ERROR_MORE_DATA );
  1528. QueryMachinesExit:
  1529. if ( Connected ) {
  1530. LOG_VERBOSE(( MSG_VERBOSE_DELETE_SESSION, pwzUncServer ));
  1531. NetpManageIPCConnect( pwzUncServer,
  1532. NULL,
  1533. NULL,
  1534. NETSETUPP_DISCONNECT_IPC );
  1535. }
  1536. if (fFreeServer)
  1537. {
  1538. NetApiBufferFree(pwzUncServer);
  1539. }
  1540. if (fDsDcInfoAllocated)
  1541. {
  1542. DsFreeDomainControllerInfo(1, Count, pDsDcInfo);
  1543. }
  1544. if (DcInfo)
  1545. {
  1546. NetApiBufferFree(DcInfo);
  1547. }
  1548. return( Win32Err );
  1549. }
  1550. DWORD
  1551. NetDompHandleQuery(ARG_RECORD * rgNetDomArgs)
  1552. /*++
  1553. Routine Description:
  1554. This function will move a machine from one domain to another
  1555. Arguments:
  1556. Args - List of command line arguments
  1557. Return Value:
  1558. ERROR_INVALID_PARAMETER - No object name was supplied
  1559. --*/
  1560. {
  1561. DWORD Win32Err = ERROR_SUCCESS;
  1562. PWSTR Domain = NULL, Server = NULL;
  1563. ND5_AUTH_INFO DomainUser;
  1564. ND5_ACCOUNT_OPERATION Operation = OperationDisplay;
  1565. ULONG DisplayFlag = 0;
  1566. RtlZeroMemory( &DomainUser, sizeof( ND5_AUTH_INFO ) );
  1567. Win32Err = NetDompValidateSecondaryArguments(rgNetDomArgs,
  1568. eQueryPDC,
  1569. eQueryServer,
  1570. eQueryWksta,
  1571. eQueryDC,
  1572. eQueryOU,
  1573. eQueryFSMO,
  1574. eQueryTrust,
  1575. eCommDomain,
  1576. eCommUserNameD,
  1577. eCommPasswordD,
  1578. eCommServer,
  1579. eCommReset,
  1580. eQueryDirect,
  1581. eCommVerbose,
  1582. eCommVerify,
  1583. eArgEnd);
  1584. if ( Win32Err != ERROR_SUCCESS ) {
  1585. DisplayHelp(ePriQuery);
  1586. return Win32Err;
  1587. }
  1588. //
  1589. // Get the server name
  1590. //
  1591. Win32Err = NetDompGetArgumentString(rgNetDomArgs,
  1592. eCommServer,
  1593. &Server);
  1594. if ( Win32Err != ERROR_SUCCESS ) {
  1595. goto HandleQueryExit;
  1596. }
  1597. //
  1598. // Ok, make sure that we have a specified domain...
  1599. //
  1600. Win32Err = NetDompGetDomainForOperation(rgNetDomArgs,
  1601. Server,
  1602. TRUE,
  1603. &Domain);
  1604. if ( Win32Err != ERROR_SUCCESS ) {
  1605. goto HandleQueryExit;
  1606. }
  1607. //
  1608. // Get the password and user if it exists
  1609. //
  1610. if ( CmdFlagOn(rgNetDomArgs, eCommUserNameD) ) {
  1611. Win32Err = NetDompGetUserAndPasswordForOperation(rgNetDomArgs,
  1612. eCommUserNameD,
  1613. Domain,
  1614. &DomainUser);
  1615. if ( Win32Err != ERROR_SUCCESS ) {
  1616. goto HandleQueryExit;
  1617. }
  1618. }
  1619. //
  1620. // Find the query sub command.
  1621. //
  1622. NETDOM_ARG_ENUM eQuery = eArgNull;
  1623. for (int i = eQueryBegin; i <= eQueryEnd; i++)
  1624. {
  1625. if (CmdFlagOn(rgNetDomArgs, static_cast<NETDOM_ARG_ENUM>(i)))
  1626. {
  1627. if (eArgNull != eQuery)
  1628. {
  1629. ASSERT(rgNetDomArgs[i].strArg1);
  1630. NetDompDisplayUnexpectedParameter(rgNetDomArgs[i].strArg1);
  1631. DisplayHelp(ePriQuery);
  1632. Win32Err = ERROR_INVALID_PARAMETER;
  1633. goto HandleQueryExit;
  1634. }
  1635. eQuery = static_cast<NETDOM_ARG_ENUM>(i);
  1636. }
  1637. }
  1638. if (eArgNull == eQuery)
  1639. {
  1640. DisplayHelp(ePriQuery);
  1641. Win32Err = ERROR_INVALID_PARAMETER;
  1642. goto HandleQueryExit;
  1643. }
  1644. if ( CmdFlagOn(rgNetDomArgs, eCommVerify) ) {
  1645. Operation = OperationVerify;
  1646. DisplayFlag = MSG_QUERY_VERIFY;
  1647. }
  1648. if ( CmdFlagOn(rgNetDomArgs, eCommReset) ) {
  1649. if ( Operation == OperationVerify ) {
  1650. Win32Err = ERROR_INVALID_PARAMETER;
  1651. goto HandleQueryExit;
  1652. } else {
  1653. Operation = OperationReset;
  1654. DisplayFlag = MSG_QUERY_RESET;
  1655. }
  1656. }
  1657. switch (eQuery)
  1658. {
  1659. case eQueryOU:
  1660. Win32Err = NetDompQueryDisplayOus( Domain,
  1661. &DomainUser );
  1662. break;
  1663. case eQueryWksta:
  1664. Win32Err = NetDompQueryMachines( Operation,
  1665. Domain,
  1666. &DomainUser,
  1667. Server,
  1668. TypeWorkstation,
  1669. DisplayFlag ? DisplayFlag : MSG_WORKSTATION_LIST );
  1670. break;
  1671. case eQueryServer:
  1672. Win32Err = NetDompQueryMachines( Operation,
  1673. Domain,
  1674. &DomainUser,
  1675. Server,
  1676. TypeServer,
  1677. DisplayFlag ? DisplayFlag : MSG_SERVER_LIST );
  1678. break;
  1679. case eQueryDC:
  1680. Win32Err = NetDompQueryMachines( Operation,
  1681. Domain,
  1682. &DomainUser,
  1683. Server,
  1684. TypeDomainController,
  1685. DisplayFlag ? DisplayFlag : MSG_DC_LIST );
  1686. break;
  1687. case eQueryPDC:
  1688. Win32Err = NetDompQueryMachines( Operation,
  1689. Domain,
  1690. &DomainUser,
  1691. NULL,
  1692. TypePDC,
  1693. MSG_PDC_LIST );
  1694. break;
  1695. case eQueryFSMO:
  1696. Win32Err = NetDompQueryFsmo( Domain,
  1697. &DomainUser );
  1698. break;
  1699. case eQueryTrust:
  1700. if (CmdFlagOn(rgNetDomArgs, eQueryDirect) &&
  1701. CmdFlagOn(rgNetDomArgs, eCommVerify))
  1702. {
  1703. DisplayHelp(ePriQuery);
  1704. Win32Err = ERROR_INVALID_PARAMETER;
  1705. goto HandleQueryExit;
  1706. }
  1707. Win32Err = NetDompQueryTrust( Domain,
  1708. &DomainUser,
  1709. Server,
  1710. CmdFlagOn(rgNetDomArgs, eQueryDirect),
  1711. CmdFlagOn(rgNetDomArgs, eCommVerify));
  1712. break;
  1713. default:
  1714. Win32Err = ERROR_INVALID_PARAMETER;
  1715. break;
  1716. }
  1717. HandleQueryExit:
  1718. NetApiBufferFree( Domain );
  1719. NetApiBufferFree( Server );
  1720. NetDompFreeAuthIdent( &DomainUser );
  1721. if (NO_ERROR != Win32Err)
  1722. {
  1723. NetDompDisplayErrorMessage(Win32Err);
  1724. }
  1725. return( Win32Err );
  1726. }