Classifying IRP_MJ_CREATE on user name (PSID)

Hi all,

In my FSFD, I’d like to classify the IRP_MJ_CREATE calls based on the user
that started request. The classification should take place in the dispatch
function.

I’ve thought about using pCurrIrpStack->Parameters.Create.SecurityContext,
taking SecurityDescriptor and converting it to PSID, but SecurityDescriptor
is NULL at this time.

I’d appreciate any ideas where to go.
Joze

I never used this field: maybe it’s NULL for FAT only, and valid for NTFS?

I used the access tokens to get the LUID for the user, and then got the
user name. However, I don’t know how to get the SID from that…
What do you need the SID for? Won’t the SUBJECT_SECURITY_CONTEXT structure
be enough if the driver is to perform access check?

Regards, Dejan.

Joze Fabcic wrote:

> Hi all,
>
> In my FSFD, I’d like to classify the IRP_MJ_CREATE calls based on the user
> that started request. The classification should take place in the dispatch
> function.
>
> I’ve thought about using pCurrIrpStack->Parameters.Create.SecurityContext,
> taking SecurityDescriptor and converting it to PSID, but SecurityDescriptor
> is NULL at this time.
>
> I’d appreciate any ideas where to go.
> Joze
>
> —
> You are currently subscribed to ntfsd as: xxxxx@alfasp.com
> To unsubscribe send a blank email to xxxxx@lists.osr.com


Kind regards, Dejan M. MVP for DDK
http://www.alfasp.com E-mail: xxxxx@alfasp.com
Alfa Transparent File Encryptor - Transparent file encryption services.
Alfa File Protector - File protection and hiding library for Win32 developers.
Alfa File Monitor - File monitoring library for Win32 developers.

Joze,

You need to get the info from the thread, not the security context create
parameter. The security descriptor there is what the user specified for the
create, if anything.

Look at the docs for SeCaptureSubjectContext (and SeReleaseSubjectContext),
SeQuerySubjectContextToken, and SeQueryInformationToken. In particular, you
want to get the TokenOwner.

  • Danilo

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of Dejan Maksimovic
Sent: Thursday, June 05, 2003 8:36 AM
To: File Systems Developers
Subject: [ntfsd] Re: Classifying IRP_MJ_CREATE on user name (PSID)

I never used this field: maybe it’s NULL for FAT only, and valid for
NTFS?

I used the access tokens to get the LUID for the user, and then got the
user name. However, I don’t know how to get the SID from that…
What do you need the SID for? Won’t the SUBJECT_SECURITY_CONTEXT
structure
be enough if the driver is to perform access check?

Regards, Dejan.

Joze Fabcic wrote:

> Hi all,
>
> In my FSFD, I’d like to classify the IRP_MJ_CREATE calls based on the user
> that started request. The classification should take place in the dispatch
> function.
>
> I’ve thought about using pCurrIrpStack->Parameters.Create.SecurityContext,
> taking SecurityDescriptor and converting it to PSID, but
SecurityDescriptor
> is NULL at this time.
>
> I’d appreciate any ideas where to go.
> Joze
>
> —
> You are currently subscribed to ntfsd as: xxxxx@alfasp.com
> To unsubscribe send a blank email to xxxxx@lists.osr.com


Kind regards, Dejan M. MVP for DDK
http://www.alfasp.com E-mail: xxxxx@alfasp.com
Alfa Transparent File Encryptor - Transparent file encryption services.
Alfa File Protector - File protection and hiding library for Win32
developers.
Alfa File Monitor - File monitoring library for Win32 developers.

> I’ve thought about using
pCurrIrpStack->Parameters.Create.SecurityContext,

taking SecurityDescriptor and converting it to PSID, but
SecurityDescriptor
is NULL at this time.

Call ObOpenObjectByPointer on the token pointer from there, then
ZwQueryInformationToken.

Max

> You need to get the info from the thread, not the security context
create

parameter.

Not so.
Any security decisions in CREATE path must be done by using the
security context from the IRP, and the current thread is irrelevant
there. It can be a system thread after all.

Max

> Look at the docs for SeCaptureSubjectContext (and
SeReleaseSubjectContext),

SeQuerySubjectContextToken, and SeQueryInformationToken. In particular,
you
want to get the TokenOwner.

