Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

808 lines
17 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) 1998, Microsoft Corp. All rights reserved.
  4. //
  5. // FILE
  6. //
  7. // ezsam.c
  8. //
  9. // SYNOPSIS
  10. //
  11. // Defines helper functions for SAM API.
  12. //
  13. // MODIFICATION HISTORY
  14. //
  15. // 08/16/1998 Original version.
  16. // 02/18/1999 Connect by DNS name not address.
  17. // 03/23/1999 Tighten up the ezsam API.
  18. // Better failover/retry logic.
  19. // 04/14/1999 Copy SIDs returned by IASSamOpenUser.
  20. //
  21. ///////////////////////////////////////////////////////////////////////////////
  22. #include <nt.h>
  23. #include <ntrtl.h>
  24. #include <nturtl.h>
  25. #include <ntlsa.h>
  26. #include <windows.h>
  27. #include <lm.h>
  28. #include <dsgetdc.h>
  29. #include <statinfo.h>
  30. #include <ezsam.h>
  31. #include <iastrace.h>
  32. //////////
  33. // Private helper functions.
  34. //////////
  35. DWORD
  36. WINAPI
  37. IASSamOpenDomain(
  38. IN PCWSTR DomainName,
  39. IN ACCESS_MASK DesiredAccess,
  40. IN ULONG Flags,
  41. IN BOOL Force,
  42. OUT PSID *DomainSid,
  43. OUT PSAM_HANDLE DomainHandle
  44. );
  45. VOID
  46. WINAPI
  47. IASSamFreeSid(
  48. IN PSID Sid
  49. );
  50. VOID
  51. WINAPI
  52. IASSamCloseDomain(
  53. IN SAM_HANDLE SamHandle,
  54. IN BOOL Valid
  55. );
  56. DWORD
  57. WINAPI
  58. IASSamLookupUser(
  59. IN SAM_HANDLE DomainHandle,
  60. IN PCWSTR UserName,
  61. IN ACCESS_MASK DesiredAccess,
  62. IN OUT OPTIONAL PULONG UserRid,
  63. OUT PSAM_HANDLE UserHandle
  64. );
  65. //////////
  66. // Handles for the local SAM domains.
  67. //////////
  68. SAM_HANDLE theAccountDomainHandle;
  69. SAM_HANDLE theBuiltinDomainHandle;
  70. //////////
  71. // State associated with a cached domain.
  72. //////////
  73. struct CachedDomain
  74. {
  75. LONG lock; // 1 if the cache is locked, 0 otherwise.
  76. WCHAR domainName[DNLEN + 1]; // Domain name.
  77. ACCESS_MASK access; // Access mask for handle.
  78. ULARGE_INTEGER expiry; // Time when entry expires.
  79. PSID sid; // SID for the domain.
  80. SAM_HANDLE handle; // Handle to domain.
  81. LONG refCount; // Reference count.
  82. };
  83. //////////
  84. // Time in 100 nsec intervals that a cache entry will be retained.
  85. // Set to 900 seconds.
  86. //////////
  87. #define CACHE_LIFETIME (9000000000ui64)
  88. //////////
  89. // The currently cached domain.
  90. //////////
  91. struct CachedDomain theCache;
  92. //////////
  93. // Try to lock the cache.
  94. //////////
  95. #define TRYLOCK_CACHE() \
  96. (InterlockedExchange(&theCache.lock, 1) == 0)
  97. //////////
  98. // Unlock the cache.
  99. //////////
  100. #define UNLOCK_CACHE() \
  101. (InterlockedExchange(&theCache.lock, 0))
  102. ///////////////////////////////////////////////////////////////////////////////
  103. //
  104. // FUNCTION
  105. //
  106. // IASSamSidDup
  107. //
  108. // DESCRIPTION
  109. //
  110. // Duplicates the passed in SID. The SID should be freed by calling
  111. // IASSamFreeSid.
  112. //
  113. ///////////////////////////////////////////////////////////////////////////////
  114. PSID
  115. WINAPI
  116. IASSamSidDup(
  117. PSID Sid
  118. )
  119. {
  120. ULONG sidLength;
  121. PSID rv;
  122. if (Sid)
  123. {
  124. sidLength = RtlLengthSid(Sid);
  125. rv = RtlAllocateHeap(
  126. RtlProcessHeap(),
  127. 0,
  128. sidLength
  129. );
  130. if (rv) { memcpy(rv, Sid, sidLength); }
  131. }
  132. else
  133. {
  134. rv = NULL;
  135. }
  136. return rv;
  137. }
  138. ///////////////////////////////////////////////////////////////////////////////
  139. //
  140. // FUNCTION
  141. //
  142. // IASSamOpenCachedDomain
  143. //
  144. // DESCRIPTION
  145. //
  146. // Attempt to open a domain from the cache.
  147. //
  148. ///////////////////////////////////////////////////////////////////////////////
  149. BOOL
  150. WINAPI
  151. IASSamOpenCachedDomain(
  152. IN PCWSTR DomainName,
  153. IN ACCESS_MASK DesiredAccess,
  154. OUT PSID *DomainSid,
  155. OUT PSAM_HANDLE DomainHandle
  156. )
  157. {
  158. BOOL success;
  159. ULARGE_INTEGER now;
  160. success = FALSE;
  161. // Can we access the cache ?
  162. if (TRYLOCK_CACHE())
  163. {
  164. // Does the domain name match ?
  165. if (_wcsicmp(DomainName, theCache.domainName) == 0)
  166. {
  167. // Does the cached handle have sufficient access rights ?
  168. if ((DesiredAccess & theCache.access) == DesiredAccess)
  169. {
  170. GetSystemTimeAsFileTime((LPFILETIME)&now);
  171. // Is the entry still valid ?
  172. if (now.QuadPart < theCache.expiry.QuadPart)
  173. {
  174. // We got a cache hit, so update the reference count ...
  175. InterlockedIncrement(&theCache.refCount);
  176. // ... and return the data.
  177. *DomainSid = theCache.sid;
  178. *DomainHandle = theCache.handle;
  179. success = TRUE;
  180. }
  181. else
  182. {
  183. // The entry has expired, so NULL out the name to prevent the
  184. // next thread from wasting its time.
  185. theCache.domainName[0] = L'\0';
  186. }
  187. }
  188. }
  189. UNLOCK_CACHE();
  190. }
  191. return success;
  192. }
  193. ///////////////////////////////////////////////////////////////////////////////
  194. //
  195. // FUNCTION
  196. //
  197. // IASSamAddCachedDomain
  198. //
  199. // DESCRIPTION
  200. //
  201. // Attempt to add a domain to the cache.
  202. //
  203. ///////////////////////////////////////////////////////////////////////////////
  204. VOID
  205. WINAPI
  206. IASSamAddCachedDomain(
  207. IN PCWSTR DomainName,
  208. IN ACCESS_MASK Access,
  209. IN PSID DomainSid,
  210. IN SAM_HANDLE DomainHandle
  211. )
  212. {
  213. // Can we access the cache ?
  214. if (TRYLOCK_CACHE())
  215. {
  216. // Is the current entry idle ?
  217. if (theCache.refCount == 0)
  218. {
  219. // Free the current entry.
  220. SamCloseHandle(theCache.handle);
  221. SamFreeMemory(theCache.sid);
  222. // Store the cached state.
  223. wcsncpy(theCache.domainName, DomainName, DNLEN);
  224. theCache.access = Access;
  225. theCache.sid = DomainSid;
  226. theCache.handle = DomainHandle;
  227. // Set the expiration time.
  228. GetSystemTimeAsFileTime((LPFILETIME)&theCache.expiry);
  229. theCache.expiry.QuadPart += CACHE_LIFETIME;
  230. // The caller already has a reference.
  231. theCache.refCount = 1;
  232. }
  233. UNLOCK_CACHE();
  234. }
  235. }
  236. ///////////////////////////////////////////////////////////////////////////////
  237. //
  238. // FUNCTION
  239. //
  240. // IASSamInitialize
  241. //
  242. // DESCRIPTION
  243. //
  244. // Initializes the handles for the local SAM domains.
  245. //
  246. ///////////////////////////////////////////////////////////////////////////////
  247. DWORD
  248. WINAPI
  249. IASSamInitialize( VOID )
  250. {
  251. DWORD status;
  252. SAM_HANDLE hLocalServer;
  253. UNICODE_STRING uniAccountDomain;
  254. //////////
  255. // Connect to the local SAM.
  256. //////////
  257. status = SamConnect(
  258. NULL,
  259. &hLocalServer,
  260. SAM_SERVER_LOOKUP_DOMAIN,
  261. &theObjectAttributes
  262. );
  263. if (!NT_SUCCESS(status)) { goto exit; }
  264. //////////
  265. // Open a handle to the account domain.
  266. //////////
  267. status = SamOpenDomain(
  268. hLocalServer,
  269. DOMAIN_LOOKUP |
  270. DOMAIN_GET_ALIAS_MEMBERSHIP |
  271. DOMAIN_READ_PASSWORD_PARAMETERS,
  272. theAccountDomainSid,
  273. &theAccountDomainHandle
  274. );
  275. if (!NT_SUCCESS(status)) { goto close_server; }
  276. //////////
  277. // Open a handle to the built-in domain.
  278. //////////
  279. status = SamOpenDomain(
  280. hLocalServer,
  281. DOMAIN_LOOKUP |
  282. DOMAIN_GET_ALIAS_MEMBERSHIP,
  283. theBuiltinDomainSid,
  284. &theBuiltinDomainHandle
  285. );
  286. if (!NT_SUCCESS(status))
  287. {
  288. SamCloseHandle(theAccountDomainHandle);
  289. theAccountDomainHandle = NULL;
  290. }
  291. close_server:
  292. SamCloseHandle(hLocalServer);
  293. exit:
  294. return RtlNtStatusToDosError(status);
  295. }
  296. ///////////////////////////////////////////////////////////////////////////////
  297. //
  298. // FUNCTION
  299. //
  300. // IASSamShutdown
  301. //
  302. // DESCRIPTION
  303. //
  304. // Cleans up global variables.
  305. //
  306. ///////////////////////////////////////////////////////////////////////////////
  307. VOID
  308. WINAPI
  309. IASSamShutdown( VOID )
  310. {
  311. // Reset the cache.
  312. SamFreeMemory(theCache.sid);
  313. SamCloseHandle(theCache.handle);
  314. memset(&theCache, 0, sizeof(theCache));
  315. SamCloseHandle(theAccountDomainHandle);
  316. theAccountDomainHandle = NULL;
  317. SamCloseHandle(theBuiltinDomainHandle);
  318. theBuiltinDomainHandle = NULL;
  319. }
  320. ///////////////////////////////////////////////////////////////////////////////
  321. //
  322. // FUNCTION
  323. //
  324. // IASSamOpenDomain
  325. //
  326. // DESCRIPTION
  327. //
  328. // Opens a connection to a SAM domain. The caller is responsible for
  329. // closing the returned handle and freeing the returned SID.
  330. //
  331. ///////////////////////////////////////////////////////////////////////////////
  332. DWORD
  333. WINAPI
  334. IASSamOpenDomain(
  335. IN PCWSTR DomainName,
  336. IN ACCESS_MASK DesiredAccess,
  337. IN ULONG Flags,
  338. IN BOOL Force,
  339. OUT PSID *DomainSid,
  340. OUT PSAM_HANDLE DomainHandle
  341. )
  342. {
  343. DWORD status;
  344. PDOMAIN_CONTROLLER_INFOW dci;
  345. UNICODE_STRING uniServerName, uniDomainName;
  346. SAM_HANDLE hServer;
  347. //////////
  348. // First check for the local account domain.
  349. //////////
  350. if (_wcsicmp(DomainName, theAccountDomain) == 0)
  351. {
  352. *DomainSid = theAccountDomainSid;
  353. *DomainHandle = theAccountDomainHandle;
  354. IASTraceString("Using cached SAM connection to local account domain.");
  355. return NO_ERROR;
  356. }
  357. //////////
  358. // Try for a cache hit.
  359. //////////
  360. if (IASSamOpenCachedDomain(
  361. DomainName,
  362. DesiredAccess,
  363. DomainSid,
  364. DomainHandle
  365. ))
  366. {
  367. IASTraceString("Using cached SAM connection.");
  368. return NO_ERROR;
  369. }
  370. //////////
  371. // No luck, so get the name of the DC to connect to.
  372. //////////
  373. status = IASGetDcName(
  374. DomainName,
  375. (Force ? DS_FORCE_REDISCOVERY : 0) | Flags,
  376. &dci
  377. );
  378. if (status != NO_ERROR) { return status; }
  379. //////////
  380. // Connect to the server.
  381. //////////
  382. IASTracePrintf("Connecting to SAM server on %S.",
  383. dci->DomainControllerName);
  384. RtlInitUnicodeString(
  385. &uniServerName,
  386. dci->DomainControllerName
  387. );
  388. status = SamConnect(
  389. &uniServerName,
  390. &hServer,
  391. SAM_SERVER_LOOKUP_DOMAIN,
  392. &theObjectAttributes
  393. );
  394. // We're through with the server name.
  395. NetApiBufferFree(dci);
  396. if (!NT_SUCCESS(status)) { goto exit; }
  397. //////////
  398. // Get SID for the domain.
  399. //////////
  400. RtlInitUnicodeString(
  401. &uniDomainName,
  402. DomainName
  403. );
  404. status = SamLookupDomainInSamServer(
  405. hServer,
  406. &uniDomainName,
  407. DomainSid
  408. );
  409. if (!NT_SUCCESS(status)) { goto close_server; }
  410. //////////
  411. // Open the domain using SID we got above
  412. //////////
  413. status = SamOpenDomain(
  414. hServer,
  415. DesiredAccess,
  416. *DomainSid,
  417. DomainHandle
  418. );
  419. if (NT_SUCCESS(status))
  420. {
  421. // Try to add this to the cache.
  422. IASSamAddCachedDomain(
  423. DomainName,
  424. DesiredAccess,
  425. *DomainSid,
  426. *DomainHandle
  427. );
  428. }
  429. else
  430. {
  431. // Free the SID. We can use SamFreeMemory since we know this SID isn't
  432. // in the cache.
  433. SamFreeMemory(*DomainSid);
  434. *DomainSid = NULL;
  435. }
  436. close_server:
  437. SamCloseHandle(hServer);
  438. exit:
  439. return RtlNtStatusToDosError(status);
  440. }
  441. ///////////////////////////////////////////////////////////////////////////////
  442. //
  443. // FUNCTION
  444. //
  445. // IASSamLookupUser
  446. //
  447. // DESCRIPTION
  448. //
  449. // Opens a user in a SAM domain. The caller is responsible for closing
  450. // the returned handle.
  451. //
  452. ///////////////////////////////////////////////////////////////////////////////
  453. DWORD
  454. WINAPI
  455. IASSamLookupUser(
  456. IN SAM_HANDLE DomainHandle,
  457. IN PCWSTR UserName,
  458. IN ACCESS_MASK DesiredAccess,
  459. IN OUT OPTIONAL PULONG UserRid,
  460. OUT PSAM_HANDLE UserHandle
  461. )
  462. {
  463. DWORD status;
  464. UNICODE_STRING uniUserName;
  465. ULONG rid, *prid;
  466. PSID_NAME_USE nameUse;
  467. if (UserName)
  468. {
  469. //////////
  470. // Caller supplied a UserName so lookup the RID.
  471. //////////
  472. RtlInitUnicodeString(
  473. &uniUserName,
  474. UserName
  475. );
  476. status = SamLookupNamesInDomain(
  477. DomainHandle,
  478. 1,
  479. &uniUserName,
  480. &prid,
  481. &nameUse
  482. );
  483. if (!NT_SUCCESS(status)) { goto exit; }
  484. // Save the RID ...
  485. rid = *prid;
  486. // ... and free the memory.
  487. SamFreeMemory(prid);
  488. SamFreeMemory(nameUse);
  489. // Return the RID to the caller if requested.
  490. if (UserRid)
  491. {
  492. *UserRid = rid;
  493. }
  494. }
  495. else if (UserRid)
  496. {
  497. // Caller supplied a RID.
  498. rid = *UserRid;
  499. }
  500. else
  501. {
  502. // Caller supplied neither a UserName or a RID.
  503. return ERROR_INVALID_PARAMETER;
  504. }
  505. //////////
  506. // Open the user object.
  507. //////////
  508. status = SamOpenUser(
  509. DomainHandle,
  510. DesiredAccess,
  511. rid,
  512. UserHandle
  513. );
  514. exit:
  515. return RtlNtStatusToDosError(status);
  516. }
  517. ///////////////////////////////////////////////////////////////////////////////
  518. //
  519. // FUNCTION
  520. //
  521. // IASSamOpenUser
  522. //
  523. // DESCRIPTION
  524. //
  525. // Opens a SAM user. The caller is responsible for closing
  526. // the returned handle.
  527. //
  528. ///////////////////////////////////////////////////////////////////////////////
  529. DWORD
  530. WINAPI
  531. IASSamOpenUser(
  532. IN PCWSTR DomainName,
  533. IN PCWSTR UserName,
  534. IN ACCESS_MASK DesiredAccess,
  535. IN ULONG Flags,
  536. IN OUT OPTIONAL PULONG UserRid,
  537. OUT OPTIONAL PSID *DomainSid,
  538. OUT PSAM_HANDLE UserHandle
  539. )
  540. {
  541. DWORD status;
  542. ULONG tries;
  543. PSID sid;
  544. SAM_HANDLE hDomain;
  545. BOOL success;
  546. // Initialize the retry state.
  547. tries = 0;
  548. success = FALSE;
  549. do
  550. {
  551. //////////
  552. // Open a connection to the domain.
  553. //////////
  554. status = IASSamOpenDomain(
  555. DomainName,
  556. DOMAIN_LOOKUP,
  557. Flags,
  558. (tries > 0),
  559. &sid,
  560. &hDomain
  561. );
  562. if (status == NO_ERROR)
  563. {
  564. //////////
  565. // Lookup the user.
  566. //////////
  567. status = IASSamLookupUser(
  568. hDomain,
  569. UserName,
  570. DesiredAccess,
  571. UserRid,
  572. UserHandle
  573. );
  574. switch (status)
  575. {
  576. case NO_ERROR:
  577. // Everything succeeded, so return the domain SID if requested.
  578. if (DomainSid && !(*DomainSid = IASSamSidDup(sid)))
  579. {
  580. SamCloseHandle(*UserHandle);
  581. *UserHandle = NULL;
  582. status = STATUS_NO_MEMORY;
  583. }
  584. // Fall through.
  585. case ERROR_NONE_MAPPED:
  586. success = TRUE;
  587. break;
  588. }
  589. // Free the sid ...
  590. IASSamFreeSid(sid);
  591. // ... and the domain handle.
  592. IASSamCloseDomain(hDomain, success);
  593. }
  594. } while (!success && ++tries < 2);
  595. return status;
  596. }
  597. ///////////////////////////////////////////////////////////////////////////////
  598. //
  599. // FUNCTION
  600. //
  601. // IASSamCloseDomain
  602. //
  603. // DESCRIPTION
  604. //
  605. // Closes a handle returned by IASSamOpenDomain.
  606. //
  607. ///////////////////////////////////////////////////////////////////////////////
  608. VOID
  609. WINAPI
  610. IASSamCloseDomain(
  611. IN SAM_HANDLE SamHandle,
  612. IN BOOL Valid
  613. )
  614. {
  615. if (SamHandle == theCache.handle)
  616. {
  617. if (!Valid)
  618. {
  619. theCache.domainName[0] = L'\0';
  620. }
  621. InterlockedDecrement(&theCache.refCount);
  622. }
  623. else if (SamHandle != theAccountDomainHandle)
  624. {
  625. SamCloseHandle(SamHandle);
  626. }
  627. }
  628. ///////////////////////////////////////////////////////////////////////////////
  629. //
  630. // FUNCTION
  631. //
  632. // IASSamFreeSid
  633. //
  634. // DESCRIPTION
  635. //
  636. // Frees a SID returned by IASSamOpenDomain.
  637. //
  638. ///////////////////////////////////////////////////////////////////////////////
  639. VOID
  640. WINAPI
  641. IASSamFreeSid (
  642. IN PSID Sid
  643. )
  644. {
  645. if (Sid != theAccountDomainSid && Sid != theCache.sid)
  646. {
  647. SamFreeMemory(Sid);
  648. }
  649. }
  650. ///////////////////////////////////////////////////////////////////////////////
  651. //
  652. // FUNCTION
  653. //
  654. // IASLengthRequiredChildSid
  655. //
  656. // DESCRIPTION
  657. //
  658. // Returns the number of bytes required for a SID immediately subordinate
  659. // to ParentSid.
  660. //
  661. ///////////////////////////////////////////////////////////////////////////////
  662. ULONG
  663. WINAPI
  664. IASLengthRequiredChildSid(
  665. IN PSID ParentSid
  666. )
  667. {
  668. // Get the parent's SubAuthority count.
  669. ULONG subAuthCount;
  670. subAuthCount = (ULONG)*RtlSubAuthorityCountSid(ParentSid);
  671. // And add one for the child RID.
  672. return RtlLengthRequiredSid(1 + subAuthCount);
  673. }
  674. ///////////////////////////////////////////////////////////////////////////////
  675. //
  676. // FUNCTION
  677. //
  678. // IASInitializeChildSid
  679. //
  680. // DESCRIPTION
  681. //
  682. // Initializes a SID with the concatenation of ParentSid + ChildRid.
  683. //
  684. ///////////////////////////////////////////////////////////////////////////////
  685. VOID
  686. WINAPI
  687. IASInitializeChildSid(
  688. IN PSID ChildSid,
  689. IN PSID ParentSid,
  690. IN ULONG ChildRid
  691. )
  692. {
  693. PUCHAR pChildCount;
  694. ULONG parentCount;
  695. // Start with the parent SID. We assume the child SID is big enough.
  696. RtlCopySid(
  697. MAXLONG,
  698. ChildSid,
  699. ParentSid
  700. );
  701. // Get a pointer to the child SubAuthority count.
  702. pChildCount = RtlSubAuthorityCountSid(ChildSid);
  703. // Save the original parent count ...
  704. parentCount = (ULONG)*pChildCount;
  705. // ... then increment the child count.
  706. ++*pChildCount;
  707. // Set the last subauthority equal to the RID.
  708. *RtlSubAuthoritySid(ChildSid, parentCount) = ChildRid;
  709. }