Windows-Server-2003/base/ntos/se/subject.c

666 lines
15 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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