If you want to get SID of the user you need to get TokenUser, not
TokenOwner. TokenOwner returns default owner for new objects, and it can be
SID of a group. Particulary, if user is an administrator, TokenOwner returns
SID of Administtrators group.

Alexei.

Max,
token pointer? From where does he get it?

-Srin.

-----Original Message-----
From: Maxim S. Shatskih [mailto:xxxxx@storagecraft.com]
Sent: Thursday, June 05, 2003 4:52 PM
To: File Systems Developers
Subject: [ntfsd] Re: Classifying IRP_MJ_CREATE on user name (PSID)

> I’ve thought about using
pCurrIrpStack->Parameters.Create.SecurityContext,
> taking SecurityDescriptor and converting it to PSID, but
SecurityDescriptor
> is NULL at this time.

Call ObOpenObjectByPointer on the token pointer from there, then
ZwQueryInformationToken.

Max


You are currently subscribed to ntfsd as: xxxxx@nai.com
To unsubscribe send a blank email to xxxxx@lists.osr.com

// Opens a token handle for specified security context
static NTSTATUS xxxOpenCreatorToken(PIO_SECURITY_CONTEXT
SecurityContext,
OUT PACCESS_TOKEN* Token,
OUT HANDLE* hToken)
{
// “Conditional expression is constant”
#pragma warning(disable:4127)
PAGED_CODE();
#pragma warning(default:4127)
// Extract a token pointer from the security context
*Token =
SecurityContext->AccessState->SubjectSecurityContext.ClientToken;
if( *Token == NULL )
*Token =
SecurityContext->AccessState->SubjectSecurityContext.PrimaryToken;
// Now create the handle
return ObOpenObjectByPointer(*Token, OBJ_CASE_INSENSITIVE, NULL,
TOKEN_QUERY,
NULL,
KernelMode, hToken);
}

Then use ZwQueryInformationToken with the handle.

Max

----- Original Message -----
From:
To: “File Systems Developers”
Sent: Friday, June 06, 2003 9:17 AM
Subject: [ntfsd] Re: Classifying IRP_MJ_CREATE on user name (PSID)

> Max,
> token pointer? From where does he get it?
>
> -Srin.
>
> > -----Original Message-----
> > From: Maxim S. Shatskih [mailto:xxxxx@storagecraft.com]
> > Sent: Thursday, June 05, 2003 4:52 PM
> > To: File Systems Developers
> > Subject: [ntfsd] Re: Classifying IRP_MJ_CREATE on user name (PSID)
> >
> > > I’ve thought about using
> > pCurrIrpStack->Parameters.Create.SecurityContext,
> > > taking SecurityDescriptor and converting it to PSID, but
> > SecurityDescriptor
> > > is NULL at this time.
> >
> > Call ObOpenObjectByPointer on the token pointer from there, then
> > ZwQueryInformationToken.
> >
> > Max
> >
> >
> >
> > —
> > You are currently subscribed to ntfsd as: xxxxx@nai.com
> > To unsubscribe send a blank email to
xxxxx@lists.osr.com
>
>
> —
> You are currently subscribed to ntfsd as: xxxxx@storagecraft.com
> To unsubscribe send a blank email to xxxxx@lists.osr.com
>

As stated in ntifs.h, the fields of SECURITY_SUBJECT_CONTEXT structure
should be considered opaque. So, the following code is somewhat cleaner:

NTSTATUS
xxxOpenCreatorToken(
IN PIO_SECURITY_CONTEXT SecurityContextPtr,
OUT PACCESS_TOKEN *TokenPtr,
OUT HANDLE *TokenHandlePtr)
{
NTSTATUS Status;

// Extract token pointer.
*TokenPtr =
SeQuerySubjectContextToken(&SecurityContextPtr->AccessState->SubjectSecurity
Context);
if (*TokenPtr == NULL)
{
// We must never get here, but still.
*TokenHandlePtr = NULL;
return STATUS_UNSUCCESSFUL;
}

// Create the handle.
Status = ObOpenObjectByPointer(*TokenPtr, OBJ_CASE_INSENSITIVE, NULL,
TOKEN_QUERY, NULL, KernelMode, TokenHandlePtr);

// Exit routine.
return Status;
}

