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.

799 lines
17 KiB

  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name:
  4. Token.cxx
  5. Abstract:
  6. Implementation for Windows NT security interfaces.
  7. Platform:
  8. Windows NT user mode.
  9. Notes:
  10. Not portable to non-Windows NT platforms.
  11. Author:
  12. Mario Goertzel [MarioGo]
  13. Revision History:
  14. MarioGo 12/21/1995 Bits 'n pieces
  15. --*/
  16. #include <or.hxx>
  17. #include <aclapi.h>
  18. #include <access.hxx>
  19. CRITICAL_SECTION gcsTokenLock;
  20. extern "C"
  21. {
  22. // The following is a private function provided to try to empiracally
  23. // determine if the two access token have been restricted with comparable
  24. // WinSafer authorization Levels. When TRUE is returned, the pdwResult
  25. // output parameter will receive any of the following values:
  26. // -1 = Client's access token is more authorized than Server's.
  27. // 0 = Client's access token is comparable level to Server's.
  28. // 1 = Server's access token is more authorized than Client's.
  29. // BUGBUG: Remove when we discover which private header this thing is in.
  30. WINADVAPI
  31. BOOL WINAPI
  32. SaferiCompareTokenLevels (
  33. IN HANDLE ClientAccessToken,
  34. IN HANDLE ServerAccessToken,
  35. OUT PDWORD pdwResult
  36. );
  37. }
  38. ORSTATUS
  39. LookupOrCreateToken2(
  40. IN HANDLE hClientToken,
  41. OUT CToken **ppToken
  42. )
  43. /*++
  44. Routine Description:
  45. Finds or allocates a new token object for the caller.
  46. Arguments:
  47. hCaller - RPC binding handle of the caller of RPCSS.
  48. pToken - Upon a successful return this will hold the token.
  49. It can be destroyed by calling Release();
  50. Return Value:
  51. OR_OK - success
  52. OR_NOACCESS - If the caller is not local, or cannot be impersonated.
  53. OR_NOMEM - Unable to allocate an object.
  54. --*/
  55. {
  56. ORSTATUS status;
  57. UINT type;
  58. LUID luid;
  59. PTOKEN_USER ptu;
  60. TOKEN_STATISTICS ts;
  61. BOOL fSuccess;
  62. DWORD needed;
  63. HANDLE hJobObject = NULL;
  64. LUID luidMod;
  65. needed = sizeof(ts);
  66. fSuccess = GetTokenInformation(hClientToken,
  67. TokenStatistics,
  68. &ts,
  69. sizeof(ts),
  70. &needed
  71. );
  72. if (!fSuccess)
  73. {
  74. KdPrintEx((DPFLTR_DCOMSS_ID,
  75. DPFLTR_WARNING_LEVEL,
  76. "OR: GetTokenInfo failed %d\n",
  77. GetLastError()));
  78. ASSERT(GetLastError() != ERROR_INSUFFICIENT_BUFFER);
  79. status = OR_NOMEM;
  80. goto Cleanup;
  81. }
  82. luid = ts.AuthenticationId;
  83. luidMod = ts.ModifiedId;
  84. //
  85. // Check if the token is already in the list
  86. //
  87. {
  88. CMutexLock lock(&gcsTokenLock);
  89. CListElement *ple;
  90. ple = gpTokenList->First();
  91. fSuccess = FALSE;
  92. while(ple)
  93. {
  94. CToken *pToken = CToken::ContainingRecord(ple);
  95. if (pToken->MatchLuid(luid) &&
  96. (pToken->MatchModifiedLuid(luidMod)) &&
  97. (S_OK == pToken->MatchToken(hClientToken, TRUE)))
  98. {
  99. pToken->AddRef();
  100. *ppToken = pToken;
  101. status = OR_OK;
  102. fSuccess = TRUE;
  103. break;
  104. }
  105. else
  106. {
  107. ple = ple->Next();
  108. }
  109. }
  110. }
  111. if (fSuccess)
  112. {
  113. status = OR_OK;
  114. CloseHandle(hClientToken);
  115. goto Cleanup;
  116. }
  117. //
  118. // New user, need to allocate a token object.
  119. //
  120. // Lookup the SID to store in the new token object.
  121. needed = DEBUG_MIN(1, 0x2c);
  122. do
  123. {
  124. ptu = (PTOKEN_USER)alloca(needed);
  125. ASSERT(ptu);
  126. fSuccess = GetTokenInformation(hClientToken,
  127. TokenUser,
  128. (PBYTE)ptu,
  129. needed,
  130. &needed);
  131. // If this assert is hit increase the 24 both here and above
  132. ASSERT(needed <= 0x2c);
  133. }
  134. while ( fSuccess == FALSE
  135. && GetLastError() == ERROR_INSUFFICIENT_BUFFER);
  136. if (!fSuccess)
  137. {
  138. KdPrintEx((DPFLTR_DCOMSS_ID,
  139. DPFLTR_WARNING_LEVEL,
  140. "OR: GetTokenInfo (2) failed %d\n",
  141. GetLastError()));
  142. ASSERT(GetLastError() != ERROR_INSUFFICIENT_BUFFER);
  143. status = OR_NOMEM;
  144. goto Cleanup;
  145. }
  146. PSID psid;
  147. psid = ptu->User.Sid;
  148. ASSERT(IsValidSid(psid) == TRUE);
  149. // Allocate the token object
  150. needed = GetLengthSid(psid) - sizeof(SID);
  151. *ppToken = new(needed) CToken(hClientToken,
  152. hJobObject,
  153. luid,
  154. psid,
  155. needed + sizeof(SID));
  156. if (*ppToken)
  157. {
  158. CMutexLock lock(&gcsTokenLock);
  159. (*ppToken)->Insert();
  160. status = OR_OK;
  161. #if DBG_DETAIL
  162. {
  163. DWORD d = 50;
  164. WCHAR buffer[50];
  165. GetUserName(buffer, &d);
  166. KdPrintEx((DPFLTR_DCOMSS_ID,
  167. DPFLTR_WARNING_LEVEL,
  168. "OR: New user connected: %S (%p)\n",
  169. buffer,
  170. *ppToken));
  171. }
  172. #endif
  173. }
  174. else
  175. {
  176. status = OR_NOMEM;
  177. }
  178. Cleanup:
  179. if (OR_OK != status)
  180. {
  181. if (NULL != hJobObject)
  182. CloseHandle(hJobObject);
  183. }
  184. // status contains the result of the operation.
  185. return(status);
  186. }
  187. ORSTATUS
  188. LookupOrCreateToken(
  189. IN handle_t hCaller,
  190. IN BOOL fLocal,
  191. OUT CToken **ppToken
  192. )
  193. /*++
  194. Routine Description:
  195. Finds or allocates a new token object for the caller.
  196. Arguments:
  197. hCaller - RPC binding handle of the caller of RPCSS.
  198. fLocal - Looking up a local client, check local security.
  199. pToken - Upon a successful return this will hold the token.
  200. It can be destroyed by calling Release();
  201. Return Value:
  202. OR_OK - success
  203. OR_NOACCESS - If the caller is not local, or cannot be impersonated.
  204. OR_NOMEM - Unable to allocate an object.
  205. --*/
  206. {
  207. ORSTATUS status;
  208. UINT type;
  209. HANDLE hClientToken = 0;
  210. BOOL fSuccess;
  211. if (fLocal)
  212. {
  213. status = I_RpcBindingInqTransportType(hCaller, &type);
  214. if (status != RPC_S_OK || type != TRANSPORT_TYPE_LPC)
  215. {
  216. return(OR_NOACCESS);
  217. }
  218. }
  219. status = RpcImpersonateClient(hCaller);
  220. if (status != RPC_S_OK)
  221. {
  222. return(OR_NOACCESS);
  223. }
  224. fSuccess = OpenThreadToken(GetCurrentThread(),
  225. TOKEN_ALL_ACCESS,
  226. TRUE,
  227. &hClientToken);
  228. if (fSuccess)
  229. {
  230. status = LookupOrCreateToken2(hClientToken, ppToken);
  231. if(OR_OK == status)
  232. {
  233. // The token object now controls the life of the token handle
  234. hClientToken = 0;
  235. }
  236. else
  237. {
  238. CloseHandle(hClientToken);
  239. hClientToken = 0;
  240. }
  241. }
  242. else
  243. {
  244. KdPrintEx((DPFLTR_DCOMSS_ID,
  245. DPFLTR_WARNING_LEVEL,
  246. "OR: OpenThreadToken failed %d\n",
  247. GetLastError()));
  248. status = OR_NOMEM;
  249. ASSERT(hClientToken == 0);
  250. goto Cleanup;
  251. }
  252. Cleanup:
  253. // status contains the result of the operation.
  254. RPC_STATUS t = RpcRevertToSelfEx(hCaller);
  255. ASSERT(t == RPC_S_OK);
  256. return(status);
  257. }
  258. CToken::~CToken()
  259. {
  260. ASSERT(_lHKeyRefs == 0);
  261. ASSERT(_hHKCRKey == NULL);
  262. if (_hHKCRKey != NULL)
  263. {
  264. // Shouldn't happen...but close it anyway just in
  265. // case. Assert above will catch this if it occurs
  266. RegCloseKey(_hHKCRKey);
  267. }
  268. CloseHandle(_hImpersonationToken);
  269. if (NULL != _hJobObject)
  270. {
  271. TerminateJobObject(_hJobObject, 0);
  272. CloseHandle(_hJobObject);
  273. }
  274. }
  275. STDMETHODIMP CToken::QueryInterface(REFIID riid, LPVOID* ppv)
  276. {
  277. if (riid == IID_IUnknown || riid == IID_IUserToken)
  278. {
  279. *ppv = this;
  280. AddRef();
  281. return S_OK;
  282. }
  283. return E_NOINTERFACE;
  284. }
  285. STDMETHODIMP_(ULONG) CToken::AddRef()
  286. {
  287. return InterlockedIncrement(&_lRefs);
  288. }
  289. STDMETHODIMP_(ULONG) CToken::Release()
  290. {
  291. LONG lNewRefs;
  292. CMutexLock lock(&gcsTokenLock);
  293. lNewRefs = InterlockedDecrement(&_lRefs);
  294. if (lNewRefs == 0)
  295. {
  296. Remove();
  297. delete this;
  298. }
  299. return lNewRefs;
  300. }
  301. STDMETHODIMP
  302. CToken::GetUserClassesRootKey(HKEY* phKey)
  303. {
  304. CMutexLock lock(&gcsTokenLock);
  305. if ( _lHKeyRefs++ == 0 )
  306. {
  307. ASSERT(_hHKCRKey == NULL);
  308. // The original IUserToken implementation allowed for not
  309. // having a token. That should never happen with a CToken.
  310. ASSERT(_hImpersonationToken);
  311. // Open per-user hive
  312. LONG lRet = RegOpenUserClassesRoot(_hImpersonationToken,
  313. 0,
  314. KEY_READ,
  315. &_hHKCRKey);
  316. if (lRet != ERROR_SUCCESS)
  317. {
  318. // In case of an error, we fall back on HKCR since that
  319. // is what the original IUserToken implementation did.
  320. _hHKCRKey = HKEY_CLASSES_ROOT;
  321. }
  322. }
  323. *phKey = _hHKCRKey;
  324. ASSERT(*phKey != NULL);
  325. return S_OK;
  326. }
  327. STDMETHODIMP
  328. CToken::ReleaseUserClassesRootKey()
  329. {
  330. CMutexLock lock(&gcsTokenLock);
  331. ASSERT(_lHKeyRefs > 0);
  332. ASSERT(_hHKCRKey != NULL);
  333. if (--_lHKeyRefs == 0)
  334. {
  335. if (_hHKCRKey != HKEY_CLASSES_ROOT)
  336. {
  337. RegCloseKey(_hHKCRKey);
  338. }
  339. _hHKCRKey = NULL;
  340. }
  341. return S_OK;
  342. }
  343. STDMETHODIMP
  344. CToken::GetUserSid(BYTE **ppSid, USHORT *pcbSid)
  345. {
  346. // IUserToken interface assumes that sid lengths always
  347. // <= USHRT_MAX. Truncating here on purpose; assert is
  348. // to catch cases where this is a bad idea. GetLengthSid
  349. // is a very cheap call, so there's no need to cache it.
  350. DWORD dwSidLen = GetLengthSid(&_sid);
  351. ASSERT(dwSidLen <= USHRT_MAX);
  352. *pcbSid = (USHORT)dwSidLen;
  353. *ppSid = (BYTE*)&_sid;
  354. return S_OK;
  355. }
  356. void
  357. CToken::Impersonate()
  358. {
  359. ASSERT(_hImpersonationToken);
  360. BOOL f = SetThreadToken(0, _hImpersonationToken);
  361. ASSERT(f);
  362. return;
  363. }
  364. void
  365. CToken::Revert()
  366. {
  367. BOOL f = SetThreadToken(0, 0);
  368. ASSERT(f);
  369. return;
  370. }
  371. ULONG GetSessionId2(
  372. HANDLE hToken)
  373. {
  374. BOOL Result;
  375. ULONG SessionId = 0;
  376. ULONG ReturnLength;
  377. //
  378. // Use the _HYDRA_ extension to GetTokenInformation to
  379. // return the SessionId from the token.
  380. //
  381. Result = GetTokenInformation(
  382. hToken,
  383. TokenSessionId,
  384. &SessionId,
  385. sizeof(SessionId),
  386. &ReturnLength
  387. );
  388. if( !Result ) {
  389. SessionId = 0; // Default to console
  390. }
  391. return SessionId;
  392. }
  393. ULONG CToken::GetSessionId()
  394. {
  395. return GetSessionId2(_hImpersonationToken);
  396. }
  397. BOOL CToken::MatchModifiedLuid(LUID luid)
  398. {
  399. ASSERT(_hImpersonationToken);
  400. TOKEN_STATISTICS ts;
  401. BOOL fSuccess;
  402. DWORD needed;
  403. LUID luidMod;
  404. needed = sizeof(ts);
  405. fSuccess = GetTokenInformation(_hImpersonationToken,
  406. TokenStatistics,
  407. &ts,
  408. sizeof(ts),
  409. &needed
  410. );
  411. if (!fSuccess)
  412. {
  413. KdPrintEx((DPFLTR_DCOMSS_ID,
  414. DPFLTR_WARNING_LEVEL,
  415. "OR: GetTokenInfo failed %d\n",
  416. GetLastError()));
  417. ASSERT(GetLastError() != ERROR_INSUFFICIENT_BUFFER);
  418. return FALSE;
  419. }
  420. luidMod = ts.ModifiedId;
  421. return( luidMod.LowPart == luid.LowPart
  422. && luidMod.HighPart == luid.HighPart);
  423. }
  424. HRESULT CompareRestrictedSids(
  425. HANDLE hToken1,
  426. HANDLE hToken2)
  427. {
  428. HRESULT hr = S_OK;
  429. PSID pRestrictedSid1 = NULL;
  430. PSID pRestrictedSid2 = NULL;
  431. #if(_WIN32_WINNT >= 0x0500)
  432. PTOKEN_GROUPS pSids1;
  433. PTOKEN_GROUPS pSids2;
  434. NTSTATUS error;
  435. ULONG needed;
  436. //Get restricted SIDs.
  437. needed = DEBUG_MIN(1, 300);
  438. do
  439. {
  440. pSids1 = (PTOKEN_GROUPS) alloca(needed);
  441. error = NtQueryInformationToken(hToken1,
  442. TokenRestrictedSids,
  443. pSids1,
  444. needed,
  445. &needed);
  446. }
  447. while (error == STATUS_BUFFER_TOO_SMALL);
  448. if(!error && pSids1->GroupCount > 0)
  449. {
  450. pRestrictedSid1 = pSids1->Groups[0].Sid;
  451. }
  452. //Get restricted SIDs.
  453. needed = DEBUG_MIN(1, 300);
  454. do
  455. {
  456. pSids2 = (PTOKEN_GROUPS) alloca(needed);
  457. error = NtQueryInformationToken(hToken2,
  458. TokenRestrictedSids,
  459. pSids2,
  460. needed,
  461. &needed);
  462. }
  463. while (error == STATUS_BUFFER_TOO_SMALL);
  464. if(!error && pSids2->GroupCount > 0)
  465. {
  466. pRestrictedSid2 = pSids2->Groups[0].Sid;
  467. }
  468. if(pRestrictedSid1 && pRestrictedSid2)
  469. {
  470. //We have two restricted tokens.
  471. //Compare the first restricted SID.
  472. if(EqualSid(pRestrictedSid1, pRestrictedSid2))
  473. {
  474. hr = S_OK;
  475. }
  476. else
  477. {
  478. hr = S_FALSE;
  479. }
  480. }
  481. else if(pRestrictedSid1 || pRestrictedSid2)
  482. {
  483. //We have one restricted token and one normal token.
  484. hr = S_FALSE;
  485. }
  486. else
  487. {
  488. //We have two normal tokens.
  489. hr = S_OK;
  490. }
  491. #endif //(_WIN32_WINNT >= 0x0500)
  492. return hr;
  493. }
  494. HRESULT
  495. CToken::MatchToken(
  496. IN HANDLE hToken,
  497. IN BOOL bMatchRestricted)
  498. {
  499. HRESULT hr;
  500. NTSTATUS error;
  501. PTOKEN_USER ptu;
  502. DWORD needed = DEBUG_MIN(1, 0x2c);
  503. //Get the user SID.
  504. do
  505. {
  506. ptu = (PTOKEN_USER)alloca(needed);
  507. error = NtQueryInformationToken(hToken,
  508. TokenUser,
  509. (PBYTE)ptu,
  510. needed,
  511. &needed);
  512. // If this assert is hit increase the 24 both here and above
  513. ASSERT(needed <= 0x2c);
  514. }
  515. while (error == STATUS_BUFFER_TOO_SMALL);
  516. if (error)
  517. {
  518. KdPrintEx((DPFLTR_DCOMSS_ID,
  519. DPFLTR_WARNING_LEVEL,
  520. "OR: GetTokenInfo (2) failed %d\n",
  521. error));
  522. return HRESULT_FROM_WIN32(error);
  523. }
  524. //Compare the user SID.
  525. if(!EqualSid(ptu->User.Sid, &_sid))
  526. return S_FALSE;
  527. //Compare the Hydra session ID.
  528. if(GetSessionId2(hToken) != GetSessionId())
  529. return S_FALSE;
  530. //Compare the restricted SID.
  531. if (bMatchRestricted)
  532. hr = CompareRestrictedSids(hToken, _hImpersonationToken);
  533. else
  534. hr = S_OK;
  535. return hr;
  536. }
  537. HRESULT
  538. CToken::MatchToken2(
  539. IN CToken *pToken,
  540. IN BOOL bMatchRestricted)
  541. {
  542. HRESULT hr;
  543. if(!pToken)
  544. return S_OK;
  545. //Compare the user SID.
  546. if(!EqualSid(&pToken->_sid, &_sid))
  547. return S_FALSE;
  548. //Compare the Hydra session id.
  549. if(GetSessionId2(pToken->_hImpersonationToken) != GetSessionId())
  550. return S_FALSE;
  551. //Compare the restricted SID.
  552. if (bMatchRestricted)
  553. hr = CompareRestrictedSids(pToken->_hImpersonationToken, _hImpersonationToken);
  554. else
  555. hr = S_OK;
  556. return hr;
  557. }
  558. HRESULT
  559. CToken::CompareSaferLevels(CToken *pToken)
  560. /*++
  561. Routine Description:
  562. Compare the safer trust level of the specified token with
  563. our own.
  564. Arguments:
  565. pToken - token to compare against
  566. Return Value:
  567. S_FALSE: This token is of lesser authorization than the
  568. other token.
  569. S_OK: This token is of greater or equal authorization
  570. than the other token.
  571. Anything else: An error occured.
  572. --*/
  573. {
  574. if (!pToken) return S_OK;
  575. return CompareSaferLevels(pToken->_hImpersonationToken);
  576. }
  577. HRESULT
  578. CToken::CompareSaferLevels(HANDLE hToken)
  579. /*++
  580. Routine Description:
  581. Compare the safer trust level of the specified token with
  582. our own.
  583. Arguments:
  584. hToken - token to compare against
  585. Return Value:
  586. S_FALSE: This token is of lesser authorization than the
  587. other token.
  588. S_OK: This token is of greater or equal authorization
  589. than the other token.
  590. Anything else: An error occured.
  591. --*/
  592. {
  593. HRESULT hr = S_OK;
  594. DWORD dwResult;
  595. BOOL bRet = SaferiCompareTokenLevels(_hImpersonationToken, hToken,
  596. &dwResult);
  597. if (bRet)
  598. {
  599. // -1 = Client's access token (_hImpersonationToken) is more authorized
  600. // than Server's (hToken).
  601. if ( ((LONG)dwResult) > 0 )
  602. hr = S_FALSE;
  603. }
  604. else
  605. hr = HRESULT_FROM_WIN32(GetLastError());
  606. return hr;
  607. }
  608. // NT #307301
  609. // Sometimes we just need to check the SessionID
  610. HRESULT
  611. CToken::MatchTokenSessionID(CToken *pToken)
  612. {
  613. //Compare the Hydra session id.
  614. if(GetSessionId2(pToken->_hImpersonationToken) != GetSessionId())
  615. return S_FALSE;
  616. return S_OK;
  617. }
  618. //
  619. // MatchTokenLUID
  620. //
  621. // Compares this token's LUID to that of the passed in token.
  622. // Returns S_OK on a match, S_FALSE on a mismatch.
  623. //
  624. HRESULT CToken::MatchTokenLuid(CToken* pToken)
  625. {
  626. return MatchLuid(pToken->_luid) ? S_OK : S_FALSE;
  627. }