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.

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