----- Original Message -----
From: “Maxim S. Shatskih”
To: “File Systems Developers”
Sent: Friday, June 06, 2003 9:02 AM
Subject: [ntfsd] Re: Classifying IRP_MJ_CREATE on user name (PSID)

> // Opens a token handle for specified security context
> static NTSTATUS xxxOpenCreatorToken(PIO_SECURITY_CONTEXT
> SecurityContext,
> OUT PACCESS_TOKEN* Token,
> OUT HANDLE* hToken)
> {
> // “Conditional expression is constant”
> #pragma warning(disable:4127)
> PAGED_CODE();
> #pragma warning(default:4127)
> // Extract a token pointer from the security context
> *Token =
> SecurityContext->AccessState->SubjectSecurityContext.ClientToken;
> if( *Token == NULL )
> *Token =
> SecurityContext->AccessState->SubjectSecurityContext.PrimaryToken;
> // Now create the handle
> return ObOpenObjectByPointer(*Token, OBJ_CASE_INSENSITIVE, NULL,
> TOKEN_QUERY,
> NULL,
> KernelMode, hToken);
> }
>
> Then use ZwQueryInformationToken with the handle.
>
> Max
>
>
> ----- Original Message -----
> From:
> To: “File Systems Developers”
> Sent: Friday, June 06, 2003 9:17 AM
> Subject: [ntfsd] Re: Classifying IRP_MJ_CREATE on user name (PSID)
>
>
> > Max,
> > token pointer? From where does he get it?
> >
> > -Srin.
> >
> > > -----Original Message-----
> > > From: Maxim S. Shatskih [mailto:xxxxx@storagecraft.com]
> > > Sent: Thursday, June 05, 2003 4:52 PM
> > > To: File Systems Developers
> > > Subject: [ntfsd] Re: Classifying IRP_MJ_CREATE on user name (PSID)
> > >
> > > > I’ve thought about using
> > > pCurrIrpStack->Parameters.Create.SecurityContext,
> > > > taking SecurityDescriptor and converting it to PSID, but
> > > SecurityDescriptor
> > > > is NULL at this time.
> > >
> > > Call ObOpenObjectByPointer on the token pointer from there, then
> > > ZwQueryInformationToken.
> > >
> > > Max
> > >
> > >
> > >
> > > —
> > > You are currently subscribed to ntfsd as: xxxxx@nai.com
> > > To unsubscribe send a blank email to
> xxxxx@lists.osr.com
> >
> >
> > —
> > You are currently subscribed to ntfsd as: xxxxx@storagecraft.com
> > To unsubscribe send a blank email to xxxxx@lists.osr.com
> >
>
>
>
> —
> You are currently subscribed to ntfsd as: xxxxx@vba.com.by
> To unsubscribe send a blank email to xxxxx@lists.osr.com
>

Thanks to Maxim, Alexey and Alexei for a quick start.

Ok, now I can get User and Owner SIDs. Next question. I’d like to check
whether the user is a member of Administrator group. IFS docs specify
SeTokenIsAdmin. I’m still compiling for NT4 and SeTokenIsAdmin is not
present there. I assume this can be basically expressed as

fIsAdmin = strcmp(“S-1-5-32-544”, szTokenOwnerSid)==0;

Is this correct? What’s the preferred coding for such a test?

IFS kit says that ZwQueryInformationToken “is available on Microsoft(r)
Windows(r) XP and later”. I tried it on NT4 and it worked. Can I just
neglect this version availability note?

Thanks, Joze

Try using this.

