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.

2076 lines
48 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. domain.c
  5. Abstract:
  6. This code implements a set of linked list data structures used to
  7. resolve domain name lookups. It achieves a similar structure to
  8. memdb but provides the ability to move an item from list to list
  9. efficiently.
  10. Author:
  11. Jim Schmidt (jimschm) 18-Jun-1997
  12. Revision History:
  13. jimschm 23-Sep-1998 Fixed trusted domains
  14. jimschm 17-Feb-1998 Updated share security for NT 5 changes
  15. --*/
  16. #include "pch.h"
  17. #include "migmainp.h"
  18. #include "security.h"
  19. #define DBG_ACCTLIST "Accounts"
  20. PACCT_DOMAINS g_FirstDomain;
  21. POOLHANDLE g_DomainPool;
  22. INT g_RetryCount;
  23. BOOL
  24. pAddAllLogonDomains (
  25. IN PCTSTR DCName OPTIONAL
  26. );
  27. VOID
  28. InitAccountList (
  29. VOID
  30. )
  31. /*++
  32. Routine Description:
  33. Initializer for the account list that is used during account
  34. lookup. This memory is freed after all accounts are found.
  35. Arguments:
  36. none
  37. Return value:
  38. none
  39. --*/
  40. {
  41. g_FirstDomain = NULL;
  42. g_DomainPool = PoolMemInitNamedPool ("Domain List");
  43. PoolMemDisableTracking (g_DomainPool);
  44. }
  45. VOID
  46. TerminateAccountList (
  47. VOID
  48. )
  49. /*++
  50. Routine Description:
  51. Termination routine for the account lookup code.
  52. Arguments:
  53. none
  54. Return value:
  55. none
  56. --*/
  57. {
  58. g_FirstDomain = NULL;
  59. PoolMemDestroyPool (g_DomainPool);
  60. }
  61. PCWSTR
  62. pReturnDomainFromEnum (
  63. IN PACCT_ENUM EnumPtr
  64. )
  65. /*++
  66. Routine Description:
  67. Common code for ListFirstDomain and ListNextDomain.
  68. Implements return parameter check.
  69. Arguments:
  70. EnumPtr - The current enumeration pointer supplied by
  71. ListFirstDomain and ListNextDomain.
  72. Return value:
  73. A pointer to the domain name allocated in our private
  74. pool, or NULL if no more domains remain.
  75. --*/
  76. {
  77. if (!EnumPtr->DomainPtr) {
  78. return NULL;
  79. }
  80. return EnumPtr->DomainPtr->Domain;
  81. }
  82. /*++
  83. Routine Description:
  84. ListFirstDomain and ListNextDomain are enumerators that list
  85. trusted domains added by BuildDomainList. They return a
  86. pointer to the domain name (managed by a private pool).
  87. The enumeration structure can be passed to all other functions
  88. that take a DomainEnumPtr as a parameter.
  89. Arguments:
  90. DomainEnumPtr - A pointer to a caller-allocated ACCT_ENUM
  91. structure, typically allocated on the stack.
  92. It does not need to be initialized.
  93. Return value:
  94. A pointer to the domain name, or NULL if no more domains
  95. exist in the list.
  96. --*/
  97. PCWSTR
  98. ListFirstDomain (
  99. OUT PACCT_ENUM DomainEnumPtr
  100. )
  101. {
  102. DomainEnumPtr->DomainPtr = g_FirstDomain;
  103. return pReturnDomainFromEnum (DomainEnumPtr);
  104. }
  105. PCWSTR
  106. ListNextDomain (
  107. IN OUT PACCT_ENUM DomainEnumPtr
  108. )
  109. {
  110. DomainEnumPtr->DomainPtr = DomainEnumPtr->DomainPtr->Next;
  111. return pReturnDomainFromEnum (DomainEnumPtr);
  112. }
  113. BOOL
  114. FindDomainInList (
  115. OUT PACCT_ENUM DomainEnumPtr,
  116. IN PCWSTR DomainToFind
  117. )
  118. /*++
  119. Routine Description:
  120. FindDomainInList searches (sequentially) through all trusted
  121. domains for the specified domain. If found, enumeration
  122. stops and TRUE is returned. If not found, the enumeration
  123. pointer is invalid and FALSE is returned.
  124. Use this function to obtain an enumeration pointer that is
  125. used in subsequent calls to the user list.
  126. The search is case-insensitive.
  127. Arguments:
  128. DomainEnumPtr - An uninitialized, caller-allocated ACCT_ENUM
  129. structure, typically allocated on the stack.
  130. When a search match is found, this structure
  131. can be used with any other function that
  132. requires a DomainEnumPtr.
  133. DomainToFind - The name of the domain to find.
  134. Return value:
  135. TRUE if a match was found (and DomainEnumPtr is valid), or
  136. FALSE if a match was not found (and DomainEnumPtr is not
  137. valid).
  138. --*/
  139. {
  140. PCWSTR DomainName;
  141. DomainName = ListFirstDomain (DomainEnumPtr);
  142. while (DomainName) {
  143. if (StringIMatchW (DomainName, DomainToFind)) {
  144. return TRUE;
  145. }
  146. DomainName = ListNextDomain (DomainEnumPtr);
  147. }
  148. return FALSE;
  149. }
  150. PCWSTR
  151. pReturnUserFromEnum (
  152. IN PACCT_ENUM UserEnumPtr
  153. )
  154. /*++
  155. Routine Description:
  156. Implements common code for ListFirstUserInDomain and
  157. ListNextUserInDomain. Performs return parameter validation.
  158. Arguments:
  159. UserEnumPtr - The current enum pointer supplied by
  160. ListFirstUserInDomain or ListNextUserInDomain.
  161. Return value:
  162. The name of the user being enumerated (not domain-qualified),
  163. or NULL if no more users exist in the domain.
  164. --*/
  165. {
  166. if (UserEnumPtr->UserPtr) {
  167. return UserEnumPtr->UserPtr->User;
  168. }
  169. return NULL;
  170. }
  171. /*++
  172. Routine Description:
  173. ListFirstUserInDomain and ListNextUserInDomain enumerate all
  174. users in the specified domain.
  175. Arguments:
  176. DomainEnumPtr - The caller-allocated ACCT_ENUM structure that
  177. has been initialized by a domain lookup function
  178. above.
  179. UserEnumPtr - Used to keep track of the current user. May be
  180. the same pointer as DomainEnumPtr.
  181. Return value:
  182. The name of the user being enumerated (not domain-qualified),
  183. or NULL if no more users exist in the domain.
  184. --*/
  185. PCWSTR
  186. ListFirstUserInDomain (
  187. IN PACCT_ENUM DomainEnumPtr,
  188. OUT PACCT_ENUM UserEnumPtr
  189. )
  190. {
  191. UserEnumPtr->UserPtr = DomainEnumPtr->DomainPtr->FirstUserPtr;
  192. return pReturnUserFromEnum (UserEnumPtr);
  193. }
  194. PCWSTR
  195. ListNextUserInDomain (
  196. IN OUT PACCT_ENUM UserEnumPtr
  197. )
  198. {
  199. if (UserEnumPtr->UserPtr) {
  200. UserEnumPtr->UserPtr = UserEnumPtr->UserPtr->Next;
  201. } else {
  202. UserEnumPtr->UserPtr = UserEnumPtr->DomainPtr->FirstUserPtr;
  203. }
  204. return pReturnUserFromEnum (UserEnumPtr);
  205. }
  206. BOOL
  207. IsTrustedDomain (
  208. IN PACCT_ENUM DomainEnumPtr
  209. )
  210. /*++
  211. Routine Description:
  212. Returns TRUE if the domain is an officially trusted domain,
  213. or FALSE if the domain is an artificially added domain. The
  214. account lookup code adds artificial domains to track the
  215. state of users. For example, the domain \unknown is used
  216. to track users who need auto-lookup. The domain \failed is
  217. used to track users who aren't in the domain they were
  218. expected to be in. All artifical domains start with a
  219. backslash.
  220. Arguments:
  221. DomainEnumPtr - Specifies the domain to examine. This structure
  222. must be the return of a domain enumeration
  223. function above.
  224. Return value:
  225. TRUE - The domain is a trusted domain
  226. FALSE - The domain is not really a domain but is instead an
  227. artifically added domain
  228. --*/
  229. {
  230. PCWSTR Domain;
  231. Domain = DomainEnumPtr->DomainPtr->Domain;
  232. //
  233. // Test domain name to see if it is one of the reserved names
  234. //
  235. if (*Domain == TEXT('\\')) {
  236. return FALSE;
  237. }
  238. return TRUE;
  239. }
  240. BOOL
  241. FindUserInDomain (
  242. IN PACCT_ENUM DomainEnumPtr,
  243. OUT PACCT_ENUM UserEnumPtr,
  244. IN PCWSTR UserToFind
  245. )
  246. /*++
  247. Routine Description:
  248. Uses ListFirstUserInDomain and ListNextUserInDomain to
  249. sequentially search for a user. The search is case-insensitive.
  250. Arguments:
  251. DomainEnumPtr - Specifies the domain to search. This structure
  252. must be the return of a domain enumeration function
  253. above.
  254. UserEnumPtr - Receives the results of the search if a user match
  255. is found. Can be the same as DomainEnumPtr.
  256. UserToFind - Specifies the name of the user to find (not
  257. domain-qualified).
  258. Return value:
  259. TRUE - A match was found and UserEnumPtr is valid
  260. FALSE - A match was not found and UserEnumPtr is not valid
  261. --*/
  262. {
  263. PCWSTR UserName;
  264. UserName = ListFirstUserInDomain (DomainEnumPtr, UserEnumPtr);
  265. while (UserName) {
  266. if (StringIMatchW (UserName, UserToFind)) {
  267. return TRUE;
  268. }
  269. UserName = ListNextUserInDomain (UserEnumPtr);
  270. }
  271. return FALSE;
  272. }
  273. INT
  274. CountUsersInDomain (
  275. IN PACCT_ENUM DomainEnumPtr
  276. )
  277. /*++
  278. Routine Description:
  279. Returns the number of users in our domain enumeration structure.
  280. Arguments:
  281. DomainEnumPtr - Specifies the domain to search. This structure
  282. must be the return of a domain enumeration function
  283. above.
  284. Return value:
  285. The count of the users in the domain.
  286. --*/
  287. {
  288. return DomainEnumPtr->DomainPtr->UserCount;
  289. }
  290. VOID
  291. AddDomainToList (
  292. IN PCWSTR Domain
  293. )
  294. /*++
  295. Routine Description:
  296. Allows domains to be added to the list of trusted domains. Normally,
  297. BuildDomainList is the only caller to this API, because it is the
  298. one who knows what the trusted domains are. However, artificial
  299. domains are added in other places through this call.
  300. Arguments:
  301. Domain - Specifies the name of the domain to add
  302. Return value:
  303. none
  304. --*/
  305. {
  306. PACCT_DOMAINS NewDomain;
  307. DEBUGMSG ((DBG_ACCTLIST, "Adding domain '%s' to domain list", Domain));
  308. NewDomain = (PACCT_DOMAINS) PoolMemGetAlignedMemory (
  309. g_DomainPool,
  310. sizeof (ACCT_DOMAINS)
  311. );
  312. ZeroMemory (NewDomain, sizeof (ACCT_DOMAINS));
  313. NewDomain->Next = g_FirstDomain;
  314. g_FirstDomain = NewDomain;
  315. NewDomain->Domain = PoolMemDuplicateString (g_DomainPool, Domain);
  316. }
  317. VOID
  318. pLinkUser (
  319. IN PACCT_USERS UserPtr,
  320. IN PACCT_DOMAINS DomainPtr
  321. )
  322. /*++
  323. Routine Description:
  324. The memory structures in this file are linked-list based. There
  325. is a linked-list of domains, and for each domain there is a linked-
  326. list of users. Each user has a linked list of possible domains.
  327. The linked lists are designed to be changed while enumerations are
  328. in progress.
  329. This function performs the simple link operation for the user list.
  330. Arguments:
  331. UserPtr - A pointer to the internally maintained ACCT_USERS structure.
  332. DomainPtr - Specifies the domain in which UserPtr is linked to.
  333. Return value:
  334. none
  335. --*/
  336. {
  337. UserPtr->Next = DomainPtr->FirstUserPtr;
  338. if (UserPtr->Next) {
  339. UserPtr->Next->Prev = UserPtr;
  340. }
  341. DomainPtr->FirstUserPtr = UserPtr;
  342. UserPtr->DomainPtr = DomainPtr;
  343. DomainPtr->UserCount++;
  344. }
  345. BOOL
  346. AddUserToDomainList (
  347. IN PCWSTR User,
  348. IN PCWSTR Domain
  349. )
  350. /*++
  351. Routine Description:
  352. This function searches for the domain name specified
  353. and adds the user to the user list for that domain. If
  354. the domain cannot be found, the function fails.
  355. Arguments:
  356. User - Specifies the name of the user to add
  357. Domain - Specifies the name of the domain that the user
  358. is added to
  359. Return value:
  360. TRUE if the user was added successfully, or FALSE if the
  361. domain is not a trusted domain.
  362. --*/
  363. {
  364. ACCT_ENUM e;
  365. PACCT_DOMAINS DomainPtr;
  366. PACCT_USERS NewUser;
  367. //
  368. // Find Domain (it must exist in the list)
  369. //
  370. if (!FindDomainInList (&e, Domain)) {
  371. return FALSE;
  372. }
  373. DomainPtr = e.DomainPtr;
  374. //
  375. // Allocate structure for the user
  376. //
  377. NewUser = (PACCT_USERS) PoolMemGetAlignedMemory (
  378. g_DomainPool,
  379. sizeof (ACCT_USERS)
  380. );
  381. ZeroMemory (NewUser, sizeof (ACCT_USERS));
  382. pLinkUser (NewUser, DomainPtr);
  383. NewUser->User = PoolMemDuplicateString (g_DomainPool, User);
  384. return TRUE;
  385. }
  386. VOID
  387. pDelinkUser (
  388. IN PACCT_USERS UserPtr
  389. )
  390. /*++
  391. Routine Description:
  392. The memory structures in this file are linked-list based. There
  393. is a linked-list of domains, and for each domain there is a linked-
  394. list of users. Each user has a linked list of possible domains.
  395. The linked lists are designed to be changed while enumerations are
  396. in progress.
  397. This function performs the simple delink operation for the user list.
  398. Arguments:
  399. UserPtr - A pointer to the internally maintained ACCT_USERS structure.
  400. Return value:
  401. none
  402. --*/
  403. {
  404. if (UserPtr->Prev) {
  405. UserPtr->Prev->Next = UserPtr->Next;
  406. } else {
  407. UserPtr->DomainPtr->FirstUserPtr = UserPtr->Next;
  408. }
  409. if (UserPtr->Next) {
  410. UserPtr->Next->Prev = UserPtr->Prev;
  411. }
  412. UserPtr->DomainPtr->UserCount--;
  413. }
  414. VOID
  415. DeleteUserFromDomainList (
  416. IN PACCT_ENUM UserEnumPtr
  417. )
  418. /*++
  419. Routine Description:
  420. Performs a delete operation for a user in a domain's user list.
  421. The memory for this user is not freed right away, because doing
  422. so may cause enumeration positions to become invalid. Instead,
  423. the links are adjusted to skip over this user.
  424. Memory is freed at termination.
  425. Arguments:
  426. UserEnumPtr - A pointer to the user to delete, obtained by calling
  427. a user enumeration or user search function that
  428. returns UserEnumPtr as an OUT.
  429. Return value:
  430. none
  431. --*/
  432. {
  433. //
  434. // Don't actually delete, just delink. This allows all in-progress
  435. // enumerations to continue working.
  436. //
  437. pDelinkUser (UserEnumPtr->UserPtr);
  438. }
  439. BOOL
  440. MoveUserToNewDomain (
  441. IN OUT PACCT_ENUM UserEnumPtr,
  442. IN PCWSTR NewDomain
  443. )
  444. /*++
  445. Routine Description:
  446. Moves a user from one domain to another by adjusting links only.
  447. The current enumeration pointer is adjusted to point to the previous
  448. user so enumeration can continue. This function may change the
  449. behavoir other enumerations that are pointing to this user, so
  450. be careful. It will never break an enumeration though.
  451. Arguments:
  452. UserEnumPtr - A pointer to the user to move, obtained by calling a
  453. user enumeration or user search function that returns
  454. UserEnumPtr as an OUT.
  455. NewDomain - The name of the new domain to move the user to.
  456. Return value:
  457. TRUE if NewDomain is a trusted domain, or FALSE if it is not.
  458. The user can only be moved to domains in the trust list.
  459. --*/
  460. {
  461. ACCT_ENUM e;
  462. PACCT_DOMAINS DomainPtr;
  463. PACCT_DOMAINS OrgDomainPtr;
  464. PACCT_USERS PrevUser;
  465. //
  466. // Find NewDomain (it must exist in the list)
  467. //
  468. if (!FindDomainInList (&e, NewDomain)) {
  469. return FALSE;
  470. }
  471. DomainPtr = e.DomainPtr;
  472. OrgDomainPtr = UserEnumPtr->UserPtr->DomainPtr;
  473. //
  474. // Remove user from original domain
  475. //
  476. PrevUser = UserEnumPtr->UserPtr->Prev;
  477. pDelinkUser (UserEnumPtr->UserPtr);
  478. //
  479. // Add user to new domain
  480. //
  481. pLinkUser (UserEnumPtr->UserPtr, DomainPtr);
  482. if (!PrevUser) {
  483. UserEnumPtr->DomainPtr = OrgDomainPtr;
  484. }
  485. UserEnumPtr->UserPtr = PrevUser;
  486. return TRUE;
  487. }
  488. VOID
  489. UserMayBeInDomain (
  490. IN PACCT_ENUM UserEnumPtr,
  491. IN PACCT_ENUM DomainEnumPtr
  492. )
  493. /*++
  494. Routine Description:
  495. Provides the caller with a way to flag a domain as a possible
  496. domain holding the account. During search, all trusted
  497. domains are queried, and because an account can be in more
  498. than one, a list of possible domains is developed. If the
  499. final list of possible domains has only one entry, that
  500. domain is used for the user. Otherwise, a dialog is presented,
  501. allowing the installer to choose an action to take for the user.
  502. The action can be to retry, make a local account, or select
  503. one of the possible domains.
  504. Arguments:
  505. UserEnumPtr - Specifies the user that may be in a domain
  506. DomainEnumPtr - Specifies the domain that the user may be in
  507. Return value:
  508. none
  509. --*/
  510. {
  511. PACCT_POSSIBLE_DOMAINS PossibleDomainPtr;
  512. PossibleDomainPtr = (PACCT_POSSIBLE_DOMAINS)
  513. PoolMemGetAlignedMemory (
  514. g_DomainPool,
  515. sizeof (ACCT_POSSIBLE_DOMAINS)
  516. );
  517. PossibleDomainPtr->DomainPtr = DomainEnumPtr->DomainPtr;
  518. PossibleDomainPtr->Next = UserEnumPtr->UserPtr->FirstPossibleDomain;
  519. UserEnumPtr->UserPtr->FirstPossibleDomain = PossibleDomainPtr;
  520. UserEnumPtr->UserPtr->PossibleDomains++;
  521. }
  522. VOID
  523. ClearPossibleDomains (
  524. IN PACCT_ENUM UserEnumPtr
  525. )
  526. /*++
  527. Routine Description:
  528. Provides the caller with a way to reset the possible domain
  529. list. This is required if the installer chose to retry the search.
  530. Arguments:
  531. UserEnumPtr - Specifies the user to reset
  532. Return value:
  533. none
  534. --*/
  535. {
  536. PACCT_POSSIBLE_DOMAINS This, Next;
  537. This = UserEnumPtr->UserPtr->FirstPossibleDomain;
  538. while (This) {
  539. Next = This->Next;
  540. PoolMemReleaseMemory (g_DomainPool, This);
  541. This = Next;
  542. }
  543. UserEnumPtr->UserPtr->FirstPossibleDomain = 0;
  544. UserEnumPtr->UserPtr->PossibleDomains = 0;
  545. }
  546. PCWSTR
  547. pReturnPossibleDomainFromEnum (
  548. IN PACCT_ENUM EnumPtr
  549. )
  550. /*++
  551. Routine Description:
  552. Common code for ListFirstPossibleDomain and ListNextPossibleDomain.
  553. Implements return parameter checking.
  554. Arguments:
  555. EnumPtr - The current enumeration pointer as supplied by
  556. ListFirstPossibleDomain or ListNextPossibleDomain.
  557. Return value:
  558. The name of the domain enumerated, or NULL if no more possible
  559. domains exist. (A possible domain is one that a user may or
  560. may not be in, but a matching account was found in the domain.)
  561. --*/
  562. {
  563. if (EnumPtr->PossibleDomainPtr) {
  564. EnumPtr->DomainPtr = EnumPtr->PossibleDomainPtr->DomainPtr;
  565. return EnumPtr->DomainPtr->Domain;
  566. }
  567. return NULL;
  568. }
  569. /*++
  570. Routine Description:
  571. ListFirstPossibleDomain and ListNextPossibleDomain are enumerators
  572. that list domains added by UserMayBeInDomain. They return a
  573. pointer to the domain name (managed by a private pool).
  574. A possible domain list is maintained to allow the installer to
  575. choose between multiple domains when a user has an account on
  576. more than one domain.
  577. Arguments:
  578. UserEnumPtr - A pointer to a caller-allocated ACCT_ENUM
  579. structure, typically allocated on the stack.
  580. It must be initialized by a user enum or user
  581. search function.
  582. PossibleDomainEnumPtr - Maintains the state of the possible
  583. domain enumeration. It can be the
  584. same pointer as UserEnumPtr.
  585. Return value:
  586. A pointer to the possible domain name, or NULL if no more domains
  587. exist in the list.
  588. --*/
  589. PCWSTR
  590. ListFirstPossibleDomain (
  591. IN PACCT_ENUM UserEnumPtr,
  592. OUT PACCT_ENUM PossibleDomainEnumPtr
  593. )
  594. {
  595. PossibleDomainEnumPtr->PossibleDomainPtr = UserEnumPtr->UserPtr->FirstPossibleDomain;
  596. return pReturnPossibleDomainFromEnum (PossibleDomainEnumPtr);
  597. }
  598. PCWSTR
  599. ListNextPossibleDomain (
  600. IN OUT PACCT_ENUM PossibleDomainEnumPtr
  601. )
  602. {
  603. PossibleDomainEnumPtr->PossibleDomainPtr = PossibleDomainEnumPtr->
  604. PossibleDomainPtr->Next;
  605. return pReturnPossibleDomainFromEnum (PossibleDomainEnumPtr);
  606. }
  607. INT
  608. CountPossibleDomains (
  609. IN PACCT_ENUM UserEnumPtr
  610. )
  611. /*++
  612. Routine Description:
  613. Returns the number of possible domains in a user.
  614. Arguments:
  615. UserEnumPtr - Specifies the user to examine. This structure
  616. must be the return of a user enumeration or user
  617. search function above.
  618. Return value:
  619. The count of the possible domains for a user.
  620. --*/
  621. {
  622. return UserEnumPtr->UserPtr->PossibleDomains;
  623. }
  624. NET_API_STATUS
  625. pGetDcNameAllowingRetry (
  626. IN PCWSTR DomainName,
  627. OUT PWSTR ServerName,
  628. IN BOOL ForceNewServer
  629. )
  630. /*++
  631. Routine Description:
  632. Implements NetGetDCName, but provides a retry capability.
  633. Arguments:
  634. DomainName - Specifies the domain to obtain the server name for
  635. ServerName - Specifies a buffer to receive the name of the server
  636. ForceNewServer - Specifies TRUE if the function should obtain a new
  637. server for the domain. Specifies FALSE if the
  638. function should use any existing connection if
  639. available.
  640. Return value:
  641. Win32 error code indicating outcome
  642. --*/
  643. {
  644. NET_API_STATUS nas;
  645. UINT ShortCircuitRetries = 1;
  646. //PCWSTR ArgArray[1];
  647. do {
  648. nas = GetAnyDC (
  649. DomainName,
  650. ServerName,
  651. ForceNewServer
  652. );
  653. if (nas != NO_ERROR) {
  654. //
  655. // Short-circuited, so user isn't bothered. The alternate behavior
  656. // is to prompt the user for retry when any domain is down. (See
  657. // RetryMessageBox code below.)
  658. //
  659. ShortCircuitRetries--;
  660. if (!ShortCircuitRetries) {
  661. DEBUGMSG ((DBG_WARNING, "Unable to connect to domain %s", DomainName));
  662. break;
  663. }
  664. #if 0
  665. ArgArray[0] = DomainName;
  666. if (!RetryMessageBox (MSG_GETPRIMARYDC_RETRY, ArgArray)) {
  667. DEBUGMSG ((DBG_WARNING, "Unable to connect to domain %s; user chose to cancel", DomainName));
  668. break;
  669. }
  670. #endif
  671. ForceNewServer = TRUE;
  672. }
  673. } while (nas != NO_ERROR);
  674. return nas;
  675. }
  676. VOID
  677. pDisableDomain (
  678. IN OUT PACCT_DOMAINS DomainPtr
  679. )
  680. /*++
  681. Routine Description:
  682. Disable the specified domain.
  683. Arguments:
  684. DomainPtr - A pointer to the internally maintained ACCT_DOMAINS
  685. structure. This structure is updated to contain
  686. an empty server name upon return.
  687. Return value:
  688. None
  689. --*/
  690. {
  691. g_DomainProblem = TRUE;
  692. if (DomainPtr->Server && *DomainPtr->Server) {
  693. PoolMemReleaseMemory (g_DomainPool, (PVOID) DomainPtr->Server);
  694. }
  695. DomainPtr->Server = S_EMPTY;
  696. }
  697. NET_API_STATUS
  698. pNetUseAddAllowingRetry (
  699. IN OUT PACCT_DOMAINS DomainPtr
  700. )
  701. /*++
  702. Routine Description:
  703. Implements NetUseAdd, but provides a retry capability.
  704. Arguments:
  705. DomainPtr - A pointer to the internally maintained ACCT_DOMAINS
  706. structure. This structure is updated to contain
  707. the server name upon success.
  708. Return value:
  709. Win32 error code indicating outcome
  710. --*/
  711. {
  712. NET_API_STATUS rc;
  713. DWORD DontCare;
  714. PCWSTR ReplacementName;
  715. NET_API_STATUS nas;
  716. USE_INFO_2 ui2;
  717. WCHAR LocalShareName[72];
  718. WCHAR NewServerBuf[MAX_SERVER_NAMEW];
  719. ReplacementName = NULL;
  720. do {
  721. //
  722. // Initialize USE_INFO_2 structure
  723. //
  724. ZeroMemory (&ui2, sizeof (ui2));
  725. StringCopyW (LocalShareName, ReplacementName ? ReplacementName : DomainPtr->Server);
  726. StringCatW (LocalShareName, L"\\IPC$");
  727. ui2.ui2_remote = LocalShareName;
  728. ui2.ui2_asg_type = USE_IPC;
  729. rc = NetUseAdd (NULL, 2, (PBYTE) &ui2, &DontCare);
  730. //
  731. // If NetUseAdd fails, give the user a chance to retry with a different server
  732. //
  733. if (rc != NO_ERROR) {
  734. PCWSTR ArgArray[2];
  735. DEBUGMSG ((
  736. DBG_WARNING,
  737. "User was alerted to problem establishing nul session to %s (domain %s), rc=%u",
  738. DomainPtr->Server,
  739. DomainPtr->Domain,
  740. rc
  741. ));
  742. ArgArray[0] = DomainPtr->Server;
  743. ArgArray[1] = DomainPtr->Domain;
  744. if (!RetryMessageBox (MSG_NULSESSION_RETRY, ArgArray)) {
  745. DEBUGMSG ((DBG_WARNING, "Unable to connect to domain %s; user chose to cancel", DomainPtr->Domain));
  746. pDisableDomain (DomainPtr);
  747. ReplacementName = NULL;
  748. break;
  749. }
  750. if (ReplacementName) {
  751. ReplacementName = NULL;
  752. }
  753. //
  754. // Get a new server because current server is not responding. If we fail to
  755. // obtain a server name, allow the user to try again.
  756. //
  757. do {
  758. nas = GetAnyDC (DomainPtr->Domain, NewServerBuf, TRUE);
  759. if (nas != NO_ERROR) {
  760. PCWSTR ArgArray2[1];
  761. DEBUGMSG ((DBG_WARNING, "User was alerted to problem locating server for domain %s", DomainPtr->Domain));
  762. ArgArray2[0] = DomainPtr->Domain;
  763. if (!RetryMessageBox (MSG_GETANYDC_RETRY, ArgArray2)) {
  764. DEBUGMSG ((DBG_WARNING, "Unable to find a server for domain %s; user chose to cancel", DomainPtr->Domain));
  765. // Disable domain and return an error
  766. pDisableDomain (DomainPtr);
  767. ReplacementName = NULL;
  768. break;
  769. }
  770. } else {
  771. ReplacementName = NewServerBuf;
  772. }
  773. } while (nas != NO_ERROR);
  774. }
  775. } while (rc != NO_ERROR);
  776. //
  777. // If ReplacementName is not NULL, we need to free the buffer. Also, if the
  778. // NetUseAdd call succeeded, we now have to use another server to query the
  779. // domain.
  780. //
  781. if (ReplacementName) {
  782. if (rc == NO_ERROR) {
  783. if (DomainPtr->Server && *DomainPtr->Server) {
  784. PoolMemReleaseMemory (g_DomainPool, (PVOID) DomainPtr->Server);
  785. }
  786. DomainPtr->Server = PoolMemDuplicateString (g_DomainPool, ReplacementName);
  787. }
  788. }
  789. return rc;
  790. }
  791. PCWSTR
  792. pLsaStringToCString (
  793. IN PLSA_UNICODE_STRING UnicodeString,
  794. OUT PWSTR Buffer
  795. )
  796. /*++
  797. Routine Description:
  798. A safe string extraction that takes the string in UnicodeString
  799. and copies it to Buffer. The caller must ensure Buffer is
  800. big enough.
  801. Arguments:
  802. UnicodeString - Specifies the source string to convert
  803. Buffer - Specifies the buffer that receives the converted string
  804. Return value:
  805. The Buffer pointer
  806. --*/
  807. {
  808. StringCopyABW (
  809. Buffer,
  810. UnicodeString->Buffer,
  811. (PWSTR) ((PBYTE) UnicodeString->Buffer + UnicodeString->Length)
  812. );
  813. return Buffer;
  814. }
  815. BOOL
  816. BuildDomainList(
  817. VOID
  818. )
  819. /*++
  820. Routine Description:
  821. Creates a trusted domain list by:
  822. 1. Determining the computer domain in which the machine participates in
  823. 2. Opening the DC's policy
  824. 3. Querying the trust list
  825. 4. Adding it to our internal domain list (ACCT_DOMAINS)
  826. This function will fail if the machine does not participate in a domain, or
  827. if the domain controller cannot be contacted.
  828. Arguments:
  829. none
  830. Return value:
  831. TRUE if the trust list was completely built, or FALSE if an error occurred
  832. and the trust list is incomplete and is probably empty. GetLastError
  833. provides the failure code.
  834. --*/
  835. {
  836. LSA_HANDLE PolicyHandle;
  837. BOOL DomainControllerFlag = FALSE;
  838. NTSTATUS Status;
  839. NET_API_STATUS nas = NO_ERROR;
  840. BOOL b = FALSE;
  841. WCHAR PrimaryDomainName[MAX_SERVER_NAMEW];
  842. PPOLICY_PRIMARY_DOMAIN_INFO PrimaryDomain;
  843. WCHAR ServerName[MAX_SERVER_NAMEW];
  844. #if DOMAINCONTROLLER
  845. PPOLICY_ACCOUNT_DOMAIN_INFO AccountDomain;
  846. #endif
  847. if (!IsMemberOfDomain()) {
  848. DEBUGMSG ((DBG_VERBOSE, "Workstation does not participate in a domain."));
  849. return FALSE;
  850. }
  851. //
  852. // Add special domains used for management of user state
  853. //
  854. // users whos domain is not known
  855. AddDomainToList (S_UNKNOWN_DOMAIN);
  856. // users whos domain was known but the account doesn't exist
  857. AddDomainToList (S_FAILED_DOMAIN);
  858. //
  859. // Open the policy on this machine
  860. //
  861. Status = OpenPolicy (
  862. NULL,
  863. POLICY_VIEW_LOCAL_INFORMATION,
  864. &PolicyHandle
  865. );
  866. if (Status != STATUS_SUCCESS) {
  867. SetLastError (LsaNtStatusToWinError (Status));
  868. return FALSE;
  869. }
  870. #if DOMAINCONTROLLER // disabled, but may be needed for DC installation
  871. //
  872. // Obtain the AccountDomain, which is common to all three cases
  873. //
  874. Status = LsaQueryInformationPolicy (
  875. PolicyHandle,
  876. PolicyAccountDomainInformation,
  877. &AccountDomain
  878. );
  879. if (Status != STATUS_SUCCESS)
  880. goto cleanup;
  881. //
  882. // Note: AccountDomain->DomainSid will contain binary Sid
  883. //
  884. AddDomainToList (pLsaStringToCString (&AccountDomain->DomainName, PrimaryDomainName));
  885. //
  886. // Free memory allocated for account domain
  887. //
  888. LsaFreeMemory (AccountDomain);
  889. //
  890. // Find out if this machine is a domain controller
  891. //
  892. if (!IsDomainController (NULL, &DomainControllerFlag)) {
  893. // IsDomainController couldn't find the answer
  894. goto cleanup;
  895. }
  896. #endif
  897. // If not a domain controller...
  898. if(!DomainControllerFlag) {
  899. //
  900. // Get the primary domain
  901. //
  902. Status = LsaQueryInformationPolicy (
  903. PolicyHandle,
  904. POLICY_PRIMARY_DOMAIN_INFORMATION,
  905. &PrimaryDomain
  906. );
  907. if (Status != STATUS_SUCCESS) {
  908. goto cleanup;
  909. }
  910. //
  911. // If the primary domain SID is NULL, we are a non-member, and
  912. // our work is done.
  913. //
  914. if (!PrimaryDomain->Sid) {
  915. LsaFreeMemory (PrimaryDomain);
  916. b = TRUE;
  917. goto cleanup;
  918. }
  919. //
  920. // We found our computer domain, add it to our list
  921. //
  922. AddDomainToList (pLsaStringToCString (&PrimaryDomain->Name, PrimaryDomainName));
  923. LsaFreeMemory (PrimaryDomain);
  924. //
  925. // Get the primary domain controller computer name. If the API fails,
  926. // alert the user and allow them to retry. ServerName is allocated by
  927. // the Net APIs.
  928. //
  929. nas = pGetDcNameAllowingRetry (PrimaryDomainName, ServerName, FALSE);
  930. if (nas != NO_ERROR) {
  931. goto cleanup;
  932. }
  933. //
  934. // Re-enable the code to open the policy on the domain controller
  935. //
  936. //
  937. // Close the prev policy handle, because we don't need it anymore.
  938. //
  939. LsaClose (PolicyHandle);
  940. PolicyHandle = INVALID_HANDLE_VALUE; // invalidate handle value
  941. //
  942. // Open the policy on the domain controller
  943. //
  944. Status = OpenPolicy(
  945. ServerName,
  946. POLICY_VIEW_LOCAL_INFORMATION,
  947. &PolicyHandle
  948. );
  949. if (Status != STATUS_SUCCESS) {
  950. goto cleanup;
  951. }
  952. }
  953. //
  954. // Build additional trusted logon domain(s) list and
  955. // indicate if successful
  956. //
  957. b = pAddAllLogonDomains (DomainControllerFlag ? NULL : ServerName);
  958. cleanup:
  959. //
  960. // Close the policy handle
  961. //
  962. if (PolicyHandle != INVALID_HANDLE_VALUE && PolicyHandle) {
  963. LsaClose (PolicyHandle);
  964. }
  965. if (!b) {
  966. if (Status != STATUS_SUCCESS)
  967. SetLastError (LsaNtStatusToWinError (Status));
  968. else if (nas != NO_ERROR)
  969. SetLastError (nas);
  970. }
  971. return b;
  972. }
  973. BOOL
  974. pAddAllLogonDomains (
  975. IN PCTSTR DCName OPTIONAL
  976. )
  977. {
  978. NET_API_STATUS rc;
  979. PWSTR Domains;
  980. PCWSTR p;
  981. rc = NetEnumerateTrustedDomains ((PTSTR)DCName, &Domains);
  982. if (rc != ERROR_SUCCESS) {
  983. SetLastError (rc);
  984. return FALSE;
  985. }
  986. for (p = Domains ; *p ; p = GetEndOfStringW (p) + 1) {
  987. AddDomainToList (p);
  988. }
  989. NetApiBufferFree (Domains);
  990. return TRUE;
  991. }
  992. BOOL
  993. pEstablishNulSessionWithDomain (
  994. IN OUT PACCT_DOMAINS DomainPtr,
  995. IN BOOL ForceNewServer
  996. )
  997. /*++
  998. Routine Description:
  999. If a nul session has not been established with a domain, this
  1000. routine finds a server in the domain and establishes the nul
  1001. session. Every network call is wrapped within a retry loop,
  1002. so the user can retry when network failures occur.
  1003. Arguments:
  1004. DomainPtr - Specifies a pointer to our private domain structure.
  1005. This structure indicates the domain to establish
  1006. the nul session with, and it receives the name
  1007. of the server upon successful connection.
  1008. ForceNewServer - Specifies TRUE if the function should obtain a new
  1009. server for the domain.
  1010. Return value:
  1011. TRUE if a nul session was established, or FALSE if an
  1012. error occurred while establishing the nul session. GetLastError
  1013. provides a failure code.
  1014. --*/
  1015. {
  1016. NET_API_STATUS nas;
  1017. WCHAR ServerName[MAX_SERVER_NAMEW];
  1018. DWORD rc;
  1019. //
  1020. // Release old name if necessary
  1021. //
  1022. if (ForceNewServer && DomainPtr->Server) {
  1023. if (*DomainPtr->Server) {
  1024. PoolMemReleaseMemory (g_DomainPool, (PVOID) DomainPtr->Server);
  1025. }
  1026. DomainPtr->Server = NULL;
  1027. }
  1028. //
  1029. // Obtain a server name if necessary
  1030. //
  1031. if (!DomainPtr->Server) {
  1032. //
  1033. // Get the primary DC name
  1034. //
  1035. nas = pGetDcNameAllowingRetry (DomainPtr->Domain, ServerName, ForceNewServer);
  1036. if (nas != NO_ERROR) {
  1037. pDisableDomain (DomainPtr);
  1038. return FALSE;
  1039. }
  1040. DomainPtr->Server = PoolMemDuplicateString (g_DomainPool, ServerName);
  1041. //
  1042. // Connect to the server, possibly finding a server that will
  1043. // service us.
  1044. //
  1045. rc = pNetUseAddAllowingRetry (DomainPtr);
  1046. if (rc != NO_ERROR) {
  1047. //
  1048. // Remove the server name because we never connected
  1049. //
  1050. pDisableDomain (DomainPtr);
  1051. SetLastError (rc);
  1052. LOG ((LOG_ERROR, "NetUseAdd failed"));
  1053. return FALSE;
  1054. }
  1055. }
  1056. return *DomainPtr->Server != 0;
  1057. }
  1058. BOOL
  1059. QueryDomainForUser (
  1060. IN PACCT_ENUM DomainEnumPtr,
  1061. IN PACCT_ENUM UserEnumPtr
  1062. )
  1063. /*++
  1064. Routine Description:
  1065. Checks the domain controller for a user account via NetUserGetInfo.
  1066. Arguments:
  1067. DomainEnumPtr - Specifies the domain to search. This structure
  1068. must be the return of a domain enumeration function
  1069. above.
  1070. UserEnumPtr - Specifies the user to look up over the network.
  1071. Return value:
  1072. TRUE if the user exists, or FALSE if the user does not exist. If
  1073. an error occurs, a retry popup appears, allowing the installer to
  1074. retry the search if necessary.
  1075. --*/
  1076. {
  1077. PACCT_DOMAINS DomainPtr;
  1078. PACCT_USERS UserPtr;
  1079. NET_API_STATUS rc;
  1080. BOOL ForceNewServer = FALSE;
  1081. TCHAR DomainQualifiedUserName[MAX_USER_NAME];
  1082. BYTE SidBuf[MAX_SID_SIZE];
  1083. DWORD SizeOfSidBuf;
  1084. TCHAR DontCareStr[MAX_SERVER_NAME];
  1085. DWORD DontCareSize;
  1086. SID_NAME_USE SidNameUse;
  1087. DomainPtr = DomainEnumPtr->DomainPtr;
  1088. UserPtr = UserEnumPtr->UserPtr;
  1089. do {
  1090. if (!pEstablishNulSessionWithDomain (DomainPtr, ForceNewServer)) {
  1091. LOG ((LOG_ERROR, "Could not query domain %s for user %s.",
  1092. DomainPtr->Domain, UserPtr->User));
  1093. return FALSE;
  1094. }
  1095. //
  1096. // Do query
  1097. //
  1098. DEBUGMSG ((DBG_ACCTLIST, "Querying %s for %s", DomainPtr->Server, UserPtr->User));
  1099. rc = NO_ERROR;
  1100. wsprintf (DomainQualifiedUserName, TEXT("%s\\%s"), DomainPtr->Domain, UserPtr->User);
  1101. SizeOfSidBuf = sizeof (SidBuf);
  1102. DontCareSize = sizeof (DontCareStr);
  1103. if (!LookupAccountName (
  1104. DomainPtr->Server,
  1105. DomainQualifiedUserName,
  1106. SidBuf,
  1107. &SizeOfSidBuf,
  1108. DontCareStr,
  1109. &DontCareSize,
  1110. &SidNameUse
  1111. )) {
  1112. rc = GetLastError();
  1113. }
  1114. if (rc != NO_ERROR && rc != ERROR_NONE_MAPPED) {
  1115. PCWSTR ArgArray2[2];
  1116. DEBUGMSG ((
  1117. DBG_WARNING,
  1118. "User was alerted to problem querying account %s (domain %s), rc=%u",
  1119. DomainPtr->Server,
  1120. DomainPtr->Domain,
  1121. rc
  1122. ));
  1123. ArgArray2[0] = DomainPtr->Server;
  1124. ArgArray2[1] = DomainPtr->Domain;
  1125. if (!RetryMessageBox (MSG_NULSESSION_RETRY, ArgArray2)) {
  1126. DEBUGMSG ((DBG_WARNING, "Unable to connect to domain %s; user chose to cancel", DomainPtr->Domain));
  1127. pDisableDomain (DomainPtr);
  1128. break;
  1129. }
  1130. ForceNewServer = TRUE;
  1131. }
  1132. } while (rc != NO_ERROR && rc != ERROR_NONE_MAPPED);
  1133. if (rc == NO_ERROR && SidNameUse != SidTypeUser) {
  1134. rc = ERROR_NONE_MAPPED;
  1135. }
  1136. if (rc != NO_ERROR && rc != ERROR_NONE_MAPPED) {
  1137. LOG ((
  1138. LOG_ERROR,
  1139. "User %s not found in %s, rc=%u",
  1140. UserPtr->User, DomainPtr->Domain,
  1141. rc
  1142. ));
  1143. }
  1144. return rc == NO_ERROR;
  1145. }
  1146. BOOL
  1147. pGetUserSecurityInfo (
  1148. IN PCWSTR User,
  1149. IN PCWSTR Domain,
  1150. IN OUT PGROWBUFFER SidBufPtr,
  1151. OUT SID_NAME_USE *UseType OPTIONAL
  1152. )
  1153. /*++
  1154. Routine Description:
  1155. A common routine that searches for a user in our private structures
  1156. and returns the SID and/or the type of user via LookupAccountName.
  1157. The lookup operation is enclosed in a retry loop.
  1158. Arguments:
  1159. User - The name of the user to get security info on
  1160. Domain - The name of the domain where the user exists. Can be
  1161. NULL for the local machine.
  1162. SidBufPtr - A pointer to a GROWBUFFER. The SID is appended to
  1163. the GROWBUFFER.
  1164. UseType - Specifies the address of a SID_NAME_USE variable, or
  1165. NULL if use type is not needed.
  1166. Return value:
  1167. TRUE if the lookup succeeded, or FALSE if an error occurred from
  1168. either establishing a nul session for a domain or looking up an
  1169. account on the domain. GetLastError provides the failure code.
  1170. --*/
  1171. {
  1172. ACCT_ENUM e;
  1173. PACCT_DOMAINS DomainPtr;
  1174. PSID Sid;
  1175. DWORD Size;
  1176. PCWSTR FullUserName = NULL;
  1177. WCHAR DomainName[MAX_SERVER_NAMEW];
  1178. DWORD DomainNameSize;
  1179. SID_NAME_USE use = 0;
  1180. DWORD End;
  1181. BOOL b = FALSE;
  1182. DWORD rc;
  1183. __try {
  1184. End = SidBufPtr->End;
  1185. if (Domain) {
  1186. //
  1187. // Domain account case -- verify domain is in trust list
  1188. //
  1189. if (!FindDomainInList (&e, Domain)) {
  1190. __leave;
  1191. }
  1192. DomainPtr = e.DomainPtr;
  1193. FullUserName = JoinPaths (Domain, User);
  1194. } else {
  1195. //
  1196. // Local account case
  1197. //
  1198. DomainPtr = NULL;
  1199. if (StringIMatch (User, g_EveryoneStr) ||
  1200. StringIMatch (User, g_NoneGroupStr) ||
  1201. StringIMatch (User, g_AdministratorsGroupStr)
  1202. ) {
  1203. FullUserName = DuplicatePathString (User, 0);
  1204. } else {
  1205. FullUserName = JoinPaths (g_ComputerName, User);
  1206. }
  1207. }
  1208. Sid = (PSID) GrowBuffer (SidBufPtr, MAX_SID_SIZE);
  1209. if (DomainPtr && !pEstablishNulSessionWithDomain (DomainPtr, FALSE)) {
  1210. LOG ((
  1211. LOG_ERROR,
  1212. "Could not query domain %s for user %s security info.",
  1213. Domain,
  1214. User
  1215. ));
  1216. __leave;
  1217. }
  1218. Size = MAX_SID_SIZE;
  1219. DomainNameSize = MAX_SERVER_NAMEW;
  1220. do {
  1221. //
  1222. // Look up account name in form of domain\user or computer\user
  1223. //
  1224. b = LookupAccountName (
  1225. DomainPtr ? DomainPtr->Server : NULL,
  1226. FullUserName,
  1227. Sid,
  1228. &Size,
  1229. DomainName,
  1230. &DomainNameSize,
  1231. &use
  1232. );
  1233. if (!b) {
  1234. rc = GetLastError();
  1235. //
  1236. // In the local account case, try the lookup again, without
  1237. // the computer name decoration. This works around a
  1238. // GetComputerName bug.
  1239. //
  1240. if (rc != ERROR_INSUFFICIENT_BUFFER) {
  1241. if (!DomainPtr) {
  1242. b = LookupAccountName (
  1243. NULL,
  1244. User,
  1245. Sid,
  1246. &Size,
  1247. DomainName,
  1248. &DomainNameSize,
  1249. &use
  1250. );
  1251. rc = GetLastError();
  1252. }
  1253. }
  1254. if (!b) {
  1255. if (rc == ERROR_INSUFFICIENT_BUFFER) {
  1256. //
  1257. // Grow the buffer if necessary, then try again
  1258. //
  1259. SidBufPtr->End = End;
  1260. Sid = (PSID) GrowBuffer (SidBufPtr, Size);
  1261. continue;
  1262. }
  1263. //
  1264. // API failed with an error
  1265. //
  1266. LOG ((
  1267. LOG_ERROR,
  1268. "Lookup Account On Network: LookupAccountName failed for %s (domain: %s)",
  1269. FullUserName,
  1270. Domain ? Domain : TEXT("*local*")
  1271. ));
  1272. //
  1273. // Ignore the error in the case of the local account "none"
  1274. //
  1275. if (StringIMatch (User, g_NoneGroupStr)) {
  1276. b = TRUE;
  1277. }
  1278. __leave;
  1279. }
  1280. }
  1281. } while (!b);
  1282. //
  1283. // We now have successfully gotten a sid. Adjust pointers, return type.
  1284. //
  1285. if (UseType) {
  1286. *UseType = use;
  1287. }
  1288. SidBufPtr->End = End + GetLengthSid (Sid);
  1289. //
  1290. // As a debugging aid, output the type
  1291. //
  1292. DEBUGMSG_IF ((use == SidTypeUser, DBG_VERBOSE, "%s is SidTypeUser", FullUserName));
  1293. DEBUGMSG_IF ((use == SidTypeGroup, DBG_VERBOSE, "%s is SidTypeGroup", FullUserName));
  1294. DEBUGMSG_IF ((use == SidTypeDomain, DBG_VERBOSE, "%s is SidTypeDomain", FullUserName));
  1295. DEBUGMSG_IF ((use == SidTypeAlias, DBG_VERBOSE, "%s is SidTypeAlias", FullUserName));
  1296. DEBUGMSG_IF ((use == SidTypeWellKnownGroup, DBG_VERBOSE, "%s is SidTypeWellKnownGroup", FullUserName));
  1297. DEBUGMSG_IF ((use == SidTypeDeletedAccount, DBG_VERBOSE, "%s is SidTypeDeletedAccount", FullUserName));
  1298. DEBUGMSG_IF ((use == SidTypeInvalid, DBG_VERBOSE, "%s is SidTypeInvalid", FullUserName));
  1299. DEBUGMSG_IF ((use == SidTypeUnknown, DBG_VERBOSE, "%s is SidTypeUnknown", FullUserName));
  1300. DEBUGMSG_IF ((use == SidTypeComputer, DBG_VERBOSE, "%s is SidTypeComputer", FullUserName));
  1301. }
  1302. __finally {
  1303. FreePathString (FullUserName);
  1304. }
  1305. return b;
  1306. }
  1307. BOOL
  1308. GetUserSid (
  1309. IN PCWSTR User,
  1310. IN PCWSTR Domain,
  1311. IN OUT PGROWBUFFER SidBufPtr
  1312. )
  1313. /*++
  1314. Routine Description:
  1315. This routine is vaild only after the domain list is perpared.
  1316. It queries a domain for a user SID.
  1317. Arguments:
  1318. User - Specifies name of user to look up
  1319. Domain - Specifies name of domain to query, or NULL for local machine
  1320. SidBufPtr - A ponter to a GROWBUFFER. The SID is appended to
  1321. the GROWBUFFER.
  1322. Return value:
  1323. TRUE if the lookup succeeded, or FALSE if an error occurred from
  1324. either establishing a nul session for a domain or looking up an
  1325. account on the domain. GetLastError provides the failure code.
  1326. --*/
  1327. {
  1328. return pGetUserSecurityInfo (User, Domain, SidBufPtr, NULL);
  1329. }
  1330. BOOL
  1331. GetUserType (
  1332. IN PCWSTR User,
  1333. IN PCWSTR Domain,
  1334. OUT SID_NAME_USE *UseType
  1335. )
  1336. /*++
  1337. Routine Description:
  1338. This routine is valid only after the domain list is prepared.
  1339. It queries a domain for a user SID type.
  1340. Arguments:
  1341. User - Specifies name of user to look up
  1342. Domain - Specifies name of domain to query, or NULL for local machine
  1343. UseType - Specifies the address of a SID_NAME_USE variable
  1344. Return value:
  1345. TRUE if the lookup succeeded, or FALSE if an error occurred from
  1346. either establishing a nul session for a domain or looking up an
  1347. account on the domain. GetLastError provides the failure code.
  1348. --*/
  1349. {
  1350. GROWBUFFER SidBuf = GROWBUF_INIT;
  1351. BOOL b;
  1352. b = pGetUserSecurityInfo (User, Domain, &SidBuf, UseType);
  1353. FreeGrowBuffer (&SidBuf);
  1354. return b;
  1355. }
  1356. VOID
  1357. PrepareForRetry (
  1358. VOID
  1359. )
  1360. /*++
  1361. Routine Description:
  1362. Provides caller a way to reset the abandoned domains. When an error
  1363. occurs during account lookup, and the installer chooses not to retry,
  1364. the domain is disabled for the rest of the lookup until the installer
  1365. is presented with a dialog detailing the problems. If they choose to
  1366. retry the search, all disabled domains must be re-enabled, and thats
  1367. what this routine does.
  1368. Arguments:
  1369. none
  1370. Return value:
  1371. none
  1372. --*/
  1373. {
  1374. ACCT_ENUM Domain;
  1375. //
  1376. // Enumerate all domains and remove any empty server names
  1377. //
  1378. if (ListFirstDomain (&Domain)) {
  1379. do {
  1380. if (Domain.DomainPtr->Server && Domain.DomainPtr->Server[0] == 0) {
  1381. Domain.DomainPtr->Server = NULL;
  1382. }
  1383. } while (ListNextDomain (&Domain));
  1384. }
  1385. //
  1386. // Reset domain lookup retry count
  1387. //
  1388. g_RetryCount = DOMAIN_RETRY_RESET;
  1389. }
  1390. BOOL
  1391. RetryMessageBox (
  1392. DWORD Id,
  1393. PCTSTR *ArgArray
  1394. )
  1395. /*++
  1396. Routine Description:
  1397. A wrapper that allows retry message box code to be simplified.
  1398. Arguments:
  1399. Id - Specifies the message ID
  1400. ArgArray - Specifies the argument array eventually passed to FormatMessage
  1401. Return value:
  1402. TRUE if the user chooses YES, FALSE if the user chooses NO
  1403. --*/
  1404. {
  1405. DWORD rc;
  1406. if (g_RetryCount < 0) {
  1407. // Either DOMAIN_RETRY_ABORT or DOMAIN_RETRY_NO
  1408. return FALSE;
  1409. }
  1410. if (g_ConfigOptions.IgnoreNetworkErrors) {
  1411. return FALSE;
  1412. }
  1413. rc = ResourceMessageBox (
  1414. g_ParentWnd,
  1415. Id,
  1416. MB_YESNO|MB_ICONQUESTION,
  1417. ArgArray
  1418. );
  1419. if (rc == IDNO && g_RetryCount < DOMAIN_RETRY_MAX) {
  1420. // disabled so the IDD_NETWORK_DOWN dialog never appears
  1421. //g_RetryCount++;
  1422. if (g_RetryCount == DOMAIN_RETRY_MAX) {
  1423. DWORD Result;
  1424. Result = DialogBoxParam (
  1425. g_hInst,
  1426. MAKEINTRESOURCE (IDD_NETWORK_DOWN),
  1427. g_ParentWnd,
  1428. NetworkDownDlgProc,
  1429. (LPARAM) (PINT) &g_RetryCount
  1430. );
  1431. if (Result == IDC_STOP) {
  1432. g_RetryCount = DOMAIN_RETRY_ABORT;
  1433. } else if (Result == IDC_NO_RETRY) {
  1434. g_RetryCount = DOMAIN_RETRY_NO;
  1435. } else {
  1436. g_RetryCount = DOMAIN_RETRY_RESET;
  1437. }
  1438. }
  1439. }
  1440. return rc != IDNO;
  1441. }