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.

790 lines
21 KiB

  1. //+-------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1997 - 1997.
  5. //
  6. // File: sidcache.cxx
  7. //
  8. // Contents: Implementation of the sid/name lookup cache
  9. //
  10. // History: 2-Feb-97 MacM Created
  11. //
  12. //--------------------------------------------------------------------
  13. #include <aclpch.hxx>
  14. #pragma hdrstop
  15. //
  16. // Global name/sid cache
  17. //
  18. PACTRL_NAME_CACHE grgNameCache[ACTRL_NAME_TABLE_SIZE];
  19. PACTRL_NAME_CACHE grgSidCache[ACTRL_NAME_TABLE_SIZE];
  20. //
  21. // Local function prototypes
  22. //
  23. PACTRL_NAME_CACHE AccctrlpLookupNameInCache(PWSTR pwszName);
  24. PACTRL_NAME_CACHE AccctrlpLookupSidInCache(PSID pSid);
  25. DWORD AccctrlpNewNameSidNode(PWSTR pwszName,
  26. PSID pSid,
  27. SID_NAME_USE SidNameUse,
  28. PACTRL_NAME_CACHE *ppNewNode);
  29. VOID AccctrlpInsertNameNode(PACTRL_NAME_CACHE *ppRootNode,
  30. PACTRL_NAME_CACHE pNewNode);
  31. VOID AccctrlpInsertSidNode(PACTRL_NAME_CACHE *ppRootNode,
  32. PACTRL_NAME_CACHE pNewNode);
  33. DWORD AccctrlpConvertUserToCacheName(PWSTR pwszServer,
  34. PWSTR pwszName,
  35. PWSTR *ppwszCacheName);
  36. VOID AccctrlpFreeUserCacheName(PWSTR pwszName,
  37. PWSTR pwszCacheName);
  38. static RTL_RESOURCE gSidCacheLock;
  39. BOOL bSidCacheLockInitialized = FALSE;
  40. //+----------------------------------------------------------------------------
  41. //
  42. // Function: ActrlHashName
  43. //
  44. // Synopsis: Determines the hash index for the given name
  45. //
  46. // Arguments: pwszName -- Name to hash
  47. //
  48. // Returns: Hash index of the string
  49. //
  50. //-----------------------------------------------------------------------------
  51. INT
  52. ActrlHashName(PWSTR pwszName)
  53. {
  54. //
  55. // We'll hash off of just the user name, not the domain name or
  56. // any other name format
  57. //
  58. PWSTR pwszUser = wcschr(pwszName, L'\\');
  59. if(pwszUser == NULL)
  60. {
  61. pwszUser = pwszName;
  62. }
  63. else
  64. {
  65. pwszUser++;
  66. }
  67. INT Hash = 0;
  68. if(pwszUser != NULL)
  69. {
  70. while(*pwszUser != L'\0')
  71. {
  72. Hash = (Hash * 16 + ( *pwszUser++)) % ACTRL_NAME_TABLE_SIZE;
  73. }
  74. }
  75. acDebugOut((DEB_TRACE_LOOKUP,"Hashing %ws to %lu\n", pwszName, Hash));
  76. return(Hash);
  77. }
  78. //+----------------------------------------------------------------------------
  79. //
  80. // Function: ActrlHashSid
  81. //
  82. // Synopsis: Determines the hash index for the given sid
  83. //
  84. // Arguments: pSid -- Sid to hash
  85. //
  86. // Returns: Hash index of the Sid
  87. //
  88. //-----------------------------------------------------------------------------
  89. INT
  90. ActrlHashSid(PSID pSid)
  91. {
  92. DWORD dwTotal = 0;
  93. //
  94. // Just deal with the sub authorities
  95. //
  96. for(INT i = 0; i < (INT)(((PISID)pSid)->SubAuthorityCount); i++)
  97. {
  98. dwTotal += ((PISID)pSid)->SubAuthority[i];
  99. }
  100. #if DBG
  101. UNICODE_STRING SidString;
  102. memset(&SidString, 0, sizeof(UNICODE_STRING));
  103. if(pSid != NULL)
  104. {
  105. NTSTATUS Status = RtlConvertSidToUnicodeString(&SidString,
  106. pSid,
  107. TRUE);
  108. if(!NT_SUCCESS(Status))
  109. {
  110. acDebugOut((DEB_ERROR, "Can't convert sid to string: 0x%lx\n", Status));
  111. }
  112. else
  113. {
  114. acDebugOut((DEB_TRACE_LOOKUP,"Hashing %wZ (Total %lu) to %lu\n", &SidString,
  115. dwTotal, dwTotal % ACTRL_NAME_TABLE_SIZE));
  116. }
  117. }
  118. #endif
  119. return(dwTotal % ACTRL_NAME_TABLE_SIZE);
  120. }
  121. //+----------------------------------------------------------------------------
  122. //
  123. // Function: AccctrlInitializeSidNameCache
  124. //
  125. // Synopsis: Initialize the name/sid lookup cache
  126. //
  127. // Arguments: VOID
  128. //
  129. // Returns: ERROR_SUCCESS -- Success
  130. //
  131. //-----------------------------------------------------------------------------
  132. DWORD AccctrlInitializeSidNameCache(VOID)
  133. {
  134. DWORD dwErr;
  135. if (TRUE == bSidCacheLockInitialized)
  136. {
  137. //
  138. // Just a precautionary measure to make sure that we do not initialize
  139. // multiple times.
  140. //
  141. ASSERT(FALSE);
  142. return ERROR_SUCCESS;
  143. }
  144. memset(grgNameCache, 0, sizeof(PACTRL_NAME_CACHE) * ACTRL_NAME_TABLE_SIZE);
  145. memset(grgSidCache, 0, sizeof(PACTRL_NAME_CACHE) * ACTRL_NAME_TABLE_SIZE);
  146. __try
  147. {
  148. RtlInitializeResource(&gSidCacheLock);
  149. dwErr = ERROR_SUCCESS;
  150. bSidCacheLockInitialized = TRUE;
  151. }
  152. __except (EXCEPTION_EXECUTE_HANDLER)
  153. {
  154. dwErr = RtlNtStatusToDosError(GetExceptionCode());
  155. }
  156. return dwErr;
  157. }
  158. //+----------------------------------------------------------------------------
  159. //
  160. // Function: AccctrlFreeSidNameCache
  161. //
  162. // Synopsis: Frees any memory allocated for the name/sid cache
  163. //
  164. // Arguments: VOID
  165. //
  166. // Returns: VOID
  167. //
  168. //-----------------------------------------------------------------------------
  169. VOID AccctrlFreeSidNameCache(VOID)
  170. {
  171. INT i;
  172. PACTRL_NAME_CACHE pNode, pNext;
  173. if (FALSE == bSidCacheLockInitialized)
  174. {
  175. return;
  176. }
  177. for(i = 0; i < ACTRL_NAME_TABLE_SIZE; i++)
  178. {
  179. //
  180. // Nodes are only inserted into the name cache, so that is the only
  181. // place we delete them from
  182. //
  183. pNode = grgNameCache[i];
  184. while(pNode != NULL)
  185. {
  186. pNext = pNode->pNextName;
  187. AccFree(pNode->pwszName);
  188. AccFree(pNode->pSid);
  189. AccFree(pNode);
  190. pNode = pNext;
  191. }
  192. }
  193. RtlDeleteResource(&gSidCacheLock);
  194. bSidCacheLockInitialized = FALSE;
  195. }
  196. //+----------------------------------------------------------------------------
  197. //
  198. // Function: AccctrlpLookupNameInCache
  199. //
  200. // Synopsis: Determines if the given name exists in the cache or not
  201. //
  202. // Arguments: [pwszName] -- Name to be looked up
  203. //
  204. // Returns: Matching node if found, NULL if not
  205. //
  206. //-----------------------------------------------------------------------------
  207. PACTRL_NAME_CACHE AccctrlpLookupNameInCache(PWSTR pwszName)
  208. {
  209. PACTRL_NAME_CACHE pNode = NULL;
  210. pNode = grgNameCache[ActrlHashName(pwszName)];
  211. while(pNode != NULL)
  212. {
  213. if(_wcsicmp(pwszName, pNode->pwszName) == 0)
  214. {
  215. break;
  216. }
  217. pNode = pNode->pNextName;
  218. }
  219. return(pNode);
  220. }
  221. //+----------------------------------------------------------------------------
  222. //
  223. // Function: AccctrlpLookupSidInCache
  224. //
  225. // Synopsis: Determines if the given sid exists in the cache or not
  226. //
  227. // Arguments: [pSid] -- Sid to be looked up
  228. //
  229. // Returns: Matching node if found, NULL if not
  230. //
  231. //-----------------------------------------------------------------------------
  232. PACTRL_NAME_CACHE AccctrlpLookupSidInCache(PSID pSid)
  233. {
  234. PACTRL_NAME_CACHE pNode = grgSidCache[ActrlHashSid(pSid)];
  235. while(pNode != NULL)
  236. {
  237. if(RtlEqualSid(pSid, pNode->pSid) == TRUE)
  238. {
  239. break;
  240. }
  241. pNode = pNode->pNextSid;
  242. }
  243. return(pNode);
  244. }
  245. //+----------------------------------------------------------------------------
  246. //
  247. // Function: AccctrlpNewNameSidNode
  248. //
  249. // Synopsis: Allocates a new node and inserts them into the caches
  250. //
  251. // Arguments: [pwszName] -- Name to insert
  252. // [pSid] -- Sid to insert
  253. // [SidNameUse] -- Name use
  254. // [pNewNode] -- Newly added node
  255. //
  256. // Returns: ERROR_SUCCESS -- Success
  257. // ERROR_NOT_ENOUGH_MEMORY A memory allocation failed
  258. //
  259. //-----------------------------------------------------------------------------
  260. DWORD AccctrlpNewNameSidNode(PWSTR pwszName,
  261. PSID pSid,
  262. SID_NAME_USE SidNameUse,
  263. PACTRL_NAME_CACHE *ppNewNode)
  264. {
  265. DWORD dwErr = ERROR_SUCCESS;
  266. PACTRL_NAME_CACHE pNewNode = (PACTRL_NAME_CACHE)AccAlloc(
  267. sizeof(ACTRL_NAME_CACHE));
  268. if(pNewNode == NULL)
  269. {
  270. dwErr = ERROR_NOT_ENOUGH_MEMORY;
  271. }
  272. else
  273. {
  274. pNewNode->pwszName = pwszName;
  275. pNewNode->pSid = pSid;
  276. pNewNode->SidUse = SidNameUse;
  277. pNewNode->pNextName= NULL;
  278. pNewNode->pNextSid = NULL;
  279. AccctrlpInsertNameNode(&(grgNameCache[ActrlHashName(pwszName)]),
  280. pNewNode);
  281. AccctrlpInsertSidNode(&(grgSidCache[ActrlHashSid(pSid)]),
  282. pNewNode);
  283. *ppNewNode = pNewNode;
  284. }
  285. return(dwErr);
  286. }
  287. //+----------------------------------------------------------------------------
  288. //
  289. // Function: AccctrlpInsertNameNode
  290. //
  291. // Synopsis: Inserts the specified new node into the caches
  292. //
  293. // Arguments: [ppRootNode] -- Root node in the name cache
  294. // [pNewNode] -- Node to insert
  295. //
  296. // Returns: VOID
  297. //
  298. //-----------------------------------------------------------------------------
  299. VOID AccctrlpInsertNameNode(PACTRL_NAME_CACHE *ppRootNode,
  300. PACTRL_NAME_CACHE pNewNode)
  301. {
  302. PACTRL_NAME_CACHE pNext = NULL;
  303. if(*ppRootNode == NULL)
  304. {
  305. *ppRootNode = pNewNode;
  306. }
  307. else
  308. {
  309. acDebugOut((DEB_TRACE_LOOKUP, "Collision inserting %ws with:\n", pNewNode->pwszName));
  310. pNext = *ppRootNode;
  311. acDebugOut((DEB_TRACE_LOOKUP, "\t%ws\n", pNext->pwszName));
  312. while(pNext->pNextName != NULL)
  313. {
  314. #if DBG
  315. if(_wcsicmp(pNewNode->pwszName, pNext->pwszName) == 0)
  316. {
  317. acDebugOut((DEB_ERROR, "Name %ws already in list: 0x%lx\n",
  318. pNewNode->pwszName,
  319. *ppRootNode));
  320. // ASSERT(FALSE);
  321. }
  322. #endif
  323. pNext = pNext->pNextName;
  324. acDebugOut((DEB_TRACE_LOOKUP, "\t%ws\n", pNext->pwszName));
  325. }
  326. pNext->pNextName = pNewNode;
  327. }
  328. }
  329. //+----------------------------------------------------------------------------
  330. //
  331. // Function: AccctrlpInsertSidNode
  332. //
  333. // Synopsis: Inserts the specified new node into the caches
  334. //
  335. // Arguments: [ppRootNode] -- Root node in the name cache
  336. // [pNewNode] -- Node to insert
  337. //
  338. // Returns: VOID
  339. //
  340. //-----------------------------------------------------------------------------
  341. VOID AccctrlpInsertSidNode(PACTRL_NAME_CACHE *ppRootNode,
  342. PACTRL_NAME_CACHE pNewNode)
  343. {
  344. PACTRL_NAME_CACHE pNext = NULL;
  345. if(*ppRootNode == NULL)
  346. {
  347. *ppRootNode = pNewNode;
  348. }
  349. else
  350. {
  351. acDebugOut((DEB_TRACE_LOOKUP, "Collision inserting %ws with:\n", pNewNode->pwszName));
  352. pNext = *ppRootNode;
  353. acDebugOut((DEB_TRACE_LOOKUP, "\t%ws\n", pNext->pwszName));
  354. while(pNext->pNextSid != NULL)
  355. {
  356. #if DBG
  357. if(RtlEqualSid(pNewNode->pSid, pNext->pSid) == TRUE)
  358. {
  359. acDebugOut((DEB_ERROR, "Sid for %ws already in list: 0x%lx\n",
  360. pNewNode->pwszName,
  361. *ppRootNode));
  362. // ASSERT(FALSE);
  363. }
  364. #endif
  365. pNext = pNext->pNextSid;
  366. acDebugOut((DEB_TRACE_LOOKUP, "\t%ws\n", pNext->pwszName));
  367. }
  368. pNext->pNextSid = pNewNode;
  369. }
  370. }
  371. //+----------------------------------------------------------------------------
  372. //
  373. // Function: AccctrlLookupName
  374. //
  375. // Synopsis: Looks up the name for the specified SID
  376. //
  377. // Arguments: [pwszServer] -- Name of the server to remote the call to
  378. // [pSid] -- Sid to lookup
  379. // [fAllocateReturn]- If true, the name returned is allocated
  380. // into a new buffer. Otherwise, a
  381. // reference is returned.
  382. // [ppwszName] -- Where the name is returned.
  383. // [pSidNameUse] -- Type of the name that's returned.
  384. //
  385. // Returns: VOID
  386. //
  387. //-----------------------------------------------------------------------------
  388. DWORD
  389. AccctrlLookupName(IN PWSTR pwszServer,
  390. IN PSID pSid,
  391. IN BOOL fAllocateReturn,
  392. OUT PWSTR *ppwszName,
  393. OUT PSID_NAME_USE pSidNameUse)
  394. {
  395. DWORD dwErr = ERROR_SUCCESS;
  396. if(pSid == NULL)
  397. {
  398. return(ERROR_NONE_MAPPED);
  399. }
  400. RtlAcquireResourceShared(&gSidCacheLock, TRUE);
  401. #if DBG
  402. UNICODE_STRING SidString;
  403. memset(&SidString, 0, sizeof(UNICODE_STRING));
  404. if(pSid != NULL)
  405. {
  406. NTSTATUS Status = RtlConvertSidToUnicodeString(&SidString,
  407. pSid,
  408. TRUE);
  409. if(!NT_SUCCESS(Status))
  410. {
  411. acDebugOut((DEB_ERROR, "Can't convert sid to string: 0x%lx\n", Status));
  412. }
  413. }
  414. #endif
  415. //
  416. // First, see if the sid alreadt exists in our cache
  417. //
  418. PACTRL_NAME_CACHE pNode = AccctrlpLookupSidInCache(pSid);
  419. if(pNode == NULL)
  420. {
  421. #if DBG
  422. acDebugOut((DEB_TRACE_LOOKUP, "Sid %wZ not found in cache\n", &SidString));
  423. #endif
  424. //
  425. // Grab a write lock
  426. //
  427. RtlConvertSharedToExclusive(&gSidCacheLock);
  428. //
  429. // We'll have to look it up...
  430. //
  431. PWSTR pwszName, pwszDomain;
  432. dwErr = AccLookupAccountName(pwszServer,
  433. pSid,
  434. &pwszName,
  435. &pwszDomain,
  436. pSidNameUse);
  437. if(dwErr == ERROR_SUCCESS)
  438. {
  439. PSID pNewSid = NULL;
  440. ACC_ALLOC_AND_COPY_SID(pSid, pNewSid, dwErr);
  441. if(dwErr == ERROR_SUCCESS)
  442. {
  443. dwErr = AccctrlpNewNameSidNode(pwszName,
  444. pNewSid,
  445. *pSidNameUse,
  446. &pNode);
  447. }
  448. if(dwErr != ERROR_SUCCESS)
  449. {
  450. AccFree(pwszName);
  451. AccFree(pwszDomain);
  452. AccFree(pNewSid);
  453. }
  454. }
  455. }
  456. #if DBG
  457. else
  458. {
  459. acDebugOut((DEB_TRACE_LOOKUP, "Sid %wZ found in cache\n", &SidString));
  460. }
  461. #endif
  462. //
  463. // Finally, return the information
  464. //
  465. if(dwErr == ERROR_SUCCESS)
  466. {
  467. if(fAllocateReturn == TRUE)
  468. {
  469. ACC_ALLOC_AND_COPY_STRINGW(pNode->pwszName, *ppwszName, dwErr);
  470. }
  471. else
  472. {
  473. *ppwszName = pNode->pwszName;
  474. }
  475. *pSidNameUse = pNode->SidUse;
  476. }
  477. RtlReleaseResource(&gSidCacheLock);
  478. return(dwErr);
  479. }
  480. //+----------------------------------------------------------------------------
  481. //
  482. // Function: AccctrlLookupSid
  483. //
  484. // Synopsis: Looks up the SID for the specified name
  485. //
  486. // Arguments: [pwszServer] -- Name of the server to remote the call to
  487. // [pwszName] -- Name to lookup
  488. // [fAllocateReturn]- If true, the name returned is allocated
  489. // into a new buffer. Otherwise, a
  490. // reference is returned.
  491. // [ppwszName] -- Where the name is returned.
  492. // [pSidNameUse] -- Type of the sid that's returned.
  493. //
  494. // Returns: VOID
  495. //
  496. //-----------------------------------------------------------------------------
  497. DWORD
  498. AccctrlLookupSid(IN PWSTR pwszServer,
  499. IN PWSTR pwszName,
  500. IN BOOL fAllocateReturn,
  501. OUT PSID *ppSid,
  502. OUT PSID_NAME_USE pSidNameUse)
  503. {
  504. DWORD dwErr = ERROR_SUCCESS;
  505. PWSTR pwszLookupName = pwszName;
  506. RtlAcquireResourceShared(&gSidCacheLock, TRUE);
  507. //
  508. // If we get a local name, convert it into machine/domain relative, so we can
  509. // look it up properly.
  510. //
  511. dwErr = AccctrlpConvertUserToCacheName(pwszServer, pwszName, &pwszLookupName);
  512. //
  513. // Just return if the conversion failed.
  514. //
  515. if (pwszLookupName == NULL)
  516. {
  517. if (dwErr == ERROR_SUCCESS)
  518. {
  519. dwErr = ERROR_ACCESS_DENIED;
  520. }
  521. }
  522. if(dwErr == ERROR_SUCCESS)
  523. {
  524. //
  525. // First, see if the sid already exists in our cache
  526. //
  527. PACTRL_NAME_CACHE pNode = AccctrlpLookupNameInCache(pwszLookupName);
  528. if(pNode == NULL)
  529. {
  530. //
  531. // Grab a write lock
  532. //
  533. RtlConvertSharedToExclusive(&gSidCacheLock);
  534. acDebugOut((DEB_TRACE_LOOKUP,"Name %ws not found in cache\n", pwszLookupName));
  535. //
  536. // We'll have to look it up...
  537. //
  538. TRUSTEE_W Trustee;
  539. memset(&Trustee, 0, sizeof(TRUSTEE_W));
  540. Trustee.TrusteeForm = TRUSTEE_IS_NAME;
  541. Trustee.ptstrName = pwszLookupName;
  542. dwErr = AccLookupAccountSid(pwszServer,
  543. &Trustee,
  544. ppSid,
  545. pSidNameUse);
  546. if(dwErr == ERROR_SUCCESS)
  547. {
  548. PWSTR pwszNewName = NULL;
  549. ACC_ALLOC_AND_COPY_STRINGW(pwszLookupName, pwszNewName, dwErr);
  550. if(dwErr == ERROR_SUCCESS)
  551. {
  552. dwErr = AccctrlpNewNameSidNode(pwszNewName,
  553. *ppSid,
  554. *pSidNameUse,
  555. &pNode);
  556. }
  557. if(dwErr != ERROR_SUCCESS)
  558. {
  559. AccFree(pwszNewName);
  560. AccFree(*ppSid);
  561. }
  562. }
  563. }
  564. #if DBG
  565. else
  566. {
  567. acDebugOut((DEB_TRACE_LOOKUP,"Name %ws found in cache\n", pwszLookupName));
  568. }
  569. #endif
  570. //
  571. // Finally, return the information
  572. //
  573. if(dwErr == ERROR_SUCCESS)
  574. {
  575. if(fAllocateReturn == TRUE)
  576. {
  577. ACC_ALLOC_AND_COPY_SID(pNode->pSid, *ppSid, dwErr);
  578. }
  579. else
  580. {
  581. *ppSid = pNode->pSid;
  582. }
  583. *pSidNameUse = pNode->SidUse;
  584. }
  585. AccctrlpFreeUserCacheName(pwszName, pwszLookupName);
  586. }
  587. RtlReleaseResource(&gSidCacheLock);
  588. return(dwErr);
  589. }
  590. //+----------------------------------------------------------------------------
  591. //
  592. // Function: AccctrlpConvertUserToCacheName
  593. //
  594. // Synopsis: Converts an input name that could be domain relative into a
  595. // standard format for caching/returning
  596. //
  597. // Arguments: [pwszServer] -- Server to lookup the name on
  598. // [pwszName] -- Original name format
  599. // [ppwszCacheName]-- Name in the proper format
  600. //
  601. // Returns: ERROR_SUCCESS -- Success
  602. // ERROR_NOT_ENOUGH_MEMORY A memory allocation failed
  603. //
  604. //-----------------------------------------------------------------------------
  605. DWORD AccctrlpConvertUserToCacheName(PWSTR pwszServer,
  606. PWSTR pwszName,
  607. PWSTR *ppwszCacheName)
  608. {
  609. DWORD dwErr = ERROR_SUCCESS;
  610. //
  611. // This is temporary until the name conversion APIs come into being
  612. //
  613. PSID pSid;
  614. SID_NAME_USE SNE;
  615. TRUSTEE_W Trustee;
  616. memset(&Trustee, 0, sizeof(TRUSTEE_W));
  617. Trustee.TrusteeForm = TRUSTEE_IS_NAME;
  618. Trustee.ptstrName = pwszName;
  619. dwErr = AccLookupAccountSid(pwszServer,
  620. &Trustee,
  621. &pSid,
  622. &SNE);
  623. if(dwErr == ERROR_SUCCESS)
  624. {
  625. PWSTR pwszDomain;
  626. dwErr = AccLookupAccountName(pwszServer,
  627. pSid,
  628. ppwszCacheName,
  629. &pwszDomain,
  630. &SNE);
  631. if(dwErr == ERROR_SUCCESS)
  632. {
  633. AccFree(pwszDomain);
  634. }
  635. AccFree(pSid);
  636. }
  637. return(dwErr);
  638. }
  639. //+----------------------------------------------------------------------------
  640. //
  641. // Function: AccctrlpFreeUserCacheName
  642. //
  643. // Synopsis: Frees any memory potentially allocated by
  644. // AccctrlpConvertUserToCacheName
  645. //
  646. // Arguments: [pwszName] -- Original name format
  647. // [pwszCacheName] -- Name returned by
  648. // AccctrlpConvertUserToCacheName
  649. //
  650. // Returns: VOID
  651. //
  652. //-----------------------------------------------------------------------------
  653. VOID AccctrlpFreeUserCacheName(PWSTR pwszName,
  654. PWSTR pwszCacheName)
  655. {
  656. if(pwszName != pwszCacheName)
  657. {
  658. AccFree(pwszCacheName);
  659. }
  660. }