NTSTATUS
IsUserAdmin(
IN HANDLE TokenHandle,
OUT BOOLEAN * UserIsAdminPtr)
{
// Routine status.
NTSTATUS Status = STATUS_UNSUCCESSFUL;
// Intialize output parameters.
*UserIsAdminPtr = FALSE;
// Resources to release in finally block.
PTOKEN_GROUPS TokenGroupsPtr = NULL;
// Use try/finally semantics for cleanup.
TRY
{
// Calculate size of security identifier.
CONST ULONG SidLength =
FIELD_OFFSET(SID, SubAuthority) + // header size
2 * RTL_FIELD_SIZE(SID, SubAuthority); // 2 sub-authorities
// Allocate temporary stack based storage for our SID.
UCHAR SidBuffer[SidLength];
PISID AdminsSidPtr = (PISID)SidBuffer;
// Initialize security identifier.
SID_IDENTIFIER_AUTHORITY NtAuthoritySid = SECURITY_NT_AUTHORITY;
RtlZeroMemory(
AdminsSidPtr,
SidLength);
AdminsSidPtr->Revision = SID_REVISION;
AdminsSidPtr->SubAuthorityCount = 2;
AdminsSidPtr->IdentifierAuthority = NtAuthoritySid;
AdminsSidPtr->SubAuthority[0] = SECURITY_BUILTIN_DOMAIN_RID;
AdminsSidPtr->SubAuthority[1] = DOMAIN_ALIAS_RID_ADMINS;
// Make sure our SID is valid.
if (!RtlValidSid(AdminsSidPtr))
{
try_return(NOTHING);
}
// Cycle until sufficient buffer is allocated.
ULONG TokenGroupsLength = 512;
for (;:wink:
{
// Free token groups buffer if one was allocated.
if (TokenGroupsPtr)
{
ExFreePool(TokenGroupsPtr);
}
// Allocate new buffer for token groups.
TokenGroupsPtr = (PTOKEN_GROUPS)
ExAllocatePool(
PagedPool,
TokenGroupsLength);
if (TokenGroupsPtr == NULL)
{
try_return(Status = STATUS_INSUFFICIENT_RESOURCES);
}
// Query token information.
Status = ZwQueryInformationToken(
TokenHandle,
TokenGroups,
TokenGroupsPtr,
TokenGroupsLength,
&TokenGroupsLength);
if (Status != STATUS_BUFFER_TOO_SMALL)
{
break;
}
// We need to reallocate buffer and query informaion again.
}
// Check if we’ve got token groups information.
if (NT_FAIL(Status))
{
try_return(NOTHING);
}
// Go through all SIDs to see if there’s one we’re looking for.
for (ULONG Index = 0; Index < TokenGroupsPtr->GroupCount; ++Index)
{
if (RtlEqualSid(AdminsSidPtr, TokenGroupsPtr->Groups[Index].Sid))
{
try_return(*UserIsAdminPtr = TRUE);
}
}
// Exit try block.
try_exit: NOTHING;
}
FINALLY
{
if (TokenGroupsPtr)
{
ExFreePool(TokenGroupsPtr);
}
}
// Exit routine.
return Status;
}

----- Original Message -----
From: “Joze Fabcic”
To: “File Systems Developers”
Sent: Wednesday, June 11, 2003 6:36 PM
Subject: [ntfsd] Re: Classifying IRP_MJ_CREATE on user name (PSID)

> Thanks to Maxim, Alexey and Alexei for a quick start.
>
> Ok, now I can get User and Owner SIDs. Next question. I’d like to check
> whether the user is a member of Administrator group. IFS docs specify
> SeTokenIsAdmin. I’m still compiling for NT4 and SeTokenIsAdmin is not
> present there. I assume this can be basically expressed as
>
> fIsAdmin = strcmp(“S-1-5-32-544”, szTokenOwnerSid)==0;
>
> Is this correct? What’s the preferred coding for such a test?
>
> IFS kit says that ZwQueryInformationToken “is available on Microsoft(r)
> Windows(r) XP and later”. I tried it on NT4 and it worked. Can I just
> neglect this version availability note?
>
> Thanks, Joze
>
>
> —
> You are currently subscribed to ntfsd as: xxxxx@vba.com.by
> To unsubscribe send a blank email to xxxxx@lists.osr.com
>

> IFS kit says that ZwQueryInformationToken "is available on
Microsoft(r)

Windows(r) XP and later". I tried it on NT4 and it worked. Can I
just
neglect this version availability note?

Surely, I have worked on NT4-only project in 2001, which used
ZwQueryInformationToken/TokenGroups instead of SeTokenIsAdmin. It
worked fine.

Here is the prototype:

NTSTATUS NTAPI ZwQueryInformationToken (IN HANDLE TokenHandle,
IN TOKEN_INFORMATION_CLASS TokenInformationClass,
OUT PVOID TokenInformation,
IN ULONG TokenInformationLength,
OUT PULONG ReturnLength);

while TOKEN_INFORMATION_CLASS is declared in NT4’s NTIFS.H.

For TokenGroups, the returned data is laid out as TOKEN_GROUPS. First
call with zero length to obtain the required length, then allocate,
then call with real length and buffer.

Max