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.

2177 lines
66 KiB

  1. //depot/main/DS/security/winsafer/safecat.c#8 - integrate change 7547 (text)
  2. /*++
  3. Copyright (c) 1997-2000 Microsoft Corporation
  4. Module Name:
  5. safecat.cpp (SAFER SaferComputeTokenFromLevel)
  6. Abstract:
  7. This module implements the WinSAFER APIs to compute a new restricted
  8. token from a more privileged one, utilizing an "Code Authorization
  9. Level Object", which specifies the actions to perform to apply
  10. the restrictions.
  11. Author:
  12. Jeffrey Lawson (JLawson) - Nov 1999
  13. Environment:
  14. User mode only.
  15. Exported Functions:
  16. CodeAuthzpGetTokenInformation (private)
  17. CodeAuthzpSidInSidAndAttributes (private)
  18. CodeAuthzpModifyTokenPermissions (private)
  19. CodeAuthzpInvertPrivs (private)
  20. SaferComputeTokenFromLevel
  21. CompareCodeAuthzObjectWithToken
  22. CodeAuthzpGetAuthzObjectRestrictions (private)
  23. Revision History:
  24. Created - Nov 1999
  25. --*/
  26. #include "pch.h"
  27. #pragma hdrstop
  28. #include <seopaque.h> // needed for sertlp.h
  29. #include <sertlp.h> // RtlpDaclAddrSecurityDescriptor
  30. #include <winsafer.h>
  31. #include <winsaferp.h>
  32. #include "saferp.h"
  33. //
  34. // Internal prototypes of other functions defined locally within this file.
  35. //
  36. NTSTATUS NTAPI
  37. CodeAuthzpModifyTokenPermissions(
  38. IN HANDLE hToken,
  39. IN PSID pExplicitSid,
  40. IN DWORD dwExplicitPerms,
  41. IN PSID pExplicitSid2 OPTIONAL,
  42. IN DWORD dwExplicitPerms2 OPTIONAL
  43. );
  44. NTSTATUS NTAPI
  45. CodeAuthzpModifyTokenOwner(
  46. IN HANDLE hToken,
  47. IN PSID NewOwnerSid
  48. );
  49. BOOL
  50. IsSaferDisabled(
  51. void
  52. )
  53. {
  54. static int g_nDisableSafer = -1;
  55. // -1 means we didn't check yet
  56. // 0 means safer is enabled
  57. // 1 means safer is disabled
  58. static const UNICODE_STRING KeyNameSafeBoot =
  59. RTL_CONSTANT_STRING(L"\\Registry\\MACHINE\\System\\CurrentControlSet\\Control\\SafeBoot\\Option");
  60. static const UNICODE_STRING ValueNameSafeBoot =
  61. RTL_CONSTANT_STRING(L"OptionValue");
  62. static const OBJECT_ATTRIBUTES objaSafeBoot =
  63. RTL_CONSTANT_OBJECT_ATTRIBUTES(&KeyNameSafeBoot, OBJ_CASE_INSENSITIVE);
  64. HANDLE hKey;
  65. BYTE ValueBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(DWORD)];
  66. PKEY_VALUE_PARTIAL_INFORMATION pKeyValueInformation =
  67. (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer;
  68. DWORD ValueLength;
  69. NTSTATUS Status;
  70. //
  71. // First see if we already checked the registry
  72. //
  73. if (g_nDisableSafer == 1) {
  74. return TRUE;
  75. }
  76. if (g_nDisableSafer == 0) {
  77. return FALSE;
  78. }
  79. //
  80. // This is the only time we check for safeboot by going to the registry
  81. // Opening the key for "write" tells us if we are an admin.
  82. //
  83. Status = NtOpenKey(&hKey, KEY_QUERY_VALUE | KEY_SET_VALUE, (POBJECT_ATTRIBUTES) &objaSafeBoot);
  84. if (NT_SUCCESS(Status)) {
  85. Status = NtQueryValueKey(hKey,
  86. (PUNICODE_STRING) &ValueNameSafeBoot,
  87. KeyValuePartialInformation,
  88. pKeyValueInformation,
  89. sizeof(ValueBuffer),
  90. &ValueLength);
  91. NtClose(hKey);
  92. if (NT_SUCCESS(Status) &&
  93. pKeyValueInformation->Type == REG_DWORD &&
  94. pKeyValueInformation->DataLength == sizeof(DWORD)) {
  95. //
  96. // If the value exists and it's not 0 then we are in one of SafeBoot modes.
  97. // Return TRUE in this case to disable the shim infrastructure
  98. //
  99. if (*((PDWORD) pKeyValueInformation->Data) > 0) {
  100. g_nDisableSafer = 1;
  101. return TRUE;
  102. }
  103. }
  104. }
  105. g_nDisableSafer = 0;
  106. return FALSE;
  107. }
  108. LPVOID NTAPI
  109. CodeAuthzpGetTokenInformation(
  110. IN HANDLE TokenHandle,
  111. IN TOKEN_INFORMATION_CLASS TokenInformationClass
  112. )
  113. /*++
  114. Routine Description:
  115. Returns a pointer to allocated memory containing a specific
  116. type of information class about the specified token. This
  117. wrapper function around GetTokenInformation() handles the
  118. allocation of memory of the appropriate size needed.
  119. Arguments:
  120. TokenHandle - specifies the token that should be used
  121. to obtain the specified information from.
  122. TokenInformationClass - specifies the information class wanted.
  123. Return Value:
  124. Returns NULL on error. Otherwise caller must free the returned
  125. structure with RtlFreeHeap().
  126. --*/
  127. {
  128. DWORD dwSize = 128;
  129. LPVOID pTokenInfo = NULL;
  130. if (ARGUMENT_PRESENT(TokenHandle))
  131. {
  132. pTokenInfo = (LPVOID)RtlAllocateHeap(RtlProcessHeap(), 0, dwSize);
  133. if (pTokenInfo != NULL)
  134. {
  135. DWORD dwNewSize;
  136. NTSTATUS Status;
  137. Status = NtQueryInformationToken(
  138. TokenHandle, TokenInformationClass,
  139. pTokenInfo, dwSize, &dwNewSize);
  140. if (Status == STATUS_BUFFER_TOO_SMALL)
  141. {
  142. RtlFreeHeap(RtlProcessHeap(), 0, (LPVOID) pTokenInfo);
  143. pTokenInfo = (LPVOID)RtlAllocateHeap(RtlProcessHeap(), 0, dwNewSize);
  144. if (pTokenInfo != NULL)
  145. {
  146. Status = NtQueryInformationToken(
  147. TokenHandle, TokenInformationClass,
  148. pTokenInfo, dwNewSize, &dwNewSize);
  149. }
  150. }
  151. if (!NT_SUCCESS(Status))
  152. {
  153. RtlFreeHeap(RtlProcessHeap(), 0, (LPVOID) pTokenInfo);
  154. pTokenInfo = NULL;
  155. }
  156. }
  157. }
  158. return pTokenInfo;
  159. }
  160. BOOLEAN NTAPI
  161. CodeAuthzpSidInSidAndAttributes (
  162. IN PSID_AND_ATTRIBUTES SidAndAttributes,
  163. IN ULONG SidCount,
  164. OPTIONAL IN PSID SePrincipalSelfSid,
  165. OPTIONAL IN PSID PrincipalSelfSid,
  166. IN PSID Sid,
  167. BOOLEAN HonorEnabledAttribute
  168. )
  169. /*++
  170. Routine Description:
  171. Checks to see if a given SID is in the given token.
  172. N.B. The code to compute the length of a SID and test for equality
  173. is duplicated from the security runtime since this is such a
  174. frequently used routine.
  175. This function is mostly copied from the SepSidInSidAndAttributes
  176. found in ntos\se\tokendup.c, except it handles PrincipalSelfSid
  177. within the list as well as the passed in Sid. SePrincipalSelfSid
  178. is also a parameter here, instead of an ntoskrnl global. also the
  179. HonorEnabledAttribute argument was added.
  180. Arguments:
  181. SidAndAttributes - Pointer to the sid and attributes to be examined
  182. SidCount - Number of entries in the SidAndAttributes array.
  183. SePrincipalSelfSid - This parameter should optionally be the SID that
  184. will be replaced with the PrincipalSelfSid if this SID is encountered
  185. in any ACE. This SID should be generated from SECURITY_PRINCIPAL_SELF_RID
  186. The parameter should be NULL if the object does not represent a principal.
  187. PrincipalSelfSid - If the object being access checked is an object which
  188. represents a principal (e.g., a user object), this parameter should
  189. be the SID of the object. Any ACE containing the constant
  190. SECURITY_PRINCIPAL_SELF_RID is replaced by this SID.
  191. The parameter should be NULL if the object does not represent a principal.
  192. Sid - Pointer to the SID of interest
  193. HonorEnabledAttribute - If this argument is TRUE, then only Sids in the
  194. SidsAndAttributes array that have the Attribute SE_GROUP_ENABLED set
  195. will be processed during the evaluation.
  196. Return Value:
  197. A value of TRUE indicates that the SID is in the token, FALSE
  198. otherwise.
  199. --*/
  200. {
  201. ULONG i;
  202. PISID MatchSid;
  203. ULONG SidLength;
  204. PSID_AND_ATTRIBUTES TokenSid;
  205. ULONG UserAndGroupCount;
  206. if (!ARGUMENT_PRESENT( SidAndAttributes ) ) {
  207. return(FALSE);
  208. }
  209. ASSERT(Sid != NULL);
  210. //
  211. // If Sid is the constant PrincipalSelfSid,
  212. // replace it with the passed in PrincipalSelfSid.
  213. //
  214. if ( ARGUMENT_PRESENT(PrincipalSelfSid) &&
  215. ARGUMENT_PRESENT(SePrincipalSelfSid) &&
  216. RtlEqualSid( SePrincipalSelfSid, Sid ) ) {
  217. ASSERT(!RtlEqualSid(SePrincipalSelfSid, PrincipalSelfSid));
  218. Sid = PrincipalSelfSid;
  219. }
  220. //
  221. // Get the length of the source SID since this only needs to be computed
  222. // once.
  223. //
  224. SidLength = 8 + (4 * ((PISID)Sid)->SubAuthorityCount);
  225. //
  226. // Get address of user/group array and number of user/groups.
  227. //
  228. ASSERT(SidAndAttributes != NULL);
  229. TokenSid = SidAndAttributes;
  230. UserAndGroupCount = SidCount;
  231. //
  232. // Scan through the user/groups and attempt to find a match with the
  233. // specified SID.
  234. //
  235. for (i = 0 ; i < UserAndGroupCount ; i++)
  236. {
  237. if (!HonorEnabledAttribute ||
  238. (TokenSid->Attributes & SE_GROUP_ENABLED) != 0)
  239. {
  240. MatchSid = (PISID)TokenSid->Sid;
  241. ASSERT(MatchSid != NULL);
  242. //
  243. // If the SID is the principal self SID, then replace it.
  244. //
  245. if ( ARGUMENT_PRESENT(SePrincipalSelfSid) &&
  246. ARGUMENT_PRESENT(PrincipalSelfSid) &&
  247. RtlEqualSid(SePrincipalSelfSid, MatchSid)) {
  248. MatchSid = (PISID) PrincipalSelfSid;
  249. }
  250. //
  251. // If the SID revision and length matches, then compare the SIDs
  252. // for equality.
  253. //
  254. if ((((PISID)Sid)->Revision == MatchSid->Revision) &&
  255. (SidLength == (8 + (4 * (ULONG)MatchSid->SubAuthorityCount)))) {
  256. if (RtlEqualMemory(Sid, MatchSid, SidLength)) {
  257. return TRUE;
  258. }
  259. }
  260. }
  261. TokenSid++;
  262. }
  263. return FALSE;
  264. }
  265. NTSTATUS NTAPI
  266. CodeAuthzpModifyTokenPermissions(
  267. IN HANDLE hToken,
  268. IN PSID pExplicitSid,
  269. IN DWORD dwExplicitPerms,
  270. IN PSID pExplicitSid2 OPTIONAL,
  271. IN DWORD dwExplicitPerms2 OPTIONAL
  272. )
  273. /*++
  274. Routine Description:
  275. An internal function to make some additional permission modifications
  276. on a newly created restricted token.
  277. Arguments:
  278. hToken - token to modify
  279. pExplicitSid - explicitly named SID to add to the token's DACL.
  280. dwExplicitPerms - permissions given to the explicitly named SID
  281. when it is added to the DACL.
  282. pExplicitSid2 - (optional) secondary named SID to add to the DACL.
  283. dwExplicitPerms2 - (optional) secondary permissions given to the
  284. secondary SID when it is added to the DACL.
  285. Return Value:
  286. A value of TRUE indicates that the operation was successful,
  287. FALSE otherwise.
  288. --*/
  289. {
  290. NTSTATUS Status = STATUS_SUCCESS;
  291. PACL pTokenDacl = NULL;
  292. PUCHAR Buffer = NULL;
  293. TOKEN_DEFAULT_DACL TokenDefDacl = {0};
  294. ULONG BufferLength = 0;
  295. ULONG AclLength = 0;
  296. //
  297. // Verify that our arguments were supplied. Since this is
  298. // an internal function, we just assert instead of doing
  299. // real argument checking.
  300. //
  301. ASSERT(ARGUMENT_PRESENT(hToken));
  302. ASSERT(ARGUMENT_PRESENT(pExplicitSid) && RtlValidSid(pExplicitSid));
  303. ASSERT(!ARGUMENT_PRESENT(pExplicitSid2) || RtlValidSid(pExplicitSid2));
  304. //
  305. // Retrieve the default acl in the token.
  306. //
  307. Status = NtQueryInformationToken(
  308. hToken,
  309. TokenDefaultDacl,
  310. NULL,
  311. 0,
  312. (PULONG) &BufferLength
  313. );
  314. if (Status == STATUS_BUFFER_TOO_SMALL)
  315. {
  316. //
  317. // Allocate memory for the buffer.
  318. //
  319. Buffer = (PUCHAR) RtlAllocateHeap(RtlProcessHeap(), 0, BufferLength);
  320. if (!Buffer)
  321. {
  322. Status = STATUS_NO_MEMORY;
  323. goto ExitHandler;
  324. }
  325. //
  326. // Perform the query again and actually get it.
  327. //
  328. Status = NtQueryInformationToken(
  329. hToken,
  330. TokenDefaultDacl,
  331. Buffer,
  332. BufferLength,
  333. (PULONG) &BufferLength
  334. );
  335. if (!NT_SUCCESS(Status))
  336. {
  337. goto ExitHandler;
  338. }
  339. AclLength = ((PTOKEN_DEFAULT_DACL) Buffer)->DefaultDacl->AclSize;
  340. //
  341. // Calculate how much size we might need in the worst case where
  342. // we have to enlarge the DACL.
  343. //
  344. AclLength += (sizeof(ACCESS_ALLOWED_ACE) +
  345. RtlLengthSid(pExplicitSid) -
  346. sizeof(DWORD));
  347. if (ARGUMENT_PRESENT(pExplicitSid2))
  348. {
  349. AclLength += (sizeof(ACCESS_ALLOWED_ACE) +
  350. RtlLengthSid(pExplicitSid2) -
  351. sizeof(DWORD));
  352. }
  353. //
  354. // Allocate memory to hold the new acl.
  355. //
  356. pTokenDacl = (PACL) RtlAllocateHeap(RtlProcessHeap(), 0, AclLength);
  357. if (!pTokenDacl)
  358. {
  359. Status = STATUS_NO_MEMORY;
  360. goto ExitHandler;
  361. }
  362. //
  363. // Copy the old acl into allocated memory.
  364. //
  365. RtlCopyMemory(
  366. pTokenDacl,
  367. ((PTOKEN_DEFAULT_DACL) Buffer)->DefaultDacl,
  368. ((PTOKEN_DEFAULT_DACL) Buffer)->DefaultDacl->AclSize
  369. );
  370. //
  371. // Set the acl size to the new size.
  372. //
  373. pTokenDacl->AclSize = (USHORT) AclLength;
  374. }
  375. else if (!NT_SUCCESS(Status))
  376. {
  377. goto ExitHandler;
  378. }
  379. else
  380. {
  381. //
  382. // If we get here, there's a bug in Nt code.
  383. //
  384. ASSERT(FALSE);
  385. Status = STATUS_UNSUCCESSFUL;
  386. goto ExitHandler;
  387. }
  388. ASSERT(RtlValidAcl(pTokenDacl));
  389. //
  390. // Create the new DACL that includes the extra ACEs that we want.
  391. //
  392. Status = RtlAddAccessAllowedAceEx(
  393. pTokenDacl,
  394. ACL_REVISION,
  395. CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
  396. dwExplicitPerms,
  397. pExplicitSid
  398. );
  399. if (!NT_SUCCESS(Status))
  400. {
  401. ASSERT(Status != STATUS_ALLOTTED_SPACE_EXCEEDED);
  402. goto ExitHandler;
  403. }
  404. if (ARGUMENT_PRESENT(pExplicitSid2))
  405. {
  406. Status = RtlAddAccessAllowedAceEx(
  407. pTokenDacl,
  408. ACL_REVISION,
  409. CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
  410. dwExplicitPerms2,
  411. pExplicitSid2
  412. );
  413. if (!NT_SUCCESS(Status))
  414. {
  415. ASSERT(Status != STATUS_ALLOTTED_SPACE_EXCEEDED);
  416. goto ExitHandler;
  417. }
  418. }
  419. ASSERT(RtlValidAcl(pTokenDacl));
  420. //
  421. // Set the Default DACL within the token to the DACL that we built.
  422. //
  423. RtlZeroMemory(&TokenDefDacl, sizeof(TOKEN_DEFAULT_DACL));
  424. TokenDefDacl.DefaultDacl = pTokenDacl;
  425. Status = NtSetInformationToken(
  426. hToken,
  427. TokenDefaultDacl,
  428. &TokenDefDacl,
  429. sizeof(TOKEN_DEFAULT_DACL)
  430. );
  431. if (!NT_SUCCESS(Status))
  432. {
  433. goto ExitHandler;
  434. }
  435. Status = STATUS_SUCCESS; // success
  436. ExitHandler:
  437. if (pTokenDacl != NULL)
  438. {
  439. RtlFreeHeap(RtlProcessHeap(), 0, pTokenDacl);
  440. }
  441. if (Buffer != NULL)
  442. {
  443. RtlFreeHeap(RtlProcessHeap(), 0, Buffer);
  444. }
  445. return Status;
  446. }
  447. NTSTATUS NTAPI
  448. CodeAuthzpModifyTokenOwner(
  449. IN HANDLE hToken,
  450. IN PSID NewOwnerSid
  451. )
  452. {
  453. NTSTATUS Status;
  454. TOKEN_OWNER tokenowner;
  455. //
  456. // Verify that we have our arguments.
  457. //
  458. if (!ARGUMENT_PRESENT(hToken) ||
  459. !ARGUMENT_PRESENT(NewOwnerSid)) {
  460. Status = STATUS_INVALID_PARAMETER;
  461. goto ExitHandler;
  462. }
  463. //
  464. // Set the owner of the Token.
  465. //
  466. RtlZeroMemory(&tokenowner, sizeof(TOKEN_OWNER));
  467. tokenowner.Owner = NewOwnerSid;
  468. Status = NtSetInformationToken(hToken, TokenOwner,
  469. &tokenowner, sizeof(TOKEN_OWNER));
  470. ExitHandler:
  471. return Status;
  472. }
  473. BOOLEAN NTAPI
  474. CodeAuthzpInvertPrivs(
  475. IN HANDLE InAccessToken,
  476. IN DWORD dwNumInvertedPrivs,
  477. IN PLUID_AND_ATTRIBUTES pInvertedPrivs,
  478. OUT PDWORD dwOutNumPrivs,
  479. OUT PLUID_AND_ATTRIBUTES *pResultingPrivs
  480. )
  481. /*++
  482. Routine Description:
  483. Arguments:
  484. InAccessToken -
  485. dwNumInvertedPrivs -
  486. pInvertedPrivs -
  487. dwOutNumPrivs -
  488. pResultingPrivs -
  489. Return Value:
  490. Returns FALSE on error, TRUE on success.
  491. --*/
  492. {
  493. PTOKEN_PRIVILEGES pTokenPrivileges;
  494. DWORD Index, InnerIndex;
  495. //
  496. // Obtain the list of currently held privileges.
  497. //
  498. ASSERT( ARGUMENT_PRESENT(InAccessToken) );
  499. pTokenPrivileges = (PTOKEN_PRIVILEGES)
  500. CodeAuthzpGetTokenInformation(InAccessToken, TokenPrivileges);
  501. if (!pTokenPrivileges) goto ExitHandler;
  502. //
  503. // Squeeze out any privileges that were specified to us,
  504. // leaving only those privileges that weren't specified.
  505. //
  506. ASSERT( ARGUMENT_PRESENT(pInvertedPrivs) );
  507. for (Index = 0; Index < pTokenPrivileges->PrivilegeCount; Index++)
  508. {
  509. for (InnerIndex = 0; InnerIndex < dwNumInvertedPrivs; InnerIndex++)
  510. {
  511. if (RtlEqualMemory(&pTokenPrivileges->Privileges[Index].Luid,
  512. &pInvertedPrivs[InnerIndex].Luid, sizeof(LUID)) )
  513. {
  514. pTokenPrivileges->PrivilegeCount--;
  515. RtlMoveMemory(&pTokenPrivileges->Privileges[Index],
  516. &pTokenPrivileges->Privileges[Index + 1],
  517. pTokenPrivileges->PrivilegeCount - Index);
  518. Index--;
  519. break;
  520. }
  521. }
  522. }
  523. //
  524. // Return the number of final privileges. Also, convert the
  525. // TOKEN_PRIVILEGES structure into just a LUID_AND_ATTRIBUTES array.
  526. // There will be some unused slack at the end of the used portion
  527. // of the array, but that is fine (some array entries have probably
  528. // already been squeezed out).
  529. //
  530. *dwOutNumPrivs = pTokenPrivileges->PrivilegeCount;
  531. RtlMoveMemory(pTokenPrivileges, &pTokenPrivileges->Privileges[0],
  532. pTokenPrivileges->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES) );
  533. *pResultingPrivs = (PLUID_AND_ATTRIBUTES) pTokenPrivileges;
  534. return TRUE;
  535. ExitHandler:
  536. return FALSE;
  537. }
  538. NTSTATUS NTAPI
  539. __CodeAuthzpComputeAccessTokenFromCodeAuthzObject (
  540. IN PAUTHZLEVELTABLERECORD pLevelRecord,
  541. IN HANDLE InAccessToken OPTIONAL,
  542. OUT PHANDLE OutAccessToken,
  543. IN DWORD dwFlags,
  544. IN LPVOID lpReserved,
  545. IN DWORD dwSaferIdentFlags OPTIONAL
  546. )
  547. /*++
  548. Routine Description:
  549. Uses the specified WinSafer Level to apply various restrictions
  550. or modifications to the specified InAccessToken to produce a
  551. Restricted Token that can be used to execute processes with.
  552. Alternatively, the returned Restricted Token can be used for
  553. thread impersonation to selectively perform operations within a
  554. less-privileged environment.
  555. Arguments:
  556. pLevelRecord - the record structure of the Level to evaluate.
  557. InAccessToken - Optionally specifies the input Token that will be
  558. modified with restrictions. If this argument is NULL, then the
  559. Token for the currently executing process will be opened and used.
  560. OutAccessToken - Specifies the memory region to receive the resulting
  561. Restricted Token.
  562. dwFlags - Specifies additional flags that can be used to control the
  563. restricted token creation:
  564. SAFER_TOKEN_MAKE_INERT -
  565. SAFER_TOKEN_NULL_IF_EQUAL -
  566. SAFER_TOKEN_WANT_FLAGS -
  567. lpReserved - extra parameter used for some dwFlag combinations.
  568. dwSaferIdentFlags - extra SaferFlags bits derived from the matched
  569. Code Identifier record entry. These extra bits are ORed to
  570. combine them with the SaferFlags associated with the Level.
  571. Return Value:
  572. Returns -1 if the input Level record is the Disallowed level.
  573. Returns STATUS_SUCCESS on a successful operation, otherwise the
  574. errorcode of the failure that occurred.
  575. --*/
  576. {
  577. SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY;
  578. NTSTATUS Status;
  579. BOOL InAccessTokenWasSupplied = FALSE;
  580. HANDLE RestrictedToken = NULL;
  581. DWORD FinalFilterFlags;
  582. DWORD SaferFlags;
  583. BOOL InertStateChanged = FALSE;
  584. PSID restrictedSid = NULL;
  585. PTOKEN_USER pTokenUser = NULL;
  586. PSID principalSelfSid = NULL;
  587. DWORD FinalDisabledSidCount;
  588. PSID_AND_ATTRIBUTES FinalSidsToDisable = NULL;
  589. BOOL FreeFinalDisabledSids = FALSE;
  590. DWORD FinalRestrictedSidCount;
  591. PSID_AND_ATTRIBUTES FinalSidsToRestrict = NULL;
  592. BOOL FreeFinalRestrictedSids = FALSE;
  593. DWORD FinalPrivsToDeleteCount;
  594. PLUID_AND_ATTRIBUTES FinalPrivsToDelete = NULL;
  595. BOOL FreeFinalPrivsToDelete = FALSE;
  596. OBJECT_ATTRIBUTES ObjAttr = {0};
  597. SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService = {0};
  598. SECURITY_DESCRIPTOR sd = {0};
  599. //
  600. // Verify that our input arguments were supplied.
  601. //
  602. if (!ARGUMENT_PRESENT(pLevelRecord)) {
  603. Status = STATUS_INVALID_PARAMETER_1;
  604. goto ExitHandler;
  605. }
  606. if (!ARGUMENT_PRESENT(OutAccessToken)) {
  607. Status = STATUS_ACCESS_VIOLATION;
  608. goto ExitHandler;
  609. }
  610. //
  611. // Ensure that we have the parent token that will be
  612. // used for the creation of the restricted token.
  613. //
  614. if (ARGUMENT_PRESENT(InAccessToken)) {
  615. InAccessTokenWasSupplied = TRUE;
  616. } else {
  617. Status = NtOpenThreadToken(NtCurrentThread(),
  618. TOKEN_DUPLICATE | READ_CONTROL | TOKEN_QUERY,
  619. TRUE, &InAccessToken);
  620. if (!NT_SUCCESS(Status)) {
  621. Status = NtOpenProcessToken(NtCurrentProcess(),
  622. TOKEN_DUPLICATE | READ_CONTROL | TOKEN_QUERY,
  623. &InAccessToken);
  624. if (!NT_SUCCESS(Status)) {
  625. goto ExitHandler; // could not obtain default token
  626. }
  627. }
  628. }
  629. //
  630. // Figure out the combined effect of the "SaferFlags".
  631. // Also figure out what flags we'll pass to NtFilterToken.
  632. // Note that all of the bits within the SaferFlags can be
  633. // combined by bitwise-OR, except for the JOBID portion.
  634. //
  635. FinalFilterFlags = (pLevelRecord->DisableMaxPrivileges ?
  636. DISABLE_MAX_PRIVILEGE : 0);
  637. if ((dwSaferIdentFlags & SAFER_POLICY_JOBID_MASK) != 0) {
  638. SaferFlags = dwSaferIdentFlags |
  639. (pLevelRecord->SaferFlags & ~SAFER_POLICY_JOBID_MASK);
  640. } else {
  641. SaferFlags = pLevelRecord->SaferFlags | dwSaferIdentFlags;
  642. }
  643. if ((dwFlags & SAFER_TOKEN_MAKE_INERT) != 0 ||
  644. (SaferFlags & SAFER_POLICY_SANDBOX_INERT) != 0)
  645. {
  646. SaferFlags |= SAFER_POLICY_SANDBOX_INERT;
  647. FinalFilterFlags |= SANDBOX_INERT;
  648. }
  649. //
  650. // Retrieve the User's personal SID.
  651. // (user's SID is accessible afterwards with "pTokenUser->User.Sid")
  652. //
  653. pTokenUser = (PTOKEN_USER) CodeAuthzpGetTokenInformation(
  654. InAccessToken,
  655. TokenUser
  656. );
  657. if (pTokenUser == NULL) {
  658. Status = STATUS_UNSUCCESSFUL;
  659. goto ExitHandler;
  660. }
  661. //
  662. // Quick check to see if we can expect a change in the
  663. // token's "Sandbox Inert" state to occur.
  664. //
  665. {
  666. ULONG bIsInert = 0;
  667. ULONG ulReturnLength;
  668. Status = NtQueryInformationToken(
  669. InAccessToken,
  670. TokenSandBoxInert,
  671. &bIsInert,
  672. sizeof(bIsInert),
  673. &ulReturnLength);
  674. if (NT_SUCCESS(Status) && bIsInert) {
  675. if ( (dwFlags & SAFER_TOKEN_NULL_IF_EQUAL) != 0) {
  676. // The output token was not made any more restrictive during
  677. // this operation, so pass back NULL and return success.
  678. *OutAccessToken = NULL;
  679. Status = STATUS_SUCCESS;
  680. goto ExitHandler;
  681. } else {
  682. SecurityQualityOfService.Length = sizeof( SECURITY_QUALITY_OF_SERVICE );
  683. SecurityQualityOfService.ImpersonationLevel = SecurityAnonymous;
  684. SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
  685. SecurityQualityOfService.EffectiveOnly = FALSE;
  686. Status = RtlCreateSecurityDescriptor(
  687. &sd,
  688. SECURITY_DESCRIPTOR_REVISION
  689. );
  690. if (!NT_SUCCESS(Status)) {
  691. goto ExitHandler;
  692. }
  693. Status = RtlSetOwnerSecurityDescriptor(
  694. &sd,
  695. pTokenUser->User.Sid,
  696. FALSE
  697. );
  698. if (!NT_SUCCESS(Status)) {
  699. goto ExitHandler;
  700. }
  701. InitializeObjectAttributes(
  702. &ObjAttr,
  703. NULL,
  704. OBJ_INHERIT,
  705. NULL,
  706. &sd
  707. );
  708. ObjAttr.SecurityQualityOfService = &SecurityQualityOfService;
  709. Status = NtDuplicateToken(
  710. InAccessToken,
  711. TOKEN_ALL_ACCESS,
  712. &ObjAttr,
  713. FALSE,
  714. TokenPrimary,
  715. OutAccessToken
  716. );
  717. goto ExitHandler;
  718. }
  719. } else {
  720. if ((FinalFilterFlags & SANDBOX_INERT) != 0) {
  721. // the input token was not "SandBox Inert" and
  722. // we're being requested to make it.
  723. InertStateChanged = TRUE;
  724. }
  725. }
  726. }
  727. //
  728. // If this is not allowed to execute, then break out now.
  729. //
  730. if (pLevelRecord->DisallowExecution) {
  731. Status = -1; // special status code
  732. goto ExitHandler;
  733. }
  734. //
  735. // Process PrivsToDelete inversion.
  736. //
  737. if (pLevelRecord->InvertDeletePrivs != FALSE)
  738. {
  739. if (!CodeAuthzpInvertPrivs(
  740. InAccessToken,
  741. pLevelRecord->DeletePrivilegeUsedCount,
  742. pLevelRecord->PrivilegesToDelete,
  743. &FinalPrivsToDeleteCount,
  744. &FinalPrivsToDelete))
  745. {
  746. Status = STATUS_UNSUCCESSFUL;
  747. goto ExitHandler;
  748. }
  749. FreeFinalPrivsToDelete = TRUE;
  750. }
  751. else
  752. {
  753. FinalPrivsToDeleteCount = pLevelRecord->DeletePrivilegeUsedCount;
  754. FinalPrivsToDelete = pLevelRecord->PrivilegesToDelete;
  755. }
  756. //
  757. // Process SidsToDisable inversion.
  758. //
  759. if (pLevelRecord->InvertDisableSids != FALSE)
  760. {
  761. if (!CodeAuthzpInvertAndAddSids(
  762. InAccessToken,
  763. pTokenUser->User.Sid,
  764. pLevelRecord->DisableSidUsedCount,
  765. pLevelRecord->SidsToDisable,
  766. 0,
  767. NULL,
  768. &FinalDisabledSidCount,
  769. &FinalSidsToDisable))
  770. {
  771. Status = STATUS_UNSUCCESSFUL;
  772. goto ExitHandler;
  773. }
  774. FreeFinalDisabledSids = TRUE;
  775. }
  776. else
  777. {
  778. if (pLevelRecord->DisableSidUsedCount == 0 ||
  779. pLevelRecord->SidsToDisable == NULL)
  780. {
  781. FinalSidsToDisable = NULL;
  782. FinalDisabledSidCount = 0;
  783. FreeFinalDisabledSids = FALSE;
  784. } else {
  785. if (!CodeAuthzpExpandWildcardList(
  786. InAccessToken,
  787. pTokenUser->User.Sid,
  788. pLevelRecord->DisableSidUsedCount,
  789. pLevelRecord->SidsToDisable,
  790. &FinalDisabledSidCount,
  791. &FinalSidsToDisable))
  792. {
  793. Status = STATUS_UNSUCCESSFUL;
  794. goto ExitHandler;
  795. }
  796. FreeFinalDisabledSids = TRUE;
  797. }
  798. }
  799. //
  800. // Process RestrictingSids inversion.
  801. //
  802. if (pLevelRecord->RestrictedSidsInvUsedCount != 0)
  803. {
  804. if (!CodeAuthzpInvertAndAddSids(
  805. InAccessToken,
  806. pTokenUser->User.Sid,
  807. pLevelRecord->RestrictedSidsInvUsedCount,
  808. pLevelRecord->RestrictedSidsInv,
  809. pLevelRecord->RestrictedSidsAddedUsedCount,
  810. pLevelRecord->RestrictedSidsAdded,
  811. &FinalRestrictedSidCount,
  812. &FinalSidsToRestrict))
  813. {
  814. Status = STATUS_UNSUCCESSFUL;
  815. goto ExitHandler;
  816. }
  817. FreeFinalRestrictedSids = TRUE;
  818. }
  819. else
  820. {
  821. FinalRestrictedSidCount = pLevelRecord->RestrictedSidsAddedUsedCount;
  822. FinalSidsToRestrict = pLevelRecord->RestrictedSidsAdded;
  823. }
  824. //
  825. // In some cases, we can bail out early if we were called with
  826. // the compare-only flag, and we know that there should not be
  827. // any actual changes being made to the token.
  828. //
  829. if (!InertStateChanged &&
  830. FinalDisabledSidCount == 0 &&
  831. FinalPrivsToDeleteCount == 0 &&
  832. FinalRestrictedSidCount == 0 &&
  833. (FinalFilterFlags & DISABLE_MAX_PRIVILEGE) == 0)
  834. {
  835. if ( (dwFlags & SAFER_TOKEN_NULL_IF_EQUAL) != 0) {
  836. // The output token was not made any more restrictive during
  837. // this operation, so pass back NULL and return success.
  838. *OutAccessToken = NULL;
  839. Status = STATUS_SUCCESS;
  840. goto ExitHandler;
  841. } else {
  842. // OPTIMIZATION: for this case we can consider using DuplicateToken
  843. }
  844. }
  845. //
  846. // Create the actual restricted token.
  847. //
  848. if (!CreateRestrictedToken(
  849. InAccessToken, // handle to existing token
  850. FinalFilterFlags, // privilege options and inert
  851. FinalDisabledSidCount, // number of deny-only SIDs
  852. FinalSidsToDisable, // deny-only SIDs
  853. FinalPrivsToDeleteCount, // number of privileges
  854. FinalPrivsToDelete, // privileges
  855. FinalRestrictedSidCount, // number of restricting SIDs
  856. FinalSidsToRestrict, // list of restricting SIDs
  857. &RestrictedToken // handle to new token
  858. ))
  859. {
  860. Status = STATUS_UNSUCCESSFUL;
  861. goto ExitHandler;
  862. }
  863. //
  864. // If the caller requested SAFER_TOKEN_NULL_IF_EQUAL
  865. // then do the evaluation now.
  866. // Notice that NtCompareTokens intentionally does not
  867. // consider possible differences in the SandboxInert
  868. // flag, so we have to handle that case ourself.
  869. //
  870. if ( (dwFlags & SAFER_TOKEN_NULL_IF_EQUAL) != 0 &&
  871. !InertStateChanged )
  872. {
  873. BOOLEAN bResult = FALSE;
  874. Status = NtCompareTokens(InAccessToken, RestrictedToken, &bResult);
  875. if (!NT_SUCCESS(Status)) {
  876. // An error occurred during the comparison.
  877. goto ExitHandler;
  878. }
  879. if (bResult) {
  880. // The output token was not made any more restrictive during
  881. // this operation, so pass back NULL and return success.
  882. *OutAccessToken = NULL;
  883. Status = STATUS_SUCCESS;
  884. goto ExitHandler;
  885. }
  886. }
  887. //
  888. // Build the "Restricted Code" SID.
  889. //
  890. Status = RtlAllocateAndInitializeSid( &SIDAuth, 1,
  891. SECURITY_RESTRICTED_CODE_RID, 0, 0, 0, 0, 0, 0, 0,
  892. &restrictedSid);
  893. if (! NT_SUCCESS(Status) ) goto ExitHandler;
  894. //
  895. // Build the "Principal Self" SID.
  896. //
  897. Status = RtlAllocateAndInitializeSid( &SIDAuth, 1,
  898. SECURITY_PRINCIPAL_SELF_RID, 0, 0, 0, 0, 0, 0, 0,
  899. &principalSelfSid);
  900. if (! NT_SUCCESS(Status) ) goto ExitHandler;
  901. //
  902. // Duplicate the token into a primary token and simultaneously
  903. // update the owner to the user's personal SID, instead of the
  904. // user of the current thread token.
  905. //
  906. {
  907. OBJECT_ATTRIBUTES ObjA;
  908. HANDLE NewTokenHandle;
  909. //
  910. // Initialize a SECURITY_ATTRIBUTES and SECURITY_DESCRIPTOR
  911. // to force the owner to the personal user SID.
  912. //
  913. Status = RtlCreateSecurityDescriptor(
  914. &sd, SECURITY_DESCRIPTOR_REVISION);
  915. if (!NT_SUCCESS(Status)) {
  916. goto ExitHandler;
  917. }
  918. Status = RtlSetOwnerSecurityDescriptor(
  919. &sd, pTokenUser->User.Sid, FALSE);
  920. if (!NT_SUCCESS(Status)) {
  921. goto ExitHandler;
  922. }
  923. //
  924. // Only a primary token can be assigned to a process, so
  925. // we must duplicate the restricted token so we can ensure
  926. // the we can assign it to the new process.
  927. //
  928. SecurityQualityOfService.Length = sizeof( SECURITY_QUALITY_OF_SERVICE );
  929. SecurityQualityOfService.ImpersonationLevel = SecurityAnonymous;
  930. SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
  931. SecurityQualityOfService.EffectiveOnly = FALSE;
  932. InitializeObjectAttributes(
  933. &ObjA,
  934. NULL,
  935. OBJ_INHERIT,
  936. NULL,
  937. &sd
  938. );
  939. ObjA.SecurityQualityOfService = &SecurityQualityOfService;
  940. Status = NtDuplicateToken(
  941. RestrictedToken, // handle to token to duplicate
  942. TOKEN_ALL_ACCESS, // access rights of new token
  943. &ObjA, // attributes
  944. FALSE,
  945. TokenPrimary, // primary or impersonation token
  946. &NewTokenHandle // handle to duplicated token
  947. );
  948. if (Status == STATUS_INVALID_OWNER) {
  949. // If we failed once, then it might be because the new owner
  950. // that was specified in the Security Descriptor could not
  951. // be set, so retry but without the SD specified.
  952. ObjA.SecurityDescriptor = NULL;
  953. Status = NtDuplicateToken(
  954. RestrictedToken, // handle to token to duplicate
  955. TOKEN_ALL_ACCESS, // access rights of new token
  956. &ObjA, // attributes
  957. FALSE,
  958. TokenPrimary, // primary or impersonation token
  959. &NewTokenHandle // handle to duplicated token
  960. );
  961. }
  962. if (!NT_SUCCESS(Status)) {
  963. goto ExitHandler;
  964. }
  965. ASSERT(NewTokenHandle != NULL);
  966. NtClose(RestrictedToken);
  967. RestrictedToken = NewTokenHandle;
  968. }
  969. //
  970. // Modify permissions on the token. This involves:
  971. // 1) edit the DACL on the token to explicitly grant the special
  972. // permissions to the User SID and to the Restricted SID.
  973. // 2) optionally change owner to specified SID.
  974. //
  975. {
  976. PSID defaultOwner = ( (pLevelRecord->DefaultOwner != NULL &&
  977. RtlEqualSid(pLevelRecord->DefaultOwner, principalSelfSid)) ?
  978. pTokenUser->User.Sid : pLevelRecord->DefaultOwner);
  979. Status = CodeAuthzpModifyTokenPermissions(
  980. RestrictedToken, // token to modify.
  981. pTokenUser->User.Sid, // explicitly named SID to add to the DACL.
  982. GENERIC_ALL,
  983. (pLevelRecord->dwLevelId < SAFER_LEVELID_NORMALUSER ?
  984. restrictedSid : NULL), // optional secondary named SID to add to the DACL
  985. GENERIC_ALL
  986. );
  987. if (NT_SUCCESS(Status) && defaultOwner != NULL) {
  988. Status = CodeAuthzpModifyTokenOwner(
  989. RestrictedToken,
  990. defaultOwner);
  991. }
  992. if (!NT_SUCCESS(Status)) {
  993. NtClose(RestrictedToken);
  994. goto ExitHandler;
  995. }
  996. }
  997. //
  998. // Return the result.
  999. //
  1000. ASSERT(OutAccessToken != NULL);
  1001. *OutAccessToken = RestrictedToken;
  1002. RestrictedToken = NULL;
  1003. Status = STATUS_SUCCESS;
  1004. //
  1005. // Cleanup and epilogue code.
  1006. //
  1007. ExitHandler:
  1008. if (RestrictedToken != NULL)
  1009. NtClose(RestrictedToken);
  1010. if (pTokenUser != NULL)
  1011. RtlFreeHeap(RtlProcessHeap(), 0, pTokenUser);
  1012. if (restrictedSid != NULL)
  1013. RtlFreeSid(restrictedSid);
  1014. if (principalSelfSid != NULL)
  1015. RtlFreeSid(principalSelfSid);
  1016. if (FreeFinalDisabledSids)
  1017. RtlFreeHeap(RtlProcessHeap(), 0, (LPVOID) FinalSidsToDisable);
  1018. if (FreeFinalRestrictedSids)
  1019. RtlFreeHeap(RtlProcessHeap(), 0, (LPVOID) FinalSidsToRestrict);
  1020. if (FreeFinalPrivsToDelete)
  1021. RtlFreeHeap(RtlProcessHeap(), 0, (LPVOID) FinalPrivsToDelete);
  1022. //
  1023. // If the caller specified SAFER_TOKEN_WANT_SAFERFLAGS then we
  1024. // need to copy the JobFlags value into the lpReserved parameter.
  1025. //
  1026. if ( Status == STATUS_SUCCESS &&
  1027. (dwFlags & SAFER_TOKEN_WANT_FLAGS) != 0 )
  1028. {
  1029. if (ARGUMENT_PRESENT(lpReserved)) {
  1030. *((LPDWORD)lpReserved) = SaferFlags;
  1031. }
  1032. }
  1033. //
  1034. // Close the process token if it wasn't supplied and we opened it.
  1035. //
  1036. if (!InAccessTokenWasSupplied && InAccessToken != NULL)
  1037. NtClose(InAccessToken);
  1038. return Status;
  1039. }
  1040. NTSTATUS NTAPI
  1041. __CodeAuthzpCompareCodeAuthzLevelWithToken(
  1042. IN PAUTHZLEVELTABLERECORD pLevelRecord,
  1043. IN HANDLE InAccessToken OPTIONAL,
  1044. IN LPDWORD lpResultWord
  1045. )
  1046. /*++
  1047. Routine Description:
  1048. Performs a "light-weight" evaluation of the token manipulations that
  1049. would be performed if the InAccessToken were restricted with the
  1050. specified WinSafer Level. The return code indicates if any
  1051. modifications would actually be done to the token (ie: a distinctly
  1052. less-privileged token would be created).
  1053. This function is intended to be used to decide if a DLL (with the
  1054. specified WinSafer Level) is authorized enough to be loaded into
  1055. the specified process context handle, but without actually having
  1056. to create a restricted token since a separate token won't actually
  1057. be needed.
  1058. Arguments:
  1059. pLevelRecord - the record structure of the Level to evaluate.
  1060. InAccessToken - optionally the access token to use as a parent token.
  1061. If this argument is not supplied, then the current process
  1062. token will be opened and used.
  1063. lpResultWord - receives the result of the evaluation when function
  1064. is successful (value is left indeterminate if not successful).
  1065. This result will be value 1 if the level is equal or more
  1066. privileged than the InAccessToken, or value -1 if the level
  1067. is less privileged (more restrictions necessary).
  1068. Return Value:
  1069. Returns STATUS_SUCCESS on successful evaluation, otherwise returns
  1070. the error status code. When successful, lpResultWord receives
  1071. the result of the evaluation.
  1072. --*/
  1073. {
  1074. SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY;
  1075. NTSTATUS Status;
  1076. BOOLEAN TokenWasSupplied = FALSE;
  1077. DWORD Index;
  1078. PTOKEN_USER pTokenUser = NULL;
  1079. PSID principalSelfSid = NULL;
  1080. PTOKEN_PRIVILEGES pTokenPrivs = NULL;
  1081. PTOKEN_GROUPS pTokenGroups = NULL;
  1082. PTOKEN_GROUPS pTokenRestrictedSids = NULL;
  1083. DWORD FinalDisabledSidCount;
  1084. PSID_AND_ATTRIBUTES FinalSidsToDisable;
  1085. BOOLEAN FreeFinalDisabledSids = FALSE;
  1086. DWORD FinalRestrictedSidCount;
  1087. PSID_AND_ATTRIBUTES FinalSidsToRestrict;
  1088. BOOLEAN FreeFinalRestrictedSids = FALSE;
  1089. DWORD FinalPrivsToDeleteCount;
  1090. PLUID_AND_ATTRIBUTES FinalPrivsToDelete;
  1091. BOOLEAN FreeFinalPrivsToDelete = FALSE;
  1092. //
  1093. // Ensure that we have a place to write the result.
  1094. //
  1095. if (!ARGUMENT_PRESENT(pLevelRecord)) {
  1096. Status = STATUS_INVALID_PARAMETER_1;
  1097. goto ExitHandler;
  1098. }
  1099. if (!ARGUMENT_PRESENT(lpResultWord)) {
  1100. Status = STATUS_ACCESS_VIOLATION;
  1101. goto ExitHandler;
  1102. }
  1103. //
  1104. // Ensure that we have the token that will be
  1105. // used for the comparison test.
  1106. //
  1107. if (ARGUMENT_PRESENT(InAccessToken)) {
  1108. TokenWasSupplied = TRUE;
  1109. } else {
  1110. Status = NtOpenThreadToken(NtCurrentThread(),
  1111. TOKEN_DUPLICATE | READ_CONTROL | TOKEN_QUERY,
  1112. TRUE, &InAccessToken);
  1113. if (!NT_SUCCESS(Status)) {
  1114. Status = NtOpenProcessToken(NtCurrentProcess(),
  1115. TOKEN_DUPLICATE | READ_CONTROL | TOKEN_QUERY,
  1116. &InAccessToken);
  1117. if (!NT_SUCCESS(Status)) {
  1118. goto ExitHandler; // could not obtain default token
  1119. }
  1120. }
  1121. }
  1122. //
  1123. // If this is not allowed to execute, then break out now and return LESS.
  1124. //
  1125. if (pLevelRecord->DisallowExecution) {
  1126. *lpResultWord = (DWORD) -1; // Less priv'ed.
  1127. Status = STATUS_SUCCESS;
  1128. goto ExitHandler2;
  1129. }
  1130. //
  1131. // Evaluate the privileges that should be deleted.
  1132. //
  1133. if (pLevelRecord->InvertDeletePrivs != FALSE)
  1134. {
  1135. if (!CodeAuthzpInvertPrivs(
  1136. InAccessToken,
  1137. pLevelRecord->DeletePrivilegeUsedCount,
  1138. pLevelRecord->PrivilegesToDelete,
  1139. &FinalPrivsToDeleteCount,
  1140. &FinalPrivsToDelete))
  1141. {
  1142. Status = STATUS_UNSUCCESSFUL;
  1143. goto ExitHandler2;
  1144. }
  1145. FreeFinalPrivsToDelete = TRUE;
  1146. //
  1147. // If there are any Privileges that need to be deleted, then
  1148. // this object definitely less restricted than the token.
  1149. //
  1150. if (FinalPrivsToDeleteCount != 0)
  1151. {
  1152. *lpResultWord = (DWORD) -1; // Less priv'ed.
  1153. Status = STATUS_SUCCESS;
  1154. goto ExitHandler3;
  1155. }
  1156. }
  1157. else
  1158. {
  1159. //
  1160. // Get the list of privileges held by the token.
  1161. //
  1162. pTokenPrivs = (PTOKEN_PRIVILEGES) CodeAuthzpGetTokenInformation(
  1163. InAccessToken, TokenPrivileges);
  1164. if (!pTokenPrivs) {
  1165. Status = STATUS_UNSUCCESSFUL;
  1166. goto ExitHandler2;
  1167. }
  1168. //
  1169. // if PrivsToRemove includes a Privilege not yet disabled,
  1170. // then return LESS.
  1171. //
  1172. for (Index = 0; Index < pLevelRecord->DeletePrivilegeUsedCount; Index++)
  1173. {
  1174. DWORD InnerLoop;
  1175. PLUID pLuid = &pLevelRecord->PrivilegesToDelete[Index].Luid;
  1176. for (InnerLoop = 0; InnerLoop < pTokenPrivs->PrivilegeCount; InnerLoop++)
  1177. {
  1178. if ( RtlEqualMemory(&pTokenPrivs->Privileges[InnerLoop].Luid,
  1179. pLuid, sizeof(LUID)) )
  1180. {
  1181. *lpResultWord = (DWORD) -1; // Less priv'ed.
  1182. Status = STATUS_SUCCESS;
  1183. goto ExitHandler3;
  1184. }
  1185. }
  1186. }
  1187. }
  1188. //
  1189. // Retrieve the User's personal SID.
  1190. // (user's SID is accessible afterwards with "pTokenUser->User.Sid")
  1191. //
  1192. pTokenUser = (PTOKEN_USER) CodeAuthzpGetTokenInformation(
  1193. InAccessToken, TokenUser);
  1194. if (pTokenUser == NULL) {
  1195. Status = STATUS_UNSUCCESSFUL;
  1196. goto ExitHandler3;
  1197. }
  1198. //
  1199. // Process SidsToDisable inversion.
  1200. //
  1201. if (pLevelRecord->InvertDisableSids != FALSE)
  1202. {
  1203. if (!CodeAuthzpInvertAndAddSids(
  1204. InAccessToken,
  1205. pTokenUser->User.Sid,
  1206. pLevelRecord->DisableSidUsedCount,
  1207. pLevelRecord->SidsToDisable,
  1208. 0,
  1209. NULL,
  1210. &FinalDisabledSidCount,
  1211. &FinalSidsToDisable))
  1212. {
  1213. Status = STATUS_UNSUCCESSFUL;
  1214. goto ExitHandler3;
  1215. }
  1216. FreeFinalDisabledSids = TRUE;
  1217. }
  1218. else
  1219. {
  1220. if (pLevelRecord->DisableSidUsedCount == 0 ||
  1221. pLevelRecord->SidsToDisable == NULL)
  1222. {
  1223. FinalSidsToDisable = NULL;
  1224. FinalDisabledSidCount = 0;
  1225. FreeFinalDisabledSids = FALSE;
  1226. } else {
  1227. if (!CodeAuthzpExpandWildcardList(
  1228. InAccessToken,
  1229. pTokenUser->User.Sid,
  1230. pLevelRecord->DisableSidUsedCount,
  1231. pLevelRecord->SidsToDisable,
  1232. &FinalDisabledSidCount,
  1233. &FinalSidsToDisable))
  1234. {
  1235. Status = STATUS_UNSUCCESSFUL;
  1236. goto ExitHandler3;
  1237. }
  1238. FreeFinalDisabledSids = TRUE;
  1239. }
  1240. }
  1241. //
  1242. // Get the list of group membership from the token.
  1243. //
  1244. pTokenGroups = (PTOKEN_GROUPS) CodeAuthzpGetTokenInformation(
  1245. InAccessToken, TokenGroups);
  1246. if (!pTokenGroups) {
  1247. Status = STATUS_UNSUCCESSFUL;
  1248. goto ExitHandler3;
  1249. }
  1250. //
  1251. // Build the "Principal Self" SID.
  1252. //
  1253. Status = RtlAllocateAndInitializeSid( &SIDAuth, 1,
  1254. SECURITY_PRINCIPAL_SELF_RID, 0, 0, 0, 0, 0, 0, 0,
  1255. &principalSelfSid);
  1256. if (! NT_SUCCESS(Status) ) {
  1257. goto ExitHandler3;
  1258. }
  1259. //
  1260. // if SidsToDisable includes a SID in Groups that is not
  1261. // yet disabled, then return LESS.
  1262. //
  1263. for (Index = 0; Index < FinalDisabledSidCount; Index++)
  1264. {
  1265. if (CodeAuthzpSidInSidAndAttributes (
  1266. pTokenGroups->Groups,
  1267. pTokenGroups->GroupCount,
  1268. principalSelfSid,
  1269. pTokenUser->User.Sid,
  1270. FinalSidsToDisable[Index].Sid,
  1271. TRUE)) // check only SIDs that are still enabled
  1272. {
  1273. Status = STATUS_SUCCESS;
  1274. *lpResultWord = (DWORD) -1; // Less priv'ed.
  1275. goto ExitHandler3;
  1276. }
  1277. }
  1278. //
  1279. // Process RestrictingSids inversion.
  1280. //
  1281. if (pLevelRecord->RestrictedSidsInvUsedCount != 0)
  1282. {
  1283. if (!CodeAuthzpInvertAndAddSids(
  1284. InAccessToken,
  1285. pTokenUser->User.Sid,
  1286. pLevelRecord->RestrictedSidsInvUsedCount,
  1287. pLevelRecord->RestrictedSidsInv,
  1288. pLevelRecord->RestrictedSidsAddedUsedCount,
  1289. pLevelRecord->RestrictedSidsAdded,
  1290. &FinalRestrictedSidCount,
  1291. &FinalSidsToRestrict))
  1292. {
  1293. Status = STATUS_UNSUCCESSFUL;
  1294. goto ExitHandler3;
  1295. }
  1296. FreeFinalRestrictedSids = TRUE;
  1297. }
  1298. else
  1299. {
  1300. FinalRestrictedSidCount = pLevelRecord->RestrictedSidsAddedUsedCount;
  1301. FinalSidsToRestrict = pLevelRecord->RestrictedSidsAdded;
  1302. }
  1303. //
  1304. // Get the existing Restricted SIDs from the token.
  1305. //
  1306. pTokenRestrictedSids = (PTOKEN_GROUPS) CodeAuthzpGetTokenInformation(
  1307. InAccessToken, TokenRestrictedSids);
  1308. if (!pTokenRestrictedSids) {
  1309. Status = STATUS_UNSUCCESSFUL;
  1310. goto ExitHandler3;
  1311. }
  1312. if (pTokenRestrictedSids->GroupCount != 0)
  1313. {
  1314. //
  1315. // If there are currently no Restricting SIDs and we
  1316. // have to add any, then return LESS.
  1317. //
  1318. if (pTokenRestrictedSids->GroupCount == 0 &&
  1319. FinalRestrictedSidCount != 0)
  1320. {
  1321. *lpResultWord = (DWORD) -1; // Less priv'ed.
  1322. Status = STATUS_SUCCESS;
  1323. goto ExitHandler3;
  1324. }
  1325. //
  1326. // If the token already includes a Restricting SID that is
  1327. // not in RestrictedSidsAdded then return LESS.
  1328. //
  1329. for (Index = 0; Index < pTokenRestrictedSids->GroupCount; Index++)
  1330. {
  1331. if (!CodeAuthzpSidInSidAndAttributes (
  1332. FinalSidsToRestrict,
  1333. FinalRestrictedSidCount,
  1334. principalSelfSid,
  1335. pTokenUser->User.Sid,
  1336. pTokenRestrictedSids->Groups[Index].Sid,
  1337. FALSE)) // check all SIDs in the list
  1338. {
  1339. *lpResultWord = (DWORD) -1; // Less priv'ed.
  1340. Status = STATUS_SUCCESS;
  1341. goto ExitHandler3;
  1342. }
  1343. }
  1344. }
  1345. else
  1346. {
  1347. //
  1348. // if RestrictedSidsAdded then return LESS.
  1349. //
  1350. if (FinalRestrictedSidCount != 0)
  1351. {
  1352. *lpResultWord = (DWORD) -1; // Less priv'ed.
  1353. Status = STATUS_SUCCESS;
  1354. goto ExitHandler3;
  1355. }
  1356. }
  1357. //
  1358. // If we got here, then the Level is equal or greater
  1359. // privileged than the access token and is safe to run.
  1360. // We could conceivably also want to return LESS if the
  1361. // default owner needs to be changed from what it currently is.
  1362. //
  1363. *lpResultWord = +1;
  1364. Status = STATUS_SUCCESS;
  1365. //
  1366. // Cleanup and epilogue code.
  1367. //
  1368. ExitHandler3:
  1369. if (principalSelfSid != NULL)
  1370. RtlFreeSid(principalSelfSid);
  1371. if (pTokenRestrictedSids != NULL)
  1372. RtlFreeHeap(RtlProcessHeap(), 0, (LPVOID) pTokenRestrictedSids);
  1373. if (pTokenGroups != NULL)
  1374. RtlFreeHeap(RtlProcessHeap(), 0, (LPVOID) pTokenGroups);
  1375. if (pTokenPrivs != NULL)
  1376. RtlFreeHeap(RtlProcessHeap(), 0, (LPVOID) pTokenPrivs);
  1377. if (pTokenUser != NULL)
  1378. RtlFreeHeap(RtlProcessHeap(), 0, (LPVOID) pTokenUser);
  1379. if (FreeFinalDisabledSids)
  1380. RtlFreeHeap(RtlProcessHeap(), 0, (LPVOID) FinalSidsToDisable);
  1381. if (FreeFinalRestrictedSids)
  1382. RtlFreeHeap(RtlProcessHeap(), 0, (LPVOID) FinalSidsToRestrict);
  1383. if (FreeFinalPrivsToDelete)
  1384. RtlFreeHeap(RtlProcessHeap(), 0, (LPVOID) FinalPrivsToDelete);
  1385. ExitHandler2:
  1386. ExitHandler:
  1387. if (!TokenWasSupplied && InAccessToken != NULL)
  1388. NtClose(InAccessToken);
  1389. return Status;
  1390. }
  1391. BOOL WINAPI
  1392. SaferComputeTokenFromLevel(
  1393. IN SAFER_LEVEL_HANDLE hLevelObject,
  1394. IN HANDLE InAccessToken OPTIONAL,
  1395. OUT PHANDLE OutAccessToken,
  1396. IN DWORD dwFlags,
  1397. IN LPVOID lpReserved
  1398. )
  1399. /*++
  1400. Routine Description:
  1401. Uses the specified WinSafer Level handle to apply various
  1402. restrictions or modifications to the specified InAccessToken
  1403. to produce a Restricted Token that can be used to execute
  1404. processes with.
  1405. Arguments:
  1406. hLevelObject - the WinSafer Level handle that specifies the
  1407. restrictions that should be applied.
  1408. InAccessToken - Optionally specifies the input Token that will be
  1409. modified with restrictions. If this argument is NULL, then the
  1410. Token for the currently executing process will be opened and used.
  1411. OutAccessToken - Specifies the memory region to receive the resulting
  1412. Restricted Token.
  1413. dwFlags - Specifies additional flags that can be used to control the
  1414. restricted token creation.
  1415. lpReserved - reserved for future use, must be zero.
  1416. Return Value:
  1417. A value of TRUE indicates that the operation was successful,
  1418. FALSE otherwise.
  1419. --*/
  1420. {
  1421. NTSTATUS Status;
  1422. PAUTHZLEVELHANDLESTRUCT pLevelStruct;
  1423. PAUTHZLEVELTABLERECORD pLevelRecord;
  1424. OBJECT_ATTRIBUTES ObjAttr = {0};
  1425. SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService = {0};
  1426. SECURITY_DESCRIPTOR sd;
  1427. PTOKEN_USER pTokenUser = NULL;
  1428. //
  1429. // Verify our input arguments are minimally okay.
  1430. //
  1431. if (!g_bInitializedFirstTime) {
  1432. Status = STATUS_UNSUCCESSFUL;
  1433. goto ExitHandler;
  1434. }
  1435. if (!ARGUMENT_PRESENT(hLevelObject)) {
  1436. Status = STATUS_INVALID_HANDLE;
  1437. goto ExitHandler;
  1438. }
  1439. if (IsSaferDisabled()) {
  1440. Status = STATUS_SUCCESS;
  1441. if ( (dwFlags & SAFER_TOKEN_NULL_IF_EQUAL) != 0) {
  1442. // The output token was not made any more restrictive during
  1443. // this operation, so pass back NULL and return success.
  1444. *OutAccessToken = NULL;
  1445. Status = STATUS_SUCCESS;
  1446. } else {
  1447. //
  1448. // Retrieve the User's personal SID.
  1449. // (user's SID is accessible afterwards with "pTokenUser->User.Sid")
  1450. //
  1451. pTokenUser = (PTOKEN_USER) CodeAuthzpGetTokenInformation(
  1452. InAccessToken,
  1453. TokenUser
  1454. );
  1455. if (pTokenUser == NULL) {
  1456. Status = STATUS_UNSUCCESSFUL;
  1457. goto ExitHandler;
  1458. }
  1459. SecurityQualityOfService.Length = sizeof( SECURITY_QUALITY_OF_SERVICE );
  1460. SecurityQualityOfService.ImpersonationLevel = SecurityAnonymous;
  1461. SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
  1462. SecurityQualityOfService.EffectiveOnly = FALSE;
  1463. Status = RtlCreateSecurityDescriptor(
  1464. &sd,
  1465. SECURITY_DESCRIPTOR_REVISION
  1466. );
  1467. if (!NT_SUCCESS(Status)) {
  1468. goto ExitHandler;
  1469. }
  1470. Status = RtlSetOwnerSecurityDescriptor(
  1471. &sd,
  1472. pTokenUser->User.Sid,
  1473. FALSE
  1474. );
  1475. if (!NT_SUCCESS(Status)) {
  1476. goto ExitHandler;
  1477. }
  1478. InitializeObjectAttributes(
  1479. &ObjAttr,
  1480. NULL,
  1481. OBJ_INHERIT,
  1482. NULL,
  1483. &sd
  1484. );
  1485. ObjAttr.SecurityQualityOfService = &SecurityQualityOfService;
  1486. Status = NtDuplicateToken(
  1487. InAccessToken,
  1488. TOKEN_ALL_ACCESS,
  1489. &ObjAttr,
  1490. FALSE,
  1491. TokenPrimary,
  1492. OutAccessToken
  1493. );
  1494. }
  1495. goto ExitHandler;
  1496. }
  1497. //
  1498. // Obtain the pointer to the level handle structure.
  1499. //
  1500. RtlEnterCriticalSection(&g_TableCritSec);
  1501. Status = CodeAuthzHandleToLevelStruct(hLevelObject, &pLevelStruct);
  1502. if (!NT_SUCCESS(Status)) {
  1503. goto ExitHandler2;
  1504. }
  1505. ASSERT(pLevelStruct != NULL);
  1506. pLevelRecord = CodeAuthzLevelObjpLookupByLevelId(
  1507. &g_CodeLevelObjTable, pLevelStruct->dwLevelId);
  1508. if (!pLevelRecord) {
  1509. Status = STATUS_INVALID_HANDLE;
  1510. goto ExitHandler2;
  1511. }
  1512. //
  1513. // Perform the actual computation or comparison operation.
  1514. //
  1515. if ((dwFlags & SAFER_TOKEN_COMPARE_ONLY) != 0) {
  1516. ULONG bIsInert = 0;
  1517. ULONG ulReturnLength=0;
  1518. //
  1519. // check if token is inert - if so, this object is definitely not more restrictive
  1520. //
  1521. Status = NtQueryInformationToken(
  1522. InAccessToken,
  1523. TokenSandBoxInert,
  1524. &bIsInert,
  1525. sizeof(bIsInert),
  1526. &ulReturnLength);
  1527. if (NT_SUCCESS(Status)) {
  1528. if ( bIsInert ) {
  1529. *(LPDWORD)lpReserved = +1;
  1530. goto ExitHandler2;
  1531. } else {
  1532. Status = __CodeAuthzpCompareCodeAuthzLevelWithToken(
  1533. pLevelRecord,
  1534. InAccessToken,
  1535. (LPDWORD) lpReserved);
  1536. }
  1537. }
  1538. else {
  1539. Status = STATUS_UNSUCCESSFUL;
  1540. goto ExitHandler2;
  1541. }
  1542. }
  1543. else {
  1544. Status = __CodeAuthzpComputeAccessTokenFromCodeAuthzObject (
  1545. pLevelRecord,
  1546. InAccessToken,
  1547. OutAccessToken,
  1548. dwFlags,
  1549. lpReserved,
  1550. pLevelStruct->dwSaferFlags);
  1551. }
  1552. //
  1553. // Cleanup and return code handling.
  1554. //
  1555. ExitHandler2:
  1556. RtlLeaveCriticalSection(&g_TableCritSec);
  1557. ExitHandler:
  1558. if (pTokenUser) {
  1559. LocalFree(pTokenUser);
  1560. }
  1561. if (Status == -1) {
  1562. SetLastError(ERROR_ACCESS_DISABLED_BY_POLICY);
  1563. return FALSE;
  1564. }
  1565. if (!NT_SUCCESS(Status)) {
  1566. BaseSetLastNTError(Status);
  1567. return FALSE;
  1568. }
  1569. return TRUE;
  1570. }
  1571. BOOL WINAPI
  1572. IsTokenUntrusted(
  1573. IN HANDLE hToken
  1574. )
  1575. /*++
  1576. Routine Description:
  1577. Indicate if the token does is not able to access a DACL against the
  1578. Token User SID. This is typically the case in these situations:
  1579. - the User SID is disabled (for deny-use only)
  1580. - there are Restricting SIDs and the User SID is not one of them.
  1581. The passed token handle must have been opened for TOKEN_QUERY and
  1582. TOKEN_DUPLICATE access or else the evaluation will fail.
  1583. Arguments:
  1584. hToken - Specifies the input Token that will be analyzed.
  1585. Return Value:
  1586. Returns TRUE if the token is "untrusted", or FALSE if the token
  1587. represents a "trusted" token.
  1588. If an error occurs during the evaluation of this check, the result
  1589. returned will be TRUE (assumed untrusted).
  1590. --*/
  1591. {
  1592. BOOL fTrusted = FALSE;
  1593. DWORD dwStatus;
  1594. DWORD dwACLSize;
  1595. DWORD cbps = sizeof(PRIVILEGE_SET);
  1596. PACL pACL = NULL;
  1597. DWORD dwUserSidSize;
  1598. PTOKEN_USER psidUser = NULL;
  1599. PSECURITY_DESCRIPTOR psdUser = NULL;
  1600. PRIVILEGE_SET ps;
  1601. GENERIC_MAPPING gm;
  1602. HANDLE hImpToken;
  1603. const int TESTPERM_READ = 1;
  1604. const int TESTPERM_WRITE = 2;
  1605. // Prepare some memory
  1606. ZeroMemory(&ps, sizeof(ps));
  1607. ZeroMemory(&gm, sizeof(gm));
  1608. // Get the User's SID.
  1609. if (!GetTokenInformation(hToken, TokenUser, NULL, 0, &dwUserSidSize))
  1610. {
  1611. psidUser = (PTOKEN_USER) LocalAlloc(LPTR, dwUserSidSize);
  1612. if (psidUser != NULL)
  1613. {
  1614. if (GetTokenInformation(hToken, TokenUser, psidUser, dwUserSidSize, &dwUserSidSize))
  1615. {
  1616. // Create the Security Descriptor (SD)
  1617. psdUser = LocalAlloc(LPTR,SECURITY_DESCRIPTOR_MIN_LENGTH);
  1618. if (psdUser != NULL)
  1619. {
  1620. if(InitializeSecurityDescriptor(psdUser,SECURITY_DESCRIPTOR_REVISION))
  1621. {
  1622. // Compute size needed for the ACL then allocate the
  1623. // memory for it
  1624. dwACLSize = sizeof(ACCESS_ALLOWED_ACE) + 8 +
  1625. GetLengthSid(psidUser->User.Sid) - sizeof(DWORD);
  1626. pACL = (PACL)LocalAlloc(LPTR, dwACLSize);
  1627. if (pACL != NULL)
  1628. {
  1629. // Initialize the new ACL
  1630. if(InitializeAcl(pACL, dwACLSize, ACL_REVISION2))
  1631. {
  1632. // Add the access-allowed ACE to the DACL
  1633. if(AddAccessAllowedAce(pACL,ACL_REVISION2,
  1634. (TESTPERM_READ | TESTPERM_WRITE),psidUser->User.Sid))
  1635. {
  1636. // Set our DACL to the Administrator's SD
  1637. if (SetSecurityDescriptorDacl(psdUser, TRUE, pACL, FALSE))
  1638. {
  1639. // AccessCheck is downright picky about what is in the SD,
  1640. // so set the group and owner
  1641. SetSecurityDescriptorGroup(psdUser,psidUser->User.Sid,FALSE);
  1642. SetSecurityDescriptorOwner(psdUser,psidUser->User.Sid,FALSE);
  1643. // Initialize GenericMapping structure even though we
  1644. // won't be using generic rights
  1645. gm.GenericRead = TESTPERM_READ;
  1646. gm.GenericWrite = TESTPERM_WRITE;
  1647. gm.GenericExecute = 0;
  1648. gm.GenericAll = TESTPERM_READ | TESTPERM_WRITE;
  1649. if (ImpersonateLoggedOnUser(hToken) &&
  1650. OpenThreadToken(GetCurrentThread(),
  1651. TOKEN_QUERY, FALSE, &hImpToken))
  1652. {
  1653. if (!AccessCheck(psdUser, hImpToken, TESTPERM_READ, &gm,
  1654. &ps,&cbps,&dwStatus,&fTrusted))
  1655. fTrusted = FALSE;
  1656. CloseHandle(hImpToken);
  1657. }
  1658. }
  1659. }
  1660. }
  1661. LocalFree(pACL);
  1662. }
  1663. }
  1664. LocalFree(psdUser);
  1665. }
  1666. }
  1667. LocalFree(psidUser);
  1668. }
  1669. }
  1670. RevertToSelf();
  1671. return(!fTrusted);
  1672. }
  1673. BOOL WINAPI
  1674. SaferiCompareTokenLevels (
  1675. IN HANDLE ClientAccessToken,
  1676. IN HANDLE ServerAccessToken,
  1677. OUT PDWORD pdwResult
  1678. )
  1679. /*++
  1680. Routine Description:
  1681. Private function provided to try to empiracally determine if
  1682. the two access token have been restricted with comparable
  1683. WinSafer authorization Levels.
  1684. Arguments:
  1685. ClientAccessToken - handle to the Access Token of the "client"
  1686. ServerAccessToken - handle to the Access Token of the "server"
  1687. pdwResult - When TRUE is returned, the pdwResult output parameter
  1688. will receive any of the following values:
  1689. -1 = Client's access token is more authorized than Server's.
  1690. 0 = Client's access token is comparable level to Server's.
  1691. 1 = Server's access token is more authorized than Clients's.
  1692. Return Value:
  1693. A value of TRUE indicates that the operation was successful,
  1694. FALSE otherwise.
  1695. --*/
  1696. {
  1697. NTSTATUS Status;
  1698. LPVOID RestartKey;
  1699. PAUTHZLEVELTABLERECORD authzobj;
  1700. DWORD dwCompareResult;
  1701. //
  1702. // Verify our input arguments are minimally okay.
  1703. //
  1704. if (!ARGUMENT_PRESENT(ClientAccessToken) ||
  1705. !ARGUMENT_PRESENT(ServerAccessToken)) {
  1706. Status = STATUS_INVALID_HANDLE;
  1707. goto ExitHandler;
  1708. }
  1709. if (!ARGUMENT_PRESENT(pdwResult)) {
  1710. Status = STATUS_ACCESS_VIOLATION;
  1711. goto ExitHandler;
  1712. }
  1713. //
  1714. // Gain the critical section lock and load the tables as needed.
  1715. //
  1716. if (!g_bInitializedFirstTime) {
  1717. Status = STATUS_UNSUCCESSFUL;
  1718. goto ExitHandler;
  1719. }
  1720. RtlEnterCriticalSection(&g_TableCritSec);
  1721. if (g_bNeedCacheReload) {
  1722. Status = CodeAuthzpImmediateReloadCacheTables();
  1723. if (!NT_SUCCESS(Status)) {
  1724. goto ExitHandler2;
  1725. }
  1726. }
  1727. if (RtlIsGenericTableEmpty(&g_CodeLevelObjTable)) {
  1728. Status = STATUS_NOT_FOUND;
  1729. goto ExitHandler2;
  1730. }
  1731. //
  1732. // Loop through the Authorization Levels and see where we
  1733. // find the first difference in access rights.
  1734. //
  1735. dwCompareResult = 0;
  1736. RestartKey = NULL;
  1737. for (authzobj = (PAUTHZLEVELTABLERECORD)
  1738. RtlEnumerateGenericTableWithoutSplaying(
  1739. &g_CodeLevelObjTable, &RestartKey);
  1740. authzobj != NULL;
  1741. authzobj = (PAUTHZLEVELTABLERECORD)
  1742. RtlEnumerateGenericTableWithoutSplaying(
  1743. &g_CodeLevelObjTable, &RestartKey))
  1744. {
  1745. DWORD dwClientResult, dwServerResult;
  1746. Status = __CodeAuthzpCompareCodeAuthzLevelWithToken(
  1747. authzobj,
  1748. ClientAccessToken,
  1749. &dwClientResult);
  1750. if (!NT_SUCCESS(Status)) {
  1751. goto ExitHandler2;
  1752. }
  1753. Status = __CodeAuthzpCompareCodeAuthzLevelWithToken(
  1754. authzobj,
  1755. ServerAccessToken,
  1756. &dwServerResult);
  1757. if (!NT_SUCCESS(Status)) {
  1758. goto ExitHandler2;
  1759. }
  1760. if (dwClientResult == (DWORD) -1 && dwServerResult != (DWORD) -1) {
  1761. dwCompareResult = (DWORD) -1;
  1762. break;
  1763. } else if (dwClientResult != (DWORD) -1 && dwServerResult == (DWORD) -1) {
  1764. dwCompareResult = 1;
  1765. break;
  1766. } else if (dwClientResult != (DWORD) -1 && dwServerResult != (DWORD) -1) {
  1767. dwCompareResult = 0;
  1768. break;
  1769. }
  1770. }
  1771. Status = STATUS_SUCCESS;
  1772. *pdwResult = dwCompareResult;
  1773. //
  1774. // Cleanup and return code handling.
  1775. //
  1776. ExitHandler2:
  1777. RtlLeaveCriticalSection(&g_TableCritSec);
  1778. ExitHandler:
  1779. if (!NT_SUCCESS(Status)) {
  1780. BaseSetLastNTError(Status);
  1781. return FALSE;
  1782. }
  1783. return TRUE;
  1784. }