- accesschk.c: Remove redundant SepAccessCheck/SepAccessCheckEx pair of private functions; instead just rename SepAccessCheckEx into SepAccessCheck and use it directly in the code. NOTE: SepAccessCheck is *incomplete* (in particular it doesn't retrieve the information needed to initialize the 'Privileges' parameter).
- sid.c: Comments formatting fix.
- token.c:
  * Finish to implement SeQueryInformationToken . This function is really the same as NtQueryInformationToken but without all the stuff needed for user-mode buffer access protection.
  * Some code simplifications in NtQueryInformationToken.
  I need this to fix a "FIXME: Use SeQueryInformationToken" in some code I'm also fixing (& commit later).

[NDK]: Fix parameter types and add annotations to RtlCopySidAndAttributesArray.

[KMTESTS:NTOS_SE]
- Reenable the 'SeQueryInfoToken' test.
- Show that SeQueryInformationToken doesn't support 4 token information classes, which are supported only by NtQueryInformationToken.
- Fix calling of SeAccessCheck. In particular the 'Privileges' parameter is not allocated by the caller, but instead is allocated by SeAccessCheck *and* returned to the caller (who then must free the buffer using SeFreePrivileges). This fixes the encountered BSODs that leaded to disabling preventively the test in r59178.
- Minor code cleaning.

svn path=/trunk/; revision=73122
This commit is contained in:
Hermès Bélusca-Maïto 2016-11-04 17:52:32 +00:00
parent 3430268b90
commit 15d77be6fd
7 changed files with 511 additions and 118 deletions

View file

@ -18,8 +18,11 @@
/* PRIVATE FUNCTIONS **********************************************************/ /* PRIVATE FUNCTIONS **********************************************************/
/*
* FIXME: Incomplete!
*/
BOOLEAN NTAPI BOOLEAN NTAPI
SepAccessCheckEx(IN PSECURITY_DESCRIPTOR SecurityDescriptor, SepAccessCheck(IN PSECURITY_DESCRIPTOR SecurityDescriptor,
IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext, IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
IN ACCESS_MASK DesiredAccess, IN ACCESS_MASK DesiredAccess,
IN POBJECT_TYPE_LIST ObjectTypeList, IN POBJECT_TYPE_LIST ObjectTypeList,
@ -46,7 +49,7 @@ SepAccessCheckEx(IN PSECURITY_DESCRIPTOR SecurityDescriptor,
NTSTATUS Status; NTSTATUS Status;
PAGED_CODE(); PAGED_CODE();
DPRINT("SepAccessCheckEx()\n"); DPRINT("SepAccessCheck()\n");
/* Check for no access desired */ /* Check for no access desired */
if (!DesiredAccess) if (!DesiredAccess)
@ -283,31 +286,6 @@ ReturnCommonStatus:
return NT_SUCCESS(Status); return NT_SUCCESS(Status);
} }
BOOLEAN NTAPI
SepAccessCheck(IN PSECURITY_DESCRIPTOR SecurityDescriptor,
IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
IN ACCESS_MASK DesiredAccess,
IN ACCESS_MASK PreviouslyGrantedAccess,
OUT PPRIVILEGE_SET* Privileges,
IN PGENERIC_MAPPING GenericMapping,
IN KPROCESSOR_MODE AccessMode,
OUT PACCESS_MASK GrantedAccess,
OUT PNTSTATUS AccessStatus)
{
return SepAccessCheckEx(SecurityDescriptor,
SubjectSecurityContext,
DesiredAccess,
NULL,
0,
PreviouslyGrantedAccess,
Privileges,
GenericMapping,
AccessMode,
GrantedAccess,
AccessStatus,
FALSE);
}
static PSID static PSID
SepGetSDOwner(IN PSECURITY_DESCRIPTOR _SecurityDescriptor) SepGetSDOwner(IN PSECURITY_DESCRIPTOR _SecurityDescriptor)
{ {
@ -443,12 +421,15 @@ SeAccessCheck(IN PSECURITY_DESCRIPTOR SecurityDescriptor,
ret = SepAccessCheck(SecurityDescriptor, ret = SepAccessCheck(SecurityDescriptor,
SubjectSecurityContext, SubjectSecurityContext,
DesiredAccess, DesiredAccess,
NULL,
0,
PreviouslyGrantedAccess, PreviouslyGrantedAccess,
Privileges, Privileges,
GenericMapping, GenericMapping,
AccessMode, AccessMode,
GrantedAccess, GrantedAccess,
AccessStatus); AccessStatus,
FALSE);
} }
/* Release the lock if needed */ /* Release the lock if needed */
@ -687,12 +668,15 @@ NtAccessCheck(IN PSECURITY_DESCRIPTOR SecurityDescriptor,
SepAccessCheck(SecurityDescriptor, // FIXME: use CapturedSecurityDescriptor SepAccessCheck(SecurityDescriptor, // FIXME: use CapturedSecurityDescriptor
&SubjectSecurityContext, &SubjectSecurityContext,
DesiredAccess, DesiredAccess,
NULL,
0,
PreviouslyGrantedAccess, PreviouslyGrantedAccess,
&PrivilegeSet, //FIXME &PrivilegeSet, //FIXME
GenericMapping, GenericMapping,
PreviousMode, PreviousMode,
GrantedAccess, GrantedAccess,
AccessStatus); AccessStatus,
FALSE);
} }
/* Release subject context and unlock the token */ /* Release subject context and unlock the token */

View file

@ -297,7 +297,7 @@ SepCaptureSid(IN PSID InputSid,
} }
_SEH2_END; _SEH2_END;
/* allocate a SID and copy it */ /* Allocate a SID and copy it */
NewSid = ExAllocatePoolWithTag(PoolType, SidSize, TAG_SID); NewSid = ExAllocatePoolWithTag(PoolType, SidSize, TAG_SID);
if (!NewSid) if (!NewSid)
return STATUS_INSUFFICIENT_RESOURCES; return STATUS_INSUFFICIENT_RESOURCES;
@ -324,7 +324,7 @@ SepCaptureSid(IN PSID InputSid,
{ {
SidSize = RtlLengthRequiredSid(Sid->SubAuthorityCount); SidSize = RtlLengthRequiredSid(Sid->SubAuthorityCount);
/* allocate a SID and copy it */ /* Allocate a SID and copy it */
NewSid = ExAllocatePoolWithTag(PoolType, SidSize, TAG_SID); NewSid = ExAllocatePoolWithTag(PoolType, SidSize, TAG_SID);
if (NewSid == NULL) if (NewSid == NULL)
return STATUS_INSUFFICIENT_RESOURCES; return STATUS_INSUFFICIENT_RESOURCES;

View file

@ -232,7 +232,7 @@ SeExchangePrimaryToken(PEPROCESS Process,
PAGED_CODE(); PAGED_CODE();
if (NewToken->TokenType != TokenPrimary) return(STATUS_BAD_TOKEN_TYPE); if (NewToken->TokenType != TokenPrimary) return STATUS_BAD_TOKEN_TYPE;
if (NewToken->TokenInUse) if (NewToken->TokenInUse)
{ {
BOOLEAN IsEqual; BOOLEAN IsEqual;
@ -348,12 +348,12 @@ SepFindPrimaryGroupAndDefaultOwner(PTOKEN Token,
if (Token->DefaultOwnerIndex == Token->UserAndGroupCount) if (Token->DefaultOwnerIndex == Token->UserAndGroupCount)
{ {
return(STATUS_INVALID_OWNER); return STATUS_INVALID_OWNER;
} }
if (Token->PrimaryGroup == 0) if (Token->PrimaryGroup == 0)
{ {
return(STATUS_INVALID_PRIMARY_GROUP); return STATUS_INVALID_PRIMARY_GROUP;
} }
return STATUS_SUCCESS; return STATUS_SUCCESS;
@ -1060,16 +1060,26 @@ SeFilterToken(IN PACCESS_TOKEN ExistingToken,
} }
/* /*
* @unimplemented * @implemented
*
* NOTE: SeQueryInformationToken is just NtQueryInformationToken without all
* the bells and whistles needed for user-mode buffer access protection.
*/ */
NTSTATUS NTSTATUS
NTAPI NTAPI
SeQueryInformationToken(IN PACCESS_TOKEN Token, SeQueryInformationToken(IN PACCESS_TOKEN AccessToken,
IN TOKEN_INFORMATION_CLASS TokenInformationClass, IN TOKEN_INFORMATION_CLASS TokenInformationClass,
OUT PVOID *TokenInformation) OUT PVOID *TokenInformation)
{ {
NTSTATUS Status; NTSTATUS Status;
PSECURITY_IMPERSONATION_LEVEL SeImpersonationLvl; PTOKEN Token = (PTOKEN)AccessToken;
ULONG RequiredLength;
union
{
PSID PSid;
ULONG Ulong;
} Unused;
PAGED_CODE(); PAGED_CODE();
if (TokenInformationClass >= MaxTokenInfoClass) if (TokenInformationClass >= MaxTokenInfoClass)
@ -1080,31 +1090,395 @@ SeQueryInformationToken(IN PACCESS_TOKEN Token,
switch (TokenInformationClass) switch (TokenInformationClass)
{ {
case TokenUser:
{
PTOKEN_USER tu;
DPRINT("SeQueryInformationToken(TokenUser)\n");
RequiredLength = sizeof(TOKEN_USER) +
RtlLengthSid(Token->UserAndGroups[0].Sid);
/* Allocate the output buffer */
tu = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
if (tu == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
Status = RtlCopySidAndAttributesArray(1,
&Token->UserAndGroups[0],
RequiredLength - sizeof(TOKEN_USER),
&tu->User,
(PSID)(tu + 1),
&Unused.PSid,
&Unused.Ulong);
/* Return the structure */
*TokenInformation = tu;
Status = STATUS_SUCCESS;
break;
}
case TokenGroups:
{
PTOKEN_GROUPS tg;
ULONG SidLen;
PSID Sid;
DPRINT("SeQueryInformationToken(TokenGroups)\n");
RequiredLength = sizeof(tg->GroupCount) +
RtlLengthSidAndAttributes(Token->UserAndGroupCount - 1, &Token->UserAndGroups[1]);
SidLen = RequiredLength - sizeof(tg->GroupCount) -
((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES));
/* Allocate the output buffer */
tg = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
if (tg == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
Sid = (PSID)((ULONG_PTR)tg + sizeof(tg->GroupCount) +
((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES)));
tg->GroupCount = Token->UserAndGroupCount - 1;
Status = RtlCopySidAndAttributesArray(Token->UserAndGroupCount - 1,
&Token->UserAndGroups[1],
SidLen,
&tg->Groups[0],
Sid,
&Unused.PSid,
&Unused.Ulong);
/* Return the structure */
*TokenInformation = tg;
Status = STATUS_SUCCESS;
break;
}
case TokenPrivileges:
{
PTOKEN_PRIVILEGES tp;
DPRINT("SeQueryInformationToken(TokenPrivileges)\n");
RequiredLength = sizeof(tp->PrivilegeCount) +
(Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
/* Allocate the output buffer */
tp = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
if (tp == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
tp->PrivilegeCount = Token->PrivilegeCount;
RtlCopyLuidAndAttributesArray(Token->PrivilegeCount,
Token->Privileges,
&tp->Privileges[0]);
/* Return the structure */
*TokenInformation = tp;
Status = STATUS_SUCCESS;
break;
}
case TokenOwner:
{
PTOKEN_OWNER to;
ULONG SidLen;
DPRINT("SeQueryInformationToken(TokenOwner)\n");
SidLen = RtlLengthSid(Token->UserAndGroups[Token->DefaultOwnerIndex].Sid);
RequiredLength = sizeof(TOKEN_OWNER) + SidLen;
/* Allocate the output buffer */
to = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
if (to == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
to->Owner = (PSID)(to + 1);
Status = RtlCopySid(SidLen,
to->Owner,
Token->UserAndGroups[Token->DefaultOwnerIndex].Sid);
/* Return the structure */
*TokenInformation = to;
Status = STATUS_SUCCESS;
break;
}
case TokenPrimaryGroup:
{
PTOKEN_PRIMARY_GROUP tpg;
ULONG SidLen;
DPRINT("SeQueryInformationToken(TokenPrimaryGroup)\n");
SidLen = RtlLengthSid(Token->PrimaryGroup);
RequiredLength = sizeof(TOKEN_PRIMARY_GROUP) + SidLen;
/* Allocate the output buffer */
tpg = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
if (tpg == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
tpg->PrimaryGroup = (PSID)(tpg + 1);
Status = RtlCopySid(SidLen,
tpg->PrimaryGroup,
Token->PrimaryGroup);
/* Return the structure */
*TokenInformation = tpg;
Status = STATUS_SUCCESS;
break;
}
case TokenDefaultDacl:
{
PTOKEN_DEFAULT_DACL tdd;
DPRINT("SeQueryInformationToken(TokenDefaultDacl)\n");
RequiredLength = sizeof(TOKEN_DEFAULT_DACL);
if (Token->DefaultDacl != NULL)
RequiredLength += Token->DefaultDacl->AclSize;
/* Allocate the output buffer */
tdd = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
if (tdd == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
if (Token->DefaultDacl != NULL)
{
tdd->DefaultDacl = (PACL)(tdd + 1);
RtlCopyMemory(tdd->DefaultDacl,
Token->DefaultDacl,
Token->DefaultDacl->AclSize);
}
else
{
tdd->DefaultDacl = NULL;
}
/* Return the structure */
*TokenInformation = tdd;
Status = STATUS_SUCCESS;
break;
}
case TokenSource:
{
PTOKEN_SOURCE ts;
DPRINT("SeQueryInformationToken(TokenSource)\n");
RequiredLength = sizeof(TOKEN_SOURCE);
/* Allocate the output buffer */
ts = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
if (ts == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
*ts = Token->TokenSource;
/* Return the structure */
*TokenInformation = ts;
Status = STATUS_SUCCESS;
break;
}
case TokenType:
{
PTOKEN_TYPE tt;
DPRINT("SeQueryInformationToken(TokenType)\n");
RequiredLength = sizeof(TOKEN_TYPE);
/* Allocate the output buffer */
tt = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
if (tt == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
*tt = Token->TokenType;
/* Return the structure */
*TokenInformation = tt;
Status = STATUS_SUCCESS;
break;
}
case TokenImpersonationLevel: case TokenImpersonationLevel:
/* It is mandatory to have an impersonation token */ {
if (((PTOKEN)Token)->TokenType != TokenImpersonation) PSECURITY_IMPERSONATION_LEVEL sil;
DPRINT("SeQueryInformationToken(TokenImpersonationLevel)\n");
RequiredLength = sizeof(SECURITY_IMPERSONATION_LEVEL);
/* Fail if the token is not an impersonation token */
if (Token->TokenType != TokenImpersonation)
{ {
Status = STATUS_INVALID_INFO_CLASS; Status = STATUS_INVALID_INFO_CLASS;
break; break;
} }
/* Allocate the output buffer */ /* Allocate the output buffer */
SeImpersonationLvl = ExAllocatePoolWithTag(PagedPool, sizeof(SECURITY_IMPERSONATION_LEVEL), TAG_SE); sil = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
if (SeImpersonationLvl == NULL) if (sil == NULL)
{ {
Status = STATUS_INSUFFICIENT_RESOURCES; Status = STATUS_INSUFFICIENT_RESOURCES;
break; break;
} }
/* Set impersonation level and return the structure */ *sil = Token->ImpersonationLevel;
*SeImpersonationLvl = ((PTOKEN)Token)->ImpersonationLevel;
*TokenInformation = SeImpersonationLvl; /* Return the structure */
*TokenInformation = sil;
Status = STATUS_SUCCESS; Status = STATUS_SUCCESS;
break; break;
}
case TokenStatistics:
{
PTOKEN_STATISTICS ts;
DPRINT("SeQueryInformationToken(TokenStatistics)\n");
RequiredLength = sizeof(TOKEN_STATISTICS);
/* Allocate the output buffer */
ts = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
if (ts == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
ts->TokenId = Token->TokenId;
ts->AuthenticationId = Token->AuthenticationId;
ts->ExpirationTime = Token->ExpirationTime;
ts->TokenType = Token->TokenType;
ts->ImpersonationLevel = Token->ImpersonationLevel;
ts->DynamicCharged = Token->DynamicCharged;
ts->DynamicAvailable = Token->DynamicAvailable;
ts->GroupCount = Token->UserAndGroupCount - 1;
ts->PrivilegeCount = Token->PrivilegeCount;
ts->ModifiedId = Token->ModifiedId;
/* Return the structure */
*TokenInformation = ts;
Status = STATUS_SUCCESS;
break;
}
/*
* The following 4 cases are only implemented in NtQueryInformationToken
*/
#if 0
case TokenOrigin:
{
PTOKEN_ORIGIN to;
DPRINT("SeQueryInformationToken(TokenOrigin)\n");
RequiredLength = sizeof(TOKEN_ORIGIN);
/* Allocate the output buffer */
to = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
if (to == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
RtlCopyLuid(&to->OriginatingLogonSession,
&Token->AuthenticationId);
/* Return the structure */
*TokenInformation = to;
Status = STATUS_SUCCESS;
break;
}
case TokenGroupsAndPrivileges:
DPRINT1("SeQueryInformationToken(TokenGroupsAndPrivileges) not implemented\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
case TokenRestrictedSids:
{
PTOKEN_GROUPS tg = (PTOKEN_GROUPS)TokenInformation;
ULONG SidLen;
PSID Sid;
DPRINT("SeQueryInformationToken(TokenRestrictedSids)\n");
RequiredLength = sizeof(tg->GroupCount) +
RtlLengthSidAndAttributes(Token->RestrictedSidCount, Token->RestrictedSids);
SidLen = RequiredLength - sizeof(tg->GroupCount) -
(Token->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES));
/* Allocate the output buffer */
tg = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
if (tg == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
Sid = (PSID)((ULONG_PTR)tg + sizeof(tg->GroupCount) +
(Token->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES)));
tg->GroupCount = Token->RestrictedSidCount;
Status = RtlCopySidAndAttributesArray(Token->RestrictedSidCount,
Token->RestrictedSids,
SidLen,
&tg->Groups[0],
Sid,
&Unused.PSid,
&Unused.Ulong);
/* Return the structure */
*TokenInformation = tg;
Status = STATUS_SUCCESS;
break;
}
case TokenSandBoxInert:
DPRINT1("SeQueryInformationToken(TokenSandboxInert) not implemented\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
#endif
case TokenSessionId:
{
DPRINT("SeQueryInformationToken(TokenSessionId)\n");
Status = SeQuerySessionIdToken(Token, (PULONG)TokenInformation);
// Status = STATUS_SUCCESS;
break;
}
default: default:
UNIMPLEMENTED; DPRINT1("SeQueryInformationToken(%d) invalid information class\n", TokenInformationClass);
Status = STATUS_NOT_IMPLEMENTED; Status = STATUS_INVALID_INFO_CLASS;
break; break;
} }
@ -1212,15 +1586,15 @@ NtQueryInformationToken(IN HANDLE TokenHandle,
IN ULONG TokenInformationLength, IN ULONG TokenInformationLength,
OUT PULONG ReturnLength) OUT PULONG ReturnLength)
{ {
union NTSTATUS Status;
{ KPROCESSOR_MODE PreviousMode;
PVOID Ptr;
ULONG Ulong;
} Unused;
PTOKEN Token; PTOKEN Token;
ULONG RequiredLength; ULONG RequiredLength;
KPROCESSOR_MODE PreviousMode; union
NTSTATUS Status; {
PSID PSid;
ULONG Ulong;
} Unused;
PAGED_CODE(); PAGED_CODE();
@ -1229,7 +1603,7 @@ NtQueryInformationToken(IN HANDLE TokenHandle,
/* Check buffers and class validity */ /* Check buffers and class validity */
Status = DefaultQueryInfoBufferCheck(TokenInformationClass, Status = DefaultQueryInfoBufferCheck(TokenInformationClass,
SeTokenInformationClass, SeTokenInformationClass,
sizeof(SeTokenInformationClass) / sizeof(SeTokenInformationClass[0]), RTL_NUMBER_OF(SeTokenInformationClass),
TokenInformation, TokenInformation,
TokenInformationLength, TokenInformationLength,
ReturnLength, ReturnLength,
@ -1268,7 +1642,7 @@ NtQueryInformationToken(IN HANDLE TokenHandle,
RequiredLength - sizeof(TOKEN_USER), RequiredLength - sizeof(TOKEN_USER),
&tu->User, &tu->User,
(PSID)(tu + 1), (PSID)(tu + 1),
&Unused.Ptr, &Unused.PSid,
&Unused.Ulong); &Unused.Ulong);
} }
else else
@ -1304,7 +1678,7 @@ NtQueryInformationToken(IN HANDLE TokenHandle,
{ {
ULONG SidLen = RequiredLength - sizeof(tg->GroupCount) - ULONG SidLen = RequiredLength - sizeof(tg->GroupCount) -
((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES)); ((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES));
PSID_AND_ATTRIBUTES Sid = (PSID_AND_ATTRIBUTES)((ULONG_PTR)TokenInformation + sizeof(tg->GroupCount) + PSID Sid = (PSID_AND_ATTRIBUTES)((ULONG_PTR)tg + sizeof(tg->GroupCount) +
((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES))); ((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES)));
tg->GroupCount = Token->UserAndGroupCount - 1; tg->GroupCount = Token->UserAndGroupCount - 1;
@ -1312,8 +1686,8 @@ NtQueryInformationToken(IN HANDLE TokenHandle,
&Token->UserAndGroups[1], &Token->UserAndGroups[1],
SidLen, SidLen,
&tg->Groups[0], &tg->Groups[0],
(PSID)Sid, Sid,
&Unused.Ptr, &Unused.PSid,
&Unused.Ulong); &Unused.Ulong);
} }
else else
@ -1373,8 +1747,8 @@ NtQueryInformationToken(IN HANDLE TokenHandle,
case TokenOwner: case TokenOwner:
{ {
ULONG SidLen;
PTOKEN_OWNER to = (PTOKEN_OWNER)TokenInformation; PTOKEN_OWNER to = (PTOKEN_OWNER)TokenInformation;
ULONG SidLen;
DPRINT("NtQueryInformationToken(TokenOwner)\n"); DPRINT("NtQueryInformationToken(TokenOwner)\n");
SidLen = RtlLengthSid(Token->UserAndGroups[Token->DefaultOwnerIndex].Sid); SidLen = RtlLengthSid(Token->UserAndGroups[Token->DefaultOwnerIndex].Sid);
@ -1410,8 +1784,8 @@ NtQueryInformationToken(IN HANDLE TokenHandle,
case TokenPrimaryGroup: case TokenPrimaryGroup:
{ {
ULONG SidLen;
PTOKEN_PRIMARY_GROUP tpg = (PTOKEN_PRIMARY_GROUP)TokenInformation; PTOKEN_PRIMARY_GROUP tpg = (PTOKEN_PRIMARY_GROUP)TokenInformation;
ULONG SidLen;
DPRINT("NtQueryInformationToken(TokenPrimaryGroup)\n"); DPRINT("NtQueryInformationToken(TokenPrimaryGroup)\n");
SidLen = RtlLengthSid(Token->PrimaryGroup); SidLen = RtlLengthSid(Token->PrimaryGroup);
@ -1453,9 +1827,7 @@ NtQueryInformationToken(IN HANDLE TokenHandle,
RequiredLength = sizeof(TOKEN_DEFAULT_DACL); RequiredLength = sizeof(TOKEN_DEFAULT_DACL);
if (Token->DefaultDacl != NULL) if (Token->DefaultDacl != NULL)
{
RequiredLength += Token->DefaultDacl->AclSize; RequiredLength += Token->DefaultDacl->AclSize;
}
_SEH2_TRY _SEH2_TRY
{ {
@ -1689,7 +2061,7 @@ NtQueryInformationToken(IN HANDLE TokenHandle,
{ {
ULONG SidLen = RequiredLength - sizeof(tg->GroupCount) - ULONG SidLen = RequiredLength - sizeof(tg->GroupCount) -
(Token->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES)); (Token->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES));
PSID_AND_ATTRIBUTES Sid = (PSID_AND_ATTRIBUTES)((ULONG_PTR)TokenInformation + sizeof(tg->GroupCount) + PSID Sid = (PSID)((ULONG_PTR)tg + sizeof(tg->GroupCount) +
(Token->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES))); (Token->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES)));
tg->GroupCount = Token->RestrictedSidCount; tg->GroupCount = Token->RestrictedSidCount;
@ -1697,8 +2069,8 @@ NtQueryInformationToken(IN HANDLE TokenHandle,
Token->RestrictedSids, Token->RestrictedSids,
SidLen, SidLen,
&tg->Groups[0], &tg->Groups[0],
(PSID)Sid, Sid,
&Unused.Ptr, &Unused.PSid,
&Unused.Ulong); &Unused.Ulong);
} }
else else
@ -1731,14 +2103,12 @@ NtQueryInformationToken(IN HANDLE TokenHandle,
DPRINT("NtQueryInformationToken(TokenSessionId)\n"); DPRINT("NtQueryInformationToken(TokenSessionId)\n");
Status = SeQuerySessionIdToken(Token, Status = SeQuerySessionIdToken(Token, &SessionId);
&SessionId);
if (NT_SUCCESS(Status)) if (NT_SUCCESS(Status))
{ {
_SEH2_TRY _SEH2_TRY
{ {
/* buffer size was already verified, no need to check here again */ /* Buffer size was already verified, no need to check here again */
*(PULONG)TokenInformation = SessionId; *(PULONG)TokenInformation = SessionId;
if (ReturnLength != NULL) if (ReturnLength != NULL)
@ -1792,7 +2162,7 @@ NtSetInformationToken(IN HANDLE TokenHandle,
Status = DefaultSetInfoBufferCheck(TokenInformationClass, Status = DefaultSetInfoBufferCheck(TokenInformationClass,
SeTokenInformationClass, SeTokenInformationClass,
sizeof(SeTokenInformationClass) / sizeof(SeTokenInformationClass[0]), RTL_NUMBER_OF(SeTokenInformationClass),
TokenInformation, TokenInformation,
TokenInformationLength, TokenInformationLength,
PreviousMode); PreviousMode);
@ -2179,9 +2549,11 @@ Cleanup:
* @implemented * @implemented
* *
* NOTE: Some sources claim 4th param is ImpersonationLevel, but on W2K * NOTE: Some sources claim 4th param is ImpersonationLevel, but on W2K
* this is certainly NOT true, thou i can't say for sure that EffectiveOnly * this is certainly NOT true, although I can't say for sure that EffectiveOnly
* is correct either. -Gunnar * is correct either. -Gunnar
* This is true. EffectiveOnly overrides SQOS.EffectiveOnly. - IAI * This is true. EffectiveOnly overrides SQOS.EffectiveOnly. - IAI
* NOTE for readers: http://hex.pp.ua/nt/NtDuplicateToken.php is therefore
* wrong in that regard.
*/ */
NTSTATUS NTAPI NTSTATUS NTAPI
NtDuplicateToken(IN HANDLE ExistingTokenHandle, NtDuplicateToken(IN HANDLE ExistingTokenHandle,
@ -2330,7 +2702,7 @@ NtAdjustGroupsToken(IN HANDLE TokenHandle,
OUT PULONG ReturnLength) OUT PULONG ReturnLength)
{ {
UNIMPLEMENTED; UNIMPLEMENTED;
return(STATUS_NOT_IMPLEMENTED); return STATUS_NOT_IMPLEMENTED;
} }

View file

@ -1279,13 +1279,13 @@ NTSYSAPI
NTSTATUS NTSTATUS
NTAPI NTAPI
RtlCopySidAndAttributesArray( RtlCopySidAndAttributesArray(
ULONG Count, _In_ ULONG Count,
PSID_AND_ATTRIBUTES Src, _In_ PSID_AND_ATTRIBUTES Src,
ULONG SidAreaSize, _In_ ULONG SidAreaSize,
PSID_AND_ATTRIBUTES Dest, _In_ PSID_AND_ATTRIBUTES Dest,
PVOID SidArea, _In_ PSID SidArea,
PVOID* RemainingSidArea, _Out_ PSID* RemainingSidArea,
PULONG RemainingSidAreaSize _Out_ PULONG RemainingSidAreaSize
); );
_IRQL_requires_max_(APC_LEVEL) _IRQL_requires_max_(APC_LEVEL)

View file

@ -250,8 +250,8 @@ RtlCopySidAndAttributesArray(IN ULONG Count,
IN PSID_AND_ATTRIBUTES Src, IN PSID_AND_ATTRIBUTES Src,
IN ULONG SidAreaSize, IN ULONG SidAreaSize,
IN PSID_AND_ATTRIBUTES Dest, IN PSID_AND_ATTRIBUTES Dest,
IN PVOID SidArea, IN PSID SidArea,
OUT PVOID* RemainingSidArea, OUT PSID* RemainingSidArea,
OUT PULONG RemainingSidAreaSize) OUT PULONG RemainingSidAreaSize)
{ {
ULONG SidLength, i; ULONG SidLength, i;
@ -273,7 +273,7 @@ RtlCopySidAndAttributesArray(IN ULONG Count,
RtlCopySid(SidLength, SidArea, Src[i].Sid); RtlCopySid(SidLength, SidArea, Src[i].Sid);
/* Push the buffer area where the SID will reset */ /* Push the buffer area where the SID will reset */
SidArea = (PVOID)((ULONG_PTR)SidArea + SidLength); SidArea = (PSID)((ULONG_PTR)SidArea + SidLength);
} }
/* Return how much space is left, and where the buffer is at now */ /* Return how much space is left, and where the buffer is at now */

View file

@ -132,8 +132,6 @@ const KMT_TEST TestList[] =
{ "-ObTypeNoClean", Test_ObTypeNoClean }, { "-ObTypeNoClean", Test_ObTypeNoClean },
{ "ObTypes", Test_ObTypes }, { "ObTypes", Test_ObTypes },
{ "PsNotify", Test_PsNotify }, { "PsNotify", Test_PsNotify },
{ "SeInheritance", Test_SeInheritance },
{ "-SeQueryInfoToken", Test_SeQueryInfoToken },
{ "RtlAvlTreeKM", Test_RtlAvlTree }, { "RtlAvlTreeKM", Test_RtlAvlTree },
{ "RtlExceptionKM", Test_RtlException }, { "RtlExceptionKM", Test_RtlException },
{ "RtlIntSafeKM", Test_RtlIntSafe }, { "RtlIntSafeKM", Test_RtlIntSafe },
@ -142,6 +140,8 @@ const KMT_TEST TestList[] =
{ "RtlRegistryKM", Test_RtlRegistry }, { "RtlRegistryKM", Test_RtlRegistry },
{ "RtlSplayTreeKM", Test_RtlSplayTree }, { "RtlSplayTreeKM", Test_RtlSplayTree },
{ "RtlUnicodeStringKM", Test_RtlUnicodeString }, { "RtlUnicodeStringKM", Test_RtlUnicodeString },
{ "SeInheritance", Test_SeInheritance },
{ "SeQueryInfoToken", Test_SeQueryInfoToken },
{ "ZwAllocateVirtualMemory", Test_ZwAllocateVirtualMemory }, { "ZwAllocateVirtualMemory", Test_ZwAllocateVirtualMemory },
{ "ZwCreateSection", Test_ZwCreateSection }, { "ZwCreateSection", Test_ZwCreateSection },
{ "ZwMapViewOfSection", Test_ZwMapViewOfSection }, { "ZwMapViewOfSection", Test_ZwMapViewOfSection },

View file

@ -44,10 +44,10 @@ void TestsSeQueryInformationToken(PACCESS_TOKEN Token)
if (Token == NULL) return; if (Token == NULL) return;
Status = SeQueryInformationToken(Token, TokenOwner, &Buffer); Status = SeQueryInformationToken(Token, TokenOwner, &Buffer);
ok((Status == STATUS_SUCCESS), "SQIT with TokenOwner arg fails with status 0x%X\n", Status); ok((Status == STATUS_SUCCESS), "SQIT with TokenOwner arg fails with status 0x%08X\n", Status);
if (Status == STATUS_SUCCESS) if (Status == STATUS_SUCCESS)
{ {
ok(Buffer != NULL, "Wrong. SQIT call was successful with TokenOwner arg. But Buffer = NULL\n"); ok(Buffer != NULL, "Wrong. SQIT call was successful with TokenOwner arg. But Buffer == NULL\n");
if (Buffer) if (Buffer)
{ {
@ -62,10 +62,10 @@ void TestsSeQueryInformationToken(PACCESS_TOKEN Token)
Buffer = NULL; Buffer = NULL;
Status = SeQueryInformationToken(Token, TokenDefaultDacl, &Buffer); Status = SeQueryInformationToken(Token, TokenDefaultDacl, &Buffer);
ok(Status == STATUS_SUCCESS, "SQIT with TokenDefaultDacl fails with status 0x%X\n", Status); ok(Status == STATUS_SUCCESS, "SQIT with TokenDefaultDacl fails with status 0x%08X\n", Status);
if (Status == STATUS_SUCCESS) if (Status == STATUS_SUCCESS)
{ {
ok(Buffer != NULL, "Wrong. SQIT call was successful with TokenDefaultDacl arg. But Buffer = NULL\n"); ok(Buffer != NULL, "Wrong. SQIT call was successful with TokenDefaultDacl arg. But Buffer == NULL\n");
if (Buffer) if (Buffer)
{ {
TDefDacl = (PTOKEN_DEFAULT_DACL)Buffer; TDefDacl = (PTOKEN_DEFAULT_DACL)Buffer;
@ -79,10 +79,10 @@ void TestsSeQueryInformationToken(PACCESS_TOKEN Token)
Buffer = NULL; Buffer = NULL;
Status = SeQueryInformationToken(Token, TokenGroups, &Buffer); Status = SeQueryInformationToken(Token, TokenGroups, &Buffer);
ok(Status == STATUS_SUCCESS, "SQIT with TokenGroups fails with status 0x%X\n", Status); ok(Status == STATUS_SUCCESS, "SQIT with TokenGroups fails with status 0x%08X\n", Status);
if (Status == STATUS_SUCCESS) if (Status == STATUS_SUCCESS)
{ {
ok(Buffer != NULL, "Wrong. SQIT call was successful with TokenGroups arg. But Buffer = NULL\n"); ok(Buffer != NULL, "Wrong. SQIT call was successful with TokenGroups arg. But Buffer == NULL\n");
if (Buffer) if (Buffer)
{ {
TGroups = (PTOKEN_GROUPS)Buffer; TGroups = (PTOKEN_GROUPS)Buffer;
@ -107,58 +107,88 @@ void TestsSeQueryInformationToken(PACCESS_TOKEN Token)
// Call SQIT with TokenImpersonationLevel argument // Call SQIT with TokenImpersonationLevel argument
// //
// What's up? Why SQIT fails with right arg? // What's up? Why SQIT fails with right arg?
// Because your token has Token->TokenType != TokenImpersonation. -hbelusca
Buffer = NULL; Buffer = NULL;
Status = SeQueryInformationToken(Token, TokenImpersonationLevel, &Buffer); Status = SeQueryInformationToken(Token, TokenImpersonationLevel, &Buffer);
ok(Status == STATUS_SUCCESS, "SQIT with TokenImpersonationLevel fails with status 0x%X\n", Status); ok(Status == STATUS_SUCCESS, "SQIT with TokenImpersonationLevel fails with status 0x%08X\n", Status);
if (Buffer) ExFreePool(Buffer);
Buffer = NULL; Buffer = NULL;
Status = SeQueryInformationToken(Token, TokenImpersonationLevel, &Buffer); Status = SeQueryInformationToken(Token, TokenImpersonationLevel, &Buffer);
ok(Status == STATUS_SUCCESS, "and again: SQIT with TokenImpersonationLevel fails with status 0x%X\n", Status); ok(Status == STATUS_SUCCESS, "and again: SQIT with TokenImpersonationLevel fails with status 0x%08X\n", Status);
if (Status == STATUS_SUCCESS) if (Status == STATUS_SUCCESS)
{ {
ok(Buffer != NULL, "Wrong. SQIT call was successful with TokenImpersonationLevel arg. But Buffer = NULL\n"); ok(Buffer != NULL, "Wrong. SQIT call was successful with TokenImpersonationLevel arg. But Buffer == NULL\n");
} else { } else {
ok(Buffer == NULL, "Wrong. SQIT call is't success. But Buffer != NULL\n"); ok(Buffer == NULL, "Wrong. SQIT call failed. But Buffer != NULL\n");
} }
if (Buffer) ExFreePool(Buffer);
//----------------------------------------------------------------//
// Call SQIT with the 4 classes (TokenOrigin, TokenGroupsAndPrivileges,
// TokenRestrictedSids and TokenSandBoxInert) are not supported by
// SeQueryInformationToken (only NtQueryInformationToken supports them).
//
Buffer = NULL;
Status = SeQueryInformationToken(Token, TokenOrigin, &Buffer);
ok(Status == STATUS_INVALID_INFO_CLASS, "SQIT with TokenOrigin failed with Status 0x%08X; expected STATUS_INVALID_INFO_CLASS\n", Status);
ok(Buffer == NULL, "Wrong. SQIT call failed. But Buffer != NULL\n");
Buffer = NULL;
Status = SeQueryInformationToken(Token, TokenGroupsAndPrivileges, &Buffer);
ok(Status == STATUS_INVALID_INFO_CLASS, "SQIT with TokenGroupsAndPrivileges failed with Status 0x%08X; expected STATUS_INVALID_INFO_CLASS\n", Status);
ok(Buffer == NULL, "Wrong. SQIT call failed. But Buffer != NULL\n");
Buffer = NULL;
Status = SeQueryInformationToken(Token, TokenRestrictedSids, &Buffer);
ok(Status == STATUS_INVALID_INFO_CLASS, "SQIT with TokenRestrictedSids failed with Status 0x%08X; expected STATUS_INVALID_INFO_CLASS\n", Status);
ok(Buffer == NULL, "Wrong. SQIT call failed. But Buffer != NULL\n");
Buffer = NULL;
Status = SeQueryInformationToken(Token, TokenSandBoxInert, &Buffer);
ok(Status == STATUS_INVALID_INFO_CLASS, "SQIT with TokenSandBoxInert failed with Status 0x%08X; expected STATUS_INVALID_INFO_CLASS\n", Status);
ok(Buffer == NULL, "Wrong. SQIT call failed. But Buffer != NULL\n");
//----------------------------------------------------------------// //----------------------------------------------------------------//
Buffer = NULL; Buffer = NULL;
Status = SeQueryInformationToken(Token, TokenStatistics, &Buffer); Status = SeQueryInformationToken(Token, TokenStatistics, &Buffer);
ok(Status == STATUS_SUCCESS, "SQIT with TokenStatistics fails with status 0x%X\n", Status); ok(Status == STATUS_SUCCESS, "SQIT with TokenStatistics fails with status 0x%08X\n", Status);
if (Status == STATUS_SUCCESS) if (Status == STATUS_SUCCESS)
{ {
ok(Buffer != NULL, "Wrong. SQIT call was successful with TokenStatistics arg. But Buffer = NULL\n"); ok(Buffer != NULL, "Wrong. SQIT call was successful with TokenStatistics arg. But Buffer == NULL\n");
if (Buffer) if (Buffer)
{ {
TStats = (PTOKEN_STATISTICS)Buffer; TStats = (PTOKEN_STATISTICS)Buffer;
// just put 0 into 1st arg or use trace to print TokenStatistics f // just put 0 into 1st arg or use trace to print TokenStatistics
ok(1, "print statistics:\n\tTokenID = %u_%d\n\tSecurityImperLevel = %d\n\tPrivCount = %d\n\tGroupCount = %d\n\n", TStats->TokenId.LowPart, ok(1, "print statistics:\n\tTokenID = %u_%d\n\tSecurityImperLevel = %d\n\tPrivCount = %d\n\tGroupCount = %d\n\n", TStats->TokenId.LowPart,
TStats->TokenId.HighPart, TStats->TokenId.HighPart,
TStats->ImpersonationLevel, TStats->ImpersonationLevel,
TStats->PrivilegeCount, TStats->PrivilegeCount,
TStats->GroupCount TStats->GroupCount
); );
ExFreePool(TStats); ExFreePool(Buffer);
} }
} else { } else {
ok(Buffer == NULL, "Wrong. SQIT call is't success. But Buffer != NULL\n"); ok(Buffer == NULL, "Wrong. SQIT call failed. But Buffer != NULL\n");
} }
//----------------------------------------------------------------// //----------------------------------------------------------------//
Buffer = NULL; Buffer = NULL;
Status = SeQueryInformationToken(Token, TokenType, &Buffer); Status = SeQueryInformationToken(Token, TokenType, &Buffer);
ok(Status == STATUS_SUCCESS, "SQIT with TokenType fails with status 0x%X\n", Status); ok(Status == STATUS_SUCCESS, "SQIT with TokenType fails with status 0x%08X\n", Status);
if (Status == STATUS_SUCCESS) if (Status == STATUS_SUCCESS)
{ {
ok(Buffer != NULL, "Wrong. SQIT call was successful with TokenType arg. But Buffer = NULL\n"); ok(Buffer != NULL, "Wrong. SQIT call was successful with TokenType arg. But Buffer == NULL\n");
if (Buffer) if (Buffer)
{ {
TType = (PTOKEN_TYPE)Buffer; TType = (PTOKEN_TYPE)Buffer;
ok((*TType == TokenPrimary || *TType == TokenImpersonation), "TokenType in not a primary nor impersonation. FAILED\n"); ok((*TType == TokenPrimary || *TType == TokenImpersonation), "TokenType in not a primary nor impersonation. FAILED\n");
ExFreePool(TType); ExFreePool(Buffer);
} }
} }
@ -169,12 +199,12 @@ void TestsSeQueryInformationToken(PACCESS_TOKEN Token)
ok(Status == STATUS_SUCCESS, "SQIT with TokenUser fails\n"); ok(Status == STATUS_SUCCESS, "SQIT with TokenUser fails\n");
if (Status == STATUS_SUCCESS) if (Status == STATUS_SUCCESS)
{ {
ok(Buffer != NULL, "Wrong. SQIT call was successful with TokenUser arg. But Buffer = NULL\n"); ok(Buffer != NULL, "Wrong. SQIT call was successful with TokenUser arg. But Buffer == NULL\n");
if (Buffer) if (Buffer)
{ {
TUser = (PTOKEN_USER)Buffer; TUser = (PTOKEN_USER)Buffer;
ok(RtlValidSid(TUser->User.Sid), "TokenUser has an invalid Sid\n"); ok(RtlValidSid(TUser->User.Sid), "TokenUser has an invalid Sid\n");
ExFreePool(TUser); ExFreePool(Buffer);
} }
} }
@ -237,7 +267,6 @@ START_TEST(SeQueryInfoToken)
SeCaptureSubjectContext(&AccessState->SubjectSecurityContext); SeCaptureSubjectContext(&AccessState->SubjectSecurityContext);
SeLockSubjectContext(&AccessState->SubjectSecurityContext); SeLockSubjectContext(&AccessState->SubjectSecurityContext);
Token = SeQuerySubjectContextToken(&AccessState->SubjectSecurityContext); Token = SeQuerySubjectContextToken(&AccessState->SubjectSecurityContext);
// Testing SQIT with AccessState Token // Testing SQIT with AccessState Token
@ -295,8 +324,7 @@ START_TEST(SeQueryInfoToken)
// Testing SeFreePrivileges // // Testing SeFreePrivileges //
//----------------------------------------------------------------// //----------------------------------------------------------------//
Privileges = ExAllocatePool(PagedPool, AuxData->PrivilegeSet->PrivilegeCount*sizeof(PRIVILEGE_SET)); Privileges = NULL;
Checker = SeAccessCheck( Checker = SeAccessCheck(
AccessState->SecurityDescriptor, AccessState->SecurityDescriptor,
&AccessState->SubjectSecurityContext, &AccessState->SubjectSecurityContext,
@ -311,6 +339,11 @@ START_TEST(SeQueryInfoToken)
); );
ok(Checker, "Checker is NULL\n"); ok(Checker, "Checker is NULL\n");
ok((Privileges != NULL), "Privileges is NULL\n"); ok((Privileges != NULL), "Privileges is NULL\n");
if (Privileges)
{
trace("AuxData->PrivilegeSet->PrivilegeCount = %d ; Privileges->PrivilegeCount = %d\n",
AuxData->PrivilegeSet->PrivilegeCount, Privileges->PrivilegeCount);
}
if (Privileges) SeFreePrivileges(Privileges); if (Privileges) SeFreePrivileges(Privileges);
@ -326,7 +359,7 @@ START_TEST(SeQueryInfoToken)
Status = SeQueryInformationToken(Token, TokenPrivileges, &Buffer); Status = SeQueryInformationToken(Token, TokenPrivileges, &Buffer);
if (Status == STATUS_SUCCESS) if (Status == STATUS_SUCCESS)
{ {
ok(Buffer != NULL, "Wrong. SQIT call was successful with TokenPrivileges arg. But Buffer = NULL\n"); ok(Buffer != NULL, "Wrong. SQIT call was successful with TokenPrivileges arg. But Buffer == NULL\n");
if (Buffer) if (Buffer)
{ {
TPrivileges = (PTOKEN_PRIVILEGES)(Buffer); TPrivileges = (PTOKEN_PRIVILEGES)(Buffer);
@ -351,8 +384,7 @@ START_TEST(SeQueryInfoToken)
// Call SeFreePrivileges again // Call SeFreePrivileges again
Privileges = ExAllocatePool(PagedPool, 20*sizeof(PRIVILEGE_SET)); Privileges = NULL;
Checker = SeAccessCheck( Checker = SeAccessCheck(
AccessState->SecurityDescriptor, AccessState->SecurityDescriptor,
&AccessState->SubjectSecurityContext, &AccessState->SubjectSecurityContext,
@ -367,6 +399,11 @@ START_TEST(SeQueryInfoToken)
); );
ok(Checker, "Checker is NULL\n"); ok(Checker, "Checker is NULL\n");
ok((Privileges != NULL), "Privileges is NULL\n"); ok((Privileges != NULL), "Privileges is NULL\n");
if (Privileges)
{
trace("AuxData->PrivilegeSet->PrivilegeCount = %d ; Privileges->PrivilegeCount = %d\n",
AuxData->PrivilegeSet->PrivilegeCount, Privileges->PrivilegeCount);
}
if (Privileges) SeFreePrivileges(Privileges); if (Privileges) SeFreePrivileges(Privileges);
//----------------------------------------------------------------// //----------------------------------------------------------------//