2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/se/access.c
5 * PURPOSE: Access state functions
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) -
8 * Based on patch by Javier M. Mellid
11 /* INCLUDES *******************************************************************/
17 /* GLOBALS ********************************************************************/
19 ERESOURCE SepSubjectContextLock
;
21 /* PRIVATE FUNCTIONS **********************************************************/
25 SepSidInTokenEx(IN PACCESS_TOKEN _Token
,
26 IN PSID PrincipalSelfSid
,
29 IN BOOLEAN Restricted
)
32 PTOKEN Token
= (PTOKEN
)_Token
;
33 PISID TokenSid
, Sid
= (PISID
)_Sid
;
34 PSID_AND_ATTRIBUTES SidAndAttributes
;
35 ULONG SidCount
, SidLength
;
39 /* Not yet supported */
40 ASSERT(PrincipalSelfSid
== NULL
);
41 ASSERT(Restricted
== FALSE
);
43 /* Check if a principal SID was given, and this is our current SID already */
44 if ((PrincipalSelfSid
) && (RtlEqualSid(SePrincipalSelfSid
, Sid
)))
46 /* Just use the principal SID in this case */
47 Sid
= PrincipalSelfSid
;
50 /* Check if this is a restricted token or not */
53 /* Use the restricted SIDs and count */
54 SidAndAttributes
= Token
->RestrictedSids
;
55 SidCount
= Token
->RestrictedSidCount
;
59 /* Use the normal SIDs and count */
60 SidAndAttributes
= Token
->UserAndGroups
;
61 SidCount
= Token
->UserAndGroupCount
;
64 /* Do checks here by hand instead of the usual 4 function calls */
65 SidLength
= FIELD_OFFSET(SID
,
66 SubAuthority
[Sid
->SubAuthorityCount
]);
67 SidMetadata
= *(PUSHORT
)&Sid
->Revision
;
70 for (i
= 0; i
< SidCount
; i
++)
72 TokenSid
= (PISID
)SidAndAttributes
->Sid
;
74 UNICODE_STRING sidString
;
75 RtlConvertSidToUnicodeString(&sidString
, TokenSid
, TRUE
);
76 DPRINT1("SID in Token: %wZ\n", &sidString
);
77 RtlFreeUnicodeString(&sidString
);
79 /* Check if the SID metadata matches */
80 if (*(PUSHORT
)&TokenSid
->Revision
== SidMetadata
)
82 /* Check if the SID data matches */
83 if (RtlEqualMemory(Sid
, TokenSid
, SidLength
))
85 /* Check if the group is enabled, or used for deny only */
86 if ((!(i
) && !(SidAndAttributes
->Attributes
& SE_GROUP_USE_FOR_DENY_ONLY
)) ||
87 (SidAndAttributes
->Attributes
& SE_GROUP_ENABLED
) ||
88 ((Deny
) && (SidAndAttributes
->Attributes
& SE_GROUP_USE_FOR_DENY_ONLY
)))
95 /* SID is not present */
101 /* Move to the next SID */
105 /* SID is not present */
111 SepSidInToken(IN PACCESS_TOKEN _Token
,
114 /* Call extended API */
115 return SepSidInTokenEx(_Token
, NULL
, Sid
, FALSE
, FALSE
);
120 SepTokenIsOwner(IN PACCESS_TOKEN _Token
,
121 IN PSECURITY_DESCRIPTOR SecurityDescriptor
,
122 IN BOOLEAN TokenLocked
)
126 PTOKEN Token
= _Token
;
128 /* Get the owner SID */
129 Sid
= SepGetOwnerFromDescriptor(SecurityDescriptor
);
132 /* Lock the token if needed */
133 if (!TokenLocked
) SepAcquireTokenLockShared(Token
);
135 /* Check if the owner SID is found, handling restricted case as well */
136 Result
= SepSidInToken(Token
, Sid
);
137 if ((Result
) && (Token
->TokenFlags
& TOKEN_IS_RESTRICTED
))
139 Result
= SepSidInTokenEx(Token
, NULL
, Sid
, FALSE
, TRUE
);
142 /* Release the lock if we had acquired it */
143 if (!TokenLocked
) SepReleaseTokenLock(Token
);
145 /* Return the result */
151 SeGetTokenControlInformation(IN PACCESS_TOKEN _Token
,
152 OUT PTOKEN_CONTROL TokenControl
)
154 PTOKEN Token
= _Token
;
157 /* Capture the main fields */
158 TokenControl
->AuthenticationId
= Token
->AuthenticationId
;
159 TokenControl
->TokenId
= Token
->TokenId
;
160 TokenControl
->TokenSource
= Token
->TokenSource
;
163 SepAcquireTokenLockShared(Token
);
165 /* Capture the modified it */
166 TokenControl
->ModifiedId
= Token
->ModifiedId
;
169 SepReleaseTokenLock(Token
);
174 SepCreateClientSecurity(IN PACCESS_TOKEN Token
,
175 IN PSECURITY_QUALITY_OF_SERVICE ClientSecurityQos
,
176 IN BOOLEAN ServerIsRemote
,
177 IN TOKEN_TYPE TokenType
,
178 IN BOOLEAN ThreadEffectiveOnly
,
179 IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel
,
180 OUT PSECURITY_CLIENT_CONTEXT ClientContext
)
183 PACCESS_TOKEN NewToken
;
186 /* Check for bogus impersonation level */
187 if (!VALID_IMPERSONATION_LEVEL(ClientSecurityQos
->ImpersonationLevel
))
190 return STATUS_INVALID_PARAMETER
;
193 /* Check what kind of token this is */
194 if (TokenType
!= TokenImpersonation
)
196 /* On a primary token, if we do direct access, copy the flag from the QOS */
197 ClientContext
->DirectAccessEffectiveOnly
= ClientSecurityQos
->EffectiveOnly
;
201 /* This is an impersonation token, is the level ok? */
202 if (ClientSecurityQos
->ImpersonationLevel
> ImpersonationLevel
)
205 return STATUS_BAD_IMPERSONATION_LEVEL
;
208 /* Is the level too low, or are we doing something other than delegation remotely */
209 if ((ImpersonationLevel
== SecurityAnonymous
) ||
210 (ImpersonationLevel
== SecurityIdentification
) ||
211 ((ServerIsRemote
) && (ImpersonationLevel
!= SecurityDelegation
)))
214 return STATUS_BAD_IMPERSONATION_LEVEL
;
217 /* Pick either the thread setting or the QOS setting */
218 ClientContext
->DirectAccessEffectiveOnly
= ((ThreadEffectiveOnly
) ||
219 (ClientSecurityQos
->EffectiveOnly
)) ? TRUE
: FALSE
;
222 /* Is this static tracking */
223 if (ClientSecurityQos
->ContextTrackingMode
== SECURITY_STATIC_TRACKING
)
225 /* Do not use direct access and make a copy */
226 ClientContext
->DirectlyAccessClientToken
= FALSE
;
227 Status
= SeCopyClientToken(Token
, ImpersonationLevel
, 0, &NewToken
);
228 if (!NT_SUCCESS(Status
)) return Status
;
232 /* Use direct access and check if this is local */
233 ClientContext
->DirectlyAccessClientToken
= TRUE
;
236 /* We are doing delegation, so make a copy of the control data */
237 SeGetTokenControlInformation(Token
,
238 &ClientContext
->ClientTokenControl
);
241 /* Keep the same token */
245 /* Fill out the context and return success */
246 ClientContext
->SecurityQos
.Length
= sizeof(SECURITY_QUALITY_OF_SERVICE
);
247 ClientContext
->SecurityQos
.ImpersonationLevel
= ClientSecurityQos
->ImpersonationLevel
;
248 ClientContext
->SecurityQos
.ContextTrackingMode
= ClientSecurityQos
->ContextTrackingMode
;
249 ClientContext
->SecurityQos
.EffectiveOnly
= ClientSecurityQos
->EffectiveOnly
;
250 ClientContext
->ServerIsRemote
= ServerIsRemote
;
251 ClientContext
->ClientToken
= NewToken
;
252 return STATUS_SUCCESS
;
255 /* PUBLIC FUNCTIONS ***********************************************************/
262 SeCaptureSubjectContextEx(IN PETHREAD Thread
,
263 IN PEPROCESS Process
,
264 OUT PSECURITY_SUBJECT_CONTEXT SubjectContext
)
266 BOOLEAN CopyOnOpen
, EffectiveOnly
;
270 /* Save the unique ID */
271 SubjectContext
->ProcessAuditId
= Process
->UniqueProcessId
;
273 /* Check if we have a thread */
276 /* We don't, so no token */
277 SubjectContext
->ClientToken
= NULL
;
281 /* Get the impersonation token */
282 SubjectContext
->ClientToken
= PsReferenceImpersonationToken(Thread
,
285 &SubjectContext
->ImpersonationLevel
);
288 /* Get the primary token */
289 SubjectContext
->PrimaryToken
= PsReferencePrimaryToken(Process
);
297 SeCaptureSubjectContext(OUT PSECURITY_SUBJECT_CONTEXT SubjectContext
)
299 /* Call the extended API */
300 SeCaptureSubjectContextEx(PsGetCurrentThread(),
301 PsGetCurrentProcess(),
310 SeLockSubjectContext(IN PSECURITY_SUBJECT_CONTEXT SubjectContext
)
312 PTOKEN PrimaryToken
, ClientToken
;
315 /* Read both tokens */
316 PrimaryToken
= SubjectContext
->PrimaryToken
;
317 ClientToken
= SubjectContext
->ClientToken
;
319 /* Always lock the primary */
320 SepAcquireTokenLockShared(PrimaryToken
);
322 /* Lock the impersonation one if it's there */
323 if (!ClientToken
) return;
324 SepAcquireTokenLockShared(ClientToken
);
332 SeUnlockSubjectContext(IN PSECURITY_SUBJECT_CONTEXT SubjectContext
)
334 PTOKEN PrimaryToken
, ClientToken
;
337 /* Read both tokens */
338 PrimaryToken
= SubjectContext
->PrimaryToken
;
339 ClientToken
= SubjectContext
->ClientToken
;
341 /* Unlock the impersonation one if it's there */
344 SepReleaseTokenLock(ClientToken
);
347 /* Always unlock the primary one */
348 SepReleaseTokenLock(PrimaryToken
);
356 SeReleaseSubjectContext(IN PSECURITY_SUBJECT_CONTEXT SubjectContext
)
360 /* Drop reference on the primary */
361 ObFastDereferenceObject(&PsGetCurrentProcess()->Token
, SubjectContext
->PrimaryToken
);
362 SubjectContext
->PrimaryToken
= NULL
;
364 /* Drop reference on the impersonation, if there was one */
365 PsDereferenceImpersonationToken(SubjectContext
->ClientToken
);
366 SubjectContext
->ClientToken
= NULL
;
374 SeCreateAccessStateEx(IN PETHREAD Thread
,
375 IN PEPROCESS Process
,
376 IN OUT PACCESS_STATE AccessState
,
377 IN PAUX_ACCESS_DATA AuxData
,
378 IN ACCESS_MASK Access
,
379 IN PGENERIC_MAPPING GenericMapping
)
381 ACCESS_MASK AccessMask
= Access
;
385 /* Map the Generic Acess to Specific Access if we have a Mapping */
386 if ((Access
& GENERIC_ACCESS
) && (GenericMapping
))
388 RtlMapGenericMask(&AccessMask
, GenericMapping
);
391 /* Initialize the Access State */
392 RtlZeroMemory(AccessState
, sizeof(ACCESS_STATE
));
393 ASSERT(AccessState
->SecurityDescriptor
== NULL
);
394 ASSERT(AccessState
->PrivilegesAllocated
== FALSE
);
396 /* Initialize and save aux data */
397 RtlZeroMemory(AuxData
, sizeof(AUX_ACCESS_DATA
));
398 AccessState
->AuxData
= AuxData
;
400 /* Capture the Subject Context */
401 SeCaptureSubjectContextEx(Thread
,
403 &AccessState
->SubjectSecurityContext
);
405 /* Set Access State Data */
406 AccessState
->RemainingDesiredAccess
= AccessMask
;
407 AccessState
->OriginalDesiredAccess
= AccessMask
;
408 ExAllocateLocallyUniqueId(&AccessState
->OperationID
);
410 /* Get the Token to use */
411 Token
= SeQuerySubjectContextToken(&AccessState
->SubjectSecurityContext
);
413 /* Check for Travers Privilege */
414 if (Token
->TokenFlags
& TOKEN_HAS_TRAVERSE_PRIVILEGE
)
416 /* Preserve the Traverse Privilege */
417 AccessState
->Flags
= TOKEN_HAS_TRAVERSE_PRIVILEGE
;
420 /* Set the Auxiliary Data */
421 AuxData
->PrivilegeSet
= (PPRIVILEGE_SET
)((ULONG_PTR
)AccessState
+
422 FIELD_OFFSET(ACCESS_STATE
,
424 if (GenericMapping
) AuxData
->GenericMapping
= *GenericMapping
;
427 return STATUS_SUCCESS
;
435 SeCreateAccessState(IN OUT PACCESS_STATE AccessState
,
436 IN PAUX_ACCESS_DATA AuxData
,
437 IN ACCESS_MASK Access
,
438 IN PGENERIC_MAPPING GenericMapping
)
442 /* Call the extended API */
443 return SeCreateAccessStateEx(PsGetCurrentThread(),
444 PsGetCurrentProcess(),
456 SeDeleteAccessState(IN PACCESS_STATE AccessState
)
458 PAUX_ACCESS_DATA AuxData
;
461 /* Get the Auxiliary Data */
462 AuxData
= AccessState
->AuxData
;
464 /* Deallocate Privileges */
465 if (AccessState
->PrivilegesAllocated
)
466 ExFreePoolWithTag(AuxData
->PrivilegeSet
, TAG_PRIVILEGE_SET
);
468 /* Deallocate Name and Type Name */
469 if (AccessState
->ObjectName
.Buffer
)
471 ExFreePool(AccessState
->ObjectName
.Buffer
);
474 if (AccessState
->ObjectTypeName
.Buffer
)
476 ExFreePool(AccessState
->ObjectTypeName
.Buffer
);
479 /* Release the Subject Context */
480 SeReleaseSubjectContext(&AccessState
->SubjectSecurityContext
);
488 SeSetAccessStateGenericMapping(IN PACCESS_STATE AccessState
,
489 IN PGENERIC_MAPPING GenericMapping
)
493 /* Set the Generic Mapping */
494 ((PAUX_ACCESS_DATA
)AccessState
->AuxData
)->GenericMapping
= *GenericMapping
;
502 SeCreateClientSecurity(IN PETHREAD Thread
,
503 IN PSECURITY_QUALITY_OF_SERVICE Qos
,
504 IN BOOLEAN RemoteClient
,
505 OUT PSECURITY_CLIENT_CONTEXT ClientContext
)
507 TOKEN_TYPE TokenType
;
508 BOOLEAN ThreadEffectiveOnly
;
509 SECURITY_IMPERSONATION_LEVEL ImpersonationLevel
;
514 /* Reference the correct token */
515 Token
= PsReferenceEffectiveToken(Thread
,
517 &ThreadEffectiveOnly
,
518 &ImpersonationLevel
);
520 /* Create client security from it */
521 Status
= SepCreateClientSecurity(Token
,
529 /* Check if we failed or static tracking was used */
530 if (!(NT_SUCCESS(Status
)) || (Qos
->ContextTrackingMode
== SECURITY_STATIC_TRACKING
))
532 /* Dereference our copy since it's not being used */
533 ObDereferenceObject(Token
);
545 SeCreateClientSecurityFromSubjectContext(IN PSECURITY_SUBJECT_CONTEXT SubjectContext
,
546 IN PSECURITY_QUALITY_OF_SERVICE ClientSecurityQos
,
547 IN BOOLEAN ServerIsRemote
,
548 OUT PSECURITY_CLIENT_CONTEXT ClientContext
)
554 /* Get the right token and reference it */
555 Token
= SeQuerySubjectContextToken(SubjectContext
);
556 ObReferenceObject(Token
);
558 /* Create the context */
559 Status
= SepCreateClientSecurity(Token
,
562 SubjectContext
->ClientToken
?
563 TokenImpersonation
: TokenPrimary
,
565 SubjectContext
->ImpersonationLevel
,
568 /* Check if we failed or static tracking was used */
569 if (!(NT_SUCCESS(Status
)) ||
570 (ClientSecurityQos
->ContextTrackingMode
== SECURITY_STATIC_TRACKING
))
572 /* Dereference our copy since it's not being used */
573 ObDereferenceObject(Token
);
585 SeImpersonateClientEx(IN PSECURITY_CLIENT_CONTEXT ClientContext
,
586 IN PETHREAD ServerThread OPTIONAL
)
588 BOOLEAN EffectiveOnly
;
591 /* Check if direct access is requested */
592 if (!ClientContext
->DirectlyAccessClientToken
)
594 /* No, so get the flag from QOS */
595 EffectiveOnly
= ClientContext
->SecurityQos
.EffectiveOnly
;
599 /* Yes, so see if direct access should be effective only */
600 EffectiveOnly
= ClientContext
->DirectAccessEffectiveOnly
;
603 /* Use the current thread if one was not passed */
604 if (!ServerThread
) ServerThread
= PsGetCurrentThread();
606 /* Call the lower layer routine */
607 return PsImpersonateClient(ServerThread
,
608 ClientContext
->ClientToken
,
611 ClientContext
->SecurityQos
.ImpersonationLevel
);
619 SeImpersonateClient(IN PSECURITY_CLIENT_CONTEXT ClientContext
,
620 IN PETHREAD ServerThread OPTIONAL
)
624 /* Call the new API */
625 SeImpersonateClientEx(ClientContext
, ServerThread
);