666 lines
15 KiB
C
666 lines
15 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
Subject.c
|
||
|
||
Abstract:
|
||
|
||
This Module implements services related to subject security context.
|
||
These services are part of the services provided by the Reference Monitor
|
||
component.
|
||
|
||
FOR PERFORMANCE SAKE, THIS MODULE IS AWARE OF INTERNAL TOKEN OBJECT
|
||
FORMATS.
|
||
|
||
Author:
|
||
|
||
Jim Kelly (JimK) 2-Aug-1990
|
||
|
||
Environment:
|
||
|
||
Kernel Mode
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "pch.h"
|
||
|
||
#pragma hdrstop
|
||
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE,SeCaptureSubjectContext)
|
||
#pragma alloc_text(PAGE,SeCaptureSubjectContextEx)
|
||
#pragma alloc_text(PAGE,SeLockSubjectContext)
|
||
#pragma alloc_text(PAGE,SeUnlockSubjectContext)
|
||
#pragma alloc_text(PAGE,SeReleaseSubjectContext)
|
||
#pragma alloc_text(PAGE,SepGetDefaultsSubjectContext)
|
||
#pragma alloc_text(PAGE,SepIdAssignableAsGroup)
|
||
#pragma alloc_text(PAGE,SepValidOwnerSubjectContext)
|
||
//#pragma alloc_text(PAGE,SeQueryAuthenticationIdSubjectContext)
|
||
#endif
|
||
|
||
|
||
VOID
|
||
SeCaptureSubjectContext (
|
||
OUT PSECURITY_SUBJECT_CONTEXT SubjectContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine takes a snapshot of the calling thread's security
|
||
context (locking tokens as necessary to do so). This function
|
||
is intended to support the object manager and other components
|
||
that utilize the reference monitor's access validation,
|
||
privilege test, and audit generation services.
|
||
|
||
A subject's security context should be captured before initiating
|
||
access validation and should be released after audit messages
|
||
are generated. This is necessary to provide a consistent security
|
||
context to all those services.
|
||
|
||
After calling access validation, privilege test, and audit generation
|
||
services, the captured context should be released as soon as possible
|
||
using the SeReleaseSubjectContext() service.
|
||
|
||
Arguments:
|
||
|
||
SubjectContext - Points to a SECURITY_SUBJECT_CONTEXT data structure
|
||
to be filled in with a snapshot of the calling thread's security
|
||
profile.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
SeCaptureSubjectContextEx (PsGetCurrentThread (),
|
||
PsGetCurrentProcess (),
|
||
SubjectContext);
|
||
}
|
||
|
||
|
||
VOID
|
||
SeCaptureSubjectContextEx (
|
||
IN PETHREAD Thread,
|
||
IN PEPROCESS Process,
|
||
OUT PSECURITY_SUBJECT_CONTEXT SubjectContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine takes a snapshot of the calling thread's security
|
||
context (locking tokens as necessary to do so). This function
|
||
is intended to support the object manager and other components
|
||
that utilize the reference monitor's access validation,
|
||
privilege test, and audit generation services.
|
||
|
||
A subject's security context should be captured before initiating
|
||
access validation and should be released after audit messages
|
||
are generated. This is necessary to provide a consistent security
|
||
context to all those services.
|
||
|
||
After calling access validation, privilege test, and audit generation
|
||
services, the captured context should be released as soon as possible
|
||
using the SeReleaseSubjectContext() service.
|
||
|
||
Arguments:
|
||
|
||
Thread - Thread to capture the thread token from. If NULL we don't capture
|
||
an impersonation token.
|
||
|
||
Process - Process to capture primary token from.
|
||
|
||
SubjectContext - Points to a SECURITY_SUBJECT_CONTEXT data structure
|
||
to be filled in with a snapshot of the calling thread's security
|
||
profile.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
//PVOID Objects[2];
|
||
|
||
BOOLEAN IgnoreCopyOnOpen;
|
||
BOOLEAN IgnoreEffectiveOnly;
|
||
|
||
PAGED_CODE();
|
||
|
||
SubjectContext->ProcessAuditId = PsProcessAuditId( Process );
|
||
|
||
//
|
||
// Get pointers to primary and impersonation tokens
|
||
//
|
||
|
||
if (Thread == NULL) {
|
||
SubjectContext->ClientToken = NULL;
|
||
} else {
|
||
SubjectContext->ClientToken = PsReferenceImpersonationToken(
|
||
Thread,
|
||
&IgnoreCopyOnOpen,
|
||
&IgnoreEffectiveOnly,
|
||
&(SubjectContext->ImpersonationLevel)
|
||
);
|
||
}
|
||
|
||
SubjectContext->PrimaryToken = PsReferencePrimaryToken(Process);
|
||
|
||
#if DBG || TOKEN_LEAK_MONITOR
|
||
|
||
if (SubjectContext->PrimaryToken) {
|
||
InterlockedIncrement(&((PTOKEN)(SubjectContext->PrimaryToken))->CaptureCount);
|
||
if (SubjectContext->PrimaryToken == SepTokenLeakToken)
|
||
{
|
||
DbgBreakPoint();
|
||
}
|
||
}
|
||
|
||
if (SubjectContext->ClientToken) {
|
||
InterlockedIncrement(&((PTOKEN)(SubjectContext->ClientToken))->CaptureCount);
|
||
if (SubjectContext->ClientToken == SepTokenLeakToken)
|
||
{
|
||
DbgBreakPoint();
|
||
}
|
||
}
|
||
|
||
#endif
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
SeLockSubjectContext(
|
||
IN PSECURITY_SUBJECT_CONTEXT SubjectContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Acquires READ LOCKS on the primary and impersonation tokens
|
||
in the passed SubjectContext.
|
||
|
||
This call must be undone by a call to SeUnlockSubjectContext().
|
||
|
||
No one outside of the SE component should need to acquire a
|
||
write lock to a token. Therefore there is no public interface
|
||
to do this.
|
||
|
||
Arguments:
|
||
|
||
SubjectContext - Points to a SECURITY_SUBJECT_CONTEXT data structure
|
||
which points to a primary token and an optional impersonation token.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
SepAcquireTokenReadLock((PTOKEN)(SubjectContext->PrimaryToken));
|
||
|
||
if (ARGUMENT_PRESENT(SubjectContext->ClientToken)) {
|
||
|
||
SepAcquireTokenReadLock((PTOKEN)(SubjectContext->ClientToken));
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
SeUnlockSubjectContext(
|
||
IN PSECURITY_SUBJECT_CONTEXT SubjectContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Releases the read locks on the token(s) in the passed SubjectContext.
|
||
|
||
Arguments:
|
||
|
||
SubjectContext - Points to a SECURITY_SUBJECT_CONTEXT data structure
|
||
which points to a primary token and an optional impersonation token.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
SepReleaseTokenReadLock((PTOKEN)(SubjectContext->PrimaryToken));
|
||
|
||
if (ARGUMENT_PRESENT(SubjectContext->ClientToken)) {
|
||
|
||
SepReleaseTokenReadLock((PTOKEN)(SubjectContext->ClientToken));
|
||
}
|
||
|
||
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
SeReleaseSubjectContext (
|
||
IN PSECURITY_SUBJECT_CONTEXT SubjectContext
|
||
)
|
||
|
||
/*++
|
||
|
||
|
||
Routine Description:
|
||
|
||
This routine releases a subject security context previously captured by
|
||
SeCaptureSubjectContext().
|
||
|
||
Arguments:
|
||
|
||
SubjectContext - Points to a SECURITY_SUBJECT_CONTEXT data structure
|
||
containing a subject's previously captured security context.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
#if DBG || TOKEN_LEAK_MONITOR
|
||
|
||
if (SubjectContext->PrimaryToken) {
|
||
InterlockedDecrement(&((PTOKEN)(SubjectContext->PrimaryToken))->CaptureCount);
|
||
if (SubjectContext->PrimaryToken == SepTokenLeakToken)
|
||
{
|
||
DbgBreakPoint();
|
||
}
|
||
}
|
||
|
||
if (SubjectContext->ClientToken) {
|
||
InterlockedDecrement(&((PTOKEN)(SubjectContext->ClientToken))->CaptureCount);
|
||
if (SubjectContext->ClientToken == SepTokenLeakToken)
|
||
{
|
||
DbgBreakPoint();
|
||
}
|
||
}
|
||
|
||
#endif
|
||
|
||
PsDereferencePrimaryTokenEx( PsGetCurrentProcess(), SubjectContext->PrimaryToken );
|
||
|
||
SubjectContext->PrimaryToken = NULL;
|
||
|
||
PsDereferenceImpersonationToken( SubjectContext->ClientToken );
|
||
|
||
SubjectContext->ClientToken = NULL;
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
VOID
|
||
SepGetDefaultsSubjectContext(
|
||
IN PSECURITY_SUBJECT_CONTEXT SubjectContext,
|
||
OUT PSID *Owner,
|
||
OUT PSID *Group,
|
||
OUT PSID *ServerOwner,
|
||
OUT PSID *ServerGroup,
|
||
OUT PACL *Dacl
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine retrieves pointers to the default owner, primary group,
|
||
and, if present, discretionary ACL of the provided subject security
|
||
context.
|
||
|
||
Arguments:
|
||
|
||
SubjectContext - Points to the subject security context whose default
|
||
values are to be retrieved.
|
||
|
||
Owner - Receives a pointer to the subject's default owner SID. This
|
||
value will always be returned as a non-zero pointer. That is,
|
||
a subject's security context must contain a owner SID.
|
||
|
||
Group - Receives a pointer to the subject's default primary group SID.
|
||
This value will always be returned as a non-zero pointer. That is,
|
||
a subject's security context must contain a primary group.
|
||
|
||
Dacl - Receives a pointer to the subject's default discretionary ACL,
|
||
if one is define for the subject. Note that a subject security context
|
||
does not have to include a default discretionary ACL. In this case,
|
||
this value will be returned as NULL.
|
||
|
||
|
||
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
PTOKEN EffectiveToken;
|
||
PTOKEN PrimaryToken;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (ARGUMENT_PRESENT(SubjectContext->ClientToken)) {
|
||
EffectiveToken = (PTOKEN)SubjectContext->ClientToken;
|
||
} else {
|
||
EffectiveToken = (PTOKEN)SubjectContext->PrimaryToken;
|
||
}
|
||
|
||
(*Owner) = EffectiveToken->UserAndGroups[EffectiveToken->DefaultOwnerIndex].Sid;
|
||
|
||
(*Group) = EffectiveToken->PrimaryGroup;
|
||
|
||
(*Dacl) = EffectiveToken->DefaultDacl;
|
||
|
||
PrimaryToken = (PTOKEN)SubjectContext->PrimaryToken;
|
||
|
||
*ServerOwner = PrimaryToken->UserAndGroups[PrimaryToken->DefaultOwnerIndex].Sid;
|
||
|
||
*ServerGroup = PrimaryToken->PrimaryGroup;
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
SepIdAssignableAsGroup(
|
||
IN PACCESS_TOKEN AToken,
|
||
IN PSID Group
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine checks to see whether the provided SID is one that
|
||
may be assigned to be the default primary group in a token.
|
||
|
||
The current criteria is that the passed SID be a group in the
|
||
token, with no other restrictions.
|
||
|
||
Arguments:
|
||
|
||
Token - Points to the token to be examined.
|
||
|
||
Group - Points to the SID to be checked.
|
||
|
||
Return Value:
|
||
|
||
TRUE - SID passed by be assigned as the default primary group in a token.
|
||
|
||
FALSE - Passed SID may not be so assigned.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG Index;
|
||
BOOLEAN Found = FALSE;
|
||
PTOKEN Token;
|
||
|
||
PAGED_CODE();
|
||
|
||
Token = (PTOKEN)AToken;
|
||
|
||
//
|
||
// Let's make it invalid to assign a NULL primary group,
|
||
// but we may need to revisit this.
|
||
//
|
||
|
||
if (Group == NULL) {
|
||
return( FALSE );
|
||
}
|
||
Index = 0;
|
||
|
||
SepAcquireTokenReadLock( Token );
|
||
|
||
//
|
||
// Walk through the list of user and group IDs looking
|
||
// for a match to the specified SID.
|
||
//
|
||
|
||
while (Index < Token->UserAndGroupCount) {
|
||
|
||
Found = RtlEqualSid(
|
||
Group,
|
||
Token->UserAndGroups[Index].Sid
|
||
);
|
||
|
||
if ( Found ) {
|
||
break;
|
||
}
|
||
|
||
Index += 1;
|
||
}
|
||
|
||
SepReleaseTokenReadLock( Token );
|
||
|
||
return Found;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
SepValidOwnerSubjectContext(
|
||
IN PSECURITY_SUBJECT_CONTEXT SubjectContext,
|
||
IN PSID Owner,
|
||
IN BOOLEAN ServerObject
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine checks to see whether the provided SID is one the subject
|
||
is authorized to assign as the owner of objects. It will also check to
|
||
see if the caller has SeRestorePrivilege, if so, the request is granted.
|
||
|
||
Arguments:
|
||
|
||
SubjectContext - Points to the subject's security context.
|
||
|
||
Owner - Points to the SID to be checked.
|
||
|
||
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
ULONG Index;
|
||
BOOLEAN Found;
|
||
PTOKEN EffectiveToken;
|
||
BOOLEAN Rc = FALSE;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// It is invalid to assign a NULL owner, regardless of
|
||
// whether you have SeRestorePrivilege or not.
|
||
//
|
||
|
||
if (Owner == NULL) {
|
||
return( FALSE );
|
||
}
|
||
|
||
//
|
||
// Allowable owners come from the primary if it's a server object.
|
||
//
|
||
|
||
if (!ServerObject && ARGUMENT_PRESENT(SubjectContext->ClientToken)) {
|
||
EffectiveToken = (PTOKEN)SubjectContext->ClientToken;
|
||
} else {
|
||
EffectiveToken = (PTOKEN)SubjectContext->PrimaryToken;
|
||
}
|
||
|
||
|
||
//
|
||
// If we're impersonating, make sure we're at TokenImpersonation
|
||
// or above. This prevents someone from setting the owner of an
|
||
// object when impersonating at less Identify or Anonymous.
|
||
//
|
||
|
||
if (EffectiveToken->TokenType == TokenImpersonation) {
|
||
|
||
if (EffectiveToken->ImpersonationLevel < SecurityImpersonation) {
|
||
|
||
return( FALSE );
|
||
|
||
}
|
||
}
|
||
|
||
Index = 0;
|
||
|
||
SepAcquireTokenReadLock( EffectiveToken );
|
||
|
||
//
|
||
// Walk through the list of user and group IDs looking
|
||
// for a match to the specified SID. If one is found,
|
||
// make sure it may be assigned as an owner.
|
||
//
|
||
// This code is similar to that performed to set the default
|
||
// owner of a token (NtSetInformationToken).
|
||
//
|
||
|
||
while (Index < EffectiveToken->UserAndGroupCount) {
|
||
|
||
|
||
Found = RtlEqualSid(
|
||
Owner,
|
||
EffectiveToken->UserAndGroups[Index].Sid
|
||
);
|
||
|
||
if ( Found ) {
|
||
|
||
//
|
||
// We may return success if the Sid is one that may be assigned
|
||
// as an owner, or if the caller has SeRestorePrivilege
|
||
//
|
||
|
||
if ( SepIdAssignableAsOwner(EffectiveToken,Index) ) {
|
||
|
||
SepReleaseTokenReadLock( EffectiveToken );
|
||
Rc = TRUE;
|
||
goto exit;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Rc is already set to FALSE, just exit.
|
||
//
|
||
|
||
SepReleaseTokenReadLock( EffectiveToken );
|
||
goto exit;
|
||
|
||
} //endif assignable
|
||
|
||
|
||
} //endif Found
|
||
|
||
|
||
Index += 1;
|
||
|
||
} //endwhile
|
||
|
||
|
||
SepReleaseTokenReadLock( EffectiveToken );
|
||
|
||
exit:
|
||
|
||
//
|
||
// If we are going to fail this call, check for Restore privilege,
|
||
// and succeed if he has it.
|
||
//
|
||
|
||
//
|
||
// We should really have gotten PreviousMode from the caller, but we
|
||
// didn't, so hard wire it to be user-mode here.
|
||
//
|
||
|
||
if ( Rc == FALSE ) {
|
||
Rc = SeSinglePrivilegeCheck( SeRestorePrivilege, UserMode );
|
||
}
|
||
|
||
return Rc;
|
||
}
|
||
|
||
|
||
#if 0
|
||
NTSTATUS
|
||
SeQueryAuthenticationIdSubjectContext(
|
||
IN PSECURITY_SUBJECT_CONTEXT SubjectContext,
|
||
OUT PLUID AuthenticationId
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine returns the authentication ID for the effective token
|
||
in a subject context
|
||
|
||
Parameters:
|
||
|
||
SubjectContext - The subject context to get the ID from
|
||
|
||
AuthenticationId - Receives the authentication ID from the token
|
||
|
||
Return Value:
|
||
|
||
Errors from SeQueryAuthenticationidToken.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
PAGED_CODE();
|
||
|
||
SeLockSubjectContext( SubjectContext );
|
||
|
||
|
||
Status = SeQueryAuthenticationIdToken(
|
||
EffectiveToken(SubjectContext),
|
||
AuthenticationId
|
||
);
|
||
|
||
SeUnlockSubjectContext( SubjectContext );
|
||
|
||
return( Status );
|
||
|
||
|
||
}
|
||
#endif
|