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.

1810 lines
44 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. security.c
  5. Abstract:
  6. This module implements the security related portions of the process
  7. structure.
  8. Author:
  9. Mark Lucovsky (markl) 25-Apr-1989
  10. Jim Kelly (JimK) 2-August-1990
  11. Neill Clift (NeillC) 14-Aug-2000
  12. Revamped for fast referencing the primary token and holding the security lock
  13. only over critical portions.
  14. Revision History:
  15. --*/
  16. #include "psp.h"
  17. #ifdef ALLOC_PRAGMA
  18. NTSTATUS
  19. PsOpenTokenOfJobObject(
  20. IN HANDLE JobObject,
  21. OUT PACCESS_TOKEN * Token
  22. );
  23. #pragma alloc_text(PAGE, PsReferencePrimaryToken)
  24. #pragma alloc_text(PAGE, PsReferenceImpersonationToken)
  25. #pragma alloc_text(PAGE, PsReferenceEffectiveToken)
  26. #pragma alloc_text(PAGE, PsOpenTokenOfThread)
  27. #pragma alloc_text(PAGE, PsOpenTokenOfProcess)
  28. #pragma alloc_text(PAGE, PsOpenTokenOfJobObject)
  29. #pragma alloc_text(PAGE, PsImpersonateClient)
  30. #pragma alloc_text(PAGE, PsDisableImpersonation)
  31. #pragma alloc_text(PAGE, PsRestoreImpersonation)
  32. #pragma alloc_text(PAGE, PsRevertToSelf)
  33. #pragma alloc_text(PAGE, PsRevertThreadToSelf)
  34. #pragma alloc_text(PAGE, PspInitializeProcessSecurity)
  35. #pragma alloc_text(PAGE, PspDeleteProcessSecurity)
  36. #pragma alloc_text(PAGE, PspAssignPrimaryToken)
  37. #pragma alloc_text(PAGE, PspInitializeThreadSecurity)
  38. #pragma alloc_text(PAGE, PspDeleteThreadSecurity)
  39. #pragma alloc_text(PAGE, PsAssignImpersonationToken)
  40. #endif //ALLOC_PRAGMA
  41. PACCESS_TOKEN
  42. PsReferencePrimaryToken(
  43. IN PEPROCESS Process
  44. )
  45. /*++
  46. Routine Description:
  47. This function returns a pointer to the primary token of a process.
  48. The reference count of that primary token is incremented to protect
  49. the pointer returned.
  50. When the pointer is no longer needed, it should be freed using
  51. PsDereferencePrimaryToken().
  52. Arguments:
  53. Process - Supplies the address of the process whose primary token
  54. is to be referenced.
  55. Return Value:
  56. A pointer to the specified process's primary token.
  57. --*/
  58. {
  59. PACCESS_TOKEN Token;
  60. PETHREAD CurrentThread;
  61. PAGED_CODE();
  62. ASSERT( Process->Pcb.Header.Type == ProcessObject );
  63. Token = ObFastReferenceObject (&Process->Token);
  64. if (Token == NULL) {
  65. CurrentThread = PsGetCurrentThread ();
  66. PspLockProcessSecurityShared (Process, CurrentThread);
  67. Token = ObFastReferenceObjectLocked (&Process->Token);
  68. PspUnlockProcessSecurityShared (Process, CurrentThread);
  69. }
  70. return Token;
  71. }
  72. PACCESS_TOKEN
  73. PsReferenceImpersonationToken(
  74. IN PETHREAD Thread,
  75. OUT PBOOLEAN CopyOnOpen,
  76. OUT PBOOLEAN EffectiveOnly,
  77. OUT PSECURITY_IMPERSONATION_LEVEL ImpersonationLevel
  78. )
  79. /*++
  80. Routine Description:
  81. This function returns a pointer to the impersonation token of a thread.
  82. The reference count of that impersonation token is incremented to protect
  83. the pointer returned.
  84. If the thread is not currently impersonating a client, then a null pointer
  85. is returned.
  86. If the thread is impersonating a client, then information about the
  87. means of impersonation are also returned (ImpersonationLevel).
  88. If a non-null value is returned, then PsDereferenceImpersonationToken()
  89. must be called to decrement the token's reference count when the pointer
  90. is no longer needed.
  91. Arguments:
  92. Thread - Supplies the address of the thread whose impersonation token
  93. is to be referenced.
  94. CopyOnOpen - The current value of the Thread->ImpersonationInfo->CopyOnOpen field.
  95. EffectiveOnly - The current value of the Thread->ImpersonationInfo->EffectiveOnly field.
  96. ImpersonationLevel - The current value of the Thread->ImpersonationInfo->ImpersonationLevel
  97. field.
  98. Return Value:
  99. A pointer to the specified thread's impersonation token.
  100. If the thread is not currently impersonating a client, then NULL is
  101. returned.
  102. --*/
  103. {
  104. PACCESS_TOKEN Token;
  105. PETHREAD CurrentThread;
  106. PPS_IMPERSONATION_INFORMATION ImpersonationInfo;
  107. PAGED_CODE();
  108. ASSERT (Thread->Tcb.Header.Type == ThreadObject);
  109. //
  110. // before going through the lock overhead just look to see if it is
  111. // null. There is no race. Grabbing the lock is not needed until
  112. // we decide to use the token at which point we re check to see it
  113. // it is null.
  114. // This check saves about 300 instructions.
  115. //
  116. if (!PS_IS_THREAD_IMPERSONATING (Thread)) {
  117. return NULL;
  118. }
  119. //
  120. // Lock the process security fields.
  121. //
  122. CurrentThread = PsGetCurrentThread ();
  123. PspLockThreadSecurityShared (Thread, CurrentThread);
  124. //
  125. // Grab impersonation info block.
  126. //
  127. ImpersonationInfo = Thread->ImpersonationInfo;
  128. if (PS_IS_THREAD_IMPERSONATING (Thread)) {
  129. //
  130. // Return the thread's impersonation level, etc.
  131. //
  132. Token = ImpersonationInfo->Token;
  133. //
  134. // Increment the reference count of the token to protect our
  135. // pointer.
  136. //
  137. ObReferenceObject (Token);
  138. (*ImpersonationLevel) = ImpersonationInfo->ImpersonationLevel;
  139. (*CopyOnOpen) = ImpersonationInfo->CopyOnOpen;
  140. (*EffectiveOnly) = ImpersonationInfo->EffectiveOnly;
  141. } else {
  142. Token = NULL;
  143. }
  144. //
  145. // Release the security fields.
  146. //
  147. PspUnlockThreadSecurityShared (Thread, CurrentThread);
  148. return Token;
  149. }
  150. PACCESS_TOKEN
  151. PsReferenceEffectiveToken(
  152. IN PETHREAD Thread,
  153. OUT PTOKEN_TYPE TokenType,
  154. OUT PBOOLEAN EffectiveOnly,
  155. OUT PSECURITY_IMPERSONATION_LEVEL ImpersonationLevel
  156. )
  157. /*++
  158. Routine Description:
  159. This function returns a pointer to the effective token of a thread. The
  160. effective token of a thread is the thread's impersonation token if it has
  161. one. Otherwise, it is the primary token of the thread's process.
  162. The reference count of the effective token is incremented to protect
  163. the pointer returned.
  164. If the thread is impersonating a client, then the impersonation level
  165. is also returned.
  166. Either PsDereferenceImpersonationToken() (for an impersonation token) or
  167. PsDereferencePrimaryToken() (for a primary token) must be called to
  168. decrement the token's reference count when the pointer is no longer
  169. needed.
  170. Arguments:
  171. Thread - Supplies the address of the thread whose effective token
  172. is to be referenced.
  173. TokenType - Receives the type of the effective token. If the thread
  174. is currently impersonating a client, then this will be
  175. TokenImpersonation. Othwerwise, it will be TokenPrimary.
  176. EffectiveOnly - If the token type is TokenImpersonation, then this
  177. receives the value of the client thread's Thread->Client->EffectiveOnly field.
  178. Otherwise, it is set to FALSE.
  179. ImpersonationLevel - The current value of the Thread->Client->ImpersonationLevel
  180. field for an impersonation token and is not set for a primary token.
  181. Return Value:
  182. A pointer to the specified thread's effective token.
  183. --*/
  184. {
  185. PACCESS_TOKEN Token;
  186. PEPROCESS Process;
  187. PETHREAD CurrentThread;
  188. PPS_IMPERSONATION_INFORMATION ImpersonationInfo;
  189. PAGED_CODE();
  190. ASSERT (Thread->Tcb.Header.Type == ThreadObject);
  191. Process = THREAD_TO_PROCESS(Thread);
  192. //
  193. // Grab the current impersonation token pointer value
  194. //
  195. Token = NULL;
  196. if (PS_IS_THREAD_IMPERSONATING (Thread)) {
  197. //
  198. // Lock the process security fields.
  199. //
  200. CurrentThread = PsGetCurrentThread ();
  201. PspLockThreadSecurityShared (Thread, CurrentThread);
  202. if (PS_IS_THREAD_IMPERSONATING (Thread)) {
  203. //
  204. // Grab impersonation info block.
  205. //
  206. ImpersonationInfo = Thread->ImpersonationInfo;
  207. Token = ImpersonationInfo->Token;
  208. //
  209. // Return the thread's impersonation level, etc.
  210. //
  211. (*TokenType) = TokenImpersonation;
  212. (*EffectiveOnly) = ImpersonationInfo->EffectiveOnly;
  213. (*ImpersonationLevel) = ImpersonationInfo->ImpersonationLevel;
  214. //
  215. // Increment the reference count of the token to protect our
  216. // pointer.
  217. //
  218. ObReferenceObject (Token);
  219. //
  220. // Release the security fields.
  221. //
  222. PspUnlockThreadSecurityShared (Thread, CurrentThread);
  223. return Token;
  224. }
  225. //
  226. // Release the security fields.
  227. //
  228. PspUnlockThreadSecurityShared (Thread, CurrentThread);
  229. }
  230. //
  231. // Get the thread's primary token if it wasn't impersonating a client.
  232. //
  233. Token = ObFastReferenceObject (&Process->Token);
  234. if (Token == NULL) {
  235. //
  236. // Fast ref failed. We go the slow way with a lock
  237. //
  238. CurrentThread = PsGetCurrentThread ();
  239. PspLockProcessSecurityShared (Process,CurrentThread);
  240. Token = ObFastReferenceObjectLocked (&Process->Token);
  241. PspUnlockProcessSecurityShared (Process,CurrentThread);
  242. }
  243. //
  244. // Only the TokenType and CopyOnOpen OUT parameters are
  245. // returned for a primary token.
  246. //
  247. (*TokenType) = TokenPrimary;
  248. (*EffectiveOnly) = FALSE;
  249. return Token;
  250. }
  251. NTSTATUS
  252. PsOpenTokenOfThread(
  253. IN HANDLE ThreadHandle,
  254. IN BOOLEAN OpenAsSelf,
  255. OUT PACCESS_TOKEN *Token,
  256. OUT PBOOLEAN CopyOnOpen,
  257. OUT PBOOLEAN EffectiveOnly,
  258. OUT PSECURITY_IMPERSONATION_LEVEL ImpersonationLevel
  259. )
  260. /*++
  261. Routine Description:
  262. This function does the thread specific processing of
  263. an NtOpenThreadToken() service.
  264. The service validates that the handle has appropriate access
  265. to reference the thread. If so, it goes on to increment
  266. the reference count of the token object to prevent it from
  267. going away while the rest of the NtOpenThreadToken() request
  268. is processed.
  269. NOTE: If this call completes successfully, the caller is responsible
  270. for decrementing the reference count of the target token.
  271. This must be done using PsDereferenceImpersonationToken().
  272. Arguments:
  273. ThreadHandle - Supplies a handle to a thread object.
  274. OpenAsSelf - Is a boolean value indicating whether the access should
  275. be made using the calling thread's current security context, which
  276. may be that of a client (if impersonating), or using the caller's
  277. process-level security context. A value of FALSE indicates the
  278. caller's current context should be used un-modified. A value of
  279. TRUE indicates the request should be fulfilled using the process
  280. level security context.
  281. Token - If successful, receives a pointer to the thread's token
  282. object.
  283. CopyOnOpen - The current value of the Thread->Client->CopyOnOpen field.
  284. EffectiveOnly - The current value of the Thread->Client->EffectiveOnly field.
  285. ImpersonationLevel - The current value of the Thread->Client->ImpersonationLevel
  286. field.
  287. Return Value:
  288. STATUS_SUCCESS - Indicates the call completed successfully.
  289. STATUS_NO_TOKEN - Indicates the referenced thread is not currently
  290. impersonating a client.
  291. STATUS_CANT_OPEN_ANONYMOUS - Indicates the client requested anonymous
  292. impersonation level. An anonymous token can not be openned.
  293. status may also be any value returned by an attemp the reference
  294. the thread object for THREAD_QUERY_INFORMATION access.
  295. --*/
  296. {
  297. NTSTATUS
  298. Status;
  299. PETHREAD
  300. Thread;
  301. KPROCESSOR_MODE
  302. PreviousMode;
  303. PAGED_CODE();
  304. PreviousMode = KeGetPreviousMode();
  305. UNREFERENCED_PARAMETER (OpenAsSelf);
  306. //
  307. // Make sure the handle grants the appropriate access to the specified
  308. // thread.
  309. //
  310. Status = ObReferenceObjectByHandle (ThreadHandle,
  311. THREAD_QUERY_INFORMATION,
  312. PsThreadType,
  313. PreviousMode,
  314. &Thread,
  315. NULL);
  316. if (!NT_SUCCESS (Status)) {
  317. return Status;
  318. }
  319. //
  320. // Reference the impersonation token, if there is one
  321. //
  322. (*Token) = PsReferenceImpersonationToken (Thread,
  323. CopyOnOpen,
  324. EffectiveOnly,
  325. ImpersonationLevel);
  326. //
  327. // dereference the target thread.
  328. //
  329. ObDereferenceObject (Thread);
  330. //
  331. // Make sure there is a token
  332. //
  333. if (*Token == NULL) {
  334. return STATUS_NO_TOKEN;
  335. }
  336. //
  337. // Make sure the ImpersonationLevel is high enough to allow
  338. // the token to be openned.
  339. //
  340. if ((*ImpersonationLevel) <= SecurityAnonymous) {
  341. PsDereferenceImpersonationToken (*Token);
  342. (*Token) = NULL;
  343. return STATUS_CANT_OPEN_ANONYMOUS;
  344. }
  345. return STATUS_SUCCESS;
  346. }
  347. NTSTATUS
  348. PsOpenTokenOfProcess(
  349. IN HANDLE ProcessHandle,
  350. OUT PACCESS_TOKEN *Token
  351. )
  352. /*++
  353. Routine Description:
  354. This function does the process specific processing of
  355. an NtOpenProcessToken() service.
  356. The service validates that the handle has appropriate access
  357. to referenced process. If so, it goes on to reference the
  358. primary token object to prevent it from going away while the
  359. rest of the NtOpenProcessToken() request is processed.
  360. NOTE: If this call completes successfully, the caller is responsible
  361. for decrementing the reference count of the target token.
  362. This must be done using the PsDereferencePrimaryToken() API.
  363. Arguments:
  364. ProcessHandle - Supplies a handle to a process object whose primary
  365. token is to be opened.
  366. Token - If successful, receives a pointer to the process's token
  367. object.
  368. Return Value:
  369. STATUS_SUCCESS - Indicates the call completed successfully.
  370. status may also be any value returned by an attemp the reference
  371. the process object for PROCESS_QUERY_INFORMATION access.
  372. --*/
  373. {
  374. NTSTATUS
  375. Status;
  376. PEPROCESS
  377. Process;
  378. KPROCESSOR_MODE
  379. PreviousMode;
  380. PAGED_CODE();
  381. PreviousMode = KeGetPreviousMode();
  382. //
  383. // Make sure the handle grants the appropriate access to the specified
  384. // process.
  385. //
  386. Status = ObReferenceObjectByHandle (ProcessHandle,
  387. PROCESS_QUERY_INFORMATION,
  388. PsProcessType,
  389. PreviousMode,
  390. &Process,
  391. NULL);
  392. if (!NT_SUCCESS (Status)) {
  393. return Status;
  394. }
  395. //
  396. // Reference the primary token
  397. // (This takes care of gaining exlusive access to the process
  398. // security fields for us)
  399. //
  400. (*Token) = PsReferencePrimaryToken (Process);
  401. //
  402. // Done with the process object
  403. //
  404. ObDereferenceObject (Process);
  405. return STATUS_SUCCESS;
  406. }
  407. NTSTATUS
  408. PsOpenTokenOfJobObject(
  409. IN HANDLE JobObject,
  410. OUT PACCESS_TOKEN * Token
  411. )
  412. /*++
  413. Routine Description:
  414. This function does the ps/job specific work for NtOpenJobObjectToken.
  415. Arguments:
  416. JobObject - Supplies a handle to a job object whose limit token
  417. token is to be opened.
  418. Token - If successful, receives a pointer to the process's token
  419. object.
  420. Return Value:
  421. STATUS_SUCCESS - Indicates the call completed successfully.
  422. STATUS_NO_TOKEN - indicates the job object does not have a token
  423. --*/
  424. {
  425. NTSTATUS Status;
  426. PEJOB Job;
  427. KPROCESSOR_MODE PreviousMode;
  428. PAGED_CODE();
  429. PreviousMode = KeGetPreviousMode();
  430. Status = ObReferenceObjectByHandle (JobObject,
  431. JOB_OBJECT_QUERY,
  432. PsJobType,
  433. PreviousMode,
  434. &Job,
  435. NULL);
  436. if (NT_SUCCESS (Status)) {
  437. if (Job->Token != NULL) {
  438. ObReferenceObject (Job->Token);
  439. *Token = Job->Token;
  440. } else {
  441. Status = STATUS_NO_TOKEN;
  442. }
  443. }
  444. return Status;
  445. }
  446. NTSTATUS
  447. PsImpersonateClient(
  448. IN PETHREAD Thread,
  449. IN PACCESS_TOKEN Token,
  450. IN BOOLEAN CopyOnOpen,
  451. IN BOOLEAN EffectiveOnly,
  452. IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel
  453. )
  454. /*++
  455. Routine Description:
  456. This routine sets up the specified thread so that it is impersonating
  457. the specified client. This will result in the reference count of the
  458. token representing the client being incremented to reflect the new
  459. reference.
  460. If the thread is currently impersonating a client, that token will be
  461. dereferenced.
  462. Arguments:
  463. Thread - points to the thread which is going to impersonate a client.
  464. Token - Points to the token to be assigned as the impersonation token.
  465. This does NOT have to be a TokenImpersonation type token. This
  466. allows direct reference of client process's primary tokens.
  467. CopyOnOpen - If TRUE, indicates the token is considered to be private
  468. by the assigner and should be copied if opened. For example, a
  469. session layer may be using a token to represent a client's context.
  470. If the session is trying to synchronize the context of the client,
  471. then user mode code should not be given direct access to the session
  472. layer's token.
  473. Basically, session layers should always specify TRUE for this, while
  474. tokens assigned by the server itself (handle based) should specify
  475. FALSE.
  476. EffectiveOnly - Is a boolean value to be assigned as the
  477. Thread->ImpersonationInfo->EffectiveOnly field value for the
  478. impersonation. A value of FALSE indicates the server is allowed
  479. to enable currently disabled groups and privileges.
  480. ImpersonationLevel - Is the impersonation level that the server is allowed
  481. to access the token with.
  482. Return Value:
  483. STATUS_SUCCESS - Indicates the call completed successfully.
  484. --*/
  485. {
  486. PPS_IMPERSONATION_INFORMATION NewClient, FreeClient;
  487. PACCESS_TOKEN OldToken;
  488. PACCESS_TOKEN NewerToken=NULL;
  489. NTSTATUS Status;
  490. PPS_JOB_TOKEN_FILTER Filter;
  491. PEPROCESS Process;
  492. PETHREAD CurrentThread;
  493. PEJOB Job;
  494. PPS_IMPERSONATION_INFORMATION ImpersonationInfo;
  495. PAGED_CODE();
  496. ASSERT (Thread->Tcb.Header.Type == ThreadObject);
  497. Process = THREAD_TO_PROCESS (Thread);
  498. if (!ARGUMENT_PRESENT(Token)) {
  499. OldToken = NULL;
  500. if (PS_IS_THREAD_IMPERSONATING (Thread)) {
  501. //
  502. // Lock the process security fields
  503. //
  504. CurrentThread = PsGetCurrentThread ();
  505. PspLockThreadSecurityExclusive (Thread, CurrentThread);
  506. if (PS_IS_THREAD_IMPERSONATING (Thread)) {
  507. //
  508. // Grab impersonation info block.
  509. //
  510. ImpersonationInfo = Thread->ImpersonationInfo;
  511. //
  512. // This is a request to revert to self.
  513. // Clean up any client information.
  514. //
  515. OldToken = ImpersonationInfo->Token;
  516. PS_CLEAR_BITS (&Thread->CrossThreadFlags,
  517. PS_CROSS_THREAD_FLAGS_IMPERSONATING);
  518. }
  519. //
  520. // Release the security fields
  521. //
  522. PspUnlockThreadSecurityExclusive (Thread, CurrentThread);
  523. }
  524. } else {
  525. //
  526. // Allocate and set up the Client block. We do this without holding the Process
  527. // security lock so we reduce contention. Only one thread will manage to assign this.
  528. // The client block once created never goes away until the last dereference of
  529. // the process. We can touch this without locks
  530. //
  531. NewClient = Thread->ImpersonationInfo;
  532. if (NewClient == NULL) {
  533. NewClient = ExAllocatePoolWithTag (PagedPool,
  534. sizeof (PS_IMPERSONATION_INFORMATION),
  535. 'mIsP'|PROTECTED_POOL);
  536. if (NewClient == NULL) {
  537. return STATUS_NO_MEMORY;
  538. }
  539. FreeClient = InterlockedCompareExchangePointer (&Thread->ImpersonationInfo,
  540. NewClient,
  541. NULL);
  542. //
  543. // We got beaten by another thread. Free our context and use the new one
  544. //
  545. if (FreeClient != NULL) {
  546. ExFreePoolWithTag (NewClient, 'mIsP'|PROTECTED_POOL);
  547. NewClient = FreeClient;
  548. }
  549. }
  550. //
  551. // Check if we're allowed to impersonate based on the job
  552. // restrictions:
  553. //
  554. Job = Process->Job;
  555. if (Job != NULL) {
  556. if ((Job->SecurityLimitFlags & JOB_OBJECT_SECURITY_NO_ADMIN) &&
  557. (SeTokenIsAdmin (Token))) {
  558. return STATUS_ACCESS_DENIED;
  559. } else if ((Job->SecurityLimitFlags & JOB_OBJECT_SECURITY_RESTRICTED_TOKEN) &&
  560. (!SeTokenIsRestricted (Token))) {
  561. return STATUS_ACCESS_DENIED;
  562. } else {
  563. Filter = Job->Filter;
  564. if (Filter != NULL) {
  565. //
  566. // Filter installed. Need to create a restricted token
  567. // dynamically.
  568. //
  569. Status = SeFastFilterToken (Token,
  570. KernelMode,
  571. 0,
  572. Filter->CapturedGroupCount,
  573. Filter->CapturedGroups,
  574. Filter->CapturedPrivilegeCount,
  575. Filter->CapturedPrivileges,
  576. Filter->CapturedSidCount,
  577. Filter->CapturedSids,
  578. Filter->CapturedSidsLength,
  579. &NewerToken);
  580. if (NT_SUCCESS (Status)) {
  581. //
  582. // If we created a filtered token then we don't need to add an extra token reference
  583. // as this is a new token with a single reference we just created.
  584. //
  585. Token = NewerToken;
  586. } else {
  587. return Status;
  588. }
  589. } else {
  590. ObReferenceObject (Token);
  591. }
  592. }
  593. } else {
  594. ObReferenceObject (Token);
  595. }
  596. //
  597. // Lock the process security fields
  598. //
  599. CurrentThread = PsGetCurrentThread ();
  600. PspLockThreadSecurityExclusive (Thread, CurrentThread);
  601. //
  602. // If we are already impersonating someone,
  603. // use the already allocated block. This avoids
  604. // an alloc and a free.
  605. //
  606. if (PS_IS_THREAD_IMPERSONATING (Thread)) {
  607. //
  608. // capture the old token pointer.
  609. // We'll dereference it after unlocking the security fields.
  610. //
  611. OldToken = NewClient->Token;
  612. } else {
  613. OldToken = NULL;
  614. PS_SET_BITS (&Thread->CrossThreadFlags, PS_CROSS_THREAD_FLAGS_IMPERSONATING);
  615. }
  616. NewClient->ImpersonationLevel = ImpersonationLevel;
  617. NewClient->EffectiveOnly = EffectiveOnly;
  618. NewClient->CopyOnOpen = CopyOnOpen;
  619. NewClient->Token = Token;
  620. //
  621. // Release the security fields
  622. //
  623. PspUnlockThreadSecurityExclusive (Thread, CurrentThread);
  624. }
  625. //
  626. // Free the old client token, if necessary.
  627. //
  628. if (OldToken != NULL) {
  629. PsDereferenceImpersonationToken (OldToken);
  630. }
  631. return STATUS_SUCCESS;
  632. }
  633. BOOLEAN
  634. PsDisableImpersonation(
  635. IN PETHREAD Thread,
  636. IN PSE_IMPERSONATION_STATE ImpersonationState
  637. )
  638. /*++
  639. Routine Description:
  640. This routine temporarily disables the impersonation of a thread.
  641. The impersonation state is saved for quick replacement later. The
  642. impersonation token is left referenced and a pointer to it is held
  643. in the IMPERSONATION_STATE data structure.
  644. PsRestoreImpersonation() must be used after this routine is called.
  645. Arguments:
  646. Thread - points to the thread whose impersonation (if any) is to
  647. be temporarily disabled.
  648. ImpersonationState - receives the current impersonation information,
  649. including a pointer to the impersonation token.
  650. Return Value:
  651. TRUE - Indicates the impersonation state has been saved and the
  652. impersonation has been temporarily disabled.
  653. FALSE - Indicates the specified thread was not impersonating a client.
  654. No action has been taken.
  655. --*/
  656. {
  657. PPS_IMPERSONATION_INFORMATION OldClient;
  658. PETHREAD CurrentThread;
  659. PAGED_CODE();
  660. ASSERT (Thread->Tcb.Header.Type == ThreadObject);
  661. //
  662. // Capture the impersonation information (if there is any).
  663. // The vast majority of cases this function is called we are not impersonating. Skip acquiring
  664. // the lock in this case.
  665. //
  666. OldClient = NULL;
  667. if (PS_IS_THREAD_IMPERSONATING (Thread)) {
  668. //
  669. // Lock the process security fields
  670. //
  671. CurrentThread = PsGetCurrentThread ();
  672. PspLockThreadSecurityExclusive (Thread, CurrentThread);
  673. //
  674. // Test and clear the impersonation bit. If we are still impersonating then capture the info.
  675. //
  676. if (PS_TEST_CLEAR_BITS (&Thread->CrossThreadFlags,
  677. PS_CROSS_THREAD_FLAGS_IMPERSONATING)&
  678. PS_CROSS_THREAD_FLAGS_IMPERSONATING) {
  679. OldClient = Thread->ImpersonationInfo;
  680. ImpersonationState->Level = OldClient->ImpersonationLevel;
  681. ImpersonationState->EffectiveOnly = OldClient->EffectiveOnly;
  682. ImpersonationState->CopyOnOpen = OldClient->CopyOnOpen;
  683. ImpersonationState->Token = OldClient->Token;
  684. }
  685. //
  686. // Release the security fields
  687. //
  688. PspUnlockThreadSecurityExclusive (Thread, CurrentThread);
  689. }
  690. if (OldClient != NULL) {
  691. return TRUE;
  692. } else {
  693. //
  694. // Not impersonating. Just make up some values.
  695. // The NULL for the token indicates we aren't impersonating.
  696. //
  697. ImpersonationState->Level = SecurityAnonymous;
  698. ImpersonationState->EffectiveOnly = FALSE;
  699. ImpersonationState->CopyOnOpen = FALSE;
  700. ImpersonationState->Token = NULL;
  701. return FALSE;
  702. }
  703. }
  704. VOID
  705. PsRestoreImpersonation(
  706. IN PETHREAD Thread,
  707. IN PSE_IMPERSONATION_STATE ImpersonationState
  708. )
  709. /*++
  710. Routine Description:
  711. This routine restores an impersonation that has been temporarily disabled
  712. using PsDisableImpersonation().
  713. Notice that if this routine finds the thread is already impersonating
  714. (again), then restoring the temporarily disabled impersonation will cause
  715. the current impersonation to be abandoned.
  716. Arguments:
  717. Thread - points to the thread whose impersonation is to be restored.
  718. ImpersontionState - receives the current impersontion information,
  719. including a pointer ot the impersonation token.
  720. Return Value:
  721. TRUE - Indicates the impersonation state has been saved and the
  722. impersonation has been temporarily disabled.
  723. FALSE - Indicates the specified thread was not impersonating a client.
  724. No action has been taken.
  725. --*/
  726. {
  727. PAGED_CODE();
  728. ASSERT (Thread->Tcb.Header.Type == ThreadObject);
  729. //
  730. // The processing for restore is identical to that for Impersonation,
  731. // except that the token's reference count will be incremented (and
  732. // so we need to do one dereference).
  733. PsImpersonateClient (Thread,
  734. ImpersonationState->Token,
  735. ImpersonationState->CopyOnOpen,
  736. ImpersonationState->EffectiveOnly,
  737. ImpersonationState->Level);
  738. ObDereferenceObject (ImpersonationState->Token);
  739. return;
  740. }
  741. VOID
  742. PsRevertToSelf( )
  743. /*++
  744. Routine Description:
  745. This routine causes the calling thread to discontinue
  746. impersonating a client. If the thread is not currently
  747. impersonating a client, no action is taken.
  748. Arguments:
  749. None.
  750. Return Value:
  751. None.
  752. --*/
  753. {
  754. PETHREAD Thread;
  755. PEPROCESS Process;
  756. PACCESS_TOKEN OldToken;
  757. PAGED_CODE();
  758. Thread = PsGetCurrentThread ();
  759. Process = THREAD_TO_PROCESS (Thread);
  760. //
  761. // Lock the process security fields
  762. //
  763. PspLockThreadSecurityExclusive (Thread, Thread);
  764. //
  765. // See if the thread is impersonating a client
  766. // and dereference that token if so.
  767. //
  768. if (PS_IS_THREAD_IMPERSONATING (Thread)) {
  769. PS_CLEAR_BITS (&Thread->CrossThreadFlags, PS_CROSS_THREAD_FLAGS_IMPERSONATING);
  770. OldToken = Thread->ImpersonationInfo->Token;
  771. } else {
  772. OldToken = NULL;
  773. }
  774. //
  775. // Release the security fields
  776. //
  777. PspUnlockThreadSecurityExclusive (Thread, Thread);
  778. //
  779. // Free the old client info...
  780. //
  781. if (OldToken != NULL) {
  782. ObDereferenceObject (OldToken);
  783. }
  784. return;
  785. }
  786. VOID
  787. PsRevertThreadToSelf (
  788. IN PETHREAD Thread
  789. )
  790. /*++
  791. Routine Description:
  792. This routine causes the specified thread to discontinue
  793. impersonating a client. If the thread is not currently
  794. impersonating a client, no action is taken.
  795. Arguments:
  796. Thread - Thread to remove impersonation from
  797. Return Value:
  798. None.
  799. --*/
  800. {
  801. PETHREAD CurrentThread;
  802. PACCESS_TOKEN OldToken;
  803. PPS_IMPERSONATION_INFORMATION ImpersonationInfo;
  804. PAGED_CODE();
  805. ASSERT (Thread->Tcb.Header.Type == ThreadObject);
  806. if (PS_IS_THREAD_IMPERSONATING (Thread)) {
  807. CurrentThread = PsGetCurrentThread ();
  808. //
  809. // Lock the process security fields
  810. //
  811. PspLockThreadSecurityExclusive (Thread, CurrentThread);
  812. //
  813. // See if the thread is impersonating a client
  814. // and dereference that token if so.
  815. //
  816. if (PS_IS_THREAD_IMPERSONATING (Thread)) {
  817. //
  818. // Grab impersonation info block.
  819. //
  820. ImpersonationInfo = Thread->ImpersonationInfo;
  821. PS_CLEAR_BITS (&Thread->CrossThreadFlags, PS_CROSS_THREAD_FLAGS_IMPERSONATING);
  822. OldToken = ImpersonationInfo->Token;
  823. } else {
  824. OldToken = NULL;
  825. }
  826. //
  827. // Release the security fields
  828. //
  829. PspUnlockThreadSecurityExclusive (Thread, CurrentThread);
  830. //
  831. // Free the old client info...
  832. //
  833. if (OldToken != NULL) {
  834. ObDereferenceObject (OldToken);
  835. }
  836. }
  837. return;
  838. }
  839. NTSTATUS
  840. PspInitializeProcessSecurity(
  841. IN PEPROCESS Parent OPTIONAL,
  842. IN PEPROCESS Child
  843. )
  844. /*++
  845. Routine Description:
  846. This function initializes a new process's security fields, including
  847. the assignment of a new primary token.
  848. The child process is assumed to not yet have been inserted into
  849. an object table.
  850. NOTE: IT IS EXPECTED THAT THIS SERVICE WILL BE CALLED WITH A NULL
  851. PARENT PROCESS POINTER EXACTLY ONCE - FOR THE INITIAL SYSTEM
  852. PROCESS.
  853. Arguments:
  854. Parent - An optional pointer to the process being used as the parent
  855. of the new process. If this value is NULL, then the process is
  856. assumed to be the initial system process, and the boot token is
  857. assigned rather than a duplicate of the parent process's primary
  858. token.
  859. Child - Supplies the address of the process being initialized. This
  860. process does not yet require security field contention protection.
  861. In particular, the security fields may be accessed without first
  862. acquiring the process security fields lock.
  863. Return Value:
  864. --*/
  865. {
  866. NTSTATUS Status;
  867. PACCESS_TOKEN ParentToken, NewToken;
  868. PAGED_CODE();
  869. //
  870. // Assign the primary token
  871. //
  872. if (ARGUMENT_PRESENT (Parent)) {
  873. //
  874. // create the primary token
  875. // This is a duplicate of the parent's token.
  876. //
  877. ParentToken = PsReferencePrimaryToken (Parent);
  878. Status = SeSubProcessToken (ParentToken,
  879. &NewToken,
  880. TRUE);
  881. PsDereferencePrimaryTokenEx (Parent, ParentToken);
  882. if (NT_SUCCESS(Status)) {
  883. ObInitializeFastReference (&Child->Token,
  884. NewToken);
  885. }
  886. } else {
  887. //
  888. // Reference and assign the boot token
  889. //
  890. // The use of a single boot access token assumes there is
  891. // exactly one parentless process in the system - the initial
  892. // process. If this ever changes, this code will need to change
  893. // to match the new condition (so that a token doesn't end up
  894. // being shared by multiple processes.
  895. //
  896. ObInitializeFastReference (&Child->Token, NULL);
  897. SeAssignPrimaryToken (Child, PspBootAccessToken);
  898. Status = STATUS_SUCCESS;
  899. }
  900. return Status;
  901. }
  902. VOID
  903. PspDeleteProcessSecurity(
  904. IN PEPROCESS Process
  905. )
  906. /*++
  907. Routine Description:
  908. This function cleans up a process's security fields as part of process
  909. deletion. It is assumed no other references to the process can occur
  910. during or after a call to this routine. This enables us to reference
  911. the process security fields without acquiring the lock protecting those
  912. fields.
  913. NOTE: It may be desirable to add auditing capability to this routine
  914. at some point.
  915. Arguments:
  916. Process - A pointer to the process being deleted.
  917. Return Value:
  918. None.
  919. --*/
  920. {
  921. PAGED_CODE();
  922. //
  923. // If we are deleting a process that didn't successfully complete
  924. // process initialization, then there may be no token associated
  925. // with it yet.
  926. //
  927. if (!ExFastRefObjectNull (Process->Token)) {
  928. SeDeassignPrimaryToken (Process);
  929. }
  930. return;
  931. }
  932. NTSTATUS
  933. PspAssignPrimaryToken(
  934. IN PEPROCESS Process,
  935. IN HANDLE Token OPTIONAL,
  936. IN PACCESS_TOKEN TokenPointer OPTIONAL
  937. )
  938. /*++
  939. Routine Description:
  940. This function performs the security portions of primary token assignment.
  941. It is expected that the proper access to the process and thread objects,
  942. as well as necessary privilege, has already been established.
  943. A primary token can only be replaced if the process has no threads, or
  944. has one thread. This is because the thread objects point to the primary
  945. token and must have those pointers updated when the primary token is
  946. changed. This is only expected to be necessary at logon time, when
  947. the process is in its infancy and either has zero threads or maybe one
  948. inactive thread.
  949. If the assignment is successful, the old token is dereferenced and the
  950. new one is referenced.
  951. Arguments:
  952. Process - A pointer to the process whose primary token is being
  953. replaced.
  954. Token - The handle value of the token to be assigned as the primary
  955. token.
  956. Return Value:
  957. STATUS_SUCCESS - Indicates the primary token has been successfully
  958. replaced.
  959. STATUS_BAD_TOKEN_TYPE - Indicates the token is not of type TokenPrimary.
  960. STATUS_TOKEN_IN_USE - Indicates the token is already in use by
  961. another process.
  962. Other status may be returned when attempting to reference the token
  963. object.
  964. --*/
  965. {
  966. NTSTATUS Status;
  967. PACCESS_TOKEN NewToken, OldToken;
  968. KPROCESSOR_MODE PreviousMode;
  969. PETHREAD CurrentThread;
  970. PAGED_CODE();
  971. CurrentThread = PsGetCurrentThread ();
  972. if (TokenPointer == NULL) {
  973. PreviousMode = KeGetPreviousModeByThread(&CurrentThread->Tcb);
  974. //
  975. // Reference the specified token, and make sure it can be assigned
  976. // as a primary token.
  977. //
  978. Status = ObReferenceObjectByHandle (Token,
  979. TOKEN_ASSIGN_PRIMARY,
  980. SeTokenObjectType,
  981. PreviousMode,
  982. &NewToken,
  983. NULL);
  984. if (!NT_SUCCESS (Status)) {
  985. return Status;
  986. }
  987. } else {
  988. NewToken = TokenPointer;
  989. }
  990. //
  991. // Lock the process security fields.
  992. //
  993. PspLockProcessSecurityExclusive (Process, CurrentThread);
  994. //
  995. // This routine makes sure the NewToken is suitable for assignment
  996. // as a primary token.
  997. //
  998. Status = SeExchangePrimaryToken (Process, NewToken, &OldToken);
  999. //
  1000. // Release the process security fields
  1001. //
  1002. PspUnlockProcessSecurityExclusive (Process, CurrentThread);
  1003. //
  1004. // Free the old token (we don't need it).
  1005. // This can't be done while the security fields are locked.
  1006. //
  1007. if (NT_SUCCESS (Status)) {
  1008. ObDereferenceObject (OldToken);
  1009. }
  1010. //
  1011. // Undo the handle reference
  1012. //
  1013. if (TokenPointer == NULL) {
  1014. ObDereferenceObject (NewToken);
  1015. }
  1016. return Status;
  1017. }
  1018. VOID
  1019. PspInitializeThreadSecurity(
  1020. IN PEPROCESS Process,
  1021. IN PETHREAD Thread
  1022. )
  1023. /*++
  1024. Routine Description:
  1025. This function initializes a new thread's security fields.
  1026. Arguments:
  1027. Process - Points to the process the thread belongs to.
  1028. Thread - Points to the thread object being initialized.
  1029. Return Value:
  1030. None.
  1031. --*/
  1032. {
  1033. PAGED_CODE();
  1034. UNREFERENCED_PARAMETER (Process);
  1035. //
  1036. // Initially not impersonating anyone. This is not currently called as we zero out the entire thread at create time anyway
  1037. //
  1038. Thread->ImpersonationInfo = NULL;
  1039. PS_CLEAR_BITS (&Thread->CrossThreadFlags, PS_CROSS_THREAD_FLAGS_IMPERSONATING);
  1040. return;
  1041. }
  1042. VOID
  1043. PspDeleteThreadSecurity(
  1044. IN PETHREAD Thread
  1045. )
  1046. /*++
  1047. Routine Description:
  1048. This function cleans up a thread's security fields as part of thread
  1049. deletion. It is assumed no other references to the thread can occur
  1050. during or after a call to this routine, so no locking is necessary
  1051. to access the thread security fields.
  1052. Arguments:
  1053. Thread - A pointer to the thread being deleted.
  1054. Return Value:
  1055. None.
  1056. --*/
  1057. {
  1058. PPS_IMPERSONATION_INFORMATION ImpersonationInfo;
  1059. PAGED_CODE();
  1060. ImpersonationInfo = Thread->ImpersonationInfo;
  1061. //
  1062. // clean-up client information, if there is any.
  1063. //
  1064. if (PS_IS_THREAD_IMPERSONATING (Thread)) {
  1065. ObDereferenceObject (ImpersonationInfo->Token);
  1066. }
  1067. if (ImpersonationInfo != NULL) {
  1068. ExFreePoolWithTag (ImpersonationInfo, 'mIsP'|PROTECTED_POOL);
  1069. PS_CLEAR_BITS (&Thread->CrossThreadFlags, PS_CROSS_THREAD_FLAGS_IMPERSONATING);
  1070. Thread->ImpersonationInfo = NULL;
  1071. }
  1072. return;
  1073. }
  1074. NTSTATUS
  1075. PsAssignImpersonationToken(
  1076. IN PETHREAD Thread,
  1077. IN HANDLE Token
  1078. )
  1079. /*++
  1080. Routine Description:
  1081. This function performs the security portions of establishing an
  1082. impersonation token. This routine is expected to be used only in
  1083. the case where the subject has asked for impersonation explicitly
  1084. providing an impersonation token. Other services are provided for
  1085. use by communication session layers that need to establish an
  1086. impersonation on a server's behalf.
  1087. It is expected that the proper access to the thread object has already
  1088. been established.
  1089. The following rules apply:
  1090. 1) The caller must have TOKEN_IMPERSONATE access to the token
  1091. for any action to be taken.
  1092. 2) If the token may NOT be used for impersonation (e.g., not an
  1093. impersonation token) no action is taken.
  1094. 3) Otherwise, any existing impersonation token is dereferenced and
  1095. the new token is established as the impersonation token.
  1096. Arguments:
  1097. Thread - A pointer to the thread whose impersonation token is being
  1098. set.
  1099. Token - The handle value of the token to be assigned as the impersonation
  1100. token. If this value is NULL, then current impersonation (if any)
  1101. is terminated and no new impersonation is established.
  1102. Return Value:
  1103. STATUS_SUCCESS - Indicates the primary token has been successfully
  1104. replaced.
  1105. STATUS_BAD_TOKEN_TYPE - Indicates the token is not of type
  1106. TokenImpersonation.
  1107. Other status may be returned when attempting to reference the token
  1108. object.
  1109. --*/
  1110. {
  1111. NTSTATUS Status;
  1112. PACCESS_TOKEN NewToken;
  1113. KPROCESSOR_MODE PreviousMode;
  1114. SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
  1115. PEPROCESS ThreadProcess;
  1116. BOOLEAN AttachedToProcess = FALSE;
  1117. PETHREAD CurrentThread;
  1118. KAPC_STATE ApcState;
  1119. PAGED_CODE();
  1120. CurrentThread = PsGetCurrentThread ();
  1121. if (!ARGUMENT_PRESENT (Token)) {
  1122. PsRevertThreadToSelf (Thread);
  1123. Status = STATUS_SUCCESS;
  1124. } else {
  1125. PreviousMode = KeGetPreviousModeByThread (&CurrentThread->Tcb);
  1126. //
  1127. // Reference the specified token for TOKEN_IMPERSONATE access
  1128. //
  1129. Status = ObReferenceObjectByHandle (Token,
  1130. TOKEN_IMPERSONATE,
  1131. SeTokenObjectType,
  1132. PreviousMode,
  1133. &NewToken,
  1134. NULL);
  1135. if (!NT_SUCCESS (Status)) {
  1136. return Status;
  1137. }
  1138. //
  1139. // Make sure the token is an impersonation token.
  1140. //
  1141. if (SeTokenType (NewToken) != TokenImpersonation) {
  1142. ObDereferenceObject (NewToken);
  1143. return STATUS_BAD_TOKEN_TYPE;
  1144. }
  1145. ImpersonationLevel = SeTokenImpersonationLevel (NewToken);
  1146. //
  1147. // The rest can be done by PsImpersonateClient.
  1148. //
  1149. // PsImpersonateClient will reference the passed token
  1150. // on success.
  1151. //
  1152. Status = PsImpersonateClient (Thread,
  1153. NewToken,
  1154. FALSE, // CopyOnOpen
  1155. FALSE, // EffectiveOnly
  1156. ImpersonationLevel);
  1157. //
  1158. // Dereference the passed token.
  1159. //
  1160. //
  1161. ObDereferenceObject (NewToken);
  1162. }
  1163. if (NT_SUCCESS (Status)) {
  1164. PTEB Teb;
  1165. //
  1166. // Indicate that 'Thread' has started to do impersonation.
  1167. // This info is useful for GetUserDefaultLCID()
  1168. //
  1169. ThreadProcess = THREAD_TO_PROCESS (Thread);
  1170. Teb = Thread->Tcb.Teb;
  1171. if (Teb != NULL) {
  1172. if (PsGetCurrentProcessByThread(CurrentThread) != ThreadProcess) {
  1173. KeStackAttachProcess (&ThreadProcess->Pcb, &ApcState);
  1174. AttachedToProcess = TRUE;
  1175. }
  1176. //
  1177. // We are doing a cross thread TEB reference here. Protect against the TEB being freed and used by
  1178. // somebody else.
  1179. //
  1180. if (ExAcquireRundownProtection (&Thread->RundownProtect)) {
  1181. //
  1182. // The TAB may still raise an exception in low memory conditions so we need try/except here
  1183. //
  1184. try {
  1185. if (ARGUMENT_PRESENT (Token)) {
  1186. Teb->ImpersonationLocale = (LCID)-1;
  1187. Teb->IsImpersonating = 1;
  1188. } else {
  1189. Teb->ImpersonationLocale = (LCID) 0;
  1190. Teb->IsImpersonating = 0;
  1191. }
  1192. } except (EXCEPTION_EXECUTE_HANDLER) {
  1193. }
  1194. ExReleaseRundownProtection (&Thread->RundownProtect);
  1195. }
  1196. if (AttachedToProcess) {
  1197. KeUnstackDetachProcess (&ApcState);
  1198. }
  1199. }
  1200. }
  1201. return Status;
  1202. }
  1203. #undef PsDereferencePrimaryToken
  1204. #pragma alloc_text(PAGE, PsDereferencePrimaryToken)
  1205. VOID
  1206. PsDereferencePrimaryToken(
  1207. IN PACCESS_TOKEN PrimaryToken
  1208. )
  1209. /*++
  1210. Routine Description:
  1211. Returns the reference obtained via PsReferencePrimaryToken
  1212. Arguments:
  1213. Returns the reference
  1214. Return Value:
  1215. None.
  1216. --*/
  1217. {
  1218. PAGED_CODE();
  1219. ObDereferenceObject (PrimaryToken);
  1220. }
  1221. #undef PsDereferenceImpersonationToken
  1222. #pragma alloc_text(PAGE, PsDereferenceImpersonationToken)
  1223. VOID
  1224. PsDereferenceImpersonationToken(
  1225. IN PACCESS_TOKEN ImpersonationToken
  1226. )
  1227. /*++
  1228. Routine Description:
  1229. Returns the reference obtained via PsReferenceImpersonationToken
  1230. Arguments:
  1231. Returns the reference
  1232. Return Value:
  1233. None.
  1234. --*/
  1235. {
  1236. PAGED_CODE();
  1237. if (ImpersonationToken != NULL) {
  1238. ObDereferenceObject (ImpersonationToken);
  1239. }
  1240. }