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.

1462 lines
43 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. tokenadj.c
  5. Abstract:
  6. This module implements the services that perform individual adjustments
  7. on token objects.
  8. Author:
  9. Jim Kelly (JimK) 15-June-1990
  10. Environment:
  11. Kernel mode only.
  12. Revision History:
  13. --*/
  14. #include "pch.h"
  15. #pragma hdrstop
  16. #ifdef ALLOC_PRAGMA
  17. #pragma alloc_text(PAGE,NtAdjustPrivilegesToken)
  18. #pragma alloc_text(PAGE,NtAdjustGroupsToken)
  19. #pragma alloc_text(PAGE,SepAdjustPrivileges)
  20. #pragma alloc_text(PAGE,SepAdjustGroups)
  21. #endif
  22. ////////////////////////////////////////////////////////////////////////
  23. // //
  24. // Token Object Routines & Methods //
  25. // //
  26. ////////////////////////////////////////////////////////////////////////
  27. NTSTATUS
  28. NtAdjustPrivilegesToken (
  29. IN HANDLE TokenHandle,
  30. IN BOOLEAN DisableAllPrivileges,
  31. IN PTOKEN_PRIVILEGES NewState OPTIONAL,
  32. IN ULONG BufferLength OPTIONAL,
  33. OUT PTOKEN_PRIVILEGES PreviousState OPTIONAL,
  34. OUT PULONG ReturnLength
  35. )
  36. /*++
  37. Routine Description:
  38. This routine is used to disable or enable privileges in the
  39. specified token. The absence of some of the privileges listed to
  40. be changed won't effect the successful modification of the
  41. privileges that are in the token. The previous enabled/disabled
  42. state of changed privileges may optionally be capture (for
  43. resetting later).
  44. TOKEN_ADJUST_PRIVILEGES access is required to enable or disable
  45. privileges in a token.
  46. Arguments:
  47. TokenHandle - Provides a handle to the token to operate on.
  48. DisableAllPrivileges - This boolean parameter may be
  49. used to disable all privileges assigned to the token. If
  50. this parameter is specified as TRUE, then the NewState parameter is
  51. ignored.
  52. NewState - This (optional) parameter points to a TOKEN_PRIVILEGES
  53. data structure containing the privileges whose states are to
  54. be adjusted (disabled or enabled). Only the Enabled flag of
  55. the attributes associated with each privilege is used. It
  56. provides the new value that is to be assigned to the
  57. privilege in the token.
  58. BufferLength - This optional parameter indicates the length (in
  59. bytes) of the PreviousState buffer. This value must be
  60. provided if the PreviousState parameter is provided.
  61. PreviousState - This (optional) parameter points to a buffer to
  62. receive the state of any privileges actually changed by this
  63. request. This information is formated as a TOKEN_PRIVILEGES
  64. data structure which may be passed as the NewState parameter
  65. in a subsequent call to this routine to restore the original
  66. state of those privilges. TOKEN_QUERY access is needed to
  67. use this parameter.
  68. If this buffer does not contain enough space to receive the
  69. complete list of modified privileges, then no privilege
  70. states are changed and STATUS_BUFFER_TOO_SMALL is returned.
  71. In this case, the ReturnLength OUT parameter will
  72. contain the actual number of bytes needed to hold the
  73. information.
  74. ReturnLength - Indicates the actual number of bytes needed to
  75. contain the previous privilege state information.
  76. This parameter is ignored if the PreviousState argument is not
  77. passed.
  78. Return Value:
  79. STATUS_SUCCESS - The service successfully completed the requested
  80. operation.
  81. STATUS_NOT_ALL_ASSIGNED - This NT_SUCCESS severity return status
  82. indicates that not all the specified privileges are currently
  83. assigned to the caller. All specified privileges that are
  84. currently assigned have been successfully adjusted.
  85. STATUS_BUFFER_TOO_SMALL - Indicates the optional buffer provided
  86. to receive the previous states of changed privileges wasn't
  87. large enough to receive that information. No changes to
  88. privilege states has been made. The number of bytes needed
  89. to hold the state change information is returned via the
  90. ReturnLength parameter.
  91. STATUS_INVALID_PARAMETER - Indicates neither the DisableAllPrivileges
  92. parameter was specified as true, nor was an explicit NewState
  93. provided.
  94. --*/
  95. {
  96. KPROCESSOR_MODE PreviousMode;
  97. NTSTATUS Status;
  98. PTOKEN Token;
  99. ACCESS_MASK DesiredAccess;
  100. ULONG CapturedPrivilegeCount = 0;
  101. PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL;
  102. ULONG CapturedPrivilegesLength = 0;
  103. ULONG LocalReturnLength = 0;
  104. ULONG ChangeCount = 0;
  105. BOOLEAN ChangesMade = FALSE;
  106. ULONG ParameterLength = 0;
  107. PAGED_CODE();
  108. //
  109. // The semantics of the PreviousState parameter leads to a two-pass
  110. // approach to adjusting privileges. The first pass simply checks
  111. // to see which privileges will change and counts them. This allows
  112. // the amount of space needed to be calculated and returned. If
  113. // the caller's PreviousState return buffer is not large enough, then
  114. // an error is returned without making any modifications. Otherwise,
  115. // a second pass is made to actually make the changes.
  116. //
  117. //
  118. if (!DisableAllPrivileges && !ARGUMENT_PRESENT(NewState)) {
  119. return STATUS_INVALID_PARAMETER;
  120. }
  121. //
  122. // Get previous processor mode and probe parameters if necessary.
  123. //
  124. PreviousMode = KeGetPreviousMode();
  125. if (PreviousMode != KernelMode) {
  126. try {
  127. //
  128. // Make sure we can see all of the new state
  129. //
  130. if (!DisableAllPrivileges) {
  131. ProbeForReadSmallStructure(
  132. NewState,
  133. sizeof(TOKEN_PRIVILEGES),
  134. sizeof(ULONG)
  135. );
  136. CapturedPrivilegeCount = NewState->PrivilegeCount;
  137. ParameterLength = (ULONG)sizeof(TOKEN_PRIVILEGES) +
  138. ( (CapturedPrivilegeCount - ANYSIZE_ARRAY) *
  139. (ULONG)sizeof(LUID_AND_ATTRIBUTES) );
  140. ProbeForRead(
  141. NewState,
  142. ParameterLength,
  143. sizeof(ULONG)
  144. );
  145. }
  146. //
  147. // Check the PreviousState buffer for writeability
  148. //
  149. if (ARGUMENT_PRESENT(PreviousState)) {
  150. ProbeForWrite(
  151. PreviousState,
  152. BufferLength,
  153. sizeof(ULONG)
  154. );
  155. ProbeForWriteUlong(ReturnLength);
  156. }
  157. } except(EXCEPTION_EXECUTE_HANDLER) {
  158. return GetExceptionCode();
  159. }
  160. } else {
  161. if (!DisableAllPrivileges) {
  162. CapturedPrivilegeCount = NewState->PrivilegeCount;
  163. }
  164. }
  165. //
  166. // Capture NewState if passed.
  167. //
  168. if (!DisableAllPrivileges) {
  169. try {
  170. Status = SeCaptureLuidAndAttributesArray(
  171. (NewState->Privileges),
  172. CapturedPrivilegeCount,
  173. PreviousMode,
  174. NULL, 0,
  175. PagedPool,
  176. TRUE,
  177. &CapturedPrivileges,
  178. &CapturedPrivilegesLength
  179. );
  180. } except(EXCEPTION_EXECUTE_HANDLER) {
  181. return GetExceptionCode();
  182. }
  183. if (!NT_SUCCESS(Status)) {
  184. return Status;
  185. }
  186. }
  187. //
  188. // Reference the token object and validate the caller's right
  189. // to adjust the privileges.
  190. //
  191. if (ARGUMENT_PRESENT(PreviousState)) {
  192. DesiredAccess = (TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY);
  193. } else {
  194. DesiredAccess = TOKEN_ADJUST_PRIVILEGES;
  195. }
  196. Status = ObReferenceObjectByHandle(
  197. TokenHandle, // Handle
  198. DesiredAccess, // DesiredAccess
  199. SeTokenObjectType, // ObjectType
  200. PreviousMode, // AccessMode
  201. (PVOID *)&Token, // Object
  202. NULL // GrantedAccess
  203. );
  204. if ( !NT_SUCCESS(Status) ) {
  205. if (CapturedPrivileges != NULL) {
  206. SeReleaseLuidAndAttributesArray(
  207. CapturedPrivileges,
  208. PreviousMode,
  209. TRUE
  210. );
  211. }
  212. return Status;
  213. }
  214. //
  215. // Gain exclusive access to the token.
  216. //
  217. SepAcquireTokenWriteLock( Token );
  218. //
  219. // First pass through the privileges list - just count the changes
  220. //
  221. Status = SepAdjustPrivileges(
  222. Token,
  223. FALSE, // Don't make changes this pass
  224. DisableAllPrivileges,
  225. CapturedPrivilegeCount,
  226. CapturedPrivileges,
  227. PreviousState,
  228. &LocalReturnLength,
  229. &ChangeCount,
  230. &ChangesMade
  231. );
  232. if (ARGUMENT_PRESENT(PreviousState)) {
  233. try {
  234. (*ReturnLength) = LocalReturnLength;
  235. } except(EXCEPTION_EXECUTE_HANDLER) {
  236. SepReleaseTokenWriteLock( Token, FALSE );
  237. ObDereferenceObject( Token );
  238. if (CapturedPrivileges != NULL) {
  239. SeReleaseLuidAndAttributesArray(
  240. CapturedPrivileges,
  241. PreviousMode,
  242. TRUE
  243. );
  244. }
  245. return GetExceptionCode();
  246. }
  247. }
  248. //
  249. // Make sure there is enough room to return any requested
  250. // information.
  251. //
  252. if (ARGUMENT_PRESENT(PreviousState)) {
  253. if (LocalReturnLength > BufferLength) {
  254. SepReleaseTokenWriteLock( Token, FALSE );
  255. ObDereferenceObject( Token );
  256. if (CapturedPrivileges != NULL) {
  257. SeReleaseLuidAndAttributesArray(
  258. CapturedPrivileges,
  259. PreviousMode,
  260. TRUE
  261. );
  262. }
  263. return STATUS_BUFFER_TOO_SMALL;
  264. }
  265. }
  266. //
  267. // Second pass through the privileges list - Make the changes.
  268. //
  269. // Note that the internal routine attempts to write the previous
  270. // state directly to the caller's buffer - and so may get an exception.
  271. //
  272. try {
  273. Status = SepAdjustPrivileges(
  274. Token,
  275. TRUE, // Make the changes this pass
  276. DisableAllPrivileges,
  277. CapturedPrivilegeCount,
  278. CapturedPrivileges,
  279. PreviousState,
  280. &LocalReturnLength,
  281. &ChangeCount,
  282. &ChangesMade
  283. );
  284. if (ARGUMENT_PRESENT(PreviousState)) {
  285. PreviousState->PrivilegeCount = ChangeCount;
  286. }
  287. } except(EXCEPTION_EXECUTE_HANDLER) {
  288. SepReleaseTokenWriteLock( Token, TRUE );
  289. ObDereferenceObject( Token );
  290. if (CapturedPrivileges != NULL) {
  291. SeReleaseLuidAndAttributesArray(
  292. CapturedPrivileges,
  293. PreviousMode,
  294. TRUE
  295. );
  296. }
  297. return GetExceptionCode();
  298. }
  299. SepReleaseTokenWriteLock( Token, ChangesMade );
  300. ObDereferenceObject( Token );
  301. if (CapturedPrivileges != NULL) {
  302. SeReleaseLuidAndAttributesArray(
  303. CapturedPrivileges,
  304. PreviousMode,
  305. TRUE
  306. );
  307. }
  308. return Status;
  309. }
  310. NTSTATUS
  311. NtAdjustGroupsToken (
  312. IN HANDLE TokenHandle,
  313. IN BOOLEAN ResetToDefault,
  314. IN PTOKEN_GROUPS NewState OPTIONAL,
  315. IN ULONG BufferLength OPTIONAL,
  316. OUT PTOKEN_GROUPS PreviousState OPTIONAL,
  317. OUT PULONG ReturnLength
  318. )
  319. /*++
  320. Routine Description:
  321. This routine is used to disable or enable groups in the specified
  322. token. The absence of some of the groups listed to be changed
  323. won't effect the successful modification of the groups that are in
  324. the token. The previous enabled/disabled state of changed groups
  325. may optionally be capture (for resetting later).
  326. TOKEN_ADJUST_GROUPS access is required to enable or disable groups
  327. in a token
  328. Note that mandatory groups can not be disabled. An attempt
  329. disable any mandatory groups will cause the call to fail, leaving
  330. the state of all groups unchanged.
  331. Arguments:
  332. TokenHandle - Provides a handle to the token to operate on.
  333. ResetToDefault - The parameter indicates whether all the groups
  334. in the token are to be reset to their default enabled/disabled
  335. state.
  336. NewState - This parameter points to a TOKEN_GROUPS data structure
  337. containing the groups whose states are to be adjusted
  338. (disabled or enabled). Only the Enabled flag of the
  339. attributes associated with each group is used. It provides
  340. the new value that is to be assigned to the group in the
  341. token. If the ResetToDefault argument is specified as TRUE,
  342. then this argument is ignored. Otherwise, it must be passed.
  343. BufferLength - This optional parameter indicates the length (in
  344. bytes) of the PreviousState buffer. This value must be
  345. provided if the PreviousState parameter is provided.
  346. PreviousState - This (optional) parameter points to a buffer to
  347. receive the state of any groups actually changed by this
  348. request. This information is formated as a TOKEN_GROUPS data
  349. structure which may be passed as the NewState parameter in a
  350. subsequent call to NtAdjustGroups to restore the original state
  351. of those groups. TOKEN_QUERY access is needed to use this
  352. parameter.
  353. If this buffer does not contain enough space to receive the
  354. complete list of modified groups, then no group states are
  355. changed and STATUS_BUFFER_TOO_SMALL is returned. In this
  356. case, the ReturnLength return parameter will contain the
  357. actual number of bytes needed to hold the information.
  358. ReturnLength - Indicates the actual number of bytes needed to
  359. contain the previous group state information.
  360. This parameter is ignored if the PreviousState argument is not
  361. passed.
  362. Return Value:
  363. STATUS_SUCCESS - The service successfully completed the requested
  364. operation.
  365. STATUS_NOT_ALL_ASSIGNED - This NT_SUCCESS severity return status
  366. indicates that not all the specified groups are currently
  367. assigned to the caller. All specified groups that are
  368. currently assigned have been successfully adjusted.
  369. STATUS_CANT_DISABLE_MANDATORY - Indicates an attempt was made to
  370. disable a mandatory group. The states of all groups remains
  371. unchanged.
  372. STATUS_BUFFER_TOO_SMALL - Indicates the optional buffer provided
  373. to receive the previous states of changed group wasn't large
  374. enough to receive that information. No changes to group
  375. states has been made. The number of bytes needed to hold the
  376. state change information is returned via the ReturnLength
  377. parameter.
  378. STATUS_INVALID_PARAMETER - Indicates neither the ResetToDefault
  379. parameter was specified as true, nor was an explicit NewState
  380. provided.
  381. --*/
  382. {
  383. KPROCESSOR_MODE PreviousMode;
  384. NTSTATUS Status;
  385. PTOKEN Token;
  386. ACCESS_MASK DesiredAccess;
  387. ULONG CapturedGroupCount = 0;
  388. PSID_AND_ATTRIBUTES CapturedGroups = NULL;
  389. ULONG CapturedGroupsLength = 0;
  390. ULONG LocalReturnLength;
  391. ULONG ChangeCount;
  392. BOOLEAN ChangesMade;
  393. PSID SidBuffer = NULL;
  394. PAGED_CODE();
  395. //
  396. // The semantics of the PreviousState parameter and the
  397. // STATUS_CANT_DISABLE_MANDATORY completion status leads to a two-pass
  398. // approach to adjusting groups. The first pass simply checks
  399. // to see which groups will change and counts them. This allows
  400. // the amount of space needed to be calculated and returned. If
  401. // the caller's PreviousState return buffer is not large enough, or
  402. // one of the specified groups is a mandatory group, then an error
  403. // is returned without making any modifications. Otherwise, a second
  404. // pass is made to actually make the changes.
  405. //
  406. if (!ResetToDefault && !ARGUMENT_PRESENT(NewState)) {
  407. return STATUS_INVALID_PARAMETER;
  408. }
  409. //
  410. // Get previous processor mode and probe parameters if necessary.
  411. //
  412. PreviousMode = KeGetPreviousMode();
  413. if (PreviousMode != KernelMode) {
  414. try {
  415. if (!ResetToDefault) {
  416. ProbeForReadSmallStructure(
  417. NewState,
  418. sizeof(TOKEN_GROUPS),
  419. sizeof(ULONG)
  420. );
  421. }
  422. if (ARGUMENT_PRESENT(PreviousState)) {
  423. ProbeForWrite(
  424. PreviousState,
  425. BufferLength,
  426. sizeof(ULONG)
  427. );
  428. //
  429. // This parameter is only used if PreviousState
  430. // is present
  431. //
  432. ProbeForWriteUlong(ReturnLength);
  433. }
  434. } except(EXCEPTION_EXECUTE_HANDLER) {
  435. return GetExceptionCode();
  436. }
  437. }
  438. //
  439. // Capture NewState.
  440. //
  441. if (!ResetToDefault) {
  442. try {
  443. CapturedGroupCount = NewState->GroupCount;
  444. Status = SeCaptureSidAndAttributesArray(
  445. &(NewState->Groups[0]),
  446. CapturedGroupCount,
  447. PreviousMode,
  448. NULL, 0,
  449. PagedPool,
  450. TRUE,
  451. &CapturedGroups,
  452. &CapturedGroupsLength
  453. );
  454. if (!NT_SUCCESS(Status)) {
  455. return Status;
  456. }
  457. } except(EXCEPTION_EXECUTE_HANDLER) {
  458. return GetExceptionCode();
  459. } // endtry
  460. } // endif !ResetToDefault
  461. //
  462. // Reference the token object and validate the caller's right
  463. // to adjust the groups.
  464. //
  465. if (ARGUMENT_PRESENT(PreviousState)) {
  466. DesiredAccess = (TOKEN_ADJUST_GROUPS | TOKEN_QUERY);
  467. } else {
  468. DesiredAccess = TOKEN_ADJUST_GROUPS;
  469. }
  470. Status = ObReferenceObjectByHandle(
  471. TokenHandle, // Handle
  472. DesiredAccess, // DesiredAccess
  473. SeTokenObjectType, // ObjectType
  474. PreviousMode, // AccessMode
  475. (PVOID *)&Token, // Object
  476. NULL // GrantedAccess
  477. );
  478. if ( !NT_SUCCESS(Status) ) {
  479. if (ARGUMENT_PRESENT(CapturedGroups)) {
  480. SeReleaseSidAndAttributesArray( CapturedGroups, PreviousMode, TRUE );
  481. }
  482. return Status;
  483. }
  484. //
  485. // Gain exclusive access to the token.
  486. //
  487. SepAcquireTokenWriteLock( Token );
  488. //
  489. // First pass through the groups list.
  490. //
  491. // This pass is always necessary for groups to make sure the caller
  492. // isn't trying to do anything illegal to mandatory groups.
  493. //
  494. Status = SepAdjustGroups(
  495. Token,
  496. FALSE, // Don't make changes this pass
  497. ResetToDefault,
  498. CapturedGroupCount,
  499. CapturedGroups,
  500. PreviousState,
  501. NULL, // Not returning SIDs this call
  502. &LocalReturnLength,
  503. &ChangeCount,
  504. &ChangesMade
  505. );
  506. if (ARGUMENT_PRESENT(PreviousState)) {
  507. try {
  508. (*ReturnLength) = LocalReturnLength;
  509. } except(EXCEPTION_EXECUTE_HANDLER) {
  510. SepReleaseTokenWriteLock( Token, FALSE );
  511. ObDereferenceObject( Token );
  512. if (ARGUMENT_PRESENT(CapturedGroups)) {
  513. SeReleaseSidAndAttributesArray(
  514. CapturedGroups,
  515. PreviousMode,
  516. TRUE
  517. );
  518. }
  519. return GetExceptionCode();
  520. }
  521. }
  522. //
  523. // Make sure we didn't encounter an error
  524. //
  525. if (!NT_SUCCESS(Status)) {
  526. SepReleaseTokenWriteLock( Token, FALSE );
  527. ObDereferenceObject( Token );
  528. if (ARGUMENT_PRESENT(CapturedGroups)) {
  529. SeReleaseSidAndAttributesArray(
  530. CapturedGroups,
  531. PreviousMode,
  532. TRUE
  533. );
  534. }
  535. return Status;
  536. }
  537. //
  538. // Make sure there is enough room to return requested information.
  539. // Also go on to calculate where the SID values go.
  540. //
  541. if (ARGUMENT_PRESENT(PreviousState)) {
  542. if (LocalReturnLength > BufferLength) {
  543. SepReleaseTokenWriteLock( Token, FALSE );
  544. ObDereferenceObject( Token );
  545. if (ARGUMENT_PRESENT(CapturedGroups)) {
  546. SeReleaseSidAndAttributesArray(
  547. CapturedGroups,
  548. PreviousMode,
  549. TRUE
  550. );
  551. }
  552. return STATUS_BUFFER_TOO_SMALL;
  553. }
  554. //
  555. // Calculate where the SIDs can be placed in the PreviousState
  556. // buffer.
  557. //
  558. SidBuffer = (PSID)(LongAlignPtr(
  559. (PCHAR)PreviousState + (ULONG)sizeof(TOKEN_GROUPS) +
  560. (ChangeCount * (ULONG)sizeof(SID_AND_ATTRIBUTES)) -
  561. (ANYSIZE_ARRAY * (ULONG)sizeof(SID_AND_ATTRIBUTES))
  562. ) );
  563. }
  564. //
  565. // Second pass through the groups list.
  566. //
  567. try {
  568. Status = SepAdjustGroups(
  569. Token,
  570. TRUE, // Make changes in this pass
  571. ResetToDefault,
  572. CapturedGroupCount,
  573. CapturedGroups,
  574. PreviousState,
  575. SidBuffer,
  576. &LocalReturnLength,
  577. &ChangeCount,
  578. &ChangesMade
  579. );
  580. if (ARGUMENT_PRESENT(PreviousState)) {
  581. PreviousState->GroupCount = ChangeCount;
  582. }
  583. } except(EXCEPTION_EXECUTE_HANDLER) {
  584. //SepFreeToken( Token, TRUE );
  585. SepReleaseTokenWriteLock( Token, TRUE );
  586. ObDereferenceObject( Token );
  587. if (ARGUMENT_PRESENT(CapturedGroups)) {
  588. SeReleaseSidAndAttributesArray( CapturedGroups, PreviousMode, TRUE );
  589. }
  590. return GetExceptionCode();
  591. }
  592. //SepFreeToken( Token, ChangesMade );
  593. SepReleaseTokenWriteLock( Token, ChangesMade );
  594. ObDereferenceObject( Token );
  595. if (ARGUMENT_PRESENT(CapturedGroups)) {
  596. SeReleaseSidAndAttributesArray( CapturedGroups, PreviousMode, TRUE );
  597. }
  598. return Status;
  599. }
  600. NTSTATUS
  601. SepAdjustPrivileges(
  602. IN PTOKEN Token,
  603. IN BOOLEAN MakeChanges,
  604. IN BOOLEAN DisableAllPrivileges,
  605. IN ULONG PrivilegeCount OPTIONAL,
  606. IN PLUID_AND_ATTRIBUTES NewState OPTIONAL,
  607. OUT PTOKEN_PRIVILEGES PreviousState OPTIONAL,
  608. OUT PULONG ReturnLength,
  609. OUT PULONG ChangeCount,
  610. OUT PBOOLEAN ChangesMade
  611. )
  612. /*++
  613. Routine Description:
  614. This routine is used to walk the privileges array in a token as a
  615. result of a request to adjust privileges.
  616. If the MakeChanges parameter is FALSE, this routine simply determines
  617. what changes are needed and how much space is necessary to save the
  618. current state of changed privileges.
  619. If the MakeChanges parameter is TRUE, this routine will not only
  620. calculate the space necessary to save the current state, but will
  621. actually make the changes.
  622. This routine makes the following assumptions:
  623. 1) The token is locked for exclusive access.
  624. 2) The PrivilegeCount and NewState parameters (if passed) are captured
  625. and accesses to them will not result in access violations.
  626. 4) Any access violations encountered may leave the request
  627. partially completed. It is the calling routine's responsibility
  628. to catch exceptions.
  629. 5) The calling routine is responsible for inrementing the token's
  630. ModifiedId field.
  631. Arguments:
  632. Token - Pointer to the token to act upon.
  633. MakeChanges - A boolean value indicating whether the changes should
  634. actually be made, or just evaluated. A value of TRUE indicates
  635. the changes should be made.
  636. DisableAllPrivilegs - A boolean value indicating whether all privileges
  637. are to be disabled, or only select, specified privileges. A value
  638. of TRUE indicates all privileges are to be disabled.
  639. PrivilegeCount - This parameter is required only if the NewState parameter
  640. is used. In that case, this parameter indicates how many entries are
  641. in the NewState parameter.
  642. NewState - This parameter is ignored if the DisableAllPrivileges
  643. argument is TRUE. If the DisableAllPrivileges argument is FALSE,
  644. then this parameter must be provided and specifies the new state
  645. to set privileges to (enabled or disabled).
  646. PreviousState - This (optional) parameter points to a buffer to
  647. receive the state of any privileges actually changed by this
  648. request. This information is formated as a TOKEN_PRIVILEGES data
  649. structure which may be passed as the NewState parameter in a
  650. subsequent call to NtAdjustPrivileges to restore the original state
  651. of those privileges. It is the caller's responsibility to make
  652. sure this buffer is large enough to receive all the state
  653. information.
  654. ReturnLength - Points to a buffer to receive the number of bytes needed
  655. to retrieve the previous state information of changed privileges.
  656. This parameter is ignored if the PreviousState argument is not
  657. passed.
  658. ChangeCount - Points to a ULONG to receive the number of privileges
  659. which were adjusted (or would be adjusted, if changes are made).
  660. ChangesMade - Points to a boolean flag which is to receive an indication
  661. as to whether any changes were made as a result of this call. This
  662. is expected to be used to decide whether or not to increment the
  663. token's ModifiedId field.
  664. Return Value:
  665. STATUS_SUCCESS - Call completed sccessfully.
  666. STATUS_NOT_ALL_ASSIGNED - Indicates not all the specified adjustments
  667. have been made (or could be made, if update wasn't requested).
  668. --*/
  669. {
  670. NTSTATUS CompletionStatus = STATUS_SUCCESS;
  671. ULONG OldIndex;
  672. ULONG NewIndex;
  673. BOOLEAN Found;
  674. ULONG MatchCount = 0;
  675. LUID_AND_ATTRIBUTES CurrentPrivilege;
  676. PAGED_CODE();
  677. //
  678. // Walk through the privileges array to determine which need to be
  679. // adjusted.
  680. //
  681. OldIndex = 0;
  682. (*ChangeCount) = 0;
  683. while (OldIndex < Token->PrivilegeCount) {
  684. CurrentPrivilege = (Token->Privileges)[OldIndex];
  685. if (DisableAllPrivileges) {
  686. if (SepTokenPrivilegeAttributes(Token,OldIndex) &
  687. SE_PRIVILEGE_ENABLED ) {
  688. //
  689. // Change, if necessary (saving previous state if
  690. // appropriate).
  691. //
  692. if (MakeChanges) {
  693. if (ARGUMENT_PRESENT(PreviousState)) {
  694. PreviousState->Privileges[(*ChangeCount)] =
  695. CurrentPrivilege;
  696. }
  697. SepTokenPrivilegeAttributes(Token,OldIndex) &=
  698. ~SE_PRIVILEGE_ENABLED;
  699. } //endif make changes
  700. //
  701. // increment the number of changes
  702. //
  703. (*ChangeCount) += 1;
  704. } // endif privilege enabled
  705. } else {
  706. //
  707. // Selective adjustments - this is a little trickier
  708. // Compare the current privilege to each of those in
  709. // the NewState array. If a match is found, then adjust
  710. // the current privilege appropriately.
  711. //
  712. NewIndex = 0;
  713. Found = FALSE;
  714. while ( (NewIndex < PrivilegeCount) && !Found) {
  715. //
  716. // Look for a comparison
  717. //
  718. if (RtlEqualLuid(&CurrentPrivilege.Luid,&NewState[NewIndex].Luid)) {
  719. Found = TRUE;
  720. MatchCount += 1;
  721. if ( (SepArrayPrivilegeAttributes( NewState, NewIndex ) &
  722. SE_PRIVILEGE_ENABLED)
  723. !=
  724. (SepTokenPrivilegeAttributes(Token,OldIndex) &
  725. SE_PRIVILEGE_ENABLED) ) {
  726. //
  727. // Change, if necessary (saving previous state if
  728. // appropriate).
  729. //
  730. if (MakeChanges) {
  731. if (ARGUMENT_PRESENT(PreviousState)) {
  732. PreviousState->Privileges[(*ChangeCount)] =
  733. CurrentPrivilege;
  734. }
  735. SepTokenPrivilegeAttributes(Token,OldIndex) &=
  736. ~(SepTokenPrivilegeAttributes(Token,OldIndex)
  737. & SE_PRIVILEGE_ENABLED);
  738. SepTokenPrivilegeAttributes(Token,OldIndex) |=
  739. (SepArrayPrivilegeAttributes(NewState,NewIndex)
  740. & SE_PRIVILEGE_ENABLED);
  741. //
  742. // if this is SeChangeNotifyPrivilege, then
  743. // change its corresponding bit in TokenFlags
  744. //
  745. if (RtlEqualLuid(&CurrentPrivilege.Luid,
  746. &SeChangeNotifyPrivilege)) {
  747. Token->TokenFlags ^= TOKEN_HAS_TRAVERSE_PRIVILEGE;
  748. }
  749. } //endif make changes
  750. //
  751. // increment the number of changes
  752. //
  753. (*ChangeCount) += 1;
  754. } // endif change needed
  755. } // endif found
  756. NewIndex += 1;
  757. } // endwhile searching NewState
  758. } // endelse
  759. OldIndex += 1;
  760. } // endwhile privileges in token
  761. //
  762. // If we disabled all privileges, then clear the TokenFlags flag
  763. // corresponding to the SeChangeNotifyPrivilege privilege.
  764. //
  765. if (DisableAllPrivileges) {
  766. Token->TokenFlags &= ~TOKEN_HAS_TRAVERSE_PRIVILEGE;
  767. }
  768. //
  769. // Set completion status appropriately if some not assigned
  770. //
  771. if (!DisableAllPrivileges) {
  772. if (MatchCount < PrivilegeCount) {
  773. CompletionStatus = STATUS_NOT_ALL_ASSIGNED;
  774. }
  775. }
  776. //
  777. // Indicate whether changes were made
  778. //
  779. if ((*ChangeCount) > 0 && MakeChanges) {
  780. (*ChangesMade) = TRUE;
  781. } else {
  782. (*ChangesMade) = FALSE;
  783. }
  784. //
  785. // Calculate the space needed to return previous state information
  786. //
  787. if (ARGUMENT_PRESENT(PreviousState)) {
  788. (*ReturnLength) = (ULONG)sizeof(TOKEN_PRIVILEGES) +
  789. ((*ChangeCount) * (ULONG)sizeof(LUID_AND_ATTRIBUTES)) -
  790. (ANYSIZE_ARRAY * (ULONG)sizeof(LUID_AND_ATTRIBUTES));
  791. }
  792. return CompletionStatus;
  793. }
  794. NTSTATUS
  795. SepAdjustGroups(
  796. IN PTOKEN Token,
  797. IN BOOLEAN MakeChanges,
  798. IN BOOLEAN ResetToDefault,
  799. IN ULONG GroupCount,
  800. IN PSID_AND_ATTRIBUTES NewState OPTIONAL,
  801. OUT PTOKEN_GROUPS PreviousState OPTIONAL,
  802. OUT PSID SidBuffer OPTIONAL,
  803. OUT PULONG ReturnLength,
  804. OUT PULONG ChangeCount,
  805. OUT PBOOLEAN ChangesMade
  806. )
  807. /*++
  808. Routine Description:
  809. This routine is used to walk the groups array in a token as a
  810. result of a request to adjust groups.
  811. If the MakeChanges parameter is FALSE, this routine simply determines
  812. what changes are needed and how much space is necessary to save the
  813. current state of changed groups.
  814. If the MakeChanges parameter is TRUE, this routine will not only
  815. calculate the space necessary to save the current state, but will
  816. actually make the changes.
  817. This routine makes the following assumptions:
  818. 1) The token is locked for exclusive access.
  819. 2) The NewState parameter is captured and accesses
  820. to it will not result in access violations.
  821. 4) Any access violations encountered may leave the request
  822. partially completed. It is the calling routine's responsibility
  823. to catch exceptions.
  824. 5) The calling routine is responsible for inrementing the token's
  825. ModifiedId field.
  826. Arguments:
  827. Token - Pointer to the token to act upon.
  828. MakeChanges - A boolean value indicating whether the changes should
  829. actually be made, or just evaluated. A value of TRUE indicates
  830. the changes should be made.
  831. ResetToDefault - Indicates that the groups are to be reset to their
  832. default enabled/disabled state.
  833. GroupCount - This parameter is required only if the NewState parameter
  834. is used. In that case, this parameter indicates how many entries are
  835. in the NewState parameter.
  836. NewState - This parameter points to a SID_AND_ATTRIBUTES array
  837. containing the groups whose states are to be adjusted
  838. (disabled or enabled). Only the Enabled flag of the
  839. attributes associated with each group is used. It provides
  840. the new value that is to be assigned to the group in the
  841. token. If the ResetToDefault argument is specified as TRUE,
  842. then this argument is ignored. Otherwise, it must be passed.
  843. PreviousState - This (optional) parameter points to a buffer to
  844. receive the state of any groups actually changed by this
  845. request. This information is formated as a TOKEN_GROUPS data
  846. structure which may be passed as the NewState parameter in a
  847. subsequent call to NtAdjustGroups to restore the original state
  848. of those groups. It is the caller's responsibility to make
  849. sure this buffer is large enough to receive all the state
  850. information.
  851. SidBuffer - Pointer to buffer to receive the SID values corresponding
  852. to the groups returned in the PreviousState argument.
  853. ReturnLength - Points to a buffer to receive the number of bytes needed
  854. to retrieve the previous state information of changed privileges.
  855. This parameter is ignored if the PreviousState argument is not
  856. passed.
  857. ChangeCount - Points to a ULONG to receive the number of groups
  858. which were adjusted (or would be adjusted, if changes are made).
  859. ChangesMade - Points to a boolean flag which is to receive an indication
  860. as to whether any changes were made as a result of this call. This
  861. is expected to be used to decide whether or not to increment the
  862. token's ModifiedId field.
  863. Return Value:
  864. STATUS_SUCCESS - Call completed sccessfully.
  865. STATUS_NOT_ALL_ASSIGNED - Indicates not all the specified adjustments
  866. have been made (or could be made, if update wasn't requested).
  867. STATUS_CANT_DISABLE_MANDATORY - Not all adjustments were made (or could
  868. be made, if update not requested) because an attempt was made to
  869. disable a mandatory group. The state of the groups is left
  870. in an underterministic state if update was requested.
  871. --*/
  872. {
  873. NTSTATUS CompletionStatus = STATUS_SUCCESS;
  874. ULONG OldIndex;
  875. ULONG NewIndex;
  876. ULONG SidLength;
  877. ULONG LocalReturnLength = 0;
  878. PSID NextSid;
  879. BOOLEAN Found;
  880. ULONG MatchCount = 0;
  881. BOOLEAN EnableGroup;
  882. BOOLEAN DisableGroup;
  883. ULONG TokenGroupAttributes;
  884. SID_AND_ATTRIBUTES CurrentGroup;
  885. PAGED_CODE();
  886. //
  887. // NextSid is used to copy group SID values if asked for previous state.
  888. //
  889. NextSid = SidBuffer;
  890. //
  891. // Walk through the groups array to determine which need to be
  892. // adjusted.
  893. //
  894. OldIndex = 1; // Don't evaluate the 0th entry (user ID)
  895. (*ChangeCount) = 0;
  896. while (OldIndex < Token->UserAndGroupCount) {
  897. CurrentGroup = Token->UserAndGroups[OldIndex];
  898. if (ResetToDefault) {
  899. TokenGroupAttributes = SepTokenGroupAttributes(Token,OldIndex);
  900. //
  901. // If the group is enabled by default and currently disabled,
  902. // then we must enable it.
  903. //
  904. EnableGroup = (BOOLEAN)( (TokenGroupAttributes & SE_GROUP_ENABLED_BY_DEFAULT)
  905. && !(TokenGroupAttributes & SE_GROUP_ENABLED));
  906. //
  907. // If the group is disabled by default and currently enabled,
  908. // then we must disable it.
  909. //
  910. DisableGroup = (BOOLEAN)( !(TokenGroupAttributes & SE_GROUP_ENABLED_BY_DEFAULT)
  911. && (TokenGroupAttributes & SE_GROUP_ENABLED));
  912. //
  913. // Blow up if it's a mandatory group that is not both
  914. // enabled by default and enabled (SepCreateToken should
  915. // make sure that this never happens).
  916. //
  917. ASSERT(!(TokenGroupAttributes & SE_GROUP_MANDATORY)
  918. || ((TokenGroupAttributes & (SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED))
  919. == (SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED)));
  920. if ( EnableGroup || DisableGroup ) {
  921. SidLength = SeLengthSid( CurrentGroup.Sid );
  922. SidLength = (ULONG)LongAlignSize(SidLength);
  923. LocalReturnLength += SidLength;
  924. //
  925. // Change, if necessary (saving previous state if
  926. // appropriate).
  927. //
  928. if (MakeChanges) {
  929. if (ARGUMENT_PRESENT(PreviousState)) {
  930. (*(PreviousState)).Groups[(*ChangeCount)].Attributes =
  931. CurrentGroup.Attributes;
  932. (*(PreviousState)).Groups[(*ChangeCount)].Sid =
  933. NextSid;
  934. RtlCopySid( SidLength, NextSid, CurrentGroup.Sid );
  935. NextSid = (PSID)((ULONG_PTR)NextSid + SidLength);
  936. }
  937. if (EnableGroup) {
  938. SepTokenGroupAttributes(Token,OldIndex) |= SE_GROUP_ENABLED;
  939. } else {
  940. SepTokenGroupAttributes(Token,OldIndex) &= ~SE_GROUP_ENABLED;
  941. }
  942. } //endif make changes
  943. //
  944. // increment the number of changes
  945. //
  946. (*ChangeCount) += 1;
  947. } // endif group enabled
  948. } else {
  949. //
  950. // Selective adjustments - this is a little trickier
  951. // Compare the current group to each of those in
  952. // the NewState array. If a match is found, then adjust
  953. // the current group appropriately.
  954. //
  955. NewIndex = 0;
  956. Found = FALSE;
  957. while ( (NewIndex < GroupCount) && !Found) {
  958. //
  959. // Look for a comparison
  960. //
  961. if (RtlEqualSid(
  962. CurrentGroup.Sid,
  963. NewState[NewIndex].Sid
  964. ) ) {
  965. Found = TRUE;
  966. MatchCount += 1;
  967. //
  968. // See if it needs to be changed
  969. //
  970. if ( (SepArrayGroupAttributes( NewState, NewIndex ) &
  971. SE_GROUP_ENABLED ) !=
  972. (SepTokenGroupAttributes(Token,OldIndex) &
  973. SE_GROUP_ENABLED ) ) {
  974. //
  975. // Make sure group is not mandatory
  976. //
  977. if (SepTokenGroupAttributes(Token,OldIndex) &
  978. SE_GROUP_MANDATORY ) {
  979. return STATUS_CANT_DISABLE_MANDATORY;
  980. }
  981. //
  982. // Make sure group is not deny-only
  983. //
  984. if (SepTokenGroupAttributes(Token,OldIndex) &
  985. SE_GROUP_USE_FOR_DENY_ONLY ) {
  986. return STATUS_CANT_ENABLE_DENY_ONLY;
  987. }
  988. SidLength = SeLengthSid( CurrentGroup.Sid );
  989. SidLength = (ULONG)LongAlignSize(SidLength);
  990. LocalReturnLength += SidLength;
  991. //
  992. // Change, if necessary (saving previous state if
  993. // appropriate).
  994. //
  995. if (MakeChanges) {
  996. if (ARGUMENT_PRESENT(PreviousState)) {
  997. PreviousState->Groups[(*ChangeCount)].Attributes =
  998. CurrentGroup.Attributes;
  999. PreviousState->Groups[(*ChangeCount)].Sid =
  1000. NextSid;
  1001. RtlCopySid( SidLength, NextSid, CurrentGroup.Sid );
  1002. NextSid = (PSID)((ULONG_PTR)NextSid + SidLength);
  1003. }
  1004. SepTokenGroupAttributes(Token,OldIndex) &=
  1005. ~(SepTokenGroupAttributes(Token,OldIndex)
  1006. & SE_GROUP_ENABLED);
  1007. SepTokenGroupAttributes(Token,OldIndex) |=
  1008. (SepArrayGroupAttributes(NewState,NewIndex)
  1009. & SE_GROUP_ENABLED);
  1010. } //endif make changes
  1011. //
  1012. // increment the number of changes
  1013. //
  1014. (*ChangeCount) += 1;
  1015. } // endif change needed
  1016. } // endif found
  1017. NewIndex += 1;
  1018. } // endwhile searching NewState
  1019. } // endelse
  1020. OldIndex += 1;
  1021. } // endwhile more groups in token
  1022. //
  1023. // Set completion status appropriately if some not assigned
  1024. //
  1025. if (!ResetToDefault) {
  1026. if (MatchCount < GroupCount) {
  1027. CompletionStatus = STATUS_NOT_ALL_ASSIGNED;
  1028. }
  1029. }
  1030. //
  1031. // Indicate whether changes were made
  1032. //
  1033. if ((*ChangeCount) > 0 && MakeChanges) {
  1034. (*ChangesMade) = TRUE;
  1035. } else {
  1036. (*ChangesMade) = FALSE;
  1037. }
  1038. //
  1039. // Calculate the space needed to return previous state information
  1040. // (The SID lengths have already been added up in LocalReturnLength).
  1041. //
  1042. if (ARGUMENT_PRESENT(PreviousState)) {
  1043. (*ReturnLength) = LocalReturnLength +
  1044. (ULONG)sizeof(TOKEN_GROUPS) +
  1045. ((*ChangeCount) * (ULONG)sizeof(SID_AND_ATTRIBUTES)) -
  1046. (ANYSIZE_ARRAY * (ULONG)sizeof(SID_AND_ATTRIBUTES));
  1047. }
  1048. return CompletionStatus;
  1049. }