Author: hbelusca
Date: Fri Nov  4 17:52:32 2016
New Revision: 73122

URL: http://svn.reactos.org/svn/reactos?rev=73122&view=rev
Log:
[NTOS]
- 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.

Modified:
    trunk/reactos/ntoskrnl/se/accesschk.c
    trunk/reactos/ntoskrnl/se/sid.c
    trunk/reactos/ntoskrnl/se/token.c
    trunk/reactos/sdk/include/ndk/rtlfuncs.h
    trunk/reactos/sdk/lib/rtl/sid.c
    trunk/rostests/kmtests/kmtest_drv/testlist.c
    trunk/rostests/kmtests/ntos_se/SeQueryInfoToken.c

Modified: trunk/reactos/ntoskrnl/se/accesschk.c
URL: 
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/se/accesschk.c?rev=73122&r1=73121&r2=73122&view=diff
==============================================================================
--- trunk/reactos/ntoskrnl/se/accesschk.c       [iso-8859-1] (original)
+++ trunk/reactos/ntoskrnl/se/accesschk.c       [iso-8859-1] Fri Nov  4 
17:52:32 2016
@@ -18,8 +18,11 @@
 
 /* PRIVATE FUNCTIONS 
**********************************************************/
 
+/*
+ * FIXME: Incomplete!
+ */
 BOOLEAN NTAPI
-SepAccessCheckEx(IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+SepAccessCheck(IN PSECURITY_DESCRIPTOR SecurityDescriptor,
                IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
                IN ACCESS_MASK DesiredAccess,
                IN POBJECT_TYPE_LIST ObjectTypeList,
@@ -46,7 +49,7 @@
     NTSTATUS Status;
     PAGED_CODE();
 
-    DPRINT("SepAccessCheckEx()\n");
+    DPRINT("SepAccessCheck()\n");
 
     /* Check for no access desired */
     if (!DesiredAccess)
@@ -281,31 +284,6 @@
     }
 
     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
@@ -443,12 +421,15 @@
         ret = SepAccessCheck(SecurityDescriptor,
                              SubjectSecurityContext,
                              DesiredAccess,
+                             NULL,
+                             0,
                              PreviouslyGrantedAccess,
                              Privileges,
                              GenericMapping,
                              AccessMode,
                              GrantedAccess,
-                             AccessStatus);
+                             AccessStatus,
+                             FALSE);
     }
 
     /* Release the lock if needed */
@@ -687,12 +668,15 @@
         SepAccessCheck(SecurityDescriptor, // FIXME: use 
CapturedSecurityDescriptor
                        &SubjectSecurityContext,
                        DesiredAccess,
+                       NULL,
+                       0,
                        PreviouslyGrantedAccess,
                        &PrivilegeSet, //FIXME
                        GenericMapping,
                        PreviousMode,
                        GrantedAccess,
-                       AccessStatus);
+                       AccessStatus,
+                       FALSE);
     }
 
     /* Release subject context and unlock the token */

Modified: trunk/reactos/ntoskrnl/se/sid.c
URL: 
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/se/sid.c?rev=73122&r1=73121&r2=73122&view=diff
==============================================================================
--- trunk/reactos/ntoskrnl/se/sid.c     [iso-8859-1] (original)
+++ trunk/reactos/ntoskrnl/se/sid.c     [iso-8859-1] Fri Nov  4 17:52:32 2016
@@ -297,7 +297,7 @@
         }
         _SEH2_END;
 
-        /* allocate a SID and copy it */
+        /* Allocate a SID and copy it */
         NewSid = ExAllocatePoolWithTag(PoolType, SidSize, TAG_SID);
         if (!NewSid)
             return STATUS_INSUFFICIENT_RESOURCES;
@@ -324,7 +324,7 @@
     {
         SidSize = RtlLengthRequiredSid(Sid->SubAuthorityCount);
 
-        /* allocate a SID and copy it */
+        /* Allocate a SID and copy it */
         NewSid = ExAllocatePoolWithTag(PoolType, SidSize, TAG_SID);
         if (NewSid == NULL)
             return STATUS_INSUFFICIENT_RESOURCES;

Modified: trunk/reactos/ntoskrnl/se/token.c
URL: 
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/se/token.c?rev=73122&r1=73121&r2=73122&view=diff
==============================================================================
--- trunk/reactos/ntoskrnl/se/token.c   [iso-8859-1] (original)
+++ trunk/reactos/ntoskrnl/se/token.c   [iso-8859-1] Fri Nov  4 17:52:32 2016
@@ -232,7 +232,7 @@
 
     PAGED_CODE();
 
-    if (NewToken->TokenType != TokenPrimary) return(STATUS_BAD_TOKEN_TYPE);
+    if (NewToken->TokenType != TokenPrimary) return STATUS_BAD_TOKEN_TYPE;
     if (NewToken->TokenInUse)
     {
         BOOLEAN IsEqual;
@@ -348,12 +348,12 @@
 
     if (Token->DefaultOwnerIndex == Token->UserAndGroupCount)
     {
-        return(STATUS_INVALID_OWNER);
+        return STATUS_INVALID_OWNER;
     }
 
     if (Token->PrimaryGroup == 0)
     {
-        return(STATUS_INVALID_PRIMARY_GROUP);
+        return STATUS_INVALID_PRIMARY_GROUP;
     }
 
     return STATUS_SUCCESS;
@@ -1060,16 +1060,26 @@
 }
 
 /*
- * @unimplemented
+ * @implemented
+ *
+ * NOTE: SeQueryInformationToken is just NtQueryInformationToken without all
+ * the bells and whistles needed for user-mode buffer access protection.
  */
 NTSTATUS
 NTAPI
-SeQueryInformationToken(IN PACCESS_TOKEN Token,
+SeQueryInformationToken(IN PACCESS_TOKEN AccessToken,
                         IN TOKEN_INFORMATION_CLASS TokenInformationClass,
                         OUT PVOID *TokenInformation)
 {
     NTSTATUS Status;
-    PSECURITY_IMPERSONATION_LEVEL SeImpersonationLvl;
+    PTOKEN Token = (PTOKEN)AccessToken;
+    ULONG RequiredLength;
+    union
+    {
+        PSID PSid;
+        ULONG Ulong;
+    } Unused;
+
     PAGED_CODE();
 
     if (TokenInformationClass >= MaxTokenInfoClass)
@@ -1080,31 +1090,395 @@
 
     switch (TokenInformationClass)
     {
-        case TokenImpersonationLevel:
-            /* It is mandatory to have an impersonation token */
-            if (((PTOKEN)Token)->TokenType != TokenImpersonation)
-            {
-                Status = STATUS_INVALID_INFO_CLASS;
-                break;
-            }
+        case TokenUser:
+        {
+            PTOKEN_USER tu;
+
+            DPRINT("SeQueryInformationToken(TokenUser)\n");
+            RequiredLength = sizeof(TOKEN_USER) +
+                RtlLengthSid(Token->UserAndGroups[0].Sid);
 
             /* Allocate the output buffer */
-            SeImpersonationLvl = ExAllocatePoolWithTag(PagedPool, 
sizeof(SECURITY_IMPERSONATION_LEVEL), TAG_SE);
-            if (SeImpersonationLvl == NULL)
+            tu = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
+            if (tu == NULL)
             {
                 Status = STATUS_INSUFFICIENT_RESOURCES;
                 break;
             }
 
-            /* Set impersonation level and return the structure */
-            *SeImpersonationLvl = ((PTOKEN)Token)->ImpersonationLevel;
-            *TokenInformation = SeImpersonationLvl;
+            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:
+        {
+            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;
+                break;
+            }
+
+            /* Allocate the output buffer */
+            sil = ExAllocatePoolWithTag(PagedPool, RequiredLength, TAG_SE);
+            if (sil == NULL)
+            {
+                Status = STATUS_INSUFFICIENT_RESOURCES;
+                break;
+            }
+
+            *sil = Token->ImpersonationLevel;
+
+            /* Return the structure */
+            *TokenInformation = sil;
+            Status = STATUS_SUCCESS;
+            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:
-            UNIMPLEMENTED;
-            Status = STATUS_NOT_IMPLEMENTED;
+            DPRINT1("SeQueryInformationToken(%d) invalid information class\n", 
TokenInformationClass);
+            Status = STATUS_INVALID_INFO_CLASS;
             break;
     }
 
@@ -1212,15 +1586,15 @@
                         IN ULONG TokenInformationLength,
                         OUT PULONG ReturnLength)
 {
+    NTSTATUS Status;
+    KPROCESSOR_MODE PreviousMode;
+    PTOKEN Token;
+    ULONG RequiredLength;
     union
     {
-        PVOID Ptr;
+        PSID PSid;
         ULONG Ulong;
     } Unused;
-    PTOKEN Token;
-    ULONG RequiredLength;
-    KPROCESSOR_MODE PreviousMode;
-    NTSTATUS Status;
 
     PAGED_CODE();
 
@@ -1229,7 +1603,7 @@
     /* Check buffers and class validity */
     Status = DefaultQueryInfoBufferCheck(TokenInformationClass,
                                          SeTokenInformationClass,
-                                         sizeof(SeTokenInformationClass) / 
sizeof(SeTokenInformationClass[0]),
+                                         
RTL_NUMBER_OF(SeTokenInformationClass),
                                          TokenInformation,
                                          TokenInformationLength,
                                          ReturnLength,
@@ -1257,7 +1631,7 @@
 
                 DPRINT("NtQueryInformationToken(TokenUser)\n");
                 RequiredLength = sizeof(TOKEN_USER) +
-                RtlLengthSid(Token->UserAndGroups[0].Sid);
+                    RtlLengthSid(Token->UserAndGroups[0].Sid);
 
                 _SEH2_TRY
                 {
@@ -1268,7 +1642,7 @@
                                                               RequiredLength - 
sizeof(TOKEN_USER),
                                                               &tu->User,
                                                               (PSID)(tu + 1),
-                                                              &Unused.Ptr,
+                                                              &Unused.PSid,
                                                               &Unused.Ulong);
                     }
                     else
@@ -1296,24 +1670,24 @@
 
                 DPRINT("NtQueryInformationToken(TokenGroups)\n");
                 RequiredLength = sizeof(tg->GroupCount) +
-                RtlLengthSidAndAttributes(Token->UserAndGroupCount - 1, 
&Token->UserAndGroups[1]);
+                    RtlLengthSidAndAttributes(Token->UserAndGroupCount - 1, 
&Token->UserAndGroups[1]);
 
                 _SEH2_TRY
                 {
                     if (TokenInformationLength >= RequiredLength)
                     {
                         ULONG SidLen = RequiredLength - sizeof(tg->GroupCount) 
-
-                        ((Token->UserAndGroupCount - 1) * 
sizeof(SID_AND_ATTRIBUTES));
-                        PSID_AND_ATTRIBUTES Sid = 
(PSID_AND_ATTRIBUTES)((ULONG_PTR)TokenInformation + sizeof(tg->GroupCount) +
-                                                                        
((Token->UserAndGroupCount - 1) * sizeof(SID_AND_ATTRIBUTES)));
+                            ((Token->UserAndGroupCount - 1) * 
sizeof(SID_AND_ATTRIBUTES));
+                        PSID Sid = (PSID_AND_ATTRIBUTES)((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],
-                                                              (PSID)Sid,
-                                                              &Unused.Ptr,
+                                                              Sid,
+                                                              &Unused.PSid,
                                                               &Unused.Ulong);
                     }
                     else
@@ -1341,7 +1715,7 @@
 
                 DPRINT("NtQueryInformationToken(TokenPrivileges)\n");
                 RequiredLength = sizeof(tp->PrivilegeCount) +
-                (Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
+                    (Token->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES));
 
                 _SEH2_TRY
                 {
@@ -1373,8 +1747,8 @@
 
             case TokenOwner:
             {
+                PTOKEN_OWNER to = (PTOKEN_OWNER)TokenInformation;
                 ULONG SidLen;
-                PTOKEN_OWNER to = (PTOKEN_OWNER)TokenInformation;
 
                 DPRINT("NtQueryInformationToken(TokenOwner)\n");
                 SidLen = 
RtlLengthSid(Token->UserAndGroups[Token->DefaultOwnerIndex].Sid);
@@ -1410,8 +1784,8 @@
 
             case TokenPrimaryGroup:
             {
+                PTOKEN_PRIMARY_GROUP tpg = 
(PTOKEN_PRIMARY_GROUP)TokenInformation;
                 ULONG SidLen;
-                PTOKEN_PRIMARY_GROUP tpg = 
(PTOKEN_PRIMARY_GROUP)TokenInformation;
 
                 DPRINT("NtQueryInformationToken(TokenPrimaryGroup)\n");
                 SidLen = RtlLengthSid(Token->PrimaryGroup);
@@ -1453,9 +1827,7 @@
                 RequiredLength = sizeof(TOKEN_DEFAULT_DACL);
 
                 if (Token->DefaultDacl != NULL)
-                {
                     RequiredLength += Token->DefaultDacl->AclSize;
-                }
 
                 _SEH2_TRY
                 {
@@ -1688,17 +2060,17 @@
                     if (TokenInformationLength >= RequiredLength)
                     {
                         ULONG SidLen = RequiredLength - sizeof(tg->GroupCount) 
-
-                        (Token->RestrictedSidCount * 
sizeof(SID_AND_ATTRIBUTES));
-                        PSID_AND_ATTRIBUTES Sid = 
(PSID_AND_ATTRIBUTES)((ULONG_PTR)TokenInformation + sizeof(tg->GroupCount) +
-                                                                        
(Token->RestrictedSidCount * sizeof(SID_AND_ATTRIBUTES)));
+                            (Token->RestrictedSidCount * 
sizeof(SID_AND_ATTRIBUTES));
+                        PSID 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],
-                                                              (PSID)Sid,
-                                                              &Unused.Ptr,
+                                                              Sid,
+                                                              &Unused.PSid,
                                                               &Unused.Ulong);
                     }
                     else
@@ -1731,14 +2103,12 @@
 
                 DPRINT("NtQueryInformationToken(TokenSessionId)\n");
 
-                Status = SeQuerySessionIdToken(Token,
-                                               &SessionId);
-
+                Status = SeQuerySessionIdToken(Token, &SessionId);
                 if (NT_SUCCESS(Status))
                 {
                     _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;
 
                         if (ReturnLength != NULL)
@@ -1792,7 +2162,7 @@
 
     Status = DefaultSetInfoBufferCheck(TokenInformationClass,
                                        SeTokenInformationClass,
-                                       sizeof(SeTokenInformationClass) / 
sizeof(SeTokenInformationClass[0]),
+                                       RTL_NUMBER_OF(SeTokenInformationClass),
                                        TokenInformation,
                                        TokenInformationLength,
                                        PreviousMode);
@@ -2179,14 +2549,16 @@
  * @implemented
  *
  * 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
  * 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
 NtDuplicateToken(IN HANDLE ExistingTokenHandle,
                  IN ACCESS_MASK DesiredAccess,
-                 IN POBJECT_ATTRIBUTES ObjectAttributes  OPTIONAL,
+                 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
                  IN BOOLEAN EffectiveOnly,
                  IN TOKEN_TYPE TokenType,
                  OUT PHANDLE NewTokenHandle)
@@ -2330,7 +2702,7 @@
                     OUT PULONG ReturnLength)
 {
     UNIMPLEMENTED;
-    return(STATUS_NOT_IMPLEMENTED);
+    return STATUS_NOT_IMPLEMENTED;
 }
 
 

Modified: trunk/reactos/sdk/include/ndk/rtlfuncs.h
URL: 
http://svn.reactos.org/svn/reactos/trunk/reactos/sdk/include/ndk/rtlfuncs.h?rev=73122&r1=73121&r2=73122&view=diff
==============================================================================
--- trunk/reactos/sdk/include/ndk/rtlfuncs.h    [iso-8859-1] (original)
+++ trunk/reactos/sdk/include/ndk/rtlfuncs.h    [iso-8859-1] Fri Nov  4 
17:52:32 2016
@@ -1279,13 +1279,13 @@
 NTSTATUS
 NTAPI
 RtlCopySidAndAttributesArray(
-    ULONG Count,
-    PSID_AND_ATTRIBUTES Src,
-    ULONG SidAreaSize,
-    PSID_AND_ATTRIBUTES Dest,
-    PVOID SidArea,
-    PVOID* RemainingSidArea,
-    PULONG RemainingSidAreaSize
+    _In_ ULONG Count,
+    _In_ PSID_AND_ATTRIBUTES Src,
+    _In_ ULONG SidAreaSize,
+    _In_ PSID_AND_ATTRIBUTES Dest,
+    _In_ PSID SidArea,
+    _Out_ PSID* RemainingSidArea,
+    _Out_ PULONG RemainingSidAreaSize
 );
 
 _IRQL_requires_max_(APC_LEVEL)

Modified: trunk/reactos/sdk/lib/rtl/sid.c
URL: 
http://svn.reactos.org/svn/reactos/trunk/reactos/sdk/lib/rtl/sid.c?rev=73122&r1=73121&r2=73122&view=diff
==============================================================================
--- trunk/reactos/sdk/lib/rtl/sid.c     [iso-8859-1] (original)
+++ trunk/reactos/sdk/lib/rtl/sid.c     [iso-8859-1] Fri Nov  4 17:52:32 2016
@@ -250,8 +250,8 @@
                              IN PSID_AND_ATTRIBUTES Src,
                              IN ULONG SidAreaSize,
                              IN PSID_AND_ATTRIBUTES Dest,
-                             IN PVOID SidArea,
-                             OUT PVOID* RemainingSidArea,
+                             IN PSID SidArea,
+                             OUT PSID* RemainingSidArea,
                              OUT PULONG RemainingSidAreaSize)
 {
     ULONG SidLength, i;
@@ -273,7 +273,7 @@
         RtlCopySid(SidLength, SidArea, Src[i].Sid);
 
         /* 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 */

Modified: trunk/rostests/kmtests/kmtest_drv/testlist.c
URL: 
http://svn.reactos.org/svn/reactos/trunk/rostests/kmtests/kmtest_drv/testlist.c?rev=73122&r1=73121&r2=73122&view=diff
==============================================================================
--- trunk/rostests/kmtests/kmtest_drv/testlist.c        [iso-8859-1] (original)
+++ trunk/rostests/kmtests/kmtest_drv/testlist.c        [iso-8859-1] Fri Nov  4 
17:52:32 2016
@@ -132,8 +132,6 @@
     { "-ObTypeNoClean",                     Test_ObTypeNoClean },
     { "ObTypes",                            Test_ObTypes },
     { "PsNotify",                           Test_PsNotify },
-    { "SeInheritance",                      Test_SeInheritance },
-    { "-SeQueryInfoToken",                  Test_SeQueryInfoToken },
     { "RtlAvlTreeKM",                       Test_RtlAvlTree },
     { "RtlExceptionKM",                     Test_RtlException },
     { "RtlIntSafeKM",                       Test_RtlIntSafe },
@@ -142,6 +140,8 @@
     { "RtlRegistryKM",                      Test_RtlRegistry },
     { "RtlSplayTreeKM",                     Test_RtlSplayTree },
     { "RtlUnicodeStringKM",                 Test_RtlUnicodeString },
+    { "SeInheritance",                      Test_SeInheritance },
+    { "SeQueryInfoToken",                   Test_SeQueryInfoToken },
     { "ZwAllocateVirtualMemory",            Test_ZwAllocateVirtualMemory },
     { "ZwCreateSection",                    Test_ZwCreateSection },
     { "ZwMapViewOfSection",                 Test_ZwMapViewOfSection },

Modified: trunk/rostests/kmtests/ntos_se/SeQueryInfoToken.c
URL: 
http://svn.reactos.org/svn/reactos/trunk/rostests/kmtests/ntos_se/SeQueryInfoToken.c?rev=73122&r1=73121&r2=73122&view=diff
==============================================================================
--- trunk/rostests/kmtests/ntos_se/SeQueryInfoToken.c   [iso-8859-1] (original)
+++ trunk/rostests/kmtests/ntos_se/SeQueryInfoToken.c   [iso-8859-1] Fri Nov  4 
17:52:32 2016
@@ -44,10 +44,10 @@
     if (Token == NULL) return;
 
     Status = SeQueryInformationToken(Token, TokenOwner, &Buffer);
-    ok((Status == STATUS_SUCCESS), "SQIT with TokenOwner arg fails with status 
0x%X\n", Status);
-    if (Status == STATUS_SUCCESS)
-    {
-        ok(Buffer != NULL, "Wrong. SQIT call was successful with TokenOwner 
arg. But Buffer = NULL\n");
+    ok((Status == STATUS_SUCCESS), "SQIT with TokenOwner arg fails with status 
0x%08X\n", Status);
+    if (Status == STATUS_SUCCESS)
+    {
+        ok(Buffer != NULL, "Wrong. SQIT call was successful with TokenOwner 
arg. But Buffer == NULL\n");
 
         if (Buffer)
         {
@@ -62,10 +62,10 @@
     
     Buffer = NULL;
     Status = SeQueryInformationToken(Token, TokenDefaultDacl, &Buffer);
-    ok(Status == STATUS_SUCCESS, "SQIT with TokenDefaultDacl fails with status 
0x%X\n", Status);
-    if (Status == STATUS_SUCCESS)
-    {
-        ok(Buffer != NULL, "Wrong. SQIT call was successful with 
TokenDefaultDacl arg. But Buffer = NULL\n");
+    ok(Status == STATUS_SUCCESS, "SQIT with TokenDefaultDacl fails with status 
0x%08X\n", Status);
+    if (Status == STATUS_SUCCESS)
+    {
+        ok(Buffer != NULL, "Wrong. SQIT call was successful with 
TokenDefaultDacl arg. But Buffer == NULL\n");
         if (Buffer)
         {
             TDefDacl = (PTOKEN_DEFAULT_DACL)Buffer;
@@ -79,10 +79,10 @@
 
     Buffer = NULL;
     Status = SeQueryInformationToken(Token, TokenGroups, &Buffer);
-    ok(Status == STATUS_SUCCESS, "SQIT with TokenGroups fails with status 
0x%X\n", Status);
-    if (Status == STATUS_SUCCESS)
-    {
-        ok(Buffer != NULL, "Wrong. SQIT call was successful with TokenGroups 
arg. But Buffer = NULL\n");
+    ok(Status == STATUS_SUCCESS, "SQIT with TokenGroups fails with status 
0x%08X\n", Status);
+    if (Status == STATUS_SUCCESS)
+    {
+        ok(Buffer != NULL, "Wrong. SQIT call was successful with TokenGroups 
arg. But Buffer == NULL\n");
         if (Buffer)
         {
             TGroups = (PTOKEN_GROUPS)Buffer;
@@ -107,58 +107,88 @@
     // Call SQIT with TokenImpersonationLevel argument
     //
     // What's up? Why SQIT fails with right arg?
+    // Because your token has Token->TokenType != TokenImpersonation. -hbelusca
 
     Buffer = NULL;
     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;
     Status = SeQueryInformationToken(Token, TokenImpersonationLevel, &Buffer);
-    ok(Status == STATUS_SUCCESS, "and again: SQIT with TokenImpersonationLevel 
fails with status 0x%X\n", Status);
-    if (Status == STATUS_SUCCESS)
-    {
-        ok(Buffer != NULL, "Wrong. SQIT call was successful with 
TokenImpersonationLevel arg. But Buffer = NULL\n");
+    ok(Status == STATUS_SUCCESS, "and again: SQIT with TokenImpersonationLevel 
fails with status 0x%08X\n", Status);
+    if (Status == STATUS_SUCCESS)
+    {
+        ok(Buffer != NULL, "Wrong. SQIT call was successful with 
TokenImpersonationLevel arg. But Buffer == NULL\n");
     } 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;
     Status = SeQueryInformationToken(Token, TokenStatistics, &Buffer);
-    ok(Status == STATUS_SUCCESS, "SQIT with TokenStatistics fails with status 
0x%X\n", Status);
-    if (Status == STATUS_SUCCESS)
-    {
-        ok(Buffer != NULL, "Wrong. SQIT call was successful with 
TokenStatistics arg. But Buffer = NULL\n");
+    ok(Status == STATUS_SUCCESS, "SQIT with TokenStatistics fails with status 
0x%08X\n", Status);
+    if (Status == STATUS_SUCCESS)
+    {
+        ok(Buffer != NULL, "Wrong. SQIT call was successful with 
TokenStatistics arg. But Buffer == NULL\n");
         if (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, 
                 TStats->TokenId.HighPart, 
                 TStats->ImpersonationLevel,
                 TStats->PrivilegeCount,
                 TStats->GroupCount
                 );
-            ExFreePool(TStats);
+            ExFreePool(Buffer);
         }
     } 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;
     Status = SeQueryInformationToken(Token, TokenType, &Buffer);
-    ok(Status == STATUS_SUCCESS, "SQIT with TokenType fails with status 
0x%X\n", Status);
-    if (Status == STATUS_SUCCESS)
-    {
-        ok(Buffer != NULL, "Wrong. SQIT call was successful with TokenType 
arg. But Buffer = NULL\n");
+    ok(Status == STATUS_SUCCESS, "SQIT with TokenType fails with status 
0x%08X\n", Status);
+    if (Status == STATUS_SUCCESS)
+    {
+        ok(Buffer != NULL, "Wrong. SQIT call was successful with TokenType 
arg. But Buffer == NULL\n");
         if (Buffer)
         {
             TType = (PTOKEN_TYPE)Buffer;
             ok((*TType == TokenPrimary || *TType == TokenImpersonation), 
"TokenType in not a primary nor impersonation. FAILED\n");
-            ExFreePool(TType);
+            ExFreePool(Buffer);
         }
     }
 
@@ -169,12 +199,12 @@
     ok(Status == STATUS_SUCCESS, "SQIT with TokenUser fails\n");
     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)
         {
             TUser = (PTOKEN_USER)Buffer;
             ok(RtlValidSid(TUser->User.Sid), "TokenUser has an invalid Sid\n");
-            ExFreePool(TUser);
+            ExFreePool(Buffer);
         }
     }
 
@@ -237,7 +267,6 @@
 
     SeCaptureSubjectContext(&AccessState->SubjectSecurityContext);
     SeLockSubjectContext(&AccessState->SubjectSecurityContext);
-
     Token = SeQuerySubjectContextToken(&AccessState->SubjectSecurityContext);
 
     // Testing SQIT with AccessState Token
@@ -295,8 +324,7 @@
     //      Testing SeFreePrivileges                                  //
     //----------------------------------------------------------------//
 
-    Privileges = ExAllocatePool(PagedPool, 
AuxData->PrivilegeSet->PrivilegeCount*sizeof(PRIVILEGE_SET));
-
+    Privileges = NULL;
     Checker = SeAccessCheck(
         AccessState->SecurityDescriptor,
         &AccessState->SubjectSecurityContext,
@@ -311,6 +339,11 @@
         );
     ok(Checker, "Checker 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);
 
 
@@ -326,7 +359,7 @@
     Status = SeQueryInformationToken(Token, TokenPrivileges, &Buffer);
     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)
         {
             TPrivileges = (PTOKEN_PRIVILEGES)(Buffer);
@@ -351,8 +384,7 @@
 
     // Call SeFreePrivileges again
 
-    Privileges = ExAllocatePool(PagedPool, 20*sizeof(PRIVILEGE_SET));
-
+    Privileges = NULL;
     Checker = SeAccessCheck(
         AccessState->SecurityDescriptor,
         &AccessState->SubjectSecurityContext,
@@ -367,6 +399,11 @@
         );
     ok(Checker, "Checker 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);
 
     //----------------------------------------------------------------//


Reply via email to