summaryrefslogtreecommitdiffstats
path: root/private/ntos/se
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--private/ntos/se/accessck.c1886
-rw-r--r--private/ntos/se/adt.h189
-rw-r--r--private/ntos/se/adtevent.c31
-rw-r--r--private/ntos/se/adtinit.c422
-rw-r--r--private/ntos/se/adtlog.c791
-rw-r--r--private/ntos/se/adtp.h288
-rw-r--r--private/ntos/se/adtutil.c51
-rw-r--r--private/ntos/se/adtvars.c62
-rw-r--r--private/ntos/se/capture.c2568
-rw-r--r--private/ntos/se/ctaccess.c1267
-rw-r--r--private/ntos/se/ctlnpqos.c1729
-rw-r--r--private/ntos/se/ctlpcqos.c1256
-rw-r--r--private/ntos/se/ctseacc.c927
-rw-r--r--private/ntos/se/ctsertl.c1137
-rw-r--r--private/ntos/se/cttoken.c7505
-rw-r--r--private/ntos/se/dirs24
-rw-r--r--private/ntos/se/dumpuser.c324
-rw-r--r--private/ntos/se/mp/makefile6
-rw-r--r--private/ntos/se/mp/sources29
-rw-r--r--private/ntos/se/nls/ntaudit.mc153
-rw-r--r--private/ntos/se/privileg.c574
-rw-r--r--private/ntos/se/rmaudit.c221
-rw-r--r--private/ntos/se/rmlogon.c1137
-rw-r--r--private/ntos/se/rmmain.c1294
-rw-r--r--private/ntos/se/rmp.h229
-rw-r--r--private/ntos/se/rmvars.c179
-rw-r--r--private/ntos/se/seassign.c1924
-rw-r--r--private/ntos/se/seastate.c599
-rw-r--r--private/ntos/se/seaudit.c4049
-rw-r--r--private/ntos/se/seclient.c633
-rw-r--r--private/ntos/se/seglobal.c965
-rw-r--r--private/ntos/se/seinit.c303
-rw-r--r--private/ntos/se/semethod.c1279
-rw-r--r--private/ntos/se/sep.c231
-rw-r--r--private/ntos/se/sep.h741
-rw-r--r--private/ntos/se/sepaudit.c2629
-rw-r--r--private/ntos/se/sources.inc74
-rw-r--r--private/ntos/se/subject.c432
-rw-r--r--private/ntos/se/token.c2385
-rw-r--r--private/ntos/se/tokenadj.c1447
-rw-r--r--private/ntos/se/tokendup.c945
-rw-r--r--private/ntos/se/tokenopn.c594
-rw-r--r--private/ntos/se/tokenp.h595
-rw-r--r--private/ntos/se/tokenqry.c1612
-rw-r--r--private/ntos/se/tokenset.c798
-rw-r--r--private/ntos/se/tse.c579
-rw-r--r--private/ntos/se/tsecomm.c136
-rw-r--r--private/ntos/se/tseum.c578
-rw-r--r--private/ntos/se/tsevars.c377
-rw-r--r--private/ntos/se/ttokend.c12339
-rw-r--r--private/ntos/se/ttseacc.c64
-rw-r--r--private/ntos/se/ttsertl.c75
-rw-r--r--private/ntos/se/uipers.c580
-rw-r--r--private/ntos/se/uipriv.c611
-rw-r--r--private/ntos/se/up/makefile6
-rw-r--r--private/ntos/se/up/sources27
-rw-r--r--private/ntos/se/utaccess.c52
-rw-r--r--private/ntos/se/utlnpqos.c76
-rw-r--r--private/ntos/se/utlpcqos.c52
-rw-r--r--private/ntos/se/utseacc.c56
-rw-r--r--private/ntos/se/utseqos.c50
-rw-r--r--private/ntos/se/utsertl.c56
-rw-r--r--private/ntos/se/uttoken.c54
-rw-r--r--private/ntos/seaudit/dirs29
-rw-r--r--private/ntos/seaudit/msaudite/audit.rc14
-rw-r--r--private/ntos/seaudit/msaudite/makefile6
-rw-r--r--private/ntos/seaudit/msaudite/makefile.inc4
-rw-r--r--private/ntos/seaudit/msaudite/msaudite.def3
-rw-r--r--private/ntos/seaudit/msaudite/msaudite.mc2580
-rw-r--r--private/ntos/seaudit/msaudite/sources41
-rw-r--r--private/ntos/seaudit/msauditt/audit.rc13
-rw-r--r--private/ntos/seaudit/msauditt/makefile6
-rw-r--r--private/ntos/seaudit/msauditt/msauditt.def7
-rw-r--r--private/ntos/seaudit/msauditt/msauditt.mc365
-rw-r--r--private/ntos/seaudit/msauditt/mstmp.c55
-rw-r--r--private/ntos/seaudit/msauditt/sources53
-rw-r--r--private/ntos/seaudit/msobjs/audit.rc14
-rw-r--r--private/ntos/seaudit/msobjs/makefile6
-rw-r--r--private/ntos/seaudit/msobjs/makefile.inc4
-rw-r--r--private/ntos/seaudit/msobjs/msobjs.def3
-rw-r--r--private/ntos/seaudit/msobjs/msobjs.mc1908
-rw-r--r--private/ntos/seaudit/msobjs/sources41
82 files changed, 67434 insertions, 0 deletions
diff --git a/private/ntos/se/accessck.c b/private/ntos/se/accessck.c
new file mode 100644
index 000000000..ec5c1c8ca
--- /dev/null
+++ b/private/ntos/se/accessck.c
@@ -0,0 +1,1886 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ Accessck.c
+
+Abstract:
+
+ This Module implements the access check procedures. Both NtAccessCheck
+ and SeAccessCheck check to is if a user (denoted by an input token) can
+ be granted the desired access rights to object protected by a security
+ descriptor and an optional object owner. Both procedures use a common
+ local procedure to do the test.
+
+Author:
+
+ Robert Reichel (RobertRe) 11-30-90
+
+Environment:
+
+ Kernel Mode
+
+Revision History:
+
+ Richard Ward (RichardW) 14-Apr-92 Changed ACE_HEADER
+--*/
+
+#include "tokenp.h"
+
+//
+// Define the local macros and procedure for this module
+//
+
+#if DBG
+
+extern BOOLEAN SepDumpSD;
+extern BOOLEAN SepDumpToken;
+BOOLEAN SepShowAccessFail;
+
+#endif // DBG
+
+
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE,SepValidateAce)
+#pragma alloc_text(PAGE,SepSidInToken)
+#pragma alloc_text(PAGE,SepAccessCheck)
+#pragma alloc_text(PAGE,NtAccessCheck)
+#pragma alloc_text(PAGE,SeFreePrivileges)
+#pragma alloc_text(PAGE,SeAccessCheck)
+#pragma alloc_text(PAGE,SePrivilegePolicyCheck)
+#pragma alloc_text(PAGE,SepTokenIsOwner)
+#pragma alloc_text(PAGE,SeFastTraverseCheck)
+#endif
+
+BOOLEAN
+SepValidateAce (
+ IN PVOID Ace,
+ IN PACL Dacl
+ )
+
+/*++
+
+Routine Description:
+
+ Performs rudimentary validation on an Ace. Ace must be within the
+ passed ACl, the SID must be within the Ace, and the Ace must be of at
+ least a minimal size.
+
+Arguments:
+
+ Ace - Pointer to Ace to be examined
+
+ Dacl - Pointer to Acl in which Ace is supposed to exist
+
+Return Value:
+
+ A value of TRUE indicates the Ace is well formed, FALSE otherwise.
+
+--*/
+
+{
+
+ USHORT AceSize;
+ USHORT AclSize;
+
+ PAGED_CODE();
+
+ //
+ // make sure ACE is within ACL
+ //
+
+ AceSize = ((PACE_HEADER)Ace)->AceSize;
+ AclSize = Dacl->AclSize;
+
+ if ( (PVOID)((PUCHAR)Ace + AceSize) > (PVOID)((PUSHORT)Dacl + AclSize)) {
+
+ return(FALSE);
+
+ }
+
+ //
+ // make sure SID is within ACE
+ //
+
+ if (IsKnownAceType( Ace )) {
+ if ( (PVOID) ( (ULONG)Ace + SeLengthSid(&(((PKNOWN_ACE)Ace)->SidStart)) ) >
+ (PVOID) ( (PUCHAR)Ace + AceSize )
+ ) {
+
+ return(FALSE);
+
+ }
+ }
+
+ //
+ // Make sure ACE is big enough for minimum grant ACE
+ //
+
+ if (AceSize < sizeof(KNOWN_ACE)) {
+
+ return(FALSE);
+
+ }
+
+
+ return(TRUE);
+}
+
+BOOLEAN
+SepSidInToken (
+ IN PACCESS_TOKEN AToken,
+ IN PSID Sid
+ )
+
+/*++
+
+Routine Description:
+
+ Checks to see if a given SID is in the given token.
+
+ N.B. The code to compute the length of a SID and test for equality
+ is duplicated from the security runtime since this is such a
+ frequently used routine.
+
+Arguments:
+
+ Token - Pointer to the token to be examined
+
+ Sid - Pointer to the SID of interest
+
+Return Value:
+
+ A value of TRUE indicates that the SID is in the token, FALSE
+ otherwise.
+
+--*/
+
+{
+
+ ULONG i;
+ PISID MatchSid;
+ ULONG SidLength;
+ PTOKEN Token;
+ PSID_AND_ATTRIBUTES TokenSid;
+ ULONG UserAndGroupCount;
+
+ PAGED_CODE();
+
+#if DBG
+
+ SepDumpTokenInfo(AToken);
+
+#endif
+
+ //
+ // Get the length of the source SID since this only needs to be computed
+ // once.
+ //
+
+ SidLength = 8 + (4 * ((PISID)Sid)->SubAuthorityCount);
+
+ //
+ // Get address of user/group array and number of user/groups.
+ //
+
+ Token = (PTOKEN)AToken;
+ TokenSid = Token->UserAndGroups;
+ UserAndGroupCount = Token->UserAndGroupCount;
+
+ //
+ // Scan through the user/groups and attempt to find a match with the
+ // specified SID.
+ //
+
+ for (i = 0 ; i < UserAndGroupCount ; i += 1) {
+ MatchSid = (PISID)TokenSid->Sid;
+
+ //
+ // If the SID revision and length matches, then compare the SIDs
+ // for equality.
+ //
+
+ if ((((PISID)Sid)->Revision == MatchSid->Revision) &&
+ (SidLength == (8 + (4 * (ULONG)MatchSid->SubAuthorityCount)))) {
+ if (RtlEqualMemory(Sid, MatchSid, SidLength)) {
+
+ //
+ // If this is the first one in the list, then it is the User,
+ // and return success immediately.
+ //
+ // If this is not the first one, then it represents a group,
+ // and we must make sure the group is currently enabled before
+ // we can say that the group is "in" the token.
+ //
+
+ if ((i == 0) || (TokenSid->Attributes & SE_GROUP_ENABLED)) {
+ return TRUE;
+
+ } else {
+ return FALSE;
+ }
+ }
+ }
+
+ TokenSid += 1;
+ }
+
+ return FALSE;
+}
+
+
+
+BOOLEAN
+SepAccessCheck (
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN PTOKEN PrimaryToken,
+ IN PTOKEN ClientToken OPTIONAL,
+ IN ACCESS_MASK DesiredAccess,
+ IN PGENERIC_MAPPING GenericMapping,
+ IN ACCESS_MASK PreviouslyGrantedAccess,
+ IN KPROCESSOR_MODE PreviousMode,
+ OUT PACCESS_MASK GrantedAccess,
+ OUT PPRIVILEGE_SET *Privileges OPTIONAL,
+ OUT PNTSTATUS AccessStatus
+ )
+
+/*++
+
+Routine Description:
+
+ Worker routine for SeAccessCheck and NtAccessCheck. We actually do the
+ access checking here.
+
+ Whether or not we actually evaluate the DACL is based on the following
+ interaction between the SE_DACL_PRESENT bit in the security descriptor
+ and the value of the DACL pointer itself.
+
+
+ SE_DACL_PRESENT
+
+ SET CLEAR
+
+ +-------------+-------------+
+ | | |
+ NULL | GRANT | GRANT |
+ | ALL | ALL |
+ DACL | | |
+ Pointer +-------------+-------------+
+ | | |
+ !NULL | EVALUATE | GRANT |
+ | ACL | ALL |
+ | | |
+ +-------------+-------------+
+
+Arguments:
+
+ SecurityDescriptor - Pointer to the security descriptor from the object
+ being accessed.
+
+ Token - Pointer to user's token object.
+
+ TokenLocked - Boolean describing whether or not there is a read lock
+ on the token.
+
+ DesiredAccess - Access mask describing the user's desired access to the
+ object. This mask is assumed not to contain generic access types.
+
+ GenericMapping - Supplies a pointer to the generic mapping associated
+ with this object type.
+
+ PreviouslyGrantedAccess - Access mask indicating any access' that have
+ already been granted by higher level routines
+
+ PrivilgedAccessMask - Mask describing access types that may not be
+ granted without a privilege.
+
+ GrantedAccess - Returns an access mask describing all granted access',
+ or NULL.
+
+ Privileges - Optionally supplies a pointer in which will be returned
+ any privileges that were used for the access. If this is null,
+ it will be assumed that privilege checks have been done already.
+
+ AccessStatus - Returns STATUS_SUCCESS or other error code to be
+ propogated back to the caller
+
+Return Value:
+
+ A value of TRUE indicates that some access' were granted, FALSE
+ otherwise.
+
+--*/
+{
+
+ ACCESS_MASK CurrentDenied = 0;
+ ACCESS_MASK CurrentGranted = 0;
+ ACCESS_MASK Remaining;
+
+ PACL Dacl;
+
+ PVOID Ace;
+ ULONG AceCount;
+
+ ULONG i;
+ ULONG PrivilegeCount = 0;
+ BOOLEAN Success = FALSE;
+ BOOLEAN SystemSecurity = FALSE;
+ BOOLEAN WriteOwner = FALSE;
+ PTOKEN EToken;
+
+ PAGED_CODE();
+
+#if DBG
+
+ SepDumpSecurityDescriptor(
+ SecurityDescriptor,
+ "Input to SeAccessCheck\n"
+ );
+
+ if (ARGUMENT_PRESENT( ClientToken )) {
+ SepDumpTokenInfo( ClientToken );
+ }
+
+ SepDumpTokenInfo( PrimaryToken );
+
+#endif
+
+
+ EToken = ARGUMENT_PRESENT( ClientToken ) ? ClientToken : PrimaryToken;
+
+ //
+ // Assert that there are no generic accesses in the DesiredAccess
+ //
+
+ SeAssertMappedCanonicalAccess( DesiredAccess );
+
+ Remaining = DesiredAccess;
+
+ //
+ // Check for ACCESS_SYSTEM_SECURITY here,
+ // fail if he's asking for it and doesn't have
+ // the privilege.
+ //
+
+ if ( Remaining & ACCESS_SYSTEM_SECURITY ) {
+
+ //
+ // Bugcheck if we weren't given a pointer to return privileges
+ // into. Our caller was supposed to have taken care of this
+ // in that case.
+ //
+
+ ASSERT( ARGUMENT_PRESENT( Privileges ));
+
+ Success = SepSinglePrivilegeCheck (
+ SeSecurityPrivilege,
+ EToken,
+ PreviousMode
+ );
+
+ if (!Success) {
+
+ *AccessStatus = STATUS_PRIVILEGE_NOT_HELD;
+ return( FALSE );
+ }
+
+ //
+ // Success, remove ACCESS_SYSTEM_SECURITY from remaining, add it
+ // to PreviouslyGrantedAccess
+ //
+
+ Remaining &= ~ACCESS_SYSTEM_SECURITY;
+ PreviouslyGrantedAccess |= ACCESS_SYSTEM_SECURITY;
+
+ PrivilegeCount++;
+ SystemSecurity = TRUE;
+
+ if ( Remaining == 0 ) {
+
+ SepAssemblePrivileges(
+ PrivilegeCount,
+ SystemSecurity,
+ WriteOwner,
+ Privileges
+ );
+
+ *AccessStatus = STATUS_SUCCESS;
+ *GrantedAccess = PreviouslyGrantedAccess;
+ return( TRUE );
+
+ }
+
+ }
+
+
+ //
+ // Get pointer to client SID's
+ //
+
+ Dacl = SepDaclAddrSecurityDescriptor( (PISECURITY_DESCRIPTOR)SecurityDescriptor );
+
+ //
+ // If the SE_DACL_PRESENT bit is not set, the object has no
+ // security, so all accesses are granted. If he's asking for
+ // MAXIMUM_ALLOWED, return the GENERIC_ALL field from the generic
+ // mapping.
+ //
+ // Also grant all access if the Dacl is NULL.
+ //
+
+ if ( !SepAreControlBitsSet(
+ (PISECURITY_DESCRIPTOR)SecurityDescriptor,
+ SE_DACL_PRESENT
+ ) || (Dacl == NULL)) {
+
+ if (DesiredAccess & MAXIMUM_ALLOWED) {
+
+ //
+ // Give him:
+ // GenericAll
+ // Anything else he asked for
+ //
+
+ *GrantedAccess = GenericMapping->GenericAll;
+ *GrantedAccess |= (DesiredAccess & ~MAXIMUM_ALLOWED);
+
+ } else {
+
+ *GrantedAccess = DesiredAccess | PreviouslyGrantedAccess;
+ }
+
+ if ( PrivilegeCount > 0 ) {
+
+ SepAssemblePrivileges(
+ PrivilegeCount,
+ SystemSecurity,
+ WriteOwner,
+ Privileges
+ );
+ }
+
+
+ *AccessStatus = STATUS_SUCCESS;
+ return(TRUE);
+ }
+
+ //
+ // There is security on this object. Check to see
+ // if he's asking for WRITE_OWNER, and perform the
+ // privilege check if so.
+ //
+
+ if ( (Remaining & WRITE_OWNER) && ARGUMENT_PRESENT( Privileges ) ) {
+
+ Success = SepSinglePrivilegeCheck (
+ SeTakeOwnershipPrivilege,
+ EToken,
+ PreviousMode
+ );
+
+ if (Success) {
+
+ //
+ // Success, remove WRITE_OWNER from remaining, add it
+ // to PreviouslyGrantedAccess
+ //
+
+ Remaining &= ~WRITE_OWNER;
+ PreviouslyGrantedAccess |= WRITE_OWNER;
+
+ PrivilegeCount++;
+ WriteOwner = TRUE;
+
+ if ( Remaining == 0 ) {
+
+ SepAssemblePrivileges(
+ PrivilegeCount,
+ SystemSecurity,
+ WriteOwner,
+ Privileges
+ );
+
+ *AccessStatus = STATUS_SUCCESS;
+ *GrantedAccess = PreviouslyGrantedAccess;
+ return( TRUE );
+
+ }
+ }
+ }
+
+
+ //
+ // If the DACL is empty,
+ // deny all access immediately.
+ //
+
+ if ((AceCount = Dacl->AceCount) == 0) {
+
+ //
+ // We know that Remaining != 0 here, because we
+ // know it was non-zero coming into this routine,
+ // and we've checked it against 0 every time we've
+ // cleared a bit.
+ //
+
+ ASSERT( Remaining != 0 );
+
+ //
+ // There are ungranted accesses. Since there is
+ // nothing in the DACL, they will not be granted.
+ // If, however, the only ungranted access at this
+ // point is MAXIMUM_ALLOWED, and something has been
+ // granted in the PreviouslyGranted mask, return
+ // what has been granted.
+ //
+
+ if ( (Remaining == MAXIMUM_ALLOWED) && (PreviouslyGrantedAccess != (ACCESS_MASK)0) ) {
+
+ *AccessStatus = STATUS_SUCCESS;
+ *GrantedAccess = PreviouslyGrantedAccess;
+
+ if ( PrivilegeCount > 0 ) {
+
+ SepAssemblePrivileges(
+ PrivilegeCount,
+ SystemSecurity,
+ WriteOwner,
+ Privileges
+ );
+ }
+
+ return( TRUE );
+
+ } else {
+
+ *AccessStatus = STATUS_ACCESS_DENIED;
+ *GrantedAccess = (ACCESS_MASK)0L;
+ return( FALSE );
+ }
+ }
+
+ //
+ // granted == NUL
+ // denied == NUL
+ //
+ // for each ACE
+ //
+ // if grant
+ // for each SID
+ // if SID match, then add all that is not denied to grant mask
+ //
+ // if deny
+ // for each SID
+ // if SID match, then add all that is not granted to deny mask
+ //
+
+ if (DesiredAccess & MAXIMUM_ALLOWED) {
+
+ for ( i = 0, Ace = FirstAce( Dacl ) ;
+ i < AceCount ;
+ i++, Ace = NextAce( Ace )
+ ) {
+
+ if ( !(((PACE_HEADER)Ace)->AceFlags & INHERIT_ONLY_ACE)) {
+
+ if ( (((PACE_HEADER)Ace)->AceType == ACCESS_ALLOWED_ACE_TYPE) ) {
+
+ if ( SepSidInToken( EToken, &((PACCESS_ALLOWED_ACE)Ace)->SidStart )) {
+
+ //
+ // Only grant access types from this mask that have
+ // not already been denied
+ //
+
+ CurrentGranted |=
+ (((PACCESS_ALLOWED_ACE)Ace)->Mask & ~CurrentDenied);
+ }
+
+ continue;
+
+ }
+
+ if ( (((PACE_HEADER)Ace)->AceType == ACCESS_ALLOWED_COMPOUND_ACE_TYPE) ) {
+
+ //
+ // If we're impersonating, EToken is set to the Client, and if we're not,
+ // EToken is set to the Primary. According to the DSA architecture, if
+ // we're asked to evaluate a compound ACE and we're not impersonating,
+ // pretend we are impersonating ourselves. So we can just use the EToken
+ // for the client token, since it's already set to the right thing.
+ //
+
+
+ if ( SepSidInToken(EToken, RtlCompoundAceClientSid( Ace )) &&
+ SepSidInToken(PrimaryToken, RtlCompoundAceServerSid( Ace ))
+ ) {
+
+ CurrentGranted |=
+ (((PACCESS_ALLOWED_ACE)Ace)->Mask & ~CurrentDenied);
+ }
+
+ continue;
+
+ }
+
+ if ( (((PACE_HEADER)Ace)->AceType == ACCESS_DENIED_ACE_TYPE) ) {
+
+ if ( SepSidInToken( EToken, &((PACCESS_DENIED_ACE)Ace)->SidStart )) {
+
+ //
+ // Only deny access types from this mask that have
+ // not already been granted
+ //
+
+ CurrentDenied |=
+ (((PACCESS_DENIED_ACE)Ace)->Mask & ~CurrentGranted);
+ }
+
+ continue;
+
+ }
+ }
+ }
+
+ //
+ // Turn off the MAXIMUM_ALLOWED bit and whatever we found that
+ // he was granted. If the user passed in extra bits in addition
+ // to MAXIMUM_ALLOWED, make sure that he was granted those access
+ // types. If not, he didn't get what he wanted, so return failure.
+ //
+
+ Remaining &= ~(MAXIMUM_ALLOWED | CurrentGranted);
+
+ if (Remaining != 0) {
+
+ *AccessStatus = STATUS_ACCESS_DENIED;
+ *GrantedAccess = 0;
+ return(FALSE);
+
+ }
+
+ *GrantedAccess = CurrentGranted | PreviouslyGrantedAccess;
+
+ if ( *GrantedAccess != 0 ) {
+
+ *AccessStatus = STATUS_SUCCESS;
+
+ if ( PrivilegeCount != 0 ) {
+
+ SepAssemblePrivileges(
+ PrivilegeCount,
+ SystemSecurity,
+ WriteOwner,
+ Privileges
+ );
+ }
+
+ return(TRUE);
+
+ } else {
+
+ *AccessStatus = STATUS_ACCESS_DENIED;
+ return(FALSE);
+ }
+
+ } // if MAXIMUM_ALLOWED...
+
+ for ( i = 0, Ace = FirstAce( Dacl ) ;
+ ( i < AceCount ) && ( Remaining != 0 ) ;
+ i++, Ace = NextAce( Ace ) ) {
+
+ if ( !(((PACE_HEADER)Ace)->AceFlags & INHERIT_ONLY_ACE)) {
+
+ if ( (((PACE_HEADER)Ace)->AceType == ACCESS_ALLOWED_ACE_TYPE) ) {
+
+ if ( SepSidInToken( EToken, &((PACCESS_ALLOWED_ACE)Ace)->SidStart ) ) {
+
+ Remaining &= ~((PACCESS_ALLOWED_ACE)Ace)->Mask;
+
+ }
+
+ continue;
+ }
+
+ if ( (((PACE_HEADER)Ace)->AceType == ACCESS_ALLOWED_COMPOUND_ACE_TYPE) ) {
+
+ //
+ // See comment in MAXIMUM_ALLOWED case as to why we can use EToken here
+ // for the client.
+ //
+
+ if ( SepSidInToken(EToken, RtlCompoundAceClientSid( Ace )) && SepSidInToken(PrimaryToken, RtlCompoundAceServerSid( Ace )) ) {
+
+ Remaining &= ~((PACCESS_ALLOWED_ACE)Ace)->Mask;
+
+ }
+
+ continue;
+ }
+
+ if ( (((PACE_HEADER)Ace)->AceType == ACCESS_DENIED_ACE_TYPE) ) {
+
+ if ( SepSidInToken( EToken, &((PACCESS_DENIED_ACE)Ace)->SidStart ) ) {
+
+ if (Remaining & ((PACCESS_DENIED_ACE)Ace)->Mask) {
+
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (Remaining != 0) {
+
+ *GrantedAccess = 0;
+ *AccessStatus = STATUS_ACCESS_DENIED;
+ return(FALSE);
+
+ }
+
+ *GrantedAccess = DesiredAccess | PreviouslyGrantedAccess;
+
+ if ( *GrantedAccess == 0 ) {
+ *AccessStatus = STATUS_ACCESS_DENIED;
+ return( FALSE );
+ }
+
+ *AccessStatus = STATUS_SUCCESS;
+
+ if ( PrivilegeCount != 0 ) {
+
+ SepAssemblePrivileges(
+ PrivilegeCount,
+ SystemSecurity,
+ WriteOwner,
+ Privileges
+ );
+ }
+
+ return(TRUE);
+
+}
+
+
+
+
+
+
+
+NTSTATUS
+NtAccessCheck (
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN HANDLE ClientToken,
+ IN ACCESS_MASK DesiredAccess,
+ IN PGENERIC_MAPPING GenericMapping,
+ OUT PPRIVILEGE_SET PrivilegeSet,
+ IN OUT PULONG PrivilegeSetLength,
+ OUT PACCESS_MASK GrantedAccess,
+ OUT PNTSTATUS AccessStatus
+ )
+
+
+/*++
+
+Routine Description:
+
+ See module abstract.
+
+Arguments:
+
+ SecurityDescriptor - Supplies the security descriptor protecting the object
+ being accessed
+
+ ClientToken - Supplies the handle of the user's token.
+
+ DesiredAccess - Supplies the desired access mask.
+
+ GenericMapping - Supplies the generic mapping associated with this
+ object type.
+
+ PrivilegeSet - A pointer to a buffer that upon return will contain
+ any privileges that were used to perform the access validation.
+ If no privileges were used, the buffer will contain a privilege
+ set consisting of zero privileges.
+
+ PrivilegeSetLength - The size of the PrivilegeSet buffer in bytes.
+
+ GrantedAccess - Returns an access mask describing the granted access.
+
+ AccessStatus - Status value that may be returned indicating the
+ reason why access was denied. Routines should avoid hardcoding a
+ return value of STATUS_ACCESS_DENIED so that a different value can
+ be returned when mandatory access control is implemented.
+
+Return Value:
+
+ STATUS_SUCCESS - The attempt proceeded normally. This does not
+ mean access was granted, rather that the parameters were
+ correct.
+
+ STATUS_GENERIC_NOT_MAPPED - The DesiredAccess mask contained
+ an unmapped generic access.
+
+ STATUS_BUFFER_TOO_SMALL - The passed buffer was not large enough
+ to contain the information being returned.
+
+ STATUS_NO_IMPERSONTAION_TOKEN - The passed Token was not an impersonation
+ token.
+
+--*/
+
+{
+ ACCESS_MASK LocalGrantedAccess;
+ KPROCESSOR_MODE PreviousMode;
+ NTSTATUS Status;
+ PTOKEN Token;
+ PSECURITY_DESCRIPTOR CapturedSecurityDescriptor = NULL;
+ ACCESS_MASK PreviouslyGrantedAccess = 0;
+ GENERIC_MAPPING LocalGenericMapping;
+ PPRIVILEGE_SET Privileges = NULL;
+ SECURITY_SUBJECT_CONTEXT SubjectContext;
+ ULONG LocalPrivilegeSetLength;
+
+ PAGED_CODE();
+
+ PreviousMode = KeGetPreviousMode();
+
+ if (PreviousMode == KernelMode) {
+ *AccessStatus = STATUS_SUCCESS;
+ *GrantedAccess = DesiredAccess;
+ return(STATUS_SUCCESS);
+ }
+
+ try {
+
+ ProbeForWrite(
+ AccessStatus,
+ sizeof(NTSTATUS),
+ sizeof(ULONG)
+ );
+
+ ProbeForWrite(
+ GrantedAccess,
+ sizeof(ACCESS_MASK),
+ sizeof(ULONG)
+ );
+
+ ProbeForRead(
+ PrivilegeSetLength,
+ sizeof(ULONG),
+ sizeof(ULONG)
+ );
+
+ ProbeForWrite(
+ PrivilegeSet,
+ *PrivilegeSetLength,
+ sizeof(ULONG)
+ );
+
+ ProbeForRead(
+ GenericMapping,
+ sizeof(GENERIC_MAPPING),
+ sizeof(ULONG)
+ );
+
+ LocalGenericMapping = *GenericMapping;
+
+ LocalPrivilegeSetLength = *PrivilegeSetLength;
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ return( GetExceptionCode() );
+ }
+
+ if (DesiredAccess &
+ ( GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL )) {
+
+ return(STATUS_GENERIC_NOT_MAPPED);
+ }
+
+ //
+ // Obtain a pointer to the passed token
+ //
+
+ Status = ObReferenceObjectByHandle(
+ ClientToken, // Handle
+ (ACCESS_MASK)TOKEN_QUERY, // DesiredAccess
+ SepTokenObjectType, // ObjectType
+ PreviousMode, // AccessMode
+ (PVOID *)&Token, // Object
+ 0 // GrantedAccess
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ return( Status );
+ }
+
+ //
+ // It must be an impersonation token, and at impersonation
+ // level of Identification or above.
+ //
+
+ if (Token->TokenType != TokenImpersonation) {
+
+ ObDereferenceObject( Token );
+ return( STATUS_NO_IMPERSONATION_TOKEN );
+ }
+
+ if ( Token->ImpersonationLevel < SecurityIdentification ) {
+ ObDereferenceObject( Token );
+ return( STATUS_BAD_IMPERSONATION_LEVEL );
+ }
+
+ //
+ // Compare the DesiredAccess with the privileges in the
+ // passed token, and see if we can either satisfy the requested
+ // access with a privilege, or bomb out immediately because
+ // we don't have a privilege we need.
+ //
+
+ Status = SePrivilegePolicyCheck(
+ &DesiredAccess,
+ &PreviouslyGrantedAccess,
+ NULL,
+ (PACCESS_TOKEN)Token,
+ &Privileges,
+ PreviousMode
+ );
+
+ if (!NT_SUCCESS( Status )) {
+
+ ObDereferenceObject( Token );
+
+ try {
+
+ *AccessStatus = Status;
+ *GrantedAccess = 0;
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ return( GetExceptionCode() );
+ }
+
+ return( STATUS_SUCCESS );
+ }
+
+ //
+ // Make sure the passed privileges buffer is large enough for
+ // whatever we have to put into it.
+ //
+
+ if (Privileges != NULL) {
+
+ if ( ((ULONG)SepPrivilegeSetSize( Privileges )) > LocalPrivilegeSetLength ) {
+
+ ObDereferenceObject( Token );
+ SeFreePrivileges( Privileges );
+
+ try {
+
+ *PrivilegeSetLength = SepPrivilegeSetSize( Privileges );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ return( GetExceptionCode() );
+ }
+
+ return( STATUS_BUFFER_TOO_SMALL );
+
+ } else {
+
+ try {
+
+ RtlCopyMemory(
+ PrivilegeSet,
+ Privileges,
+ SepPrivilegeSetSize( Privileges )
+ );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ ObDereferenceObject( Token );
+ SeFreePrivileges( Privileges );
+ return( GetExceptionCode() );
+ }
+
+ }
+ SeFreePrivileges( Privileges );
+
+ } else {
+
+ //
+ // No privileges were used, construct an empty privilege set
+ //
+
+ if ( LocalPrivilegeSetLength < sizeof(PRIVILEGE_SET) ) {
+
+ ObDereferenceObject( Token );
+
+ try {
+
+ *PrivilegeSetLength = sizeof(PRIVILEGE_SET);
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ return( GetExceptionCode() );
+ }
+
+ return( STATUS_BUFFER_TOO_SMALL );
+ }
+
+ try {
+
+ PrivilegeSet->PrivilegeCount = 0;
+ PrivilegeSet->Control = 0;
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ ObDereferenceObject( Token );
+ return( GetExceptionCode() );
+
+ }
+
+ }
+
+
+ //
+ // Capture the passed security descriptor.
+ //
+ // SeCaptureSecurityDescriptor probes the input security descriptor,
+ // so we don't have to
+ //
+
+ Status = SeCaptureSecurityDescriptor (
+ SecurityDescriptor,
+ PreviousMode,
+ PagedPool,
+ FALSE,
+ &CapturedSecurityDescriptor
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ ObDereferenceObject( Token );
+
+ try {
+
+ *AccessStatus = Status;
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ return( GetExceptionCode() );
+
+ }
+
+ return(STATUS_SUCCESS);
+ }
+
+ if ( CapturedSecurityDescriptor == NULL ) {
+
+ //
+ // If there's no security descriptor, then we've been
+ // called without all the parameters we need.
+ // Return invalid security descriptor.
+ //
+
+ ObDereferenceObject( Token );
+
+ return(STATUS_INVALID_SECURITY_DESCR);
+
+ }
+
+ //
+ // A valid security descriptor must have an owner and a group
+ //
+
+ if ( SepOwnerAddrSecurityDescriptor(
+ (PISECURITY_DESCRIPTOR)CapturedSecurityDescriptor
+ ) == NULL ||
+ SepGroupAddrSecurityDescriptor(
+ (PISECURITY_DESCRIPTOR)CapturedSecurityDescriptor
+ ) == NULL ) {
+
+ SeReleaseSecurityDescriptor (
+ CapturedSecurityDescriptor,
+ PreviousMode,
+ FALSE
+ );
+
+ ObDereferenceObject( Token );
+
+ return( STATUS_INVALID_SECURITY_DESCR );
+ }
+
+
+ SeCaptureSubjectContext( &SubjectContext );
+
+ SepAcquireTokenReadLock( Token );
+
+ //
+ // If the user in the token is the owner of the object, we
+ // must automatically grant ReadControl and WriteDac access
+ // if desired. If the DesiredAccess mask is empty after
+ // these bits are turned off, we don't have to do any more
+ // access checking (ref section 4, DSA ACL Arch)
+ //
+
+
+ if ( DesiredAccess & (WRITE_DAC | READ_CONTROL | MAXIMUM_ALLOWED) ) {
+
+ if (SepTokenIsOwner( Token, CapturedSecurityDescriptor, TRUE )) {
+
+ if ( DesiredAccess & MAXIMUM_ALLOWED ) {
+
+ PreviouslyGrantedAccess |= (WRITE_DAC | READ_CONTROL);
+
+ } else {
+
+ PreviouslyGrantedAccess |= (DesiredAccess & (WRITE_DAC | READ_CONTROL));
+ }
+
+ DesiredAccess &= ~(WRITE_DAC | READ_CONTROL);
+ }
+
+ }
+
+ if (DesiredAccess == 0) {
+
+ LocalGrantedAccess = PreviouslyGrantedAccess;
+ Status = STATUS_SUCCESS;
+
+ } else {
+
+ SepAccessCheck (
+ CapturedSecurityDescriptor,
+ SubjectContext.PrimaryToken,
+ Token,
+ DesiredAccess,
+ &LocalGenericMapping,
+ PreviouslyGrantedAccess,
+ PreviousMode,
+ &LocalGrantedAccess,
+ NULL,
+ &Status
+ );
+
+
+ }
+
+ SepReleaseTokenReadLock( Token );
+
+ SeReleaseSubjectContext( &SubjectContext );
+
+ SeReleaseSecurityDescriptor (
+ CapturedSecurityDescriptor,
+ PreviousMode,
+ FALSE
+ );
+
+ ObDereferenceObject( Token );
+
+ try {
+
+ *AccessStatus = Status;
+ *GrantedAccess = LocalGrantedAccess;
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ return( GetExceptionCode() );
+
+ }
+
+ return(STATUS_SUCCESS);
+}
+
+
+
+VOID
+SeFreePrivileges(
+ IN PPRIVILEGE_SET Privileges
+ )
+
+/*++
+
+Routine Description:
+
+ This routine frees a privilege set returned by SeAccessCheck.
+
+Arguments:
+
+ Privileges - Supplies a pointer to the privilege set to be freed.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ ExFreePool( Privileges );
+}
+
+
+
+BOOLEAN
+SeAccessCheck (
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
+ IN BOOLEAN SubjectContextLocked,
+ IN ACCESS_MASK DesiredAccess,
+ IN ACCESS_MASK PreviouslyGrantedAccess,
+ OUT PPRIVILEGE_SET *Privileges OPTIONAL,
+ IN PGENERIC_MAPPING GenericMapping,
+ IN KPROCESSOR_MODE AccessMode,
+ OUT PACCESS_MASK GrantedAccess,
+ OUT PNTSTATUS AccessStatus
+ )
+
+/*++
+
+Routine Description:
+
+ See module abstract
+
+ This routine MAY perform tests for the following
+ privileges:
+
+ SeTakeOwnershipPrivilege
+ SeSecurityPrivilege
+
+ depending upon the accesses being requested.
+
+ This routine may also check to see if the subject is the owner
+ of the object (to grant WRITE_DAC access).
+
+Arguments:
+
+ SecurityDescriptor - Supplies the security descriptor protecting the
+ object being accessed
+
+ SubjectSecurityContext - A pointer to the subject's captured security
+ context
+
+ SubjectContextLocked - Supplies a flag indiciating whether or not
+ the user's subject context is locked, so that it does not have
+ to be locked again.
+
+ DesiredAccess - Supplies the access mask that the user is attempting to
+ acquire
+
+ PreviouslyGrantedAccess - Supplies any accesses that the user has
+ already been granted, for example, as a result of holding a
+ privilege.
+
+ Privileges - Supplies a pointer in which will be returned a privilege
+ set indicating any privileges that were used as part of the
+ access validation.
+
+ GenericMapping - Supplies the generic mapping associated with this
+ object type.
+
+ AccessMode - Supplies the access mode to be used in the check
+
+ GrantedAccess - Pointer to a returned access mask indicatating the
+ granted access
+
+ AccessStatus - Status value that may be returned indicating the
+ reason why access was denied. Routines should avoid hardcoding a
+ return value of STATUS_ACCESS_DENIED so that a different value can
+ be returned when mandatory access control is implemented.
+
+
+Return Value:
+
+ BOOLEAN - TRUE if access is allowed and FALSE otherwise
+
+--*/
+
+{
+ BOOLEAN Success;
+
+ PAGED_CODE();
+
+ if (AccessMode == KernelMode) {
+
+ if (DesiredAccess & MAXIMUM_ALLOWED) {
+
+ //
+ // Give him:
+ // GenericAll
+ // Anything else he asked for
+ //
+
+ *GrantedAccess = GenericMapping->GenericAll;
+ *GrantedAccess |= (DesiredAccess & ~MAXIMUM_ALLOWED);
+ *GrantedAccess |= PreviouslyGrantedAccess;
+
+ } else {
+
+ *GrantedAccess = DesiredAccess | PreviouslyGrantedAccess;
+ *AccessStatus = STATUS_SUCCESS;
+ return(TRUE);
+ }
+
+ }
+
+ //
+ // If the object doesn't have a security descriptor (and it's supposed
+ // to), return access denied.
+ //
+
+ if ( SecurityDescriptor == NULL) {
+
+ *AccessStatus = STATUS_ACCESS_DENIED;
+ return( FALSE );
+
+ }
+
+ //
+ // If we're impersonating a client, we have to be at impersonation level
+ // of SecurityImpersonation or above.
+ //
+
+ if ( (SubjectSecurityContext->ClientToken != NULL) &&
+ (SubjectSecurityContext->ImpersonationLevel < SecurityImpersonation)
+ ) {
+ *AccessStatus = STATUS_BAD_IMPERSONATION_LEVEL;
+ return( FALSE );
+ }
+
+ if ( DesiredAccess == 0 ) {
+
+ if ( PreviouslyGrantedAccess == 0 ) {
+ *AccessStatus = STATUS_ACCESS_DENIED;
+ return( FALSE );
+ }
+
+ *GrantedAccess = PreviouslyGrantedAccess;
+ *AccessStatus = STATUS_SUCCESS;
+ *Privileges = NULL;
+ return( TRUE );
+
+ }
+
+ SeAssertMappedCanonicalAccess( DesiredAccess );
+
+
+ //
+ // If the caller did not lock the subject context for us,
+ // lock it here to keep lower level routines from having
+ // to lock it.
+ //
+
+ if ( !SubjectContextLocked ) {
+ SeLockSubjectContext( SubjectSecurityContext );
+ }
+
+ //
+ // If the user in the token is the owner of the object, we
+ // must automatically grant ReadControl and WriteDac access
+ // if desired. If the DesiredAccess mask is empty after
+ // these bits are turned off, we don't have to do any more
+ // access checking (ref section 4, DSA ACL Arch)
+ //
+
+ if ( DesiredAccess & (WRITE_DAC | READ_CONTROL | MAXIMUM_ALLOWED) ) {
+
+ if ( SepTokenIsOwner(
+ EffectiveToken( SubjectSecurityContext ),
+ SecurityDescriptor,
+ TRUE
+ ) ) {
+
+ if ( DesiredAccess & MAXIMUM_ALLOWED ) {
+
+ PreviouslyGrantedAccess |= (WRITE_DAC | READ_CONTROL);
+
+ } else {
+
+ PreviouslyGrantedAccess |= (DesiredAccess & (WRITE_DAC | READ_CONTROL));
+ }
+
+ DesiredAccess &= ~(WRITE_DAC | READ_CONTROL);
+ }
+ }
+
+ if (DesiredAccess == 0) {
+
+ if ( !SubjectContextLocked ) {
+ SeUnlockSubjectContext( SubjectSecurityContext );
+ }
+
+ *GrantedAccess = PreviouslyGrantedAccess;
+ *AccessStatus = STATUS_SUCCESS;
+ return( TRUE );
+
+ } else {
+
+ Success = SepAccessCheck(
+ SecurityDescriptor,
+ SubjectSecurityContext->PrimaryToken,
+ SubjectSecurityContext->ClientToken,
+ DesiredAccess,
+ GenericMapping,
+ PreviouslyGrantedAccess,
+ AccessMode,
+ GrantedAccess,
+ Privileges,
+ AccessStatus
+ );
+#if DBG
+ if (!Success && SepShowAccessFail) {
+ DbgPrint("SE: Access check failed\n");
+ SepDumpSD = TRUE;
+ SepDumpSecurityDescriptor(
+ SecurityDescriptor,
+ "Input to SeAccessCheck\n"
+ );
+ SepDumpSD = FALSE;
+ SepDumpToken = TRUE;
+ SepDumpTokenInfo( EffectiveToken( SubjectSecurityContext ) );
+ SepDumpToken = FALSE;
+ }
+#endif
+
+ //
+ // If we locked it in this routine, unlock it before we
+ // leave.
+ //
+
+ if ( !SubjectContextLocked ) {
+ SeUnlockSubjectContext( SubjectSecurityContext );
+ }
+
+ return( Success );
+ }
+}
+
+
+BOOLEAN
+SeProxyAccessCheck (
+ IN PUNICODE_STRING Volume,
+ IN PUNICODE_STRING RelativePath,
+ IN BOOLEAN ContainerObject,
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
+ IN BOOLEAN SubjectContextLocked,
+ IN ACCESS_MASK DesiredAccess,
+ IN ACCESS_MASK PreviouslyGrantedAccess,
+ OUT PPRIVILEGE_SET *Privileges OPTIONAL,
+ IN PGENERIC_MAPPING GenericMapping,
+ IN KPROCESSOR_MODE AccessMode,
+ OUT PACCESS_MASK GrantedAccess,
+ OUT PNTSTATUS AccessStatus
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ Volume - Supplies the volume information of the file being opened.
+
+ RelativePath - The volume-relative path of the file being opened. The full path of the
+ file is the RelativePath appended to the Volume string.
+
+ ContainerObject - Indicates if the access is to a container object (TRUE), or a leaf object (FALSE).
+
+ SecurityDescriptor - Supplies the security descriptor protecting the
+ object being accessed
+
+ SubjectSecurityContext - A pointer to the subject's captured security
+ context
+
+ SubjectContextLocked - Supplies a flag indiciating whether or not
+ the user's subject context is locked, so that it does not have
+ to be locked again.
+
+ DesiredAccess - Supplies the access mask that the user is attempting to
+ acquire
+
+ PreviouslyGrantedAccess - Supplies any accesses that the user has
+ already been granted, for example, as a result of holding a
+ privilege.
+
+ Privileges - Supplies a pointer in which will be returned a privilege
+ set indicating any privileges that were used as part of the
+ access validation.
+
+ GenericMapping - Supplies the generic mapping associated with this
+ object type.
+
+ AccessMode - Supplies the access mode to be used in the check
+
+ GrantedAccess - Pointer to a returned access mask indicatating the
+ granted access
+
+ AccessStatus - Status value that may be returned indicating the
+ reason why access was denied. Routines should avoid hardcoding a
+ return value of STATUS_ACCESS_DENIED so that a different value can
+ be returned when mandatory access control is implemented.
+
+
+Return Value:
+
+ BOOLEAN - TRUE if access is allowed and FALSE otherwise
+
+--*/
+
+{
+ return SeAccessCheck (
+ SecurityDescriptor,
+ SubjectSecurityContext,
+ SubjectContextLocked,
+ DesiredAccess,
+ PreviouslyGrantedAccess,
+ Privileges,
+ GenericMapping,
+ AccessMode,
+ GrantedAccess,
+ AccessStatus
+ );
+}
+
+
+NTSTATUS
+SePrivilegePolicyCheck(
+ IN OUT PACCESS_MASK RemainingDesiredAccess,
+ IN OUT PACCESS_MASK PreviouslyGrantedAccess,
+ IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext OPTIONAL,
+ IN PACCESS_TOKEN ExplicitToken OPTIONAL,
+ OUT PPRIVILEGE_SET *PrivilegeSet,
+ IN KPROCESSOR_MODE PreviousMode
+ )
+
+/*++
+
+Routine Description:
+
+ This routine implements privilege policy by examining the bits in
+ a DesiredAccess mask and adjusting them based on privilege checks.
+
+ Currently, a request for ACCESS_SYSTEM_SECURITY may only be satisfied
+ by the caller having SeSecurityPrivilege. WRITE_OWNER may optionally
+ be satisfied via SeTakeOwnershipPrivilege.
+
+Arguments:
+
+ RemainingDesiredAccess - The desired access for the current operation.
+ Bits may be cleared in this if the subject has particular privileges.
+
+ PreviouslyGrantedAccess - Supplies an access mask describing any
+ accesses that have already been granted. Bits may be set in
+ here as a result of privilge checks.
+
+ SubjectSecurityContext - Optionally provides the subject's security
+ context.
+
+ ExplicitToken - Optionally provides the token to be examined.
+
+ PrivilegeSet - Supplies a pointer to a location in which will be
+ returned a pointer to a privilege set.
+
+ PreviousMode - The previous processor mode.
+
+Return Value:
+
+ STATUS_SUCCESS - Any access requests that could be satisfied via
+ privileges were done.
+
+ STATUS_PRIVILEGE_NOT_HELD - An access type was being requested that
+ requires a privilege, and the current subject did not have the
+ privilege.
+
+
+
+--*/
+
+{
+ BOOLEAN Success;
+ PTOKEN Token;
+ BOOLEAN WriteOwner = FALSE;
+ BOOLEAN SystemSecurity = FALSE;
+ ULONG PrivilegeNumber = 0;
+ ULONG PrivilegeCount = 0;
+ ULONG SizeRequired;
+
+ PAGED_CODE();
+
+ if (ARGUMENT_PRESENT( SubjectSecurityContext )) {
+
+ Token = (PTOKEN)EffectiveToken( SubjectSecurityContext );
+
+ } else {
+
+ Token = (PTOKEN)ExplicitToken;
+ }
+
+
+ if (*RemainingDesiredAccess & ACCESS_SYSTEM_SECURITY) {
+
+ Success = SepSinglePrivilegeCheck (
+ SeSecurityPrivilege,
+ Token,
+ PreviousMode
+ );
+
+ if (!Success) {
+
+ return( STATUS_PRIVILEGE_NOT_HELD );
+ }
+
+ PrivilegeCount++;
+ SystemSecurity = TRUE;
+
+ *RemainingDesiredAccess &= ~ACCESS_SYSTEM_SECURITY;
+ *PreviouslyGrantedAccess |= ACCESS_SYSTEM_SECURITY;
+ }
+
+ if (*RemainingDesiredAccess & WRITE_OWNER) {
+
+ Success = SepSinglePrivilegeCheck (
+ SeTakeOwnershipPrivilege,
+ Token,
+ PreviousMode
+ );
+
+ if (Success) {
+
+ PrivilegeCount++;
+ WriteOwner = TRUE;
+
+ *RemainingDesiredAccess &= ~WRITE_OWNER;
+ *PreviouslyGrantedAccess |= WRITE_OWNER;
+
+ }
+ }
+
+ if (PrivilegeCount > 0) {
+ SizeRequired = sizeof(PRIVILEGE_SET) +
+ (PrivilegeCount - ANYSIZE_ARRAY) *
+ (ULONG)sizeof(LUID_AND_ATTRIBUTES);
+
+ *PrivilegeSet = ExAllocatePoolWithTag( PagedPool, SizeRequired, 'rPeS' );
+
+ if ( *PrivilegeSet == NULL ) {
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ (*PrivilegeSet)->PrivilegeCount = PrivilegeCount;
+ (*PrivilegeSet)->Control = 0;
+
+ if (WriteOwner) {
+ (*PrivilegeSet)->Privilege[PrivilegeNumber].Luid = SeTakeOwnershipPrivilege;
+ (*PrivilegeSet)->Privilege[PrivilegeNumber].Attributes = SE_PRIVILEGE_USED_FOR_ACCESS;
+ PrivilegeNumber++;
+ }
+
+ if (SystemSecurity) {
+ (*PrivilegeSet)->Privilege[PrivilegeNumber].Luid = SeSecurityPrivilege;
+ (*PrivilegeSet)->Privilege[PrivilegeNumber].Attributes = SE_PRIVILEGE_USED_FOR_ACCESS;
+ }
+ }
+
+ return( STATUS_SUCCESS );
+}
+
+
+
+BOOLEAN
+SepTokenIsOwner(
+ IN PACCESS_TOKEN EffectiveToken,
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN BOOLEAN TokenLocked
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will determine of the Owner of the passed security descriptor
+ is in the passed token.
+
+
+Arguments:
+
+ Token - The token representing the current user.
+
+ SecurityDescriptor - The security descriptor for the object being
+ accessed.
+
+ TokenLocked - A boolean describing whether the caller has taken
+ a read lock for the token.
+
+
+Return Value:
+
+ TRUE - The user of the token is the owner of the object.
+
+ FALSE - The user of the token is not the owner of the object.
+
+--*/
+
+{
+ PSID Owner;
+ BOOLEAN rc;
+
+ PISECURITY_DESCRIPTOR ISecurityDescriptor;
+ PTOKEN Token;
+
+ PAGED_CODE();
+
+ ISecurityDescriptor = (PISECURITY_DESCRIPTOR)SecurityDescriptor;
+ Token = (PTOKEN)EffectiveToken;
+
+ if (!TokenLocked) {
+ SepAcquireTokenReadLock( Token );
+ }
+
+ Owner = SepOwnerAddrSecurityDescriptor( ISecurityDescriptor );
+ ASSERT( Owner != NULL );
+
+ rc = SepSidInToken( Token, Owner );
+
+ if (!TokenLocked) {
+ SepReleaseTokenReadLock( Token );
+ }
+
+ return( rc );
+}
+
+
+
+
+BOOLEAN
+SeFastTraverseCheck(
+ PSECURITY_DESCRIPTOR SecurityDescriptor,
+ ACCESS_MASK TraverseAccess,
+ KPROCESSOR_MODE AccessMode
+ )
+/*++
+
+Routine Description:
+
+ This routine will examine the DACL of the passed Security Descriptor
+ to see if WORLD has Traverse access. If so, no further access checking
+ is necessary.
+
+ Note that the SubjectContext for the client process does not have
+ to be locked to make this call, since it does not examine any data
+ structures in the Token.
+
+Arguments:
+
+ SecurityDescriptor - The Security Descriptor protecting the container
+ object being traversed.
+
+ TraverseAccess - Access mask describing Traverse access for this
+ object type.
+
+ AccessMode - Supplies the access mode to be used in the check
+
+
+Return Value:
+
+ TRUE - WORLD has Traverse access to this container. FALSE
+ otherwise.
+
+--*/
+
+{
+ PACL Dacl;
+ ULONG i;
+ PVOID Ace;
+ ULONG AceCount;
+
+ PAGED_CODE();
+
+ if ( AccessMode == KernelMode ) {
+ return( TRUE );
+ }
+
+ if (SecurityDescriptor == NULL) {
+ return( FALSE );
+ }
+
+ //
+ // See if there is a valid DACL in the passed Security Descriptor.
+ // No DACL, no security, all is granted.
+ //
+
+ Dacl = SepDaclAddrSecurityDescriptor( (PISECURITY_DESCRIPTOR)SecurityDescriptor );
+
+ //
+ // If the SE_DACL_PRESENT bit is not set, the object has no
+ // security, so all accesses are granted.
+ //
+ // Also grant all access if the Dacl is NULL.
+ //
+
+ if ( !SepAreControlBitsSet(
+ (PISECURITY_DESCRIPTOR)SecurityDescriptor, SE_DACL_PRESENT
+ )
+ || (Dacl == NULL)) {
+
+ return(TRUE);
+ }
+
+ //
+ // There is security on this object. If the DACL is empty,
+ // deny all access immediately
+ //
+
+ if ((AceCount = Dacl->AceCount) == 0) {
+
+ return( FALSE );
+ }
+
+ //
+ // There's stuff in the DACL, walk down the list and see
+ // if WORLD has been granted TraverseAccess
+ //
+
+ for ( i = 0, Ace = FirstAce( Dacl ) ;
+ i < AceCount ;
+ i++, Ace = NextAce( Ace )
+ ) {
+
+ if ( !(((PACE_HEADER)Ace)->AceFlags & INHERIT_ONLY_ACE)) {
+
+ if ( (((PACE_HEADER)Ace)->AceType == ACCESS_ALLOWED_ACE_TYPE) ) {
+
+ if ( (TraverseAccess & ((PACCESS_ALLOWED_ACE)Ace)->Mask) ) {
+
+ if ( RtlEqualSid( SeWorldSid, &((PACCESS_ALLOWED_ACE)Ace)->SidStart ) ) {
+
+ return( TRUE );
+ }
+ }
+
+ } else {
+
+ if ( (((PACE_HEADER)Ace)->AceType == ACCESS_DENIED_ACE_TYPE) ) {
+
+ if ( (TraverseAccess & ((PACCESS_DENIED_ACE)Ace)->Mask) ) {
+
+ if ( RtlEqualSid( SeWorldSid, &((PACCESS_DENIED_ACE)Ace)->SidStart ) ) {
+
+ return( FALSE );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return( FALSE );
+}
diff --git a/private/ntos/se/adt.h b/private/ntos/se/adt.h
new file mode 100644
index 000000000..9292b07d6
--- /dev/null
+++ b/private/ntos/se/adt.h
@@ -0,0 +1,189 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ adt.h
+
+Abstract:
+
+ Auditing - Defines, Fuction Prototypes and Macro Functions.
+ These are public to the Security Component only.
+
+Author:
+
+ Scott Birrell (ScottBi) January 17, 1991
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include <ntlsa.h>
+
+
+//////////////////////////////////////////////////////////////////////////////
+// //
+// Auditing Routines visible to rest of Security Component outside Auditing //
+// subcomponent. //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+
+/*++
+
+BOOLEAN
+SepAdtEventOnSuccess(
+ IN POLICY_AUDIT_EVENT_TYPE AuditEventType
+ )
+
+Routine Description:
+
+ This macro function checks if a given Audit Event Type is enabled for
+ Auditing of successful occurrences of the Event.
+
+Arguments:
+
+ AuditEventType - Specifies the type of the Audit Event to be checked.
+
+Return Value:
+
+ BOOLEAN - TRUE if the event type is enabled for auditing of successful
+ occurrences of the event, else FALSE
+--*/
+
+#define SepAdtEventOnSuccess(AuditEventType) \
+ (SepAdtState.EventAuditingOptions[AuditEventType] & \
+ POLICY_AUDIT_EVENT_SUCCESS)
+
+
+/*++
+
+BOOLEAN
+SepAdtEventOnFailure(
+ IN POLICY_AUDIT_EVENT_TYPE AuditEventType
+ )
+
+Routine Description:
+
+ This macro function checks if a given Audit Event Type is enabled for
+ Auditing of unsuccessful attempts to cause an event of the given type
+ to occur.
+
+Arguments:
+
+ AuditEventType - Specifies the type of the Audit Event to be checked.
+
+Return Value:
+
+ BOOLEAN - TRUE if the event type is enabled for auditing of unsuccessful
+ attempts to make the event type occur, else FALSE
+--*/
+
+#define SepAdtEventOnFailure(AuditEventType) \
+ (SepAdtState.EventAuditingOptions[AuditEventType] & \
+ POLICY_AUDIT_EVENT_FAILURE)
+
+/*++
+
+BOOLEAN
+SepAdtAuditingEvent(
+ IN POLICY_AUDIT_EVENT_TYPE AuditEventType
+ )
+
+Routine Description:
+
+ This macro function checks if a given Audit Event Type is enabled for
+ Auditing.
+
+Arguments:
+
+ AuditEventType - Specifies the type of the Audit Event to be checked.
+
+Return Value:
+
+ BOOLEAN - TRUE if the event type is enabled for auditing, else FALSE.
+
+--*/
+
+#define SepAdtAuditingEvent(AuditEventType) \
+ (SepAdtEventOnSuccess(AuditEventType) || \
+ (SepAdtEventOnFailure(AuditEventType))
+
+/*++
+
+BOOLEAN
+SepAdtAuditingEnabled()
+
+Routine Description:
+
+ This macro function tests if auditing is enabled.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ BOOLEAN - TRUE if auditing is enabled, else FALSE
+
+--*/
+
+#define SepAdtAuditingEnabled() (SepAdtState.AuditingMode == TRUE)
+
+
+/*++
+
+BOOLEAN
+SepAdtAuditingDisabled()
+
+Routine Description:
+
+ This macro function tests if auditing is disabled.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ BOOLEAN - TRUE if auditing is disabled, else FALSE
+
+--*/
+
+#define SepAdtAuditingDisabled() (!SepAdtAuditingEnabled)
+
+//
+// Audit Event Information array. Although internal to the Auditing
+// Subcomponent, this structure is exported to all of Security so that the
+// above macro functions can be used to access it efficiently from there.
+//
+
+extern POLICY_AUDIT_EVENTS_INFO SepAdtState;
+
+BOOLEAN
+SepAdtInitializePhase0();
+
+BOOLEAN
+SepAdtInitializePhase1();
+
+//VOID
+//SepAdtLogAuditRecord(
+// IN POLICY_AUDIT_EVENT_TYPE AuditEventType,
+// IN PVOID AuditInformation
+// );
+
+VOID
+SepAdtLogAuditRecord(
+ IN PSE_ADT_PARAMETER_ARRAY AuditParameters
+ );
+
+NTSTATUS
+SepAdtCopyToLsaSharedMemory(
+ IN HANDLE LsaProcessHandle,
+ IN PVOID Buffer,
+ IN ULONG BufferLength,
+ OUT PVOID *LsaBufferAddress
+ );
diff --git a/private/ntos/se/adtevent.c b/private/ntos/se/adtevent.c
new file mode 100644
index 000000000..293fa8779
--- /dev/null
+++ b/private/ntos/se/adtevent.c
@@ -0,0 +1,31 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ adtevent.c - Audit Event Management
+
+Abstract:
+
+ This module contains functions that update the Audit Event Information.
+
+Author:
+
+ Scott Birrell (ScottBi) November 14, 1991
+
+Environment:
+
+ Kernel mode only.
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntos.h>
+#include "rmp.h"
+#include "adt.h"
+#include "adtp.h"
+
+
diff --git a/private/ntos/se/adtinit.c b/private/ntos/se/adtinit.c
new file mode 100644
index 000000000..21cb34fe2
--- /dev/null
+++ b/private/ntos/se/adtinit.c
@@ -0,0 +1,422 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ adtinit.c
+
+Abstract:
+
+ Auditing - Initialization Routines
+
+Author:
+
+ Scott Birrell (ScottBi) November 12, 1991
+
+Environment:
+
+ Kernel Mode only
+
+Revision History:
+
+--*/
+
+
+#include <nt.h>
+#include "sep.h"
+#include "adt.h"
+#include "adtp.h"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE,SepAdtInitializePhase1)
+#pragma alloc_text(PAGE,SepAdtInitializeBounds)
+#pragma alloc_text(PAGE,SepAdtValidateAuditBounds)
+#endif
+
+
+
+BOOLEAN
+SepAdtValidateAuditBounds(
+ ULONG Upper,
+ ULONG Lower
+ )
+
+/*++
+
+Routine Description:
+
+ Examines the audit queue high and low water mark values and performs
+ a general sanity check on them.
+
+Arguments:
+
+ Upper - High water mark.
+
+ Lower - Low water mark.
+
+Return Value:
+
+ TRUE - values are acceptable.
+
+ FALSE - values are unacceptable.
+
+
+--*/
+
+{
+ PAGED_CODE();
+
+ if ( Lower >= Upper ) {
+ return( FALSE );
+ }
+
+ if ( Lower < 16 ) {
+ return( FALSE );
+ }
+
+ if ( (Upper - Lower) < 16 ) {
+ return( FALSE );
+ }
+
+ return( TRUE );
+}
+
+BOOLEAN
+SepAdtInitializePhase1()
+
+/*++
+
+Routine Description:
+
+ This function performs Phase 1 Initialization for the Auditing subcomponent
+ of Security. Global variables are initialized within the Nt Executive
+ and Auditing is turned off.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ BOOLEAN - TRUE if Auditing has been initialized correctly, else FALSE.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ RtlInitUnicodeString( &SeSubsystemName, L"Security" );
+
+ return( TRUE );
+}
+
+
+
+
+VOID
+SepAdtInitializeBounds(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Queries the registry for the high and low water mark values for the
+ audit log. If they are not found or are unacceptable, returns without
+ modifying the current values, which are statically initialized.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ HANDLE KeyHandle;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ UNICODE_STRING KeyName;
+ UNICODE_STRING ValueName;
+ NTSTATUS Status;
+ PSEP_AUDIT_BOUNDS AuditBounds;
+ PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation;
+ ULONG Length;
+
+ PAGED_CODE();
+
+ //
+ // Get the high and low water marks out of the registry.
+ //
+
+ RtlInitUnicodeString( &KeyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Lsa");
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &KeyName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ Status = NtOpenKey(
+ &KeyHandle,
+ KEY_QUERY_VALUE,
+ &ObjectAttributes
+ );
+
+ if (!NT_SUCCESS( Status )) {
+
+ //
+ // Didn't work, take the defaults
+ //
+
+ return;
+ }
+
+ RtlInitUnicodeString( &ValueName, L"Bounds");
+
+ Length = sizeof( KEY_VALUE_PARTIAL_INFORMATION ) - sizeof( UCHAR ) + sizeof( SEP_AUDIT_BOUNDS );
+
+ KeyValueInformation = ExAllocatePool( PagedPool, Length );
+
+ if ( KeyValueInformation == NULL ) {
+
+ NtClose( KeyHandle );
+ return;
+ }
+
+ Status = NtQueryValueKey(
+ KeyHandle,
+ &ValueName,
+ KeyValuePartialInformation,
+ (PVOID)KeyValueInformation,
+ Length,
+ &Length
+ );
+
+ NtClose( KeyHandle );
+
+ if (!NT_SUCCESS( Status )) {
+
+ ExFreePool( KeyValueInformation );
+ return;
+ }
+
+
+ AuditBounds = (PSEP_AUDIT_BOUNDS) &KeyValueInformation->Data;
+
+ //
+ // Sanity check what we got back
+ //
+
+ if(!SepAdtValidateAuditBounds( AuditBounds->UpperBound, AuditBounds->LowerBound )) {
+
+ //
+ // The values we got back are not to our liking. Use the defaults.
+ //
+
+ ExFreePool( KeyValueInformation );
+ return;
+ }
+
+ //
+ // Take what we got from the registry.
+ //
+
+ SepAdtMaxListLength = AuditBounds->UpperBound;
+ SepAdtMinListLength = AuditBounds->LowerBound;
+
+ ExFreePool( KeyValueInformation );
+
+ return;
+}
+
+
+
+NTSTATUS
+SepAdtInitializeCrashOnFail(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Reads the registry to see if the user has told us to crash if an audit fails.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ STATUS_SUCCESS
+
+--*/
+
+{
+ HANDLE KeyHandle;
+ NTSTATUS Status;
+ NTSTATUS TmpStatus;
+ OBJECT_ATTRIBUTES Obja;
+ ULONG ResultLength;
+ UNICODE_STRING KeyName;
+ UNICODE_STRING ValueName;
+ CHAR KeyInfo[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(BOOLEAN)];
+ PKEY_VALUE_PARTIAL_INFORMATION pKeyInfo;
+
+ SepCrashOnAuditFail = FALSE;
+
+ //
+ // Check the value of the CrashOnAudit flag in the registry.
+ //
+
+ RtlInitUnicodeString( &KeyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Lsa");
+
+ InitializeObjectAttributes( &Obja,
+ &KeyName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ Status = NtOpenKey(
+ &KeyHandle,
+ KEY_QUERY_VALUE | KEY_SET_VALUE,
+ &Obja
+ );
+
+ if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
+ return( STATUS_SUCCESS );
+ }
+
+ RtlInitUnicodeString( &ValueName, CRASH_ON_AUDIT_FAIL_VALUE );
+
+ Status = NtQueryValueKey(
+ KeyHandle,
+ &ValueName,
+ KeyValuePartialInformation,
+ KeyInfo,
+ sizeof(KeyInfo),
+ &ResultLength
+ );
+
+ TmpStatus = NtClose(KeyHandle);
+ ASSERT(NT_SUCCESS(TmpStatus));
+
+ //
+ // If the key isn't there, don't turn on CrashOnFail.
+ //
+
+ if (NT_SUCCESS( Status )) {
+
+ pKeyInfo = (PKEY_VALUE_PARTIAL_INFORMATION)KeyInfo;
+ if ((UCHAR) *(pKeyInfo->Data) == LSAP_CRASH_ON_AUDIT_FAIL) {
+ SepCrashOnAuditFail = TRUE;
+ }
+ }
+
+ return( STATUS_SUCCESS );
+}
+
+
+BOOLEAN
+SepAdtInitializePrivilegeAuditing(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Checks to see if there is an entry in the registry telling us to do full privilege auditing
+ (which currently means audit everything we normall audit, plus backup and restore privileges).
+
+Arguments:
+
+ None
+
+Return Value:
+
+ BOOLEAN - TRUE if Auditing has been initialized correctly, else FALSE.
+
+--*/
+
+{
+ HANDLE KeyHandle;
+ NTSTATUS Status;
+ NTSTATUS TmpStatus;
+ OBJECT_ATTRIBUTES Obja;
+ ULONG ResultLength;
+ UNICODE_STRING KeyName;
+ UNICODE_STRING ValueName;
+ CHAR KeyInfo[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(BOOLEAN)];
+ PKEY_VALUE_PARTIAL_INFORMATION pKeyInfo;
+ BOOLEAN Verbose;
+
+ PAGED_CODE();
+
+ //
+ // Query the registry to set up the privilege auditing filter.
+ //
+
+ RtlInitUnicodeString( &KeyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Lsa");
+
+ InitializeObjectAttributes( &Obja,
+ &KeyName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ Status = NtOpenKey(
+ &KeyHandle,
+ KEY_QUERY_VALUE | KEY_SET_VALUE,
+ &Obja
+ );
+
+
+ if (!NT_SUCCESS( Status )) {
+
+ if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
+
+ return ( SepInitializePrivilegeFilter( FALSE ));
+
+ } else {
+
+ return( FALSE );
+ }
+ }
+
+ RtlInitUnicodeString( &ValueName, FULL_PRIVILEGE_AUDITING );
+
+ Status = NtQueryValueKey(
+ KeyHandle,
+ &ValueName,
+ KeyValuePartialInformation,
+ KeyInfo,
+ sizeof(KeyInfo),
+ &ResultLength
+ );
+
+ TmpStatus = NtClose(KeyHandle);
+ ASSERT(NT_SUCCESS(TmpStatus));
+
+ if (!NT_SUCCESS( Status )) {
+
+ Verbose = FALSE;
+
+ } else {
+
+ pKeyInfo = (PKEY_VALUE_PARTIAL_INFORMATION)KeyInfo;
+ Verbose = (BOOLEAN) *(pKeyInfo->Data);
+ }
+
+ return ( SepInitializePrivilegeFilter( Verbose ));
+}
diff --git a/private/ntos/se/adtlog.c b/private/ntos/se/adtlog.c
new file mode 100644
index 000000000..48fbbf3f7
--- /dev/null
+++ b/private/ntos/se/adtlog.c
@@ -0,0 +1,791 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ adtlog.c
+
+Abstract:
+
+ Auditing - Audit Record Queuing and Logging Routines
+
+ This file contains functions that construct Audit Records in self-
+ relative form from supplied information, enqueue/dequeue them and
+ write them to the log.
+
+Author:
+
+ Scott Birrell (ScottBi) November 8, 1991
+
+Environment:
+
+ Kernel Mode only
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntos.h>
+#include <zwapi.h>
+#include "sep.h"
+#include "adt.h"
+#include "adtp.h"
+#include "rmp.h"
+
+
+
+#ifdef ALLOC_PRAGMA
+
+#pragma alloc_text(PAGE,SepAdtCopyToLsaSharedMemory)
+#pragma alloc_text(PAGE,SepAdtLogAuditRecord)
+#pragma alloc_text(PAGE,SepAdtMarshallAuditRecord)
+#pragma alloc_text(PAGE,SepAdtSetAuditLogInformation)
+#pragma alloc_text(PAGE,SepDequeueWorkItem)
+#pragma alloc_text(PAGE,SepQueueWorkItem)
+
+#endif
+
+VOID
+SepAdtLogAuditRecord(
+ IN PSE_ADT_PARAMETER_ARRAY AuditParameters
+ )
+
+/*++
+
+Routine Description:
+
+ This function manages the logging of Audit Records. It provides the
+ single interface to the Audit Logging component from the Audit/Alarm
+ generation routines. The function constructs an Audit Record in
+ self-relative format from the information provided and appends it to
+ the Audit Record Queue, a doubly-linked list of Audit Records awaiting
+ output to the Audit Log. A dedicated thread reads this queue, writing
+ Audit Records to the Audit Log and removing them from the Audit Queue.
+
+Arguments:
+
+ AuditEventType - Specifies the type of the Audit Event described by
+ the audit information provided.
+
+ AuditInformation - Pointer to buffer containing captured auditing
+ information related to an Audit Event of type AuditEventType.
+
+Return Value:
+
+ STATUS_SUCCESS
+ STATUS_UNSUCCESSFUL - Audit record was not queued
+ STATUS_INSUFFICIENT_RESOURCES - unable to allocate heap
+
+--*/
+
+{
+ NTSTATUS Status;
+ PSEP_LSA_WORK_ITEM AuditWorkItem;
+
+ PAGED_CODE();
+
+ AuditWorkItem = ExAllocatePoolWithTag( PagedPool, sizeof( SEP_LSA_WORK_ITEM ), 'iAeS' );
+
+ if ( AuditWorkItem == NULL ) {
+
+ SepAuditFailed();
+ return;
+ }
+
+ AuditWorkItem->Tag = SepAuditRecord;
+ AuditWorkItem->CommandNumber = LsapWriteAuditMessageCommand;
+ AuditWorkItem->ReplyBuffer = NULL;
+ AuditWorkItem->ReplyBufferLength = 0;
+ AuditWorkItem->CleanupFunction = NULL;
+
+ //
+ // Build an Audit record in self-relative format from the supplied
+ // Audit Information.
+ //
+
+ Status = SepAdtMarshallAuditRecord(
+ AuditParameters,
+ (PSE_ADT_PARAMETER_ARRAY *) &AuditWorkItem->CommandParams.BaseAddress,
+ &AuditWorkItem->CommandParamsMemoryType
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Extract the length of the Audit Record. Store it as the length
+ // of the Command Parameters buffer.
+ //
+
+ AuditWorkItem->CommandParamsLength =
+ ((PSE_ADT_PARAMETER_ARRAY) AuditWorkItem->CommandParams.BaseAddress)->Length;
+
+ //
+ // If we're going to crash on a discarded audit, ignore the queue bounds
+ // check and force the item onto the queue.
+ //
+
+ if (!SepQueueWorkItem( AuditWorkItem, (BOOLEAN)(SepCrashOnAuditFail ? TRUE : FALSE) )) {
+
+ ExFreePool( AuditWorkItem->CommandParams.BaseAddress );
+ ExFreePool( AuditWorkItem );
+
+ //
+ // We failed to put the record on the queue. Take whatever action is
+ // appropriate.
+ //
+
+ SepAuditFailed();
+ }
+
+ } else {
+
+ ExFreePool( AuditWorkItem );
+ SepAuditFailed();
+ }
+}
+
+
+
+VOID
+SepAuditFailed(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Bugchecks the system due to a missed audit (optional requirement
+ for C2 compliance).
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES Obja;
+ HANDLE KeyHandle;
+ UNICODE_STRING KeyName;
+ UNICODE_STRING ValueName;
+ UCHAR NewValue;
+
+ ASSERT(sizeof(UCHAR) == sizeof(BOOLEAN));
+
+ if (!SepCrashOnAuditFail) {
+ return;
+ }
+
+ //
+ // Turn off flag in the registry that controls crashing on audit failure
+ //
+
+ RtlInitUnicodeString( &KeyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Lsa");
+
+ InitializeObjectAttributes( &Obja,
+ &KeyName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+ do {
+
+ Status = ZwOpenKey(
+ &KeyHandle,
+ KEY_SET_VALUE,
+ &Obja
+ );
+
+ } while ((Status == STATUS_INSUFFICIENT_RESOURCES) || (Status == STATUS_NO_MEMORY));
+
+ //
+ // If the LSA key isn't there, he's got big problems. But don't crash.
+ //
+
+ if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
+ SepCrashOnAuditFail = FALSE;
+ return;
+ }
+
+ if (!NT_SUCCESS( Status )) {
+ goto bugcheck;
+ }
+
+ RtlInitUnicodeString( &ValueName, CRASH_ON_AUDIT_FAIL_VALUE );
+
+ NewValue = LSAP_ALLOW_ADIMIN_LOGONS_ONLY;
+
+ do {
+
+ Status = ZwSetValueKey( KeyHandle,
+ &ValueName,
+ 0,
+ REG_NONE,
+ &NewValue,
+ sizeof(UCHAR)
+ );
+
+ } while ((Status == STATUS_INSUFFICIENT_RESOURCES) || (Status == STATUS_NO_MEMORY));
+ ASSERT(NT_SUCCESS(Status));
+
+ if (!NT_SUCCESS( Status )) {
+ goto bugcheck;
+ }
+
+ do {
+
+ Status = ZwFlushKey( KeyHandle );
+
+ } while ((Status == STATUS_INSUFFICIENT_RESOURCES) || (Status == STATUS_NO_MEMORY));
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // go boom.
+ //
+
+bugcheck:
+
+ KeBugCheck(AUDIT_FAILURE);
+}
+
+
+
+NTSTATUS
+SepAdtMarshallAuditRecord(
+ IN PSE_ADT_PARAMETER_ARRAY AuditParameters,
+ OUT PSE_ADT_PARAMETER_ARRAY *MarshalledAuditParameters,
+ OUT PSEP_RM_LSA_MEMORY_TYPE RecordMemoryType
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will take an AuditParamters structure and create
+ a new AuditParameters structure that is suitable for sending
+ to LSA. It will be in self-relative form and allocated as
+ a single chunk of memory.
+
+Arguments:
+
+
+ AuditParameters - A filled in set of AuditParameters to be marshalled.
+
+ MarshalledAuditParameters - Returns a pointer to a block of heap memory
+ containing the passed AuditParameters in self-relative form suitable
+ for passing to LSA.
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ ULONG i;
+ ULONG TotalSize = sizeof( SE_ADT_PARAMETER_ARRAY );
+ PUNICODE_STRING TargetString;
+ PCHAR Base;
+ ULONG BaseIncr;
+
+ PAGED_CODE();
+
+ //
+ // Calculate the total size required for the passed AuditParameters
+ // block. This calculation will probably be an overestimate of the
+ // amount of space needed, because data smaller that 2 dwords will
+ // be stored directly in the parameters structure, but their length
+ // will be counted here anyway. The overestimate can't be more than
+ // 24 dwords, and will never even approach that amount, so it isn't
+ // worth the time it would take to avoid it.
+ //
+
+ for (i=0; i<AuditParameters->ParameterCount; i++) {
+ TotalSize += (ULONG)LongAlign(AuditParameters->Parameters[i].Length);
+ }
+
+ //
+ // Allocate a big enough block of memory to hold everything.
+ // If it fails, quietly abort, since there isn't much else we
+ // can do.
+ //
+
+ *MarshalledAuditParameters = ExAllocatePoolWithTag( PagedPool, TotalSize, 'pAeS' );
+
+ if (*MarshalledAuditParameters == NULL) {
+
+ *RecordMemoryType = SepRmNoMemory;
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+
+ *RecordMemoryType = SepRmPagedPoolMemory;
+
+ RtlMoveMemory (
+ *MarshalledAuditParameters,
+ AuditParameters,
+ sizeof( SE_ADT_PARAMETER_ARRAY )
+ );
+
+ (*MarshalledAuditParameters)->Length = TotalSize;
+ (*MarshalledAuditParameters)->Flags = SE_ADT_PARAMETERS_SELF_RELATIVE;
+
+ //
+ // Start walking down the list of parameters and marshall them
+ // into the target buffer.
+ //
+
+ Base = (PCHAR) ((PCHAR)(*MarshalledAuditParameters) + sizeof( SE_ADT_PARAMETER_ARRAY ));
+
+ for (i=0; i<AuditParameters->ParameterCount; i++) {
+
+
+ switch (AuditParameters->Parameters[i].Type) {
+ case SeAdtParmTypeNone:
+ case SeAdtParmTypeUlong:
+ case SeAdtParmTypeLogonId:
+ case SeAdtParmTypeNoLogonId:
+ case SeAdtParmTypeAccessMask:
+ {
+ //
+ // Nothing to do for this
+ //
+
+ break;
+
+ }
+ case SeAdtParmTypeString:
+ case SeAdtParmTypeFileSpec:
+ {
+ PUNICODE_STRING SourceString;
+ //
+ // We must copy the body of the unicode string
+ // and then copy the body of the string. Pointers
+ // must be turned into offsets.
+
+ TargetString = (PUNICODE_STRING)Base;
+
+ SourceString = AuditParameters->Parameters[i].Address;
+
+ *TargetString = *SourceString;
+
+ //
+ // Reset the data pointer in the output parameters to
+ // 'point' to the new string structure.
+ //
+
+ (*MarshalledAuditParameters)->Parameters[i].Address = Base - (ULONG)(*MarshalledAuditParameters);
+
+ Base += sizeof( UNICODE_STRING );
+
+ RtlMoveMemory( Base, SourceString->Buffer, SourceString->Length );
+
+ //
+ // Make the string buffer in the target string point to where we
+ // just copied the data.
+ //
+
+ TargetString->Buffer = (PWSTR)(Base - (ULONG)(*MarshalledAuditParameters));
+
+ BaseIncr = (ULONG)LongAlign(SourceString->Length);
+
+ Base += BaseIncr;
+
+ break;
+ }
+ case SeAdtParmTypeSid:
+ {
+ PSID TargetSid = (PSID) Base;
+ PSID SourceSid = AuditParameters->Parameters[i].Address;
+
+ //
+ // Copy the Sid into the output buffer
+ //
+
+ RtlMoveMemory( TargetSid, SourceSid, RtlLengthSid( SourceSid ) );
+
+ //
+ // Reset the 'address' of the Sid to be its offset in the
+ // buffer.
+ //
+
+ (*MarshalledAuditParameters)->Parameters[i].Address = Base - (ULONG)(*MarshalledAuditParameters);
+
+ BaseIncr = (ULONG)LongAlign(RtlLengthSid( SourceSid ));
+
+ Base += BaseIncr;
+
+ break;
+ }
+ case SeAdtParmTypePrivs:
+ {
+ PPRIVILEGE_SET TargetPrivileges = (PPRIVILEGE_SET) Base;
+ PPRIVILEGE_SET SourcePrivileges = AuditParameters->Parameters[i].Address;
+
+ RtlCopyMemory( TargetPrivileges, SourcePrivileges, SepPrivilegeSetSize( SourcePrivileges ));
+
+ (*MarshalledAuditParameters)->Parameters[i].Address = Base - (ULONG)(*MarshalledAuditParameters);
+
+ BaseIncr = (ULONG)LongAlign(SepPrivilegeSetSize( SourcePrivileges ));
+
+ Base += BaseIncr;
+
+ break;
+ }
+ default:
+ {
+ //
+ // We got passed junk, complain.
+ //
+
+ ASSERT( FALSE );
+ break;
+ }
+ }
+ }
+
+ return( STATUS_SUCCESS );
+}
+
+
+VOID
+SepAdtSetAuditLogInformation(
+ IN PPOLICY_AUDIT_LOG_INFO AuditLogInformation
+ )
+
+/*++
+
+Routine Description:
+
+ This function stores Audit Log Information in the Reference Monitor's
+ in-memory database. This information contains parameters such as Audit
+ Log size etc. It is the caller's responsibility to ensure that the
+ supplied information is valid.
+
+ NOTE: After initialization, this function is only called by the LSA
+ via a Reference Monitor command. This is a necessary restriction
+ because the Audit Log Information stored in the LSA Database must
+ remain in sync
+
+Arguments:
+
+ AuditLogInformation - Pointer to Audit Log Information structure.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ //
+ // Acquire Reference Monitor Database write lock.
+ //
+
+ SepRmAcquireDbWriteLock();
+
+ //
+ // Write the information
+ //
+
+ SepAdtLogInformation = *AuditLogInformation;
+
+ //
+ // Release Reference Monitor Database write lock
+ //
+
+ SepRmReleaseDbWriteLock();
+}
+
+
+
+NTSTATUS
+SepAdtCopyToLsaSharedMemory(
+ IN HANDLE LsaProcessHandle,
+ IN PVOID Buffer,
+ IN ULONG BufferLength,
+ OUT PVOID *LsaBufferAddress
+ )
+
+/*++
+
+Routine Description:
+
+ This function allocates memory shared with the LSA and optionally copies
+ a given buffer to it.
+
+Arguments:
+
+ LsaProcessHandle - Specifies a handle to the Lsa Process.
+
+ Buffer - Pointer to the buffer to be copied.
+
+ BufferLength - Length of buffer.
+
+ LsaBufferAddress - Receives the address of the buffer valid in the
+ Lsa process context.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ Result codes returned by called routines.
+--*/
+
+{
+ NTSTATUS Status, SecondaryStatus;
+ PVOID OutputLsaBufferAddress = NULL;
+ ULONG RegionSize = BufferLength;
+
+ PAGED_CODE();
+
+ Status = ZwAllocateVirtualMemory(
+ LsaProcessHandle,
+ &OutputLsaBufferAddress,
+ 0,
+ &RegionSize,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CopyToLsaSharedMemoryError;
+ }
+
+ Status = ZwWriteVirtualMemory(
+ LsaProcessHandle,
+ OutputLsaBufferAddress,
+ Buffer,
+ BufferLength,
+ NULL
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto CopyToLsaSharedMemoryError;
+ }
+
+ *LsaBufferAddress = OutputLsaBufferAddress;
+ return(Status);
+
+CopyToLsaSharedMemoryError:
+
+ //
+ // If we allocated memory, free it.
+ //
+
+ if (OutputLsaBufferAddress != NULL) {
+
+ RegionSize = 0;
+
+ SecondaryStatus = ZwFreeVirtualMemory(
+ LsaProcessHandle,
+ &OutputLsaBufferAddress,
+ &RegionSize,
+ MEM_RELEASE
+ );
+
+ ASSERT(NT_SUCCESS(SecondaryStatus));
+
+ OutputLsaBufferAddress = NULL;
+ }
+
+ return(Status);
+}
+
+
+BOOLEAN
+SepQueueWorkItem(
+ IN PSEP_LSA_WORK_ITEM LsaWorkItem,
+ IN BOOLEAN ForceQueue
+ )
+
+/*++
+
+Routine Description:
+
+ Puts the passed work item on the queue to be passed to LSA,
+ and returns the state of the queue upon arrival.
+
+Arguments:
+
+ LsaWorkItem - Pointer to the work item to be queued.
+
+ ForceQueue - Indicate that this item is not to be discarded
+ due because of a full queue.
+
+Return Value:
+
+ TRUE - The item was successfully queued.
+
+ FALSE - The item was not queued and must be discarded.
+
+--*/
+
+{
+ BOOLEAN rc = TRUE;
+
+ PAGED_CODE();
+
+#if 0
+
+ DbgPrint("Queueing an audit\n");
+
+#endif
+
+ SepLockLsaQueue();
+
+ if (SepAdtDiscardingAudits && !ForceQueue) {
+
+ if (SepAdtCurrentListLength < SepAdtMinListLength) {
+
+ //
+ // We need to generate an audit saying how many audits we've
+ // discarded.
+ //
+ // Since we have the mutex protecting the Audit queue, we don't
+ // have to worry about anyone coming along and logging an
+ // audit. But *we* can, since a mutex may be acquired recursively.
+ //
+ // Since we are so protected, turn off the SepAdtDiscardingAudits
+ // flag here so that we don't come through this path again.
+ //
+
+ SepAdtDiscardingAudits = FALSE;
+
+ SepAdtGenerateDiscardAudit();
+#if 0
+ DbgPrint("Auditing resumed\n");
+#endif
+
+ //
+ // We must assume that that worked, so clear the discard count.
+ //
+
+ SepAdtCountEventsDiscarded = 0;
+
+ //
+ // Our 'audits discarded' audit is now on the queue,
+ // continue logging the one we started with.
+ //
+
+ } else {
+
+ //
+ // We are not yet below our low water mark. Toss
+ // this audit and increment the discard count.
+ //
+
+ SepAdtCountEventsDiscarded++;
+ rc = FALSE;
+ goto Exit;
+ }
+ }
+
+ if (SepAdtCurrentListLength < SepAdtMaxListLength || ForceQueue) {
+
+ InsertTailList(&SepLsaQueue, &LsaWorkItem->List);
+
+ if (++SepAdtCurrentListLength == 1) {
+
+#if 0
+ DbgPrint("Queueing a work item\n");
+#endif
+
+ ExInitializeWorkItem( &SepExWorkItem.WorkItem,
+ (PWORKER_THREAD_ROUTINE) SepRmCallLsa,
+ &SepExWorkItem
+ );
+
+ ExQueueWorkItem( &SepExWorkItem.WorkItem, DelayedWorkQueue );
+ }
+
+ } else {
+
+ //
+ // There is no room for this audit on the queue,
+ // so change our state to 'discarding' and tell
+ // the caller to toss this audit.
+ //
+
+ SepAdtDiscardingAudits = TRUE;
+
+#if 0
+ DbgPrint("Starting to discard audits\n");
+#endif
+
+ rc = FALSE;
+ }
+
+Exit:
+
+ SepUnlockLsaQueue();
+ return( rc );
+}
+
+
+
+PSEP_LSA_WORK_ITEM
+SepDequeueWorkItem(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Removes the top element of the SepLsaQueue and returns the
+ next element if there is one, NULL otherwise.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ A pointer to the next SEP_LSA_WORK_ITEM, or NULL.
+
+--*/
+
+{
+ PSEP_LSA_WORK_ITEM OldWorkQueueItem;
+
+ PAGED_CODE();
+
+ SepLockLsaQueue();
+
+ OldWorkQueueItem = (PSEP_LSA_WORK_ITEM)RemoveHeadList(&SepLsaQueue);
+ OldWorkQueueItem->List.Flink = NULL;
+ ExFreePool( OldWorkQueueItem );
+
+ SepAdtCurrentListLength--;
+
+#if 0
+ DbgPrint("Removing item\n");
+#endif
+
+ if (IsListEmpty( &SepLsaQueue )) {
+
+ SepUnlockLsaQueue();
+ return( NULL );
+ }
+
+ //
+ // We know there's something on the queue now, so we
+ // can unlock it.
+ //
+
+ SepUnlockLsaQueue();
+ return((PSEP_LSA_WORK_ITEM)(&SepLsaQueue)->Flink);
+}
diff --git a/private/ntos/se/adtp.h b/private/ntos/se/adtp.h
new file mode 100644
index 000000000..bfa41c889
--- /dev/null
+++ b/private/ntos/se/adtp.h
@@ -0,0 +1,288 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ adtp.h
+
+Abstract:
+
+ Auditing - Private Defines, Fuction Prototypes and Macro Functions
+
+Author:
+
+ Scott Birrell (ScottBi) November 6, 1991
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include "tokenp.h"
+
+//
+// Audit Log Information
+//
+
+POLICY_AUDIT_LOG_INFO SepAdtLogInformation;
+
+extern BOOLEAN SepAdtAuditingEnabled;
+
+//
+// High and low water marks to control the length of the audit queue
+//
+
+extern ULONG SepAdtMaxListLength;
+extern ULONG SepAdtMinListLength;
+
+//
+// Structure used to query the above values from the registry
+//
+
+typedef struct _SEP_AUDIT_BOUNDS {
+
+ ULONG UpperBound;
+ ULONG LowerBound;
+
+} SEP_AUDIT_BOUNDS, *PSEP_AUDIT_BOUNDS;
+
+
+//
+// Number of events discarded
+//
+
+extern ULONG SepAdtCountEventsDiscarded;
+
+
+//
+// Number of events on the queue
+//
+
+extern ULONG SepAdtCurrentListLength;
+
+
+//
+// Flag to tell us that we're discarding audits
+//
+
+extern BOOLEAN SepAdtDiscardingAudits;
+
+//
+// Flag to tell us that we should crash if we miss
+// and audit.
+//
+
+extern BOOLEAN SepCrashOnAuditFail;
+
+//
+// Value name for verbose privilege auditing
+//
+
+#define FULL_PRIVILEGE_AUDITING L"FullPrivilegeAuditing"
+
+
+VOID
+SepAdtSetAuditEventInformation(
+ IN OPTIONAL PBOOLEAN AuditingMode,
+ IN OPTIONAL PPOLICY_AUDIT_EVENT_OPTIONS EventAuditingOptions
+ );
+
+VOID
+SepAdtGetAuditEventInformation(
+ OUT OPTIONAL PBOOLEAN AuditingMode,
+ OUT OPTIONAL PPOLICY_AUDIT_EVENT_OPTIONS EventAuditingOptions
+ );
+
+VOID
+SepAdtSetAuditLogInformation(
+ IN PPOLICY_AUDIT_LOG_INFO AuditLogInformation
+ );
+
+NTSTATUS
+SepAdtMarshallAuditRecord(
+ IN PSE_ADT_PARAMETER_ARRAY AuditParameters,
+ OUT PSE_ADT_PARAMETER_ARRAY *MarshalledAuditParameters,
+ OUT PSEP_RM_LSA_MEMORY_TYPE RecordMemoryType
+ );
+
+
+BOOLEAN
+SepAdtPrivilegeObjectAuditAlarm (
+ IN PUNICODE_STRING CapturedSubsystemName OPTIONAL,
+ IN PVOID HandleId,
+ IN PTOKEN ClientToken OPTIONAL,
+ IN PTOKEN PrimaryToken,
+ IN PVOID ProcessId,
+ IN ACCESS_MASK DesiredAccess,
+ IN PPRIVILEGE_SET CapturedPrivileges,
+ IN BOOLEAN AccessGranted
+ );
+
+VOID
+SepAdtTraverseAuditAlarm(
+ IN PLUID OperationID,
+ IN PVOID DirectoryObject,
+ IN PSID UserSid,
+ IN LUID AuthenticationId,
+ IN ACCESS_MASK DesiredAccess,
+ IN PPRIVILEGE_SET Privileges OPTIONAL,
+ IN BOOLEAN AccessGranted,
+ IN BOOLEAN GenerateAudit,
+ IN BOOLEAN GenerateAlarm
+ );
+
+VOID
+SepAdtCreateInstanceAuditAlarm(
+ IN PLUID OperationID,
+ IN PVOID Object,
+ IN PSID UserSid,
+ IN LUID AuthenticationId,
+ IN ACCESS_MASK DesiredAccess,
+ IN PPRIVILEGE_SET Privileges OPTIONAL,
+ IN BOOLEAN AccessGranted,
+ IN BOOLEAN GenerateAudit,
+ IN BOOLEAN GenerateAlarm
+ );
+
+VOID
+SepAdtCreateObjectAuditAlarm(
+ IN PLUID OperationID,
+ IN PUNICODE_STRING DirectoryName,
+ IN PUNICODE_STRING ComponentName,
+ IN PSID UserSid,
+ IN LUID AuthenticationId,
+ IN ACCESS_MASK DesiredAccess,
+ IN BOOLEAN AccessGranted,
+ IN BOOLEAN GenerateAudit,
+ IN BOOLEAN GenerateAlarm
+ );
+
+
+VOID
+SepAdtHandleAuditAlarm(
+ IN PUNICODE_STRING Source,
+ IN LUID OperationId,
+ IN HANDLE Handle,
+ IN PSID UserSid
+ );
+
+VOID
+SepAdtPrivilegedServiceAuditAlarm (
+ IN PUNICODE_STRING CapturedSubsystemName,
+ IN PUNICODE_STRING CapturedServiceName,
+ IN PTOKEN ClientToken OPTIONAL,
+ IN PTOKEN PrimaryToken,
+ IN PPRIVILEGE_SET CapturedPrivileges,
+ IN BOOLEAN AccessGranted
+ );
+
+
+VOID
+SepAdtCloseObjectAuditAlarm(
+ IN PUNICODE_STRING CapturedSubsystemName,
+ IN PVOID HandleId,
+ IN PVOID Object,
+ IN PSID UserSid,
+ IN LUID AuthenticationId
+ );
+
+VOID
+SepAdtDeleteObjectAuditAlarm(
+ IN PUNICODE_STRING CapturedSubsystemName,
+ IN PVOID HandleId,
+ IN PVOID Object,
+ IN PSID UserSid,
+ IN LUID AuthenticationId
+ );
+
+BOOLEAN
+SepAdtOpenObjectAuditAlarm(
+ IN PUNICODE_STRING CapturedSubsystemName,
+ IN PVOID *HandleId,
+ IN PUNICODE_STRING CapturedObjectTypeName,
+ IN PVOID Object,
+ IN PUNICODE_STRING CapturedObjectName,
+ IN PTOKEN ClientToken OPTIONAL,
+ IN PTOKEN PrimaryToken,
+ IN ACCESS_MASK DesiredAccess,
+ IN ACCESS_MASK GrantedAccess,
+ IN PLUID OperationId,
+ IN PPRIVILEGE_SET CapturedPrivileges OPTIONAL,
+ IN BOOLEAN ObjectCreated,
+ IN BOOLEAN AccessGranted,
+ IN BOOLEAN GenerateAudit,
+ IN BOOLEAN GenerateAlarm,
+ IN HANDLE ProcessID
+ );
+
+BOOLEAN
+SepAdtOpenObjectForDeleteAuditAlarm(
+ IN PUNICODE_STRING CapturedSubsystemName,
+ IN PVOID *HandleId,
+ IN PUNICODE_STRING CapturedObjectTypeName,
+ IN PVOID Object,
+ IN PUNICODE_STRING CapturedObjectName,
+ IN PTOKEN ClientToken OPTIONAL,
+ IN PTOKEN PrimaryToken,
+ IN ACCESS_MASK DesiredAccess,
+ IN ACCESS_MASK GrantedAccess,
+ IN PLUID OperationId,
+ IN PPRIVILEGE_SET CapturedPrivileges OPTIONAL,
+ IN BOOLEAN ObjectCreated,
+ IN BOOLEAN AccessGranted,
+ IN BOOLEAN GenerateAudit,
+ IN BOOLEAN GenerateAlarm,
+ IN HANDLE ProcessID
+ );
+
+VOID
+SepAdtObjectReferenceAuditAlarm(
+ IN PLUID OperationID OPTIONAL,
+ IN PVOID Object,
+ IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
+ IN ACCESS_MASK DesiredAccess,
+ IN PPRIVILEGE_SET Privileges OPTIONAL,
+ IN BOOLEAN AccessGranted,
+ IN BOOLEAN GenerateAudit,
+ IN BOOLEAN GenerateAlarm
+ );
+
+//
+// BOOLEAN
+// SepAdtAuditThisEvent(
+// IN POLICY_AUDIT_EVENT_TYPE AuditType,
+// IN PBOOLEAN AccessGranted
+// );
+//
+
+#define SepAdtAuditThisEvent(AuditType, AccessGranted) \
+ (SepAdtAuditingEnabled && \
+ ((SeAuditingState[AuditType].AuditOnSuccess && *AccessGranted) || \
+ (SeAuditingState[AuditType].AuditOnFailure && !(*AccessGranted))))
+
+VOID
+SepAdtInitializeBounds(
+ VOID
+ );
+
+VOID
+SepAuditFailed(
+ VOID
+ );
+
+NTSTATUS
+SepAdtInitializeCrashOnFail(
+ VOID
+ );
+
+BOOLEAN
+SepInitializePrivilegeFilter(
+ BOOLEAN Verbose
+ );
+
+BOOLEAN
+SepAdtInitializePrivilegeAuditing(
+ VOID
+ );
diff --git a/private/ntos/se/adtutil.c b/private/ntos/se/adtutil.c
new file mode 100644
index 000000000..3fe9bdf65
--- /dev/null
+++ b/private/ntos/se/adtutil.c
@@ -0,0 +1,51 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ adtutil.c - Security Auditing - Utility Routines
+
+Abstract:
+
+ This Module contains miscellaneous utility routines private to the
+ Security Auditing Component.
+
+Author:
+
+ Robert Reichel (robertre) September 10, 1991
+
+Environment:
+
+ Kernel Mode
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include "tokenp.h"
+#include "adt.h"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE,SepDumpString)
+#endif
+
+
+VOID
+SepDumpString(
+ IN PUNICODE_STRING String
+ )
+
+{
+ PAGED_CODE();
+
+ if ( String == NULL) {
+ KdPrint(("<NULL>"));
+ return;
+ }
+
+ KdPrint(("%Z",String->Buffer));
+
+}
+
diff --git a/private/ntos/se/adtvars.c b/private/ntos/se/adtvars.c
new file mode 100644
index 000000000..834052f29
--- /dev/null
+++ b/private/ntos/se/adtvars.c
@@ -0,0 +1,62 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ adtvars.c
+
+Abstract:
+
+ Auditing - Private Variables
+
+Author:
+
+ Scott Birrell (ScottBi) November 14, 1991
+
+Environment:
+
+ Kernel Mode only
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntos.h>
+#include "sep.h"
+#include "adt.h"
+#include "adtp.h"
+
+
+//
+// Auditing State. This contains the Auditing Mode and the array of
+// Event Auditing Options
+//
+
+POLICY_AUDIT_EVENTS_INFO SepAdtState;
+
+//
+// Audit Log Information
+//
+
+POLICY_AUDIT_LOG_INFO SepAdtLogInformation;
+
+//
+// High and low water marks to control the length of the audit queue
+// These are initialized to their default values in case we can't get
+// them out of the registry.
+//
+
+ULONG SepAdtMaxListLength = 0x3000;
+ULONG SepAdtMinListLength = 0x2000;
+
+ULONG SepAdtCurrentListLength = 0;
+
+//
+// Number of events discarded
+//
+
+ULONG SepAdtCountEventsDiscarded = 0;
+
+BOOLEAN SepAdtDiscardingAudits = FALSE;
diff --git a/private/ntos/se/capture.c b/private/ntos/se/capture.c
new file mode 100644
index 000000000..30d6fc2f3
--- /dev/null
+++ b/private/ntos/se/capture.c
@@ -0,0 +1,2568 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ Capture.c
+
+Abstract:
+
+ This Module implements the security data structure capturing routines.
+ There are corresponding Release routines for the data structures that
+ are captured into allocated pool.
+
+Author:
+
+ Gary Kimura (GaryKi) 9-Nov-1989
+ Jim Kelly (JimK) 1-Feb-1990
+
+Environment:
+
+ Kernel Mode
+
+Revision History:
+
+--*/
+
+#include "sep.h"
+#include "seopaque.h"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE,SeCaptureSecurityDescriptor)
+#pragma alloc_text(PAGE,SeReleaseSecurityDescriptor)
+#pragma alloc_text(PAGE,SeCaptureSecurityQos)
+#pragma alloc_text(PAGE,SeCaptureSid)
+#pragma alloc_text(PAGE,SeReleaseSid)
+#pragma alloc_text(PAGE,SeCaptureAcl)
+#pragma alloc_text(PAGE,SeReleaseAcl)
+#pragma alloc_text(PAGE,SeCaptureLuidAndAttributesArray)
+#pragma alloc_text(PAGE,SeReleaseLuidAndAttributesArray)
+#pragma alloc_text(PAGE,SeCaptureSidAndAttributesArray)
+#pragma alloc_text(PAGE,SeReleaseSidAndAttributesArray)
+#pragma alloc_text(PAGE,SeComputeQuotaInformationSize)
+#pragma alloc_text(PAGE,SepCopyProxyData)
+#pragma alloc_text(PAGE,SepProbeAndCaptureQosData)
+#pragma alloc_text(PAGE,SepFreeProxyData)
+#endif
+
+#define LongAligned( ptr ) (LongAlign(ptr) == (ptr))
+
+
+NTSTATUS
+SeCaptureSecurityDescriptor (
+ IN PSECURITY_DESCRIPTOR InputSecurityDescriptor,
+ IN KPROCESSOR_MODE RequestorMode,
+ IN POOL_TYPE PoolType,
+ IN BOOLEAN ForceCapture,
+ OUT PSECURITY_DESCRIPTOR *OutputSecurityDescriptor
+ )
+
+/*++
+
+Routine Description:
+
+ This routine probes and captures a copy of the security descriptor based
+ upon the following tests.
+
+ if the requestor mode is not kernel mode then
+
+ probe and capture the input descriptor
+ (the captured descriptor is self-relative)
+
+ if the requstor mode is kernel mode then
+
+ if force capture is true then
+
+ do not probe the input descriptor, but do capture it.
+ (the captured descriptor is self-relative)
+
+ else
+
+ do nothing
+ (the input descriptor is expected to be self-relative)
+
+Arguments:
+
+ InputSecurityDescriptor - Supplies the security descriptor to capture.
+ This parameter is assumed to have been provided by the mode specified
+ in RequestorMode.
+
+ RequestorMode - Specifies the caller's access mode.
+
+ PoolType - Specifies which pool type to allocate the captured
+ descriptor from
+
+ ForceCapture - Specifies whether the input descriptor should always be
+ captured
+
+ OutputSecurityDescriptor - Supplies the address of a pointer to the
+ output security descriptor. The captured descriptor will be
+ self-relative format.
+
+Return Value:
+
+ STATUS_SUCCESS if the operation is successful.
+
+ STATUS_INVALID_SID - An SID within the security descriptor is not
+ a valid SID.
+
+ STATUS_INVALID_ACL - An ACL within the security descriptor is not
+ a valid ACL.
+
+ STATUS_UNKNOWN_REVISION - The revision level of the security descriptor
+ is not one known to this revision of the capture routine.
+--*/
+
+{
+ SECURITY_DESCRIPTOR Captured;
+ SECURITY_DESCRIPTOR *PIOutputSecurityDescriptor;
+ PCHAR DescriptorOffset;
+
+ ULONG SaclSize;
+ ULONG NewSaclSize;
+
+ ULONG DaclSize;
+ ULONG NewDaclSize;
+
+ ULONG OwnerSubAuthorityCount;
+ ULONG OwnerSize;
+ ULONG NewOwnerSize;
+
+ ULONG GroupSubAuthorityCount;
+ ULONG GroupSize;
+ ULONG NewGroupSize;
+
+ ULONG Size;
+
+ PAGED_CODE();
+
+ //
+ // if the security descriptor is null then there is really nothing to
+ // capture
+ //
+
+ if (InputSecurityDescriptor == NULL) {
+
+ (*OutputSecurityDescriptor) = NULL;
+
+ return STATUS_SUCCESS;
+
+ }
+
+ //
+ // check if the requestors mode is kernel mode and we are not
+ // to force a capture
+ //
+
+ if ((RequestorMode == KernelMode) && (ForceCapture == FALSE)) {
+
+ //
+ // Yes it is so we don't need to do any work and can simply
+ // return a pointer to the input descriptor
+ //
+
+ (*OutputSecurityDescriptor) = InputSecurityDescriptor;
+
+ return STATUS_SUCCESS;
+
+ }
+
+
+ //
+ // We need to probe and capture the descriptor.
+ // To do this we need to probe the main security descriptor record
+ // first.
+ //
+
+ if (RequestorMode != KernelMode) {
+
+ //
+ // Capture of UserMode SecurityDescriptor.
+ //
+
+ try {
+
+ //
+ // Probe the main record of the input SecurityDescriptor
+ //
+
+ ProbeForRead( InputSecurityDescriptor,
+ sizeof(SECURITY_DESCRIPTOR),
+ sizeof(ULONG) );
+
+ //
+ // Capture the SecurityDescriptor main record.
+ //
+
+ RtlMoveMemory( (&Captured),
+ InputSecurityDescriptor,
+ sizeof(SECURITY_DESCRIPTOR) );
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ return GetExceptionCode();
+ }
+
+ } else {
+
+ //
+ // Force capture of kernel mode SecurityDescriptor.
+ //
+ // Capture the SecurityDescriptor main record.
+ // It doesn't need probing because requestor mode is kernel.
+ //
+
+ RtlMoveMemory( (&Captured),
+ InputSecurityDescriptor,
+ sizeof(SECURITY_DESCRIPTOR) );
+
+ }
+
+ //
+ // Make sure it is a revision we recognize
+ //
+
+ if (Captured.Revision != SECURITY_DESCRIPTOR_REVISION) {
+ return STATUS_UNKNOWN_REVISION;
+ }
+
+
+ //
+ // In case the input security descriptor is self-relative, change the
+ // captured main record to appear as an absolute form so we can use
+ // common code for both cases below.
+ //
+ // Note that the fields of Captured are left pointing to user
+ // space addresses. Treat them carefully.
+ //
+
+ try {
+
+ Captured.Owner = SepOwnerAddrSecurityDescriptor(
+ (SECURITY_DESCRIPTOR *)InputSecurityDescriptor
+ );
+ Captured.Group = SepGroupAddrSecurityDescriptor(
+ (SECURITY_DESCRIPTOR *)InputSecurityDescriptor
+ );
+ Captured.Sacl = SepSaclAddrSecurityDescriptor (
+ (SECURITY_DESCRIPTOR *)InputSecurityDescriptor
+ );
+ Captured.Dacl = SepDaclAddrSecurityDescriptor (
+ (SECURITY_DESCRIPTOR *)InputSecurityDescriptor
+ );
+ Captured.Control &= ~SE_SELF_RELATIVE;
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ return GetExceptionCode();
+ }
+
+
+
+ //
+ // Indicate the size we are going to need to allocate for the captured
+ // acls
+ //
+
+ SaclSize = 0;
+ DaclSize = 0;
+
+ NewSaclSize = 0;
+ NewDaclSize = 0;
+ NewGroupSize = 0;
+ NewOwnerSize = 0;
+
+ //
+ // Probe (if necessary) and capture each of the components of a
+ // SECURITY_DESCRIPTOR.
+ //
+
+ //
+ // System ACL first
+ //
+
+ if ((Captured.Control & SE_SACL_PRESENT) &&
+ (Captured.Sacl != NULL) ) {
+
+ if (RequestorMode != KernelMode) {
+
+ try {
+ SaclSize = ProbeAndReadUshort( &(Captured.Sacl->AclSize) );
+ ProbeForRead( Captured.Sacl,
+ SaclSize,
+ sizeof(ULONG) );
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ return GetExceptionCode();
+ }
+
+ } else {
+
+ SaclSize = Captured.Sacl->AclSize;
+
+ }
+
+ NewSaclSize = (ULONG)LongAlign( SaclSize );
+
+ }
+
+ //
+ // Discretionary ACL
+ //
+
+ if ((Captured.Control & SE_DACL_PRESENT) &&
+ (Captured.Dacl != NULL) ) {
+
+ if (RequestorMode != KernelMode) {
+
+ try {
+ DaclSize = ProbeAndReadUshort( &(Captured.Dacl->AclSize) );
+ ProbeForRead( Captured.Dacl,
+ DaclSize,
+ sizeof(ULONG) );
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ return GetExceptionCode();
+ }
+
+ } else {
+
+ DaclSize = Captured.Dacl->AclSize;
+
+ }
+
+ NewDaclSize = (ULONG)LongAlign( DaclSize );
+
+ }
+
+ //
+ // Owner SID
+ //
+
+ if (Captured.Owner != NULL) {
+
+ if (RequestorMode != KernelMode) {
+
+ try {
+ OwnerSubAuthorityCount =
+ ProbeAndReadUchar( &(((SID *)(Captured.Owner))->SubAuthorityCount) );
+ OwnerSize = RtlLengthRequiredSid( OwnerSubAuthorityCount );
+ ProbeForRead( Captured.Owner,
+ OwnerSize,
+ sizeof(ULONG) );
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ return GetExceptionCode();
+ }
+
+ } else {
+
+ OwnerSubAuthorityCount = ((SID *)(Captured.Owner))->SubAuthorityCount;
+ OwnerSize = RtlLengthRequiredSid( OwnerSubAuthorityCount );
+
+ }
+
+ NewOwnerSize = (ULONG)LongAlign( OwnerSize );
+
+ }
+
+ //
+ // Group SID
+ //
+
+ if (Captured.Group != NULL) {
+
+ if (RequestorMode != KernelMode) {
+
+ try {
+ GroupSubAuthorityCount =
+ ProbeAndReadUchar( &(((SID *)(Captured.Group))->SubAuthorityCount) );
+ GroupSize = RtlLengthRequiredSid( GroupSubAuthorityCount );
+ ProbeForRead( Captured.Group,
+ GroupSize,
+ sizeof(ULONG) );
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ return GetExceptionCode();
+ }
+
+ } else {
+
+ GroupSubAuthorityCount = ((SID *)(Captured.Group))->SubAuthorityCount;
+ GroupSize = RtlLengthRequiredSid( GroupSubAuthorityCount );
+
+ }
+
+ NewGroupSize = (ULONG)LongAlign( GroupSize );
+
+ }
+
+
+
+ //
+ // Now allocate enough pool to hold the descriptor
+ //
+
+ Size = sizeof(SECURITY_DESCRIPTOR) +
+ NewSaclSize +
+ NewDaclSize +
+ NewOwnerSize +
+ NewGroupSize;
+
+ (PIOutputSecurityDescriptor) = (SECURITY_DESCRIPTOR *)ExAllocatePoolWithTag( PoolType,
+ Size,
+ 'cSeS' );
+
+ if ( PIOutputSecurityDescriptor == NULL ) {
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ (*OutputSecurityDescriptor) = (PSECURITY_DESCRIPTOR)PIOutputSecurityDescriptor;
+ DescriptorOffset = (PCHAR)(PIOutputSecurityDescriptor);
+
+
+ //
+ // Copy the main security descriptor record over
+ //
+
+ RtlMoveMemory( DescriptorOffset,
+ &Captured,
+ sizeof(SECURITY_DESCRIPTOR) );
+ DescriptorOffset += sizeof(SECURITY_DESCRIPTOR);
+
+ //
+ // Indicate the output descriptor is self-relative
+ //
+
+ PIOutputSecurityDescriptor->Control |= SE_SELF_RELATIVE;
+
+ //
+ // If there is a System Acl, copy it over and set
+ // the output descriptor's offset to point to the newly captured copy.
+ //
+
+ if ((Captured.Control & SE_SACL_PRESENT) && (Captured.Sacl != NULL)) {
+
+
+ try {
+ RtlMoveMemory( DescriptorOffset,
+ Captured.Sacl,
+ SaclSize );
+
+ PIOutputSecurityDescriptor->Sacl = (PACL)DescriptorOffset;
+
+ DescriptorOffset += NewSaclSize;
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ ExFreePool( PIOutputSecurityDescriptor );
+ return GetExceptionCode();
+ }
+
+ if ((RequestorMode != KernelMode) &&
+ (!SepCheckAcl( PIOutputSecurityDescriptor->Sacl, SaclSize )) ) {
+
+ ExFreePool( PIOutputSecurityDescriptor );
+ return STATUS_INVALID_ACL;
+ }
+
+ //
+ // Change pointer to offset
+ //
+
+ PIOutputSecurityDescriptor->Sacl =
+ (PACL)( RtlPointerToOffset(
+ PIOutputSecurityDescriptor,
+ PIOutputSecurityDescriptor->Sacl
+ ));
+
+
+ }
+
+ //
+ // If there is a Discretionary Acl, copy it over and set
+ // the output descriptor's offset to point to the newly captured copy.
+ //
+
+ if ((Captured.Control & SE_DACL_PRESENT) && (Captured.Dacl != NULL)) {
+
+
+ try {
+ RtlMoveMemory( DescriptorOffset,
+ Captured.Dacl,
+ DaclSize );
+ PIOutputSecurityDescriptor->Dacl = (PACL)DescriptorOffset;
+ DescriptorOffset += NewDaclSize;
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ ExFreePool( PIOutputSecurityDescriptor );
+ return GetExceptionCode();
+ }
+
+ if ((RequestorMode != KernelMode) &&
+ (!SepCheckAcl( PIOutputSecurityDescriptor->Dacl, DaclSize )) ) {
+
+ ExFreePool( PIOutputSecurityDescriptor );
+ return STATUS_INVALID_ACL;
+ }
+
+ //
+ // Change pointer to offset
+ //
+
+ PIOutputSecurityDescriptor->Dacl =
+ (PACL)( RtlPointerToOffset(
+ PIOutputSecurityDescriptor,
+ PIOutputSecurityDescriptor->Dacl
+ ));
+
+ }
+
+ //
+ // If there is an Owner SID, copy it over and set
+ // the output descriptor's offset to point to the newly captured copy.
+ //
+
+ if (Captured.Owner != NULL) {
+
+
+ try {
+ RtlMoveMemory( DescriptorOffset,
+ Captured.Owner,
+ OwnerSize );
+ PIOutputSecurityDescriptor->Owner = (PSID)DescriptorOffset;
+ DescriptorOffset += NewOwnerSize;
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ ExFreePool( PIOutputSecurityDescriptor );
+ return GetExceptionCode();
+ }
+
+ if ((RequestorMode != KernelMode) &&
+ (!RtlValidSid( PIOutputSecurityDescriptor->Owner )) ) {
+
+ ExFreePool( PIOutputSecurityDescriptor );
+ return STATUS_INVALID_SID;
+ }
+
+ //
+ // Change pointer to offset
+ //
+
+ PIOutputSecurityDescriptor->Owner =
+ (PSID)( RtlPointerToOffset(
+ PIOutputSecurityDescriptor,
+ PIOutputSecurityDescriptor->Owner
+ ));
+
+ }
+
+ //
+ // If there is a group SID, copy it over and set
+ // the output descriptor's offset to point to the newly captured copy.
+ //
+
+ if (Captured.Group != NULL) {
+
+
+ try {
+ RtlMoveMemory( DescriptorOffset,
+ Captured.Group,
+ GroupSize );
+ PIOutputSecurityDescriptor->Group = (PSID)DescriptorOffset;
+ DescriptorOffset += NewGroupSize;
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ ExFreePool( PIOutputSecurityDescriptor );
+ return GetExceptionCode();
+ }
+
+ if ((RequestorMode != KernelMode) &&
+ (!RtlValidSid( PIOutputSecurityDescriptor->Group )) ) {
+
+ ExFreePool( PIOutputSecurityDescriptor );
+ return STATUS_INVALID_SID;
+ }
+
+ //
+ // Change pointer to offset
+ //
+
+ PIOutputSecurityDescriptor->Group =
+ (PSID)( RtlPointerToOffset(
+ PIOutputSecurityDescriptor,
+ PIOutputSecurityDescriptor->Group
+ ));
+
+ }
+
+ //
+ // And return to our caller
+ //
+
+ return STATUS_SUCCESS;
+
+}
+
+
+VOID
+SeReleaseSecurityDescriptor (
+ IN PSECURITY_DESCRIPTOR CapturedSecurityDescriptor,
+ IN KPROCESSOR_MODE RequestorMode,
+ IN BOOLEAN ForceCapture
+ )
+
+/*++
+
+Routine Description:
+
+ This routine releases a previously captured security descriptor.
+ Only
+
+Arguments:
+
+ CapturedSecurityDescriptor - Supplies the security descriptor to release.
+
+ RequestorMode - The processor mode specified when the descriptor was
+ captured.
+
+ ForceCapture - The ForceCapture value specified when the descriptor was
+ captured.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ //
+ // We only have something to deallocate if the requestor was user
+ // mode or kernel mode requesting ForceCapture.
+ //
+
+ PAGED_CODE();
+
+ if ( ((RequestorMode == KernelMode) && (ForceCapture == TRUE)) ||
+ (RequestorMode == UserMode ) ) {
+ if ( CapturedSecurityDescriptor ) {
+ ExFreePool(CapturedSecurityDescriptor);
+ }
+ }
+
+ return;
+
+}
+
+
+NTSTATUS
+SepCopyProxyData (
+ OUT PSECURITY_TOKEN_PROXY_DATA * DestProxyData,
+ IN PSECURITY_TOKEN_PROXY_DATA SourceProxyData
+ )
+
+/*++
+
+Routine Description:
+
+ This routine copies a token proxy data structure from one token to another.
+
+Arguments:
+
+ DestProxyData - Receives a pointer to a new proxy data structure.
+
+ SourceProxyData - Supplies a pointer to an already existing proxy data structure.
+
+Return Value:
+
+ STATUS_INSUFFICIENT_RESOURCES on failure.
+
+--*/
+
+{
+
+ PAGED_CODE();
+
+ *DestProxyData = ExAllocatePoolWithTag( PagedPool, sizeof( SECURITY_TOKEN_PROXY_DATA ), 'dPoT' );
+
+ if (*DestProxyData == NULL) {
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+
+
+ (*DestProxyData)->PathInfo.Buffer = ExAllocatePoolWithTag( PagedPool, SourceProxyData->PathInfo.Length, 'dPoT' );
+
+ if ((*DestProxyData)->PathInfo.Buffer == NULL) {
+ ExFreePool( *DestProxyData );
+ *DestProxyData = NULL;
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ (*DestProxyData)->Length = SourceProxyData->Length;
+ (*DestProxyData)->ProxyClass = SourceProxyData->ProxyClass;
+ (*DestProxyData)->PathInfo.MaximumLength =
+ (*DestProxyData)->PathInfo.Length = SourceProxyData->PathInfo.Length;
+ (*DestProxyData)->ContainerMask = SourceProxyData->ContainerMask;
+ (*DestProxyData)->ObjectMask = SourceProxyData->ObjectMask;
+
+ RtlCopyUnicodeString( &(*DestProxyData)->PathInfo, &SourceProxyData->PathInfo );
+
+ return( STATUS_SUCCESS );
+}
+
+VOID
+SepFreeProxyData (
+ IN PSECURITY_TOKEN_PROXY_DATA ProxyData
+ )
+
+/*++
+
+Routine Description:
+
+ This routine frees a SECURITY_TOKEN_PROXY_DATA structure and all sub structures.
+
+Arguments:
+
+ ProxyData - Supplies a pointer to an existing proxy data structure.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PAGED_CODE();
+
+ if (ProxyData != NULL) {
+
+ if (ProxyData->PathInfo.Buffer != NULL) {
+ ExFreePool( ProxyData->PathInfo.Buffer );
+ }
+
+ ExFreePool( ProxyData );
+ }
+}
+
+
+
+
+NTSTATUS
+SepProbeAndCaptureQosData(
+ IN PSECURITY_ADVANCED_QUALITY_OF_SERVICE CapturedSecurityQos
+ )
+
+/*++
+
+Routine Description:
+
+ This routine probes and captures the imbedded structures in a
+ Security Quality of Service structure.
+
+ This routine assumes that it is being called under an existing
+ try-except clause.
+
+Arguments:
+
+ CapturedSecurityQos - Points to the captured body of a QOS
+ structure. The pointers in this structure are presumed
+ not to be probed or captured at this point.
+
+Return Value:
+
+ STATUS_SUCCESS indicates no exceptions were encountered.
+
+ Any access violations encountered will be returned.
+
+--*/
+{
+ NTSTATUS Status;
+ PSECURITY_TOKEN_PROXY_DATA CapturedProxyData;
+ PSECURITY_TOKEN_AUDIT_DATA CapturedAuditData;
+ PAGED_CODE();
+
+ CapturedProxyData = CapturedSecurityQos->ProxyData;
+ CapturedSecurityQos->ProxyData = NULL;
+ CapturedAuditData = CapturedSecurityQos->AuditData;
+ CapturedSecurityQos->AuditData = NULL;
+
+ if (ARGUMENT_PRESENT( CapturedProxyData )) {
+
+ PSECURITY_TOKEN_PROXY_DATA LocalProxyData = NULL;
+ UNICODE_STRING SavedPathInfo;
+
+ //
+ // Make sure the body of the proxy data is ok to read.
+ //
+
+ ProbeForRead(
+ CapturedProxyData,
+ sizeof(SECURITY_TOKEN_PROXY_DATA),
+ sizeof(ULONG)
+ );
+
+ if (CapturedProxyData->Length != sizeof( SECURITY_TOKEN_PROXY_DATA )) {
+ return( STATUS_INVALID_PARAMETER );
+ }
+
+ //
+ // Probe the passed pathinfo buffer
+ //
+
+ ProbeForRead(
+ CapturedProxyData->PathInfo.Buffer,
+ CapturedProxyData->PathInfo.Length,
+ sizeof( UCHAR )
+ );
+
+ Status = SepCopyProxyData( &CapturedSecurityQos->ProxyData, CapturedProxyData );
+
+ if (!NT_SUCCESS(Status)) {
+
+ if (CapturedSecurityQos->ProxyData != NULL) {
+ SepFreeProxyData( CapturedSecurityQos->ProxyData );
+ CapturedSecurityQos->ProxyData = NULL;
+ }
+
+ return( Status );
+ }
+
+ }
+
+ if (ARGUMENT_PRESENT( CapturedAuditData )) {
+
+ PSECURITY_TOKEN_AUDIT_DATA LocalAuditData;
+
+ //
+ // Probe the audit data structure and make sure it looks ok
+ //
+
+ ProbeForRead(
+ CapturedAuditData,
+ sizeof( SECURITY_TOKEN_AUDIT_DATA ),
+ sizeof( ULONG )
+ );
+
+ if ( CapturedAuditData->Length != sizeof( SECURITY_TOKEN_AUDIT_DATA ) ) {
+ SepFreeProxyData( CapturedSecurityQos->ProxyData );
+ CapturedSecurityQos->ProxyData = NULL;
+ return( STATUS_INVALID_PARAMETER );
+ }
+
+ LocalAuditData = ExAllocatePool( PagedPool, sizeof( SECURITY_TOKEN_AUDIT_DATA ));
+
+ if (LocalAuditData == NULL) {
+
+ //
+ // Cleanup any proxy data we may have allocated.
+ //
+
+ SepFreeProxyData( CapturedSecurityQos->ProxyData );
+ CapturedSecurityQos->ProxyData = NULL;
+
+ return( STATUS_INSUFFICIENT_RESOURCES );
+
+ }
+
+ //
+ // Copy the data to the local buffer. Note: we do this in this
+ // order so that if the final assignment fails the caller will
+ // still be able to free the allocated pool.
+ //
+
+ CapturedSecurityQos->AuditData = LocalAuditData;
+
+ *CapturedSecurityQos->AuditData = *CapturedAuditData;
+
+ }
+
+ return( STATUS_SUCCESS );
+
+}
+
+
+VOID
+SeFreeCapturedSecurityQos(
+ IN PVOID SecurityQos
+ )
+
+/*++
+
+Routine Description:
+
+ This routine frees the data associated with a captured SecurityQos
+ structure. It does not free the body of the structure, just whatever
+ its internal fields point to.
+
+Arguments:
+
+ SecurityQos - Points to a captured security QOS structure.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PSECURITY_ADVANCED_QUALITY_OF_SERVICE IAdvancedSecurityQos;
+
+ PAGED_CODE();
+
+ IAdvancedSecurityQos = (PSECURITY_ADVANCED_QUALITY_OF_SERVICE)SecurityQos;
+
+ if (IAdvancedSecurityQos->Length == sizeof( SECURITY_ADVANCED_QUALITY_OF_SERVICE )) {
+
+ if (IAdvancedSecurityQos->AuditData != NULL) {
+ ExFreePool( IAdvancedSecurityQos->AuditData );
+ }
+
+ SepFreeProxyData( IAdvancedSecurityQos->ProxyData );
+ }
+
+ return;
+}
+
+
+NTSTATUS
+SeCaptureSecurityQos (
+ IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
+ IN KPROCESSOR_MODE RequestorMode,
+ OUT PBOOLEAN SecurityQosPresent,
+ OUT PSECURITY_ADVANCED_QUALITY_OF_SERVICE CapturedSecurityQos
+)
+/*++
+
+Routine Description:
+
+ This routine probes and captures a copy of any security quality
+ of service parameters that might have been provided via the
+ ObjectAttributes argument.
+
+Arguments:
+
+ ObjectAttributes - The object attributes from which the QOS
+ information is to be retrieved.
+
+ RequestorMode - Indicates the processor mode by which the access
+ is being requested.
+
+ SecurityQosPresent - Receives a boolean value indicating whether
+ or not the optional security QOS information was available
+ and copied.
+
+ CapturedSecurityQos - Receives the security QOS information if available.
+
+Return Value:
+
+ STATUS_SUCCESS indicates no exceptions were encountered.
+
+ Any access violations encountered will be returned.
+
+--*/
+
+{
+
+ PSECURITY_QUALITY_OF_SERVICE LocalSecurityQos;
+ PSECURITY_ADVANCED_QUALITY_OF_SERVICE LocalAdvancedSecurityQos;
+ NTSTATUS Status;
+ BOOLEAN CapturedQos;
+
+ PAGED_CODE();
+
+ CapturedQos = FALSE;
+ //
+ // Set default return
+ //
+
+ (*SecurityQosPresent) = FALSE;
+
+ //
+ // check if the requestors mode is kernel mode
+ //
+
+ if (RequestorMode != KernelMode) {
+ try {
+
+ if ( ARGUMENT_PRESENT(ObjectAttributes) ) {
+
+ ProbeForRead( ObjectAttributes,
+ sizeof(OBJECT_ATTRIBUTES),
+ sizeof(ULONG)
+ );
+
+ LocalSecurityQos =
+ (PSECURITY_QUALITY_OF_SERVICE)ObjectAttributes->SecurityQualityOfService;
+
+ if ( ARGUMENT_PRESENT(LocalSecurityQos) ) {
+
+ ProbeForRead(
+ LocalSecurityQos,
+ sizeof(SECURITY_QUALITY_OF_SERVICE),
+ sizeof(ULONG)
+ );
+
+ //
+ // Check the length and see if this is a QOS or Advanced QOS
+ // structure.
+ //
+
+ if (LocalSecurityQos->Length == sizeof( SECURITY_QUALITY_OF_SERVICE )) {
+
+ //
+ // It's a downlevel QOS, copy what's there and leave.
+ //
+
+ (*SecurityQosPresent) = TRUE;
+ RtlMoveMemory( CapturedSecurityQos, LocalSecurityQos, sizeof( SECURITY_QUALITY_OF_SERVICE ));
+ CapturedSecurityQos->ProxyData = NULL;
+ CapturedSecurityQos->AuditData = NULL;
+
+ } else {
+
+ if (LocalSecurityQos->Length == sizeof( SECURITY_ADVANCED_QUALITY_OF_SERVICE )) {
+
+ LocalAdvancedSecurityQos =
+ (PSECURITY_ADVANCED_QUALITY_OF_SERVICE)ObjectAttributes->SecurityQualityOfService;
+
+ ProbeForRead(
+ LocalAdvancedSecurityQos,
+ sizeof(SECURITY_ADVANCED_QUALITY_OF_SERVICE),
+ sizeof(ULONG)
+ );
+
+ (*SecurityQosPresent) = TRUE;
+ *CapturedSecurityQos = *LocalAdvancedSecurityQos;
+
+ //
+ // Capture the proxy and audit data, if necessary.
+ //
+
+ if ( ARGUMENT_PRESENT(CapturedSecurityQos->ProxyData) || ARGUMENT_PRESENT( CapturedSecurityQos->AuditData ) ) {
+
+ CapturedQos = TRUE;
+ Status = SepProbeAndCaptureQosData( CapturedSecurityQos );
+
+ if (!NT_SUCCESS( Status )) {
+
+ return( Status );
+ }
+ }
+
+ } else {
+
+ return( STATUS_INVALID_PARAMETER );
+ }
+ }
+
+ } // end_if
+
+
+ } // end_if
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+
+ //
+ // If we captured any proxy data, we need to free it now.
+ //
+
+ if ( CapturedQos ) {
+
+ SepFreeProxyData( CapturedSecurityQos->ProxyData );
+
+ if ( CapturedSecurityQos->AuditData != NULL ) {
+ ExFreePool( CapturedSecurityQos->AuditData );
+ }
+ }
+
+ return GetExceptionCode();
+ } // end_try
+
+
+ } else {
+
+ if ( ARGUMENT_PRESENT(ObjectAttributes) ) {
+ if ( ARGUMENT_PRESENT(ObjectAttributes->SecurityQualityOfService) ) {
+ (*SecurityQosPresent) = TRUE;
+
+ if (((PSECURITY_QUALITY_OF_SERVICE)(ObjectAttributes->SecurityQualityOfService))->Length == sizeof( SECURITY_QUALITY_OF_SERVICE )) {
+
+ RtlMoveMemory( CapturedSecurityQos, ObjectAttributes->SecurityQualityOfService, sizeof( SECURITY_QUALITY_OF_SERVICE ));
+ CapturedSecurityQos->ProxyData = NULL;
+ CapturedSecurityQos->AuditData = NULL;
+
+ } else {
+
+ (*CapturedSecurityQos) =
+ (*(SECURITY_ADVANCED_QUALITY_OF_SERVICE *)(ObjectAttributes->SecurityQualityOfService));
+ }
+
+
+ } // end_if
+ } // end_if
+
+ } // end_if
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+SeCaptureSid (
+ IN PSID InputSid,
+ IN KPROCESSOR_MODE RequestorMode,
+ IN PVOID CaptureBuffer OPTIONAL,
+ IN ULONG CaptureBufferLength,
+ IN POOL_TYPE PoolType,
+ IN BOOLEAN ForceCapture,
+ OUT PSID *CapturedSid
+)
+/*++
+
+Routine Description:
+
+ This routine probes and captures a copy of the specified SID.
+ The SID is either captured into a provided buffer, or pool
+ allocated to receive the SID.
+
+
+ if the requestor mode is not kernel mode then
+
+ probe and capture the input SID
+
+ if the requstor mode is kernel mode then
+
+ if force capture is true then
+
+ do not probe the input SID, but do capture it
+
+ else
+
+ return address of original, but don't copy
+
+Arguments:
+
+ InputSid - Supplies the SID to capture. This parameter is assumed
+ to have been provided by the mode specified in RequestorMode.
+
+ RequestorMode - Specifies the caller's access mode.
+
+ CaptureBuffer - Specifies a buffer into which the SID is to be
+ captured. If this parameter is not provided, pool will be allocated
+ to hold the captured data.
+
+ CaptureBufferLength - Indicates the length, in bytes, of the capture
+ buffer.
+
+ PoolType - Specifies which pool type to allocate to capture the
+ SID into. This parameter is ignored if CaptureBuffer is provided.
+
+ ForceCapture - Specifies whether the SID should be captured even if
+ requestor mode is kernel.
+
+ CapturedSid - Supplies the address of a pointer to an SID.
+ The pointer will be set to point to the captured (or uncaptured) SID.
+
+ AlignedSidSize - Supplies the address of a ULONG to receive the length
+ of the SID rounded up to the next longword boundary.
+
+Return Value:
+
+ STATUS_SUCCESS indicates the capture was successful.
+
+ STATUS_BUFFER_TOO_SMALL - indicates the buffer provided to capture the SID
+ into wasn't large enough to hold the SID.
+
+ Any access violations encountered will be returned.
+
+--*/
+
+{
+
+
+
+ ULONG GetSidSubAuthorityCount;
+ ULONG SidSize;
+
+ PAGED_CODE();
+
+ //
+ // check if the requestors mode is kernel mode and we are not
+ // to force a capture.
+ //
+
+ if ((RequestorMode == KernelMode) && (ForceCapture == FALSE)) {
+
+ //
+ // We don't need to do any work and can simply
+ // return a pointer to the input SID
+ //
+
+ (*CapturedSid) = InputSid;
+
+ return STATUS_SUCCESS;
+ }
+
+
+ //
+ // Get the length needed to hold the SID
+ //
+
+ if (RequestorMode != KernelMode) {
+
+ try {
+ GetSidSubAuthorityCount =
+ ProbeAndReadUchar( &(((SID *)(InputSid))->SubAuthorityCount) );
+ SidSize = RtlLengthRequiredSid( GetSidSubAuthorityCount );
+ ProbeForRead( InputSid,
+ SidSize,
+ sizeof(ULONG) );
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ return GetExceptionCode();
+ }
+
+ } else {
+
+ GetSidSubAuthorityCount = ((SID *)(InputSid))->SubAuthorityCount;
+ SidSize = RtlLengthRequiredSid( GetSidSubAuthorityCount );
+
+ }
+
+
+ //
+ // If a buffer was provided, compare lengths.
+ // Otherwise, allocate a buffer.
+ //
+
+ if (ARGUMENT_PRESENT(CaptureBuffer)) {
+
+ if (SidSize > CaptureBufferLength) {
+ return STATUS_BUFFER_TOO_SMALL;
+ } else {
+
+ (*CapturedSid) = CaptureBuffer;
+ }
+
+ } else {
+
+ (*CapturedSid) = (PSID)ExAllocatePoolWithTag(PoolType, SidSize, 'iSeS');
+
+ if ( *CapturedSid == NULL ) {
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ }
+
+ //
+ // Now copy the SID and validate it
+ //
+
+ try {
+
+ RtlMoveMemory( (*CapturedSid), InputSid, SidSize );
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ if (!ARGUMENT_PRESENT(CaptureBuffer)) {
+ ExFreePool( (*CapturedSid) );
+ }
+
+ return GetExceptionCode();
+ }
+
+ if ((!RtlValidSid( (*CapturedSid) )) ) {
+
+ if (!ARGUMENT_PRESENT(CaptureBuffer)) {
+ ExFreePool( (*CapturedSid) );
+ }
+
+ return STATUS_INVALID_SID;
+ }
+
+ return STATUS_SUCCESS;
+
+}
+
+
+VOID
+SeReleaseSid (
+ IN PSID CapturedSid,
+ IN KPROCESSOR_MODE RequestorMode,
+ IN BOOLEAN ForceCapture
+ )
+
+/*++
+
+Routine Description:
+
+ This routine releases a previously captured SID.
+
+ This routine should NOT be called if the SID was captured into a
+ provided CaptureBuffer (see SeCaptureSid).
+
+Arguments:
+
+ CapturedSid - Supplies the SID to release.
+
+ RequestorMode - The processor mode specified when the SID was captured.
+
+ ForceCapture - The ForceCapture value specified when the SID was
+ captured.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ //
+ // We only have something to deallocate if the requestor was user
+ // mode or kernel mode requesting ForceCapture.
+ //
+
+ PAGED_CODE();
+
+ if ( ((RequestorMode == KernelMode) && (ForceCapture == TRUE)) ||
+ (RequestorMode == UserMode ) ) {
+
+ ExFreePool(CapturedSid);
+
+ }
+
+ return;
+
+}
+
+NTSTATUS
+SeCaptureAcl (
+ IN PACL InputAcl,
+ IN KPROCESSOR_MODE RequestorMode,
+ IN PVOID CaptureBuffer OPTIONAL,
+ IN ULONG CaptureBufferLength,
+ IN POOL_TYPE PoolType,
+ IN BOOLEAN ForceCapture,
+ OUT PACL *CapturedAcl,
+ OUT PULONG AlignedAclSize
+ )
+
+/*++
+
+Routine Description:
+
+ This routine probes and captures a copy of the specified ACL.
+ The ACL is either captured into a provided buffer, or pool
+ allocated to receive the ACL.
+
+ Any ACL captured will have its structure validated.
+
+
+ if the requestor mode is not kernel mode then
+
+ probe and capture the input ACL
+
+ if the requstor mode is kernel mode then
+
+ if force capture is true then
+
+ do not probe the input ACL, but do capture it
+
+ else
+
+ return address of original, but don't copy
+
+Arguments:
+
+ InputAcl - Supplies the ACL to capture. This parameter is assumed
+ to have been provided by the mode specified in RequestorMode.
+
+ RequestorMode - Specifies the caller's access mode.
+
+ CaptureBuffer - Specifies a buffer into which the ACL is to be
+ captured. If this parameter is not provided, pool will be allocated
+ to hold the captured data.
+
+ CaptureBufferLength - Indicates the length, in bytes, of the capture
+ buffer.
+
+ PoolType - Specifies which pool type to allocate to capture the
+ ACL into. This parameter is ignored if CaptureBuffer is provided.
+
+ ForceCapture - Specifies whether the ACL should be captured even if
+ requestor mode is kernel.
+
+ CapturedAcl - Supplies the address of a pointer to an ACL.
+ The pointer will be set to point to the captured (or uncaptured) ACL.
+
+ AlignedAclSize - Supplies the address of a ULONG to receive the length
+ of the ACL rounded up to the next longword boundary.
+
+Return Value:
+
+ STATUS_SUCCESS indicates the capture was successful.
+
+ STATUS_BUFFER_TOO_SMALL - indicates the buffer provided to capture the ACL
+ into wasn't large enough to hold the ACL.
+
+ Any access violations encountered will be returned.
+
+--*/
+
+{
+
+ ULONG AclSize;
+
+ PAGED_CODE();
+
+ //
+ // check if the requestors mode is kernel mode and we are not
+ // to force a capture.
+ //
+
+ if ((RequestorMode == KernelMode) && (ForceCapture == FALSE)) {
+
+ //
+ // We don't need to do any work and can simply
+ // return a pointer to the input ACL
+ //
+
+ (*CapturedAcl) = InputAcl;
+
+ return STATUS_SUCCESS;
+ }
+
+
+ //
+ // Get the length needed to hold the ACL
+ //
+
+ if (RequestorMode != KernelMode) {
+
+ try {
+
+ AclSize = ProbeAndReadUshort( &(InputAcl->AclSize) );
+
+ ProbeForRead( InputAcl,
+ AclSize,
+ sizeof(ULONG) );
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ return GetExceptionCode();
+ }
+
+ } else {
+
+ AclSize = InputAcl->AclSize;
+
+ }
+
+ //
+ // If the passed pointer is non-null, it has better at least
+ // point to a well formed ACL
+ //
+
+ if (AclSize < sizeof(ACL)) {
+ return( STATUS_INVALID_ACL );
+ }
+
+ (*AlignedAclSize) = (ULONG)LongAlign( AclSize );
+
+
+ //
+ // If a buffer was provided, compare lengths.
+ // Otherwise, allocate a buffer.
+ //
+
+ if (ARGUMENT_PRESENT(CaptureBuffer)) {
+
+ if (AclSize > CaptureBufferLength) {
+ return STATUS_BUFFER_TOO_SMALL;
+ } else {
+
+ (*CapturedAcl) = CaptureBuffer;
+ }
+
+ } else {
+
+ (*CapturedAcl) = (PACL)ExAllocatePoolWithTag(PoolType, AclSize, 'cAeS');
+
+ if ( *CapturedAcl == NULL ) {
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ }
+
+ //
+ // Now copy the ACL and validate it
+ //
+
+ try {
+
+ RtlMoveMemory( (*CapturedAcl), InputAcl, AclSize );
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ if (!ARGUMENT_PRESENT(CaptureBuffer)) {
+ ExFreePool( (*CapturedAcl) );
+ }
+
+ return GetExceptionCode();
+ }
+
+ if ( (!SepCheckAcl( (*CapturedAcl), AclSize )) ) {
+
+ if (!ARGUMENT_PRESENT(CaptureBuffer)) {
+ ExFreePool( (*CapturedAcl) );
+ }
+
+ return STATUS_INVALID_ACL;
+ }
+
+ return STATUS_SUCCESS;
+
+}
+
+
+VOID
+SeReleaseAcl (
+ IN PACL CapturedAcl,
+ IN KPROCESSOR_MODE RequestorMode,
+ IN BOOLEAN ForceCapture
+ )
+
+/*++
+
+Routine Description:
+
+ This routine releases a previously captured ACL.
+
+ This routine should NOT be called if the ACL was captured into a
+ provided CaptureBuffer (see SeCaptureAcl).
+
+Arguments:
+
+ CapturedAcl - Supplies the ACL to release.
+
+ RequestorMode - The processor mode specified when the ACL was captured.
+
+ ForceCapture - The ForceCapture value specified when the ACL was
+ captured.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ //
+ // We only have something to deallocate if the requestor was user
+ // mode or kernel mode requesting ForceCapture.
+ //
+
+ PAGED_CODE();
+
+ if ( ((RequestorMode == KernelMode) && (ForceCapture == TRUE)) ||
+ (RequestorMode == UserMode ) ) {
+
+ ExFreePool(CapturedAcl);
+
+ }
+
+}
+
+NTSTATUS
+SeCaptureLuidAndAttributesArray (
+ IN PLUID_AND_ATTRIBUTES InputArray,
+ IN ULONG ArrayCount,
+ IN KPROCESSOR_MODE RequestorMode,
+ IN PVOID CaptureBuffer OPTIONAL,
+ IN ULONG CaptureBufferLength,
+ IN POOL_TYPE PoolType,
+ IN BOOLEAN ForceCapture,
+ OUT PLUID_AND_ATTRIBUTES *CapturedArray,
+ OUT PULONG AlignedArraySize
+ )
+
+/*++
+
+Routine Description:
+
+ This routine probes and captures a copy of the specified
+ LUID_AND_ATTRIBUTES array.
+
+ The array is either captured into a provided buffer, or pool
+ allocated to receive the array.
+
+
+ if the requestor mode is not kernel mode then
+
+ probe and capture the input array
+
+ if the requstor mode is kernel mode then
+
+ if force capture is true then
+
+ do not probe the input array, but do capture it
+
+ else
+
+ return address of original, but don't copy
+
+Arguments:
+
+ InputArray - Supplies the array to capture. This parameter is assumed
+ to have been provided by the mode specified in RequestorMode.
+
+ ArrayCount - Indicates the number of elements in the array to capture.
+
+ RequestorMode - Specifies the caller's access mode.
+
+ CaptureBuffer - Specifies a buffer into which the array is to be
+ captured. If this parameter is not provided, pool will be allocated
+ to hold the captured data.
+
+ CaptureBufferLength - Indicates the length, in bytes, of the capture
+ buffer.
+
+ PoolType - Specifies which pool type to allocate to capture the
+ array into. This parameter is ignored if CaptureBuffer is provided.
+
+ ForceCapture - Specifies whether the array should be captured even if
+ requestor mode is kernel.
+
+ CapturedArray - Supplies the address of a pointer to an array.
+ The pointer will be set to point to the captured (or uncaptured) array.
+
+ AlignedArraySize - Supplies the address of a ULONG to receive the length
+ of the array rounded up to the next longword boundary.
+
+Return Value:
+
+ STATUS_SUCCESS indicates the capture was successful.
+
+ STATUS_BUFFER_TOO_SMALL - indicates the buffer provided to capture the array
+ into wasn't large enough to hold the array.
+
+ Any access violations encountered will be returned.
+
+--*/
+
+{
+
+ ULONG ArraySize;
+
+ PAGED_CODE();
+
+ //
+ // Make sure the array isn't empty
+ //
+
+ if (ArrayCount == 0) {
+ (*CapturedArray) = NULL;
+ (*AlignedArraySize) = 0;
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // check if the requestors mode is kernel mode and we are not
+ // to force a capture.
+ //
+
+ if ((RequestorMode == KernelMode) && (ForceCapture == FALSE)) {
+
+ //
+ // We don't need to do any work and can simply
+ // return a pointer to the input array
+ //
+
+ (*CapturedArray) = InputArray;
+
+ return STATUS_SUCCESS;
+ }
+
+
+ //
+ // Get the length needed to hold the array
+ //
+
+ ArraySize = ArrayCount * (ULONG)sizeof(LUID_AND_ATTRIBUTES);
+ (*AlignedArraySize) = (ULONG)LongAlign( ArraySize );
+
+ if (RequestorMode != KernelMode) {
+
+ try {
+
+
+ ProbeForRead( InputArray,
+ ArraySize,
+ sizeof(ULONG) );
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ return GetExceptionCode();
+ }
+
+ }
+
+
+
+ //
+ // If a buffer was provided, compare lengths.
+ // Otherwise, allocate a buffer.
+ //
+
+ if (ARGUMENT_PRESENT(CaptureBuffer)) {
+
+ if (ArraySize > CaptureBufferLength) {
+ return STATUS_BUFFER_TOO_SMALL;
+ } else {
+
+ (*CapturedArray) = CaptureBuffer;
+ }
+
+ } else {
+
+ (*CapturedArray) =
+ (PLUID_AND_ATTRIBUTES)ExAllocatePoolWithTag(PoolType, ArraySize, 'uLeS');
+
+ if ( *CapturedArray == NULL ) {
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ }
+
+ //
+ // Now copy the array
+ //
+
+ try {
+
+ RtlMoveMemory( (*CapturedArray), InputArray, ArraySize );
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ if (!ARGUMENT_PRESENT(CaptureBuffer)) {
+ ExFreePool( (*CapturedArray) );
+ }
+
+ return GetExceptionCode();
+ }
+
+ return STATUS_SUCCESS;
+
+}
+
+
+VOID
+SeReleaseLuidAndAttributesArray (
+ IN PLUID_AND_ATTRIBUTES CapturedArray,
+ IN KPROCESSOR_MODE RequestorMode,
+ IN BOOLEAN ForceCapture
+ )
+
+/*++
+
+Routine Description:
+
+ This routine releases a previously captured array of LUID_AND_ATTRIBUTES.
+
+ This routine should NOT be called if the array was captured into a
+ provided CaptureBuffer (see SeCaptureLuidAndAttributesArray).
+
+Arguments:
+
+ CapturedArray - Supplies the array to release.
+
+ RequestorMode - The processor mode specified when the array was captured.
+
+ ForceCapture - The ForceCapture value specified when the array was
+ captured.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ //
+ // We only have something to deallocate if the requestor was user
+ // mode or kernel mode requesting ForceCapture.
+ //
+
+ PAGED_CODE();
+
+ if ( ((RequestorMode == KernelMode) && (ForceCapture == TRUE)) ||
+ (RequestorMode == UserMode ) ) {
+
+ ExFreePool(CapturedArray);
+
+ }
+
+ return;
+
+}
+
+NTSTATUS
+SeCaptureSidAndAttributesArray (
+ IN PSID_AND_ATTRIBUTES InputArray,
+ IN ULONG ArrayCount,
+ IN KPROCESSOR_MODE RequestorMode,
+ IN PVOID CaptureBuffer OPTIONAL,
+ IN ULONG CaptureBufferLength,
+ IN POOL_TYPE PoolType,
+ IN BOOLEAN ForceCapture,
+ OUT PSID_AND_ATTRIBUTES *CapturedArray,
+ OUT PULONG AlignedArraySize
+ )
+
+/*++
+
+Routine Description:
+
+ This routine probes and captures a copy of the specified
+ SID_AND_ATTRIBUTES array, along with the SID values pointed
+ to.
+
+ The array is either captured into a provided buffer, or pool
+ allocated to receive the array.
+
+ The format of the captured information is an array of SID_AND_ATTRIBUTES
+ data structures followed by the SID values. THIS MAY NOT BE THE CASE
+ FOR KERNEL MODE UNLESS A FORCE CAPTURE IS SPECIFIED.
+
+
+ if the requestor mode is not kernel mode then
+
+ probe and capture the input array
+
+ if the requstor mode is kernel mode then
+
+ if force capture is true then
+
+ do not probe the input array, but do capture it
+
+ else
+
+ return address of original, but don't copy
+
+Arguments:
+
+ InputArray - Supplies the array to capture. This parameter is assumed
+ to have been provided by the mode specified in RequestorMode.
+
+ ArrayCount - Indicates the number of elements in the array to capture.
+
+ RequestorMode - Specifies the caller's access mode.
+
+ CaptureBuffer - Specifies a buffer into which the array is to be
+ captured. If this parameter is not provided, pool will be allocated
+ to hold the captured data.
+
+ CaptureBufferLength - Indicates the length, in bytes, of the capture
+ buffer.
+
+ PoolType - Specifies which pool type to allocate to capture the
+ array into. This parameter is ignored if CaptureBuffer is provided.
+
+ ForceCapture - Specifies whether the array should be captured even if
+ requestor mode is kernel.
+
+ CapturedArray - Supplies the address of a pointer to an array.
+ The pointer will be set to point to the captured (or uncaptured) array.
+
+ AlignedArraySize - Supplies the address of a ULONG to receive the length
+ of the array rounded up to the next longword boundary.
+
+Return Value:
+
+ STATUS_SUCCESS indicates the capture was successful.
+
+ STATUS_BUFFER_TOO_SMALL - indicates the buffer provided to capture the array
+ into wasn't large enough to hold the array.
+
+ Any access violations encountered will be returned.
+
+--*/
+
+{
+
+typedef struct _TEMP_ARRAY_ELEMENT {
+ PISID Sid;
+ ULONG SidLength;
+} TEMP_ARRAY_ELEMENT;
+
+
+ TEMP_ARRAY_ELEMENT *TempArray;
+
+ NTSTATUS CompletionStatus = STATUS_SUCCESS;
+
+ ULONG ArraySize;
+ ULONG AlignedLengthRequired;
+
+ ULONG NextIndex;
+
+ PSID_AND_ATTRIBUTES NextElement;
+ PVOID NextBufferLocation;
+
+ ULONG GetSidSubAuthorityCount;
+ ULONG SidSize;
+ ULONG AlignedSidSize;
+
+ PAGED_CODE();
+
+ //
+ // Make sure the array isn't empty
+ //
+
+ if (ArrayCount == 0) {
+ (*CapturedArray) = NULL;
+ (*AlignedArraySize) = 0;
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // check if the requestor's mode is kernel mode and we are not
+ // to force a capture.
+ //
+
+ if ((RequestorMode == KernelMode) && (ForceCapture == FALSE)) {
+
+ //
+ // We don't need to do any work and can simply
+ // return a pointer to the input array
+ //
+
+ (*CapturedArray) = InputArray;
+
+ return STATUS_SUCCESS;
+ }
+
+
+ //
+ // ---------- For RequestorMode == UserMode ----------------------
+ //
+ // the algorithm for capturing an SID_AND_ATTRIBUTES array is somewhat
+ // convoluted to avoid problems that could occur if the data is
+ // being changed while being captured.
+ //
+ // The algorithm uses two loops.
+ //
+ // Allocate a temporary buffer to house the fixed length data.
+ //
+ // 1st loop:
+ // For each SID:
+ // Capture the Pointers to the SID and the length of the SID.
+ //
+ // Allocate a buffer large enough to hold all of the data.
+ //
+ // 2nd loop:
+ // For each SID:
+ // Capture the Attributes.
+ // Capture the SID.
+ // Set the pointer to the SID.
+ //
+ // Deallocate temporary buffer.
+ //
+ // ------------ For RequestorMode == KernelMode --------------------
+ //
+ // There is no need to capture the length and address of the SIDs
+ // in the first loop (since the kernel can be trusted not to change
+ // them while they are being copied.) So for kernel mode, the first
+ // loop just adds up the length needed. Kernel mode, thus, avoids
+ // having to allocate a temporary buffer.
+ //
+
+ //
+ // Get the length needed to hold the array elements.
+ //
+
+ ArraySize = ArrayCount * (ULONG)sizeof(SID_AND_ATTRIBUTES);
+ AlignedLengthRequired = (ULONG)LongAlign( ArraySize );
+
+ if (RequestorMode != KernelMode) {
+
+ //
+ // Allocate a temporary array to capture the array elements into
+ //
+
+ TempArray =
+ (TEMP_ARRAY_ELEMENT *)ExAllocatePoolWithTag(PoolType, AlignedLengthRequired, 'aTeS');
+
+ if ( TempArray == NULL ) {
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+
+ try {
+
+ //
+ // Make sure we can read each SID_AND_ATTRIBUTE
+ //
+
+ ProbeForRead( InputArray,
+ ArraySize,
+ sizeof(ULONG) );
+
+ //
+ // Probe and capture the length and address of each SID
+ //
+
+ NextIndex = 0;
+ while (NextIndex < ArrayCount) {
+
+ GetSidSubAuthorityCount =
+ ProbeAndReadUchar( &( ((PISID)(InputArray[NextIndex].Sid))->SubAuthorityCount) );
+
+ TempArray[NextIndex].Sid = ((PISID)(InputArray[NextIndex].Sid));
+ TempArray[NextIndex].SidLength =
+ RtlLengthRequiredSid( GetSidSubAuthorityCount );
+
+ ProbeForRead( TempArray[NextIndex].Sid,
+ TempArray[NextIndex].SidLength,
+ sizeof(ULONG) );
+
+ AlignedLengthRequired +=
+ (ULONG)LongAlign( TempArray[NextIndex].SidLength );
+
+ NextIndex += 1;
+
+ } //end while
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ ExFreePool( TempArray );
+ return GetExceptionCode();
+ }
+
+
+ } else {
+
+ //
+ // No need to capture anything.
+ // But, we do need to add up the lengths of the SIDs
+ // so we can allocate a buffer (or check the size of one provided).
+ //
+
+ NextIndex = 0;
+
+ while (NextIndex < ArrayCount) {
+
+ GetSidSubAuthorityCount =
+ ((PISID)(InputArray[NextIndex].Sid))->SubAuthorityCount;
+
+ AlignedLengthRequired +=
+ (ULONG)LongAlign(RtlLengthRequiredSid(GetSidSubAuthorityCount));
+
+ NextIndex += 1;
+
+ } //end while
+
+ }
+
+
+ //
+ // Now we know how much memory we need.
+ // Return this value in the output parameter.
+ //
+
+ (*AlignedArraySize) = AlignedLengthRequired;
+
+ //
+ // If a buffer was provided, make sure it is long enough.
+ // Otherwise, allocate a buffer.
+ //
+
+ if (ARGUMENT_PRESENT(CaptureBuffer)) {
+
+ if (AlignedLengthRequired > CaptureBufferLength) {
+
+ if (RequestorMode != KernelMode) {
+ ExFreePool( TempArray );
+ }
+
+ return STATUS_BUFFER_TOO_SMALL;
+
+ } else {
+
+ (*CapturedArray) = CaptureBuffer;
+ }
+
+ } else {
+
+ (*CapturedArray) =
+ (PSID_AND_ATTRIBUTES)ExAllocatePoolWithTag(PoolType, AlignedLengthRequired, 'aSeS');
+
+ if ( *CapturedArray == NULL ) {
+ if (RequestorMode != KernelMode) {
+ ExFreePool( TempArray );
+ }
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+ }
+
+
+ //
+ // Now copy everything.
+ // This is done by copying all the SID_AND_ATTRIBUTES and then
+ // copying each individual SID.
+ //
+ // All SIDs have already been probed for READ access. We just
+ // need to copy them.
+ //
+ //
+
+ if (RequestorMode != KernelMode) {
+ try {
+
+ //
+ // Copy the SID_AND_ATTRIBUTES array elements
+ // This really only sets the attributes, since we
+ // over-write the SID pointer field later on.
+ //
+
+ NextBufferLocation = (*CapturedArray);
+ RtlMoveMemory( NextBufferLocation, InputArray, ArraySize );
+ NextBufferLocation = (PVOID)((ULONG)NextBufferLocation +
+ (ULONG)LongAlign(ArraySize) );
+
+ //
+ // Now go through and copy each referenced SID.
+ // Validate each SID as it is copied.
+ //
+
+ NextIndex = 0;
+ NextElement = (*CapturedArray);
+ while ( (NextIndex < ArrayCount) &&
+ (CompletionStatus == STATUS_SUCCESS) ) {
+
+
+ RtlMoveMemory( NextBufferLocation,
+ TempArray[NextIndex].Sid,
+ TempArray[NextIndex].SidLength );
+
+ if (!RtlValidSid(TempArray[NextIndex].Sid) ) {
+ CompletionStatus = STATUS_INVALID_SID;
+ }
+
+ NextElement[NextIndex].Sid = (PSID)NextBufferLocation;
+ NextBufferLocation =
+ (PVOID)((ULONG)NextBufferLocation +
+ (ULONG)LongAlign(TempArray[NextIndex].SidLength));
+
+ NextIndex += 1;
+
+ } //end while
+
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ if (!ARGUMENT_PRESENT(CaptureBuffer)) {
+ ExFreePool( (*CapturedArray) );
+ }
+
+ ExFreePool( TempArray );
+
+ return GetExceptionCode();
+ }
+ } else {
+
+ //
+ // Requestor mode is kernel mode -
+ // don't need protection, probing, and validating
+ //
+
+ //
+ // Copy the SID_AND_ATTRIBUTES array elements
+ // This really only sets the attributes, since we
+ // over-write the SID pointer field later on.
+ //
+
+ NextBufferLocation = (*CapturedArray);
+ RtlMoveMemory( NextBufferLocation, InputArray, ArraySize );
+ NextBufferLocation = (PVOID)( (ULONG)NextBufferLocation +
+ (ULONG)LongAlign(ArraySize));
+
+ //
+ // Now go through and copy each referenced SID
+ //
+
+ NextIndex = 0;
+ NextElement = (*CapturedArray);
+ while (NextIndex < ArrayCount) {
+
+ GetSidSubAuthorityCount =
+ ((PISID)(NextElement[NextIndex].Sid))->SubAuthorityCount;
+
+ RtlMoveMemory(
+ NextBufferLocation,
+ NextElement[NextIndex].Sid,
+ RtlLengthRequiredSid(GetSidSubAuthorityCount) );
+ SidSize = RtlLengthRequiredSid( GetSidSubAuthorityCount );
+ AlignedSidSize = (ULONG)LongAlign(SidSize);
+
+ NextElement[NextIndex].Sid = (PSID)NextBufferLocation;
+
+ NextIndex += 1;
+ NextBufferLocation = (PVOID)((ULONG)NextBufferLocation +
+ AlignedSidSize);
+
+ } //end while
+
+ }
+
+ if (RequestorMode != KernelMode) {
+ ExFreePool( TempArray );
+ }
+
+ if (!ARGUMENT_PRESENT(CaptureBuffer) && !NT_SUCCESS(CompletionStatus)) {
+ ExFreePool( (*CapturedArray) );
+ }
+
+ return CompletionStatus;
+}
+
+
+VOID
+SeReleaseSidAndAttributesArray (
+ IN PSID_AND_ATTRIBUTES CapturedArray,
+ IN KPROCESSOR_MODE RequestorMode,
+ IN BOOLEAN ForceCapture
+ )
+
+/*++
+
+Routine Description:
+
+ This routine releases a previously captured array of SID_AND_ATTRIBUTES.
+
+ This routine should NOT be called if the array was captured into a
+ provided CaptureBuffer (see SeCaptureSidAndAttributesArray).
+
+Arguments:
+
+ CapturedArray - Supplies the array to release.
+
+ RequestorMode - The processor mode specified when the array was captured.
+
+ ForceCapture - The ForceCapture value specified when the array was
+ captured.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ //
+ // We only have something to deallocate if the requestor was user
+ // mode or kernel mode requesting ForceCapture.
+ //
+
+ PAGED_CODE();
+
+ if ( ((RequestorMode == KernelMode) && (ForceCapture == TRUE)) ||
+ (RequestorMode == UserMode ) ) {
+
+ ExFreePool(CapturedArray);
+
+ }
+
+ return;
+
+}
+
+
+
+
+NTSTATUS
+SeComputeQuotaInformationSize(
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ OUT PULONG Size
+ )
+
+/*++
+
+Routine Description:
+
+ This routine computes the size of the Group and DACL for the
+ passed security descriptor.
+
+ This quantity will later be used in calculating the amount
+ of quota to charge for this object.
+
+Arguments:
+
+ SecurityDescriptor - Supplies a pointer to the security descriptor
+ to be examined.
+
+ Size - Returns the size in bytes of the sum of the Group and Dacl
+ fields of the security descriptor.
+
+Return Value:
+
+ STATUS_SUCCESS - The operation was successful.
+
+ STATUS_INVALID_REVISION - The passed security descriptor was of
+ an unknown revision.
+
+--*/
+
+{
+ PISECURITY_DESCRIPTOR ISecurityDescriptor;
+
+ PSID Group;
+ PACL Dacl;
+
+ PAGED_CODE();
+
+ ISecurityDescriptor = (PISECURITY_DESCRIPTOR)SecurityDescriptor;
+ *Size = 0;
+
+ if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
+ return( STATUS_UNKNOWN_REVISION );
+ }
+
+ Group = SepGroupAddrSecurityDescriptor( ISecurityDescriptor );
+
+ Dacl = SepDaclAddrSecurityDescriptor( ISecurityDescriptor );
+
+ if (Group != NULL) {
+ *Size += (ULONG)LongAlign(SeLengthSid( Group ));
+ }
+
+ if (Dacl != NULL) {
+ *Size += (ULONG)LongAlign(Dacl->AclSize);
+ }
+
+ return( STATUS_SUCCESS );
+}
+
+
+BOOLEAN
+SeValidSecurityDescriptor(
+ IN ULONG Length,
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor
+ )
+
+/*++
+
+Routine Description:
+
+ Validates a security descriptor for structural correctness. The idea is to make
+ sure that the security descriptor may be passed to other kernel callers, without
+ fear that they're going to choke while manipulating it.
+
+ This routine does not enforce policy (e.g., ACL/ACE revision information). It is
+ entirely possible for a security descriptor to be approved by this routine, only
+ to be later found to be invalid by some later routine.
+
+ This routine is designed to be used by callers who have a security descriptor in
+ kernel memory. Callers wishing to validate a security descriptor passed from user
+ mode should call RtlValidSecurityDescriptor.
+
+Arguments:
+
+ Length - Length in bytes of passed Security Descriptor.
+
+ SecurityDescriptor - Points to the Security Descriptor (in kernel memory) to be
+ validatated.
+
+Return Value:
+
+ TRUE - The passed security descriptor is correctly structured
+ FALSE - The passed security descriptor is badly formed
+
+--*/
+
+{
+ PISECURITY_DESCRIPTOR ISecurityDescriptor = (PISECURITY_DESCRIPTOR)SecurityDescriptor;
+ PISID OwnerSid;
+ PISID GroupSid;
+ PACE_HEADER Ace;
+ PISID Sid;
+ PISID Sid2;
+ PACL Dacl;
+ PACL Sacl;
+ ULONG i;
+
+ if (Length < sizeof(SECURITY_DESCRIPTOR)) {
+ return(FALSE);
+ }
+
+ //
+ // Check the revision information.
+ //
+
+ if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
+ return(FALSE);
+ }
+
+ //
+ // Make sure the passed SecurityDescriptor is in self-relative form
+ //
+
+ if (!(ISecurityDescriptor->Control & SE_SELF_RELATIVE)) {
+ return(FALSE);
+ }
+
+ //
+ // Check the owner. A valid SecurityDescriptor must have an owner.
+ // It must also be long aligned.
+ //
+
+ if (ISecurityDescriptor->Owner == NULL || !LongAligned(ISecurityDescriptor->Owner) ||
+ (ULONG)((PCHAR)(ISecurityDescriptor->Owner)+sizeof(SID)) > Length) {
+
+ return(FALSE);
+ }
+
+ //
+ // It is safe to reference the owner's SubAuthorityCount, compute the
+ // expected length of the SID
+ //
+
+ OwnerSid = (PSID)RtlOffsetToPointer( ISecurityDescriptor, ISecurityDescriptor->Owner );
+
+ if (OwnerSid->Revision != SID_REVISION) {
+ return(FALSE);
+ }
+
+ if (OwnerSid->SubAuthorityCount > SID_MAX_SUB_AUTHORITIES) {
+ return(FALSE);
+ }
+
+ if ((ULONG)((PCHAR)ISecurityDescriptor->Owner+SeLengthSid(OwnerSid)) > Length) {
+ return(FALSE);
+ }
+
+ //
+ // The owner appears to be a structurally valid SID that lies within
+ // the bounds of the security descriptor. Do the same for the Group
+ // if there is one.
+ //
+
+ if (ISecurityDescriptor->Group != NULL) {
+
+ //
+ // Check alignment
+ //
+
+ if (!LongAligned(ISecurityDescriptor->Group)) {
+ return(FALSE);
+ }
+
+ if ((ULONG)((PCHAR)(ISecurityDescriptor->Group)+sizeof(SID)) > Length) {
+ return(FALSE);
+ }
+
+ //
+ // It is safe to reference the Group's SubAuthorityCount, compute the
+ // expected length of the SID
+ //
+
+ GroupSid = (PSID)RtlOffsetToPointer( ISecurityDescriptor, ISecurityDescriptor->Group );
+
+ if (GroupSid->Revision != SID_REVISION) {
+ return(FALSE);
+ }
+
+ if (GroupSid->SubAuthorityCount > SID_MAX_SUB_AUTHORITIES) {
+ return(FALSE);
+ }
+
+ if ((ULONG)((PCHAR)ISecurityDescriptor->Group+SeLengthSid(GroupSid)) > Length) {
+ return(FALSE);
+ }
+ }
+
+ //
+ // Validate the DACL. A structurally valid SecurityDescriptor may not necessarily
+ // have a DACL.
+ //
+
+ if (ISecurityDescriptor->Dacl != NULL) {
+
+ //
+ // Check alignment
+ //
+
+ if (!LongAligned(ISecurityDescriptor->Dacl)) {
+ return(FALSE);
+ }
+
+ //
+ // Make sure the DACL structure is within the bounds of the security descriptor.
+ //
+
+ if ((ULONG)((PCHAR)(ISecurityDescriptor->Dacl)+sizeof(ACL)) > Length) {
+ return(FALSE);
+ }
+
+ Dacl = (PACL)RtlOffsetToPointer( ISecurityDescriptor, ISecurityDescriptor->Dacl );
+
+ //
+ // Make sure the DACL is at least as big as an ACL structure
+ //
+
+ if (Dacl->AclSize < sizeof( ACL )) {
+ return( FALSE );
+ }
+
+ //
+ // Make sure the DACL length fits within the bounds of the security descriptor.
+ //
+
+ if ((ULONG)((PUCHAR)ISecurityDescriptor->Dacl + Dacl->AclSize) > Length) {
+ return(FALSE);
+ }
+
+ //
+ // Make sure the ACL is structurally valid.
+ //
+
+ if (!SepCheckAcl( Dacl, Dacl->AclSize )) {
+ return(FALSE);
+ }
+ }
+
+ //
+ // Validate the SACL. A structurally valid SecurityDescriptor may not
+ // have a SACL.
+ //
+
+ if (ISecurityDescriptor->Sacl != NULL) {
+
+ //
+ // Check alignment
+ //
+
+ if (!LongAligned(ISecurityDescriptor->Sacl)) {
+ return(FALSE);
+ }
+
+ //
+ // Make sure the Sacl structure is within the bounds of the security descriptor.
+ //
+
+ if ((ULONG)((PCHAR)(ISecurityDescriptor->Sacl)+sizeof(ACL)) > Length) {
+ return(FALSE);
+ }
+
+ Sacl = (PACL)RtlOffsetToPointer( ISecurityDescriptor, ISecurityDescriptor->Sacl );
+
+ //
+ // Make sure the SACL is at least as big as an ACL structure
+ //
+
+ if (Sacl->AclSize < sizeof( ACL )) {
+ return( FALSE );
+ }
+
+ //
+ // Make sure the Sacl length fits within the bounds of the security descriptor.
+ //
+
+ if ((ULONG)((PUCHAR)ISecurityDescriptor->Sacl + Sacl->AclSize) > Length) {
+ return(FALSE);
+ }
+
+ //
+ // Make sure the ACL is structurally valid.
+ //
+
+ if (!SepCheckAcl( Sacl, Sacl->AclSize )) {
+ return(FALSE);
+ }
+ }
+
+ return(TRUE);
+}
diff --git a/private/ntos/se/ctaccess.c b/private/ntos/se/ctaccess.c
new file mode 100644
index 000000000..014acc4bd
--- /dev/null
+++ b/private/ntos/se/ctaccess.c
@@ -0,0 +1,1267 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ ctaccess.c
+
+Abstract:
+
+ Common access validation test routines
+
+ These routines are used in both kernel and user mode tests.
+
+ This test assumes the security runtime library routines are
+ functioning correctly.
+
+
+Author:
+
+ Robert Reichel (robertre) 12/14/90
+
+Environment:
+
+ Test of access validation routines
+
+Revision History:
+
+ v1: robertre
+ Created
+
+--*/
+
+#include "tsecomm.c" // Mode dependent macros and routines.
+
+
+
+//
+// Define the local macros and procedure for this module
+//
+
+//
+// Return a pointer to the first Ace in an Acl (even if the Acl is empty).
+//
+// PACE_HEADER
+// FirstAce (
+// IN PACL Acl
+// );
+//
+
+#define FirstAce(Acl) ((PVOID)((PUCHAR)(Acl) + sizeof(ACL)))
+
+//
+// Return a pointer to the next Ace in a sequence (even if the input
+// Ace is the one in the sequence).
+//
+// PACE_HEADER
+// NextAce (
+// IN PACE_HEADER Ace
+// );
+//
+
+#define NextAce(Ace) ((PVOID)((PUCHAR)(Ace) + ((PACE_HEADER)(Ace))->AceSize))
+
+VOID
+DumpAcl (
+ IN PACL Acl
+ );
+
+////////////////////////////////////////////////////////////////
+// //
+// Module wide variables //
+// //
+////////////////////////////////////////////////////////////////
+
+#define DEFAULT_DACL_LENGTH (1024L)
+#define GROUP_IDS_LENGTH (1024L)
+#define NEW_GROUP_STATE_LENGTH (1024L)
+#define PRIVILEGES_LENGTH (128L)
+#define TOO_BIG_ACL_SIZE (2048L)
+
+//
+// definitions related to TokenWithGroups
+//
+
+#define FLINTSTONE_INDEX (0L)
+#define CHILD_INDEX (1L)
+#define NEANDERTHOL_INDEX (2L)
+#define WORLD_INDEX (3L)
+#define GROUP_COUNT (4L)
+
+
+//
+// Definitions related to TokenWithPrivileges
+//
+
+#define UNSOLICITED_INDEX (0L)
+#define SECURITY_INDEX (1L)
+#define PRIVILEGE_COUNT (2L)
+
+//
+// Access types
+//
+
+#define SET_WIDGET_COLOR 0x00000001
+#define SET_WIDGET_SIZE 0x00000002
+#define GET_WIDGET_COLOR 0x00000004
+#define GET_WIDGET_SIZE 0x00000008
+#define START_WIDGET 0x00000010
+#define STOP_WIDGET 0x00000020
+#define GIVE_WIDGET 0x00000040
+#define TAKE_WIDGET 0x00000080
+
+
+ NTSTATUS Status;
+
+ HANDLE SimpleToken;
+ HANDLE TokenWithGroups;
+ HANDLE TokenWithDefaultOwner;
+ HANDLE TokenWithPrivileges;
+ HANDLE TokenWithDefaultDacl;
+
+ HANDLE Token;
+ HANDLE ImpersonationToken;
+
+ HANDLE PrimaryToken;
+
+ HANDLE AnonymousToken;
+
+ OBJECT_ATTRIBUTES PrimaryTokenAttributes;
+ PSECURITY_DESCRIPTOR PrimarySecurityDescriptor;
+ SECURITY_QUALITY_OF_SERVICE PrimarySecurityQos;
+
+ OBJECT_ATTRIBUTES ImpersonationTokenAttributes;
+ PSECURITY_DESCRIPTOR ImpersonationSecurityDescriptor;
+ SECURITY_QUALITY_OF_SERVICE ImpersonationSecurityQos;
+
+ OBJECT_ATTRIBUTES AnonymousTokenAttributes;
+ PSECURITY_DESCRIPTOR AnonymousSecurityDescriptor;
+ SECURITY_QUALITY_OF_SERVICE AnonymousSecurityQos;
+
+ ULONG DisabledGroupAttributes;
+ ULONG OptionalGroupAttributes;
+ ULONG NormalGroupAttributes;
+ ULONG OwnerGroupAttributes;
+
+ ULONG LengthAvailable;
+ ULONG CurrentLength;
+
+
+ TIME_FIELDS TempTimeFields = {3000, 1, 1, 1, 1, 1, 1, 1};
+ LARGE_INTEGER NoExpiration;
+
+ LUID DummyAuthenticationId;
+ LUID SystemAuthenticationId = SYSTEM_LUID;
+
+ TOKEN_SOURCE TestSource = {"SE: TEST", 0};
+
+ PSID Owner;
+ PSID Group;
+ PACL Dacl;
+
+ PSID TempOwner;
+ PSID TempGroup;
+ PACL TempDacl;
+
+
+
+
+
+////////////////////////////////////////////////////////////////
+// //
+// Initialization Routine //
+// //
+////////////////////////////////////////////////////////////////
+
+BOOLEAN
+TestTokenInitialize()
+{
+
+ TSeVariableInitialization(); // Initialize global variables
+
+
+ DisabledGroupAttributes = (SE_GROUP_ENABLED_BY_DEFAULT);
+
+ OptionalGroupAttributes = (SE_GROUP_ENABLED_BY_DEFAULT |
+ SE_GROUP_ENABLED
+ );
+ NormalGroupAttributes = (SE_GROUP_MANDATORY |
+ SE_GROUP_ENABLED_BY_DEFAULT |
+ SE_GROUP_ENABLED
+ );
+ OwnerGroupAttributes = (SE_GROUP_MANDATORY |
+ SE_GROUP_ENABLED_BY_DEFAULT |
+ SE_GROUP_ENABLED |
+ SE_GROUP_OWNER
+ );
+
+
+ PrimarySecurityDescriptor =
+ (PSECURITY_DESCRIPTOR)TstAllocatePool( PagedPool, 1024 );
+
+ InitializeObjectAttributes(
+ &PrimaryTokenAttributes,
+ NULL,
+ OBJ_INHERIT,
+ NULL,
+ NULL
+ );
+
+
+ ImpersonationSecurityDescriptor =
+ (PSECURITY_DESCRIPTOR)TstAllocatePool( PagedPool, 1024 );
+
+ ImpersonationSecurityQos.Length = (ULONG)sizeof(SECURITY_QUALITY_OF_SERVICE);
+ ImpersonationSecurityQos.ImpersonationLevel = SecurityImpersonation;
+ ImpersonationSecurityQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ ImpersonationSecurityQos.EffectiveOnly = FALSE;
+
+ InitializeObjectAttributes(
+ &ImpersonationTokenAttributes,
+ NULL,
+ OBJ_INHERIT,
+ NULL,
+ NULL
+ );
+ ImpersonationTokenAttributes.SecurityQualityOfService =
+ &ImpersonationSecurityQos;
+
+
+ AnonymousSecurityDescriptor =
+ (PSECURITY_DESCRIPTOR)TstAllocatePool( PagedPool, 1024 );
+
+ AnonymousSecurityQos.Length = (ULONG)sizeof(SECURITY_QUALITY_OF_SERVICE);
+ AnonymousSecurityQos.ImpersonationLevel = SecurityAnonymous;
+ AnonymousSecurityQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ AnonymousSecurityQos.EffectiveOnly = FALSE;
+
+ InitializeObjectAttributes(
+ &AnonymousTokenAttributes,
+ NULL,
+ OBJ_INHERIT,
+ NULL,
+ NULL
+ );
+ AnonymousTokenAttributes.SecurityQualityOfService =
+ &AnonymousSecurityQos;
+
+
+ //
+ // Build an ACL for use.
+ //
+
+ Dacl = (PACL)TstAllocatePool( PagedPool, 256 );
+
+ Dacl->AclRevision=ACL_REVISION;
+ Dacl->Sbz1=0;
+ Dacl->Sbz2=0;
+ Dacl->AclSize=256;
+ Dacl->AceCount=0;
+
+
+ //
+ // Set up expiration times
+ //
+
+ TempTimeFields.Year = 3000;
+ TempTimeFields.Month = 1;
+ TempTimeFields.Day = 1;
+ TempTimeFields.Hour = 1;
+ TempTimeFields.Minute = 1;
+ TempTimeFields.Second = 1;
+ TempTimeFields.Milliseconds = 1;
+ TempTimeFields.Weekday = 1;
+
+ RtlTimeFieldsToTime( &TempTimeFields, &NoExpiration );
+
+
+ //
+ // Use a dummy authentication ID for a while.
+ //
+
+ DummyAuthenticationId = FredLuid;
+
+
+ //
+ // Use a token source specific to security test
+ //
+
+ NtAllocateLocallyUniqueId( &(TestSource.SourceIdentifier) );
+
+ DbgPrint("Done.\n");
+
+ return TRUE;
+}
+
+
+BOOLEAN
+CreateDAclToken()
+{
+
+ BOOLEAN CompletionStatus = TRUE;
+
+ TOKEN_USER UserId;
+ TOKEN_PRIMARY_GROUP PrimaryGroup;
+ PTOKEN_GROUPS GroupIds;
+ PTOKEN_PRIVILEGES Privileges;
+ TOKEN_DEFAULT_DACL DefaultDacl;
+ TOKEN_OWNER Owner;
+
+ PSECURITY_DESCRIPTOR Widget1SecurityDescriptor;
+
+ NTSTATUS AccessStatus;
+
+ ACCESS_MASK GrantedAccess;
+
+ PACCESS_ALLOWED_ACE AllowBarneySetColor;
+ PACCESS_ALLOWED_ACE AllowFredSetColor;
+
+ PACCESS_DENIED_ACE DenyPebblesSetColor;
+
+ PACCESS_ALLOWED_ACE AllowPebblesSetColor;
+ PACCESS_DENIED_ACE DenyFredSetColor;
+ PACCESS_ALLOWED_ACE AllowBarneySetSize;
+ PACCESS_ALLOWED_ACE AllowPebblesSetSize;
+
+ PACCESS_ALLOWED_ACE AllowPebblesGetColor;
+ PACCESS_ALLOWED_ACE AllowPebblesGetSize;
+
+ USHORT AllowBarneySetColorLength;
+ USHORT AllowFredSetColorLength;
+ USHORT DenyPebblesSetColorLength;
+
+ USHORT AllowPebblesSetColorLength;
+ USHORT DenyFredSetColorLength;
+ USHORT AllowBarneySetSizeLength;
+ USHORT AllowPebblesSetSizeLength;
+
+ USHORT AllowPebblesGetColorLength;
+ USHORT AllowPebblesGetSizeLength;
+
+
+ DbgPrint("\n");
+
+ GroupIds = (PTOKEN_GROUPS)TstAllocatePool( PagedPool,
+ GROUP_IDS_LENGTH
+ );
+
+ Privileges = (PTOKEN_PRIVILEGES)TstAllocatePool( PagedPool,
+ PRIVILEGES_LENGTH
+ );
+
+ DefaultDacl.DefaultDacl = (PACL)TstAllocatePool( PagedPool,
+ DEFAULT_DACL_LENGTH
+ );
+
+
+ //
+ // Create a token with default DACL
+ //
+
+ DbgPrint("Se: Create Token With Default Dacl ... ");
+
+ GroupIds->GroupCount = GROUP_COUNT;
+
+ GroupIds->Groups[FLINTSTONE_INDEX].Sid = FlintstoneSid;
+ GroupIds->Groups[CHILD_INDEX].Sid = ChildSid;
+ GroupIds->Groups[NEANDERTHOL_INDEX].Sid = NeandertholSid;
+ GroupIds->Groups[WORLD_INDEX].Sid = WorldSid;
+
+ GroupIds->Groups[FLINTSTONE_INDEX].Attributes = OwnerGroupAttributes;
+ GroupIds->Groups[CHILD_INDEX].Attributes = OptionalGroupAttributes;
+ GroupIds->Groups[NEANDERTHOL_INDEX].Attributes = OptionalGroupAttributes;
+ GroupIds->Groups[WORLD_INDEX].Attributes = NormalGroupAttributes;
+
+ UserId.User.Sid = PebblesSid;
+ UserId.User.Attributes = 0;
+
+ Owner.Owner = FlintstoneSid;
+
+ Privileges->PrivilegeCount = PRIVILEGE_COUNT;
+
+ Privileges->Privileges[UNSOLICITED_INDEX].Luid = UnsolicitedInputPrivilege;
+ Privileges->Privileges[SECURITY_INDEX].Luid = SecurityPrivilege;
+ Privileges->Privileges[UNSOLICITED_INDEX].Attributes = 0;
+ Privileges->Privileges[SECURITY_INDEX].Attributes = 0;
+
+ PrimaryGroup.PrimaryGroup = FlintstoneSid;
+
+ Status = RtlCreateAcl( DefaultDacl.DefaultDacl, DEFAULT_DACL_LENGTH, ACL_REVISION);
+
+ ASSERT(NT_SUCCESS(Status) );
+
+ Status = NtCreateToken(
+ &PrimaryToken, // Handle
+ (TOKEN_ALL_ACCESS), // DesiredAccess
+ &PrimaryTokenAttributes, // ObjectAttributes
+ TokenPrimary, // TokenType
+ &DummyAuthenticationId, // Authentication LUID
+ &NoExpiration, // Expiration Time
+ &UserId, // Owner ID
+ GroupIds, // Group IDs
+ Privileges, // Privileges
+ &Owner, // Owner
+ &PrimaryGroup, // Primary Group
+ &DefaultDacl, // Default Dacl
+ &TestSource // TokenSource
+ );
+
+ if (NT_SUCCESS(Status)) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(NT_SUCCESS(Status));
+
+
+
+ //
+ // Create an impersonation token, Impersonation level = Impersonation
+ //
+
+ DbgPrint("Se: Create an impersonation token ... ");
+
+ GroupIds->GroupCount = GROUP_COUNT;
+
+ GroupIds->Groups[FLINTSTONE_INDEX].Sid = FlintstoneSid;
+ GroupIds->Groups[CHILD_INDEX].Sid = ChildSid;
+ GroupIds->Groups[NEANDERTHOL_INDEX].Sid = NeandertholSid;
+ GroupIds->Groups[WORLD_INDEX].Sid = WorldSid;
+
+ GroupIds->Groups[FLINTSTONE_INDEX].Attributes = OwnerGroupAttributes;
+ GroupIds->Groups[CHILD_INDEX].Attributes = OptionalGroupAttributes;
+ GroupIds->Groups[NEANDERTHOL_INDEX].Attributes = OptionalGroupAttributes;
+ GroupIds->Groups[WORLD_INDEX].Attributes = NormalGroupAttributes;
+
+ UserId.User.Sid = PebblesSid;
+ UserId.User.Attributes = 0;
+
+ Owner.Owner = FlintstoneSid;
+
+ Privileges->PrivilegeCount = PRIVILEGE_COUNT;
+
+ Privileges->Privileges[UNSOLICITED_INDEX].Luid = UnsolicitedInputPrivilege;
+ Privileges->Privileges[SECURITY_INDEX].Luid = SecurityPrivilege;
+ Privileges->Privileges[UNSOLICITED_INDEX].Attributes = 0;
+ Privileges->Privileges[SECURITY_INDEX].Attributes = 0;
+
+ PrimaryGroup.PrimaryGroup = FlintstoneSid;
+
+ Status = RtlCreateAcl( DefaultDacl.DefaultDacl, DEFAULT_DACL_LENGTH, ACL_REVISION);
+
+ ASSERT(NT_SUCCESS(Status) );
+
+ Status = NtCreateToken(
+ &ImpersonationToken, // Handle
+ (TOKEN_ALL_ACCESS), // DesiredAccess
+ &ImpersonationTokenAttributes, // ObjectAttributes
+ TokenImpersonation, // TokenType
+ &DummyAuthenticationId, // Authentication LUID
+ &NoExpiration, // Expiration Time
+ &UserId, // Owner ID
+ GroupIds, // Group IDs
+ Privileges, // Privileges
+ &Owner, // Owner
+ &PrimaryGroup, // Primary Group
+ &DefaultDacl, // Default Dacl
+ &TestSource // TokenSource
+ );
+
+ if (NT_SUCCESS(Status)) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(NT_SUCCESS(Status));
+
+//
+// Attach tokens to process
+//
+
+ NtSetInformationProcess(
+ NtCurrentProcess(),
+ ProcessAccessToken,
+ &PrimaryToken,
+ sizeof( PHANDLE ));
+
+
+ NtSetInformationThread(
+ NtCurrentThread(),
+ ThreadImpersonationToken,
+ &ImpersonationToken,
+ sizeof( PHANDLE ));
+
+
+
+// Create some ACEs
+
+// AllowBarneySetColor
+
+ AllowBarneySetColorLength = (USHORT)(sizeof( ACCESS_ALLOWED_ACE ) - sizeof( ULONG ) +
+ SeLengthSid( BarneySid ));
+
+ AllowBarneySetColor = (PVOID) TstAllocatePool ( PagedPool, AllowBarneySetColorLength );
+
+ AllowBarneySetColor->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
+ AllowBarneySetColor->Header.AceSize = AllowBarneySetColorLength;
+ AllowBarneySetColor->Header.AceFlags = 0;
+
+ AllowBarneySetColor->Mask = SET_WIDGET_COLOR;
+
+ RtlCopySid(
+ SeLengthSid( BarneySid ),
+ &(AllowBarneySetColor->SidStart),
+ BarneySid );
+
+
+// DenyPebblesSetColor
+
+ DenyPebblesSetColorLength = (USHORT)(sizeof( ACCESS_DENIED_ACE ) - sizeof( ULONG ) +
+ SeLengthSid( BarneySid ));
+
+ DenyPebblesSetColor = (PVOID) TstAllocatePool ( PagedPool, DenyPebblesSetColorLength );
+
+ DenyPebblesSetColor->Header.AceType = ACCESS_DENIED_ACE_TYPE;
+ DenyPebblesSetColor->Header.AceSize = DenyPebblesSetColorLength;
+ DenyPebblesSetColor->Header.AceFlags = 0;
+
+ DenyPebblesSetColor->Mask = SET_WIDGET_COLOR;
+
+ RtlCopySid(
+ SeLengthSid( PebblesSid ),
+ &(DenyPebblesSetColor->SidStart),
+ PebblesSid );
+
+
+// AllowFredSetColor
+
+ AllowFredSetColorLength = (USHORT)(sizeof( ACCESS_ALLOWED_ACE ) - sizeof( ULONG ) +
+ SeLengthSid( FredSid ));
+
+ AllowFredSetColor = (PVOID) TstAllocatePool ( PagedPool, AllowFredSetColorLength );
+
+ AllowFredSetColor->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
+ AllowFredSetColor->Header.AceSize = AllowFredSetColorLength;
+ AllowFredSetColor->Header.AceFlags = 0;
+
+ AllowFredSetColor->Mask = SET_WIDGET_COLOR;
+
+ RtlCopySid(
+ SeLengthSid( FredSid ),
+ &(AllowFredSetColor->SidStart),
+ FredSid );
+
+
+
+
+// AllowPebblesSetColor
+
+
+ AllowPebblesSetColorLength = (USHORT)(sizeof( ACCESS_ALLOWED_ACE ) - sizeof( ULONG ) +
+ SeLengthSid( PebblesSid ));
+
+ AllowPebblesSetColor = (PVOID) TstAllocatePool ( PagedPool, AllowPebblesSetColorLength );
+
+ AllowPebblesSetColor->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
+ AllowPebblesSetColor->Header.AceSize = AllowPebblesSetColorLength;
+ AllowPebblesSetColor->Header.AceFlags = 0;
+
+ AllowPebblesSetColor->Mask = SET_WIDGET_COLOR;
+
+ RtlCopySid(
+ SeLengthSid( PebblesSid ),
+ &(AllowPebblesSetColor->SidStart),
+ PebblesSid );
+
+
+// DenyFredSetColor
+
+ DenyFredSetColorLength = (USHORT)(sizeof( ACCESS_DENIED_ACE ) - sizeof( ULONG ) +
+ SeLengthSid( FredSid ));
+
+ DenyFredSetColor = (PVOID) TstAllocatePool ( PagedPool, DenyFredSetColorLength );
+
+ DenyFredSetColor->Header.AceType = ACCESS_DENIED_ACE_TYPE;
+ DenyFredSetColor->Header.AceSize = DenyFredSetColorLength;
+ DenyFredSetColor->Header.AceFlags = 0;
+
+ DenyFredSetColor->Mask = SET_WIDGET_COLOR;
+
+ RtlCopySid(
+ SeLengthSid( FredSid ),
+ &(DenyFredSetColor->SidStart),
+ FredSid );
+
+// AllowBarneySetSize
+
+ AllowBarneySetSizeLength = (USHORT)(sizeof( ACCESS_ALLOWED_ACE ) - sizeof( ULONG ) +
+ SeLengthSid( BarneySid ));
+
+ AllowBarneySetSize = (PVOID) TstAllocatePool ( PagedPool, AllowBarneySetSizeLength );
+
+ AllowBarneySetSize->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
+ AllowBarneySetSize->Header.AceSize = AllowBarneySetSizeLength;
+ AllowBarneySetSize->Header.AceFlags = 0;
+
+ AllowBarneySetSize->Mask = SET_WIDGET_SIZE;
+
+ RtlCopySid(
+ SeLengthSid( BarneySid ),
+ &(AllowBarneySetSize->SidStart),
+ BarneySid );
+
+// AllowPebblesSetSize
+
+ AllowPebblesSetSizeLength = (USHORT)(sizeof( ACCESS_ALLOWED_ACE ) - sizeof( ULONG ) +
+ SeLengthSid( PebblesSid ));
+
+ AllowPebblesSetSize = (PVOID) TstAllocatePool ( PagedPool, AllowPebblesSetSizeLength );
+
+ AllowPebblesSetSize->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
+ AllowPebblesSetSize->Header.AceSize = AllowPebblesSetSizeLength;
+ AllowPebblesSetSize->Header.AceFlags = 0;
+
+ AllowPebblesSetSize->Mask = SET_WIDGET_SIZE;
+
+ RtlCopySid(
+ SeLengthSid( PebblesSid ),
+ &(AllowPebblesSetSize->SidStart),
+ PebblesSid );
+
+
+// AllowPebblesGetSize
+
+ AllowPebblesGetSizeLength = (USHORT)(sizeof( ACCESS_ALLOWED_ACE ) - sizeof( ULONG ) +
+ SeLengthSid( PebblesSid ));
+
+ AllowPebblesGetSize = (PVOID) TstAllocatePool ( PagedPool, AllowPebblesGetSizeLength );
+
+ AllowPebblesGetSize->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
+ AllowPebblesGetSize->Header.AceSize = AllowPebblesGetSizeLength;
+ AllowPebblesGetSize->Header.AceFlags = 0;
+
+ AllowPebblesGetSize->Mask = SET_WIDGET_SIZE;
+
+ RtlCopySid(
+ SeLengthSid( PebblesSid ),
+ &(AllowPebblesGetSize->SidStart),
+ PebblesSid );
+
+
+// AllowPebblesGetColor
+
+ AllowPebblesGetColorLength = (USHORT)(sizeof( ACCESS_ALLOWED_ACE ) - sizeof( ULONG ) +
+ SeLengthSid( PebblesSid ));
+
+ AllowPebblesGetColor = (PVOID) TstAllocatePool ( PagedPool, AllowPebblesGetColorLength );
+
+ AllowPebblesGetColor->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
+ AllowPebblesGetColor->Header.AceSize = AllowPebblesGetColorLength;
+ AllowPebblesGetColor->Header.AceFlags = 0;
+
+ AllowPebblesGetColor->Mask = SET_WIDGET_COLOR;
+
+ RtlCopySid(
+ SeLengthSid( PebblesSid ),
+ &(AllowPebblesGetColor->SidStart),
+ PebblesSid );
+
+//
+// Create some ACLs that we can put into a Security Descriptor
+//
+ DbgBreakPoint();
+
+//
+// Dacl
+//
+// +----------------+ +----------------+ +----------------+
+// | 1st ACE | | 2nd ACE | | 3rd ACE |
+// +----------------+ +----------------+ +----------------+
+// | AccessAllowed | | AccessDenied | | AccessAllowed |
+// +----------------+ +----------------+ +----------------+
+// | BARNEY | | PEBBLES | | FRED |
+// +----------------+ +----------------+ +----------------+
+// | SetWidgeColor | | SetWidgeColor | | SetWidgeColor |
+// +----------------+ +----------------+ +----------------+
+//
+
+ Dacl = (PACL) TstAllocatePool ( PagedPool, 2048 );
+
+ RtlCreateAcl( Dacl, 2048, ACL_REVISION);
+
+
+ RtlAddAce ( Dacl,
+ ACL_REVISION,
+ 0,
+ AllowBarneySetColor,
+ AllowBarneySetColorLength );
+
+ RtlAddAce ( Dacl,
+ ACL_REVISION,
+ 1,
+ DenyPebblesSetColor,
+ DenyPebblesSetColorLength );
+
+ RtlAddAce ( Dacl,
+ ACL_REVISION,
+ 2,
+ DenyFredSetColor,
+ AllowFredSetColorLength );
+
+ DumpAcl (Dacl);
+
+
+
+
+
+// Create a security descriptor
+//
+// Owner = Pebbles
+// Group = Flintstone
+// Dacl = Dacl
+// Sacl = NULL
+//
+
+ Widget1SecurityDescriptor =
+ (PSECURITY_DESCRIPTOR)TstAllocatePool( PagedPool, 1024 );
+
+ RtlCreateSecurityDescriptor( Widget1SecurityDescriptor,
+ 1 );
+
+
+ RtlSetOwnerSecurityDescriptor( Widget1SecurityDescriptor,
+ PebblesSid,
+ FALSE );
+
+ RtlSetGroupSecurityDescriptor( Widget1SecurityDescriptor,
+ FlintstoneSid,
+ FALSE );
+
+ RtlSetDaclSecurityDescriptor( Widget1SecurityDescriptor,
+ TRUE,
+ Dacl,
+ FALSE );
+
+ RtlSetSaclSecurityDescriptor( Widget1SecurityDescriptor,
+ FALSE,
+ NULL,
+ NULL );
+
+// See if Pebbles is allowed SET_WIDGET_COLOR (should be denied)
+
+ Status = NtAccessCheck( Widget1SecurityDescriptor,
+ PrimaryToken,
+ (ACCESS_MASK) SET_WIDGET_COLOR,
+ &GrantedAccess,
+ &AccessStatus );
+
+// DbgBreakPoint();
+
+ ASSERT(NT_SUCCESS(Status));
+
+ ASSERT(!NT_SUCCESS(AccessStatus));
+
+ ASSERT(GrantedAccess == NULL);
+
+
+// Update Dacl to be the following:
+//
+// Dacl2
+//
+// +----------------+ +----------------+ +----------------+
+// | 1st ACE | | 2nd ACE | | 3rd ACE |
+// +----------------+ +----------------+ +----------------+
+// | AccessAllowed | | AccessAllowed | | AccessDenied |
+// +----------------+ +----------------+ +----------------+
+// | BARNEY | | PEBBLES | | FRED |
+// +----------------+ +----------------+ +----------------+
+// | SetWidgeColor | | SetWidgeColor | | SetWidgeColor |
+// +----------------+ +----------------+ +----------------+
+//
+
+// Delete 2nd Ace
+
+ RtlDeleteAce (Dacl, 1);
+
+ RtlAddAce ( Dacl,
+ ACL_REVISION,
+ 1,
+ AllowPebblesSetColor,
+ AllowPebblesSetColorLength );
+
+ RtlDeleteAce ( Dacl, 2 );
+
+ RtlAddAce ( Dacl,
+ ACL_REVISION,
+ 1,
+ DenyFredSetColor,
+ DenyFredSetColorLength );
+
+
+
+
+// Change the security descriptor to use updated Dacl
+//
+// Owner = Pebbles
+// Group = Flintstone
+// Dacl = Dacl2
+// Sacl = NULL
+//
+
+ RtlSetDaclSecurityDescriptor( Widget1SecurityDescriptor,
+ TRUE,
+ Dacl,
+ FALSE );
+
+// See if Pebbles is allowed SET_WIDGET_COLOR (should be permitted)
+
+ Status = NtAccessCheck( Widget1SecurityDescriptor,
+ PrimaryToken,
+ (ACCESS_MASK) SET_WIDGET_COLOR,
+ &GrantedAccess,
+ &AccessStatus );
+
+
+ ASSERT(NT_SUCCESS(Status));
+
+ ASSERT(NT_SUCCESS(AccessStatus));
+
+ ASSERT(GrantedAccess == (ACCESS_MASK)SET_WIDGET_COLOR);
+
+//
+// Dacl3
+//
+// +----------------+ +----------------+ +----------------+
+// | 1st ACE | | 2nd ACE | | 3rd ACE |
+// +----------------+ +----------------+ +----------------+
+// | AccessAllowed | | AccessAllowed | | AccessDenied |
+// +----------------+ +----------------+ +----------------+
+// | BARNEY | | PEBBLES | | FRED |
+// +----------------+ +----------------+ +----------------+
+// | SetWidgeColor | | SetWidgeColor | | SetWidgeColor |
+// +----------------+ +----------------+ +----------------+
+//
+// +----------------+ +----------------+
+// | 4th ACE | | 5th ACE |
+// +----------------+ +----------------+
+// | AccessAllowed | | AccessAllowed |
+// +----------------+ +----------------+
+// | BARNEY | | PEBBLES |
+// +----------------+ +----------------+
+// | SetWidgeSize | | SetWidgeSize |
+// +----------------+ +----------------+
+//
+
+
+ RtlAddAce ( Dacl,
+ ACL_REVISION,
+ MAXULONG,
+ AllowBarneySetSize,
+ AllowBarneySetSizeLength );
+
+ RtlAddAce ( Dacl,
+ ACL_REVISION,
+ MAXULONG,
+ AllowPebblesSetSize,
+ AllowPebblesSetSizeLength );
+
+// Change the security descriptor to use Dacl3
+//
+// Owner = Pebbles
+// Group = Flintstone
+// Dacl = Dacl3
+// Sacl = NULL
+//
+
+ RtlSetDaclSecurityDescriptor( Widget1SecurityDescriptor,
+ TRUE,
+ Dacl,
+ FALSE );
+
+// Request MAXIMUM_ACCESS for Pebbles. Should get back SetWidgetSize
+// and SetWidgetColor
+
+ Status = NtAccessCheck( Widget1SecurityDescriptor,
+ PrimaryToken,
+ (ACCESS_MASK) MAXIMUM_ALLOWED,
+ &GrantedAccess,
+ &AccessStatus );
+
+
+ ASSERT(NT_SUCCESS(Status));
+
+ ASSERT(NT_SUCCESS(AccessStatus));
+
+ ASSERT(GrantedAccess == (ACCESS_MASK) (SET_WIDGET_COLOR | SET_WIDGET_SIZE));
+
+
+//
+// Dacl4
+//
+// +----------------+ +----------------+ +----------------+
+// | 1st ACE | | 2nd ACE | | 3rd ACE |
+// +----------------+ +----------------+ +----------------+
+// | AccessAllowed | | AccessAllowed | | AccessDenied |
+// +----------------+ +----------------+ +----------------+
+// | BARNEY | | PEBBLES | | FRED |
+// +----------------+ +----------------+ +----------------+
+// | SetWidgeColor | | SetWidgeColor | | SetWidgeColor |
+// +----------------+ +----------------+ +----------------+
+//
+// +----------------+ +----------------+ +----------------+
+// | 4th ACE | | 5th ACE | | 6th ACE |
+// +----------------+ +----------------+ +----------------+
+// | AccessAllowed | | AccessAllowed | | AccessDenied |
+// +----------------+ +----------------+ +----------------+
+// | BARNEY | | PEBBLES | | PEBBLES |
+// +----------------+ +----------------+ +----------------+
+// | SetWidgeSize | | SetWidgeSize | | SetWidgeColor |
+// +----------------+ +----------------+ +----------------+
+//
+
+ RtlAddAce ( Dacl,
+ ACL_REVISION,
+ MAXULONG,
+ DenyPebblesSetColor,
+ DenyPebblesSetColorLength );
+
+ RtlSetDaclSecurityDescriptor( Widget1SecurityDescriptor,
+ TRUE,
+ Dacl,
+ FALSE );
+
+// Request MAXIMUM_ACCESS for Pebbles. Should get back SetWidgetSize
+// and SetWidgetColor
+
+ Status = NtAccessCheck( Widget1SecurityDescriptor,
+ PrimaryToken,
+ (ACCESS_MASK) MAXIMUM_ALLOWED,
+ &GrantedAccess,
+ &AccessStatus );
+
+
+ ASSERT(NT_SUCCESS(Status));
+
+ ASSERT(NT_SUCCESS(AccessStatus));
+
+ ASSERT(GrantedAccess == (ACCESS_MASK) (SET_WIDGET_COLOR | SET_WIDGET_SIZE));
+
+
+//
+// Dacl5
+//
+// +----------------+ +----------------+ +----------------+
+// | 1st ACE | | 2nd ACE | | 3rd ACE |
+// +----------------+ +----------------+ +----------------+
+// | AccessAllowed | | AccessDenied | | AccessDenied |
+// +----------------+ +----------------+ +----------------+
+// | BARNEY | | PEBBLES | | FRED |
+// +----------------+ +----------------+ +----------------+
+// | SetWidgeColor | | SetWidgeColor | | SetWidgeColor |
+// +----------------+ +----------------+ +----------------+
+//
+// +----------------+ +----------------+ +----------------+
+// | 4th ACE | | 5th ACE | | 6th ACE |
+// +----------------+ +----------------+ +----------------+
+// | AccessAllowed | | AccessAllowed | | AccessAllowed |
+// +----------------+ +----------------+ +----------------+
+// | BARNEY | | PEBBLES | | PEBBLES |
+// +----------------+ +----------------+ +----------------+
+// | SetWidgeSize | | SetWidgeSize | | SetWidgeColor |
+// +----------------+ +----------------+ +----------------+
+//
+
+ RtlDeleteAce (Dacl, 1);
+
+ RtlAddAce ( Dacl,
+ ACL_REVISION,
+ 1,
+ DenyPebblesSetColor,
+ DenyPebblesSetColorLength );
+
+ RtlDeleteAce (Dacl, 5);
+
+ RtlAddAce ( Dacl,
+ ACL_REVISION,
+ MAXULONG,
+ AllowPebblesSetColor,
+ AllowPebblesSetColorLength );
+
+
+ DumpAcl ( Dacl );
+
+ RtlSetDaclSecurityDescriptor( Widget1SecurityDescriptor,
+ TRUE,
+ Dacl,
+ FALSE );
+
+// Request MAXIMUM_ACCESS for Pebbles. Should get back SetWidgetSize
+
+ Status = NtAccessCheck( Widget1SecurityDescriptor,
+ PrimaryToken,
+ (ACCESS_MASK) MAXIMUM_ALLOWED,
+ &GrantedAccess,
+ &AccessStatus );
+
+
+ ASSERT(NT_SUCCESS(Status));
+
+ ASSERT(NT_SUCCESS(AccessStatus));
+
+ ASSERT(GrantedAccess == (ACCESS_MASK) SET_WIDGET_SIZE);
+
+
+//
+// Dacl6
+//
+// +----------------+ +----------------+ +----------------+
+// | 1st ACE | | 2nd ACE | | 3rd ACE |
+// +----------------+ +----------------+ +----------------+
+// | AccessAllowed | | AccessDenied | | AccessDenied |
+// +----------------+ +----------------+ +----------------+
+// | BARNEY | | PEBBLES | | FRED |
+// +----------------+ +----------------+ +----------------+
+// | SetWidgeColor | | SetWidgeColor | | SetWidgeColor |
+// +----------------+ +----------------+ +----------------+
+//
+// +----------------+ +----------------+ +----------------+
+// | 4th ACE | | 5th ACE | | 6th ACE |
+// +----------------+ +----------------+ +----------------+
+// | AccessAllowed | | AccessAllowed | | AccessAllowed |
+// +----------------+ +----------------+ +----------------+
+// | BARNEY | | PEBBLES | | PEBBLES |
+// +----------------+ +----------------+ +----------------+
+// | SetWidgeSize | | SetWidgeSize | | SetWidgeColor |
+// +----------------+ +----------------+ +----------------+
+//
+// +----------------+ +----------------+
+// | 7th ACE | | 8th ACE |
+// +----------------+ +----------------+
+// | AccessAllowed | | AccessAllowed |
+// +----------------+ +----------------+
+// | PEBBLES | | PEBBLES |
+// +----------------+ +----------------+
+// | GetWidgeSize | | GetWidgeColor |
+// +----------------+ +----------------+
+//
+
+ RtlAddAce ( Dacl,
+ ACL_REVISION,
+ MAXULONG,
+ AllowPebblesGetSize,
+ AllowPebblesGetSizeLength );
+
+ RtlAddAce ( Dacl,
+ ACL_REVISION,
+ MAXULONG,
+ AllowPebblesGetColor,
+ AllowPebblesGetColorLength );
+
+ DumpAcl ( Dacl );
+
+ RtlSetDaclSecurityDescriptor( Widget1SecurityDescriptor,
+ TRUE,
+ Dacl,
+ FALSE );
+
+// Request MAXIMUM_ACCESS for Pebbles. Should get back SetWidgetSize
+
+ Status = NtAccessCheck( Widget1SecurityDescriptor,
+ PrimaryToken,
+ (ACCESS_MASK) MAXIMUM_ALLOWED,
+ &GrantedAccess,
+ &AccessStatus );
+
+
+ ASSERT(NT_SUCCESS(Status));
+
+ ASSERT(NT_SUCCESS(AccessStatus));
+
+ ASSERT(GrantedAccess == (ACCESS_MASK) SET_WIDGET_SIZE);
+
+
+
+ return(TRUE);
+
+
+}
+
+
+/////////////////////////////////////////////////////////////////////
+// //
+// //
+// Main test entry point //
+// //
+// //
+/////////////////////////////////////////////////////////////////////
+
+
+BOOLEAN
+CTAccess()
+{
+
+ BOOLEAN Result = TRUE;
+
+ if (!TSeVariableInitialization()) {
+ DbgPrint("Se: Failed to initialize global test variables.\n");
+ return FALSE;
+ }
+
+ DbgPrint("Se: Initialization...");
+ TestTokenInitialize();
+ CreateDAclToken();
+
+}
+
+
+//
+// Debug support routine
+//
+
+
+typedef struct _STANDARD_ACE {
+ ACE_HEADER Header;
+ ACCESS_MASK Mask;
+ PSID Sid;
+} STANDARD_ACE;
+typedef STANDARD_ACE *PSTANDARD_ACE;
+
+
+
+VOID
+DumpAcl (
+ IN PACL Acl
+ )
+
+/*++
+
+Routine Description:
+
+ This routine dumps via (DbgPrint) an Acl for debug purposes. It is
+ specialized to dump standard aces.
+
+Arguments:
+
+ Acl - Supplies the Acl to dump
+
+Return Value:
+
+ None
+
+--*/
+
+
+{
+ ULONG i;
+ PSTANDARD_ACE Ace;
+
+ DbgPrint("DumpAcl @ %8lx", Acl);
+
+ //
+ // Check if the Acl is null
+ //
+
+ if (Acl == NULL) {
+
+ return;
+
+ }
+
+ //
+ // Dump the Acl header
+ //
+
+ DbgPrint(" Revision: %02x", Acl->AclRevision);
+ DbgPrint(" Size: %04x", Acl->AclSize);
+ DbgPrint(" AceCount: %04x\n", Acl->AceCount);
+
+ //
+ // Now for each Ace we want do dump it
+ //
+
+ for (i = 0, Ace = FirstAce(Acl);
+ i < Acl->AceCount;
+ i++, Ace = NextAce(Ace) ) {
+
+ //
+ // print out the ace header
+ //
+
+ DbgPrint(" AceHeader: %08lx ", *(PULONG)Ace);
+
+ //
+ // special case on the standard ace types
+ //
+
+ if ((Ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE) ||
+ (Ace->Header.AceType == ACCESS_DENIED_ACE_TYPE) ||
+ (Ace->Header.AceType == SYSTEM_AUDIT_ACE_TYPE) ||
+ (Ace->Header.AceType == SYSTEM_ALARM_ACE_TYPE)) {
+
+ //
+ // The following array is indexed by ace types and must
+ // follow the allowed, denied, audit, alarm seqeuence
+ //
+
+ static PCHAR AceTypes[] = { "Access Allowed",
+ "Access Denied ",
+ "System Audit ",
+ "System Alarm "
+ };
+
+ DbgPrint(AceTypes[Ace->Header.AceType]);
+ DbgPrint("\nAccess Mask: %08lx ", Ace->Mask);
+
+ } else {
+
+ DbgPrint("Unknown Ace Type\n");
+
+ }
+
+ DbgPrint("\n");
+
+ DbgPrint("AceSize = %d\n",Ace->Header.AceSize);
+ DbgPrint("Ace Flags = ");
+ if (Ace->Header.AceFlags & OBJECT_INHERIT_ACE) {
+ DbgPrint("OBJECT_INHERIT_ACE\n");
+ DbgPrint(" ");
+ }
+ if (Ace->Header.AceFlags & CONTAINER_INHERIT_ACE) {
+ DbgPrint("CONTAINER_INHERIT_ACE\n");
+ DbgPrint(" ");
+ }
+
+ if (Ace->Header.AceFlags & NO_PROPAGATE_INHERIT_ACE) {
+ DbgPrint("NO_PROPAGATE_INHERIT_ACE\n");
+ DbgPrint(" ");
+ }
+
+ if (Ace->Header.AceFlags & INHERIT_ONLY_ACE) {
+ DbgPrint("INHERIT_ONLY_ACE\n");
+ DbgPrint(" ");
+ }
+
+
+ if (Ace->Header.AceFlags & SUCCESSFUL_ACCESS_ACE_FLAG) {
+ DbgPrint("SUCCESSFUL_ACCESS_ACE_FLAG\n");
+ DbgPrint(" ");
+ }
+
+ if (Ace->Header.AceFlags & FAILED_ACCESS_ACE_FLAG) {
+ DbgPrint("FAILED_ACCESS_ACE_FLAG\n");
+ DbgPrint(" ");
+ }
+
+ DbgPrint("\n");
+
+
+ }
+
+}
diff --git a/private/ntos/se/ctlnpqos.c b/private/ntos/se/ctlnpqos.c
new file mode 100644
index 000000000..ffaf1520d
--- /dev/null
+++ b/private/ntos/se/ctlnpqos.c
@@ -0,0 +1,1729 @@
+
+//////////////////////////////////////////////////////////////////////////////
+// //
+// Global Definitions //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+#define DevPrint
+//#define DevPrint DbgPrint
+
+#define Error(N,S) { DbgPrint(#N); DbgPrint(" Error %08lx\n", S); }
+
+#define Delay(SECONDS) { \
+ LARGE_INTEGER Time; \
+ Time.QuadPart = -10 * 1000 * 1000, ((LONGLONG)SECONDS); \
+ NtDelayExecution(TRUE,(PLARGE_INTEGER)&Time); \
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+// //
+// Global Variables //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ STRING EventName;
+ UNICODE_STRING UnicodeEventName;
+ HANDLE EventHandle;
+ STRING PortName;
+ UNICODE_STRING UnicodePortName;
+ STRING RelativePortName;
+ UNICODE_STRING UnicodeRelativePortName;
+ HANDLE EarPort;
+ HANDLE TalkPort;
+ SECURITY_QUALITY_OF_SERVICE SecurityQos;
+ ULONG RequestCount;
+ HANDLE ClientToken;
+ TOKEN_STATISTICS ClientTokenStatistics;
+ ULONG IgnoreLength;
+
+ HANDLE SepServerThread;
+
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+// //
+// Test Routine Definitions //
+// //
+//////////////////////////////////////////////////////////////////////////////
+BOOLEAN
+SepClientTestStatic(VOID);
+
+BOOLEAN
+SepClientTestDynamic(VOID);
+
+BOOLEAN
+SepClientTestEffectiveOnly(
+ BOOLEAN StaticTest
+ );
+
+BOOLEAN
+SepClientTestNotEffectiveOnly(
+ BOOLEAN StaticTest
+ );
+
+BOOLEAN
+SepClientTestAnonymous(
+ BOOLEAN StaticTest,
+ BOOLEAN EffectiveOnly
+ );
+
+BOOLEAN
+SepClientTestIdentification(
+ BOOLEAN StaticTest,
+ BOOLEAN EffectiveOnly
+ );
+
+BOOLEAN
+SepClientTestImpersonation(
+ BOOLEAN StaticTest,
+ BOOLEAN EffectiveOnly
+ );
+
+VOID
+SepClientConnect(
+ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
+ SECURITY_CONTEXT_TRACKING_MODE TrackingMode,
+ BOOLEAN EffectiveOnly
+ );
+
+VOID
+SepClientMakeRemoteCall( VOID );
+
+VOID
+SepClientDropConnection( VOID );
+
+BOOLEAN
+SepClientTest(VOID);
+
+NTSTATUS
+SepClientInitialize(
+ );
+
+
+
+
+
+
+BOOLEAN
+SepServerTestStatic(VOID);
+
+BOOLEAN
+SepServerTestDynamic(VOID);
+
+BOOLEAN
+SepServerTestEffectiveOnly(
+ BOOLEAN StaticTest
+ );
+
+BOOLEAN
+SepServerTestNotEffectiveOnly(
+ BOOLEAN StaticTest
+ );
+
+BOOLEAN
+SepServerTestAnonymous(
+ BOOLEAN StaticTest,
+ BOOLEAN EffectiveOnly
+ );
+
+BOOLEAN
+SepServerTestIdentification(
+ BOOLEAN StaticTest,
+ BOOLEAN EffectiveOnly
+ );
+
+BOOLEAN
+SepServerTestImpersonation(
+ BOOLEAN StaticTest,
+ BOOLEAN EffectiveOnly
+ );
+
+VOID
+SepServerWaitForNextConnect( VOID );
+
+VOID
+SepServerGetNextMessage( VOID );
+
+VOID
+SepServerCompleteMessage( VOID );
+
+VOID
+SepServerDropConnection( VOID );
+
+
+
+BOOLEAN
+SepServerTest(VOID);
+
+NTSTATUS
+SepServerInitialize(
+ );
+
+VOID
+SepServerSpawnClientProcess(VOID);
+
+
+
+
+VOID
+SepWritePipe( PSZ String );
+
+VOID
+SepReadPipe(VOID);
+
+VOID
+SepTransceivePipe( PSZ String );
+
+
+
+
+HANDLE
+SepServerCreatePipe(VOID);
+
+VOID
+SepServerListenPipe(VOID);
+
+VOID
+SepServerImpersonatePipe(VOID);
+
+VOID
+SepServerDisconnectPipe(VOID);
+
+
+
+
+HANDLE
+SepClientOpenPipe( VOID );
+
+
+
+
+BOOLEAN
+CtLnpQos (VOID);
+
+
+//////////////////////////////////////////////////////////////////////////////
+// //
+// Client-Side Test Routines //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+
+VOID
+SepClientConnect(
+ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
+ SECURITY_CONTEXT_TRACKING_MODE TrackingMode,
+ BOOLEAN EffectiveOnly
+ )
+
+{
+
+ SecurityQos.ImpersonationLevel = ImpersonationLevel;
+ SecurityQos.ContextTrackingMode = TrackingMode;
+ SecurityQos.EffectiveOnly = EffectiveOnly;
+
+ DevPrint("\nClient: ");
+ TalkPort = SepClientOpenPipe();
+
+ return;
+}
+
+
+VOID
+SepClientMakeRemoteCall( VOID )
+
+{
+
+ DevPrint("\nClient: ");
+ SepTransceivePipe( "Make Client Call\n" );
+
+ RequestCount += 1;
+
+ return;
+}
+
+
+VOID
+SepClientDropConnection( VOID )
+
+{
+
+ Status = NtClose( TalkPort ); SEASSERT_SUCCESS(Status);
+
+ return;
+
+}
+
+
+BOOLEAN
+SepClientTestStatic(VOID)
+
+{
+
+ BOOLEAN CompletionStatus;
+
+ //
+ // Static Context Tracking ... Suite
+ //
+
+ CompletionStatus = SepClientTestEffectiveOnly( TRUE );
+
+
+ if (CompletionStatus == TRUE) {
+
+ CompletionStatus = SepClientTestNotEffectiveOnly( TRUE );
+ }
+
+ return CompletionStatus;
+
+}
+
+
+BOOLEAN
+SepClientTestDynamic(VOID)
+
+{
+ BOOLEAN CompletionStatus;
+
+ //
+ // Dynamic Context Tracking ... Suite
+ //
+
+ CompletionStatus = SepClientTestEffectiveOnly( FALSE );
+
+
+ if (CompletionStatus == TRUE) {
+
+ CompletionStatus = SepClientTestNotEffectiveOnly( FALSE );
+ }
+
+ return CompletionStatus;
+
+}
+
+
+BOOLEAN
+SepClientTestEffectiveOnly(
+ BOOLEAN StaticTest
+ )
+
+
+{
+
+ BOOLEAN CompletionStatus;
+
+ //
+ // Effective Only ... Test
+ //
+
+ CompletionStatus = SepClientTestAnonymous( StaticTest, TRUE );
+ if (CompletionStatus == TRUE) {
+ CompletionStatus = SepClientTestIdentification( StaticTest, TRUE );
+ }
+ if (CompletionStatus == TRUE) {
+ CompletionStatus = SepClientTestImpersonation( StaticTest, TRUE );
+ }
+
+ return CompletionStatus;
+
+}
+
+
+BOOLEAN
+SepClientTestNotEffectiveOnly(
+ BOOLEAN StaticTest
+ )
+
+{
+ BOOLEAN CompletionStatus;
+
+ //
+ // Not Effective Only ... Test
+ //
+
+ CompletionStatus = SepClientTestAnonymous( StaticTest, FALSE );
+ if (CompletionStatus == TRUE) {
+ CompletionStatus = SepClientTestIdentification( StaticTest, FALSE );
+ }
+ if (CompletionStatus == TRUE) {
+ CompletionStatus = SepClientTestImpersonation( StaticTest, FALSE );
+ }
+
+ return CompletionStatus;
+
+}
+
+
+BOOLEAN
+SepClientTestAnonymous(
+ BOOLEAN StaticTest,
+ BOOLEAN EffectiveOnly
+ )
+
+{
+
+ //////////////////////////////////////////////////////////////////////////
+ // //
+ // Anonymous Use Test //
+ // //
+ //////////////////////////////////////////////////////////////////////////
+
+ SECURITY_CONTEXT_TRACKING_MODE TrackingMode;
+
+ if (StaticTest) {
+ TrackingMode = SECURITY_STATIC_TRACKING;
+ } else {
+ TrackingMode = SECURITY_DYNAMIC_TRACKING;
+ }
+
+ if (!StaticTest) {
+ //
+ // No action for dynamic test
+ //
+ return TRUE;
+ }
+
+ //
+ // Anonymous Use ... Test
+ //
+
+
+ SepClientConnect(
+ SecurityAnonymous,
+ TrackingMode,
+ EffectiveOnly
+ );
+
+ SepClientMakeRemoteCall();
+
+ SepClientDropConnection();
+
+
+ return TRUE;
+}
+
+
+BOOLEAN
+SepClientTestIdentification(
+ BOOLEAN StaticTest,
+ BOOLEAN EffectiveOnly
+ )
+
+{
+
+ //////////////////////////////////////////////////////////////////////////
+ // //
+ // Identification Use Test //
+ // //
+ //////////////////////////////////////////////////////////////////////////
+
+ SECURITY_CONTEXT_TRACKING_MODE TrackingMode;
+
+ if (StaticTest) {
+ TrackingMode = SECURITY_STATIC_TRACKING;
+ } else {
+ TrackingMode = SECURITY_DYNAMIC_TRACKING;
+ }
+
+ //
+ // Identification Use ... Test
+ //
+
+
+ SepClientConnect(
+ SecurityIdentification,
+ TrackingMode,
+ EffectiveOnly
+ );
+
+ SepClientMakeRemoteCall();
+
+ SepClientDropConnection();
+
+
+ return TRUE;
+
+}
+
+
+BOOLEAN
+SepClientTestImpersonation(
+ BOOLEAN StaticTest,
+ BOOLEAN EffectiveOnly
+ )
+
+{
+
+ //////////////////////////////////////////////////////////////////////////
+ // //
+ // Impersonation Use Test //
+ // //
+ //////////////////////////////////////////////////////////////////////////
+
+ SECURITY_CONTEXT_TRACKING_MODE TrackingMode;
+
+ if (StaticTest) {
+ TrackingMode = SECURITY_STATIC_TRACKING;
+ } else {
+ TrackingMode = SECURITY_DYNAMIC_TRACKING;
+ }
+
+
+ //
+ // Impersonation Use ... Test
+ //
+
+
+ SepClientConnect(
+ SecurityImpersonation,
+ TrackingMode,
+ EffectiveOnly
+ );
+
+ SepClientMakeRemoteCall();
+
+ SepClientDropConnection();
+
+
+
+ return TRUE;
+
+}
+
+
+
+
+BOOLEAN
+SepClientTest(VOID)
+//
+// Tests:
+//
+// Static Context Tracking Tests
+// Effective Only
+// Anonymous
+// Identification
+// Impersonation
+// Not Effective Only
+// Anonymous
+// Identification
+// Impersonation
+//
+// Dynamic Context Tracking Tests
+// Effective Only
+// Identification
+// Impersonation
+// Not Effective Only
+// Identification
+// Impersonation
+//
+{
+
+ BOOLEAN CompletionStatus;
+
+
+
+
+ //
+ // Run the static test suite...
+ //
+
+ CompletionStatus = SepClientTestStatic();
+
+ //
+ // Run the dynamic test suite...
+ //
+
+ if (CompletionStatus == TRUE) {
+ CompletionStatus = SepClientTestDynamic();
+ }
+
+ DbgPrint("Se: Client Test Complete.\n");
+
+
+ return CompletionStatus;
+}
+
+
+NTSTATUS
+SepClientInitialize(
+ )
+
+{
+
+
+
+ DbgPrint("Se: Client Initializing ...\n");
+
+
+ RequestCount = 0;
+
+
+ //
+ // Signal the named event to start the test
+ //
+
+ DbgPrint("Se: Client Starting Test ...\n");
+ Status = NtSetEvent( EventHandle, NULL ); SEASSERT_SUCCESS(Status);
+
+ Status = NtClose( EventHandle ); SEASSERT_SUCCESS(Status);
+
+
+ return STATUS_SUCCESS;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+// //
+// Server-Side Test Routines //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+
+VOID
+SepServerWaitForNextConnect( VOID )
+{
+
+ DevPrint("\nServer: ");
+ SepServerListenPipe();
+
+ Status = NtDuplicateObject(
+ NtCurrentProcess(), // SourceProcessHandle
+ EarPort, // SourceHandle
+ NtCurrentProcess(), // TargetProcessHandle
+ &TalkPort, // TargetHandle
+ 0, // DesiredAccess (over-ridden by option)
+ 0, // HandleAttributes
+ DUPLICATE_SAME_ACCESS // Options
+ );
+ ASSERT(NT_SUCCESS(Status));
+
+
+ return;
+
+}
+
+VOID
+SepServerGetNextMessage( VOID )
+
+{
+
+
+
+ DevPrint("\nServer: ");
+ SepReadPipe();
+
+ RequestCount += 1;
+
+ return;
+}
+
+VOID
+SepServerCompleteMessage( VOID )
+
+{
+
+ DevPrint("\nServer: ");
+ SepWritePipe("Return From Server\n");
+ return;
+}
+
+VOID
+SepServerImpersonateClient( VOID )
+
+{
+
+ DevPrint("\nServer: ");
+ SepServerImpersonatePipe( );
+
+}
+
+
+VOID
+SepServerRevertToSelf( VOID )
+
+{
+ NTSTATUS TmpStatus;
+ HANDLE NullHandle;
+
+ NullHandle = NULL;
+ TmpStatus = NtSetInformationThread(
+ SepServerThread,
+ ThreadImpersonationToken,
+ (PVOID)&NullHandle,
+ (ULONG)sizeof(HANDLE)
+ ); SEASSERT_SUCCESS(TmpStatus);
+
+}
+
+
+VOID
+SepServerDropConnection( VOID )
+
+{
+ DevPrint("\nServer: ");
+ SepServerDisconnectPipe();
+
+ return;
+}
+
+BOOLEAN
+SepServerTestStatic(VOID)
+
+{
+ BOOLEAN CompletionStatus;
+
+ DbgPrint("Se: Static Context Tracking ... Suite\n");
+
+ CompletionStatus = SepServerTestEffectiveOnly( TRUE );
+
+
+ if (CompletionStatus == TRUE) {
+
+ CompletionStatus = SepServerTestNotEffectiveOnly( TRUE );
+ }
+
+ return CompletionStatus;
+
+}
+
+
+BOOLEAN
+SepServerTestDynamic(VOID)
+
+{
+ BOOLEAN CompletionStatus;
+
+ DbgPrint("Se: Dynamic Context Tracking ... Suite\n");
+
+ CompletionStatus = SepServerTestEffectiveOnly( FALSE );
+
+
+ if (CompletionStatus == TRUE) {
+
+ CompletionStatus = SepServerTestNotEffectiveOnly( FALSE );
+ }
+
+ return CompletionStatus;
+
+}
+
+
+BOOLEAN
+SepServerTestEffectiveOnly(
+ BOOLEAN StaticTest
+ )
+
+{
+
+ BOOLEAN CompletionStatus;
+
+ DbgPrint("Se: Effective Only ... Test\n");
+
+ CompletionStatus = SepServerTestAnonymous( StaticTest, TRUE );
+ if (CompletionStatus == TRUE) {
+ CompletionStatus = SepServerTestIdentification( StaticTest, TRUE );
+ }
+ if (CompletionStatus == TRUE) {
+ CompletionStatus = SepServerTestImpersonation( StaticTest, TRUE );
+ }
+
+ return CompletionStatus;
+
+}
+
+
+BOOLEAN
+SepServerTestNotEffectiveOnly(
+ BOOLEAN StaticTest
+ )
+
+{
+
+ BOOLEAN CompletionStatus;
+
+ DbgPrint("Se: Not Effective Only ... Test\n");
+
+ CompletionStatus = SepServerTestAnonymous( StaticTest, FALSE );
+ if (CompletionStatus == TRUE) {
+ CompletionStatus = SepServerTestIdentification( StaticTest, FALSE );
+ }
+ if (CompletionStatus == TRUE) {
+ CompletionStatus = SepServerTestImpersonation( StaticTest, FALSE );
+ }
+
+ return CompletionStatus;
+
+}
+
+
+BOOLEAN
+SepServerTestAnonymous(
+ BOOLEAN StaticTest,
+ BOOLEAN EffectiveOnly
+ )
+
+{
+ BOOLEAN CompletionStatus = TRUE;
+
+ //////////////////////////////////////////////////////////////////////////
+ // //
+ // Anonymous Use Test //
+ // //
+ //////////////////////////////////////////////////////////////////////////
+
+
+ if (!StaticTest) {
+ //
+ // No action for dynamic test
+ //
+
+ return TRUE;
+ }
+
+ DbgPrint("Se: Anonymous Use ... ");
+
+ SepServerWaitForNextConnect();
+
+ SepServerGetNextMessage();
+
+
+ SepServerImpersonateClient();
+ Status = NtOpenThreadToken(
+ SepServerThread,
+ TOKEN_ALL_ACCESS,
+ TRUE,
+ &ClientToken
+ );
+ SepServerRevertToSelf();
+ if (Status == STATUS_CANT_OPEN_ANONYMOUS) {
+
+ DbgPrint(" Succeeded\n");
+
+ } else {
+ DbgPrint("* ! FAILED (srvr) ! *\n");
+ DbgPrint("Status is: 0x%lx \n", Status );
+ CompletionStatus = FALSE;
+ }
+
+
+ SepServerCompleteMessage();
+
+ SepServerDropConnection();
+
+ //
+ // Appease the compiler Gods..
+ //
+
+ if (EffectiveOnly) {;}
+
+
+ return CompletionStatus;
+
+}
+
+
+BOOLEAN
+SepServerTestIdentification(
+ BOOLEAN StaticTest,
+ BOOLEAN EffectiveOnly
+ )
+
+{
+
+ BOOLEAN CompletionStatus = TRUE;
+ //////////////////////////////////////////////////////////////////////////
+ // //
+ // Identification Use Test //
+ // //
+ //////////////////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Identification Use ... ");
+
+ SepServerWaitForNextConnect();
+
+ SepServerGetNextMessage();
+
+ SepServerImpersonateClient();
+ Status = NtOpenThreadToken(
+ SepServerThread,
+ TOKEN_ALL_ACCESS,
+ TRUE,
+ &ClientToken
+ ); SEASSERT_SUCCESS(Status);
+ SepServerRevertToSelf();
+ Status = NtQueryInformationToken(
+ ClientToken,
+ TokenStatistics,
+ &ClientTokenStatistics,
+ (ULONG)sizeof(TOKEN_STATISTICS),
+ &IgnoreLength
+ ); SEASSERT_SUCCESS(Status);
+
+ if ( (ClientTokenStatistics.TokenType == TokenImpersonation) &&
+ (ClientTokenStatistics.ImpersonationLevel == SecurityIdentification)
+ ) {
+ DbgPrint(" Succeeded\n");
+
+ } else {
+ DbgPrint("* ! FAILED (srvr) ! *\n");
+ CompletionStatus = FALSE;
+ }
+
+
+ SepServerCompleteMessage();
+
+ SepServerDropConnection();
+
+ //
+ // Appease the compiler Gods..
+ //
+ if (StaticTest) {;}
+ if (EffectiveOnly) {;}
+
+ return CompletionStatus;
+}
+
+
+BOOLEAN
+SepServerTestImpersonation(
+ BOOLEAN StaticTest,
+ BOOLEAN EffectiveOnly
+ )
+
+{
+ BOOLEAN CompletionStatus = TRUE;
+
+ //////////////////////////////////////////////////////////////////////////
+ // //
+ // Impersonation Use Test //
+ // //
+ //////////////////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Impersonation Use ... ");
+
+
+ SepServerWaitForNextConnect();
+
+ SepServerGetNextMessage();
+
+
+
+ SepServerImpersonateClient();
+ Status = NtOpenThreadToken(
+ SepServerThread,
+ TOKEN_ALL_ACCESS,
+ TRUE,
+ &ClientToken
+ ); SEASSERT_SUCCESS(Status);
+ SepServerRevertToSelf();
+ Status = NtQueryInformationToken(
+ ClientToken,
+ TokenStatistics,
+ &ClientTokenStatistics,
+ (ULONG)sizeof(TOKEN_STATISTICS),
+ &IgnoreLength
+ ); SEASSERT_SUCCESS(Status);
+
+ if ( (ClientTokenStatistics.TokenType == TokenImpersonation) &&
+ (ClientTokenStatistics.ImpersonationLevel == SecurityImpersonation)
+ ) {
+ DbgPrint(" Succeeded\n");
+
+ } else {
+ DbgPrint("* ! FAILED (srvr) ! *\n");
+ CompletionStatus = FALSE;
+ }
+
+
+
+
+ SepServerCompleteMessage();
+
+ SepServerDropConnection();
+
+ //
+ // Appease the compiler gods
+ //
+ if (StaticTest) {;}
+ if (EffectiveOnly) {;}
+
+ return CompletionStatus;
+}
+
+
+BOOLEAN
+SepServerTest(VOID)
+//
+// Tests:
+//
+// Static Context Tracking Tests
+// Effective Only
+// Anonymous
+// Identification
+// Impersonation
+// Not Effective Only
+// Anonymous
+// Identification
+// Impersonation
+//
+// Dynamic Context Tracking Tests
+// Effective Only
+// Identification
+// Impersonation
+// Not Effective Only
+// Identification
+// Impersonation
+//
+{
+
+ BOOLEAN CompletionStatus;
+
+
+ DbgPrint("Se: Server Starting Test ...\n");
+
+ //
+ // Run the static test suite...
+ //
+
+ CompletionStatus = SepServerTestStatic();
+
+ //
+ // Run the dynamic test suite...
+ //
+
+ if (CompletionStatus == TRUE) {
+ CompletionStatus = SepServerTestDynamic();
+ }
+
+ DbgPrint("Se: Server Test Complete.\n");
+
+ //
+ // Print test results
+ //
+
+ DbgPrint("\n");
+ DbgPrint("\n");
+ DbgPrint("**********************\n");
+ DbgPrint("** **\n");
+
+ if (CompletionStatus == TRUE) {
+ DbgPrint("** Test Succeeded **\n");
+ } else {
+ DbgPrint("** Test Failed !! **\n");
+ }
+
+ DbgPrint("** **\n");
+ DbgPrint("**********************\n");
+
+ return CompletionStatus;
+
+}
+
+NTSTATUS
+SepServerInitialize(
+ )
+
+{
+
+ OBJECT_ATTRIBUTES ThreadAttributes;
+ PTEB CurrentTeb;
+
+
+ DbgPrint("Se: Server Initializing ...\n");
+
+ //
+ // Initialize global variables
+ //
+
+ RequestCount = 0;
+
+ //
+ // Get a handle to our thread to so that we can access our thread
+ // even when impersonating an anonymous client (which we can't do
+ // using NtCurrentThread()).
+ //
+
+ CurrentTeb = NtCurrentTeb();
+ InitializeObjectAttributes(&ThreadAttributes, NULL, 0, NULL, NULL);
+ Status = NtOpenThread(
+ &SepServerThread, // TargetHandle
+ THREAD_ALL_ACCESS, // DesiredAccess
+ &ThreadAttributes, // ObjectAttributes
+ &CurrentTeb->ClientId // ClientId
+ );
+ ASSERT( NT_SUCCESS(Status) );
+
+
+ //
+ // Create the server's port
+ //
+
+ EarPort = SepServerCreatePipe();
+
+
+
+ //
+ // Spawn a copy of ourselves...
+ //
+
+ DbgPrint("Se: Server Spawning client process ...\n");
+ SepServerSpawnClientProcess();
+
+
+ DbgPrint("Se: Server waiting for start of test signal ...\n");
+
+ Status = NtWaitForSingleObject(
+ EventHandle,
+ TRUE,
+ NULL
+ ); SEASSERT_SUCCESS(Status);
+
+ Status = NtClose( EventHandle ); SEASSERT_SUCCESS(Status);
+
+
+ return STATUS_SUCCESS;
+}
+
+VOID
+SepServerSpawnClientProcess(VOID)
+
+{
+
+ RTL_USER_PROCESS_INFORMATION ProcessInformation;
+ STRING ProgramName;
+ UNICODE_STRING UnicodeProgramName;
+ STRING ImagePathName;
+ UNICODE_STRING UnicodeImagePathName;
+ PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
+
+ RtlInitString( &ProgramName, "\\SystemRoot\\Bin\\utlnpqos.exe" );
+ Status = RtlAnsiStringToUnicodeString(
+ &UnicodeProgramName,
+ &ProgramName,
+ TRUE ); SEASSERT_SUCCESS( NT_SUCCESS(Status) );
+ RtlInitString( &ImagePathName, "utlnpqos.exe");
+ Status = RtlAnsiStringToUnicodeString(
+ &UnicodeImagePathName,
+ &ImagePathName,
+ TRUE ); SEASSERT_SUCCESS( NT_SUCCESS(Status) );
+
+
+ Status = RtlCreateProcessParameters(
+ &ProcessParameters,
+ &ImagePathName, // FIX, FIX &UnicodeImagePathName, (when converted to unicode)
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ );
+
+ SEASSERT_SUCCESS(Status);
+ RtlFreeUnicodeString( &UnicodeImagePathName );
+
+
+ Status = RtlCreateUserProcess(
+ &ProgramName, // FIX, FIX &UnicodeProgramName (when converted to unicode)
+ ProcessParameters, // ProcessParameters
+ NULL, // ProcessSecurityDescriptor
+ NULL, // ThreadSecurityDescriptor
+ NtCurrentProcess(), // ParentProcess
+ FALSE, // InheritHandles
+ NULL, // DebugPort
+ NULL, // ExceptionPort
+ &ProcessInformation // ProcessInformation
+ ); SEASSERT_SUCCESS(Status);
+ RtlFreeUnicodeString( &UnicodeProgramName );
+
+ Status = NtResumeThread(
+ ProcessInformation.Thread,
+ NULL
+ ); SEASSERT_SUCCESS(Status);
+
+ RtlDestroyProcessParameters( ProcessParameters );
+
+}
+
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+// //
+// Main Program Entry Routine //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+BOOLEAN
+CtLnpQos (VOID)
+{
+
+ BOOLEAN Result = TRUE;
+
+ RtlInitString( &PortName, "\\Device\\NamedPipe\\TestLnpQosServerPort" );
+ Status = RtlAnsiStringToUnicodeString(
+ &UnicodePortName,
+ &PortName,
+ TRUE ); SEASSERT_SUCCESS( NT_SUCCESS(Status) );
+
+ RtlInitString( &RelativePortName, "TestLnpQosServerPort" );
+ Status = RtlAnsiStringToUnicodeString(
+ &UnicodeRelativePortName,
+ &RelativePortName,
+ TRUE ); SEASSERT_SUCCESS( NT_SUCCESS(Status) );
+
+
+ //
+ // Determine whether we are the client or server side of the test.
+ // This is done by creating or opening a named event object. If the
+ // event does not yet exist, then we are the client, and must create
+ // the server process. Otherwise, we are the server and the client
+ // is waiting for us to signal the event.
+ //
+
+ RtlInitString( &EventName, "\\TestLnpQosEvent" );
+ Status = RtlAnsiStringToUnicodeString(
+ &UnicodeEventName,
+ &EventName,
+ TRUE ); SEASSERT_SUCCESS( NT_SUCCESS(Status) );
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &UnicodeEventName,
+ OBJ_OPENIF,
+ NULL,
+ NULL
+ );
+ Status = NtCreateEvent(
+ &EventHandle,
+ EVENT_ALL_ACCESS,
+ &ObjectAttributes,
+ SynchronizationEvent,
+ FALSE
+ );
+ RtlFreeUnicodeString( &UnicodeEventName );
+
+ if (Status == STATUS_OBJECT_NAME_EXISTS) {
+
+ //
+ // Server is already running, therefore, this process gets to be
+ // the client.
+ //
+
+ Status = SepClientInitialize(); SEASSERT_SUCCESS(Status);
+ Result = SepClientTest();
+
+ } else {
+
+ SEASSERT_SUCCESS(Status);
+
+ //
+ // Event wasn't yet there, so we must be the server.
+ //
+
+ DbgPrint("Se: Starting Local Named Pipe Impersonation Test.\n");
+
+ Status = SepServerInitialize(); SEASSERT_SUCCESS(Status);
+ Result = SepServerTest();
+
+ DbgPrint("Se: End Test.\n");
+
+ }
+
+
+
+ Status = NtTerminateThread( NtCurrentThread(), STATUS_SUCCESS);
+ SEASSERT_SUCCESS(Status);
+
+ return Result;
+
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+// //
+// Named Pipe Common Operations //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+
+VOID
+SepReadPipe(
+ )
+{
+ IO_STATUS_BLOCK Iosb;
+ UCHAR Buffer[512];
+
+ DevPrint("ReadPipe...\n", 0);
+
+ if (!NT_SUCCESS(Status = NtReadFile( TalkPort,
+ (HANDLE)NULL,
+ (PIO_APC_ROUTINE)NULL,
+ (PVOID)NULL,
+ &Iosb,
+ Buffer,
+ 512,
+ (PLARGE_INTEGER)NULL,
+ (PULONG) NULL ))) {
+ Error( NtReadFile, Status );
+ }
+
+ if (!NT_SUCCESS(Status = NtWaitForSingleObject( TalkPort, TRUE, NULL ))) {
+
+ Error( NtWaitForSingleObject, Status );
+ }
+
+ if (!NT_SUCCESS(Iosb.Status)) {
+
+ Error( NtReadFileFinalStatus, Iosb.Status );
+ }
+
+ return;
+}
+
+
+VOID
+SepWritePipe(
+ PSZ String
+ )
+{
+ NTSTATUS Status;
+ IO_STATUS_BLOCK Iosb;
+
+
+ DevPrint("WritePipe...\n", 0);
+
+ if (!NT_SUCCESS(Status = NtWriteFile( TalkPort,
+ (HANDLE)NULL,
+ (PIO_APC_ROUTINE)NULL,
+ (PVOID)NULL,
+ &Iosb,
+ String,
+ strlen( String ),
+ (PLARGE_INTEGER)NULL,
+ (PULONG)NULL ))) {
+ Error( NtWriteFile, Status );
+ }
+
+ if (!NT_SUCCESS(Status = NtWaitForSingleObject( TalkPort, TRUE, NULL ))) {
+
+ Error( NtWaitForSingleObject, Status );
+ }
+
+ if (!NT_SUCCESS(Iosb.Status)) {
+
+ Error( NtWriteFileFinalStatus, Iosb.Status );
+ }
+
+ return;
+}
+
+
+VOID
+SepTransceivePipe(
+ PSZ String
+ )
+{
+ NTSTATUS Status;
+ IO_STATUS_BLOCK Iosb;
+ UCHAR Buffer[512];
+
+
+ DevPrint("TransceivePipe...\n", 0);
+
+ if (!NT_SUCCESS(Status = NtFsControlFile(
+ TalkPort,
+ NULL, // Event
+ NULL, // ApcRoutine
+ NULL, // ApcContext
+ &Iosb,
+ FSCTL_PIPE_TRANSCEIVE,
+ String,
+ strlen( String ),
+ Buffer,
+ 511
+ ))) {
+ Error( NtTransceiveFile, Status );
+ }
+
+ if (!NT_SUCCESS(Status = NtWaitForSingleObject( TalkPort, TRUE, NULL ))) {
+
+ Error( NtWaitForSingleObject, Status );
+ }
+
+ if (!NT_SUCCESS(Iosb.Status)) {
+
+ Error( NtTransceiveFileFinalStatus, Iosb.Status );
+ }
+
+ return;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+// //
+// Named Pipe Server Operations //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+HANDLE
+SepServerCreatePipe(
+ VOID
+ )
+{
+ HANDLE PipeHandle;
+ NTSTATUS Status;
+ IO_STATUS_BLOCK Iosb;
+ LARGE_INTEGER Timeout;
+ READ_MODE Mode;
+ ULONG Share;
+ NAMED_PIPE_CONFIGURATION Config = FILE_PIPE_FULL_DUPLEX;
+ NAMED_PIPE_TYPE PipeType = FILE_PIPE_MESSAGE_TYPE;
+ COMPLETION_MODE CompletionMode = FILE_PIPE_QUEUE_OPERATION;
+ ULONG MaximumInstances = 4;
+
+
+ //
+ // Set the default timeout to 60 seconds, and initalize the attributes
+ //
+
+ Timeout.QuadPart = -10 * 1000 * 1000 * 60;
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &UnicodePortName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ //
+ // Calculate the readmode and share access
+ //
+
+ Mode = (PipeType == FILE_PIPE_MESSAGE_TYPE ? FILE_PIPE_MESSAGE_MODE :
+ FILE_PIPE_BYTE_STREAM_MODE);
+
+ Share = (Config == FILE_PIPE_INBOUND ? FILE_SHARE_WRITE :
+ (Config == FILE_PIPE_OUTBOUND ? FILE_SHARE_READ :
+ FILE_SHARE_READ | FILE_SHARE_WRITE));
+
+ if (!NT_SUCCESS(Status = NtCreateNamedPipeFile(
+ &PipeHandle,
+ GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
+ &ObjectAttributes,
+ &Iosb,
+ Share,
+ FILE_CREATE,
+ 0,
+ PipeType,
+ Mode,
+ CompletionMode,
+ MaximumInstances,
+ 1024,
+ 1024,
+ (PLARGE_INTEGER)&Timeout ))) {
+
+ Error( CreatePipe, Status );
+ }
+ RtlFreeUnicodeString( &UnicodePortName );
+
+ return PipeHandle;
+}
+
+
+VOID
+SepServerListenPipe(
+ )
+{
+ NTSTATUS Status;
+ IO_STATUS_BLOCK Iosb;
+
+ DevPrint("ListenPipe...\n", 0);
+
+ if (!NT_SUCCESS(Status = NtFsControlFile(
+ EarPort,
+ NULL, // Event
+ NULL, // ApcRoutine
+ NULL, // ApcContext
+ &Iosb,
+ FSCTL_PIPE_LISTEN,
+ NULL, // InputBuffer
+ 0, // InputBufferLength,
+ NULL, // OutputBuffer
+ 0 // OutputBufferLength
+ ))) {
+
+ Error( ListenPipe, Status );
+ }
+ if (!NT_SUCCESS(Status = NtWaitForSingleObject( EarPort, TRUE, NULL ))) {
+
+ Error( NtWaitForSingleObject, Status );
+ }
+
+ if (!NT_SUCCESS(Iosb.Status)) {
+
+ Error( ListenPipeFinalStatus, Iosb.Status );
+ }
+
+
+ return;
+}
+
+
+VOID
+SepServerImpersonatePipe(
+ )
+{
+ NTSTATUS Status;
+ IO_STATUS_BLOCK Iosb;
+
+ DevPrint("ImpersonatePipe...\n", 0);
+
+ if (!NT_SUCCESS(Status = NtFsControlFile(
+ TalkPort,
+ NULL, // Event
+ NULL, // ApcRoutine
+ NULL, // ApcContext
+ &Iosb,
+ FSCTL_PIPE_IMPERSONATE,
+ NULL, // InputBuffer
+ 0, // InputBufferLength,
+ NULL, // OutputBuffer
+ 0 // OutputBufferLength
+ ))) {
+
+ Error( ImpersonatePipe, Status );
+ }
+ if (!NT_SUCCESS(Status = NtWaitForSingleObject( TalkPort, TRUE, NULL ))) {
+
+ Error( NtWaitForSingleObject, Status );
+ }
+
+ if (!NT_SUCCESS(Iosb.Status)) {
+
+ Error( ImpersonatePipeFinalStatus, Iosb.Status );
+ }
+
+ return;
+}
+
+
+VOID
+SepServerDisconnectPipe(
+ )
+{
+ NTSTATUS Status;
+ IO_STATUS_BLOCK Iosb;
+
+ DevPrint("DisconnectPipe...\n", 0);
+ DevPrint(" (Flush)...\n", 0);
+
+ if (!NT_SUCCESS(Status = NtFlushBuffersFile(
+ TalkPort,
+ &Iosb
+ ))) {
+ Error( DisconnectPipe, Status );
+ }
+
+ if (!NT_SUCCESS(Iosb.Status)) {
+
+ Error( FlushPipeFinalStatus, Iosb.Status );
+ }
+
+
+ DevPrint(" (Close Talk Port)...\n", 0);
+ Status = NtClose( TalkPort ); SEASSERT_SUCCESS(Status);
+
+ DevPrint(" (Disconnect)...\n", 0);
+ if (!NT_SUCCESS(Status = NtFsControlFile(
+ EarPort,
+ NULL, // Event
+ NULL, // ApcRoutine
+ NULL, // ApcContext
+ &Iosb,
+ FSCTL_PIPE_DISCONNECT,
+ NULL, // InputBuffer
+ 0, // InputBufferLength,
+ NULL, // OutputBuffer
+ 0 // OutputBufferLength
+ ))) {
+
+ Error( DisconnectPipe, Status );
+ }
+ if (!NT_SUCCESS(Status = NtWaitForSingleObject( EarPort, TRUE, NULL ))) {
+
+ Error( NtWaitForSingleObject, Status );
+ }
+
+ if (!NT_SUCCESS(Iosb.Status)) {
+
+ Error( DisconnectPipeFinalStatus, Iosb.Status );
+ }
+
+ return;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+// //
+// Named Pipe Client Operations //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+HANDLE
+SepClientOpenPipe(
+ VOID
+ )
+{
+ HANDLE PipeHandle, NpfsHandle;
+ NTSTATUS Status;
+ IO_STATUS_BLOCK Iosb;
+ ULONG Share;
+ STRING Npfs;
+ UNICODE_STRING UnicodeNpfs;
+ PFILE_PIPE_WAIT_FOR_BUFFER WaitPipe;
+ ULONG WaitPipeLength;
+ NAMED_PIPE_CONFIGURATION Config = FILE_PIPE_FULL_DUPLEX;
+ READ_MODE ReadMode = FILE_PIPE_MESSAGE_MODE;
+ COMPLETION_MODE CompletionMode = FILE_PIPE_QUEUE_OPERATION;
+
+
+//#ifdef NOT_YET_WORKING
+ //
+ // Wait for the server's pipe to reach a listen state...
+ //
+
+ RtlInitString( &Npfs, "\\Device\\NamedPipe\\");
+ Status = RtlAnsiStringToUnicodeString(
+ &UnicodeNpfs,
+ &Npfs,
+ TRUE ); SEASSERT_SUCCESS( NT_SUCCESS(Status) );
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &UnicodeNpfs,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL);
+
+ if (!NT_SUCCESS(Status = NtOpenFile(
+ &NpfsHandle,
+ GENERIC_READ | SYNCHRONIZE,
+ &ObjectAttributes,
+ &Iosb,
+ FILE_SHARE_READ,
+ 0 ))) {
+
+ Error( OpenNpfs, Status );
+ }
+ RtlFreeUnicodeString( &UnicodeNpfs );
+
+ WaitPipeLength =
+ FIELD_OFFSET(FILE_PIPE_WAIT_FOR_BUFFER, Name[0]) +
+ RelativePortName.MaximumLength; //UNICODEFIX UnicodeRelativePortName.MaximumLength;
+ WaitPipe = RtlAllocateHeap(RtlProcessHeap(), 0, WaitPipeLength);
+ WaitPipe->TimeoutSpecified = FALSE;
+
+ WaitPipe->NameLength = RelativePortName.Length; //UNICODEFIX UnicodeRelativePortName.Length;
+ strcpy(WaitPipe->Name, RelativePortName.Buffer); //UNICODEFIX UnicodePortName.Buffer;
+
+ if (!NT_SUCCESS(Status = NtFsControlFile(
+ NpfsHandle,
+ NULL, // Event
+ NULL, // ApcRoutine
+ NULL, // ApcContext
+ &Iosb,
+ FSCTL_PIPE_WAIT,
+ WaitPipe, // Buffer for data to the FS
+ WaitPipeLength,
+ NULL, // OutputBuffer
+ 0 // OutputBufferLength
+ ))) {
+
+ Error( ClientWaitPipe, Status );
+ }
+ if (Status == STATUS_PENDING) {
+ if (!NT_SUCCESS(Status = NtWaitForSingleObject( NpfsHandle, TRUE, NULL ))) {
+
+ Error( NtWaitForSingleObject, Status );
+ }
+ }
+
+ if (!NT_SUCCESS(Iosb.Status)) {
+
+ Error( ClientWaitPipeFinalStatus, Iosb.Status );
+ }
+
+ Status = NtClose( NpfsHandle );
+ ASSERT(NT_SUCCESS(Status));
+//#endif // NOT_YET_WORKING
+// Delay(1);
+
+
+ //
+ // Initialize the attributes
+ //
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &UnicodePortName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+ ObjectAttributes.SecurityQualityOfService = (PVOID)(&SecurityQos);
+
+ //
+ // Calculate the share access
+ //
+
+ Share = (Config == FILE_PIPE_INBOUND ? FILE_SHARE_WRITE :
+ (Config == FILE_PIPE_OUTBOUND ? FILE_SHARE_READ :
+ FILE_SHARE_READ | FILE_SHARE_WRITE));
+
+
+
+ //
+ // And now open it...
+ //
+
+ if (!NT_SUCCESS(Status = NtOpenFile(
+ &PipeHandle,
+ GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
+ &ObjectAttributes,
+ &Iosb,
+ Share,
+ 0 ))) {
+
+ Error( OpenPipe, Status );
+ }
+
+ if ((ReadMode != FILE_PIPE_BYTE_STREAM_MODE) ||
+ (CompletionMode != FILE_PIPE_QUEUE_OPERATION)) {
+
+ FILE_PIPE_INFORMATION Buffer;
+
+ Buffer.ReadMode = ReadMode;
+ Buffer.CompletionMode = CompletionMode;
+
+ if (!NT_SUCCESS(Status = NtSetInformationFile(
+ PipeHandle,
+ &Iosb,
+ &Buffer,
+ sizeof(FILE_PIPE_INFORMATION),
+ FilePipeInformation ))) {
+
+ Error( NtSetInformationFile, Status );
+ }
+ }
+
+ return PipeHandle;
+}
diff --git a/private/ntos/se/ctlpcqos.c b/private/ntos/se/ctlpcqos.c
new file mode 100644
index 000000000..d3b2a7a22
--- /dev/null
+++ b/private/ntos/se/ctlpcqos.c
@@ -0,0 +1,1256 @@
+
+//////////////////////////////////////////////////////////////////////////////
+// //
+// Global Definitions //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+// //
+// Global Variables //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ STRING EventName;
+ UNICODE_STRING UnicodeEventName;
+ HANDLE EventHandle;
+ UNICODE_STRING PortName;
+ HANDLE EarPort;
+ HANDLE TalkPort;
+ PORT_MESSAGE RequestMessage;
+ SECURITY_QUALITY_OF_SERVICE SecurityQos;
+ ULONG RequestCount;
+ HANDLE ClientToken;
+ TOKEN_STATISTICS ClientTokenStatistics;
+ ULONG IgnoreLength;
+
+ HANDLE SepServerThread;
+
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+// //
+// Test Routine Definitions //
+// //
+//////////////////////////////////////////////////////////////////////////////
+BOOLEAN
+SepClientTestStatic(VOID);
+
+BOOLEAN
+SepClientTestDynamic(VOID);
+
+BOOLEAN
+SepClientTestEffectiveOnly(
+ BOOLEAN StaticTest
+ );
+
+BOOLEAN
+SepClientTestNotEffectiveOnly(
+ BOOLEAN StaticTest
+ );
+
+BOOLEAN
+SepClientTestAnonymous(
+ BOOLEAN StaticTest,
+ BOOLEAN EffectiveOnly
+ );
+
+BOOLEAN
+SepClientTestIdentification(
+ BOOLEAN StaticTest,
+ BOOLEAN EffectiveOnly
+ );
+
+BOOLEAN
+SepClientTestImpersonation(
+ BOOLEAN StaticTest,
+ BOOLEAN EffectiveOnly
+ );
+
+VOID
+SepClientConnect(
+ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
+ SECURITY_CONTEXT_TRACKING_MODE TrackingMode,
+ BOOLEAN EffectiveOnly
+ );
+
+VOID
+SepClientMakeRemoteCall( VOID );
+
+VOID
+SepClientDropConnection( VOID );
+
+BOOLEAN
+SepClientTest(VOID);
+
+NTSTATUS
+SepClientInitialize(
+ );
+
+
+
+
+
+
+BOOLEAN
+SepServerTestStatic(VOID);
+
+BOOLEAN
+SepServerTestDynamic(VOID);
+
+BOOLEAN
+SepServerTestEffectiveOnly(
+ BOOLEAN StaticTest
+ );
+
+BOOLEAN
+SepServerTestNotEffectiveOnly(
+ BOOLEAN StaticTest
+ );
+
+BOOLEAN
+SepServerTestAnonymous(
+ BOOLEAN StaticTest,
+ BOOLEAN EffectiveOnly
+ );
+
+BOOLEAN
+SepServerTestIdentification(
+ BOOLEAN StaticTest,
+ BOOLEAN EffectiveOnly
+ );
+
+BOOLEAN
+SepServerTestImpersonation(
+ BOOLEAN StaticTest,
+ BOOLEAN EffectiveOnly
+ );
+
+VOID
+SepServerWaitForNextConnect( VOID );
+
+VOID
+SepServerGetNextMessage( VOID );
+
+VOID
+SepServerCompleteMessage( VOID );
+
+VOID
+SepServerDropConnection( VOID );
+
+
+
+BOOLEAN
+SepServerTest(VOID);
+
+NTSTATUS
+SepServerInitialize(
+ );
+
+VOID
+SepServerSpawnClientProcess(VOID);
+
+
+
+
+BOOLEAN
+CtLpcQos (VOID);
+
+
+//////////////////////////////////////////////////////////////////////////////
+// //
+// Client-Side Test Routines //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+
+VOID
+SepClientConnect(
+ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
+ SECURITY_CONTEXT_TRACKING_MODE TrackingMode,
+ BOOLEAN EffectiveOnly
+ )
+
+{
+
+ SecurityQos.ImpersonationLevel = ImpersonationLevel;
+ SecurityQos.ContextTrackingMode = TrackingMode;
+ SecurityQos.EffectiveOnly = EffectiveOnly;
+
+ Status = NtConnectPort(
+ &TalkPort,
+ &PortName,
+ &SecurityQos,
+ 0L,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ ); SEASSERT_SUCCESS(Status);
+
+ return;
+}
+
+
+VOID
+SepClientMakeRemoteCall( VOID )
+
+{
+ PORT_MESSAGE ReplyMessage;
+
+ Status = NtRequestWaitReplyPort(
+ TalkPort,
+ &RequestMessage,
+ &ReplyMessage
+ );
+
+ RequestCount += 1;
+
+ return;
+}
+
+
+VOID
+SepClientDropConnection( VOID )
+
+{
+
+ Status = NtClose( TalkPort ); SEASSERT_SUCCESS(Status);
+
+ return;
+
+}
+
+
+BOOLEAN
+SepClientTestStatic(VOID)
+
+{
+
+ BOOLEAN CompletionStatus;
+
+ //
+ // Static Context Tracking ... Suite
+ //
+
+ CompletionStatus = SepClientTestEffectiveOnly( TRUE );
+
+
+ if (CompletionStatus == TRUE) {
+
+ CompletionStatus = SepClientTestNotEffectiveOnly( TRUE );
+ }
+
+ return CompletionStatus;
+
+}
+
+
+BOOLEAN
+SepClientTestDynamic(VOID)
+
+{
+ BOOLEAN CompletionStatus;
+
+ //
+ // Dynamic Context Tracking ... Suite
+ //
+
+ CompletionStatus = SepClientTestEffectiveOnly( FALSE );
+
+
+ if (CompletionStatus == TRUE) {
+
+ CompletionStatus = SepClientTestNotEffectiveOnly( FALSE );
+ }
+
+ return CompletionStatus;
+
+}
+
+
+BOOLEAN
+SepClientTestEffectiveOnly(
+ BOOLEAN StaticTest
+ )
+
+
+{
+
+ BOOLEAN CompletionStatus;
+
+ //
+ // Effective Only ... Test
+ //
+
+ CompletionStatus = SepClientTestAnonymous( StaticTest, TRUE );
+ if (CompletionStatus == TRUE) {
+ CompletionStatus = SepClientTestIdentification( StaticTest, TRUE );
+ }
+ if (CompletionStatus == TRUE) {
+ CompletionStatus = SepClientTestImpersonation( StaticTest, TRUE );
+ }
+
+ return CompletionStatus;
+
+}
+
+
+BOOLEAN
+SepClientTestNotEffectiveOnly(
+ BOOLEAN StaticTest
+ )
+
+{
+ BOOLEAN CompletionStatus;
+
+ //
+ // Not Effective Only ... Test
+ //
+
+ CompletionStatus = SepClientTestAnonymous( StaticTest, FALSE );
+ if (CompletionStatus == TRUE) {
+ CompletionStatus = SepClientTestIdentification( StaticTest, FALSE );
+ }
+ if (CompletionStatus == TRUE) {
+ CompletionStatus = SepClientTestImpersonation( StaticTest, FALSE );
+ }
+
+ return CompletionStatus;
+
+}
+
+
+BOOLEAN
+SepClientTestAnonymous(
+ BOOLEAN StaticTest,
+ BOOLEAN EffectiveOnly
+ )
+
+{
+
+ //////////////////////////////////////////////////////////////////////////
+ // //
+ // Anonymous Use Test //
+ // //
+ //////////////////////////////////////////////////////////////////////////
+
+ SECURITY_CONTEXT_TRACKING_MODE TrackingMode;
+
+ if (StaticTest) {
+ TrackingMode = SECURITY_STATIC_TRACKING;
+ } else {
+ TrackingMode = SECURITY_DYNAMIC_TRACKING;
+ }
+
+ if (!StaticTest) {
+ //
+ // No action for dynamic test
+ //
+ return TRUE;
+ }
+
+ //
+ // Anonymous Use ... Test
+ //
+
+
+ SepClientConnect(
+ SecurityAnonymous,
+ TrackingMode,
+ EffectiveOnly
+ );
+
+ SepClientMakeRemoteCall();
+
+ SepClientDropConnection();
+
+
+ return TRUE;
+}
+
+
+BOOLEAN
+SepClientTestIdentification(
+ BOOLEAN StaticTest,
+ BOOLEAN EffectiveOnly
+ )
+
+{
+
+ //////////////////////////////////////////////////////////////////////////
+ // //
+ // Identification Use Test //
+ // //
+ //////////////////////////////////////////////////////////////////////////
+
+ SECURITY_CONTEXT_TRACKING_MODE TrackingMode;
+
+ if (StaticTest) {
+ TrackingMode = SECURITY_STATIC_TRACKING;
+ } else {
+ TrackingMode = SECURITY_DYNAMIC_TRACKING;
+ }
+
+ //
+ // Identification Use ... Test
+ //
+
+
+ SepClientConnect(
+ SecurityIdentification,
+ TrackingMode,
+ EffectiveOnly
+ );
+
+ SepClientMakeRemoteCall();
+
+ SepClientDropConnection();
+
+
+ return TRUE;
+
+}
+
+
+BOOLEAN
+SepClientTestImpersonation(
+ BOOLEAN StaticTest,
+ BOOLEAN EffectiveOnly
+ )
+
+{
+
+ //////////////////////////////////////////////////////////////////////////
+ // //
+ // Impersonation Use Test //
+ // //
+ //////////////////////////////////////////////////////////////////////////
+
+ SECURITY_CONTEXT_TRACKING_MODE TrackingMode;
+
+ if (StaticTest) {
+ TrackingMode = SECURITY_STATIC_TRACKING;
+ } else {
+ TrackingMode = SECURITY_DYNAMIC_TRACKING;
+ }
+
+
+ //
+ // Impersonation Use ... Test
+ //
+
+
+ SepClientConnect(
+ SecurityImpersonation,
+ TrackingMode,
+ EffectiveOnly
+ );
+
+ SepClientMakeRemoteCall();
+
+ SepClientDropConnection();
+
+
+
+ return TRUE;
+
+}
+
+
+
+
+BOOLEAN
+SepClientTest(VOID)
+//
+// Tests:
+//
+// Static Context Tracking Tests
+// Effective Only
+// Anonymous
+// Identification
+// Impersonation
+// Not Effective Only
+// Anonymous
+// Identification
+// Impersonation
+//
+// Dynamic Context Tracking Tests
+// Effective Only
+// Identification
+// Impersonation
+// Not Effective Only
+// Identification
+// Impersonation
+//
+{
+
+ BOOLEAN CompletionStatus;
+
+
+
+
+ //
+ // Run the static test suite...
+ //
+
+ CompletionStatus = SepClientTestStatic();
+
+ //
+ // Run the dynamic test suite...
+ //
+
+ if (CompletionStatus == TRUE) {
+ CompletionStatus = SepClientTestDynamic();
+ }
+
+ DbgPrint("Se: Client Test Complete.\n");
+
+
+ return CompletionStatus;
+}
+
+
+NTSTATUS
+SepClientInitialize(
+ )
+
+{
+
+
+
+ DbgPrint("Se: Client Initializing ...\n");
+
+ //
+ // Initialize global variables
+ //
+
+ RequestMessage.u1.s1.DataLength = 0;
+ RequestMessage.u1.s1.TotalLength = (CSHORT)sizeof(PORT_MESSAGE);
+ RequestMessage.u2.ZeroInit = 0;
+
+ RequestCount = 0;
+
+
+ //
+ // Signal the named event to start the test
+ //
+
+ DbgPrint("Se: Client Starting Test ...\n");
+ Status = NtSetEvent( EventHandle, NULL ); SEASSERT_SUCCESS(Status);
+
+ Status = NtClose( EventHandle ); SEASSERT_SUCCESS(Status);
+
+
+ return STATUS_SUCCESS;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+// //
+// Server-Side Test Routines //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+
+VOID
+SepServerWaitForNextConnect( VOID )
+{
+
+ CONNECTION_REQUEST ConnectionRequest;
+
+ ConnectionRequest.Length = (ULONG)sizeof(CONNECTION_REQUEST);
+
+ //
+ // Wait for the client to connect to the port
+ //
+
+ Status = NtListenPort(
+ EarPort,
+ &ConnectionRequest,
+ NULL,
+ 0L
+ ); SEASSERT_SUCCESS(Status);
+
+ Status = NtAcceptConnectPort(
+ &TalkPort,
+ NULL,
+ &ConnectionRequest,
+ TRUE,
+ NULL,
+ NULL,
+ NULL,
+ 0L
+ ); SEASSERT_SUCCESS(Status);
+
+ Status = NtCompleteConnectPort( TalkPort ); SEASSERT_SUCCESS(Status);
+
+ return;
+
+}
+
+VOID
+SepServerGetNextMessage( VOID )
+
+{
+
+ //
+ // Wait for the next message to come in...
+ //
+
+ Status = NtReplyWaitReceivePort(
+ EarPort,
+ NULL,
+ NULL,
+ &RequestMessage
+ ); SEASSERT_SUCCESS(Status);
+
+ RequestCount += 1;
+
+ return;
+}
+
+VOID
+SepServerCompleteMessage( VOID )
+
+{
+ PORT_MESSAGE ReplyMessage;
+
+ ReplyMessage.u1.s1.DataLength = 0;
+ ReplyMessage.u1.s1.TotalLength = (CSHORT)sizeof(PORT_MESSAGE);
+ ReplyMessage.u2.ZeroInit = 0;
+ ReplyMessage.ClientId = RequestMessage.ClientId;
+ ReplyMessage.MessageId = RequestMessage.MessageId;
+
+ //
+ // Send the response message
+ //
+
+ Status = NtReplyPort(
+ EarPort,
+ &ReplyMessage
+ ); SEASSERT_SUCCESS(Status);
+
+ return;
+}
+
+VOID
+SepServerImpersonateClient( VOID )
+
+{
+
+ Status = NtImpersonateClientOfPort(
+ TalkPort,
+ &RequestMessage
+ ); SEASSERT_SUCCESS(Status);
+
+}
+
+
+VOID
+SepServerRevertToSelf( VOID )
+
+{
+ NTSTATUS TmpStatus;
+ HANDLE NullHandle;
+
+ NullHandle = NULL;
+ TmpStatus = NtSetInformationThread(
+ SepServerThread,
+ ThreadImpersonationToken,
+ (PVOID)&NullHandle,
+ (ULONG)sizeof(HANDLE)
+ ); SEASSERT_SUCCESS(TmpStatus);
+
+}
+
+
+VOID
+SepServerDropConnection( VOID )
+
+{
+ Status = NtClose( TalkPort ); SEASSERT_SUCCESS(Status);
+
+ return;
+}
+
+BOOLEAN
+SepServerTestStatic(VOID)
+
+{
+ BOOLEAN CompletionStatus;
+
+ DbgPrint("Se: Static Context Tracking ... Suite\n");
+
+ CompletionStatus = SepServerTestEffectiveOnly( TRUE );
+
+
+ if (CompletionStatus == TRUE) {
+
+ CompletionStatus = SepServerTestNotEffectiveOnly( TRUE );
+ }
+
+ return CompletionStatus;
+
+}
+
+
+BOOLEAN
+SepServerTestDynamic(VOID)
+
+{
+ BOOLEAN CompletionStatus;
+
+ DbgPrint("Se: Dynamic Context Tracking ... Suite\n");
+
+ CompletionStatus = SepServerTestEffectiveOnly( FALSE );
+
+
+ if (CompletionStatus == TRUE) {
+
+ CompletionStatus = SepServerTestNotEffectiveOnly( FALSE );
+ }
+
+ return CompletionStatus;
+
+}
+
+
+BOOLEAN
+SepServerTestEffectiveOnly(
+ BOOLEAN StaticTest
+ )
+
+{
+
+ BOOLEAN CompletionStatus;
+
+ DbgPrint("Se: Effective Only ... Test\n");
+
+ CompletionStatus = SepServerTestAnonymous( StaticTest, TRUE );
+ if (CompletionStatus == TRUE) {
+ CompletionStatus = SepServerTestIdentification( StaticTest, TRUE );
+ }
+ if (CompletionStatus == TRUE) {
+ CompletionStatus = SepServerTestImpersonation( StaticTest, TRUE );
+ }
+
+ return CompletionStatus;
+
+}
+
+
+BOOLEAN
+SepServerTestNotEffectiveOnly(
+ BOOLEAN StaticTest
+ )
+
+{
+
+ BOOLEAN CompletionStatus;
+
+ DbgPrint("Se: Not Effective Only ... Test\n");
+
+ CompletionStatus = SepServerTestAnonymous( StaticTest, FALSE );
+ if (CompletionStatus == TRUE) {
+ CompletionStatus = SepServerTestIdentification( StaticTest, FALSE );
+ }
+ if (CompletionStatus == TRUE) {
+ CompletionStatus = SepServerTestImpersonation( StaticTest, FALSE );
+ }
+
+ return CompletionStatus;
+
+}
+
+
+BOOLEAN
+SepServerTestAnonymous(
+ BOOLEAN StaticTest,
+ BOOLEAN EffectiveOnly
+ )
+
+{
+ BOOLEAN CompletionStatus = TRUE;
+
+ //////////////////////////////////////////////////////////////////////////
+ // //
+ // Anonymous Use Test //
+ // //
+ //////////////////////////////////////////////////////////////////////////
+
+
+ if (!StaticTest) {
+ //
+ // No action for dynamic test
+ //
+
+ return TRUE;
+ }
+
+ DbgPrint("Se: Anonymous Use ... ");
+
+ SepServerWaitForNextConnect();
+
+ SepServerGetNextMessage();
+
+
+ SepServerImpersonateClient();
+ Status = NtOpenThreadToken(
+ SepServerThread,
+ TOKEN_ALL_ACCESS,
+ TRUE,
+ &ClientToken
+ );
+ SepServerRevertToSelf();
+ if (Status == STATUS_CANT_OPEN_ANONYMOUS) {
+
+ DbgPrint(" Succeeded\n");
+
+ } else {
+ DbgPrint("* ! FAILED (srvr) ! *\n");
+ DbgPrint("Status is: 0x%lx \n", Status );
+ CompletionStatus = FALSE;
+ }
+
+
+ SepServerCompleteMessage();
+
+ SepServerDropConnection();
+
+ //
+ // Appease the compiler Gods..
+ //
+
+ if (EffectiveOnly) {;}
+
+
+ return CompletionStatus;
+
+}
+
+
+BOOLEAN
+SepServerTestIdentification(
+ BOOLEAN StaticTest,
+ BOOLEAN EffectiveOnly
+ )
+
+{
+
+ BOOLEAN CompletionStatus = TRUE;
+ //////////////////////////////////////////////////////////////////////////
+ // //
+ // Identification Use Test //
+ // //
+ //////////////////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Identification Use ... ");
+
+ SepServerWaitForNextConnect();
+
+ SepServerGetNextMessage();
+
+ SepServerImpersonateClient();
+ Status = NtOpenThreadToken(
+ SepServerThread,
+ TOKEN_ALL_ACCESS,
+ TRUE,
+ &ClientToken
+ ); SEASSERT_SUCCESS(Status);
+ SepServerRevertToSelf();
+ Status = NtQueryInformationToken(
+ ClientToken,
+ TokenStatistics,
+ &ClientTokenStatistics,
+ (ULONG)sizeof(TOKEN_STATISTICS),
+ &IgnoreLength
+ ); SEASSERT_SUCCESS(Status);
+
+ if ( (ClientTokenStatistics.TokenType == TokenImpersonation) &&
+ (ClientTokenStatistics.ImpersonationLevel == SecurityIdentification)
+ ) {
+ DbgPrint(" Succeeded\n");
+
+ } else {
+ DbgPrint("* ! FAILED (srvr) ! *\n");
+ CompletionStatus = FALSE;
+ }
+
+
+ SepServerCompleteMessage();
+
+ SepServerDropConnection();
+
+ //
+ // Appease the compiler Gods..
+ //
+ if (StaticTest) {;}
+ if (EffectiveOnly) {;}
+
+ return CompletionStatus;
+}
+
+
+BOOLEAN
+SepServerTestImpersonation(
+ BOOLEAN StaticTest,
+ BOOLEAN EffectiveOnly
+ )
+
+{
+ BOOLEAN CompletionStatus = TRUE;
+
+ //////////////////////////////////////////////////////////////////////////
+ // //
+ // Impersonation Use Test //
+ // //
+ //////////////////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Impersonation Use ... ");
+
+
+ SepServerWaitForNextConnect();
+
+ SepServerGetNextMessage();
+
+
+
+ SepServerImpersonateClient();
+ Status = NtOpenThreadToken(
+ SepServerThread,
+ TOKEN_ALL_ACCESS,
+ TRUE,
+ &ClientToken
+ ); SEASSERT_SUCCESS(Status);
+ SepServerRevertToSelf();
+ Status = NtQueryInformationToken(
+ ClientToken,
+ TokenStatistics,
+ &ClientTokenStatistics,
+ (ULONG)sizeof(TOKEN_STATISTICS),
+ &IgnoreLength
+ ); SEASSERT_SUCCESS(Status);
+
+ if ( (ClientTokenStatistics.TokenType == TokenImpersonation) &&
+ (ClientTokenStatistics.ImpersonationLevel == SecurityImpersonation)
+ ) {
+ DbgPrint(" Succeeded\n");
+
+ } else {
+ DbgPrint("* ! FAILED (srvr) ! *\n");
+ CompletionStatus = FALSE;
+ }
+
+
+
+
+ SepServerCompleteMessage();
+
+ SepServerDropConnection();
+
+ //
+ // Appease the compiler gods
+ //
+ if (StaticTest) {;}
+ if (EffectiveOnly) {;}
+
+ return CompletionStatus;
+}
+
+
+BOOLEAN
+SepServerTest(VOID)
+//
+// Tests:
+//
+// Static Context Tracking Tests
+// Effective Only
+// Anonymous
+// Identification
+// Impersonation
+// Not Effective Only
+// Anonymous
+// Identification
+// Impersonation
+//
+// Dynamic Context Tracking Tests
+// Effective Only
+// Identification
+// Impersonation
+// Not Effective Only
+// Identification
+// Impersonation
+//
+{
+
+ BOOLEAN CompletionStatus;
+
+
+ DbgPrint("Se: Server Starting Test ...\n");
+
+ //
+ // Run the static test suite...
+ //
+
+ CompletionStatus = SepServerTestStatic();
+
+ //
+ // Run the dynamic test suite...
+ //
+
+ if (CompletionStatus == TRUE) {
+ CompletionStatus = SepServerTestDynamic();
+ }
+
+ DbgPrint("Se: Server Test Complete.\n");
+
+ //
+ // Print test results
+ //
+
+ DbgPrint("\n");
+ DbgPrint("\n");
+ DbgPrint("**********************\n");
+ DbgPrint("** **\n");
+
+ if (CompletionStatus == TRUE) {
+ DbgPrint("** Test Succeeded **\n");
+ } else {
+ DbgPrint("** Test Failed !! **\n");
+ }
+
+ DbgPrint("** **\n");
+ DbgPrint("**********************\n");
+
+ return CompletionStatus;
+
+}
+
+NTSTATUS
+SepServerInitialize(
+ )
+
+{
+
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ThreadAttributes;
+ PTEB CurrentTeb;
+
+
+ DbgPrint("Se: Server Initializing ...\n");
+
+ //
+ // Initialize global variables
+ //
+
+ RequestCount = 0;
+
+ //
+ // Get a handle to our thread to so that we can access our thread
+ // even when impersonating an anonymous client (which we can't do
+ // using NtCurrentThread()).
+ //
+
+ CurrentTeb = NtCurrentTeb();
+ InitializeObjectAttributes(&ThreadAttributes, NULL, 0, NULL, NULL);
+ Status = NtOpenThread(
+ &SepServerThread, // TargetHandle
+ THREAD_ALL_ACCESS, // DesiredAccess
+ &ThreadAttributes, // ObjectAttributes
+ &CurrentTeb->ClientId // ClientId
+ );
+ ASSERT( NT_SUCCESS(Status) );
+
+
+ //
+ // Create the server's port
+ //
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &PortName,
+ 0,
+ NULL,
+ NULL );
+
+ Status = NtCreatePort(
+ &EarPort,
+ &ObjectAttributes,
+ 0,
+ 4,
+ 4 * 256
+ ); SEASSERT_SUCCESS(Status);
+
+
+
+ //
+ // Spawn a copy of ourselves...
+ //
+
+ DbgPrint("Se: Server Spawning client process ...\n");
+ SepServerSpawnClientProcess();
+
+
+ DbgPrint("Se: Server waiting for start of test signal ...\n");
+
+ Status = NtWaitForSingleObject(
+ EventHandle,
+ TRUE,
+ NULL
+ ); SEASSERT_SUCCESS(Status);
+
+ Status = NtClose( EventHandle ); SEASSERT_SUCCESS(Status);
+
+
+ return STATUS_SUCCESS;
+}
+
+VOID
+SepServerSpawnClientProcess(VOID)
+
+{
+
+
+ RTL_USER_PROCESS_INFORMATION ProcessInformation;
+ STRING ImagePathName, ProgramName;
+ UNICODE_STRING UnicodeImagePathName, UnicodeProgramName;
+ PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
+
+ RtlInitString( &ProgramName, "\\SystemRoot\\Bin\\utlpcqos.exe" );
+ Status = RtlAnsiStringToUnicodeString(
+ &UnicodeProgramName,
+ &ProgramName,
+ TRUE ); SEASSERT_SUCCESS( NT_SUCCESS(Status) );
+ RtlInitString( &ImagePathName, "utlpcqos.exe");
+ Status = RtlAnsiStringToUnicodeString(
+ &UnicodeImagePathName,
+ &ImagePathName,
+ TRUE ); SEASSERT_SUCCESS( NT_SUCCESS(Status) );
+
+ Status = RtlCreateProcessParameters(
+ &ProcessParameters,
+ &ImagePathName, //UNICODEFIX &UnicodeImagePathName,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ );
+
+ SEASSERT_SUCCESS(Status);
+
+
+ Status = RtlCreateUserProcess(
+ &ProgramName, // UNICODEFIX &UnicodeProgramName,
+ ProcessParameters, // ProcessParameters
+ NULL, // ProcessSecurityDescriptor
+ NULL, // ThreadSecurityDescriptor
+ NtCurrentProcess(), // ParentProcess
+ FALSE, // InheritHandles
+ NULL, // DebugPort
+ NULL, // ExceptionPort
+ &ProcessInformation // ProcessInformation
+ ); SEASSERT_SUCCESS(Status);
+
+ Status = NtResumeThread(
+ ProcessInformation.Thread,
+ NULL
+ ); SEASSERT_SUCCESS(Status);
+
+ RtlDestroyProcessParameters( ProcessParameters );
+ RtlFreeUnicodeString( &UnicodeProgramName );
+ RtlFreeUnicodeString( &UnicodeImagePathName );
+
+}
+
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+// //
+// Main Program Entry Routine //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+BOOLEAN
+CtLpcQos (VOID)
+{
+
+ BOOLEAN Result = TRUE;
+
+ RtlInitUnicodeString( &PortName, L"\\TestLpcQosServerPort" );
+
+ //
+ // Determine whether we are the client or server side of the test.
+ // This is done by creating or opening a named event object. If the
+ // event does not yet exist, then we are the client, and must create
+ // the server process. Otherwise, we are the server and the client
+ // is waiting for us to signal the event.
+ //
+
+ RtlInitString( &EventName, "\\TestLpcQosEvent" );
+ Status = RtlAnsiStringToUnicodeString(
+ &UnicodeEventName,
+ &EventName,
+ TRUE ); SEASSERT_SUCCESS( NT_SUCCESS(Status) );
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &UnicodeEventName,
+ OBJ_OPENIF,
+ NULL,
+ NULL
+ );
+ Status = NtCreateEvent(
+ &EventHandle,
+ EVENT_ALL_ACCESS,
+ &ObjectAttributes,
+ SynchronizationEvent,
+ FALSE
+ );
+
+ if (Status == STATUS_OBJECT_NAME_EXISTS) {
+
+ //
+ // Server is already running, therefore, this process gets to be
+ // the client.
+ //
+
+ Status = SepClientInitialize(); SEASSERT_SUCCESS(Status);
+ Result = SepClientTest();
+
+ } else {
+
+ SEASSERT_SUCCESS(Status);
+
+ //
+ // Event wasn't yet there, so we must be the server.
+ //
+
+ DbgPrint("Se: Starting LPC Impersonation Test.\n");
+
+ Status = SepServerInitialize(); SEASSERT_SUCCESS(Status);
+ Result = SepServerTest();
+
+ DbgPrint("Se: End Test.\n");
+
+ }
+
+
+
+ Status = NtTerminateThread( NtCurrentThread(), STATUS_SUCCESS);
+ SEASSERT_SUCCESS(Status);
+
+ return Result;
+
+}
diff --git a/private/ntos/se/ctseacc.c b/private/ntos/se/ctseacc.c
new file mode 100644
index 000000000..09b24096e
--- /dev/null
+++ b/private/ntos/se/ctseacc.c
@@ -0,0 +1,927 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ ctseacc.c
+
+Abstract:
+
+ Common security accessibility test routines.
+
+ These routines are used in both the kernel and user mode RTL tests.
+
+ This test assumes the security runtime library routines are
+ functioning correctly.
+
+
+
+Author:
+
+ Jim Kelly (JimK) 23-Mar-1990
+
+Environment:
+
+ Test of security.
+
+Revision History:
+
+ v5: robertre
+ Updated ACL_REVISION
+
+--*/
+
+#include "tsecomm.c" // Mode dependent macros and routines.
+
+
+
+////////////////////////////////////////////////////////////////
+// //
+// Module wide variables //
+// //
+////////////////////////////////////////////////////////////////
+
+ NTSTATUS Status;
+ STRING Event1Name, Process1Name;
+ UNICODE_STRING UnicodeEvent1Name, UnicodeProcess1Name;
+
+ OBJECT_ATTRIBUTES NullObjectAttributes;
+
+ HANDLE Event1;
+ OBJECT_ATTRIBUTES Event1ObjectAttributes;
+ PSECURITY_DESCRIPTOR Event1SecurityDescriptor;
+ PSID Event1Owner;
+ PSID Event1Group;
+ PACL Event1Dacl;
+ PACL Event1Sacl;
+
+ PACL TDacl;
+ BOOLEAN TDaclPresent;
+ BOOLEAN TDaclDefaulted;
+
+ PACL TSacl;
+ BOOLEAN TSaclPresent;
+ BOOLEAN TSaclDefaulted;
+
+ PSID TOwner;
+ BOOLEAN TOwnerDefaulted;
+ PSID TGroup;
+ BOOLEAN TGroupDefaulted;
+
+
+HANDLE Process1;
+OBJECT_ATTRIBUTES Process1ObjectAttributes;
+
+
+
+
+////////////////////////////////////////////////////////////////
+// //
+// Initialization Routine //
+// //
+////////////////////////////////////////////////////////////////
+
+BOOLEAN
+TestSeInitialize()
+{
+
+ Event1SecurityDescriptor = (PSECURITY_DESCRIPTOR)TstAllocatePool( PagedPool, 1024 );
+
+ RtlInitString(&Event1Name, "\\SecurityTestEvent1");
+ Status = RtlAnsiStringToUnicodeString(
+ &UnicodeEvent1Name,
+ &Event1Name,
+ TRUE ); SEASSERT_SUCCESS( NT_SUCCESS(Status) );
+ RtlInitString(&Process1Name, "\\SecurityTestProcess1");
+ Status = RtlAnsiStringToUnicodeString(
+ &UnicodeProcess1Name,
+ &Process1Name,
+ TRUE ); SEASSERT_SUCCESS( NT_SUCCESS(Status) );
+
+ InitializeObjectAttributes(&NullObjectAttributes, NULL, 0, NULL, NULL);
+
+ //
+ // Build an ACL or two for use.
+
+ TDacl = (PACL)TstAllocatePool( PagedPool, 256 );
+ TSacl = (PACL)TstAllocatePool( PagedPool, 256 );
+
+ TDacl->AclRevision=TSacl->AclRevision=ACL_REVISION;
+ TDacl->Sbz1=TSacl->Sbz1=0;
+ TDacl->Sbz2=TSacl->Sbz2=0;
+ TDacl->AclSize=256;
+ TSacl->AclSize=8;
+ TDacl->AceCount=TSacl->AceCount=0;
+
+ return TRUE;
+}
+
+
+
+////////////////////////////////////////////////////////////////
+// //
+// Test routines //
+// //
+////////////////////////////////////////////////////////////////
+
+BOOLEAN
+TestSeUnnamedCreate()
+//
+// Test:
+// No Security Specified
+// No Inheritence
+// Dacl Inheritence
+// Sacl Inheritence
+// Dacl Inheritence With Creator ID
+// Dacl & Sacl Inheritence
+//
+// Empty Security Descriptor Explicitly Specified
+// No Inheritence
+// Dacl Inheritence
+// Sacl Inheritence
+// Dacl & Sacl Inheritence
+//
+// Explicit Dacl Specified
+// No Inheritence
+// Dacl Inheritence
+// Sacl Inheritence
+// Dacl & Sacl Inheritence
+//
+// Explicit Sacl Specified (W/Privilege)
+// No Inheritence
+// Dacl & Sacl Inheritence
+//
+// Default Dacl Specified
+// No Inheritence
+// Dacl Inheritence
+// Sacl Inheritence
+// Dacl & Sacl Inheritence
+//
+// Default Sacl Specified (W/Privilege)
+// No Inheritence
+// Dacl & Sacl Inheritence
+//
+// Explicit Sacl Specified (W/O Privilege - should be rejected)
+// Default Sacl Specified (W/O Privilege - should be rejected)
+//
+// Valid Owner Explicitly Specified
+// Invalid Owner Explicitly Specified
+//
+// Explicit Group Specified
+//
+{
+
+
+ BOOLEAN CompletionStatus = TRUE;
+
+ InitializeObjectAttributes(&Event1ObjectAttributes, NULL, 0, NULL, NULL);
+ DbgPrint("Se: No Security Descriptor... Test\n");
+ DbgPrint("Se: No Inheritence... ");
+
+ Status = NtCreateEvent(
+ &Event1,
+ DELETE,
+ &Event1ObjectAttributes,
+ NotificationEvent,
+ FALSE
+ );
+ if (NT_SUCCESS(Status)) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint(" **** Failed ****\n");
+ CompletionStatus = FALSE;
+ }
+ ASSERT(NT_SUCCESS(Status));
+ Status = NtClose(Event1);
+ ASSERT(NT_SUCCESS(Status));
+
+ DbgPrint("Se: Dacl Inheritence... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Sacl Inheritence... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Dacl Inheritence W/ Creator ID... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Dacl And Sacl Inheritence... ");
+ DbgPrint(" Not Implemented.\n");
+
+ return CompletionStatus;
+
+}
+
+BOOLEAN
+TestSeNamedCreate()
+//
+// Test:
+// No Security Specified
+// No Inheritence
+// Dacl Inheritence
+// Sacl Inheritence
+// Dacl Inheritence With Creator ID
+// Dacl & Sacl Inheritence
+//
+// Empty Security Descriptor Explicitly Specified
+// No Inheritence
+// Dacl Inheritence
+// Sacl Inheritence
+// Dacl & Sacl Inheritence
+//
+// Explicit Dacl Specified
+// No Inheritence
+// Dacl Inheritence
+// Sacl Inheritence
+// Dacl & Sacl Inheritence
+//
+// Explicit Sacl Specified (W/Privilege)
+// No Inheritence
+// Dacl & Sacl Inheritence
+//
+// Default Dacl Specified
+// No Inheritence
+// Dacl Inheritence
+// Sacl Inheritence
+// Dacl & Sacl Inheritence
+//
+// Default Sacl Specified (W/Privilege)
+// No Inheritence
+// Dacl & Sacl Inheritence
+//
+// Explicit Sacl Specified (W/O Privilege - should be rejected)
+// Default Sacl Specified (W/O Privilege - should be rejected)
+//
+// Valid Owner Explicitly Specified
+// Invalid Owner Explicitly Specified
+//
+// Explicit Group Specified
+//
+{
+
+ BOOLEAN CompletionStatus = TRUE;
+
+
+ InitializeObjectAttributes(
+ &Event1ObjectAttributes,
+ &UnicodeEvent1Name,
+ 0,
+ NULL,
+ NULL);
+
+ DbgPrint("Se: No Security Specified... Test\n");
+ DbgPrint("Se: No Inheritence... ");
+ Status = NtCreateEvent(
+ &Event1,
+ DELETE,
+ &Event1ObjectAttributes,
+ NotificationEvent,
+ FALSE
+ );
+ if (NT_SUCCESS(Status)) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint(" **** Failed ****\n");
+ CompletionStatus = FALSE;
+ }
+ ASSERT(NT_SUCCESS(Status));
+ Status = NtClose(Event1);
+ ASSERT(NT_SUCCESS(Status));
+
+ DbgPrint("Se: Dacl Inheritence... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Sacl Inheritence... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Dacl Inheritence With Creator ID... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Dacl & Sacl Inheritence... ");
+ DbgPrint(" Not Implemented.\n");
+
+ DbgPrint("Se: Empty Security Descriptor Explicitly Specified... Test\n");
+ DbgPrint("Se: No Inheritence... ");
+
+ RtlCreateSecurityDescriptor( Event1SecurityDescriptor, 1 );
+ InitializeObjectAttributes(&Event1ObjectAttributes,
+ &UnicodeEvent1Name,
+ 0,
+ NULL,
+ Event1SecurityDescriptor);
+ Status = NtCreateEvent(
+ &Event1,
+ DELETE,
+ &Event1ObjectAttributes,
+ NotificationEvent,
+ FALSE
+ );
+ if (NT_SUCCESS(Status)) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint(" **** Failed ****\n");
+ CompletionStatus = FALSE;
+ }
+ ASSERT(NT_SUCCESS(Status));
+ Status = NtClose(Event1);
+ ASSERT(NT_SUCCESS(Status));
+
+
+
+
+ DbgPrint("Se: Dacl Inheritence... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Sacl Inheritence... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Dacl & Sacl Inheritence... ");
+ DbgPrint(" Not Implemented.\n");
+
+ DbgPrint("Se: Explicit Dacl Specified... Test\n");
+ DbgPrint("Se: No Inheritence... ");
+
+ RtlCreateSecurityDescriptor( Event1SecurityDescriptor, 1 );
+ RtlSetDaclSecurityDescriptor( Event1SecurityDescriptor, TRUE, TDacl, FALSE );
+
+ InitializeObjectAttributes(&Event1ObjectAttributes,
+ &UnicodeEvent1Name,
+ 0,
+ NULL,
+ Event1SecurityDescriptor);
+ Status = NtCreateEvent(
+ &Event1,
+ DELETE,
+ &Event1ObjectAttributes,
+ NotificationEvent,
+ FALSE
+ );
+ if (NT_SUCCESS(Status)) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint(" **** Failed ****\n");
+ CompletionStatus = FALSE;
+ }
+ ASSERT(NT_SUCCESS(Status));
+ Status = NtClose(Event1);
+ ASSERT(NT_SUCCESS(Status));
+
+ DbgPrint("Se: Dacl Inheritence... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Sacl Inheritence... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Dacl & Sacl Inheritence... ");
+ DbgPrint(" Not Implemented.\n");
+
+ DbgPrint("Se: Explicit Sacl Specified (W/Privilege)... Test\n");
+ DbgPrint("Se: No Inheritence... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Dacl & Sacl Inheritence... ");
+ DbgPrint(" Not Implemented.\n");
+
+ DbgPrint("Se: Default Dacl Specified... Test\n");
+ DbgPrint("Se: No Inheritence... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Dacl Inheritence... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Sacl Inheritence... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Dacl & Sacl Inheritence... ");
+ DbgPrint(" Not Implemented.\n");
+
+ DbgPrint("Se: Default Sacl (W/Privilege)... Test\n");
+ DbgPrint("Se: No Inheritence... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Dacl & Sacl Inheritence... ");
+ DbgPrint(" Not Implemented.\n");
+
+ DbgPrint("Se: Explicit Sacl (W/O Privilege)... Test\n");
+ DbgPrint(" ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Default Sacl (W/O Privilege)... Test\n");
+ DbgPrint(" ");
+ DbgPrint(" Not Implemented.\n");
+
+ DbgPrint("Se: Valid Owner Explicitly Specified... Test\n");
+ DbgPrint(" ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Invalid Owner Explicitly Specified... Test\n");
+ DbgPrint(" ");
+ DbgPrint(" Not Implemented.\n");
+
+ DbgPrint("Se: Explicit Group Specified... Test\n");
+ DbgPrint(" ");
+ DbgPrint(" Not Implemented.\n");
+
+
+
+ return CompletionStatus;
+
+}
+
+BOOLEAN
+TestSeQuerySecurity()
+//
+// Test:
+// No Security Descriptor
+// Query Owner
+// Query Group
+// Query Dacl
+// Query Sacl (Privileged)
+// Query Sacl (Unprivileged - should be rejected)
+//
+// Empty Security Descriptor
+// Query Owner
+// Query Group
+// Query Dacl
+// Query Sacl (Privileged)
+// Query Sacl (Unprivileged - should be rejected)
+//
+// Security Descriptor W/ Owner & Group
+// Query Owner
+// Query Group
+// Query Dacl
+// Query Sacl (Privileged)
+// Query Sacl (Unprivileged - should be rejected)
+//
+// Full Security Descriptor
+// Query Owner
+// Query Group
+// Query Dacl
+// Query Sacl (Privileged)
+// Query Sacl (Unprivileged - should be rejected)
+//
+{
+
+ BOOLEAN CompletionStatus = TRUE;
+
+ DbgPrint(" ");
+ DbgPrint(" Not Implemented.\n");
+
+#if 0
+ DbgPrint("Se: No Security Descriptor... \n");
+ DbgPrint("Se: Query Owner... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Query Group... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Query Dacl... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Query Sacl (Privileged)... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Query Sacl (Unprivileged)... ");
+ DbgPrint(" Not Implemented.\n");
+
+ DbgPrint("Se: Empty Security Descriptor... \n");
+ DbgPrint("Se: Query Owner... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Query Group... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Query Dacl... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Query Sacl (Privileged)... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Query Sacl (Unprivileged)... ");
+ DbgPrint(" Not Implemented.\n");
+
+ DbgPrint("Se: Security Descriptor W/ Owner & Group... \n");
+ DbgPrint("Se: Query Owner... ");
+ DbgPrint(" Not Implemented. \n");
+ DbgPrint("Se: Query Group... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Query Dacl... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Query Sacl (Privileged)... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Query Sacl (Unprivileged)... ");
+ DbgPrint(" Not Implemented.\n");
+
+ DbgPrint("Se: Full Security Descriptor...\n");
+ DbgPrint("Se: Query Owner... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Query Group... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Query Dacl... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Query Sacl (Privileged)... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Query Sacl (Unprivileged)... ");
+ DbgPrint(" Not Implemented.\n");
+#endif //0
+
+ return CompletionStatus;
+}
+
+BOOLEAN
+TestSeSetSecurity()
+//
+// Test:
+// No Security Descriptor
+// Set Valid Owner SID
+// Set Invalid Owner SID
+// Set Group
+// Set Dacl (explicitly granted by dacl)
+// Set Dacl (by virtue of ownership)
+// Set Dacl (invalid attempt)
+// Set Sacl (privileged)
+// Set Sacl (unprivileged - should be rejected)
+//
+// Empty Security Descriptor
+// Set Valid Owner SID
+// Set Invalid Owner SID
+// Set Group
+// Set Dacl (explicitly granted by dacl)
+// Set Dacl (by virtue of ownership)
+// Set Dacl (invalid attempt)
+// Set Sacl (privileged)
+// Set Sacl (unprivileged - should be rejected)
+//
+// Security Descriptor W/ Owner & Group Only
+// Set Valid Owner SID
+// Set Invalid Owner SID
+// Set Group
+// Set Dacl (explicitly granted by dacl)
+// Set Dacl (by virtue of ownership)
+// Set Dacl (invalid attempt)
+// Set Sacl (privileged)
+// Set Sacl (unprivileged - should be rejected)
+//
+// Full Security Descriptor
+// Set Valid Owner SID
+// Set Invalid Owner SID
+// Set Group
+// Set Dacl (explicitly granted by dacl)
+// Set Dacl (by virtue of ownership)
+// Set Dacl (invalid attempt)
+// Set Sacl (privileged)
+// Set Sacl (unprivileged - should be rejected)
+//
+{
+
+ BOOLEAN CompletionStatus = TRUE;
+
+ DbgPrint(" ");
+ DbgPrint(" Not Implemented.\n");
+#if 0
+ DbgPrint("Se: No Security Descriptor...\n");
+ DbgPrint("Se: Set Valid Owner SID... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Set Invalid Owner SID... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Set Group... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Set Dacl (explicitly granted by dacl)... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Set Dacl (by virtue of ownership)... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Set Dacl (invalid attempt)... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Set Sacl (privileged)... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Set Sacl (unprivileged - should be rejected)... ");
+ DbgPrint(" Not Implemented.\n");
+
+ DbgPrint("Se: Empty Security Descriptor...\n");
+ DbgPrint("Se: Set Valid Owner SID... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Set Invalid Owner SID... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Set Group... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Set Dacl (explicitly granted by dacl)... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Set Dacl (by virtue of ownership)... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Set Dacl (invalid attempt)... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Set Sacl (privileged)... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Set Sacl (unprivileged - should be rejected)... ");
+ DbgPrint(" Not Implemented.\n");
+
+ DbgPrint("Se: Security Descriptor W/ Owner & Group Only...\n");
+ DbgPrint("Se: Set Valid Owner SID... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Set Invalid Owner SID... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Set Group... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Set Dacl (explicitly granted by dacl)... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Set Dacl (by virtue of ownership)... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Set Dacl (invalid attempt)... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Set Sacl (privileged)... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Set Sacl (unprivileged - should be rejected)... ");
+ DbgPrint(" Not Implemented.\n");
+
+ DbgPrint("Se: Full Security Descriptor...\n");
+ DbgPrint("Se: Set Valid Owner SID... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Set Invalid Owner SID... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Set Group... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Set Dacl (explicitly granted by dacl)... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Set Dacl (by virtue of ownership)... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Set Dacl (invalid attempt)... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Set Sacl (privileged)... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Set Sacl (unprivileged - should be rejected)... ");
+ DbgPrint(" Not Implemented.\n");
+
+#endif //0
+
+ return CompletionStatus;
+
+}
+
+BOOLEAN
+TestSeAccess()
+//
+// Test:
+//
+// Creation
+// No Access Requested (should be rejected)
+// Specific Access Requested
+// - Attempted Granted
+// - Attempt Ungranted
+// Access System Security
+//
+// Open Existing
+// No Access Requested (should be rejected)
+// Specific Access Requested
+// - Attempted Granted
+// - Attempt Ungranted
+// Access System Security
+//
+
+{
+ BOOLEAN CompletionStatus = TRUE;
+
+ DbgPrint(" ");
+ DbgPrint(" Not Implemented.\n");
+#if 0
+
+ DbgPrint("Se: Creation...\n");
+ DbgPrint("Se: No Access Requested (should be rejected)... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Specific Access Requested... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: - Attempted Granted... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: - Attempt Ungranted... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Access System Security... ");
+ DbgPrint(" Not Implemented.\n");
+
+ DbgPrint("Se: Open Existing...\n");
+ DbgPrint("Se: No Access Requested (should be rejected)... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Specific Access Requested... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: - Attempted Granted... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: - Attempt Ungranted... ");
+ DbgPrint(" Not Implemented.\n");
+ DbgPrint("Se: Access System Security... ");
+ DbgPrint(" Not Implemented.\n");
+#endif //0
+
+#if 0 //old code
+// Without security descriptor
+// Simple desired access mask...
+//
+
+ DbgPrint("Se: Test1b... \n"); // Attempt ungranted access
+ Status = NtSetEvent(
+ Event1,
+ NULL
+ );
+ ASSERT(!NT_SUCCESS(Status));
+
+ DbgPrint("Se: Test1c... \n"); // Delete object
+ Status = NtClose(Event1);
+ ASSERT(NT_SUCCESS(Status));
+
+
+ //
+ // Without security descriptor
+ // Simple desired access mask...
+ //
+
+ DbgPrint("Se: Test2a... \n"); // unnamed object, specific access
+ Status = NtCreateEvent(
+ &Event1,
+ (EVENT_MODIFY_STATE | STANDARD_DELETE),
+ &Event1ObjectAttributes,
+ NotificationEvent,
+ FALSE
+ );
+ ASSERT(NT_SUCCESS(Status));
+
+ DbgPrint("Se: Test2b... \n"); // Attempt granted specific access
+ Status = NtSetEvent(
+ Event1,
+ NULL
+ );
+ ASSERT(NT_SUCCESS(Status));
+
+ DbgPrint("Se: Test2c... \n"); // Delete object
+
+
+ //
+ // Without security descriptor
+ // Generic desired access mask...
+ //
+
+ DbgPrint("Se: Test3a... \n"); // Unnamed object, generic mask
+ Status = NtCreateEvent(
+ &Event1,
+ GENERIC_EXECUTE,
+ &Event1ObjectAttributes,
+ NotificationEvent,
+ FALSE
+ );
+ ASSERT(NT_SUCCESS(Status));
+
+ DbgPrint("Se: Test3b... \n"); // Attempt implied granted access
+ Status = NtSetEvent(
+ Event1,
+ NULL
+ );
+ ASSERT(NT_SUCCESS(Status));
+
+ DbgPrint("Se: Test3c... \n"); // Delete object
+ Status = NtClose(Event1);
+ ASSERT(NT_SUCCESS(Status));
+
+
+ //
+ // Without security descriptor
+ // Empty desired access mask...
+ //
+
+ DbgPrint("Se: Test4a... \n"); // Empty desired access
+ Status = NtCreateEvent(
+ &Event1,
+ 0,
+ &Event1ObjectAttributes,
+ NotificationEvent,
+ FALSE
+ );
+ ASSERT(!NT_SUCCESS(Status));
+
+
+ RtlCreateSecurityDescriptor( Event1SecurityDescriptor,
+ SECURITY_DESCRIPTOR_REVISION);
+ InitializeObjectAttributes(&Event1ObjectAttributes,
+ NULL, 0, NULL,
+ Event1SecurityDescriptor);
+ DbgPrint("Se: Empty Security Descriptor... \n");
+
+ //
+ // Without security descriptor
+ // Simple desired access mask...
+ //
+
+ DbgPrint("Se: Test1a... \n"); // Create unnamed object
+ Status = NtCreateEvent(
+ &Event1,
+ STANDARD_DELETE,
+ &Event1ObjectAttributes,
+ NotificationEvent,
+ FALSE
+ );
+ ASSERT(NT_SUCCESS(Status));
+
+ DbgPrint("Se: Test1b... \n"); // Attempt ungranted access
+ Status = NtSetEvent(
+ Event1,
+ NULL
+ );
+ ASSERT(!NT_SUCCESS(Status));
+
+ DbgPrint("Se: Test1c... \n"); // Delete object
+ Status = NtClose(Event1);
+ ASSERT(NT_SUCCESS(Status));
+
+
+ //
+ // Without security descriptor
+ // Simple desired access mask...
+ //
+
+ DbgPrint("Se: Test2a... \n"); // unnamed object, specific access
+ Status = NtCreateEvent(
+ &Event1,
+ (EVENT_MODIFY_STATE | STANDARD_DELETE),
+ &Event1ObjectAttributes,
+ NotificationEvent,
+ FALSE
+ );
+ ASSERT(NT_SUCCESS(Status));
+
+ DbgPrint("Se: Test2b... \n"); // Attempt granted specific access
+ Status = NtSetEvent(
+ Event1,
+ NULL
+ );
+ ASSERT(NT_SUCCESS(Status));
+
+ DbgPrint("Se: Test2c... \n"); // Delete object
+ Status = NtClose(Event1);
+ ASSERT(NT_SUCCESS(Status));
+
+
+ //
+ // Without security descriptor
+ // Generic desired access mask...
+ //
+
+ DbgPrint("Se: Test3a... \n"); // Unnamed object, generic mask
+ Status = NtCreateEvent(
+ &Event1,
+ GENERIC_EXECUTE,
+ &Event1ObjectAttributes,
+ NotificationEvent,
+ FALSE
+ );
+ ASSERT(NT_SUCCESS(Status));
+
+ DbgPrint("Se: Test3b... \n"); // Attempt implied granted access
+ Status = NtSetEvent(
+ Event1,
+ NULL
+ );
+ ASSERT(NT_SUCCESS(Status));
+
+ DbgPrint("Se: Test3c... \n"); // Delete object
+ Status = NtClose(Event1);
+ ASSERT(NT_SUCCESS(Status));
+
+
+ //
+ // Without security descriptor
+ // Empty desired access mask...
+ //
+
+ DbgPrint("Se: Test4a... \n"); // Empty desired access
+ Status = NtCreateEvent(
+ &Event1,
+ 0,
+ &Event1ObjectAttributes,
+ NotificationEvent,
+ FALSE
+ );
+ ASSERT(!NT_SUCCESS(Status));
+#endif // old code
+
+ return CompletionStatus;
+}
+
+BOOLEAN
+TSeAcc()
+{
+ BOOLEAN Result = TRUE;
+
+ DbgPrint("Se: Initialization... ");
+ TestSeInitialize();
+ DbgPrint("Succeeded.\n");
+
+ DbgPrint("Se: Unnamed Object Creation Test... Suite\n");
+ if (!TestSeUnnamedCreate()) {
+ Result = FALSE;
+ }
+ DbgPrint("Se: Named Object Creation Test... Suite\n");
+ if (!TestSeNamedCreate()) {
+ Result = FALSE;
+ }
+ DbgPrint("Se: Query Object Security Descriptor Test... Suite\n");
+ if (!TestSeQuerySecurity()) {
+ Result = FALSE;
+ }
+ DbgPrint("Se: Set Object Security Descriptor Test... Suite\n");
+ if (!TestSeSetSecurity()) {
+ Result = FALSE;
+ }
+ DbgPrint("Se: Access Test... Suite\n");
+ if (!TestSeAccess()) {
+ Result = FALSE;
+ }
+
+ DbgPrint("\n");
+ DbgPrint("\n");
+ DbgPrint(" ********************\n");
+ DbgPrint(" ** **\n");
+
+ if (Result = TRUE) {
+ DbgPrint(" ** Test Succeeded **\n");
+ } else {
+ DbgPrint(" ** Test Failed **\n");
+ }
+
+ DbgPrint(" ** **\n");
+ DbgPrint(" ********************\n");
+ DbgPrint("\n");
+ DbgPrint("\n");
+
+ return Result;
+}
+
diff --git a/private/ntos/se/ctsertl.c b/private/ntos/se/ctsertl.c
new file mode 100644
index 000000000..8ed54303f
--- /dev/null
+++ b/private/ntos/se/ctsertl.c
@@ -0,0 +1,1137 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ ctsertl.c
+
+Abstract:
+
+ Common security RTL test routines.
+
+ These routines are used in both the kernel and user mode RTL tests.
+
+
+
+Author:
+
+ Jim Kelly (JimK) 23-Mar-1990
+
+Environment:
+
+ Test of security.
+
+Revision History:
+
+--*/
+
+#include "tsecomm.c"
+
+
+
+
+////////////////////////////////////////////////////////////////
+// //
+// Test routines //
+// //
+////////////////////////////////////////////////////////////////
+
+
+BOOLEAN
+TestSeSid()
+{
+
+#define TARGET_SID_ARRAY_LENGTH 1024
+
+typedef struct _TALT_SID1 {
+ ULONG Value[3];
+} TALT_SID1;
+typedef TALT_SID1 *PTALT_SID1;
+
+ NTSTATUS Status;
+
+ PVOID Ignore;
+
+ PSID TFredSid;
+ PSID TBarneySid;
+ PSID TWilmaSid;
+ PSID TWilmaSubSid;
+ PSID TNoSubSid;
+ PSID TTempSid;
+
+ PSID_AND_ATTRIBUTES SourceArray;
+ PSID_AND_ATTRIBUTES TargetArray;
+ ULONG TargetLength;
+ ULONG OriginalTargetLength;
+
+ ULONG NormalGroupAttributes;
+ ULONG OwnerGroupAttributes;
+
+ // Temporary Hack ...
+ NormalGroupAttributes = 7;
+ OwnerGroupAttributes = 15;
+ // End Temporary Hack...
+
+
+ TFredSid = (PSID)TstAllocatePool( PagedPool, 256 );
+ TBarneySid = (PSID)TstAllocatePool( PagedPool, 256 );
+ TWilmaSid = (PSID)TstAllocatePool( PagedPool, 256 );
+ TWilmaSubSid = (PSID)TstAllocatePool( PagedPool, 256 );
+ TNoSubSid = (PSID)TstAllocatePool( PagedPool, 256 );
+ TTempSid = (PSID)TstAllocatePool( PagedPool, 256 );
+
+
+ //
+ // Valid SID structure test
+ //
+
+ if (!RtlValidSid( TFredSid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlValidSid, TFredSid\n");
+ return FALSE;
+ }
+
+ if (!RtlValidSid( TBarneySid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlValidSid, TBarneySid\n");
+ return FALSE;
+ }
+
+ if (!RtlValidSid( TWilmaSid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlValidSid, TWilmaSid\n");
+ return FALSE;
+ }
+
+ if (!RtlValidSid( TWilmaSubSid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlValidSid, TWilmaSubSid\n");
+ return FALSE;
+ }
+
+ if (!RtlValidSid( TNoSubSid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlValidSid, TNoSubSid\n");
+ return FALSE;
+ }
+
+
+ //
+ // Equal SIDs Test
+ //
+
+ if (RtlEqualSid( TFredSid, TBarneySid )) {
+ DbgPrint("*Se** Failure: RtlEqualSid, TFredSid - TBarneySid\n");
+ DbgPrint("**** Failed **** \n");
+ return FALSE;
+ }
+
+ if (!RtlEqualSid( TFredSid, TFredSid )) {
+ DbgPrint("*Se** Failure: RtlEqualSid, TFredSid - TFredSid\n");
+ return FALSE;
+ }
+
+ if (RtlEqualSid( TWilmaSid, TWilmaSubSid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlEqualSid, TWilmaSid - TWilmaSubSid\n");
+ return FALSE;
+ }
+
+ if (RtlEqualSid( TWilmaSid, TNoSubSid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlEqualSid, TWilmaSid - TNoSubSid\n");
+ return FALSE;
+ }
+
+
+ //
+ // Length Required test
+ //
+
+ if (RtlLengthRequiredSid( 0 ) != 8) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlLengthRequiredSid, 0 SubAuthorities\n");
+ return FALSE;
+ }
+
+ if (RtlLengthRequiredSid( 1 ) != 12) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlLengthRequiredSid, 1 SubAuthorities\n");
+ return FALSE;
+ }
+
+ if (RtlLengthRequiredSid( 2 ) != 16) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlLengthRequiredSid, 2 SubAuthorities\n");
+ return FALSE;
+ }
+
+
+ //
+ // Length of SID test
+ //
+
+ if (SeLengthSid( TNoSubSid ) != 8) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: SeLengthSid, TNoSubSid\n");
+ return FALSE;
+ }
+
+ if (SeLengthSid( TFredSid ) != 12) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: SeLengthSid, TFredSid\n");
+ return FALSE;
+ }
+
+ if (SeLengthSid( TWilmaSubSid ) != 16) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: SeLengthSid, TWilmaSubSid\n");
+ return FALSE;
+ }
+
+
+ //
+ // Copy SID Test
+ //
+
+ if (NT_SUCCESS(RtlCopySid( 7, TTempSid, TNoSubSid ))) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlCopySid, insufficient TNoSubSid\n");
+ return FALSE;
+ }
+
+
+ if (!NT_SUCCESS(RtlCopySid( 256, TTempSid, TNoSubSid ))) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlCopySid, TNoSubSid\n");
+ return FALSE;
+ }
+
+ if (!RtlEqualSid( TTempSid, TNoSubSid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlCopySid compare, TNoSubSid\n");
+ return FALSE;
+ }
+
+
+ if (NT_SUCCESS(RtlCopySid( 11, TTempSid, TBarneySid ))) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlCopySid, insufficient TBarneySid\n");
+ return FALSE;
+ }
+
+
+ if (!NT_SUCCESS(RtlCopySid( 256, TTempSid, TBarneySid ))) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlCopySid, TBarneySid\n");
+ return FALSE;
+ }
+
+ if (!RtlEqualSid( TTempSid, TBarneySid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlCopySid compare, TBarneySid\n");
+ return FALSE;
+ }
+
+ if (NT_SUCCESS(RtlCopySid( 15, TTempSid, TWilmaSubSid ))) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlCopySid, insufficient TWilmaSubSid\n");
+ return FALSE;
+ }
+
+ if (!NT_SUCCESS(RtlCopySid( 256, TTempSid, TWilmaSubSid ))) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlCopySid, TNoSubSid\n");
+ return FALSE;
+ }
+
+ if (!RtlEqualSid( TTempSid, TWilmaSubSid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlCopySid compare, TWilmaSubSid\n");
+ return FALSE;
+ }
+
+
+ //
+ // Validate all the tsevars SIDs
+ //
+
+ //
+ // Bedrock SIDs
+ //
+
+
+ if (!RtlValidSid( BedrockDomainSid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlValidSid, BedrockDomainSid\n");
+ return FALSE;
+ }
+
+ if (!RtlValidSid( FredSid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlValidSid, FredSid\n");
+ return FALSE;
+ }
+
+ if (!RtlValidSid( WilmaSid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlValidSid, WilmaSid\n");
+ return FALSE;
+ }
+
+ if (!RtlValidSid( PebblesSid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlValidSid, PebblesSid\n");
+ return FALSE;
+ }
+
+ if (!RtlValidSid( DinoSid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlValidSid, DinoSid\n");
+ return FALSE;
+ }
+
+ if (!RtlValidSid( BarneySid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlValidSid, BarneySid\n");
+ return FALSE;
+ }
+
+ if (!RtlValidSid( BettySid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlValidSid, BettySid\n");
+ return FALSE;
+ }
+
+ if (!RtlValidSid( BambamSid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlValidSid, BambamSid\n");
+ return FALSE;
+ }
+
+ if (!RtlValidSid( FlintstoneSid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlValidSid, FlintstoneSid\n");
+ return FALSE;
+ }
+
+ if (!RtlValidSid( RubbleSid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlValidSid, RubbleSid\n");
+ return FALSE;
+ }
+
+ if (!RtlValidSid( AdultSid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlValidSid, AdultSid\n");
+ return FALSE;
+ }
+
+ if (!RtlValidSid( ChildSid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlValidSid, ChildSid\n");
+ return FALSE;
+ }
+
+ if (!RtlValidSid( NeandertholSid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlValidSid, NeandertholSid\n");
+ return FALSE;
+ }
+
+ //
+ // Well known SIDs
+ //
+
+ if (!RtlValidSid( NullSid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlValidSid, NullSid\n");
+ return FALSE;
+ }
+
+ if (!RtlValidSid( WorldSid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlValidSid, WorldSid\n");
+ return FALSE;
+ }
+
+ if (!RtlValidSid( LocalSid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlValidSid, CreatorSid\n");
+ return FALSE;
+ }
+
+ if (!RtlValidSid( NtAuthoritySid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlValidSid, NtAuthoritySid\n");
+ return FALSE;
+ }
+
+ if (!RtlValidSid( DialupSid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlValidSid, DialupSid\n");
+ return FALSE;
+ }
+
+ if (!RtlValidSid( NetworkSid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlValidSid, NetworkSid\n");
+ return FALSE;
+ }
+
+ if (!RtlValidSid( BatchSid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlValidSid, BatchSid\n");
+ return FALSE;
+ }
+
+ if (!RtlValidSid( InteractiveSid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlValidSid, InteractiveSid\n");
+ return FALSE;
+ }
+
+ if (!RtlValidSid( LocalManagerSid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlValidSid, LocalManagerSid\n");
+ return FALSE;
+ }
+
+ if (!RtlValidSid( LocalGuestSid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlValidSid, LocalGuestSid\n");
+ return FALSE;
+ }
+
+ if (!RtlValidSid( LocalSystemSid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlValidSid, LocalSystemSid\n");
+ return FALSE;
+ }
+
+ if (!RtlValidSid( LocalAdminSid )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlValidSid, LocalAdminSid\n");
+ return FALSE;
+ }
+
+
+
+
+ //
+ // Test SidAndAttributesArray copy routine
+ //
+
+ SourceArray = (PSID_AND_ATTRIBUTES)TstAllocatePool( PagedPool, 100 );
+ TargetArray = (PSID_AND_ATTRIBUTES)TstAllocatePool( PagedPool,
+ TARGET_SID_ARRAY_LENGTH
+ );
+ TargetLength = TARGET_SID_ARRAY_LENGTH - (5 * sizeof(PSID_AND_ATTRIBUTES));
+ OriginalTargetLength = TargetLength;
+
+ SourceArray[0].Sid = &PebblesSid;
+ SourceArray[0].Attributes = 0;
+ SourceArray[1].Sid = &FlintstoneSid;
+ SourceArray[1].Attributes = OwnerGroupAttributes;
+ SourceArray[2].Sid = &ChildSid;
+ SourceArray[2].Attributes = NormalGroupAttributes;
+ SourceArray[3].Sid = &NeandertholSid;
+ SourceArray[3].Attributes = NormalGroupAttributes;
+ SourceArray[4].Sid = &WorldSid;
+ SourceArray[4].Attributes = NormalGroupAttributes;
+
+ Status = RtlCopySidAndAttributesArray(
+ 0,
+ SourceArray,
+ TargetLength,
+ TargetArray,
+ &(TargetArray[5]),
+ &(PSID)Ignore,
+ &TargetLength
+ );
+
+ if (!NT_SUCCESS(Status) || TargetLength != OriginalTargetLength ) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtLCopySidAndAttributesArray, Zero length.\n");
+ return FALSE;
+ }
+
+
+
+ Status = RtlCopySidAndAttributesArray(
+ 1,
+ SourceArray,
+ 1, // too short buffer
+ TargetArray,
+ &(TargetArray[1]),
+ &(PSID)Ignore,
+ &TargetLength
+ );
+
+ if (NT_SUCCESS(Status)) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtLCopySidAndAttributesArray,\n");
+ DbgPrint("*Se** Buffer Too Short Test.\n");
+ return FALSE;
+ }
+
+
+ TargetLength = TARGET_SID_ARRAY_LENGTH - (5 * sizeof(PSID_AND_ATTRIBUTES));
+ OriginalTargetLength = TargetLength;
+
+ Status = RtlCopySidAndAttributesArray(
+ 5,
+ SourceArray,
+ TargetLength,
+ TargetArray,
+ &(TargetArray[5]),
+ &(PSID)Ignore,
+ &TargetLength
+ );
+
+ if (!NT_SUCCESS(Status) ||
+ !RtlEqualSid( SourceArray[0].Sid, TargetArray[0].Sid ) ||
+ !RtlEqualSid( SourceArray[1].Sid, TargetArray[1].Sid ) ||
+ !RtlEqualSid( SourceArray[2].Sid, TargetArray[2].Sid ) ||
+ !RtlEqualSid( SourceArray[3].Sid, TargetArray[3].Sid ) ||
+ !RtlEqualSid( SourceArray[4].Sid, TargetArray[4].Sid ) ||
+ ( SourceArray[0].Attributes != TargetArray[0].Attributes ) ||
+ ( SourceArray[1].Attributes != TargetArray[1].Attributes ) ||
+ ( SourceArray[2].Attributes != TargetArray[2].Attributes ) ||
+ ( SourceArray[3].Attributes != TargetArray[3].Attributes ) ||
+ ( SourceArray[4].Attributes != TargetArray[4].Attributes ) ) {
+
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtLCopySidAndAttributesArray,\n");
+ DbgPrint("*Se** Valid copy of 5 SIDs test.\n");
+ return FALSE;
+ }
+
+
+
+ return TRUE;
+
+
+}
+
+
+BOOLEAN
+TestSeSecurityDescriptor()
+{
+ NTSTATUS Status;
+ PSECURITY_DESCRIPTOR TFredDescriptor;
+ PSECURITY_DESCRIPTOR TBarneyDescriptor;
+ PSECURITY_DESCRIPTOR TWilmaDescriptor;
+
+ PSECURITY_DESCRIPTOR TTempDescriptor;
+
+ SECURITY_DESCRIPTOR_CONTROL Control;
+ ULONG Revision;
+
+ PACL TDacl;
+ BOOLEAN TDaclPresent;
+ BOOLEAN TDaclDefaulted;
+
+ PACL TSacl;
+ BOOLEAN TSaclPresent;
+ BOOLEAN TSaclDefaulted;
+
+ PSID TOwner;
+ BOOLEAN TOwnerDefaulted;
+ PSID TGroup;
+ BOOLEAN TGroupDefaulted;
+
+
+ PSID TFredSid;
+ PSID TBarneySid;
+ PSID TWilmaSid;
+ PSID TWilmaSubSid;
+ PSID TNoSubSid;
+ PSID TTempSid;
+
+
+ TFredDescriptor = (PSECURITY_DESCRIPTOR)TstAllocatePool( PagedPool, 1024 );
+ TBarneyDescriptor = (PSECURITY_DESCRIPTOR)TstAllocatePool( PagedPool, 1024 );
+ TWilmaDescriptor = (PSECURITY_DESCRIPTOR)TstAllocatePool( PagedPool, 1024 );
+ TTempDescriptor = (PSECURITY_DESCRIPTOR)TstAllocatePool( PagedPool, 1024 );
+
+
+ TFredSid = (PSID)TstAllocatePool( PagedPool, 256 );
+ TBarneySid = (PSID)TstAllocatePool( PagedPool, 256 );
+ TWilmaSid = (PSID)TstAllocatePool( PagedPool, 256 );
+ TWilmaSubSid = (PSID)TstAllocatePool( PagedPool, 256 );
+ TNoSubSid = (PSID)TstAllocatePool( PagedPool, 256 );
+ TTempSid = (PSID)TstAllocatePool( PagedPool, 256 );
+
+
+ //
+ // Build an ACL or two for use.
+
+ TDacl = (PACL)TstAllocatePool( PagedPool, 256 );
+ TSacl = (PACL)TstAllocatePool( PagedPool, 256 );
+
+ TDacl->AclRevision=TSacl->AclRevision=ACL_REVISION;
+ TDacl->Sbz1=TSacl->Sbz1=0;
+ TDacl->Sbz2=TSacl->Sbz2=0;
+ TDacl->AclSize=256;
+ TSacl->AclSize=8;
+ TDacl->AceCount=TSacl->AceCount=0;
+
+
+ //
+ // Create Security Descriptor test
+ //
+
+ if (NT_SUCCESS(RtlCreateSecurityDescriptor( TTempDescriptor, 0 ))) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlCreateSecurityDescriptor, Rev=0\n");
+ return FALSE;
+ }
+
+ if (NT_SUCCESS(RtlCreateSecurityDescriptor( TTempDescriptor, 2 ))) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlCreateSecurityDescriptor, Rev=2\n");
+ return FALSE;
+ }
+
+ if (!NT_SUCCESS(RtlCreateSecurityDescriptor( TTempDescriptor, 1 ))) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlCreateSecurityDescriptor, Rev=1\n");
+ return FALSE;
+ }
+
+#ifdef NOT_YET_DEBUGGED
+ //
+ // Make sure fields have been set properly
+ //
+
+ if (!NT_SUCCESS(RtlGetControlSecurityDescriptor( TTempDescriptor,
+ &Control,
+ &Revision))) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlGetControlSecurityDescriptor\n");
+ DbgPrint("*Se** Call failed. Status = 0x%lx\n", Status);
+ return FALSE;
+ }
+
+ if ( (Control != 0) || (Revision != 1) ) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlGetControlSecurityDescriptor\n");
+ DbgPrint("*Se** Bad Control or Revision value. \n");
+ DbgPrint("*Se** Status = 0x%lx\n", Status);
+ DbgPrint("*Se** Returned Revision = 0x%lx\n",Revision );
+ DbgPrint("*Se** Returned Control = 0x%lx\n", (ULONG)Control);
+ return FALSE;
+ }
+#else
+ DBG_UNREFERENCED_LOCAL_VARIABLE( Status );
+ DBG_UNREFERENCED_LOCAL_VARIABLE( Revision );
+ DBG_UNREFERENCED_LOCAL_VARIABLE( Control );
+#endif //NOT_YET_DEFINED
+
+ if (!NT_SUCCESS(RtlGetDaclSecurityDescriptor( TTempDescriptor,
+ &TDaclPresent,
+ &TDacl,
+ &TDaclDefaulted))) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlGetDaclSecurityDescriptor, Empty\n");
+ return FALSE;
+ }
+
+ if (TDaclPresent) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlGetDaclSecurityDescriptor, Empty-TDaclPresent\n");
+ return FALSE;
+ }
+
+ if (!NT_SUCCESS(RtlGetSaclSecurityDescriptor( TTempDescriptor,
+ &TSaclPresent,
+ &TSacl,
+ &TSaclDefaulted))) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlGetSaclSecurityDescriptor, Empty\n");
+ return FALSE;
+ }
+
+ if (TSaclPresent) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlGetSaclSecurityDescriptor, Empty-TSaclPresent\n");
+ return FALSE;
+ }
+
+ if (!NT_SUCCESS(RtlGetOwnerSecurityDescriptor( TTempDescriptor,
+ &TOwner,
+ &TOwnerDefaulted))) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlGetOwnerSecurityDescriptor, Empty\n");
+ return FALSE;
+ }
+
+ if (TOwner != NULL) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlGetOwnerSecurityDescriptor, Empty-TOwner\n");
+ return FALSE;
+ }
+
+ if (!NT_SUCCESS(RtlGetGroupSecurityDescriptor( TTempDescriptor,
+ &TGroup,
+ &TGroupDefaulted))) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlGetGroupSecurityDescriptor, Empty\n");
+ return FALSE;
+ }
+
+ if (TGroup != NULL) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlGetGroupSecurityDescriptor, Empty-TGroup\n");
+ return FALSE;
+ }
+
+ //
+ // Valid Security Descriptor test
+ //
+
+ ((SECURITY_DESCRIPTOR *)TTempDescriptor)->Revision=0;
+ if (RtlValidSecurityDescriptor( TTempDescriptor )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlValidSecurityDescriptor, Rev=0\n");
+ return FALSE;
+ }
+ ((SECURITY_DESCRIPTOR *)TTempDescriptor)->Revision=1;
+
+ if (!RtlValidSecurityDescriptor( TTempDescriptor )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlValidSecurityDescriptor, Empty\n");
+ return FALSE;
+ }
+
+
+ //
+ // Length test
+ //
+
+ if (RtlLengthSecurityDescriptor( TTempDescriptor ) != 20) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlLengthSecurityDescriptor, Empty\n");
+ return FALSE;
+ }
+
+ //
+ // Add in an owner
+ //
+
+ if (!NT_SUCCESS(RtlSetOwnerSecurityDescriptor( TTempDescriptor, TWilmaSid, FALSE ))) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlSetOwnerSecurityDescriptor, TWilmaSid\n");
+ return FALSE;
+ }
+ if (RtlLengthSecurityDescriptor( TTempDescriptor ) != 32) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlLengthSecurityDescriptor, Wilma Owner\n");
+ return FALSE;
+ }
+
+ //
+ // Add in a Dacl
+ //
+
+ if (!NT_SUCCESS(RtlSetDaclSecurityDescriptor( TTempDescriptor, TRUE,
+ TDacl, FALSE ))) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlSetDaclSecurityDescriptor, TDacl\n");
+ return FALSE;
+ }
+ if (RtlLengthSecurityDescriptor( TTempDescriptor ) != 40) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlLengthSecurityDescriptor, TDacl Dacl\n");
+ return FALSE;
+ }
+
+ //
+ // Add in a Sacl
+ //
+
+ if (!NT_SUCCESS(RtlSetSaclSecurityDescriptor( TTempDescriptor, TRUE,
+ TSacl, FALSE ))) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlSetSaclSecurityDescriptor, TSacl\n");
+ return FALSE;
+ }
+ if (RtlLengthSecurityDescriptor( TTempDescriptor ) != 48) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlLengthSecurityDescriptor, TSacl Sacl\n");
+ return FALSE;
+ }
+
+ //
+ // Add in a Group (with 2 sub-authorities)
+ //
+
+ if (!NT_SUCCESS(RtlSetGroupSecurityDescriptor( TTempDescriptor, TWilmaSubSid, FALSE ))) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlSetGroupSecurityDescriptor, TWilmaSubSid\n");
+ return FALSE;
+ }
+ if (RtlLengthSecurityDescriptor( TTempDescriptor ) != 64) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("*Se** Failure: RtlLengthSecurityDescriptor, WilmaSub Group\n");
+ return FALSE;
+ }
+
+
+ return TRUE;
+
+}
+
+
+BOOLEAN
+TestSeAccessMask()
+{
+ return TRUE;
+}
+
+
+
+
+VOID
+DumpAclSizeInfo(PACL_SIZE_INFORMATION AclSizeInfo)
+{
+ DbgPrint("\n");
+ DbgPrint("Acl size info:\n");
+ DbgPrint("AceCount = %d\n",AclSizeInfo->AceCount);
+ DbgPrint("AclBytesInUse = %d\n",AclSizeInfo->AclBytesInUse);
+ DbgPrint("AclBytesFree = %d\n",AclSizeInfo->AclBytesFree);
+ return;
+}
+
+#define NUM_ACE 6
+
+typedef struct _SIMPLE_ACE {
+ ACE_HEADER Header;
+ ACCESS_MASK Mask;
+ SID Sid;
+ } SIMPLE_ACE, *PSIMPLE_ACE;
+
+
+BOOLEAN
+TestSeAclRtl()
+{
+
+ PACL TDacl;
+ NTSTATUS Status;
+
+ ACL_REVISION_INFORMATION AclInformation;
+ ACL_REVISION_INFORMATION AclInformationOut;
+
+ ACL_SIZE_INFORMATION AclSizeInfo;
+
+ PVOID AceList;
+ PVOID Ace;
+
+ ULONG AceSize;
+
+//
+// Define the Dead domain
+//
+// Dead Domain S-1-54399-23-18-02
+// Bobby S-1-54399-23-18-02-2
+// Jerry S-1-54399-23-18-02-3
+// Phil S-1-54399-23-18-02-4
+// Kreutzman S-1-54399-23-18-02-5
+// Brent S-1-54399-23-18-02-6
+// Micky S-1-54399-23-18-02-7
+//
+
+#define DEAD_AUTHORITY {0,0,0,0,212,127}
+#define DEAD_SUBAUTHORITY_0 0x00000017L
+#define DEAD_SUBAUTHORITY_1 0x00000012L
+#define DEAD_SUBAUTHORITY_2 0x00000002L
+
+#define BOBBY_RID 0x00000002
+#define JERRY_RID 0x00000003
+#define PHIL_RID 0x00000004
+#define KREUTZMAN_RID 0x00000005
+#define BRENT_RID 0x00000006
+#define MICKY_RID 0x00000007
+
+ PSID DeadDomainSid;
+
+ PSID BobbySid;
+ PSID JerrySid;
+ PSID PhilSid;
+ PSID KreutzmanSid;
+ PSID BrentSid;
+ PSID MickySid;
+
+ ULONG SidWithZeroSubAuthorities;
+ ULONG SidWithOneSubAuthority;
+ ULONG SidWithThreeSubAuthorities;
+ ULONG SidWithFourSubAuthorities;
+
+ SID_IDENTIFIER_AUTHORITY DeadAuthority = DEAD_AUTHORITY;
+
+
+ //
+ // The following SID sizes need to be allocated
+ //
+
+ SidWithZeroSubAuthorities = RtlLengthRequiredSid( 0 );
+ SidWithOneSubAuthority = RtlLengthRequiredSid( 1 );
+ SidWithThreeSubAuthorities = RtlLengthRequiredSid( 3 );
+ SidWithFourSubAuthorities = RtlLengthRequiredSid( 4 );
+
+ DeadDomainSid = (PSID)TstAllocatePool(PagedPool,SidWithThreeSubAuthorities);
+
+ BobbySid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+ JerrySid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+ PhilSid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+ KreutzmanSid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+
+ BrentSid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+ MickySid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+
+ RtlInitializeSid( DeadDomainSid, &DeadAuthority, 3 );
+ *(RtlSubAuthoritySid( DeadDomainSid, 0)) = DEAD_SUBAUTHORITY_0;
+ *(RtlSubAuthoritySid( DeadDomainSid, 1)) = DEAD_SUBAUTHORITY_1;
+ *(RtlSubAuthoritySid( DeadDomainSid, 2)) = DEAD_SUBAUTHORITY_2;
+
+ RtlCopySid( SidWithFourSubAuthorities, BobbySid, DeadDomainSid);
+ *(RtlSubAuthorityCountSid( BobbySid )) += 1;
+ *(RtlSubAuthoritySid( BobbySid, 3)) = BOBBY_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, JerrySid, DeadDomainSid);
+ *(RtlSubAuthorityCountSid( JerrySid )) += 1;
+ *(RtlSubAuthoritySid( JerrySid, 3)) = JERRY_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, PhilSid, DeadDomainSid);
+ *(RtlSubAuthorityCountSid( PhilSid )) += 1;
+ *(RtlSubAuthoritySid( PhilSid, 3)) = PHIL_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, KreutzmanSid, DeadDomainSid);
+ *(RtlSubAuthorityCountSid( KreutzmanSid )) += 1;
+ *(RtlSubAuthoritySid( KreutzmanSid, 3)) = KREUTZMAN_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, BrentSid, DeadDomainSid);
+ *(RtlSubAuthorityCountSid( BrentSid )) += 1;
+ *(RtlSubAuthoritySid( BrentSid, 3)) = BRENT_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, MickySid, DeadDomainSid);
+ *(RtlSubAuthorityCountSid( MickySid )) += 1;
+ *(RtlSubAuthoritySid( MickySid, 3)) = MICKY_RID;
+
+ TDacl = (PACL)TstAllocatePool( PagedPool, 256 );
+
+ //DbgBreakPoint();
+
+ if (!NT_SUCCESS(Status = RtlCreateAcl( TDacl, 256, ACL_REVISION ))) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("RtlCreateAcl returned %X \n",Status);
+ return(FALSE);
+ }
+
+ //DbgBreakPoint();
+
+ if (!NT_SUCCESS( Status = RtlValidAcl( TDacl ) )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("RtlValidAcl returned %X \n",Status);
+ return(FALSE);
+ }
+
+ //DbgBreakPoint();
+
+ AclInformation.AclRevision = ACL_REVISION;
+
+ if (!NT_SUCCESS( Status = RtlSetInformationAcl( TDacl, &AclInformation,
+ sizeof(AclInformation), AclRevisionInformation ) )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("RtlSetInformation returned %X \n",Status);
+ return(FALSE);
+ }
+
+ if (!NT_SUCCESS( Status = RtlQueryInformationAcl( TDacl, (PVOID)&AclInformationOut,
+ sizeof(AclInformationOut), AclRevisionInformation ) )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("RtlQueryInformation returned %X during revision query \n",Status);
+ return(FALSE);
+ }
+
+ if (AclInformationOut.AclRevision != ACL_REVISION) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("RtlQueryInformation returned incorrect revision \n");
+ return(FALSE);
+ }
+
+ if (!NT_SUCCESS( Status = RtlQueryInformationAcl( TDacl, (PVOID)&AclSizeInfo,
+ sizeof(AclSizeInfo), AclSizeInformation ) )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("RtlQueryInformation returned %X during size query \n",Status);
+ return(FALSE);
+ }
+
+ // DumpAclSizeInfo(&AclSizeInfo);
+
+ AceSize = 6 * SidWithFourSubAuthorities + 1 * SidWithThreeSubAuthorities
+ + 7 * (sizeof( ACE_HEADER ) + sizeof( ACCESS_MASK ));
+
+ AceList = (PVOID)TstAllocatePool(PagedPool, AceSize);
+
+ Ace = AceList;
+
+ ((PSIMPLE_ACE)Ace)->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
+ ((PSIMPLE_ACE)Ace)->Header.AceSize = (USHORT)SidWithThreeSubAuthorities +
+ (USHORT)sizeof(ACE_HEADER) + (USHORT)sizeof( ACCESS_MASK );
+ ((PSIMPLE_ACE)Ace)->Header.AceFlags = OBJECT_INHERIT_ACE;
+ ((PSIMPLE_ACE)Ace)->Mask = DELETE;
+ RtlCopySid(SidWithThreeSubAuthorities,&((PSIMPLE_ACE)Ace)->Sid,DeadDomainSid);
+
+ (ULONG)Ace += ((PSIMPLE_ACE)Ace)->Header.AceSize;
+
+ ((PSIMPLE_ACE)Ace)->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
+ ((PSIMPLE_ACE)Ace)->Header.AceSize = (USHORT)SidWithFourSubAuthorities +
+ (USHORT)sizeof(ACE_HEADER) + (USHORT)sizeof( ACCESS_MASK );
+ ((PSIMPLE_ACE)Ace)->Header.AceFlags = OBJECT_INHERIT_ACE;
+ ((PSIMPLE_ACE)Ace)->Mask = DELETE;
+ RtlCopySid(SidWithFourSubAuthorities,&((PSIMPLE_ACE)Ace)->Sid,BobbySid);
+
+ (ULONG)Ace += ((PSIMPLE_ACE)Ace)->Header.AceSize;
+
+ ((PSIMPLE_ACE)Ace)->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
+ ((PSIMPLE_ACE)Ace)->Header.AceSize = (USHORT)SidWithFourSubAuthorities +
+ (USHORT)sizeof(ACE_HEADER) + (USHORT)sizeof( ACCESS_MASK );
+ ((PSIMPLE_ACE)Ace)->Header.AceFlags = OBJECT_INHERIT_ACE;
+ ((PSIMPLE_ACE)Ace)->Mask = DELETE;
+ RtlCopySid(SidWithFourSubAuthorities,&((PSIMPLE_ACE)Ace)->Sid,JerrySid);
+
+ (ULONG)Ace += ((PSIMPLE_ACE)Ace)->Header.AceSize;
+
+ ((PSIMPLE_ACE)Ace)->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
+ ((PSIMPLE_ACE)Ace)->Header.AceSize = (USHORT)SidWithFourSubAuthorities +
+ (USHORT)sizeof(ACE_HEADER) + (USHORT)sizeof( ACCESS_MASK );
+ ((PSIMPLE_ACE)Ace)->Header.AceFlags = OBJECT_INHERIT_ACE;
+ ((PSIMPLE_ACE)Ace)->Mask = DELETE;
+ RtlCopySid(SidWithFourSubAuthorities,&((PSIMPLE_ACE)Ace)->Sid,PhilSid);
+
+ (ULONG)Ace += ((PSIMPLE_ACE)Ace)->Header.AceSize;
+
+ ((PSIMPLE_ACE)Ace)->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
+ ((PSIMPLE_ACE)Ace)->Header.AceSize = (USHORT)SidWithFourSubAuthorities +
+ (USHORT)sizeof(ACE_HEADER) + (USHORT)sizeof( ACCESS_MASK );
+ ((PSIMPLE_ACE)Ace)->Header.AceFlags = OBJECT_INHERIT_ACE;
+ ((PSIMPLE_ACE)Ace)->Mask = DELETE;
+ RtlCopySid(SidWithFourSubAuthorities,&((PSIMPLE_ACE)Ace)->Sid,KreutzmanSid);
+
+ (ULONG)Ace += ((PSIMPLE_ACE)Ace)->Header.AceSize;
+
+ ((PSIMPLE_ACE)Ace)->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
+ ((PSIMPLE_ACE)Ace)->Header.AceSize = (USHORT)SidWithFourSubAuthorities +
+ (USHORT)sizeof(ACE_HEADER) + (USHORT)sizeof( ACCESS_MASK );
+ ((PSIMPLE_ACE)Ace)->Header.AceFlags = OBJECT_INHERIT_ACE;
+ ((PSIMPLE_ACE)Ace)->Mask = DELETE;
+ RtlCopySid(SidWithFourSubAuthorities,&((PSIMPLE_ACE)Ace)->Sid,BrentSid);
+
+ (ULONG)Ace += ((PSIMPLE_ACE)Ace)->Header.AceSize;
+
+ ((PSIMPLE_ACE)Ace)->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
+ ((PSIMPLE_ACE)Ace)->Header.AceSize = (USHORT)SidWithFourSubAuthorities +
+ (USHORT)sizeof(ACE_HEADER) + (USHORT)sizeof( ACCESS_MASK );
+ ((PSIMPLE_ACE)Ace)->Header.AceFlags = OBJECT_INHERIT_ACE;
+ ((PSIMPLE_ACE)Ace)->Mask = DELETE;
+ RtlCopySid(SidWithFourSubAuthorities,&((PSIMPLE_ACE)Ace)->Sid,MickySid);
+
+ //DbgBreakPoint();
+
+ RtlAddAce(TDacl, ACL_REVISION, 0, AceList, AceSize);
+
+ if (!NT_SUCCESS( Status = RtlQueryInformationAcl( TDacl, (PVOID)&AclSizeInfo,
+ sizeof(AclSizeInfo), AclSizeInformation ) )) {
+ DbgPrint("**** Failed **** \n");
+ DbgPrint("RtlQueryInformation returned %X during size query \n",Status);
+ return(FALSE);
+ }
+
+#if 0
+ RtlDumpAcl(TDacl);
+#endif
+
+ RtlGetAce( TDacl, 5, &Ace );
+
+ if ( !RtlEqualSid( &((PSIMPLE_ACE)Ace)->Sid, BrentSid) ) {
+ DbgPrint("\n **** Failed **** \n");
+ DbgPrint("RtlGetAce returned wrong Ace\n");
+ return(FALSE);
+ }
+
+ if (!NT_SUCCESS(RtlDeleteAce (TDacl, 5))) {
+ DbgPrint("\n **** Failed **** \n");
+ DbgPrint("RtlDeleteAce failed\n");
+ return(FALSE);
+ }
+
+#if 0
+ RtlDumpAcl(TDacl);
+#endif
+
+ return(TRUE);
+}
+
+
+BOOLEAN
+TestSeRtl()
+{
+
+ BOOLEAN Result = TRUE;
+
+ DbgPrint("Se: Global Variable Initialization... ");
+ if (TSeVariableInitialization()) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ Result = FALSE;
+ }
+
+ DbgPrint("Se: SID test... ");
+ if (TestSeSid()) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ Result = FALSE;
+ }
+
+ DbgPrint("Se: SECURITY_DESCRIPTOR test... ");
+ if (TestSeSecurityDescriptor()) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ Result = FALSE;
+ }
+
+ DbgPrint("Se: ACCESS_MASK test... ");
+ if (TestSeAccessMask()) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ Result = FALSE;
+ }
+
+ DbgPrint("Se: ACL test... ");
+ if (TestSeAclRtl()) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ Result = FALSE;
+ }
+
+ DbgPrint("\n");
+ DbgPrint("\n");
+ DbgPrint(" ********************\n");
+ DbgPrint(" ** **\n");
+
+ if (Result = TRUE) {
+ DbgPrint(" ** Test Succeeded **\n");
+ } else {
+ DbgPrint(" ** Test Failed **\n");
+ }
+
+ DbgPrint(" ** **\n");
+ DbgPrint(" ********************\n");
+ DbgPrint("\n");
+ DbgPrint("\n");
+
+ return Result;
+}
diff --git a/private/ntos/se/cttoken.c b/private/ntos/se/cttoken.c
new file mode 100644
index 000000000..9f5d0bed0
--- /dev/null
+++ b/private/ntos/se/cttoken.c
@@ -0,0 +1,7505 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ cttoken.c
+
+Abstract:
+
+ Common token object test routines.
+
+ These routines are used in both kernel and user mode tests.
+
+ This test assumes the security runtime library routines are
+ functioning correctly.
+
+ NOTE: This test program allocates a lot of memory and frees
+ none of it ! ! !
+
+
+
+Author:
+
+ Jim Kelly (JimK) 27-June-1990
+
+Environment:
+
+ Test of token object.
+
+Revision History:
+
+--*/
+
+#include "tsecomm.c" // Mode dependent macros and routines.
+
+
+
+////////////////////////////////////////////////////////////////
+// //
+// Module wide variables //
+// //
+////////////////////////////////////////////////////////////////
+
+#define DEFAULT_DACL_LENGTH (1024L)
+#define GROUP_IDS_LENGTH (1024L)
+#define NEW_GROUP_STATE_LENGTH (1024L)
+#define PRIVILEGES_LENGTH (128L)
+#define TOO_BIG_ACL_SIZE (2048L)
+#define TOO_BIG_PRIMARY_GROUP_SIZE (39L)
+
+//
+// definitions related to TokenWithGroups
+// (we also substitute SYSTEM for NEANDERTHOL in some tests)
+//
+
+#define FLINTSTONE_INDEX (0L)
+#define CHILD_INDEX (1L)
+#define NEANDERTHOL_INDEX (2L)
+#define SYSTEM_INDEX (2L)
+#define WORLD_INDEX (3L)
+#define GROUP_COUNT (4L)
+
+
+//
+// Definitions related to TokenWithPrivileges
+//
+
+#define UNSOLICITED_INDEX (0L)
+#define SECURITY_INDEX (1L)
+#define ASSIGN_PRIMARY_INDEX (2L)
+#define PRIVILEGE_COUNT (3L)
+
+
+ NTSTATUS Status;
+
+ HANDLE SimpleToken;
+ HANDLE TokenWithGroups;
+ HANDLE TokenWithDefaultOwner;
+ HANDLE TokenWithPrivileges;
+ HANDLE TokenWithDefaultDacl;
+
+ HANDLE Token;
+ HANDLE ProcessToken;
+ HANDLE ImpersonationToken;
+ HANDLE AnonymousToken;
+
+ OBJECT_ATTRIBUTES PrimaryTokenAttributes;
+ PSECURITY_DESCRIPTOR PrimarySecurityDescriptor;
+ SECURITY_QUALITY_OF_SERVICE PrimarySecurityQos;
+
+ OBJECT_ATTRIBUTES ImpersonationTokenAttributes;
+ PSECURITY_DESCRIPTOR ImpersonationSecurityDescriptor;
+ SECURITY_QUALITY_OF_SERVICE ImpersonationSecurityQos;
+
+ OBJECT_ATTRIBUTES AnonymousTokenAttributes;
+ PSECURITY_DESCRIPTOR AnonymousSecurityDescriptor;
+ SECURITY_QUALITY_OF_SERVICE AnonymousSecurityQos;
+
+ ULONG DisabledGroupAttributes;
+ ULONG OptionalGroupAttributes;
+ ULONG NormalGroupAttributes;
+ ULONG OwnerGroupAttributes;
+
+ ULONG LengthAvailable;
+ ULONG CurrentLength;
+
+
+ TIME_FIELDS TempTimeFields = {3000, 1, 1, 1, 1, 1, 1, 1};
+ LARGE_INTEGER NoExpiration;
+
+ LUID BadAuthenticationId;
+ LUID SystemAuthenticationId = SYSTEM_LUID;
+ LUID OriginalAuthenticationId;
+
+ TOKEN_SOURCE TestSource = {"SE: TEST", 0};
+
+ PSID Owner;
+ PSID Group;
+ PACL Dacl;
+
+ PSID TempOwner;
+ PSID TempGroup;
+ PACL TempDacl;
+
+ UQUAD ThreadStack[256];
+ INITIAL_TEB InitialTeb;
+ NTSTATUS Status;
+ CLIENT_ID ThreadClientId;
+ CONTEXT ThreadContext;
+ HANDLE ThreadHandle;
+ OBJECT_ATTRIBUTES ThreadObja;
+
+
+
+
+
+
+////////////////////////////////////////////////////////////////
+// //
+// Private Macros //
+// //
+////////////////////////////////////////////////////////////////
+
+
+#define TestpPrintLuid(G) \
+ DbgPrint( "(0x%lx, 0x%hx, 0x%hx, 0x%hx, 0x%hx, 0x%hx, 0x%hx, 0x%hx, 0x%hx, 0x%hx, 0x%hx)", \
+ (G).Data1, (G).Data2, (G).Data3, \
+ (G).Data4[0], (G).Data4[1], (G).Data4[2], \
+ (G).Data4[3], (G).Data4[4], (G).Data4[5], \
+ (G).Data4[6], (G).Data4[7]); \
+
+
+
+
+////////////////////////////////////////////////////////////////
+// //
+// Initialization Routine //
+// //
+////////////////////////////////////////////////////////////////
+
+BOOLEAN
+TestTokenInitialize()
+{
+
+ NTSTATUS Status;
+ ULONG ReturnLength;
+ HANDLE ProcessToken;
+ TOKEN_STATISTICS ProcessTokenStatistics;
+
+
+ if (!TSeVariableInitialization()) {
+ DbgPrint("Se: Failed to initialize global test variables.\n");
+ return FALSE;
+ }
+
+
+ DisabledGroupAttributes = (SE_GROUP_ENABLED_BY_DEFAULT);
+
+ OptionalGroupAttributes = (SE_GROUP_ENABLED_BY_DEFAULT |
+ SE_GROUP_ENABLED
+ );
+ NormalGroupAttributes = (SE_GROUP_MANDATORY |
+ SE_GROUP_ENABLED_BY_DEFAULT |
+ SE_GROUP_ENABLED
+ );
+ OwnerGroupAttributes = (SE_GROUP_MANDATORY |
+ SE_GROUP_ENABLED_BY_DEFAULT |
+ SE_GROUP_ENABLED |
+ SE_GROUP_OWNER
+ );
+
+
+ PrimarySecurityDescriptor =
+ (PSECURITY_DESCRIPTOR)TstAllocatePool( PagedPool, 1024 );
+
+ Status = RtlCreateSecurityDescriptor (
+ PrimarySecurityDescriptor,
+ SECURITY_DESCRIPTOR_REVISION1
+ ); ASSERT(NT_SUCCESS(Status));
+ Status = RtlSetDaclSecurityDescriptor (
+ PrimarySecurityDescriptor,
+ TRUE, //DaclPresent,
+ NULL, //Dacl OPTIONAL, // No protection
+ FALSE //DaclDefaulted OPTIONAL
+ ); ASSERT(NT_SUCCESS(Status));
+
+
+ InitializeObjectAttributes(
+ &PrimaryTokenAttributes,
+ NULL,
+ OBJ_INHERIT,
+ NULL,
+ PrimarySecurityDescriptor
+ );
+
+
+ ImpersonationSecurityDescriptor =
+ (PSECURITY_DESCRIPTOR)TstAllocatePool( PagedPool, 1024 );
+
+ ImpersonationSecurityQos.Length = (ULONG)sizeof(SECURITY_QUALITY_OF_SERVICE);
+ ImpersonationSecurityQos.ImpersonationLevel = SecurityImpersonation;
+ ImpersonationSecurityQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ ImpersonationSecurityQos.EffectiveOnly = FALSE;
+
+ InitializeObjectAttributes(
+ &ImpersonationTokenAttributes,
+ NULL,
+ OBJ_INHERIT,
+ NULL,
+ NULL
+ );
+ ImpersonationTokenAttributes.SecurityQualityOfService =
+ &ImpersonationSecurityQos;
+
+
+ AnonymousSecurityDescriptor =
+ (PSECURITY_DESCRIPTOR)TstAllocatePool( PagedPool, 1024 );
+
+ AnonymousSecurityQos.Length = (ULONG)sizeof(SECURITY_QUALITY_OF_SERVICE);
+ AnonymousSecurityQos.ImpersonationLevel = SecurityAnonymous;
+ AnonymousSecurityQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ AnonymousSecurityQos.EffectiveOnly = FALSE;
+
+ InitializeObjectAttributes(
+ &AnonymousTokenAttributes,
+ NULL,
+ OBJ_INHERIT,
+ NULL,
+ NULL
+ );
+ AnonymousTokenAttributes.SecurityQualityOfService =
+ &AnonymousSecurityQos;
+
+
+ //
+ // Build an ACL for use.
+ //
+
+ Dacl = (PACL)TstAllocatePool( PagedPool, 256 );
+
+ Dacl->AclRevision=ACL_REVISION;
+ Dacl->Sbz1=0;
+ Dacl->Sbz2=0;
+ Dacl->AclSize=256;
+ Dacl->AceCount=0;
+
+
+ //
+ // Set up expiration times
+ //
+
+ TempTimeFields.Year = 3000;
+ TempTimeFields.Month = 1;
+ TempTimeFields.Day = 1;
+ TempTimeFields.Hour = 1;
+ TempTimeFields.Minute = 1;
+ TempTimeFields.Second = 1;
+ TempTimeFields.Milliseconds = 1;
+ TempTimeFields.Weekday = 1;
+
+ RtlTimeFieldsToTime( &TempTimeFields, &NoExpiration );
+
+ //
+ // Set up a bad authentication ID
+ //
+
+ BadAuthenticationId = FredLuid;
+
+
+ //
+ // Use a token source specific to security test
+ //
+
+ NtAllocateLocallyUniqueId( &(TestSource.SourceIdentifier) );
+
+ //
+ // Create a new thread for impersonation tests
+ //
+
+
+ //
+ // Initialize object attributes.
+ // Note that the name of the thread is NULL so that we
+ // can run multiple copies of the test at the same time
+ // without collisions.
+ //
+
+ InitializeObjectAttributes(&ThreadObja, NULL, 0, NULL, NULL);
+
+ //
+ // Initialize thread context and initial TEB.
+ //
+
+ RtlInitializeContext(NtCurrentProcess(),
+ &ThreadContext,
+ NULL,
+ (PVOID)TestTokenInitialize,
+ &ThreadStack[254]);
+
+ InitialTeb.StackBase = &ThreadStack[254];
+ InitialTeb.StackLimit = &ThreadStack[0];
+
+ //
+ // Create a thread in a suspended state.
+ //
+
+ Status = NtCreateThread(&ThreadHandle,
+ THREAD_ALL_ACCESS,
+ &ThreadObja,
+ NtCurrentProcess(),
+ &ThreadClientId,
+ &ThreadContext,
+ &InitialTeb,
+ TRUE);
+
+ ASSERT(NT_SUCCESS(Status));
+
+
+
+ //
+ // The following is sortof a horse-before-the-cart type of initialization.
+ // Now that the system is enforcing things like "you can only create a
+ // token with an AuthenticationId that the reference monitor has been told
+ // about, it is necessary to obtain some information out of our current
+ // token.
+ //
+
+ Status = NtOpenProcessToken(
+ NtCurrentProcess(),
+ TOKEN_ALL_ACCESS,
+ &ProcessToken
+ );
+ ASSERT( NT_SUCCESS(Status) );
+ Status = NtQueryInformationToken(
+ ProcessToken, // Handle
+ TokenStatistics, // TokenInformationClass
+ &ProcessTokenStatistics, // TokenInformation
+ sizeof(TOKEN_STATISTICS), // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+ ASSERT(NT_SUCCESS(Status));
+ OriginalAuthenticationId = ProcessTokenStatistics.AuthenticationId;
+
+ DbgPrint("Done.\n");
+
+ return TRUE;
+}
+
+
+
+////////////////////////////////////////////////////////////////
+// //
+// Test routines //
+// //
+////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////
+// //
+// Token Creation Test //
+// //
+////////////////////////////////////////////////////////////////
+
+BOOLEAN
+TestTokenCreate()
+{
+
+ BOOLEAN CompletionStatus = TRUE;
+
+ TOKEN_USER UserId;
+ TOKEN_PRIMARY_GROUP PrimaryGroup;
+ PTOKEN_GROUPS GroupIds;
+ PTOKEN_PRIVILEGES Privileges;
+ TOKEN_DEFAULT_DACL DefaultDacl;
+ TOKEN_DEFAULT_DACL NullDefaultDacl;
+ TOKEN_OWNER Owner;
+
+ DbgPrint("\n");
+
+ GroupIds = (PTOKEN_GROUPS)TstAllocatePool( PagedPool,
+ GROUP_IDS_LENGTH
+ );
+
+ Privileges = (PTOKEN_PRIVILEGES)TstAllocatePool( PagedPool,
+ PRIVILEGES_LENGTH
+ );
+
+ DefaultDacl.DefaultDacl = (PACL)TstAllocatePool( PagedPool,
+ DEFAULT_DACL_LENGTH
+ );
+
+
+ //
+ // Create the simplest token possible
+ // (no Groups, explicit Owner, or DefaultDacl)
+ //
+
+ DbgPrint("Se: Create Simple Token ... ");
+
+ UserId.User.Sid = PebblesSid;
+ UserId.User.Attributes = 0;
+ GroupIds->GroupCount = 0;
+ Privileges->PrivilegeCount = 0;
+ PrimaryGroup.PrimaryGroup = FlintstoneSid;
+
+
+ Status = NtCreateToken(
+ &Token, // Handle
+ (TOKEN_ALL_ACCESS), // DesiredAccess
+ &PrimaryTokenAttributes, // ObjectAttributes
+ TokenPrimary, // TokenType
+ &SystemAuthenticationId, // Authentication LUID
+ &NoExpiration, // Expiration Time
+ &UserId, // Owner ID
+ GroupIds, // Group IDs
+ Privileges, // Privileges
+ NULL, // Owner
+ &PrimaryGroup, // Primary Group
+ NULL, // Default Dacl
+ &TestSource // TokenSource
+ );
+
+ if (NT_SUCCESS(Status)) {
+ DbgPrint("Succeeded.\n");
+ Status = NtDuplicateObject(
+ NtCurrentProcess(), // SourceProcessHandle
+ Token, // SourceHandle
+ NtCurrentProcess(), // TargetProcessHandle
+ &SimpleToken, // TargetHandle
+ 0, // DesiredAccess (over-ridden by option)
+ 0, // HandleAttributes
+ DUPLICATE_SAME_ACCESS // Options
+ );
+ ASSERT(NT_SUCCESS(Status));
+ Status = NtClose(Token);
+ ASSERT(NT_SUCCESS(Status));
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(NT_SUCCESS(Status));
+
+
+
+ //
+ // Create a token with groups
+ //
+
+ DbgPrint("Se: Create Token With Groups ... ");
+
+ GroupIds->GroupCount = GROUP_COUNT;
+
+ GroupIds->Groups[0].Sid = FlintstoneSid;
+ GroupIds->Groups[1].Sid = ChildSid;
+ GroupIds->Groups[2].Sid = NeandertholSid;
+ GroupIds->Groups[3].Sid = WorldSid;
+
+ GroupIds->Groups[FLINTSTONE_INDEX].Attributes = OwnerGroupAttributes;
+ GroupIds->Groups[CHILD_INDEX].Attributes = OptionalGroupAttributes;
+ GroupIds->Groups[NEANDERTHOL_INDEX].Attributes = OptionalGroupAttributes;
+ GroupIds->Groups[WORLD_INDEX].Attributes = NormalGroupAttributes;
+
+
+ UserId.User.Sid = PebblesSid;
+ UserId.User.Attributes = 0;
+
+ Privileges->PrivilegeCount = 0;
+
+ PrimaryGroup.PrimaryGroup = FlintstoneSid;
+
+
+ Status = NtCreateToken(
+ &Token, // Handle
+ (TOKEN_ALL_ACCESS), // DesiredAccess
+ &PrimaryTokenAttributes, // ObjectAttributes
+ TokenPrimary, // TokenType
+ &OriginalAuthenticationId, // Authentication LUID
+ &NoExpiration, // Expiration Time
+ &UserId, // Owner ID
+ GroupIds, // Group IDs
+ Privileges, // Privileges
+ NULL, // Owner
+ &PrimaryGroup, // Primary Group
+ NULL, // Default Dacl
+ &TestSource // TokenSource
+ );
+
+ if (NT_SUCCESS(Status)) {
+ DbgPrint("Succeeded.\n");
+ Status = NtDuplicateObject(
+ NtCurrentProcess(), // SourceProcessHandle
+ Token, // SourceHandle
+ NtCurrentProcess(), // TargetProcessHandle
+ &TokenWithGroups, // TargetHandle
+ 0, // DesiredAccess (over-ridden by option)
+ 0, // HandleAttributes
+ DUPLICATE_SAME_ACCESS // Options
+ );
+ ASSERT(NT_SUCCESS(Status));
+ Status = NtClose(Token);
+ ASSERT(NT_SUCCESS(Status));
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(NT_SUCCESS(Status));
+
+
+
+
+ //
+ // Create a token with default owner
+ //
+
+ DbgPrint("Se: Create Token Default Owner ... ");
+
+ GroupIds->GroupCount = GROUP_COUNT;
+
+ GroupIds->Groups[FLINTSTONE_INDEX].Sid = FlintstoneSid;
+ GroupIds->Groups[CHILD_INDEX].Sid = ChildSid;
+ GroupIds->Groups[NEANDERTHOL_INDEX].Sid = NeandertholSid;
+ GroupIds->Groups[WORLD_INDEX].Sid = WorldSid;
+
+ GroupIds->Groups[FLINTSTONE_INDEX].Attributes = OwnerGroupAttributes;
+ GroupIds->Groups[CHILD_INDEX].Attributes = OptionalGroupAttributes;
+ GroupIds->Groups[NEANDERTHOL_INDEX].Attributes = OptionalGroupAttributes;
+ GroupIds->Groups[WORLD_INDEX].Attributes = NormalGroupAttributes;
+
+
+ UserId.User.Sid = PebblesSid;
+ UserId.User.Attributes = 0;
+
+ Owner.Owner = FlintstoneSid;
+
+ Privileges->PrivilegeCount = 0;
+
+ PrimaryGroup.PrimaryGroup = FlintstoneSid;
+
+
+ Status = NtCreateToken(
+ &Token, // Handle
+ (TOKEN_ALL_ACCESS), // DesiredAccess
+ &PrimaryTokenAttributes, // ObjectAttributes
+ TokenPrimary, // TokenType
+ &SystemAuthenticationId, // Authentication LUID
+ &NoExpiration, // Expiration Time
+ &UserId, // Owner ID
+ GroupIds, // Group IDs
+ Privileges, // Privileges
+ &Owner, // Owner
+ &PrimaryGroup, // Primary Group
+ NULL, // Default Dacl
+ &TestSource // TokenSource
+ );
+
+ if (NT_SUCCESS(Status)) {
+ DbgPrint("Succeeded.\n");
+ Status = NtDuplicateObject(
+ NtCurrentProcess(), // SourceProcessHandle
+ Token, // SourceHandle
+ NtCurrentProcess(), // TargetProcessHandle
+ &TokenWithDefaultOwner, // TargetHandle
+ 0, // DesiredAccess (over-ridden by option)
+ 0, // HandleAttributes
+ DUPLICATE_SAME_ACCESS // Options
+ );
+ ASSERT(NT_SUCCESS(Status));
+ Status = NtClose(Token);
+ ASSERT(NT_SUCCESS(Status));
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(NT_SUCCESS(Status));
+
+
+
+
+ //
+ // Create a token with default privileges
+ //
+
+ DbgPrint("Se: Create Token privileges ... ");
+
+ GroupIds->GroupCount = GROUP_COUNT;
+
+ GroupIds->Groups[FLINTSTONE_INDEX].Sid = FlintstoneSid;
+ GroupIds->Groups[CHILD_INDEX].Sid = ChildSid;
+ GroupIds->Groups[NEANDERTHOL_INDEX].Sid = NeandertholSid;
+ GroupIds->Groups[WORLD_INDEX].Sid = WorldSid;
+
+ GroupIds->Groups[FLINTSTONE_INDEX].Attributes = OwnerGroupAttributes;
+ GroupIds->Groups[CHILD_INDEX].Attributes = OptionalGroupAttributes;
+ GroupIds->Groups[NEANDERTHOL_INDEX].Attributes = OptionalGroupAttributes;
+ GroupIds->Groups[WORLD_INDEX].Attributes = NormalGroupAttributes;
+
+
+ UserId.User.Sid = PebblesSid;
+ UserId.User.Attributes = 0;
+
+ Owner.Owner = FlintstoneSid;
+
+ Privileges->PrivilegeCount = PRIVILEGE_COUNT;
+
+ Privileges->Privileges[UNSOLICITED_INDEX].Luid = UnsolicitedInputPrivilege;
+ Privileges->Privileges[SECURITY_INDEX].Luid = SecurityPrivilege;
+ Privileges->Privileges[ASSIGN_PRIMARY_INDEX].Luid = AssignPrimaryTokenPrivilege;
+ Privileges->Privileges[UNSOLICITED_INDEX].Attributes = 0;
+ Privileges->Privileges[SECURITY_INDEX].Attributes = 0;
+ Privileges->Privileges[ASSIGN_PRIMARY_INDEX].Attributes = SE_PRIVILEGE_ENABLED;
+
+ PrimaryGroup.PrimaryGroup = FlintstoneSid;
+
+
+ Status = NtCreateToken(
+ &Token, // Handle
+ (TOKEN_ALL_ACCESS), // DesiredAccess
+ &PrimaryTokenAttributes, // ObjectAttributes
+ TokenPrimary, // TokenType
+ &OriginalAuthenticationId, // Authentication LUID
+ &NoExpiration, // Expiration Time
+ &UserId, // Owner ID
+ GroupIds, // Group IDs
+ Privileges, // Privileges
+ &Owner, // Owner
+ &PrimaryGroup, // Primary Group
+ NULL, // Default Dacl
+ &TestSource // TokenSource
+ );
+
+ if (NT_SUCCESS(Status)) {
+ DbgPrint("Succeeded.\n");
+ Status = NtDuplicateObject(
+ NtCurrentProcess(), // SourceProcessHandle
+ Token, // SourceHandle
+ NtCurrentProcess(), // TargetProcessHandle
+ &TokenWithPrivileges, // TargetHandle
+ 0, // DesiredAccess (over-ridden by option)
+ 0, // HandleAttributes
+ DUPLICATE_SAME_ACCESS // Options
+ );
+ ASSERT(NT_SUCCESS(Status));
+ Status = NtClose(Token);
+ ASSERT(NT_SUCCESS(Status));
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(NT_SUCCESS(Status));
+
+
+
+
+ //
+ // Create a token with default DACL
+ //
+
+ DbgPrint("Se: Create Token With Default Dacl ... ");
+
+ GroupIds->GroupCount = GROUP_COUNT;
+
+ GroupIds->Groups[FLINTSTONE_INDEX].Sid = FlintstoneSid;
+ GroupIds->Groups[CHILD_INDEX].Sid = ChildSid;
+ GroupIds->Groups[NEANDERTHOL_INDEX].Sid = NeandertholSid;
+ GroupIds->Groups[WORLD_INDEX].Sid = WorldSid;
+
+ GroupIds->Groups[FLINTSTONE_INDEX].Attributes = OwnerGroupAttributes;
+ GroupIds->Groups[CHILD_INDEX].Attributes = OptionalGroupAttributes;
+ GroupIds->Groups[NEANDERTHOL_INDEX].Attributes = OptionalGroupAttributes;
+ GroupIds->Groups[WORLD_INDEX].Attributes = NormalGroupAttributes;
+
+ UserId.User.Sid = PebblesSid;
+ UserId.User.Attributes = 0;
+
+ Owner.Owner = FlintstoneSid;
+
+ Privileges->PrivilegeCount = PRIVILEGE_COUNT;
+
+ Privileges->Privileges[UNSOLICITED_INDEX].Luid = UnsolicitedInputPrivilege;
+ Privileges->Privileges[SECURITY_INDEX].Luid = SecurityPrivilege;
+ Privileges->Privileges[ASSIGN_PRIMARY_INDEX].Luid = AssignPrimaryTokenPrivilege;
+ Privileges->Privileges[UNSOLICITED_INDEX].Attributes = 0;
+ Privileges->Privileges[SECURITY_INDEX].Attributes = 0;
+ Privileges->Privileges[ASSIGN_PRIMARY_INDEX].Attributes = SE_PRIVILEGE_ENABLED;
+
+ PrimaryGroup.PrimaryGroup = FlintstoneSid;
+
+ Status = RtlCreateAcl( DefaultDacl.DefaultDacl, DEFAULT_DACL_LENGTH, ACL_REVISION);
+
+ ASSERT(NT_SUCCESS(Status) );
+
+ Status = NtCreateToken(
+ &Token, // Handle
+ (TOKEN_ALL_ACCESS), // DesiredAccess
+ &PrimaryTokenAttributes, // ObjectAttributes
+ TokenPrimary, // TokenType
+ &SystemAuthenticationId, // Authentication LUID
+ &NoExpiration, // Expiration Time
+ &UserId, // Owner ID
+ GroupIds, // Group IDs
+ Privileges, // Privileges
+ &Owner, // Owner
+ &PrimaryGroup, // Primary Group
+ &DefaultDacl, // Default Dacl
+ &TestSource // TokenSource
+ );
+
+ if (NT_SUCCESS(Status)) {
+ DbgPrint("Succeeded.\n");
+
+ //
+ // Save a copy of this for later use...
+ //
+
+ Status = NtDuplicateObject(
+ NtCurrentProcess(), // SourceProcessHandle
+ Token, // SourceHandle
+ NtCurrentProcess(), // TargetProcessHandle
+ &TokenWithDefaultDacl, // TargetHandle
+ 0, // DesiredAccess (over-ridden by option)
+ 0, // HandleAttributes
+ DUPLICATE_SAME_ACCESS // Options
+ );
+ ASSERT(NT_SUCCESS(Status));
+ Status = NtClose(Token);
+ ASSERT(NT_SUCCESS(Status));
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(NT_SUCCESS(Status));
+
+
+
+
+ //
+ // Create a token with a null default DACL
+ //
+
+ DbgPrint("Se: Create Token With a Null Default Dacl ... ");
+
+ GroupIds->GroupCount = GROUP_COUNT;
+
+ GroupIds->Groups[FLINTSTONE_INDEX].Sid = FlintstoneSid;
+ GroupIds->Groups[CHILD_INDEX].Sid = ChildSid;
+ GroupIds->Groups[NEANDERTHOL_INDEX].Sid = NeandertholSid;
+ GroupIds->Groups[WORLD_INDEX].Sid = WorldSid;
+
+ GroupIds->Groups[FLINTSTONE_INDEX].Attributes = OwnerGroupAttributes;
+ GroupIds->Groups[CHILD_INDEX].Attributes = OptionalGroupAttributes;
+ GroupIds->Groups[NEANDERTHOL_INDEX].Attributes = OptionalGroupAttributes;
+ GroupIds->Groups[WORLD_INDEX].Attributes = NormalGroupAttributes;
+
+ UserId.User.Sid = PebblesSid;
+ UserId.User.Attributes = 0;
+
+ Owner.Owner = FlintstoneSid;
+
+ Privileges->PrivilegeCount = PRIVILEGE_COUNT;
+
+ Privileges->Privileges[UNSOLICITED_INDEX].Luid = UnsolicitedInputPrivilege;
+ Privileges->Privileges[SECURITY_INDEX].Luid = SecurityPrivilege;
+ Privileges->Privileges[ASSIGN_PRIMARY_INDEX].Luid = AssignPrimaryTokenPrivilege;
+ Privileges->Privileges[UNSOLICITED_INDEX].Attributes = 0;
+ Privileges->Privileges[SECURITY_INDEX].Attributes = 0;
+ Privileges->Privileges[ASSIGN_PRIMARY_INDEX].Attributes = SE_PRIVILEGE_ENABLED;
+
+ PrimaryGroup.PrimaryGroup = FlintstoneSid;
+
+ NullDefaultDacl.DefaultDacl = NULL;
+
+
+ Status = NtCreateToken(
+ &Token, // Handle
+ (TOKEN_ALL_ACCESS), // DesiredAccess
+ &PrimaryTokenAttributes, // ObjectAttributes
+ TokenPrimary, // TokenType
+ &OriginalAuthenticationId, // Authentication LUID
+ &NoExpiration, // Expiration Time
+ &UserId, // Owner ID
+ GroupIds, // Group IDs
+ Privileges, // Privileges
+ &Owner, // Owner
+ &PrimaryGroup, // Primary Group
+ &NullDefaultDacl, // Default Dacl
+ &TestSource // TokenSource
+ );
+
+ if (NT_SUCCESS(Status)) {
+ DbgPrint("Succeeded.\n");
+ Status = NtClose(Token);
+ ASSERT(NT_SUCCESS(Status));
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(NT_SUCCESS(Status));
+
+
+
+
+ //
+ // Create an impersonation token, Impersonation level = Impersonation
+ //
+
+ DbgPrint("Se: Create an impersonation token ... ");
+
+ GroupIds->GroupCount = GROUP_COUNT;
+
+ GroupIds->Groups[FLINTSTONE_INDEX].Sid = FlintstoneSid;
+ GroupIds->Groups[CHILD_INDEX].Sid = ChildSid;
+ GroupIds->Groups[NEANDERTHOL_INDEX].Sid = NeandertholSid;
+ GroupIds->Groups[WORLD_INDEX].Sid = WorldSid;
+
+ GroupIds->Groups[FLINTSTONE_INDEX].Attributes = OwnerGroupAttributes;
+ GroupIds->Groups[CHILD_INDEX].Attributes = OptionalGroupAttributes;
+ GroupIds->Groups[NEANDERTHOL_INDEX].Attributes = OptionalGroupAttributes;
+ GroupIds->Groups[WORLD_INDEX].Attributes = NormalGroupAttributes;
+
+ UserId.User.Sid = PebblesSid;
+ UserId.User.Attributes = 0;
+
+ Owner.Owner = FlintstoneSid;
+
+ Privileges->PrivilegeCount = PRIVILEGE_COUNT;
+
+ Privileges->Privileges[UNSOLICITED_INDEX].Luid = UnsolicitedInputPrivilege;
+ Privileges->Privileges[SECURITY_INDEX].Luid = SecurityPrivilege;
+ Privileges->Privileges[ASSIGN_PRIMARY_INDEX].Luid = AssignPrimaryTokenPrivilege;
+ Privileges->Privileges[UNSOLICITED_INDEX].Attributes = 0;
+ Privileges->Privileges[SECURITY_INDEX].Attributes = 0;
+ Privileges->Privileges[ASSIGN_PRIMARY_INDEX].Attributes = SE_PRIVILEGE_ENABLED;
+
+ PrimaryGroup.PrimaryGroup = FlintstoneSid;
+
+ Status = RtlCreateAcl( DefaultDacl.DefaultDacl, DEFAULT_DACL_LENGTH, ACL_REVISION);
+
+ ASSERT(NT_SUCCESS(Status) );
+
+ Status = NtCreateToken(
+ &Token, // Handle
+ (TOKEN_ALL_ACCESS), // DesiredAccess
+ &ImpersonationTokenAttributes, // ObjectAttributes
+ TokenImpersonation, // TokenType
+ &SystemAuthenticationId, // Authentication LUID
+ &NoExpiration, // Expiration Time
+ &UserId, // Owner ID
+ GroupIds, // Group IDs
+ Privileges, // Privileges
+ &Owner, // Owner
+ &PrimaryGroup, // Primary Group
+ &DefaultDacl, // Default Dacl
+ &TestSource // TokenSource
+ );
+
+ if (NT_SUCCESS(Status)) {
+ DbgPrint("Succeeded.\n");
+ Status = NtDuplicateObject(
+ NtCurrentProcess(), // SourceProcessHandle
+ Token, // SourceHandle
+ NtCurrentProcess(), // TargetProcessHandle
+ &ImpersonationToken, // TargetHandle
+ 0, // DesiredAccess (over-ridden by option)
+ 0, // HandleAttributes
+ DUPLICATE_SAME_ACCESS // Options
+ );
+ ASSERT(NT_SUCCESS(Status));
+ Status = NtClose(Token);
+ ASSERT(NT_SUCCESS(Status));
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(NT_SUCCESS(Status));
+
+
+
+
+ //
+ // Create an impersonation token, Impersonation level = Anonymous
+ //
+
+ DbgPrint("Se: Create an anonymous token ... ");
+
+ GroupIds->GroupCount = GROUP_COUNT;
+
+ GroupIds->Groups[FLINTSTONE_INDEX].Sid = FlintstoneSid;
+ GroupIds->Groups[CHILD_INDEX].Sid = ChildSid;
+ GroupIds->Groups[NEANDERTHOL_INDEX].Sid = NeandertholSid;
+ GroupIds->Groups[WORLD_INDEX].Sid = WorldSid;
+
+ GroupIds->Groups[FLINTSTONE_INDEX].Attributes = OwnerGroupAttributes;
+ GroupIds->Groups[CHILD_INDEX].Attributes = OptionalGroupAttributes;
+ GroupIds->Groups[NEANDERTHOL_INDEX].Attributes = OptionalGroupAttributes;
+ GroupIds->Groups[WORLD_INDEX].Attributes = NormalGroupAttributes;
+
+ UserId.User.Sid = PebblesSid;
+ UserId.User.Attributes = 0;
+
+ Owner.Owner = FlintstoneSid;
+
+ Privileges->PrivilegeCount = PRIVILEGE_COUNT;
+
+ Privileges->Privileges[UNSOLICITED_INDEX].Luid = UnsolicitedInputPrivilege;
+ Privileges->Privileges[SECURITY_INDEX].Luid = SecurityPrivilege;
+ Privileges->Privileges[ASSIGN_PRIMARY_INDEX].Luid = AssignPrimaryTokenPrivilege;
+ Privileges->Privileges[UNSOLICITED_INDEX].Attributes = 0;
+ Privileges->Privileges[SECURITY_INDEX].Attributes = 0;
+ Privileges->Privileges[ASSIGN_PRIMARY_INDEX].Attributes = SE_PRIVILEGE_ENABLED;
+
+ PrimaryGroup.PrimaryGroup = FlintstoneSid;
+
+ Status = RtlCreateAcl( DefaultDacl.DefaultDacl, DEFAULT_DACL_LENGTH, ACL_REVISION);
+
+ ASSERT(NT_SUCCESS(Status) );
+
+ Status = NtCreateToken(
+ &Token, // Handle
+ (TOKEN_ALL_ACCESS), // DesiredAccess
+ &AnonymousTokenAttributes, // ObjectAttributes
+ TokenImpersonation, // TokenType
+ &OriginalAuthenticationId, // Authentication LUID
+ &NoExpiration, // Expiration Time
+ &UserId, // Owner ID
+ GroupIds, // Group IDs
+ Privileges, // Privileges
+ &Owner, // Owner
+ &PrimaryGroup, // Primary Group
+ &DefaultDacl, // Default Dacl
+ &TestSource // TokenSource
+ );
+
+ if (NT_SUCCESS(Status)) {
+ DbgPrint("Succeeded.\n");
+ Status = NtDuplicateObject(
+ NtCurrentProcess(), // SourceProcessHandle
+ Token, // SourceHandle
+ NtCurrentProcess(), // TargetProcessHandle
+ &AnonymousToken, // TargetHandle
+ 0, // DesiredAccess (over-ridden by option)
+ 0, // HandleAttributes
+ DUPLICATE_SAME_ACCESS // Options
+ );
+ ASSERT(NT_SUCCESS(Status));
+ Status = NtClose(Token);
+ ASSERT(NT_SUCCESS(Status));
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(NT_SUCCESS(Status));
+
+
+
+ //
+ // Create the simplest token possible
+ // (no Groups, explicit Owner, or DefaultDacl)
+ //
+
+ DbgPrint("Se: Create Token With Bad Authentication Id ... ");
+
+ UserId.User.Sid = PebblesSid;
+ UserId.User.Attributes = 0;
+ GroupIds->GroupCount = 0;
+ Privileges->PrivilegeCount = 0;
+ PrimaryGroup.PrimaryGroup = FlintstoneSid;
+
+
+ Status = NtCreateToken(
+ &Token, // Handle
+ (TOKEN_ALL_ACCESS), // DesiredAccess
+ &PrimaryTokenAttributes, // ObjectAttributes
+ TokenPrimary, // TokenType
+ &BadAuthenticationId, // Authentication LUID
+ &NoExpiration, // Expiration Time
+ &UserId, // Owner ID
+ GroupIds, // Group IDs
+ Privileges, // Privileges
+ NULL, // Owner
+ &PrimaryGroup, // Primary Group
+ NULL, // Default Dacl
+ &TestSource // TokenSource
+ );
+
+ if (Status == STATUS_NO_SUCH_LOGON_SESSION) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Status should be: 0x%lx \n", STATUS_NO_SUCH_LOGON_SESSION);
+ CompletionStatus = FALSE;
+ }
+
+
+
+
+
+
+ //
+ // All done with this test
+ //
+
+ return CompletionStatus;
+}
+
+////////////////////////////////////////////////////////////////
+// //
+// Open Primary Token Test //
+// //
+////////////////////////////////////////////////////////////////
+
+BOOLEAN
+TestTokenOpenPrimary()
+{
+ NTSTATUS Status;
+ BOOLEAN CompletionStatus = TRUE;
+
+ HANDLE ProcessToken;
+ HANDLE SubProcessToken;
+ HANDLE SubProcess;
+
+ TOKEN_STATISTICS ProcessTokenStatistics;
+ TOKEN_STATISTICS SubProcessTokenStatistics;
+
+ ULONG ReturnLength;
+
+ DbgPrint("\n");
+
+ //
+ // Open the current process's token
+ //
+
+ DbgPrint("Se: Open own process's token ... ");
+
+ Status = NtOpenProcessToken(
+ NtCurrentProcess(),
+ TOKEN_ALL_ACCESS,
+ &ProcessToken
+ );
+ if (NT_SUCCESS(Status)) {
+ Status = NtQueryInformationToken(
+ ProcessToken, // Handle
+ TokenStatistics, // TokenInformationClass
+ &ProcessTokenStatistics, // TokenInformation
+ sizeof(TOKEN_STATISTICS), // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+ ASSERT(NT_SUCCESS(Status));
+ if ( ProcessTokenStatistics.TokenType == TokenPrimary) {
+ if ( RtlEqualLuid( &ProcessTokenStatistics.AuthenticationId,
+ &OriginalAuthenticationId ) ) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Unexpected authentication ID value.\n");
+ DbgPrint("Authentication ID is: ");
+ TestpPrintLuid(ProcessTokenStatistics.AuthenticationId);
+ DbgPrint("\n");
+ CompletionStatus = FALSE;
+ }
+
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Token type not TokenPrimary.\n");
+ DbgPrint("Returned token type is: 0x%lx \n",
+ ProcessTokenStatistics.TokenType);
+ DbgPrint("Authentication ID is: ");
+ TestpPrintLuid(ProcessTokenStatistics.AuthenticationId);
+ DbgPrint("\n");
+ CompletionStatus = FALSE;
+ }
+ Status = NtClose(ProcessToken);
+ ASSERT(NT_SUCCESS(Status));
+
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ CompletionStatus = FALSE;
+ }
+
+
+
+
+
+
+ //
+ // Open another process's token
+ //
+
+ DbgPrint("Se: Open another process's token ... ");
+
+ Status = NtCreateProcess(
+ &SubProcess,
+ (GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | DELETE),
+ NULL,
+ NtCurrentProcess(), // ParentProcess
+ FALSE, // InheritObjectTable
+ NULL, // SectionHandle,
+ NULL, // DebugPort,
+ NULL // ExceptionPort
+ );
+
+ Status = NtOpenProcessToken(
+ SubProcess,
+ TOKEN_ALL_ACCESS,
+ &SubProcessToken
+ );
+ if (NT_SUCCESS(Status)) {
+ Status = NtQueryInformationToken(
+ SubProcessToken, // Handle
+ TokenStatistics, // TokenInformationClass
+ &SubProcessTokenStatistics, // TokenInformation
+ sizeof(TOKEN_STATISTICS), // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+ ASSERT(NT_SUCCESS(Status));
+ if ( SubProcessTokenStatistics.TokenType == TokenPrimary) {
+ if ( RtlEqualLuid( &SubProcessTokenStatistics.AuthenticationId,
+ &OriginalAuthenticationId ) ) {
+ if ( (ProcessTokenStatistics.TokenId.HighPart ==
+ SubProcessTokenStatistics.TokenId.HighPart) &&
+ (ProcessTokenStatistics.TokenId.LowPart ==
+ SubProcessTokenStatistics.TokenId.LowPart) ) {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Same token as parent process (token IDs match).\n");
+ DbgPrint("Authentication ID is: ");
+ TestpPrintLuid(SubProcessTokenStatistics.AuthenticationId);
+ DbgPrint("\n");
+ CompletionStatus = FALSE;
+
+ } else {
+ DbgPrint("Succeeded.\n");
+ }
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Unexpected authentication ID value.\n");
+ DbgPrint("Authentication ID is: ");
+ TestpPrintLuid(SubProcessTokenStatistics.AuthenticationId);
+ DbgPrint("\n");
+ CompletionStatus = FALSE;
+ }
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Token type not TokenPrimary.\n");
+ DbgPrint("Returned token type is: 0x%lx \n",
+ SubProcessTokenStatistics.TokenType);
+ DbgPrint("Authentication ID is: ");
+ TestpPrintLuid(SubProcessTokenStatistics.AuthenticationId);
+ DbgPrint("\n");
+ CompletionStatus = FALSE;
+ }
+ Status = NtClose(SubProcessToken);
+ ASSERT(NT_SUCCESS(Status));
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ CompletionStatus = FALSE;
+ }
+
+
+ return CompletionStatus;
+}
+
+////////////////////////////////////////////////////////////////
+// //
+// Query Token Test //
+// //
+////////////////////////////////////////////////////////////////
+
+BOOLEAN
+TestTokenQuery()
+{
+ BOOLEAN CompletionStatus = TRUE;
+ ULONG ReturnLength;
+ BOOLEAN ValuesCompare;
+
+ PTOKEN_USER UserId;
+ PTOKEN_PRIMARY_GROUP PrimaryGroup;
+ PTOKEN_GROUPS GroupIds;
+ PTOKEN_PRIVILEGES Privileges;
+ PTOKEN_OWNER Owner;
+ PTOKEN_DEFAULT_DACL DefaultDacl;
+
+ SECURITY_IMPERSONATION_LEVEL QueriedImpersonationLevel;
+ TOKEN_SOURCE QueriedSource;
+ TOKEN_TYPE QueriedType;
+ TOKEN_STATISTICS QueriedStatistics;
+
+ DbgPrint("\n");
+
+
+
+#if 0
+
+ //
+ // Query invalid return buffer address
+ //
+
+ DbgPrint("Se: Query with invalid buffer address ... ");
+
+ UserId = (PTOKEN_USER)((PVOID)0x200L);
+ Status = NtQueryInformationToken(
+ SimpleToken, // Handle
+ TokenUser, // TokenInformationClass
+ UserId, // TokenInformation
+ 3000, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (Status == STATUS_ACCESS_VIOLATION) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(!NT_SUCCESS(Status));
+
+#endif //0
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Query User ID //
+// //
+////////////////////////////////////////////////////////////////////////
+
+ //
+ // Query User ID with zero length buffer
+ //
+
+ DbgPrint("Se: Query User ID with zero length buffer ... ");
+
+ Status = NtQueryInformationToken(
+ SimpleToken, // Handle
+ TokenUser, // TokenInformationClass
+ UserId, // TokenInformation
+ 0, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (Status == STATUS_BUFFER_TOO_SMALL) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(!NT_SUCCESS(Status));
+
+
+
+
+ UserId = (PTOKEN_USER)TstAllocatePool( PagedPool,
+ ReturnLength
+ );
+
+ //
+ // Query user SID
+ // (This relies upon the ReturnLength returned from previous call)
+ //
+
+ DbgPrint("Se: Query token user ... ");
+
+ Status = NtQueryInformationToken(
+ SimpleToken, // Handle
+ TokenUser, // TokenInformationClass
+ UserId, // TokenInformation
+ ReturnLength, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Check returned value
+ //
+
+ if (RtlEqualSid((UserId->User.Sid), PebblesSid) ) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Unexpected value returned by query.\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(NT_SUCCESS(Status));
+
+
+ //
+ // Query with too little buffer
+ // (This relies upon the ReturnLength returned from previous call)
+ //
+
+ DbgPrint("Se: Query user with too small buffer ... ");
+
+ Status = NtQueryInformationToken(
+ SimpleToken, // Handle
+ TokenUser, // TokenInformationClass
+ UserId, // TokenInformation
+ ReturnLength-1, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (Status == STATUS_BUFFER_TOO_SMALL) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(!NT_SUCCESS(Status));
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Query Primary Group //
+// //
+////////////////////////////////////////////////////////////////////////
+
+ //
+ // Query primary group with zero length buffer
+ //
+
+ DbgPrint("Se: Query primary group with zero length buffer ... ");
+
+ Status = NtQueryInformationToken(
+ SimpleToken, // Handle
+ TokenPrimaryGroup, // TokenInformationClass
+ PrimaryGroup, // TokenInformation
+ 0, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (Status == STATUS_BUFFER_TOO_SMALL) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(!NT_SUCCESS(Status));
+
+
+
+
+ PrimaryGroup = (PTOKEN_PRIMARY_GROUP)TstAllocatePool( PagedPool,
+ ReturnLength
+ );
+
+ //
+ // Query primary group SID
+ // (This relies upon the ReturnLength returned from previous call)
+ //
+
+ DbgPrint("Se: Query primary group ... ");
+
+ Status = NtQueryInformationToken(
+ SimpleToken, // Handle
+ TokenPrimaryGroup, // TokenInformationClass
+ PrimaryGroup, // TokenInformation
+ ReturnLength, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Check returned value
+ //
+
+ if (RtlEqualSid( PrimaryGroup->PrimaryGroup, FlintstoneSid) ) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Unexpected value returned by query.\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(NT_SUCCESS(Status));
+
+
+ //
+ // Query with too little buffer
+ // (This relies upon the ReturnLength returned from previous call)
+ //
+
+ DbgPrint("Se: Query primary group with too small buffer ... ");
+
+ Status = NtQueryInformationToken(
+ SimpleToken, // Handle
+ TokenPrimaryGroup, // TokenInformationClass
+ PrimaryGroup, // TokenInformation
+ ReturnLength-1, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (Status == STATUS_BUFFER_TOO_SMALL) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(!NT_SUCCESS(Status));
+
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Query Groups //
+// //
+////////////////////////////////////////////////////////////////////////
+
+ //
+ // Query groups with zero length buffer
+ //
+
+ DbgPrint("Se: Query groups with zero length buffer ... ");
+
+ Status = NtQueryInformationToken(
+ TokenWithGroups, // Handle
+ TokenGroups, // TokenInformationClass
+ GroupIds, // TokenInformation
+ 0, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (Status == STATUS_BUFFER_TOO_SMALL) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(!NT_SUCCESS(Status));
+
+
+
+
+ GroupIds = (PTOKEN_GROUPS)TstAllocatePool( PagedPool,
+ ReturnLength
+ );
+
+ //
+ // Query Group SIDs
+ // (This relies upon the ReturnLength returned from previous call)
+ //
+
+ DbgPrint("Se: Query groups ... ");
+
+ Status = NtQueryInformationToken(
+ TokenWithGroups, // Handle
+ TokenGroups, // TokenInformationClass
+ GroupIds, // TokenInformation
+ ReturnLength, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Check returned value
+ // Group count = 4
+ // SID 0 = Flintstone
+ // SID 1 = ChildSid
+ // SID 2 = NeandertholSid
+ // SID 3 = WorldSid
+ //
+
+ ValuesCompare = TRUE;
+
+ if (GroupIds->GroupCount != GROUP_COUNT) {
+ ValuesCompare = FALSE;
+ }
+
+ if ( (!RtlEqualSid((GroupIds->Groups[FLINTSTONE_INDEX].Sid),
+ FlintstoneSid)) ||
+ (GroupIds->Groups[FLINTSTONE_INDEX].Attributes !=
+ OwnerGroupAttributes) ) {
+ ValuesCompare = FALSE;
+ }
+
+ if ( (!RtlEqualSid((GroupIds->Groups[CHILD_INDEX].Sid), ChildSid)) ||
+ (GroupIds->Groups[CHILD_INDEX].Attributes !=
+ OptionalGroupAttributes) ) {
+ ValuesCompare = FALSE;
+ }
+
+ if ( (!RtlEqualSid((GroupIds->Groups[NEANDERTHOL_INDEX].Sid),
+ NeandertholSid)) ||
+ (GroupIds->Groups[NEANDERTHOL_INDEX].Attributes !=
+ OptionalGroupAttributes) ) {
+ ValuesCompare = FALSE;
+ }
+
+ if ( (!RtlEqualSid((GroupIds->Groups[WORLD_INDEX].Sid), WorldSid)) ||
+ (GroupIds->Groups[WORLD_INDEX].Attributes != NormalGroupAttributes) ) {
+ ValuesCompare = FALSE;
+ }
+
+
+ if ( ValuesCompare ) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Unexpected value returned by query.\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ DbgPrint("Returned group count is: 0x%lx \n", GroupIds->GroupCount);
+ CompletionStatus = FALSE;
+ }
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(NT_SUCCESS(Status));
+
+
+ //
+ // Query with too little buffer
+ // (This relies upon the ReturnLength returned from previous call)
+ //
+
+ DbgPrint("Se: Query groups with too small buffer ... ");
+
+ Status = NtQueryInformationToken(
+ TokenWithGroups, // Handle
+ TokenGroups, // TokenInformationClass
+ GroupIds, // TokenInformation
+ ReturnLength-1, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (Status == STATUS_BUFFER_TOO_SMALL) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(!NT_SUCCESS(Status));
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Query Privileges //
+// //
+////////////////////////////////////////////////////////////////////////
+
+
+ //
+ // Query groups with zero length buffer
+ //
+
+ DbgPrint("Se: Query privileges with zero length buffer ... ");
+
+ Status = NtQueryInformationToken(
+ TokenWithPrivileges, // Handle
+ TokenPrivileges, // TokenInformationClass
+ NULL, // TokenInformation
+ 0, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (Status == STATUS_BUFFER_TOO_SMALL) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(!NT_SUCCESS(Status));
+
+
+
+
+ Privileges = (PTOKEN_PRIVILEGES)TstAllocatePool( PagedPool,
+ ReturnLength
+ );
+
+ //
+ // Query privileges
+ // (This relies upon the ReturnLength returned from previous call)
+ //
+
+ DbgPrint("Se: Query privileges ... ");
+
+ Status = NtQueryInformationToken(
+ TokenWithPrivileges, // Handle
+ TokenPrivileges, // TokenInformationClass
+ Privileges, // TokenInformation
+ ReturnLength, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Check returned value
+ // Privilege count = PRIVILEGE_COUNT
+ // Privilege UNSOLICITED_INDEX = UnsolicitedInputPrivilege
+ // Privilege SECURITY_INDEX = SecurityPrivilege
+ // Privilege ASSIGN_PRIMARY_INDEX = AssignPrimaryPrivilege
+ //
+
+ ValuesCompare = TRUE;
+
+ if (Privileges->PrivilegeCount != PRIVILEGE_COUNT) {
+ ValuesCompare = FALSE;
+ }
+
+ if ( !(Privileges->Privileges[UNSOLICITED_INDEX].Luid.QuadPart ==
+ UnsolicitedInputPrivilege.QuadPart) ||
+ (Privileges->Privileges[UNSOLICITED_INDEX].Attributes != 0) ) {
+ ValuesCompare = FALSE;
+ }
+
+ if ( !(Privileges->Privileges[SECURITY_INDEX].Luid.QuadPart ==
+ SecurityPrivilege.QuadPart) ||
+ (Privileges->Privileges[SECURITY_INDEX].Attributes != 0) ) {
+ ValuesCompare = FALSE;
+ }
+
+ if ( !(Privileges->Privileges[ASSIGN_PRIMARY_INDEX].Luid.QuadPart ==
+ AssignPrimaryTokenPrivilege.QuadPart) ||
+ (Privileges->Privileges[ASSIGN_PRIMARY_INDEX].Attributes != SE_PRIVILEGE_ENABLED) ) {
+ ValuesCompare = FALSE;
+ }
+
+
+ if ( ValuesCompare ) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Unexpected value returned by query.\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(NT_SUCCESS(Status));
+
+
+ //
+ // Query with too little buffer
+ // (This relies upon the ReturnLength returned from previous call)
+ //
+
+ DbgPrint("Se: Query privileges with too small buffer ... ");
+
+ Status = NtQueryInformationToken(
+ TokenWithPrivileges, // Handle
+ TokenPrivileges, // TokenInformationClass
+ Privileges, // TokenInformation
+ ReturnLength-1, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (Status == STATUS_BUFFER_TOO_SMALL) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(!NT_SUCCESS(Status));
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Query Owner //
+// //
+////////////////////////////////////////////////////////////////////////
+
+ //
+ // Query Owner of simple token with zero length buffer
+ //
+
+ DbgPrint("Se: Query Owner of simple token with zero length buffer... ");
+
+ Status = NtQueryInformationToken(
+ SimpleToken, // Handle
+ TokenOwner, // TokenInformationClass
+ Owner, // TokenInformation
+ 0, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (Status == STATUS_BUFFER_TOO_SMALL) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(!NT_SUCCESS(Status));
+
+
+
+ Owner = (PTOKEN_OWNER)TstAllocatePool( PagedPool,
+ ReturnLength
+ );
+
+ //
+ // Query Owner SID
+ // (This relies upon the ReturnLength returned from previous call)
+ //
+
+ DbgPrint("Se: Query owner of simple token ... ");
+
+ Status = NtQueryInformationToken(
+ SimpleToken, // Handle
+ TokenOwner, // TokenInformationClass
+ Owner, // TokenInformation
+ ReturnLength, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Check returned value
+ //
+
+ if (RtlEqualSid((Owner->Owner), PebblesSid) ) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Unexpected value returned by query.\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(NT_SUCCESS(Status));
+
+
+ //
+ // Query owner of simple token with too little buffer
+ // (This relies upon the ReturnLength returned from previous call)
+ //
+
+ DbgPrint("Se: Query owner of simple token with too small buffer ... ");
+
+ Status = NtQueryInformationToken(
+ SimpleToken, // Handle
+ TokenOwner, // TokenInformationClass
+ Owner, // TokenInformation
+ ReturnLength-1, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (Status == STATUS_BUFFER_TOO_SMALL) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(!NT_SUCCESS(Status));
+
+
+ //
+ // Query default owner of token with zero length buffer
+ //
+
+ DbgPrint("Se: Query Default Owner of token with zero length buffer...");
+
+ Status = NtQueryInformationToken(
+ TokenWithDefaultOwner, // Handle
+ TokenOwner, // TokenInformationClass
+ Owner, // TokenInformation
+ 0, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (Status == STATUS_BUFFER_TOO_SMALL) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(!NT_SUCCESS(Status));
+
+
+
+
+ Owner = (PTOKEN_OWNER)TstAllocatePool( PagedPool,
+ ReturnLength
+ );
+
+ //
+ // Query default owner of token
+ // (This relies upon the ReturnLength returned from previous call)
+ //
+
+ DbgPrint("Se: Query default owner of token ... ");
+
+ Status = NtQueryInformationToken(
+ TokenWithDefaultOwner, // Handle
+ TokenOwner, // TokenInformationClass
+ Owner, // TokenInformation
+ ReturnLength, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Check returned value
+ //
+
+ if (RtlEqualSid((Owner->Owner), FlintstoneSid) ) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Unexpected value returned by query.\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(NT_SUCCESS(Status));
+
+
+ //
+ // Query default owner of token with too little buffer
+ // (This relies upon the ReturnLength returned from previous call)
+ //
+
+ DbgPrint("Se: Query default owner of token with too small buffer ... ");
+
+ Status = NtQueryInformationToken(
+ TokenWithDefaultOwner, // Handle
+ TokenOwner, // TokenInformationClass
+ Owner, // TokenInformation
+ ReturnLength-1, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (Status == STATUS_BUFFER_TOO_SMALL) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(!NT_SUCCESS(Status));
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Query Default Dacl //
+// //
+////////////////////////////////////////////////////////////////////////
+
+ //
+ // Query default dacl with zero length buffer
+ //
+
+ DbgPrint("Se: Query default DACL with zero length buffer ... ");
+
+ Status = NtQueryInformationToken(
+ TokenWithDefaultDacl, // Handle
+ TokenDefaultDacl, // TokenInformationClass
+ DefaultDacl, // TokenInformation
+ 0, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (Status == STATUS_BUFFER_TOO_SMALL) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(!NT_SUCCESS(Status));
+
+
+
+
+ DefaultDacl = (PTOKEN_DEFAULT_DACL)TstAllocatePool( PagedPool,
+ ReturnLength
+ );
+
+ //
+ // Query default dacl
+ // (This relies upon the ReturnLength returned from previous call)
+ //
+
+ DbgPrint("Se: Query default dacl ... ");
+
+ Status = NtQueryInformationToken(
+ TokenWithDefaultDacl, // Handle
+ TokenDefaultDacl, // TokenInformationClass
+ DefaultDacl, // TokenInformation
+ ReturnLength, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Check returned value
+ //
+
+ if (RtlValidAcl(DefaultDacl->DefaultDacl)) {
+
+ if (DefaultDacl->DefaultDacl->AceCount == 0) {
+
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Unexpected value returned by query.\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Unexpected value returned by query.\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(NT_SUCCESS(Status));
+
+
+ //
+ // Query with too little buffer
+ // (This relies upon the ReturnLength returned from previous call)
+ //
+
+ DbgPrint("Se: Query default Dacl with too small buffer ... ");
+
+ Status = NtQueryInformationToken(
+ TokenWithDefaultDacl, // Handle
+ TokenDefaultDacl, // TokenInformationClass
+ DefaultDacl, // TokenInformation
+ ReturnLength-1, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (Status == STATUS_BUFFER_TOO_SMALL) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(!NT_SUCCESS(Status));
+
+
+ //
+ // Query token with no default dacl
+ //
+
+ DbgPrint("Se: Query default dacl from token with none ... ");
+
+ Status = NtQueryInformationToken(
+ SimpleToken, // Handle
+ TokenDefaultDacl, // TokenInformationClass
+ DefaultDacl, // TokenInformation
+ sizeof(TOKEN_DEFAULT_DACL), // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (NT_SUCCESS(Status)) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(NT_SUCCESS(Status));
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Query Token Source //
+// //
+////////////////////////////////////////////////////////////////////////
+
+ //
+ // Query Token Source with zero length buffer
+ //
+
+ DbgPrint("Se: Query Token Source with zero length buffer ... ");
+
+ Status = NtQueryInformationToken(
+ TokenWithPrivileges, // Handle
+ TokenSource, // TokenInformationClass
+ &QueriedSource, // TokenInformation
+ 0, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (Status == STATUS_BUFFER_TOO_SMALL) {
+ if (ReturnLength == sizeof(TOKEN_SOURCE)) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ DbgPrint("TOKEN_SOURCE data size is 0x%lx \n", sizeof(TOKEN_SOURCE));
+ CompletionStatus = FALSE;
+ }
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(!NT_SUCCESS(Status));
+
+
+
+ //
+ // Query token source
+ //
+
+ DbgPrint("Se: Query token source ... ");
+
+ Status = NtQueryInformationToken(
+ TokenWithPrivileges, // Handle
+ TokenSource, // TokenInformationClass
+ &QueriedSource, // TokenInformation
+ sizeof(TOKEN_SOURCE), // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Check returned value against TestSource
+ //
+
+ ValuesCompare = TRUE;
+
+ if ( (QueriedSource.SourceName[0] != TestSource.SourceName[0]) ||
+ (QueriedSource.SourceName[1] != TestSource.SourceName[1]) ||
+ (QueriedSource.SourceName[2] != TestSource.SourceName[2]) ||
+ (QueriedSource.SourceName[3] != TestSource.SourceName[3]) ||
+ (QueriedSource.SourceName[4] != TestSource.SourceName[4]) ||
+ (QueriedSource.SourceName[5] != TestSource.SourceName[5]) ||
+ (QueriedSource.SourceName[6] != TestSource.SourceName[6]) ||
+ (QueriedSource.SourceName[7] != TestSource.SourceName[7]) ) {
+
+ ValuesCompare = FALSE;
+
+ }
+
+ if ( !(QueriedSource.SourceIdentifier.QuadPart ==
+ TestSource.SourceIdentifier.QuadPart) ) {
+
+ ValuesCompare = FALSE;
+
+ }
+
+
+ if ( ValuesCompare ) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Unexpected value returned by query.\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(NT_SUCCESS(Status));
+
+
+ //
+ // Query with too little buffer
+ // (This relies upon the ReturnLength returned from previous call)
+ //
+
+ DbgPrint("Se: Query token source with too small buffer ... ");
+
+ Status = NtQueryInformationToken(
+ TokenWithPrivileges, // Handle
+ TokenSource, // TokenInformationClass
+ &QueriedSource, // TokenInformation
+ ReturnLength - 1, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (Status == STATUS_BUFFER_TOO_SMALL) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(!NT_SUCCESS(Status));
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Query Token Type //
+// //
+////////////////////////////////////////////////////////////////////////
+
+ //
+ // Query Token type with zero length buffer
+ //
+
+ DbgPrint("Se: Query Token type with zero length buffer ... ");
+
+ Status = NtQueryInformationToken(
+ TokenWithPrivileges, // Handle
+ TokenType, // TokenInformationClass
+ &QueriedType, // TokenInformation
+ 0, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (Status == STATUS_BUFFER_TOO_SMALL) {
+ if (ReturnLength == sizeof(TOKEN_TYPE)) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ DbgPrint("TOKEN_TYPE data size is 0x%lx \n", sizeof(TOKEN_TYPE));
+ CompletionStatus = FALSE;
+ }
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(!NT_SUCCESS(Status));
+
+
+
+
+ //
+ // Query token type
+ //
+
+ DbgPrint("Se: Query token type ... ");
+
+ Status = NtQueryInformationToken(
+ TokenWithPrivileges, // Handle
+ TokenType, // TokenInformationClass
+ &QueriedType, // TokenInformation
+ sizeof(TOKEN_TYPE), // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Check returned value against TestSource
+ //
+
+
+ if ( QueriedType == TokenPrimary ) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Unexpected value returned by query.\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ DbgPrint("Returned token type is: 0x%lx \n", QueriedType);
+ CompletionStatus = FALSE;
+ }
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(NT_SUCCESS(Status));
+
+
+ //
+ // Query with too little buffer
+ // (This relies upon the ReturnLength returned from previous call)
+ //
+
+ DbgPrint("Se: Query token type with too small buffer ... ");
+
+ Status = NtQueryInformationToken(
+ TokenWithPrivileges, // Handle
+ TokenType, // TokenInformationClass
+ &QueriedType, // TokenInformation
+ ReturnLength - 1, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (Status == STATUS_BUFFER_TOO_SMALL) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(!NT_SUCCESS(Status));
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Query Impersonation Level //
+// //
+////////////////////////////////////////////////////////////////////////
+
+ //
+ // Query Impersonation Level of primary token
+ //
+
+ DbgPrint("Se: Query Impersonation level of primary token ... ");
+
+ Status = NtQueryInformationToken(
+ TokenWithPrivileges, // Handle
+ TokenImpersonationLevel, // TokenInformationClass
+ &QueriedImpersonationLevel, // TokenInformation
+ sizeof(SECURITY_IMPERSONATION_LEVEL), // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (Status == STATUS_INVALID_INFO_CLASS) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(Status == STATUS_INVALID_INFO_CLASS);
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Query Token Statistics //
+// //
+////////////////////////////////////////////////////////////////////////
+
+ //
+ // Query Token statistics with zero length buffer
+ //
+
+ DbgPrint("Se: Query Token statistics with zero length buffer ... ");
+
+ Status = NtQueryInformationToken(
+ TokenWithPrivileges, // Handle
+ TokenStatistics, // TokenInformationClass
+ &QueriedStatistics, // TokenInformation
+ 0, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (Status == STATUS_BUFFER_TOO_SMALL) {
+ if (ReturnLength == sizeof(TOKEN_STATISTICS)) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ DbgPrint("TOKEN_STATISTICS data size is 0x%lx \n", sizeof(TOKEN_STATISTICS));
+ CompletionStatus = FALSE;
+ }
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(!NT_SUCCESS(Status));
+
+
+
+
+ //
+ // Query token statistics
+ //
+
+ DbgPrint("Se: Query token statistics ... ");
+
+ Status = NtQueryInformationToken(
+ TokenWithPrivileges, // Handle
+ TokenStatistics, // TokenInformationClass
+ &QueriedStatistics, // TokenInformation
+ sizeof(TOKEN_STATISTICS), // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Check returned value against TestSource
+ //
+
+ if ( ( QueriedStatistics.TokenType == TokenPrimary) &&
+ ( QueriedStatistics.GroupCount == 4 ) &&
+ ( QueriedStatistics.PrivilegeCount == PRIVILEGE_COUNT) ) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Unexpected value returned by query.\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ DbgPrint("Returned token type is: 0x%lx \n", QueriedStatistics.TokenType);
+ DbgPrint("Returned group count is: 0x%lx \n", QueriedStatistics.GroupCount);
+ DbgPrint("Returned privilege count is: 0x%lx \n", QueriedStatistics.PrivilegeCount);
+ CompletionStatus = FALSE;
+ }
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(NT_SUCCESS(Status));
+
+
+ //
+ // Query with too little buffer
+ // (This relies upon the ReturnLength returned from previous call)
+ //
+
+ DbgPrint("Se: Query token statistics with too small buffer ... ");
+
+ Status = NtQueryInformationToken(
+ TokenWithPrivileges, // Handle
+ TokenStatistics, // TokenInformationClass
+ &QueriedStatistics, // TokenInformation
+ ReturnLength - 1, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (Status == STATUS_BUFFER_TOO_SMALL) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(!NT_SUCCESS(Status));
+
+
+
+ return CompletionStatus;
+}
+
+////////////////////////////////////////////////////////////////
+// //
+// Set Token Test //
+// //
+////////////////////////////////////////////////////////////////
+
+BOOLEAN
+TestTokenSet()
+{
+ BOOLEAN CompletionStatus = TRUE;
+ ULONG InformationLength;
+ ULONG ReturnLength;
+
+ TOKEN_STATISTICS QueriedStatistics;
+
+ TOKEN_PRIMARY_GROUP AssignedPrimaryGroup;
+ PTOKEN_PRIMARY_GROUP QueriedPrimaryGroup;
+
+ TOKEN_OWNER AssignedOwner;
+ PTOKEN_OWNER QueriedOwner;
+
+ TOKEN_DEFAULT_DACL AssignedDefaultDacl;
+ PTOKEN_DEFAULT_DACL QueriedDefaultDacl;
+
+ PSID TooBigSid;
+
+ SID_IDENTIFIER_AUTHORITY BedrockAuthority = BEDROCK_AUTHORITY;
+
+ DbgPrint("\n");
+
+
+ //
+ // Set owner of a token to be an invalid group
+ //
+
+ DbgPrint("Se: Set default owner to be an invalid group ... ");
+
+ AssignedOwner.Owner = NeandertholSid;
+ InformationLength = (ULONG)sizeof(TOKEN_OWNER);
+
+ Status = NtSetInformationToken(
+ TokenWithGroups, // Handle
+ TokenOwner, // TokenInformationClass
+ &AssignedOwner, // TokenInformation
+ InformationLength // TokenInformationLength
+ );
+
+ if (Status == STATUS_INVALID_OWNER) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("InformationLength is: 0x%lx \n", InformationLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(Status == STATUS_INVALID_OWNER);
+
+
+ //
+ // Set owner of a token to be an ID not in the token
+ //
+
+ DbgPrint("Se: Set default owner to be an ID not in the token ... ");
+
+ AssignedOwner.Owner = BarneySid;
+ InformationLength = (ULONG)sizeof(TOKEN_OWNER);
+
+ Status = NtSetInformationToken(
+ TokenWithGroups, // Handle
+ TokenOwner, // TokenInformationClass
+ &AssignedOwner, // TokenInformation
+ InformationLength // TokenInformationLength
+ );
+
+ if (Status == STATUS_INVALID_OWNER) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("InformationLength is: 0x%lx \n", InformationLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(Status == STATUS_INVALID_OWNER);
+
+
+ //
+ // Set owner of a token to be a valid group
+ //
+
+ DbgPrint("Se: Set default owner to be a valid group ... ");
+
+ AssignedOwner.Owner = FlintstoneSid;
+ InformationLength = (ULONG)sizeof(TOKEN_OWNER);
+
+ Status = NtSetInformationToken(
+ TokenWithGroups, // Handle
+ TokenOwner, // TokenInformationClass
+ &AssignedOwner, // TokenInformation
+ InformationLength // TokenInformationLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("InformationLength is: 0x%lx \n", InformationLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // Query the Owner to see that it was set properly
+ //
+
+ Status = NtQueryInformationToken(
+ TokenWithGroups, // Handle
+ TokenOwner, // TokenInformationClass
+ QueriedOwner, // TokenInformation
+ 0, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (Status != STATUS_BUFFER_TOO_SMALL) {
+ DbgPrint("********** Failed Query of length ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(!NT_SUCCESS(Status));
+
+ QueriedOwner = (PTOKEN_OWNER)TstAllocatePool( PagedPool,
+ ReturnLength
+ );
+
+ Status = NtQueryInformationToken(
+ TokenWithGroups, // Handle
+ TokenOwner, // TokenInformationClass
+ QueriedOwner, // TokenInformation
+ ReturnLength, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Check returned value
+ //
+
+ if (RtlEqualSid((QueriedOwner->Owner), AssignedOwner.Owner) ) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed Comparison ************\n");
+ DbgPrint("Unexpected value returned by query.\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+ } else {
+ DbgPrint("********** Failed Query Of Value ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(NT_SUCCESS(Status));
+
+
+ //
+ // Set Default Dacl
+
+ //
+ // Get a buffer for use in all Default Dacl assignment tests.
+ // This will be initialized to different sizes for each test.
+ //
+
+ AssignedDefaultDacl.DefaultDacl =
+ (PACL)TstAllocatePool( PagedPool, TOO_BIG_ACL_SIZE );
+
+
+ //
+ // Assign a discretionary ACL to a token that doesn't yet have one
+ //
+
+ DbgPrint("Se: Set original discretionary ACL in token ... ");
+
+ InformationLength = (ULONG)sizeof(TOKEN_DEFAULT_DACL);
+ RtlCreateAcl( AssignedDefaultDacl.DefaultDacl, 200, ACL_REVISION );
+
+ Status = NtQueryInformationToken(
+ TokenWithGroups, // Handle
+ TokenDefaultDacl, // TokenInformationClass
+ &QueriedDefaultDacl, // TokenInformation
+ 0, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(Status == STATUS_BUFFER_TOO_SMALL);
+
+ if (ReturnLength != sizeof(TOKEN_DEFAULT_DACL)) {
+
+ //
+ // Wait a minute, this token has a default Dacl
+ //
+
+ DbgPrint("******** Failed - token has default dacl *********\n");
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ } else {
+
+ Status = NtSetInformationToken(
+ TokenWithGroups, // Handle
+ TokenDefaultDacl, // TokenInformationClass
+ &AssignedDefaultDacl, // TokenInformation
+ InformationLength // TokenInformationLength
+ );
+
+ if (NT_SUCCESS(Status)) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ CompletionStatus = FALSE;
+ }
+
+ }
+
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // Replace a discretionary ACL in a token that already has one
+ // Make it big to help with future "too big" tests...
+ //
+
+
+ //
+ // find out how much space is available
+ //
+
+ Status = NtQueryInformationToken(
+ TokenWithGroups, // Handle
+ TokenStatistics, // TokenInformationClass
+ &QueriedStatistics, // TokenInformation
+ (ULONG)sizeof(TOKEN_STATISTICS), // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = NtQueryInformationToken(
+ TokenWithGroups, // Handle
+ TokenDefaultDacl, // TokenInformationClass
+ &QueriedDefaultDacl, // TokenInformation
+ 0, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(Status == STATUS_BUFFER_TOO_SMALL);
+
+
+ if (ReturnLength > sizeof(TOKEN_STATISTICS)) {
+ CurrentLength = ReturnLength - (ULONG)sizeof(TOKEN_STATISTICS);
+ } else {
+ CurrentLength = 0;
+ }
+
+ LengthAvailable = QueriedStatistics.DynamicAvailable + CurrentLength;
+
+ DbgPrint("Se: Replace discretionary ACL in token ... ");
+
+ InformationLength = (ULONG)sizeof(TOKEN_DEFAULT_DACL);
+ RtlCreateAcl( AssignedDefaultDacl.DefaultDacl,
+ (ULONG)(LengthAvailable - 50),
+ ACL_REVISION
+ );
+
+ Status = NtQueryInformationToken(
+ TokenWithGroups, // Handle
+ TokenDefaultDacl, // TokenInformationClass
+ &QueriedDefaultDacl, // TokenInformation
+ 0, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+
+ if (!(ReturnLength > sizeof(TOKEN_DEFAULT_DACL))) {
+
+ //
+ // Wait a minute, this token doesn't have a default Dacl
+ //
+
+ DbgPrint("******** Failed - No default dacl *********\n");
+ CompletionStatus = FALSE;
+
+ } else {
+
+ Status = NtSetInformationToken(
+ TokenWithGroups, // Handle
+ TokenDefaultDacl, // TokenInformationClass
+ &AssignedDefaultDacl, // TokenInformation
+ InformationLength // TokenInformationLength
+ );
+
+ if (NT_SUCCESS(Status)) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ CompletionStatus = FALSE;
+ }
+
+ }
+
+ ASSERT(NT_SUCCESS(Status));
+
+
+ //
+ // Assign a discretionary ACL that doesn't fit into the dynamic part of the
+ // token.
+ //
+
+
+ //
+ // find out how much space is available
+ //
+
+ Status = NtQueryInformationToken(
+ TokenWithGroups, // Handle
+ TokenStatistics, // TokenInformationClass
+ &QueriedStatistics, // TokenInformation
+ (ULONG)sizeof(TOKEN_STATISTICS), // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = NtQueryInformationToken(
+ TokenWithGroups, // Handle
+ TokenDefaultDacl, // TokenInformationClass
+ &QueriedDefaultDacl, // TokenInformation
+ 0, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(Status == STATUS_BUFFER_TOO_SMALL);
+
+
+ if (ReturnLength > sizeof(TOKEN_STATISTICS)) {
+ CurrentLength = ReturnLength - (ULONG)sizeof(TOKEN_STATISTICS);
+ } else {
+ CurrentLength = 0;
+ }
+
+ LengthAvailable = QueriedStatistics.DynamicAvailable + CurrentLength;
+
+ DbgPrint("Se: Set too big discretionary ACL ... ");
+
+
+ //
+ // Now make sure our ACL is large enough to exceed the available
+ // space.
+ //
+
+ RtlCreateAcl( AssignedDefaultDacl.DefaultDacl,
+ TOO_BIG_ACL_SIZE,
+ ACL_REVISION
+ );
+
+ if (TOO_BIG_ACL_SIZE < LengthAvailable) {
+
+ DbgPrint("********** Failed - Dynamic too big ************\n");
+ DbgPrint("Dynamic available is: 0x%lx \n",
+ QueriedStatistics.DynamicAvailable);
+ DbgPrint("Current default Dacl size is: 0x%lx \n", CurrentLength);
+ DbgPrint("Big ACL size is: 0x%lx \n", TOO_BIG_ACL_SIZE);
+ CompletionStatus = FALSE;
+ }
+
+
+ InformationLength = (ULONG)sizeof(TOKEN_DEFAULT_DACL);
+
+ Status = NtSetInformationToken(
+ TokenWithGroups, // Handle
+ TokenDefaultDacl, // TokenInformationClass
+ &AssignedDefaultDacl, // TokenInformation
+ InformationLength // TokenInformationLength
+ );
+
+ if (Status == STATUS_ALLOTTED_SPACE_EXCEEDED) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Dynamic available is: 0x%lx \n",
+ QueriedStatistics.DynamicAvailable);
+ DbgPrint("Current default Dacl size is: 0x%lx \n", CurrentLength);
+ DbgPrint("Big ACL size is: 0x%lx \n", TOO_BIG_ACL_SIZE);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(Status == STATUS_ALLOTTED_SPACE_EXCEEDED);
+
+
+ //
+ // Set primary group
+ //
+
+ DbgPrint("Se: Set primary group ... ");
+
+ AssignedPrimaryGroup.PrimaryGroup = RubbleSid;
+ InformationLength = (ULONG)sizeof(TOKEN_PRIMARY_GROUP);
+
+ Status = NtSetInformationToken(
+ TokenWithGroups, // Handle
+ TokenPrimaryGroup, // TokenInformationClass
+ &AssignedPrimaryGroup, // TokenInformation
+ InformationLength // TokenInformationLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("InformationLength is: 0x%lx \n", InformationLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(NT_SUCCESS(Status));
+
+
+ //
+ // Query the Primary Group to see that it was set properly
+ //
+
+ Status = NtQueryInformationToken(
+ TokenWithGroups, // Handle
+ TokenPrimaryGroup, // TokenInformationClass
+ QueriedPrimaryGroup, // TokenInformation
+ 0, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (Status != STATUS_BUFFER_TOO_SMALL) {
+ DbgPrint("********** Failed Query of length ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(!NT_SUCCESS(Status));
+
+ QueriedPrimaryGroup =
+ (PTOKEN_PRIMARY_GROUP)TstAllocatePool( PagedPool,
+ ReturnLength
+ );
+
+ Status = NtQueryInformationToken(
+ TokenWithGroups, // Handle
+ TokenPrimaryGroup, // TokenInformationClass
+ QueriedPrimaryGroup, // TokenInformation
+ ReturnLength, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Check returned value
+ //
+
+ if (RtlEqualSid((QueriedPrimaryGroup->PrimaryGroup),
+ AssignedPrimaryGroup.PrimaryGroup) ) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed Comparison ************\n");
+ DbgPrint("Unexpected value returned by query.\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+ } else {
+ DbgPrint("********** Failed Query Of Value ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Required return length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(NT_SUCCESS(Status));
+
+
+ //
+ // Assign a primary group that doesn't fit into the dynamic part of the
+ // token.
+ //
+
+
+ DbgPrint("Se: Set too big primary group ... ");
+
+ //
+ // First, find out how much space is available
+ //
+
+ Status = NtQueryInformationToken(
+ TokenWithGroups, // Handle
+ TokenStatistics, // TokenInformationClass
+ &QueriedStatistics, // TokenInformation
+ (ULONG)sizeof(TOKEN_STATISTICS), // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = NtQueryInformationToken(
+ TokenWithGroups, // Handle
+ TokenPrimaryGroup, // TokenInformationClass
+ QueriedPrimaryGroup, // TokenInformation
+ ReturnLength, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(NT_SUCCESS(Status));
+
+ CurrentLength = SeLengthSid(QueriedPrimaryGroup->PrimaryGroup);
+ LengthAvailable = QueriedStatistics.DynamicAvailable + CurrentLength;
+
+ //
+ // Now make sure our fake group ID is large enough to exceed the available
+ // space.
+ //
+
+ TooBigSid = (PSID)TstAllocatePool(
+ PagedPool,
+ RtlLengthRequiredSid( TOO_BIG_PRIMARY_GROUP_SIZE )
+ );
+
+ RtlInitializeSid(
+ TooBigSid,
+ &BedrockAuthority,
+ TOO_BIG_PRIMARY_GROUP_SIZE
+ );
+
+ if (SeLengthSid(TooBigSid) < LengthAvailable) {
+
+ DbgPrint("********** Failed - Dynamic too big ************\n");
+ DbgPrint("Dynamic available is: 0x%lx \n",
+ QueriedStatistics.DynamicAvailable);
+ DbgPrint("Existing primary group length is: 0x%lx \n", CurrentLength);
+ DbgPrint("Big SID size is: 0x%lx \n", SeLengthSid(TooBigSid));
+ CompletionStatus = FALSE;
+ }
+
+
+ AssignedPrimaryGroup.PrimaryGroup = TooBigSid;
+ InformationLength = (ULONG)sizeof(TOKEN_PRIMARY_GROUP);
+
+ Status = NtSetInformationToken(
+ TokenWithGroups, // Handle
+ TokenPrimaryGroup, // TokenInformationClass
+ &AssignedPrimaryGroup, // TokenInformation
+ InformationLength // TokenInformationLength
+ );
+
+ if (Status == STATUS_ALLOTTED_SPACE_EXCEEDED) {
+ DbgPrint("Succeeded.\n");
+ } else {
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Dynamic available is: 0x%lx \n",
+ QueriedStatistics.DynamicAvailable);
+ DbgPrint("Existing primary group length is: 0x%lx \n", CurrentLength);
+ DbgPrint("Big SID size is: 0x%lx \n", SeLengthSid(TooBigSid));
+ CompletionStatus = FALSE;
+ }
+
+ ASSERT(Status == STATUS_ALLOTTED_SPACE_EXCEEDED);
+
+
+
+ return CompletionStatus;
+}
+
+////////////////////////////////////////////////////////////////
+// //
+// Adjust Privileges Test //
+// //
+////////////////////////////////////////////////////////////////
+
+BOOLEAN
+TestTokenAdjustPrivileges()
+{
+
+ BOOLEAN CompletionStatus = TRUE;
+ NTSTATUS Status;
+ NTSTATUS IgnoreStatus;
+
+ PTOKEN_PRIVILEGES NewState;
+ PTOKEN_PRIVILEGES PreviousState;
+ PTOKEN_PRIVILEGES PrePrivileges;
+ PTOKEN_PRIVILEGES PostPrivileges;
+
+ ULONG NewStateBufferLength = 200;
+ ULONG PreviousStateBufferLength = 200;
+ ULONG PrePrivilegesLength = 200;
+ ULONG PostPrivilegesLength = 200;
+
+ ULONG ReturnLength;
+ ULONG IgnoreReturnLength;
+
+ DbgPrint("\n");
+
+ PreviousState = (PTOKEN_PRIVILEGES)TstAllocatePool(
+ PagedPool,
+ PreviousStateBufferLength
+ );
+
+ PrePrivileges = (PTOKEN_PRIVILEGES)TstAllocatePool(
+ PagedPool,
+ PrePrivilegesLength
+ );
+
+ PostPrivileges = (PTOKEN_PRIVILEGES)TstAllocatePool(
+ PagedPool,
+ PostPrivilegesLength
+ );
+
+ NewState = (PTOKEN_PRIVILEGES)TstAllocatePool(
+ PagedPool,
+ NewStateBufferLength
+ );
+
+
+
+
+
+ //////////////////////////////////////////////////////////////////////
+ // //
+ // Adjust privileges giving no instructions //
+ // //
+ //////////////////////////////////////////////////////////////////////
+
+
+ DbgPrint("Se: Adjust privileges with no instructions ... ");
+
+ Status = NtAdjustPrivilegesToken(
+ SimpleToken, // TokenHandle
+ FALSE, // DisableAllPrivileges
+ NULL, // NewState (OPTIONAL)
+ 0, // BufferLength
+ NULL, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+
+ if (Status == STATUS_INVALID_PARAMETER) {
+
+ DbgPrint("Succeeded. \n");
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+ ASSERT(Status == STATUS_INVALID_PARAMETER);
+
+
+ //////////////////////////////////////////////////////////////////////
+ // //
+ // Enable privileges in token with no privileges //
+ // //
+ //////////////////////////////////////////////////////////////////////
+
+
+ NewState->PrivilegeCount = 1;
+ NewState->Privileges[0].Luid = SecurityPrivilege;
+ NewState->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+ DbgPrint("Se: Enable privilege in token with none ... ");
+
+ Status = NtAdjustPrivilegesToken(
+ SimpleToken, // TokenHandle
+ FALSE, // DisableAllPrivileges
+ NewState, // NewState (OPTIONAL)
+ 0, // BufferLength
+ NULL, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+
+ if (Status == STATUS_NOT_ALL_ASSIGNED) {
+
+ DbgPrint("Succeeded. \n");
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+ ASSERT(Status == STATUS_NOT_ALL_ASSIGNED);
+
+
+ //////////////////////////////////////////////////////////////////////
+ // //
+ // Enable a privilege that isn't assigned //
+ // //
+ //////////////////////////////////////////////////////////////////////
+
+ NewState->PrivilegeCount = 1;
+ NewState->Privileges[0].Luid = CreateTokenPrivilege;
+ NewState->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+ DbgPrint("Se: Enable unassigned privilege in token with some ... ");
+
+ PrePrivileges->PrivilegeCount = 77;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithPrivileges, // TokenHandle
+ TokenPrivileges, // TokenInformationClass
+ PrePrivileges, // TokenInformation
+ PrePrivilegesLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT( PrePrivileges->PrivilegeCount == PRIVILEGE_COUNT );
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ Status = NtAdjustPrivilegesToken(
+ TokenWithPrivileges, // TokenHandle
+ FALSE, // DisableAllPrivileges
+ NewState, // NewState (OPTIONAL)
+ 0, // BufferLength
+ NULL, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+
+ PostPrivileges->PrivilegeCount = 88;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithPrivileges, // TokenHandle
+ TokenPrivileges, // TokenInformationClass
+ PostPrivileges, // TokenInformation
+ PostPrivilegesLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT( PostPrivileges->PrivilegeCount == PRIVILEGE_COUNT );
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ if (Status == STATUS_NOT_ALL_ASSIGNED) {
+
+ //
+ // Check the privilege values
+ //
+
+ if ( (PrePrivileges->Privileges[0].Attributes ==
+ PostPrivileges->Privileges[0].Attributes) &&
+ (PrePrivileges->Privileges[1].Attributes ==
+ PostPrivileges->Privileges[1].Attributes) ) {
+
+ DbgPrint("Succeeded. \n");
+
+ } else {
+
+ DbgPrint("********** Failed Value Check ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Before and after privilege 0 state: 0x%lx, 0x%lx \n",
+ PrePrivileges->Privileges[0].Attributes,
+ PostPrivileges->Privileges[0].Attributes);
+ DbgPrint("Before and after privilege 1 state: 0x%lx, 0x%lx \n",
+ PrePrivileges->Privileges[1].Attributes,
+ PostPrivileges->Privileges[1].Attributes);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+ ASSERT(Status == STATUS_NOT_ALL_ASSIGNED);
+
+
+
+
+ //////////////////////////////////////////////////////////////////////
+ // //
+ // Disable All Privileges (which they already are) //
+ // //
+ //////////////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Disable already disabled privileges ... ");
+
+ PrePrivileges->PrivilegeCount = 77;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithPrivileges, // TokenHandle
+ TokenPrivileges, // TokenInformationClass
+ PrePrivileges, // TokenInformation
+ PrePrivilegesLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT( PrePrivileges->PrivilegeCount == PRIVILEGE_COUNT );
+ ASSERT( PrePrivileges->Privileges[0].Attributes == 0 );
+ ASSERT( PrePrivileges->Privileges[1].Attributes == 0 );
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ Status = NtAdjustPrivilegesToken(
+ TokenWithPrivileges, // TokenHandle
+ TRUE, // DisableAllPrivileges
+ NULL, // NewState (OPTIONAL)
+ 0, // BufferLength
+ NULL, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+
+
+ PostPrivileges->PrivilegeCount = 88;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithPrivileges, // TokenHandle
+ TokenPrivileges, // TokenInformationClass
+ PostPrivileges, // TokenInformation
+ PostPrivilegesLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT( PostPrivileges->PrivilegeCount == PRIVILEGE_COUNT );
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ if (Status == STATUS_SUCCESS) {
+
+ //
+ // Check the privilege values
+ //
+
+ if ( (PostPrivileges->Privileges[0].Attributes == 0) &&
+ (PostPrivileges->Privileges[1].Attributes == 0) ) {
+
+ DbgPrint("Succeeded. \n");
+
+ } else {
+
+ DbgPrint("********** Failed Value Check ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Before and after privilege 0 state: 0x%lx, 0x%lx \n",
+ PrePrivileges->Privileges[0].Attributes,
+ PostPrivileges->Privileges[0].Attributes);
+ DbgPrint("Before and after privilege 1 state: 0x%lx, 0x%lx \n",
+ PrePrivileges->Privileges[1].Attributes,
+ PostPrivileges->Privileges[1].Attributes);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+ ASSERT(Status == STATUS_SUCCESS);
+
+
+
+ //////////////////////////////////////////////////////////////////////
+ // //
+ // Enable currently disabled privileges //
+ // //
+ //////////////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Enable currently disabled privileges ... ");
+
+ PrePrivileges->PrivilegeCount = 77;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithPrivileges, // TokenHandle
+ TokenPrivileges, // TokenInformationClass
+ PrePrivileges, // TokenInformation
+ PrePrivilegesLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT( PrePrivileges->PrivilegeCount == PRIVILEGE_COUNT );
+ ASSERT( PrePrivileges->Privileges[0].Attributes == 0 );
+ ASSERT( PrePrivileges->Privileges[1].Attributes == 0 );
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ NewState->PrivilegeCount = 2;
+ NewState->Privileges[0].Luid = SecurityPrivilege;
+ NewState->Privileges[1].Luid = UnsolicitedInputPrivilege;
+ NewState->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ NewState->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;
+
+ Status = NtAdjustPrivilegesToken(
+ TokenWithPrivileges, // TokenHandle
+ FALSE, // DisableAllPrivileges
+ NewState, // NewState (OPTIONAL)
+ 0, // BufferLength
+ NULL, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+
+ PostPrivileges->PrivilegeCount = 88;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithPrivileges, // TokenHandle
+ TokenPrivileges, // TokenInformationClass
+ PostPrivileges, // TokenInformation
+ PostPrivilegesLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT( PostPrivileges->PrivilegeCount == PRIVILEGE_COUNT );
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ if (Status == STATUS_SUCCESS) {
+
+ //
+ // Check the privilege values
+ //
+
+ if ( (PostPrivileges->Privileges[0].Attributes == SE_PRIVILEGE_ENABLED) &&
+ (PostPrivileges->Privileges[1].Attributes == SE_PRIVILEGE_ENABLED)
+ ) {
+
+ DbgPrint("Succeeded. \n");
+
+ } else {
+
+ DbgPrint("********** Failed Value Check ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Before and after privilege 0 state: 0x%lx, 0x%lx \n",
+ PrePrivileges->Privileges[0].Attributes,
+ PostPrivileges->Privileges[0].Attributes);
+ DbgPrint("Before and after privilege 1 state: 0x%lx, 0x%lx \n",
+ PrePrivileges->Privileges[1].Attributes,
+ PostPrivileges->Privileges[1].Attributes);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+ ASSERT(Status == STATUS_SUCCESS);
+
+
+
+ //////////////////////////////////////////////////////////////////////
+ // //
+ // Disable all enabled privileges //
+ // //
+ //////////////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Disable all enabled privileges ... ");
+
+ PrePrivileges->PrivilegeCount = 77;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithPrivileges, // TokenHandle
+ TokenPrivileges, // TokenInformationClass
+ PrePrivileges, // TokenInformation
+ PrePrivilegesLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+
+ ASSERT( PrePrivileges->PrivilegeCount == PRIVILEGE_COUNT );
+ ASSERT( PrePrivileges->Privileges[0].Attributes == SE_PRIVILEGE_ENABLED );
+ ASSERT( PrePrivileges->Privileges[1].Attributes == SE_PRIVILEGE_ENABLED );
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ Status = NtAdjustPrivilegesToken(
+ TokenWithPrivileges, // TokenHandle
+ TRUE, // DisableAllPrivileges
+ NULL, // NewState (OPTIONAL)
+ 0, // BufferLength
+ NULL, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+
+
+ PostPrivileges->PrivilegeCount = 88;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithPrivileges, // TokenHandle
+ TokenPrivileges, // TokenInformationClass
+ PostPrivileges, // TokenInformation
+ PostPrivilegesLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT( PostPrivileges->PrivilegeCount == PRIVILEGE_COUNT );
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ if (Status == STATUS_SUCCESS) {
+
+ //
+ // Check the privilege values
+ //
+
+ if ( (PostPrivileges->Privileges[0].Attributes == 0) &&
+ (PostPrivileges->Privileges[1].Attributes == 0) ) {
+
+ DbgPrint("Succeeded. \n");
+
+ } else {
+
+ DbgPrint("********** Failed Value Check ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Before and after privilege 0 state: 0x%lx, 0x%lx \n",
+ PrePrivileges->Privileges[0].Attributes,
+ PostPrivileges->Privileges[0].Attributes);
+ DbgPrint("Before and after privilege 1 state: 0x%lx, 0x%lx \n",
+ PrePrivileges->Privileges[1].Attributes,
+ PostPrivileges->Privileges[1].Attributes);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+ ASSERT(Status == STATUS_SUCCESS);
+
+
+
+ //////////////////////////////////////////////////////////////////////
+ // //
+ // Enable privileges requesting previous state with no return //
+ // length buffer //
+ // //
+ //////////////////////////////////////////////////////////////////////
+
+
+ DbgPrint("Se: PreviousState not NULL, ReturnLength NULL... ");
+
+ NewState->PrivilegeCount = 2;
+ NewState->Privileges[0].Luid = SecurityPrivilege;
+ NewState->Privileges[1].Luid = UnsolicitedInputPrivilege;
+ NewState->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ NewState->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;
+
+ Status = NtAdjustPrivilegesToken(
+ TokenWithPrivileges, // TokenHandle
+ FALSE, // DisableAllPrivileges
+ NewState, // NewState (OPTIONAL)
+ 0, // BufferLength
+ PreviousState, // PreviousState (OPTIONAL)
+ NULL // ReturnLength
+ );
+
+ if (Status == STATUS_ACCESS_VIOLATION) {
+
+ DbgPrint("Succeeded. \n");
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ CompletionStatus = FALSE;
+
+ }
+
+ ASSERT(Status == STATUS_ACCESS_VIOLATION);
+
+
+
+
+ //////////////////////////////////////////////////////////////////////
+ // //
+ // Enable privileges without requesting previous state and //
+ // providing no return length buffer //
+ // //
+ //////////////////////////////////////////////////////////////////////
+
+
+ DbgPrint("Se: PreviousState and ReturnLength both NULL... ");
+
+ NewState->PrivilegeCount = 2;
+ NewState->Privileges[0].Luid = SecurityPrivilege;
+ NewState->Privileges[1].Luid = UnsolicitedInputPrivilege;
+ NewState->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ NewState->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;
+
+ Status = NtAdjustPrivilegesToken(
+ TokenWithPrivileges, // TokenHandle
+ FALSE, // DisableAllPrivileges
+ NewState, // NewState (OPTIONAL)
+ 0, // BufferLength
+ NULL, // PreviousState (OPTIONAL)
+ NULL // ReturnLength
+ );
+
+ if (Status == STATUS_SUCCESS) {
+
+ DbgPrint("Succeeded. \n");
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ CompletionStatus = FALSE;
+
+ }
+
+ ASSERT(Status == STATUS_SUCCESS);
+
+
+
+
+
+
+ //////////////////////////////////////////////////////////////////////
+ // //
+ // Enable privileges requesting previous state with insufficient //
+ // buffer //
+ // //
+ //////////////////////////////////////////////////////////////////////
+
+
+ DbgPrint("Se: Too small buffer for previous state ... ");
+
+ //
+ // Establish a known previous state first...
+ //
+
+ Status = NtAdjustPrivilegesToken(
+ TokenWithPrivileges, // TokenHandle
+ TRUE, // DisableAllPrivileges
+ NULL, // NewState (OPTIONAL)
+ 0, // BufferLength
+ NULL, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+
+ NewState->PrivilegeCount = 2;
+ NewState->Privileges[0].Luid = SecurityPrivilege;
+ NewState->Privileges[1].Luid = UnsolicitedInputPrivilege;
+ NewState->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ NewState->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;
+
+ Status = NtAdjustPrivilegesToken(
+ TokenWithPrivileges, // TokenHandle
+ FALSE, // DisableAllPrivileges
+ NewState, // NewState (OPTIONAL)
+ 0, // BufferLength
+ PreviousState, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (Status == STATUS_BUFFER_TOO_SMALL) {
+
+ DbgPrint("Succeeded. \n");
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+ ASSERT(Status == STATUS_BUFFER_TOO_SMALL);
+
+
+
+
+
+ //////////////////////////////////////////////////////////////////////
+ // //
+ // Enable one of the privileges requesting previous state //
+ // //
+ //////////////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Enable one requesting previous state ... ");
+
+ PrePrivileges->PrivilegeCount = 77;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithPrivileges, // TokenHandle
+ TokenPrivileges, // TokenInformationClass
+ PrePrivileges, // TokenInformation
+ PrePrivilegesLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT( PrePrivileges->PrivilegeCount == PRIVILEGE_COUNT );
+ ASSERT( PrePrivileges->Privileges[0].Attributes == 0 );
+ ASSERT( PrePrivileges->Privileges[1].Attributes == 0 );
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+
+ NewState->PrivilegeCount = 1;
+ NewState->Privileges[0].Luid = SecurityPrivilege;
+ NewState->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+ Status = NtAdjustPrivilegesToken(
+ TokenWithPrivileges, // TokenHandle
+ FALSE, // DisableAllPrivileges
+ NewState, // NewState (OPTIONAL)
+ PreviousStateBufferLength, // BufferLength
+ PreviousState, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(NT_SUCCESS(Status));
+ ASSERT(PreviousState->PrivilegeCount == 1);
+
+
+ PostPrivileges->PrivilegeCount = 88;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithPrivileges, // TokenHandle
+ TokenPrivileges, // TokenInformationClass
+ PostPrivileges, // TokenInformation
+ PostPrivilegesLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT( PostPrivileges->PrivilegeCount == PRIVILEGE_COUNT );
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ if (Status == STATUS_SUCCESS) {
+
+ //
+ // Check the privilege values
+ //
+
+ if ( (PostPrivileges->Privileges[SECURITY_INDEX].Attributes ==
+ SE_PRIVILEGE_ENABLED) &&
+ (PostPrivileges->Privileges[UNSOLICITED_INDEX].Attributes == 0)
+ ) {
+
+ DbgPrint("Succeeded. \n");
+
+ } else {
+
+ DbgPrint("********** Failed Value Check ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Before and after privilege 0 state: 0x%lx, 0x%lx \n",
+ PrePrivileges->Privileges[0].Attributes,
+ PostPrivileges->Privileges[0].Attributes);
+ DbgPrint("Before and after privilege 1 state: 0x%lx, 0x%lx \n",
+ PrePrivileges->Privileges[1].Attributes,
+ PostPrivileges->Privileges[1].Attributes);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ DbgPrint("Change Count is: 0x%lx \n", PreviousState->PrivilegeCount);
+ CompletionStatus = FALSE;
+
+ }
+
+ ASSERT(Status == STATUS_SUCCESS);
+
+
+
+
+ //////////////////////////////////////////////////////////////////////
+ // //
+ // Enable the other privilege requesting previous state //
+ // //
+ //////////////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Enable one requesting previous state ... ");
+
+ PrePrivileges->PrivilegeCount = 77;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithPrivileges, // TokenHandle
+ TokenPrivileges, // TokenInformationClass
+ PrePrivileges, // TokenInformation
+ PrePrivilegesLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT( PrePrivileges->PrivilegeCount == PRIVILEGE_COUNT );
+ ASSERT( PrePrivileges->Privileges[SECURITY_INDEX].Attributes ==
+ SE_PRIVILEGE_ENABLED );
+ ASSERT( PrePrivileges->Privileges[UNSOLICITED_INDEX].Attributes == 0 );
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ NewState->PrivilegeCount = 1;
+ NewState->Privileges[0].Luid = UnsolicitedInputPrivilege;
+ NewState->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+ Status = NtAdjustPrivilegesToken(
+ TokenWithPrivileges, // TokenHandle
+ FALSE, // DisableAllPrivileges
+ NewState, // NewState (OPTIONAL)
+ PreviousStateBufferLength, // BufferLength
+ PreviousState, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(NT_SUCCESS(Status));
+ ASSERT(PreviousState->PrivilegeCount == 1);
+
+
+ PostPrivileges->PrivilegeCount = 88;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithPrivileges, // TokenHandle
+ TokenPrivileges, // TokenInformationClass
+ PostPrivileges, // TokenInformation
+ PostPrivilegesLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT( PostPrivileges->PrivilegeCount == PRIVILEGE_COUNT );
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ if (Status == STATUS_SUCCESS) {
+
+ //
+ // Check the privilege values
+ //
+
+ if ( (PostPrivileges->Privileges[0].Attributes == SE_PRIVILEGE_ENABLED) &&
+ (PostPrivileges->Privileges[1].Attributes == SE_PRIVILEGE_ENABLED)
+ ) {
+
+ DbgPrint("Succeeded. \n");
+
+ } else {
+
+ DbgPrint("********** Failed Value Check ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Before and after privilege 0 state: 0x%lx, 0x%lx \n",
+ PrePrivileges->Privileges[0].Attributes,
+ PostPrivileges->Privileges[0].Attributes);
+ DbgPrint("Before and after privilege 1 state: 0x%lx, 0x%lx \n",
+ PrePrivileges->Privileges[1].Attributes,
+ PostPrivileges->Privileges[1].Attributes);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ DbgPrint("Change Count is: 0x%lx \n", PreviousState->PrivilegeCount);
+ CompletionStatus = FALSE;
+
+ }
+
+ ASSERT(Status == STATUS_SUCCESS);
+
+
+
+
+
+ //////////////////////////////////////////////////////////////////////
+ // //
+ // Return privileges to their previous state //
+ // Uses PreviousState from previous call //
+ // //
+ //////////////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Return privileges to previous state ... ");
+
+ PrePrivileges->PrivilegeCount = 77;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithPrivileges, // TokenHandle
+ TokenPrivileges, // TokenInformationClass
+ PrePrivileges, // TokenInformation
+ PrePrivilegesLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT( PrePrivileges->PrivilegeCount == PRIVILEGE_COUNT );
+ ASSERT( PrePrivileges->Privileges[0].Attributes == SE_PRIVILEGE_ENABLED );
+ ASSERT( PrePrivileges->Privileges[1].Attributes == SE_PRIVILEGE_ENABLED );
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ Status = NtAdjustPrivilegesToken(
+ TokenWithPrivileges, // TokenHandle
+ FALSE, // DisableAllPrivileges
+ PreviousState, // NewState (OPTIONAL)
+ 0, // BufferLength
+ NULL, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+
+ ASSERT(NT_SUCCESS(Status));
+ ASSERT(PreviousState->PrivilegeCount == 1);
+
+
+ PostPrivileges->PrivilegeCount = 88;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithPrivileges, // TokenHandle
+ TokenPrivileges, // TokenInformationClass
+ PostPrivileges, // TokenInformation
+ PostPrivilegesLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT( PostPrivileges->PrivilegeCount == PRIVILEGE_COUNT );
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ if (Status == STATUS_SUCCESS) {
+
+ //
+ // Check the privilege values
+ //
+
+ if ( (PostPrivileges->Privileges[SECURITY_INDEX].Attributes ==
+ SE_PRIVILEGE_ENABLED) &&
+ (PostPrivileges->Privileges[UNSOLICITED_INDEX].Attributes == 0)
+ ) {
+
+ DbgPrint("Succeeded. \n");
+
+ } else {
+
+ DbgPrint("********** Failed Value Check ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Before and after privilege 0 state: 0x%lx, 0x%lx \n",
+ PrePrivileges->Privileges[0].Attributes,
+ PostPrivileges->Privileges[0].Attributes);
+ DbgPrint("Before and after privilege 1 state: 0x%lx, 0x%lx \n",
+ PrePrivileges->Privileges[1].Attributes,
+ PostPrivileges->Privileges[1].Attributes);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+
+ ASSERT(Status == STATUS_SUCCESS);
+
+
+
+
+ ////////////////////////////////////////////////////////////////
+ // //
+ // Done with test //
+ // //
+ ////////////////////////////////////////////////////////////////
+
+
+
+ TstDeallocatePool( PreviousState, PreviousStateBufferLength );
+ TstDeallocatePool( NewState, NewStateBufferLength );
+ TstDeallocatePool( PrePrivileges, PrePrivilegesLength );
+ TstDeallocatePool( PostPrivileges, PostPrivilegesLength );
+
+
+ return CompletionStatus;
+}
+
+////////////////////////////////////////////////////////////////
+// //
+// Adjust Groups Test //
+// //
+////////////////////////////////////////////////////////////////
+
+BOOLEAN
+TestTokenAdjustGroups()
+{
+ BOOLEAN CompletionStatus = TRUE;
+ NTSTATUS Status;
+ NTSTATUS IgnoreStatus;
+
+ PTOKEN_GROUPS NewState;
+ PTOKEN_GROUPS PreviousState;
+ PTOKEN_GROUPS PreGroups;
+ PTOKEN_GROUPS PostGroups;
+
+ ULONG NewStateBufferLength = 600;
+ ULONG PreviousStateBufferLength = 600;
+ ULONG PreGroupsLength = 600;
+ ULONG PostGroupsLength = 600;
+
+ ULONG ReturnLength;
+ ULONG IgnoreReturnLength;
+
+ DbgPrint("\n");
+
+ PreviousState = (PTOKEN_GROUPS)TstAllocatePool(
+ PagedPool,
+ PreviousStateBufferLength
+ );
+
+ PreGroups = (PTOKEN_GROUPS)TstAllocatePool(
+ PagedPool,
+ PreGroupsLength
+ );
+
+ PostGroups = (PTOKEN_GROUPS)TstAllocatePool(
+ PagedPool,
+ PostGroupsLength
+ );
+
+ NewState = (PTOKEN_GROUPS)TstAllocatePool(
+ PagedPool,
+ NewStateBufferLength
+ );
+
+
+
+
+
+ //////////////////////////////////////////////////////////////////////
+ // //
+ // Adjust groups giving no instructions //
+ // //
+ //////////////////////////////////////////////////////////////////////
+
+
+ DbgPrint("Se: Adjust groups with no instructions ... ");
+
+ Status = NtAdjustGroupsToken(
+ SimpleToken, // TokenHandle
+ FALSE, // ResetToDefault
+ NULL, // NewState (OPTIONAL)
+ 0, // BufferLength
+ NULL, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+
+ if (Status == STATUS_INVALID_PARAMETER) {
+
+ DbgPrint("Succeeded. \n");
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+ ASSERT(Status == STATUS_INVALID_PARAMETER);
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Disable unknown group //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Disable unknown group ... ");
+
+ PreGroups->GroupCount = 77;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PreGroups, // TokenInformation
+ PreGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ if (IgnoreStatus != STATUS_SUCCESS) {
+ DbgPrint(" \n IgnoreStatus = 0x%lx \n", IgnoreStatus);
+ DbgPrint(" \n IgnoreReturnLength = 0x%lx \n", IgnoreReturnLength);
+ }
+
+ ASSERT(PreGroups->GroupCount == GROUP_COUNT );
+ ASSERT(PreGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes);
+ ASSERT(PreGroups->Groups[CHILD_INDEX].Attributes == OptionalGroupAttributes);
+ ASSERT(PreGroups->Groups[NEANDERTHOL_INDEX].Attributes == OptionalGroupAttributes);
+ ASSERT(PreGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes);
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ NewState->GroupCount = 1;
+ NewState->Groups[0].Sid = RubbleSid;
+ NewState->Groups[0].Attributes = DisabledGroupAttributes;
+
+ Status = NtAdjustGroupsToken(
+ TokenWithGroups, // TokenHandle
+ FALSE, // ResetToDefault
+ NewState, // NewState (OPTIONAL)
+ 0, // BufferLength
+ NULL, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+
+ PostGroups->GroupCount = 88;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PostGroups, // TokenInformation
+ PostGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ if (Status == STATUS_NOT_ALL_ASSIGNED) {
+
+ //
+ // Check the group values
+ //
+
+ ASSERT( PostGroups->GroupCount == GROUP_COUNT );
+ if ( (PostGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes) &&
+ (PostGroups->Groups[CHILD_INDEX].Attributes == OptionalGroupAttributes) &&
+ (PostGroups->Groups[NEANDERTHOL_INDEX].Attributes == OptionalGroupAttributes) &&
+ (PostGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes)
+ ) {
+
+ DbgPrint("Succeeded. \n");
+
+ } else {
+
+ DbgPrint("********** Failed Value Check ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+
+ DbgPrint("Before/after Flintstone state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[FLINTSTONE_INDEX].Attributes,
+ PostGroups->Groups[FLINTSTONE_INDEX].Attributes);
+
+ DbgPrint("Before/after Child state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[CHILD_INDEX].Attributes,
+ PostGroups->Groups[CHILD_INDEX].Attributes);
+
+ DbgPrint("Before/after Neanderthol state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[NEANDERTHOL_INDEX].Attributes,
+ PostGroups->Groups[NEANDERTHOL_INDEX].Attributes);
+
+ DbgPrint("Before/after World state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[WORLD_INDEX].Attributes,
+ PostGroups->Groups[WORLD_INDEX].Attributes);
+
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+
+ CompletionStatus = FALSE;
+
+ }
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+ ASSERT(Status == STATUS_NOT_ALL_ASSIGNED);
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Enable unknown group //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Enable unknown group ... ");
+
+ PreGroups->GroupCount = 77;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PreGroups, // TokenInformation
+ PreGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(PreGroups->GroupCount == GROUP_COUNT );
+ ASSERT(PreGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes);
+ ASSERT(PreGroups->Groups[CHILD_INDEX].Attributes == OptionalGroupAttributes);
+ ASSERT(PreGroups->Groups[NEANDERTHOL_INDEX].Attributes == OptionalGroupAttributes);
+ ASSERT(PreGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes);
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ NewState->GroupCount = 1;
+ NewState->Groups[0].Sid = RubbleSid;
+ NewState->Groups[0].Attributes = OptionalGroupAttributes;
+
+ Status = NtAdjustGroupsToken(
+ TokenWithGroups, // TokenHandle
+ FALSE, // ResetToDefault
+ NewState, // NewState (OPTIONAL)
+ 0, // BufferLength
+ NULL, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+
+ PostGroups->GroupCount = 88;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PostGroups, // TokenInformation
+ PostGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ if (Status == STATUS_NOT_ALL_ASSIGNED) {
+
+ //
+ // Check the group values
+ //
+
+ ASSERT( PostGroups->GroupCount == GROUP_COUNT );
+ if ( (PostGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes) &&
+ (PostGroups->Groups[CHILD_INDEX].Attributes == OptionalGroupAttributes) &&
+ (PostGroups->Groups[NEANDERTHOL_INDEX].Attributes == OptionalGroupAttributes) &&
+ (PostGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes)
+ ) {
+
+ DbgPrint("Succeeded. \n");
+
+ } else {
+
+ DbgPrint("********** Failed Value Check ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+
+ DbgPrint("Before/after Flintstone state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[FLINTSTONE_INDEX].Attributes,
+ PostGroups->Groups[FLINTSTONE_INDEX].Attributes);
+
+ DbgPrint("Before/after Child state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[CHILD_INDEX].Attributes,
+ PostGroups->Groups[CHILD_INDEX].Attributes);
+
+ DbgPrint("Before/after Neanderthol state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[NEANDERTHOL_INDEX].Attributes,
+ PostGroups->Groups[NEANDERTHOL_INDEX].Attributes);
+
+ DbgPrint("Before/after World state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[WORLD_INDEX].Attributes,
+ PostGroups->Groups[WORLD_INDEX].Attributes);
+
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+
+ CompletionStatus = FALSE;
+
+ }
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+ ASSERT(Status == STATUS_NOT_ALL_ASSIGNED);
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Disable mandatory group //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Disable mandatory group ... ");
+
+ PreGroups->GroupCount = 77;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PreGroups, // TokenInformation
+ PreGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(PreGroups->GroupCount == GROUP_COUNT );
+ ASSERT(PreGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes);
+ ASSERT(PreGroups->Groups[CHILD_INDEX].Attributes == OptionalGroupAttributes);
+ ASSERT(PreGroups->Groups[NEANDERTHOL_INDEX].Attributes == OptionalGroupAttributes);
+ ASSERT(PreGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes);
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ NewState->GroupCount = 1;
+ NewState->Groups[0].Sid = WorldSid;
+ NewState->Groups[0].Attributes = DisabledGroupAttributes;
+
+ Status = NtAdjustGroupsToken(
+ TokenWithGroups, // TokenHandle
+ FALSE, // ResetToDefault
+ NewState, // NewState (OPTIONAL)
+ 0, // BufferLength
+ NULL, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+
+ PostGroups->GroupCount = 88;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PostGroups, // TokenInformation
+ PostGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ if (Status == STATUS_CANT_DISABLE_MANDATORY) {
+
+ //
+ // Check the group values
+ //
+
+ ASSERT( PostGroups->GroupCount == GROUP_COUNT );
+ if ( (PostGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes) &&
+ (PostGroups->Groups[CHILD_INDEX].Attributes == OptionalGroupAttributes) &&
+ (PostGroups->Groups[NEANDERTHOL_INDEX].Attributes == OptionalGroupAttributes) &&
+ (PostGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes)
+ ) {
+
+ DbgPrint("Succeeded. \n");
+
+ } else {
+
+ DbgPrint("********** Failed Value Check ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+
+ DbgPrint("Before/after Flintstone state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[FLINTSTONE_INDEX].Attributes,
+ PostGroups->Groups[FLINTSTONE_INDEX].Attributes);
+
+ DbgPrint("Before/after Child state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[CHILD_INDEX].Attributes,
+ PostGroups->Groups[CHILD_INDEX].Attributes);
+
+ DbgPrint("Before/after Neanderthol state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[NEANDERTHOL_INDEX].Attributes,
+ PostGroups->Groups[NEANDERTHOL_INDEX].Attributes);
+
+ DbgPrint("Before/after World state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[WORLD_INDEX].Attributes,
+ PostGroups->Groups[WORLD_INDEX].Attributes);
+
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+
+ CompletionStatus = FALSE;
+
+ }
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+ ASSERT(Status == STATUS_CANT_DISABLE_MANDATORY);
+
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Enable mandatory group //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Enable mandatory group ... ");
+
+ PreGroups->GroupCount = 77;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PreGroups, // TokenInformation
+ PreGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(PreGroups->GroupCount == GROUP_COUNT );
+ ASSERT(PreGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes);
+ ASSERT(PreGroups->Groups[CHILD_INDEX].Attributes == OptionalGroupAttributes);
+ ASSERT(PreGroups->Groups[NEANDERTHOL_INDEX].Attributes == OptionalGroupAttributes);
+ ASSERT(PreGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes);
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ NewState->GroupCount = 1;
+ NewState->Groups[0].Sid = WorldSid;
+ NewState->Groups[0].Attributes = OptionalGroupAttributes;
+
+ Status = NtAdjustGroupsToken(
+ TokenWithGroups, // TokenHandle
+ FALSE, // ResetToDefault
+ NewState, // NewState (OPTIONAL)
+ 0, // BufferLength
+ NULL, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+
+ PostGroups->GroupCount = 88;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PostGroups, // TokenInformation
+ PostGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ if (Status == STATUS_SUCCESS) {
+
+ //
+ // Check the group values
+ //
+
+ ASSERT( PostGroups->GroupCount == GROUP_COUNT );
+ if ( (PostGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes) &&
+ (PostGroups->Groups[CHILD_INDEX].Attributes == OptionalGroupAttributes) &&
+ (PostGroups->Groups[NEANDERTHOL_INDEX].Attributes == OptionalGroupAttributes) &&
+ (PostGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes)
+ ) {
+
+ DbgPrint("Succeeded. \n");
+
+ } else {
+
+ DbgPrint("********** Failed Value Check ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+
+ DbgPrint("Before/after Flintstone state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[FLINTSTONE_INDEX].Attributes,
+ PostGroups->Groups[FLINTSTONE_INDEX].Attributes);
+
+ DbgPrint("Before/after Child state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[CHILD_INDEX].Attributes,
+ PostGroups->Groups[CHILD_INDEX].Attributes);
+
+ DbgPrint("Before/after Neanderthol state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[NEANDERTHOL_INDEX].Attributes,
+ PostGroups->Groups[NEANDERTHOL_INDEX].Attributes);
+
+ DbgPrint("Before/after World state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[WORLD_INDEX].Attributes,
+ PostGroups->Groups[WORLD_INDEX].Attributes);
+
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+
+ CompletionStatus = FALSE;
+
+ }
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+ ASSERT(Status == STATUS_SUCCESS);
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Disable optional group //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Disable optional group ... ");
+
+ PreGroups->GroupCount = 77;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PreGroups, // TokenInformation
+ PreGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(PreGroups->GroupCount == GROUP_COUNT );
+ ASSERT(PreGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes);
+ ASSERT(PreGroups->Groups[CHILD_INDEX].Attributes == OptionalGroupAttributes);
+ ASSERT(PreGroups->Groups[NEANDERTHOL_INDEX].Attributes == OptionalGroupAttributes);
+ ASSERT(PreGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes);
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ NewState->GroupCount = 1;
+ NewState->Groups[0].Sid = ChildSid;
+ NewState->Groups[0].Attributes = DisabledGroupAttributes;
+
+ Status = NtAdjustGroupsToken(
+ TokenWithGroups, // TokenHandle
+ FALSE, // ResetToDefault
+ NewState, // NewState (OPTIONAL)
+ 0, // BufferLength
+ NULL, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+
+ PostGroups->GroupCount = 88;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PostGroups, // TokenInformation
+ PostGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ if (Status == STATUS_SUCCESS) {
+
+ //
+ // Check the group values
+ //
+
+ ASSERT( PostGroups->GroupCount == GROUP_COUNT );
+ if ( (PostGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes) &&
+ (PostGroups->Groups[CHILD_INDEX].Attributes == DisabledGroupAttributes) &&
+ (PostGroups->Groups[NEANDERTHOL_INDEX].Attributes == OptionalGroupAttributes) &&
+ (PostGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes)
+ ) {
+
+ DbgPrint("Succeeded. \n");
+
+ } else {
+
+ DbgPrint("********** Failed Value Check ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+
+ DbgPrint("Before/after Flintstone state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[FLINTSTONE_INDEX].Attributes,
+ PostGroups->Groups[FLINTSTONE_INDEX].Attributes);
+
+ DbgPrint("Before/after Child state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[CHILD_INDEX].Attributes,
+ PostGroups->Groups[CHILD_INDEX].Attributes);
+
+ DbgPrint("Before/after Neanderthol state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[NEANDERTHOL_INDEX].Attributes,
+ PostGroups->Groups[NEANDERTHOL_INDEX].Attributes);
+
+ DbgPrint("Before/after World state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[WORLD_INDEX].Attributes,
+ PostGroups->Groups[WORLD_INDEX].Attributes);
+
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+
+ CompletionStatus = FALSE;
+
+ }
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+ ASSERT(Status == STATUS_SUCCESS);
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Disable already disabled group //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Disable already disabled group ... ");
+
+ PreGroups->GroupCount = 77;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PreGroups, // TokenInformation
+ PreGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(PreGroups->GroupCount == GROUP_COUNT );
+ ASSERT(PreGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes);
+ ASSERT(PreGroups->Groups[CHILD_INDEX].Attributes == DisabledGroupAttributes);
+ ASSERT(PreGroups->Groups[NEANDERTHOL_INDEX].Attributes == OptionalGroupAttributes);
+ ASSERT(PreGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes);
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ NewState->GroupCount = 1;
+ NewState->Groups[0].Sid = ChildSid;
+ NewState->Groups[0].Attributes = 0;
+
+ Status = NtAdjustGroupsToken(
+ TokenWithGroups, // TokenHandle
+ FALSE, // ResetToDefault
+ NewState, // NewState (OPTIONAL)
+ 0, // BufferLength
+ NULL, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+
+ PostGroups->GroupCount = 88;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PostGroups, // TokenInformation
+ PostGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ if (Status == STATUS_SUCCESS) {
+
+ //
+ // Check the group values
+ //
+
+ ASSERT( PostGroups->GroupCount == GROUP_COUNT );
+ if ( (PostGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes) &&
+ (PostGroups->Groups[CHILD_INDEX].Attributes == DisabledGroupAttributes) &&
+ (PostGroups->Groups[NEANDERTHOL_INDEX].Attributes == OptionalGroupAttributes) &&
+ (PostGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes)
+ ) {
+
+ DbgPrint("Succeeded. \n");
+
+ } else {
+
+ DbgPrint("********** Failed Value Check ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+
+ DbgPrint("Before/after Flintstone state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[FLINTSTONE_INDEX].Attributes,
+ PostGroups->Groups[FLINTSTONE_INDEX].Attributes);
+
+ DbgPrint("Before/after Child state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[CHILD_INDEX].Attributes,
+ PostGroups->Groups[CHILD_INDEX].Attributes);
+
+ DbgPrint("Before/after Neanderthol state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[NEANDERTHOL_INDEX].Attributes,
+ PostGroups->Groups[NEANDERTHOL_INDEX].Attributes);
+
+ DbgPrint("Before/after World state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[WORLD_INDEX].Attributes,
+ PostGroups->Groups[WORLD_INDEX].Attributes);
+
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+
+ CompletionStatus = FALSE;
+
+ }
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+ ASSERT(Status == STATUS_SUCCESS);
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Enable optional group //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Enable optional group ... ");
+
+ PreGroups->GroupCount = 77;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PreGroups, // TokenInformation
+ PreGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(PreGroups->GroupCount == GROUP_COUNT );
+ ASSERT(PreGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes);
+ ASSERT(PreGroups->Groups[CHILD_INDEX].Attributes == DisabledGroupAttributes);
+ ASSERT(PreGroups->Groups[NEANDERTHOL_INDEX].Attributes == OptionalGroupAttributes);
+ ASSERT(PreGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes);
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ NewState->GroupCount = 1;
+ NewState->Groups[0].Sid = ChildSid;
+ NewState->Groups[0].Attributes = SE_GROUP_ENABLED;
+
+ Status = NtAdjustGroupsToken(
+ TokenWithGroups, // TokenHandle
+ FALSE, // ResetToDefault
+ NewState, // NewState (OPTIONAL)
+ 0, // BufferLength
+ NULL, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+
+ PostGroups->GroupCount = 88;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PostGroups, // TokenInformation
+ PostGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ if (Status == STATUS_SUCCESS) {
+
+ //
+ // Check the group values
+ //
+
+ ASSERT( PostGroups->GroupCount == GROUP_COUNT );
+ if ( (PostGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes) &&
+ (PostGroups->Groups[CHILD_INDEX].Attributes == OptionalGroupAttributes) &&
+ (PostGroups->Groups[NEANDERTHOL_INDEX].Attributes == OptionalGroupAttributes) &&
+ (PostGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes)
+ ) {
+
+ DbgPrint("Succeeded. \n");
+
+ } else {
+
+ DbgPrint("********** Failed Value Check ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+
+ DbgPrint("Before/after Flintstone state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[FLINTSTONE_INDEX].Attributes,
+ PostGroups->Groups[FLINTSTONE_INDEX].Attributes);
+
+ DbgPrint("Before/after Child state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[CHILD_INDEX].Attributes,
+ PostGroups->Groups[CHILD_INDEX].Attributes);
+
+ DbgPrint("Before/after Neanderthol state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[NEANDERTHOL_INDEX].Attributes,
+ PostGroups->Groups[NEANDERTHOL_INDEX].Attributes);
+
+ DbgPrint("Before/after World state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[WORLD_INDEX].Attributes,
+ PostGroups->Groups[WORLD_INDEX].Attributes);
+
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+
+ CompletionStatus = FALSE;
+
+ }
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+ ASSERT(Status == STATUS_SUCCESS);
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Enable already enabled group //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Enable already enabled group ... ");
+
+ PreGroups->GroupCount = 77;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PreGroups, // TokenInformation
+ PreGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(PreGroups->GroupCount == GROUP_COUNT );
+ ASSERT(PreGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes);
+ ASSERT(PreGroups->Groups[CHILD_INDEX].Attributes == OptionalGroupAttributes);
+ ASSERT(PreGroups->Groups[NEANDERTHOL_INDEX].Attributes == OptionalGroupAttributes);
+ ASSERT(PreGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes);
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ NewState->GroupCount = 1;
+ NewState->Groups[0].Sid = ChildSid;
+ NewState->Groups[0].Attributes = SE_GROUP_ENABLED;
+
+ Status = NtAdjustGroupsToken(
+ TokenWithGroups, // TokenHandle
+ FALSE, // ResetToDefault
+ NewState, // NewState (OPTIONAL)
+ 0, // BufferLength
+ NULL, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+
+ PostGroups->GroupCount = 88;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PostGroups, // TokenInformation
+ PostGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ if (Status == STATUS_SUCCESS) {
+
+ //
+ // Check the group values
+ //
+
+ ASSERT( PostGroups->GroupCount == GROUP_COUNT );
+ if ( (PostGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes) &&
+ (PostGroups->Groups[CHILD_INDEX].Attributes == OptionalGroupAttributes) &&
+ (PostGroups->Groups[NEANDERTHOL_INDEX].Attributes == OptionalGroupAttributes) &&
+ (PostGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes)
+ ) {
+
+ DbgPrint("Succeeded. \n");
+
+ } else {
+
+ DbgPrint("********** Failed Value Check ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+
+ DbgPrint("Before/after Flintstone state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[FLINTSTONE_INDEX].Attributes,
+ PostGroups->Groups[FLINTSTONE_INDEX].Attributes);
+
+ DbgPrint("Before/after Child state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[CHILD_INDEX].Attributes,
+ PostGroups->Groups[CHILD_INDEX].Attributes);
+
+ DbgPrint("Before/after Neanderthol state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[NEANDERTHOL_INDEX].Attributes,
+ PostGroups->Groups[NEANDERTHOL_INDEX].Attributes);
+
+ DbgPrint("Before/after World state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[WORLD_INDEX].Attributes,
+ PostGroups->Groups[WORLD_INDEX].Attributes);
+
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+
+ CompletionStatus = FALSE;
+
+ }
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+ ASSERT(Status == STATUS_SUCCESS);
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Disable optional and unknown group //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Disable optional and unknown group ... ");
+
+ PreGroups->GroupCount = 77;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PreGroups, // TokenInformation
+ PreGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(PreGroups->GroupCount == GROUP_COUNT );
+ ASSERT(PreGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes);
+ ASSERT(PreGroups->Groups[CHILD_INDEX].Attributes == OptionalGroupAttributes);
+ ASSERT(PreGroups->Groups[NEANDERTHOL_INDEX].Attributes == OptionalGroupAttributes);
+ ASSERT(PreGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes);
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ NewState->GroupCount = 2;
+ NewState->Groups[0].Sid = ChildSid;
+ NewState->Groups[1].Sid = RubbleSid;
+ NewState->Groups[0].Attributes = DisabledGroupAttributes;
+ NewState->Groups[1].Attributes = DisabledGroupAttributes;
+
+ Status = NtAdjustGroupsToken(
+ TokenWithGroups, // TokenHandle
+ FALSE, // ResetToDefault
+ NewState, // NewState (OPTIONAL)
+ 0, // BufferLength
+ NULL, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+
+ PostGroups->GroupCount = 88;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PostGroups, // TokenInformation
+ PostGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ if (Status == STATUS_NOT_ALL_ASSIGNED) {
+
+ //
+ // Check the group values
+ //
+
+ ASSERT( PostGroups->GroupCount == GROUP_COUNT );
+ if ( (PostGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes) &&
+ (PostGroups->Groups[CHILD_INDEX].Attributes == DisabledGroupAttributes) &&
+ (PostGroups->Groups[NEANDERTHOL_INDEX].Attributes == OptionalGroupAttributes) &&
+ (PostGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes)
+ ) {
+
+ DbgPrint("Succeeded. \n");
+
+ } else {
+
+ DbgPrint("********** Failed Value Check ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+
+ DbgPrint("Before/after Flintstone state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[FLINTSTONE_INDEX].Attributes,
+ PostGroups->Groups[FLINTSTONE_INDEX].Attributes);
+
+ DbgPrint("Before/after Child state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[CHILD_INDEX].Attributes,
+ PostGroups->Groups[CHILD_INDEX].Attributes);
+
+ DbgPrint("Before/after Neanderthol state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[NEANDERTHOL_INDEX].Attributes,
+ PostGroups->Groups[NEANDERTHOL_INDEX].Attributes);
+
+ DbgPrint("Before/after World state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[WORLD_INDEX].Attributes,
+ PostGroups->Groups[WORLD_INDEX].Attributes);
+
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+
+ CompletionStatus = FALSE;
+
+ }
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+ ASSERT(Status == STATUS_NOT_ALL_ASSIGNED);
+
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Enable optional and unknown group //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Enable optional and unknown group ... ");
+
+ PreGroups->GroupCount = 77;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PreGroups, // TokenInformation
+ PreGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(PreGroups->GroupCount == GROUP_COUNT );
+ ASSERT(PreGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes);
+ ASSERT(PreGroups->Groups[CHILD_INDEX].Attributes == DisabledGroupAttributes);
+ ASSERT(PreGroups->Groups[NEANDERTHOL_INDEX].Attributes == OptionalGroupAttributes);
+ ASSERT(PreGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes);
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ NewState->GroupCount = 2;
+ NewState->Groups[0].Sid = ChildSid;
+ NewState->Groups[1].Sid = RubbleSid;
+ NewState->Groups[0].Attributes = OptionalGroupAttributes;
+ NewState->Groups[1].Attributes = OptionalGroupAttributes;
+
+ Status = NtAdjustGroupsToken(
+ TokenWithGroups, // TokenHandle
+ FALSE, // ResetToDefault
+ NewState, // NewState (OPTIONAL)
+ 0, // BufferLength
+ NULL, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+
+ PostGroups->GroupCount = 88;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PostGroups, // TokenInformation
+ PostGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ if (Status == STATUS_NOT_ALL_ASSIGNED) {
+
+ //
+ // Check the group values
+ //
+
+ ASSERT( PostGroups->GroupCount == GROUP_COUNT );
+ if ( (PostGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes) &&
+ (PostGroups->Groups[CHILD_INDEX].Attributes == OptionalGroupAttributes) &&
+ (PostGroups->Groups[NEANDERTHOL_INDEX].Attributes == OptionalGroupAttributes) &&
+ (PostGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes)
+ ) {
+
+ DbgPrint("Succeeded. \n");
+
+ } else {
+
+ DbgPrint("********** Failed Value Check ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+
+ DbgPrint("Before/after Flintstone state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[FLINTSTONE_INDEX].Attributes,
+ PostGroups->Groups[FLINTSTONE_INDEX].Attributes);
+
+ DbgPrint("Before/after Child state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[CHILD_INDEX].Attributes,
+ PostGroups->Groups[CHILD_INDEX].Attributes);
+
+ DbgPrint("Before/after Neanderthol state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[NEANDERTHOL_INDEX].Attributes,
+ PostGroups->Groups[NEANDERTHOL_INDEX].Attributes);
+
+ DbgPrint("Before/after World state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[WORLD_INDEX].Attributes,
+ PostGroups->Groups[WORLD_INDEX].Attributes);
+
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+
+ CompletionStatus = FALSE;
+
+ }
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+ ASSERT(Status == STATUS_NOT_ALL_ASSIGNED);
+
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Disable optional and mandatory group //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Disable optional and mandatory group ... ");
+
+ PreGroups->GroupCount = 77;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PreGroups, // TokenInformation
+ PreGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(PreGroups->GroupCount == GROUP_COUNT );
+ ASSERT(PreGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes);
+ ASSERT(PreGroups->Groups[CHILD_INDEX].Attributes == OptionalGroupAttributes);
+ ASSERT(PreGroups->Groups[NEANDERTHOL_INDEX].Attributes == OptionalGroupAttributes);
+ ASSERT(PreGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes);
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ NewState->GroupCount = 2;
+ NewState->Groups[0].Sid = ChildSid;
+ NewState->Groups[1].Sid = WorldSid;
+ NewState->Groups[0].Attributes = DisabledGroupAttributes;
+ NewState->Groups[1].Attributes = DisabledGroupAttributes;
+
+ Status = NtAdjustGroupsToken(
+ TokenWithGroups, // TokenHandle
+ FALSE, // ResetToDefault
+ NewState, // NewState (OPTIONAL)
+ 0, // BufferLength
+ NULL, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+
+ PostGroups->GroupCount = 88;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PostGroups, // TokenInformation
+ PostGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ if (Status == STATUS_CANT_DISABLE_MANDATORY) {
+
+ //
+ // Check the group values
+ //
+
+ ASSERT( PostGroups->GroupCount == GROUP_COUNT );
+ if ( (PostGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes) &&
+ (PostGroups->Groups[CHILD_INDEX].Attributes == OptionalGroupAttributes) &&
+ (PostGroups->Groups[NEANDERTHOL_INDEX].Attributes == OptionalGroupAttributes) &&
+ (PostGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes)
+ ) {
+
+ DbgPrint("Succeeded. \n");
+
+ } else {
+
+ DbgPrint("********** Failed Value Check ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+
+ DbgPrint("Before/after Flintstone state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[FLINTSTONE_INDEX].Attributes,
+ PostGroups->Groups[FLINTSTONE_INDEX].Attributes);
+
+ DbgPrint("Before/after Child state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[CHILD_INDEX].Attributes,
+ PostGroups->Groups[CHILD_INDEX].Attributes);
+
+ DbgPrint("Before/after Neanderthol state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[NEANDERTHOL_INDEX].Attributes,
+ PostGroups->Groups[NEANDERTHOL_INDEX].Attributes);
+
+ DbgPrint("Before/after World state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[WORLD_INDEX].Attributes,
+ PostGroups->Groups[WORLD_INDEX].Attributes);
+
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+
+ CompletionStatus = FALSE;
+
+ }
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+ ASSERT(Status == STATUS_CANT_DISABLE_MANDATORY);
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Enable optional and mandatory group //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Enable optional and mandatory group ... ");
+
+ NewState->GroupCount = 1;
+ NewState->Groups[0].Sid = ChildSid;
+ NewState->Groups[0].Attributes = DisabledGroupAttributes;
+
+ Status = NtAdjustGroupsToken(
+ TokenWithGroups, // TokenHandle
+ FALSE, // ResetToDefault
+ NewState, // NewState (OPTIONAL)
+ 0, // BufferLength
+ NULL, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+ ASSERT(Status == STATUS_SUCCESS);
+
+ PreGroups->GroupCount = 77;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PreGroups, // TokenInformation
+ PreGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(PreGroups->GroupCount == GROUP_COUNT );
+ ASSERT(PreGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes);
+ ASSERT(PreGroups->Groups[CHILD_INDEX].Attributes == DisabledGroupAttributes);
+ ASSERT(PreGroups->Groups[NEANDERTHOL_INDEX].Attributes == OptionalGroupAttributes);
+ ASSERT(PreGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes);
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ NewState->GroupCount = 2;
+ NewState->Groups[0].Sid = ChildSid;
+ NewState->Groups[1].Sid = WorldSid;
+ NewState->Groups[0].Attributes = OptionalGroupAttributes;
+ NewState->Groups[1].Attributes = OptionalGroupAttributes;
+
+ Status = NtAdjustGroupsToken(
+ TokenWithGroups, // TokenHandle
+ FALSE, // ResetToDefault
+ NewState, // NewState (OPTIONAL)
+ 0, // BufferLength
+ NULL, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+
+ PostGroups->GroupCount = 88;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PostGroups, // TokenInformation
+ PostGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ if (Status == STATUS_SUCCESS) {
+
+ //
+ // Check the group values
+ //
+
+ ASSERT( PostGroups->GroupCount == GROUP_COUNT );
+ if ( (PostGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes) &&
+ (PostGroups->Groups[CHILD_INDEX].Attributes == OptionalGroupAttributes) &&
+ (PostGroups->Groups[NEANDERTHOL_INDEX].Attributes == OptionalGroupAttributes) &&
+ (PostGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes)
+ ) {
+
+ DbgPrint("Succeeded. \n");
+
+ } else {
+
+ DbgPrint("********** Failed Value Check ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+
+ DbgPrint("Before/after Flintstone state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[FLINTSTONE_INDEX].Attributes,
+ PostGroups->Groups[FLINTSTONE_INDEX].Attributes);
+
+ DbgPrint("Before/after Child state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[CHILD_INDEX].Attributes,
+ PostGroups->Groups[CHILD_INDEX].Attributes);
+
+ DbgPrint("Before/after Neanderthol state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[NEANDERTHOL_INDEX].Attributes,
+ PostGroups->Groups[NEANDERTHOL_INDEX].Attributes);
+
+ DbgPrint("Before/after World state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[WORLD_INDEX].Attributes,
+ PostGroups->Groups[WORLD_INDEX].Attributes);
+
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+
+ CompletionStatus = FALSE;
+
+ }
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+ ASSERT(Status == STATUS_SUCCESS);
+
+
+
+//////////////////////////////////////////////////////////////////////
+// //
+// Disable optional group requesting previous state with //
+// insufficient buffer //
+// //
+//////////////////////////////////////////////////////////////////////
+
+
+ DbgPrint("Se: Too small buffer for previous state ... ");
+
+
+ PreGroups->GroupCount = 77;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PreGroups, // TokenInformation
+ PreGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(PreGroups->GroupCount == GROUP_COUNT );
+ ASSERT(PreGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes);
+ ASSERT(PreGroups->Groups[CHILD_INDEX].Attributes == OptionalGroupAttributes);
+ ASSERT(PreGroups->Groups[NEANDERTHOL_INDEX].Attributes == OptionalGroupAttributes);
+ ASSERT(PreGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes);
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ NewState->GroupCount = 1;
+ NewState->Groups[0].Sid = ChildSid;
+ NewState->Groups[0].Attributes = DisabledGroupAttributes;
+
+ Status = NtAdjustGroupsToken(
+ TokenWithGroups, // TokenHandle
+ FALSE, // ResetToDefault
+ NewState, // NewState (OPTIONAL)
+ 0, // BufferLength
+ PreviousState, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ PostGroups->GroupCount = 88;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PostGroups, // TokenInformation
+ PostGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ if (Status == STATUS_BUFFER_TOO_SMALL) {
+
+ //
+ // Check the group values
+ //
+
+ ASSERT( PostGroups->GroupCount == GROUP_COUNT );
+ if ( (PostGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes) &&
+ (PostGroups->Groups[CHILD_INDEX].Attributes == OptionalGroupAttributes) &&
+ (PostGroups->Groups[NEANDERTHOL_INDEX].Attributes == OptionalGroupAttributes) &&
+ (PostGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes)
+ ) {
+
+ DbgPrint("Succeeded. \n");
+
+ } else {
+
+ DbgPrint("********** Failed Value Check ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+
+ DbgPrint("Before/after Flintstone state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[FLINTSTONE_INDEX].Attributes,
+ PostGroups->Groups[FLINTSTONE_INDEX].Attributes);
+
+ DbgPrint("Before/after Child state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[CHILD_INDEX].Attributes,
+ PostGroups->Groups[CHILD_INDEX].Attributes);
+
+ DbgPrint("Before/after Neanderthol state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[NEANDERTHOL_INDEX].Attributes,
+ PostGroups->Groups[NEANDERTHOL_INDEX].Attributes);
+
+ DbgPrint("Before/after World state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[WORLD_INDEX].Attributes,
+ PostGroups->Groups[WORLD_INDEX].Attributes);
+
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+
+ CompletionStatus = FALSE;
+
+ }
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+ ASSERT(Status == STATUS_BUFFER_TOO_SMALL);
+
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Disable optional requesting previous state //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Disable optional, requesting previous state ... ");
+
+ PreGroups->GroupCount = 77;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PreGroups, // TokenInformation
+ PreGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(PreGroups->GroupCount == GROUP_COUNT );
+ ASSERT(PreGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes);
+ ASSERT(PreGroups->Groups[CHILD_INDEX].Attributes == OptionalGroupAttributes);
+ ASSERT(PreGroups->Groups[NEANDERTHOL_INDEX].Attributes == OptionalGroupAttributes);
+ ASSERT(PreGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes);
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ NewState->GroupCount = 2;
+ NewState->Groups[0].Sid = NeandertholSid;
+ NewState->Groups[1].Sid = ChildSid;
+ NewState->Groups[0].Attributes = DisabledGroupAttributes;
+ NewState->Groups[1].Attributes = DisabledGroupAttributes;
+ PreviousState->GroupCount = 99;
+
+ Status = NtAdjustGroupsToken(
+ TokenWithGroups, // TokenHandle
+ FALSE, // ResetToDefault
+ NewState, // NewState (OPTIONAL)
+ PreviousStateBufferLength, // BufferLength
+ PreviousState, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ PostGroups->GroupCount = 88;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PostGroups, // TokenInformation
+ PostGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ if (Status == STATUS_SUCCESS) {
+
+ //
+ // Check the group values
+ //
+
+ ASSERT( PostGroups->GroupCount == GROUP_COUNT );
+ ASSERT( PreviousState->GroupCount == 2 );
+ if ( (PostGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes) &&
+ (PostGroups->Groups[CHILD_INDEX].Attributes == DisabledGroupAttributes) &&
+ (PostGroups->Groups[NEANDERTHOL_INDEX].Attributes == DisabledGroupAttributes) &&
+ (PostGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes) &&
+ (PreviousState->Groups[0].Attributes == OptionalGroupAttributes) &&
+ (PreviousState->Groups[1].Attributes == OptionalGroupAttributes)
+ ) {
+
+ DbgPrint("Succeeded. \n");
+
+ } else {
+
+ DbgPrint("********** Failed Value Check ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+
+ DbgPrint("Before/after Flintstone state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[FLINTSTONE_INDEX].Attributes,
+ PostGroups->Groups[FLINTSTONE_INDEX].Attributes);
+
+ DbgPrint("Before/after Child state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[CHILD_INDEX].Attributes,
+ PostGroups->Groups[CHILD_INDEX].Attributes);
+
+ DbgPrint("Before/after Neanderthol state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[NEANDERTHOL_INDEX].Attributes,
+ PostGroups->Groups[NEANDERTHOL_INDEX].Attributes);
+
+ DbgPrint("Before/after World state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[WORLD_INDEX].Attributes,
+ PostGroups->Groups[WORLD_INDEX].Attributes);
+
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+
+ DbgPrint("Previous count is: 0x%lx \n", PreviousState->GroupCount);
+ DbgPrint("Previous state of group 0 is: 0x%lx \n",
+ PreviousState->Groups[0].Attributes);
+ DbgPrint("Previous state of group 1 is: 0x%lx \n",
+ PreviousState->Groups[1].Attributes);
+
+
+ CompletionStatus = FALSE;
+
+ }
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+ ASSERT(Status == STATUS_SUCCESS);
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Return group to previous state //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Return to previous state ... ");
+
+ PreGroups->GroupCount = 77;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PreGroups, // TokenInformation
+ PreGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(PreGroups->GroupCount == GROUP_COUNT );
+ ASSERT(PreGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes);
+ ASSERT(PreGroups->Groups[CHILD_INDEX].Attributes == DisabledGroupAttributes);
+ ASSERT(PreGroups->Groups[NEANDERTHOL_INDEX].Attributes == DisabledGroupAttributes);
+ ASSERT(PreGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes);
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ Status = NtAdjustGroupsToken(
+ TokenWithGroups, // TokenHandle
+ FALSE, // ResetToDefault
+ PreviousState, // NewState (OPTIONAL)
+ PreviousStateBufferLength, // BufferLength
+ PreviousState, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ PostGroups->GroupCount = 88;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PostGroups, // TokenInformation
+ PostGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ if (Status == STATUS_SUCCESS) {
+
+ //
+ // Check the group values
+ //
+
+ ASSERT( PostGroups->GroupCount == GROUP_COUNT );
+ if ( (PostGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes) &&
+ (PostGroups->Groups[CHILD_INDEX].Attributes == OptionalGroupAttributes) &&
+ (PostGroups->Groups[NEANDERTHOL_INDEX].Attributes == OptionalGroupAttributes) &&
+ (PostGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes) &&
+ (PreviousState->Groups[0].Attributes == DisabledGroupAttributes) &&
+ (PreviousState->Groups[1].Attributes == DisabledGroupAttributes)
+ ) {
+
+ DbgPrint("Succeeded. \n");
+
+ } else {
+
+ DbgPrint("********** Failed Value Check ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+
+ DbgPrint("Before/after Flintstone state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[FLINTSTONE_INDEX].Attributes,
+ PostGroups->Groups[FLINTSTONE_INDEX].Attributes);
+
+ DbgPrint("Before/after Child state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[CHILD_INDEX].Attributes,
+ PostGroups->Groups[CHILD_INDEX].Attributes);
+
+ DbgPrint("Before/after Neanderthol state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[NEANDERTHOL_INDEX].Attributes,
+ PostGroups->Groups[NEANDERTHOL_INDEX].Attributes);
+
+ DbgPrint("Before/after World state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[WORLD_INDEX].Attributes,
+ PostGroups->Groups[WORLD_INDEX].Attributes);
+
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+
+ CompletionStatus = FALSE;
+
+ }
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+ ASSERT(Status == STATUS_SUCCESS);
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Return to previous state again //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Return to previous state again ... ");
+
+ PreGroups->GroupCount = 77;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PreGroups, // TokenInformation
+ PreGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(PreGroups->GroupCount == GROUP_COUNT );
+ ASSERT(PreGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes);
+ ASSERT(PreGroups->Groups[CHILD_INDEX].Attributes == OptionalGroupAttributes);
+ ASSERT(PreGroups->Groups[NEANDERTHOL_INDEX].Attributes == OptionalGroupAttributes);
+ ASSERT(PreGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes);
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ Status = NtAdjustGroupsToken(
+ TokenWithGroups, // TokenHandle
+ FALSE, // ResetToDefault
+ PreviousState, // NewState (OPTIONAL)
+ PreviousStateBufferLength, // BufferLength
+ PreviousState, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ PostGroups->GroupCount = 88;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PostGroups, // TokenInformation
+ PostGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ if (Status == STATUS_SUCCESS) {
+
+ //
+ // Check the group values
+ //
+
+ ASSERT( PostGroups->GroupCount == GROUP_COUNT );
+ if ( (PostGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes) &&
+ (PostGroups->Groups[CHILD_INDEX].Attributes == DisabledGroupAttributes) &&
+ (PostGroups->Groups[NEANDERTHOL_INDEX].Attributes == DisabledGroupAttributes) &&
+ (PostGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes) &&
+ (PreviousState->Groups[0].Attributes == OptionalGroupAttributes) &&
+ (PreviousState->Groups[1].Attributes == OptionalGroupAttributes)
+ ) {
+
+ DbgPrint("Succeeded. \n");
+
+ } else {
+
+ DbgPrint("********** Failed Value Check ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+
+ DbgPrint("Before/after Flintstone state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[FLINTSTONE_INDEX].Attributes,
+ PostGroups->Groups[FLINTSTONE_INDEX].Attributes);
+
+ DbgPrint("Before/after Child state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[CHILD_INDEX].Attributes,
+ PostGroups->Groups[CHILD_INDEX].Attributes);
+
+ DbgPrint("Before/after Neanderthol state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[NEANDERTHOL_INDEX].Attributes,
+ PostGroups->Groups[NEANDERTHOL_INDEX].Attributes);
+
+ DbgPrint("Before/after World state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[WORLD_INDEX].Attributes,
+ PostGroups->Groups[WORLD_INDEX].Attributes);
+
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+
+ CompletionStatus = FALSE;
+
+ }
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+ ASSERT(Status == STATUS_SUCCESS);
+
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Return to default state (capture previous state) //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Return to default state (w/previous state) ... ");
+
+ PreGroups->GroupCount = 77;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PreGroups, // TokenInformation
+ PreGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(PreGroups->GroupCount == GROUP_COUNT );
+ ASSERT(PreGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes);
+ ASSERT(PreGroups->Groups[CHILD_INDEX].Attributes == DisabledGroupAttributes);
+ ASSERT(PreGroups->Groups[NEANDERTHOL_INDEX].Attributes == DisabledGroupAttributes);
+ ASSERT(PreGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes);
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ Status = NtAdjustGroupsToken(
+ TokenWithGroups, // TokenHandle
+ TRUE, // ResetToDefault
+ NULL, // NewState (OPTIONAL)
+ PreviousStateBufferLength, // BufferLength
+ PreviousState, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) return length: 0x%lx \n", ReturnLength);
+#endif //TOKEN_DEBUG
+
+ PostGroups->GroupCount = 88;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PostGroups, // TokenInformation
+ PostGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ if (Status == STATUS_SUCCESS) {
+
+ //
+ // Check the group values
+ //
+
+ ASSERT( PostGroups->GroupCount == GROUP_COUNT );
+ if ( (PostGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes) &&
+ (PostGroups->Groups[CHILD_INDEX].Attributes == OptionalGroupAttributes) &&
+ (PostGroups->Groups[NEANDERTHOL_INDEX].Attributes == OptionalGroupAttributes) &&
+ (PostGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes) &&
+ (PreviousState->Groups[0].Attributes == DisabledGroupAttributes) &&
+ (PreviousState->Groups[1].Attributes == DisabledGroupAttributes)
+ ) {
+
+ DbgPrint("Succeeded. \n");
+
+ } else {
+
+ DbgPrint("********** Failed Value Check ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+
+ DbgPrint("Before/after Flintstone state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[FLINTSTONE_INDEX].Attributes,
+ PostGroups->Groups[FLINTSTONE_INDEX].Attributes);
+
+ DbgPrint("Before/after Child state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[CHILD_INDEX].Attributes,
+ PostGroups->Groups[CHILD_INDEX].Attributes);
+
+ DbgPrint("Before/after Neanderthol state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[NEANDERTHOL_INDEX].Attributes,
+ PostGroups->Groups[NEANDERTHOL_INDEX].Attributes);
+
+ DbgPrint("Before/after World state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[WORLD_INDEX].Attributes,
+ PostGroups->Groups[WORLD_INDEX].Attributes);
+
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+
+ CompletionStatus = FALSE;
+
+ }
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+ ASSERT(Status == STATUS_SUCCESS);
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Return to default state (don't capture previous state) //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Return to default state (no previous state) ... ");
+
+ Status = NtAdjustGroupsToken(
+ TokenWithGroups, // TokenHandle
+ FALSE, // ResetToDefault
+ PreviousState, // NewState (OPTIONAL)
+ 0, // BufferLength
+ NULL, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+
+ ASSERT(Status == STATUS_SUCCESS);
+
+ PreGroups->GroupCount = 77;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PreGroups, // TokenInformation
+ PreGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(PreGroups->GroupCount == GROUP_COUNT );
+ ASSERT(PreGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes);
+ ASSERT(PreGroups->Groups[CHILD_INDEX].Attributes == DisabledGroupAttributes);
+ ASSERT(PreGroups->Groups[NEANDERTHOL_INDEX].Attributes == DisabledGroupAttributes);
+ ASSERT(PreGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes);
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ Status = NtAdjustGroupsToken(
+ TokenWithGroups, // TokenHandle
+ TRUE, // ResetToDefault
+ NULL, // NewState (OPTIONAL)
+ 0, // BufferLength
+ NULL, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+
+ PostGroups->GroupCount = 88;
+ IgnoreStatus = NtQueryInformationToken(
+ TokenWithGroups, // TokenHandle
+ TokenGroups, // TokenInformationClass
+ PostGroups, // TokenInformation
+ PostGroupsLength, // TokenInformationLength
+ &IgnoreReturnLength // ReturnLength
+ );
+#ifdef TOKEN_DEBUG
+DbgPrint("\n (debug) ignore return length: 0x%lx \n", IgnoreReturnLength);
+#endif //TOKEN_DEBUG
+
+ ASSERT(NT_SUCCESS(IgnoreStatus) );
+
+ if (Status == STATUS_SUCCESS) {
+
+ //
+ // Check the group values
+ //
+
+ ASSERT( PostGroups->GroupCount == GROUP_COUNT );
+ if ( (PostGroups->Groups[FLINTSTONE_INDEX].Attributes == OwnerGroupAttributes) &&
+ (PostGroups->Groups[CHILD_INDEX].Attributes == OptionalGroupAttributes) &&
+ (PostGroups->Groups[NEANDERTHOL_INDEX].Attributes == OptionalGroupAttributes) &&
+ (PostGroups->Groups[WORLD_INDEX].Attributes == NormalGroupAttributes)
+ ) {
+
+ DbgPrint("Succeeded. \n");
+
+ } else {
+
+ DbgPrint("********** Failed Value Check ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+
+ DbgPrint("Before/after Flintstone state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[FLINTSTONE_INDEX].Attributes,
+ PostGroups->Groups[FLINTSTONE_INDEX].Attributes);
+
+ DbgPrint("Before/after Child state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[CHILD_INDEX].Attributes,
+ PostGroups->Groups[CHILD_INDEX].Attributes);
+
+ DbgPrint("Before/after Neanderthol state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[NEANDERTHOL_INDEX].Attributes,
+ PostGroups->Groups[NEANDERTHOL_INDEX].Attributes);
+
+ DbgPrint("Before/after World state: 0x%lx / 0x%lx \n",
+ PreGroups->Groups[WORLD_INDEX].Attributes,
+ PostGroups->Groups[WORLD_INDEX].Attributes);
+
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+
+ CompletionStatus = FALSE;
+
+ }
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ DbgPrint("Return Length is: 0x%lx \n", ReturnLength);
+ CompletionStatus = FALSE;
+
+ }
+
+ ASSERT(Status == STATUS_SUCCESS);
+
+
+
+
+ ////////////////////////////////////////////////////////////////
+ // //
+ // Done with test //
+ // //
+ ////////////////////////////////////////////////////////////////
+
+
+
+ TstDeallocatePool( PreviousState, PreviousStateBufferLength );
+ TstDeallocatePool( NewState, NewStateBufferLength );
+ TstDeallocatePool( PreGroups, PreGroupsLength );
+ TstDeallocatePool( PostGroups, PostGroupsLength );
+
+
+ return CompletionStatus;
+}
+
+
+////////////////////////////////////////////////////////////////
+// //
+// Compare duplicate to original token & display test results //
+// //
+////////////////////////////////////////////////////////////////
+
+BOOLEAN
+TestpCompareDuplicateToken(
+ IN NTSTATUS Status,
+ IN HANDLE OldToken,
+ IN OBJECT_ATTRIBUTES NewAttributes,
+ IN BOOLEAN EffectiveOnly,
+ IN TOKEN_TYPE NewType,
+ IN HANDLE NewToken
+ )
+
+{
+ BOOLEAN CompletionStatus = TRUE;
+
+ ULONG OldReturnLength;
+ ULONG NewReturnLength;
+
+ PTOKEN_USER OldUserId;
+ PTOKEN_USER NewUserId;
+
+ TOKEN_SOURCE OldSource;
+ TOKEN_SOURCE NewSource;
+
+ TOKEN_STATISTICS OldStatistics;
+ TOKEN_STATISTICS NewStatistics;
+
+ BOOLEAN SomeNotCompared = FALSE;
+
+
+ //
+ // Appease the compiler Gods
+ //
+ NewAttributes = NewAttributes;
+ NewType = NewType;
+ EffectiveOnly = EffectiveOnly;
+
+
+ //
+ // If the status isn't success, don't bother comparing the tokens
+ //
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ return FALSE;
+ }
+
+ //
+ // Compare the user IDs
+ //
+
+ Status = NtQueryInformationToken(
+ OldToken, // Handle
+ TokenUser, // TokenInformationClass
+ OldUserId, // TokenInformation
+ 0, // TokenInformationLength
+ &OldReturnLength // ReturnLength
+ ); ASSERT(Status == STATUS_BUFFER_TOO_SMALL);
+ OldUserId = (PTOKEN_USER)TstAllocatePool( PagedPool, OldReturnLength );
+
+ Status = NtQueryInformationToken(
+ OldToken, // Handle
+ TokenUser, // TokenInformationClass
+ OldUserId, // TokenInformation
+ OldReturnLength, // TokenInformationLength
+ &OldReturnLength // ReturnLength
+ ); ASSERT(NT_SUCCESS(Status));
+
+
+ Status = NtQueryInformationToken(
+ NewToken, // Handle
+ TokenUser, // TokenInformationClass
+ NewUserId, // TokenInformation
+ 0, // TokenInformationLength
+ &NewReturnLength // ReturnLength
+ ); ASSERT(Status == STATUS_BUFFER_TOO_SMALL);
+
+ NewUserId = (PTOKEN_USER)TstAllocatePool( PagedPool, NewReturnLength );
+
+ Status = NtQueryInformationToken(
+ NewToken, // Handle
+ TokenUser, // TokenInformationClass
+ NewUserId, // TokenInformation
+ NewReturnLength, // TokenInformationLength
+ &NewReturnLength // ReturnLength
+ ); ASSERT(NT_SUCCESS(Status));
+
+
+ if ( !RtlEqualSid(OldUserId->User.Sid, NewUserId->User.Sid) ) {
+
+ if (CompletionStatus) {
+ DbgPrint("*** Failed Value Comparison ***\n");
+ }
+ DbgPrint("User IDs don't match.\n");
+ CompletionStatus = FALSE;
+ }
+
+ TstDeallocatePool( OldUserId, OldReturnLength );
+ TstDeallocatePool( NewUserId, NewReturnLength );
+
+
+ //
+ // Check the token statistics
+ //
+
+ if (CompletionStatus) {
+ Status = NtQueryInformationToken(
+ OldToken, // Handle
+ TokenStatistics, // TokenInformationClass
+ &OldStatistics, // TokenInformation
+ (ULONG)sizeof(TOKEN_STATISTICS), // TokenInformationLength
+ &OldReturnLength // ReturnLength
+ ); ASSERT(NT_SUCCESS(Status));
+
+ Status = NtQueryInformationToken(
+ NewToken, // Handle
+ TokenStatistics, // TokenInformationClass
+ &NewStatistics, // TokenInformation
+ (ULONG)sizeof(TOKEN_STATISTICS), // TokenInformationLength
+ &NewReturnLength // ReturnLength
+ ); ASSERT(NT_SUCCESS(Status));
+ //
+ // Must have:
+ // Different TokenId values
+ // Same authenticationId value
+ // Same ExpirationTime
+ // Same token type
+ // Same ImpersonationLevel (if correct token type)
+ // Same DynamicCharged & DynamicAvailable
+ //
+ // GroupCount and PrivilegeCount are deferred to the group and
+ // privilege comparison due to the difficulty involved with
+ // taking EffectiveOnly into account.
+ //
+ // The new token must have a ModifiedId that is the same as the
+ // original.
+ //
+
+ //
+ // Token ID
+ //
+
+ if ( (OldStatistics.TokenId.HighPart ==
+ NewStatistics.TokenId.HighPart) &&
+ (OldStatistics.TokenId.LowPart ==
+ NewStatistics.TokenId.LowPart) ) {
+
+ DbgPrint("*** Failed ***\n");
+ DbgPrint(" TokenIds are equal.\n");
+ DbgPrint(" Old TokenId is: (0x%xl, 0x%xl)\n",
+ OldStatistics.TokenId.HighPart,
+ OldStatistics.TokenId.LowPart);
+ DbgPrint(" New TokenId is: (0x%xl, 0x%xl)\n",
+ NewStatistics.TokenId.HighPart,
+ NewStatistics.TokenId.LowPart);
+ DbgPrint(" ");
+ CompletionStatus = FALSE;
+ }
+
+
+ //
+ // Authentication ID
+ //
+
+ if ( !RtlEqualLuid(&OldStatistics.AuthenticationId,
+ &NewStatistics.AuthenticationId) ) {
+
+ DbgPrint("*** Failed ***\n");
+ DbgPrint(" AuthenticationIds are not equal.\n");
+ DbgPrint("Original Authentication ID is: ");
+ TestpPrintLuid(OldStatistics.AuthenticationId);
+ DbgPrint("\n");
+ DbgPrint("New Authentication ID is: ");
+ TestpPrintLuid(NewStatistics.AuthenticationId);
+ DbgPrint("\n");
+ DbgPrint(" ");
+ CompletionStatus = FALSE;
+ }
+
+ //
+ // ExpirationTime
+ //
+
+ if ( (OldStatistics.ExpirationTime.HighPart !=
+ NewStatistics.ExpirationTime.HighPart) ||
+ (OldStatistics.ExpirationTime.LowPart !=
+ NewStatistics.ExpirationTime.LowPart) ) {
+
+ DbgPrint("*** Failed ***\n");
+ DbgPrint(" ExpirationTimes differ.\n");
+ DbgPrint(" ");
+ CompletionStatus = FALSE;
+ }
+
+ //
+ // TokenType
+ //
+
+ if ( OldStatistics.TokenType != NewStatistics.TokenType ) {
+
+ DbgPrint("*** Failed ***\n");
+ DbgPrint(" Token types are different.\n");
+ DbgPrint(" Old token type is: 0x%lx \n", OldStatistics.TokenType );
+ DbgPrint(" New token type is: 0x%lx \n", NewStatistics.TokenType );
+ DbgPrint(" ");
+ CompletionStatus = FALSE;
+ }
+
+ //
+ // ImpersonationLevel
+ //
+
+ if (NewStatistics.TokenType = TokenImpersonation) {
+ if ( OldStatistics.ImpersonationLevel !=
+ NewStatistics.ImpersonationLevel ) {
+
+ DbgPrint("*** Failed ***\n");
+ DbgPrint(" Impersonation levels are different.\n");
+ DbgPrint(" Old impersonation level is: 0x%lx \n",
+ OldStatistics.ImpersonationLevel );
+ DbgPrint(" New impersonation level is: 0x%lx \n",
+ NewStatistics.ImpersonationLevel );
+ DbgPrint(" ");
+ CompletionStatus = FALSE;
+ }
+ }
+
+ //
+ // DynamicCharged
+ //
+
+ if ( OldStatistics.DynamicCharged != NewStatistics.DynamicCharged ) {
+
+ DbgPrint("*** Failed ***\n");
+ DbgPrint(" DynamicCharges are different.\n");
+ DbgPrint(" Old value is: 0x%lx \n", OldStatistics.DynamicCharged );
+ DbgPrint(" New value is: 0x%lx \n", NewStatistics.DynamicCharged );
+ DbgPrint(" ");
+ CompletionStatus = FALSE;
+ }
+
+ //
+ // DynamicAvailable
+ //
+
+ if ( OldStatistics.DynamicAvailable != NewStatistics.DynamicAvailable ) {
+
+ DbgPrint("*** Failed ***\n");
+ DbgPrint(" DynamicAvailable are different.\n");
+ DbgPrint(" Old value is: 0x%lx \n", OldStatistics.DynamicAvailable );
+ DbgPrint(" New value is: 0x%lx \n", NewStatistics.DynamicAvailable );
+ DbgPrint(" ");
+ CompletionStatus = FALSE;
+ }
+
+
+ //
+ // ModifiedId
+ //
+
+ if ( (NewStatistics.ModifiedId.HighPart !=
+ OldStatistics.ModifiedId.HighPart) ||
+ (NewStatistics.ModifiedId.LowPart !=
+ OldStatistics.ModifiedId.LowPart) ) {
+
+ DbgPrint("*** Failed ***\n");
+ DbgPrint(" ModifiedIds different.\n");
+ DbgPrint(" Old ModifiedId is: (0x%xl, 0x%xl)\n",
+ OldStatistics.ModifiedId.HighPart,
+ OldStatistics.ModifiedId.LowPart);
+ DbgPrint(" New ModifiedId is: (0x%xl, 0x%xl)\n",
+ NewStatistics.ModifiedId.HighPart,
+ NewStatistics.ModifiedId.LowPart);
+ DbgPrint(" ");
+ CompletionStatus = FALSE;
+ }
+
+ }
+
+ //
+ // Compare the group IDs
+ //
+
+ SomeNotCompared = TRUE;
+
+ //
+ // Compare the privileges
+ //
+
+ SomeNotCompared = TRUE;
+
+ //
+ // Compare the owner IDs
+ //
+
+ SomeNotCompared = TRUE;
+
+ //
+ // Compare the primary group IDs
+ //
+
+ SomeNotCompared = TRUE;
+
+ //
+ // Compare the default dacls
+ //
+
+ SomeNotCompared = TRUE;
+
+ //
+ // Compare the token source
+ //
+
+ if (CompletionStatus) {
+ Status = NtQueryInformationToken(
+ OldToken, // Handle
+ TokenSource, // TokenInformationClass
+ &OldSource, // TokenInformation
+ (ULONG)sizeof(TOKEN_SOURCE), // TokenInformationLength
+ &OldReturnLength // ReturnLength
+ ); ASSERT(NT_SUCCESS(Status));
+
+ Status = NtQueryInformationToken(
+ NewToken, // Handle
+ TokenSource, // TokenInformationClass
+ &NewSource, // TokenInformation
+ (ULONG)sizeof(TOKEN_SOURCE), // TokenInformationLength
+ &NewReturnLength // ReturnLength
+ ); ASSERT(NT_SUCCESS(Status));
+
+ if ( (OldSource.SourceIdentifier.HighPart ==
+ NewSource.SourceIdentifier.HighPart) &&
+ (OldSource.SourceIdentifier.LowPart ==
+ NewSource.SourceIdentifier.LowPart) ) {
+ if ( (OldSource.SourceName[0] != NewSource.SourceName[0]) ||
+ (OldSource.SourceName[1] != NewSource.SourceName[1]) ||
+ (OldSource.SourceName[2] != NewSource.SourceName[2]) ||
+ (OldSource.SourceName[3] != NewSource.SourceName[3]) ||
+ (OldSource.SourceName[4] != NewSource.SourceName[4]) ||
+ (OldSource.SourceName[5] != NewSource.SourceName[5]) ||
+ (OldSource.SourceName[6] != NewSource.SourceName[6]) ||
+ (OldSource.SourceName[7] != NewSource.SourceName[7]) ) {
+
+ DbgPrint("*** Failed Value Comparison ***\n");
+ DbgPrint(" SourceName changed.\n");
+ CompletionStatus = FALSE;
+
+ }
+ } else {
+
+ DbgPrint("*** Failed Value Comparison ***\n");
+ DbgPrint(" SourceIdentifier changed.\n");
+ DbgPrint(" Old SourceIdentifier is: (0x%xl, 0x%xl)\n",
+ OldSource.SourceIdentifier.HighPart,
+ OldSource.SourceIdentifier.LowPart);
+ DbgPrint(" New SourceIdentifier is: (0x%xl, 0x%xl)\n",
+ NewSource.SourceIdentifier.HighPart,
+ NewSource.SourceIdentifier.LowPart);
+ CompletionStatus = FALSE;
+
+ }
+ }
+
+ ////////////////////////////////// Done /////////////////////////
+
+
+ if (SomeNotCompared) {
+ DbgPrint("Incomplete\n");
+ DbgPrint(" Some fields not yet compared ... ");
+ }
+
+ if (CompletionStatus) {
+
+ DbgPrint("Succeeded. \n");
+ }
+
+ return CompletionStatus;
+}
+
+
+////////////////////////////////////////////////////////////////
+// //
+// Duplicate Token Test //
+// //
+////////////////////////////////////////////////////////////////
+
+BOOLEAN
+TestTokenDuplicate()
+{
+ BOOLEAN CompletionStatus = TRUE;
+
+ BOOLEAN EffectiveOnly;
+ TOKEN_TYPE NewType;
+ HANDLE NewToken;
+
+ OBJECT_ATTRIBUTES NewAttributes;
+
+ SECURITY_QUALITY_OF_SERVICE ImpersonationLevel;
+ SECURITY_QUALITY_OF_SERVICE IdentificationLevel;
+
+
+
+ DbgPrint("\n");
+
+ //
+ // Initialize variables
+ //
+
+ ImpersonationLevel.Length = (ULONG)sizeof(SECURITY_QUALITY_OF_SERVICE);
+ ImpersonationLevel.ImpersonationLevel = SecurityImpersonation;
+ ImpersonationLevel.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ ImpersonationLevel.EffectiveOnly = FALSE;
+
+ IdentificationLevel.Length = (ULONG)sizeof(SECURITY_QUALITY_OF_SERVICE);
+ IdentificationLevel.ImpersonationLevel = SecurityImpersonation;
+ IdentificationLevel.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ IdentificationLevel.EffectiveOnly = FALSE;
+
+
+ InitializeObjectAttributes(
+ &NewAttributes,
+ NULL,
+ OBJ_INHERIT,
+ NULL,
+ NULL
+ );
+
+
+
+ ////////////////////////////////////////////////////////////
+ // //
+ // Duplicate the simple token //
+ // //
+ ////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Duplicate primary token ... ");
+
+ EffectiveOnly = FALSE;
+ NewType = TokenImpersonation;
+ NewAttributes.SecurityQualityOfService = &ImpersonationLevel;
+
+ Status = NtDuplicateToken(
+ SimpleToken, // ExistingTokenHandle
+ 0, // DesiredAccess
+ &NewAttributes, // ObjectAttributes
+ EffectiveOnly, // EffectiveOnly
+ NewType, // TokenType
+ &NewToken // NewTokenHandle
+ );
+
+ if (NT_SUCCESS(Status)) {
+ DbgPrint("Succeeded.\n");
+ Status = NtClose( NewToken ); ASSERT(NT_SUCCESS(NewToken));
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ return FALSE;
+ }
+
+
+ ////////////////////////////////////////////////////////////
+ // //
+ // Duplicate the full impersonation token //
+ // //
+ ////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Duplicate full impersonation token ... ");
+
+ EffectiveOnly = FALSE;
+ NewType = TokenImpersonation;
+ NewAttributes.SecurityQualityOfService = &ImpersonationLevel;
+
+ Status = NtDuplicateToken(
+ ImpersonationToken, // ExistingTokenHandle
+ 0, // DesiredAccess
+ &NewAttributes, // ObjectAttributes
+ EffectiveOnly, // EffectiveOnly
+ NewType, // TokenType
+ &NewToken // NewTokenHandle
+ );
+ //
+ // Check to see that the duplicate is really a duplicate of
+ // the original and display the test results.
+ //
+
+ if (!TestpCompareDuplicateToken( Status,
+ ImpersonationToken,
+ NewAttributes,
+ EffectiveOnly,
+ NewType,
+ NewToken ) ) {
+
+ CompletionStatus = FALSE;
+ }
+
+ if (NT_SUCCESS(Status)) {
+
+ Status = NtClose( NewToken );
+
+ ASSERT(NT_SUCCESS(Status));
+ }
+
+
+ ////////////////////////////////////////////////////////////
+ // //
+ // Duplicate the full token, effective only //
+ // //
+ ////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Duplicate full token, effective only ... ");
+
+ EffectiveOnly = TRUE;
+ NewType = TokenImpersonation;
+ NewAttributes.SecurityQualityOfService = &ImpersonationLevel;
+
+ Status = NtDuplicateToken(
+ ImpersonationToken, // ExistingTokenHandle
+ 0, // DesiredAccess
+ &NewAttributes, // ObjectAttributes
+ EffectiveOnly, // EffectiveOnly
+ NewType, // TokenType
+ &NewToken // NewTokenHandle
+ );
+ //
+ // Check to see that the duplicate is really a duplicate of
+ // the original and display the test results.
+ //
+
+ if (!TestpCompareDuplicateToken( Status,
+ ImpersonationToken,
+ NewAttributes,
+ EffectiveOnly,
+ NewType,
+ NewToken ) ) {
+
+ CompletionStatus = FALSE;
+ }
+
+ if (NT_SUCCESS(Status)) {
+
+ Status = NtClose( NewToken );
+
+ ASSERT(NT_SUCCESS(Status));
+ }
+
+
+
+
+
+
+
+
+
+
+ return CompletionStatus;
+}
+
+////////////////////////////////////////////////////////////////
+// //
+// Assign Primary Token Test //
+// //
+////////////////////////////////////////////////////////////////
+
+BOOLEAN
+TestTokenAssignPrimary()
+{
+ BOOLEAN CompletionStatus = TRUE;
+ ULONG ReturnLength;
+
+ TOKEN_STATISTICS OriginalTokenStatistics;
+ TOKEN_STATISTICS NewTokenStatistics;
+ TOKEN_STATISTICS AssignedTokenStatistics;
+
+
+ TOKEN_USER UserId;
+ TOKEN_PRIMARY_GROUP PrimaryGroup;
+ PTOKEN_GROUPS GroupIds;
+ PTOKEN_PRIVILEGES Privileges;
+ TOKEN_DEFAULT_DACL DefaultDacl;
+ TOKEN_OWNER Owner;
+
+ PROCESS_ACCESS_TOKEN PrimaryTokenInfo;
+
+ DbgPrint("\n");
+
+
+ ////////////////////////////////////////////////////////////
+ // //
+ // Assign a valid primary token //
+ // //
+ ////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Assign new primary token ... ");
+
+ //
+ // Get information about the current token
+ //
+
+ Status = NtOpenProcessToken(
+ NtCurrentProcess(),
+ TOKEN_ALL_ACCESS,
+ &ProcessToken
+ );
+ ASSERT (NT_SUCCESS(Status));
+
+ Status = NtQueryInformationToken(
+ ProcessToken, // Handle
+ TokenStatistics, // TokenInformationClass
+ &OriginalTokenStatistics, // TokenInformation
+ sizeof(TOKEN_STATISTICS), // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+ ASSERT(NT_SUCCESS(Status));
+
+
+
+
+ //
+ // Create a token with default DACL for use
+ //
+
+ GroupIds = (PTOKEN_GROUPS)TstAllocatePool( PagedPool,
+ GROUP_IDS_LENGTH
+ );
+
+ Privileges = (PTOKEN_PRIVILEGES)TstAllocatePool( PagedPool,
+ PRIVILEGES_LENGTH
+ );
+
+ DefaultDacl.DefaultDacl = (PACL)TstAllocatePool( PagedPool,
+ DEFAULT_DACL_LENGTH
+ );
+
+ GroupIds->GroupCount = GROUP_COUNT;
+
+ GroupIds->Groups[FLINTSTONE_INDEX].Sid = FlintstoneSid;
+ GroupIds->Groups[CHILD_INDEX].Sid = ChildSid;
+ GroupIds->Groups[SYSTEM_INDEX].Sid = LocalSystemSid;
+ GroupIds->Groups[WORLD_INDEX].Sid = WorldSid;
+
+ GroupIds->Groups[FLINTSTONE_INDEX].Attributes = OwnerGroupAttributes;
+ GroupIds->Groups[CHILD_INDEX].Attributes = OptionalGroupAttributes;
+ GroupIds->Groups[SYSTEM_INDEX].Attributes = OptionalGroupAttributes;
+ GroupIds->Groups[WORLD_INDEX].Attributes = NormalGroupAttributes;
+
+ UserId.User.Sid = PebblesSid;
+ UserId.User.Attributes = 0;
+
+ Owner.Owner = FlintstoneSid;
+
+ Privileges->PrivilegeCount = PRIVILEGE_COUNT;
+
+ Privileges->Privileges[UNSOLICITED_INDEX].Luid = UnsolicitedInputPrivilege;
+ Privileges->Privileges[SECURITY_INDEX].Luid = SecurityPrivilege;
+ Privileges->Privileges[ASSIGN_PRIMARY_INDEX].Luid = AssignPrimaryTokenPrivilege;
+ Privileges->Privileges[UNSOLICITED_INDEX].Attributes = 0;
+ Privileges->Privileges[SECURITY_INDEX].Attributes = 0;
+ Privileges->Privileges[ASSIGN_PRIMARY_INDEX].Attributes = SE_PRIVILEGE_ENABLED;
+
+ PrimaryGroup.PrimaryGroup = FlintstoneSid;
+
+ Status = RtlCreateAcl( DefaultDacl.DefaultDacl, DEFAULT_DACL_LENGTH, ACL_REVISION);
+
+ ASSERT(NT_SUCCESS(Status) );
+
+ Status = NtCreateToken(
+ &Token, // Handle
+ (TOKEN_ALL_ACCESS), // DesiredAccess
+ &PrimaryTokenAttributes, // ObjectAttributes
+ TokenPrimary, // TokenType
+ &SystemAuthenticationId, // Authentication LUID
+ &NoExpiration, // Expiration Time
+ &UserId, // Owner ID
+ GroupIds, // Group IDs
+ Privileges, // Privileges
+ &Owner, // Owner
+ &PrimaryGroup, // Primary Group
+ &DefaultDacl, // Default Dacl
+ &TestSource // TokenSource
+ );
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // Make sure key data is different than what is already on the process.
+ //
+
+ Status = NtQueryInformationToken(
+ Token, // Handle
+ TokenStatistics, // TokenInformationClass
+ &NewTokenStatistics, // TokenInformation
+ sizeof(TOKEN_STATISTICS), // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+ ASSERT(NT_SUCCESS(Status));
+
+ ASSERT( (OriginalTokenStatistics.TokenId.HighPart !=
+ NewTokenStatistics.TokenId.HighPart) ||
+ (OriginalTokenStatistics.TokenId.LowPart !=
+ NewTokenStatistics.TokenId.LowPart) );
+
+
+
+ //
+ // Assign the new token
+ //
+
+ PrimaryTokenInfo.Token = Token;
+ PrimaryTokenInfo.Thread = NtCurrentThread();
+ Status = NtSetInformationProcess(
+ NtCurrentProcess(),
+ ProcessAccessToken,
+ (PVOID)&PrimaryTokenInfo,
+ (ULONG)sizeof(PROCESS_ACCESS_TOKEN)
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ CompletionStatus = FALSE;
+
+ } else {
+
+ Status = NtClose( Token );
+ ASSERT(NT_SUCCESS(Status));
+
+
+ //
+ // Get information about the assigned token
+ //
+
+ Status = NtOpenProcessToken(
+ NtCurrentProcess(),
+ TOKEN_QUERY | TOKEN_QUERY_SOURCE,
+ &Token
+ );
+ ASSERT (NT_SUCCESS(Status));
+
+ Status = NtQueryInformationToken(
+ Token, // Handle
+ TokenStatistics, // TokenInformationClass
+ &AssignedTokenStatistics, // TokenInformation
+ sizeof(TOKEN_STATISTICS), // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = NtClose( Token );
+ ASSERT(NT_SUCCESS(Status));
+
+
+ //
+ // Information about assigned token and the new token
+ // should be the same
+ //
+
+ ASSERT(AssignedTokenStatistics.TokenType == TokenPrimary);
+
+ if ( (NewTokenStatistics.TokenId.HighPart ==
+ AssignedTokenStatistics.TokenId.HighPart) &&
+ (NewTokenStatistics.TokenId.LowPart ==
+ AssignedTokenStatistics.TokenId.LowPart) ) {
+
+ DbgPrint("Succeeded.\n");
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Token ID mismatch.\n");
+ DbgPrint("New token ID is: (0x%lx, 0x%lx) \n",
+ NewTokenStatistics.TokenId.HighPart,
+ NewTokenStatistics.TokenId.LowPart);
+ DbgPrint("Assigned token ID is: (0x%lx, 0x%lx) \n",
+ AssignedTokenStatistics.TokenId.HighPart,
+ AssignedTokenStatistics.TokenId.LowPart);
+ CompletionStatus = FALSE;
+
+ }
+ }
+
+ //
+ // Change back to the original token
+ //
+
+ PrimaryTokenInfo.Token = ProcessToken;
+ PrimaryTokenInfo.Thread = NtCurrentThread();
+ Status = NtSetInformationProcess(
+ NtCurrentProcess(),
+ ProcessAccessToken,
+ (PVOID)&PrimaryTokenInfo,
+ (ULONG)sizeof(PROCESS_ACCESS_TOKEN)
+ );
+
+ ASSERT(NT_SUCCESS(Status));
+ Status = NtClose( ProcessToken );
+ ASSERT(NT_SUCCESS(Status));
+
+
+ ////////////////////////////////////////////////////////////
+ // //
+ // Attempt to assign an impersonation token as primary //
+ // //
+ ////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Assign impersonation token as primary ... ");
+
+
+ //
+ // Create an impersonation token
+ //
+ GroupIds->GroupCount = GROUP_COUNT;
+
+ GroupIds->Groups[FLINTSTONE_INDEX].Sid = FlintstoneSid;
+ GroupIds->Groups[CHILD_INDEX].Sid = ChildSid;
+ GroupIds->Groups[NEANDERTHOL_INDEX].Sid = NeandertholSid;
+ GroupIds->Groups[WORLD_INDEX].Sid = WorldSid;
+
+ GroupIds->Groups[FLINTSTONE_INDEX].Attributes = OwnerGroupAttributes;
+ GroupIds->Groups[CHILD_INDEX].Attributes = OptionalGroupAttributes;
+ GroupIds->Groups[NEANDERTHOL_INDEX].Attributes = OptionalGroupAttributes;
+ GroupIds->Groups[WORLD_INDEX].Attributes = NormalGroupAttributes;
+
+ UserId.User.Sid = PebblesSid;
+ UserId.User.Attributes = 0;
+
+ Owner.Owner = FlintstoneSid;
+
+ Privileges->PrivilegeCount = PRIVILEGE_COUNT;
+
+ Privileges->Privileges[UNSOLICITED_INDEX].Luid = UnsolicitedInputPrivilege;
+ Privileges->Privileges[SECURITY_INDEX].Luid = SecurityPrivilege;
+ Privileges->Privileges[ASSIGN_PRIMARY_INDEX].Luid = AssignPrimaryTokenPrivilege;
+ Privileges->Privileges[UNSOLICITED_INDEX].Attributes = 0;
+ Privileges->Privileges[SECURITY_INDEX].Attributes = 0;
+ Privileges->Privileges[ASSIGN_PRIMARY_INDEX].Attributes = SE_PRIVILEGE_ENABLED;
+
+ PrimaryGroup.PrimaryGroup = FlintstoneSid;
+
+ Status = RtlCreateAcl( DefaultDacl.DefaultDacl, DEFAULT_DACL_LENGTH, ACL_REVISION);
+
+ ASSERT(NT_SUCCESS(Status) );
+
+ Status = NtCreateToken(
+ &Token, // Handle
+ (TOKEN_ALL_ACCESS), // DesiredAccess
+ &ImpersonationTokenAttributes, // ObjectAttributes
+ TokenImpersonation, // TokenType
+ &OriginalAuthenticationId, // Authentication LUID
+ &NoExpiration, // Expiration Time
+ &UserId, // Owner ID
+ GroupIds, // Group IDs
+ Privileges, // Privileges
+ &Owner, // Owner
+ &PrimaryGroup, // Primary Group
+ &DefaultDacl, // Default Dacl
+ &TestSource // TokenSource
+ );
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // Assign the new token
+ //
+
+ PrimaryTokenInfo.Token = Token;
+ PrimaryTokenInfo.Thread = NtCurrentThread();
+ Status = NtSetInformationProcess(
+ NtCurrentProcess(),
+ ProcessAccessToken,
+ (PVOID)&PrimaryTokenInfo,
+ (ULONG)sizeof(PROCESS_ACCESS_TOKEN)
+ );
+
+ if (Status == STATUS_BAD_TOKEN_TYPE) {
+
+ DbgPrint("Succeeded.\n");
+
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ CompletionStatus = FALSE;
+
+ }
+
+ Status = NtClose( Token );
+ ASSERT(NT_SUCCESS(Status));
+
+
+ return CompletionStatus;
+}
+
+////////////////////////////////////////////////////////////////
+// //
+// Impersonation Test (with open test) //
+// //
+////////////////////////////////////////////////////////////////
+
+BOOLEAN
+TestTokenImpersonation()
+{
+ BOOLEAN CompletionStatus = TRUE;
+
+ HANDLE OpenedToken;
+ HANDLE NewToken;
+
+ DbgPrint("\n");
+
+
+ ////////////////////////////////////////////////////////////
+ // //
+ // Terminate impersonation using NtSetInformationThread() //
+ // //
+ ////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Revert to self (specify NULL handle) ... ");
+
+ NewToken = NULL;
+ Status = NtSetInformationThread(
+ NtCurrentThread(),
+ ThreadImpersonationToken,
+ (PVOID)&NewToken,
+ (ULONG)sizeof(HANDLE)
+ );
+
+ if (NT_SUCCESS(Status)) {
+ DbgPrint("Succeeded.\n");
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ CompletionStatus = FALSE;
+
+ }
+
+
+ ////////////////////////////////////////////////////////////
+ // //
+ // Attempt to assign a primary token as an impersonation //
+ // token. //
+ // //
+ ////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Assigning primary token as impersonation token ... ");
+
+ NewToken = TokenWithGroups;
+ Status = NtSetInformationThread(
+ NtCurrentThread(),
+ ThreadImpersonationToken,
+ (PVOID)&NewToken,
+ (ULONG)sizeof(HANDLE)
+ );
+
+ if (Status == STATUS_BAD_TOKEN_TYPE) {
+ DbgPrint("Succeeded.\n");
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ CompletionStatus = FALSE;
+
+ }
+
+
+ ////////////////////////////////////////////////////////////
+ // //
+ // Assign a valid impersonation token //
+ // //
+ ////////////////////////////////////////////////////////////
+
+ DbgPrint("Se: Assign valid impersonation token ... ");
+
+ NewToken = ImpersonationToken;
+ Status = NtSetInformationThread(
+ NtCurrentThread(),
+ ThreadImpersonationToken,
+ (PVOID)&NewToken,
+ (ULONG)sizeof(HANDLE)
+ );
+
+ if (NT_SUCCESS(Status)) {
+ DbgPrint("Succeeded.\n");
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ CompletionStatus = FALSE;
+
+ }
+
+
+ ////////////////////////////////////////////////////////////
+ // //
+ // Open the impersonation token //
+ // //
+ ////////////////////////////////////////////////////////////
+
+
+ DbgPrint("Se: Open an impersonation token ... ");
+
+ Status = NtOpenThreadToken(
+ NtCurrentThread(),
+ TOKEN_ALL_ACCESS,
+ TRUE,
+ &OpenedToken
+ );
+
+ if (NT_SUCCESS(Status)) {
+ DbgPrint("Succeeded.\n");
+ Status = NtClose( OpenedToken );
+ ASSERT(NT_SUCCESS(Status));
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ CompletionStatus = FALSE;
+
+ }
+
+
+
+ ////////////////////////////////////////////////////////////
+ // //
+ // Open a non-existent impersonation token //
+ // //
+ ////////////////////////////////////////////////////////////
+
+
+ DbgPrint("Se: Open a non-existent impersonation token ... ");
+
+ //
+ // Clear any existing impersonation token.
+ //
+
+ NewToken = NULL;
+ Status = NtSetInformationThread(
+ NtCurrentThread(),
+ ThreadImpersonationToken,
+ (PVOID)&NewToken,
+ (ULONG)sizeof(HANDLE)
+ ); ASSERT(NT_SUCCESS(Status));
+
+ Status = NtOpenThreadToken(
+ NtCurrentThread(),
+ TOKEN_ALL_ACCESS,
+ TRUE,
+ &OpenedToken
+ );
+
+ if (Status == STATUS_NO_TOKEN) {
+ DbgPrint("Succeeded.\n");
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ CompletionStatus = FALSE;
+
+ }
+
+
+ ////////////////////////////////////////////////////////////
+ // //
+ // Open an anonymous impersonation token //
+ // //
+ ////////////////////////////////////////////////////////////
+
+
+ DbgPrint("Se: Open an anonymous impersonation token ... ");
+
+ //
+ // Assign an anonymous impersonation token
+ //
+
+ NewToken = AnonymousToken;
+ Status = NtSetInformationThread(
+ ThreadHandle,
+ ThreadImpersonationToken,
+ (PVOID)&NewToken,
+ (ULONG)sizeof(HANDLE)
+ ); ASSERT(NT_SUCCESS(Status));
+
+
+ Status = NtOpenThreadToken(
+ ThreadHandle,
+ TOKEN_ALL_ACCESS,
+ TRUE,
+ &OpenedToken
+ );
+
+ if (Status == STATUS_CANT_OPEN_ANONYMOUS) {
+ DbgPrint("Succeeded.\n");
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ CompletionStatus = FALSE;
+
+ }
+
+
+ ////////////////////////////////////////////////////////////
+ // //
+ // Change the impersonation of a thread //
+ // //
+ ////////////////////////////////////////////////////////////
+
+
+ DbgPrint("Se: Change the impersonation token ... ");
+
+ NewToken = NULL;
+ Status = NtSetInformationThread(
+ ThreadHandle,
+ ThreadImpersonationToken,
+ (PVOID)&NewToken,
+ (ULONG)sizeof(HANDLE)
+ ); ASSERT(NT_SUCCESS(Status));
+
+ NewToken = AnonymousToken;
+ Status = NtSetInformationThread(
+ ThreadHandle,
+ ThreadImpersonationToken,
+ (PVOID)&NewToken,
+ (ULONG)sizeof(HANDLE)
+ ); ASSERT(NT_SUCCESS(Status));
+
+ NewToken = ImpersonationToken;
+ Status = NtSetInformationThread(
+ ThreadHandle,
+ ThreadImpersonationToken,
+ (PVOID)&NewToken,
+ (ULONG)sizeof(HANDLE)
+ );
+
+ if (NT_SUCCESS(Status)) {
+ DbgPrint("Succeeded.\n");
+ } else {
+
+ DbgPrint("********** Failed ************\n");
+ DbgPrint("Status is: 0x%lx \n", Status);
+ CompletionStatus = FALSE;
+
+ }
+
+ Status = NtTerminateThread(
+ ThreadHandle,
+ (NTSTATUS)0
+ );
+
+ ASSERT(NT_SUCCESS(Status));
+
+ return CompletionStatus;
+}
+
+////////////////////////////////////////////////////////////////
+// //
+// Main Program Entry //
+// //
+////////////////////////////////////////////////////////////////
+
+BOOLEAN
+CTToken() // Common Test for Token object
+{
+ BOOLEAN Result = TRUE;
+
+ DbgPrint("Se: Initialization...");
+ TestTokenInitialize();
+
+ DbgPrint("Se: Token Creation Test... Test");
+ if (!TestTokenCreate()) { Result = FALSE; }
+
+ DbgPrint("Se: Token Open Test (with primary token)... Test");
+ if (!TestTokenOpenPrimary()) { Result = FALSE; }
+
+ DbgPrint("Se: Token Query Test... Test");
+ if (!TestTokenQuery()) { Result = FALSE; }
+
+ DbgPrint("Se: Token Set Test... Test");
+ if (!TestTokenSet()) { Result = FALSE; }
+
+ DbgPrint("Se: Token Adjust Privileges Test... Test");
+ if (!TestTokenAdjustPrivileges()) {Result = FALSE; }
+
+ DbgPrint("Se: Token Adjust Group Test... Test");
+ if (!TestTokenAdjustGroups()) { Result = FALSE; }
+
+ DbgPrint("Se: Token Duplication Test... Test");
+ if (!TestTokenDuplicate()) { Result = FALSE; }
+
+ DbgPrint("Se: Primary Token Assignment Test... Test");
+ if (!TestTokenAssignPrimary()) { Result = FALSE; }
+
+ DbgPrint("Se: Impersonation Test (and impersonation open)... Test");
+ if (!TestTokenImpersonation()) { Result = FALSE; }
+
+
+ DbgPrint("\n");
+ DbgPrint("\n");
+ DbgPrint(" ********************\n");
+ DbgPrint(" ** **\n");
+ if (Result) {
+ DbgPrint("Se: ** Test Succeeded **\n");
+ } else {
+ DbgPrint("Se: ** Test Failed **\n");
+ }
+
+ DbgPrint(" ** **\n");
+ DbgPrint(" ********************\n");
+ DbgPrint("\n");
+ DbgPrint("\n");
+
+ return Result;
+}
+
diff --git a/private/ntos/se/dirs b/private/ntos/se/dirs
new file mode 100644
index 000000000..a2a38f0fd
--- /dev/null
+++ b/private/ntos/se/dirs
@@ -0,0 +1,24 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+DIRS=up
+
+OPTIONAL_DIRS=mp
diff --git a/private/ntos/se/dumpuser.c b/private/ntos/se/dumpuser.c
new file mode 100644
index 000000000..e35dac938
--- /dev/null
+++ b/private/ntos/se/dumpuser.c
@@ -0,0 +1,324 @@
+
+/* Defines for Jim */
+// #define LONGNAMES
+#define SECPKG "[Tmpv1_0]"
+
+#ifdef LONGNAMES
+#define SERVOP "DOMAIN_SERVER_OPERATORS "
+#define ACCTOP "DOMAIN_ACCOUNT_OPERATORS "
+#define COMMOP "DOMAIN_COMM_OPERATORS "
+#define PRINTOP "DOMAIN_PRINT_OPERATORS "
+#define DISKOP "DOMAIN_DISK_OPERATORS "
+#define AUDITOP "DOMAIN_AUDIT_OPERATORS "
+#define GADMINS "DOMAIN_ADMIN "
+#define GUSERS "DOMAIN_USERS "
+#define GGUESTS "DOMAIN_GUESTS "
+#else
+#define SERVOP "D_SERVER "
+#define ACCTOP "D_ACCOUN "
+#define COMMOP "D_COMM_O "
+#define PRINTOP "D_PRINT_ "
+#define DISKOP "D_DISK_O "
+#define AUDITOP "D_AUDIT_ "
+#define GADMINS "D_ADMIN "
+#define GUSERS "D_USERS "
+#define GGUESTS "D_GUESTS "
+#endif
+
+
+#define INCL_DOSMEMMGR
+#include <os2.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <netcons.h>
+#include <neterr.h>
+#include <netlib.h>
+#include <uascache.h>
+#include <access.h>
+#include <permit.h>
+#include "ssitools.h"
+
+char FAR * buf1;
+char FAR * buf2;
+char FAR * GroupCache;
+char path[256];
+char userbuf[MAX_USER_SIZE];
+unsigned short Handle;
+struct _ahdr FAR * Header;
+
+void InitMemStuff(void);
+void loadit(void);
+void showentries(void);
+void doargs(int, char**);
+
+struct my_group {
+ char name[GNLEN + 1];
+ char pad;
+ unsigned short gid;
+ unsigned long serial;
+};
+
+static int
+UserHashVal(uname, domain)
+const char far *uname;
+int domain;
+{
+ register unsigned val=0;
+ register unsigned char c;
+
+ while ((c= (unsigned char) *uname++))
+ val += toupper(c);
+
+ return (int) (val % domain);
+}
+
+unsigned
+dread(unsigned handle, unsigned long pos, char far * buffer, unsigned length)
+{
+ unsigned short err, bread;
+ unsigned long newpos;
+
+ err = DosChgFilePtr(handle, pos, FILE_BEGIN, &newpos);
+ if (err)
+ return err;
+ if (newpos != pos)
+ return NERR_ACFFileIOFail;
+ err = DosRead( handle, buffer, length, &bread);
+ if (err)
+ return err;
+ if (bread != length)
+ return NERR_ACFFileIOFail;
+ return 0;
+}
+
+unsigned read_object(unsigned handle, unsigned long pos, char FAR * buf)
+{
+ unsigned err;
+ struct disk_obj_hdr FAR * dobj;
+
+ err = dread(handle, pos, buf, sizeof(struct disk_obj_hdr));
+ if (err)
+ return err;
+ dobj = (struct disk_obj_hdr FAR *) buf;
+ return dread(handle, pos, buf, dobj->do_numblocks * 64);
+}
+
+void InitMemStuff()
+{
+ unsigned short err;
+ unsigned short sel, action;
+
+ err = DosAllocSeg(0x8000, &sel, 0);
+ if (err) {
+ printf("Could not alloc memory, error %d\n", err);
+ exit(1);
+ }
+ buf1 = MAKEP(sel, 0);
+ err = DosAllocSeg(0x8000, &sel, 0);
+ if (err) {
+ printf("Could not alloc memory, error %d\n", err);
+ exit(1);
+ }
+ buf2 = MAKEP(sel, 0);
+ err = DosAllocSeg(1024, &sel, 0);
+ if (err) {
+ printf("Could not alloc memory, error %d\n", err);
+ exit(1);
+ }
+ Header = MAKEP(sel, 0);
+ if (err = DosAllocSeg(0x8000, &sel, 0)) {
+ printf("Could not alloc memory, error %d\n", err);
+ exit(1);
+ }
+ GroupCache = MAKEP(sel, 0);
+ err = DosOpen(path, &Handle, &action, 0L, 0, FILE_OPEN,
+ OPEN_ACCESS_READONLY | OPEN_SHARE_DENYNONE, 0L);
+ if (err) {
+ printf("Error opening %s, code %d\n", path, err);
+ exit(1);
+ }
+ err = DosRead(Handle, Header, 512, &action);
+ if (err || action != 512) {
+ printf("Error reading from file, code %d\n", err);
+ exit(1);
+ }
+ printf("Total users, %d\n", Header->num_users);
+}
+
+void loadit()
+{
+ unsigned short err;
+ unsigned short action;
+ unsigned short i;
+ unsigned long pos;
+ struct diskuserhash FAR *diskhashentry;
+ struct userhash FAR *userhashentry;
+ struct _grouprec FAR * grec;
+ struct my_group FAR * mygroup;
+
+ err = DosChgFilePtr(Handle, 512L, FILE_BEGIN, &pos);
+ err = DosRead(Handle, buf1, sizeof(struct _grouprec) * MAXGROUP, &action);
+ if (err || action != sizeof(struct _grouprec) * MAXGROUP) {
+ printf("Error reading from file, %d\n", err);
+ exit(1);
+ }
+ mygroup = (struct my_group FAR *) GroupCache;
+ grec = (struct _grouprec FAR *) buf1;
+ for (i = 0; i < MAXGROUP ; i++, grec++, mygroup++ ) {
+ if (grec->name[0] != REC_EMPTY && grec->name[0] != REC_DELETE) {
+/* if (strcmpf(grec->name, "ADMINS") == 0) {
+ strcpyf(mygroup->name, GADMIN);
+ else if (strcmpf(grec->name, "USERS") == 0)
+ strcpyf(mygroup->name, GUSERS);
+ else if (strcmpf(grec->name, "GUESTS") == 0)
+ strcpyf(mygroup->name, GGUESTS);
+ else */
+ strncpyf(mygroup->name, grec->name, GNLEN);
+ mygroup->gid = UserHashVal(mygroup->name, MAXGROUP);
+ mygroup->serial = grec->serial;
+ } else
+ mygroup->name[0] = '\0';
+ }
+
+ err = DosChgFilePtr(Handle, (unsigned long) HASH_TBL_OFFSET, FILE_BEGIN, &pos);
+ diskhashentry = (struct diskuserhash FAR *) buf1;
+ if (err = DosRead (Handle, diskhashentry, HASH_TBL_SIZE, &action)) {
+ printf("Could not read hash table, error %d\n", err);
+ exit(1);
+ }
+
+ if (action != HASH_TBL_SIZE) {
+ printf("Could not read hash table\n");
+ exit(1);
+ }
+ /*
+ * Copy disk user hash table into memory
+ */
+ userhashentry = (struct userhash FAR *) buf2;
+ for (i = 0; i < USER_HASH_ENTRIES; i++) {
+ userhashentry->uh_disk = diskhashentry->dh_disk;
+ userhashentry->uh_serial = diskhashentry->dh_serial;
+ userhashentry->uh_cache = NULL;
+ userhashentry++;
+ diskhashentry++;
+ }
+}
+
+void printgroups()
+{
+ struct my_group FAR * mygroup = (struct my_group FAR *) GroupCache;
+ unsigned i;
+ unsigned long relid;
+
+ printf("\t// Groups\n");
+ for (i = 0; i< MAXGROUP ; i++, mygroup++ ) {
+ if (mygroup->name[0]) {
+ relid = ((mygroup->gid | GROUPIDMASK) + (mygroup->serial << 16)) ^ 0x80000000L;
+ printf("\tGroup = %ld %Fs\n", relid, mygroup->name);
+ }
+ }
+}
+void showentries()
+{
+ unsigned i, j, k;
+ unsigned err;
+ unsigned long pos, oprights;
+ signed long relativeid;
+ unsigned usercount = 0;
+ struct userhash FAR * entry = (struct userhash FAR *) buf2;
+ struct user_object FAR * uo = (struct user_object FAR *) userbuf;
+ struct my_group FAR * mygroup = (struct my_group FAR *) GroupCache;
+ unsigned char FAR * gmask;
+ unsigned char test;
+ char groupnames[256];
+
+ printf("\n\n\t// Users\n");
+ for (i = 0; i < USER_HASH_ENTRIES ; i++ ) {
+ pos = entry->uh_disk;
+ while (pos) {
+ if (err = read_object(Handle, pos, userbuf)) {
+ printf("Failed reading object, code %d\n", err);
+ exit(1);
+ }
+ relativeid = ( ((uo->uo_record.user.uc0_guid.guid_serial) << 16) +
+ (uo->uo_record.user.uc0_guid.guid_uid) ) ^ 0x80000000;
+ gmask = uo->uo_record.user.uc0_groups;
+ groupnames[0] = '\0';
+ for (j = 0; j < 32 ; j++ ) {
+ test = gmask[j];
+ k = 0;
+ while (test) {
+ if (test & 1) {
+ strcatf(groupnames, mygroup[j * 8 + k].name);
+ strcatf(groupnames, " ");
+ }
+ test >>= 1;
+ k++;
+ }
+ }
+
+ oprights = uo->uo_record.user.uc0_auth_flags;
+ if (oprights & AF_OP_PRINT)
+ strcat(groupnames, PRINTOP);
+ if (oprights & AF_OP_COMM)
+ strcat(groupnames, COMMOP);
+ if (oprights & AF_OP_SERVER)
+ strcat(groupnames, SERVOP);
+ if (oprights & AF_OP_ACCOUNTS)
+ strcat(groupnames, ACCTOP);
+
+ printf("\tUser = %ld %Fs %Fs\n", relativeid,
+ (char FAR *) uo->uo_record.name,
+ (char FAR *) groupnames);
+ usercount++;
+ pos = uo->uo_header.do_next;
+ }
+ entry++;
+ }
+ if (usercount != Header->num_users) {
+ printf("Huh? Didn't find all the users (found %d)\n", usercount);
+ }
+}
+
+void banner(unsigned longnames)
+{
+
+ printf(SECPKG);
+ printf("\n\n\t//\n\t// Account Setup information\n");
+ printf("\t// This file produced from the LAN Manager 2.0 UAS\n");
+ printf("\t// %s\n", path);
+
+ printf("\tGroup = 501 %s\n", GADMINS);
+ printf("\tGroup = 502 %s\n", GUSERS);
+ printf("\tGroup = 503 %s\n", GGUESTS);
+ printf("\tGroup = 504 %s\n", ACCTOP);
+ printf("\tGroup = 505 %s\n", SERVOP);
+ printf("\tGroup = 506 %s\n", PRINTOP);
+ printf("\tGroup = 507 %s\n", COMMOP);
+ printf("\tGroup = 508 %s\n", DISKOP);
+ printf("\tGroup = 509 %s\n", AUDITOP);
+}
+
+void doargs(int argc, char **argv)
+{
+ if (argc != 2 || *argv[1] == '?') {
+ printf("usage: %s path\\net.acc\n", argv[0]);
+ printf("\tlists all the users/groups and their hash/serial numbers for a UAS\n");
+ exit(0);
+ }
+ strcpy(path, argv[1]);
+}
+
+main (int argc, char *argv[])
+{
+ printf(SIGNON);
+ doargs(argc, argv);
+ InitMemStuff();
+ loadit();
+
+ banner(0);
+
+ printgroups();
+ showentries();
+}
diff --git a/private/ntos/se/mp/makefile b/private/ntos/se/mp/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/ntos/se/mp/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/ntos/se/mp/sources b/private/ntos/se/mp/sources
new file mode 100644
index 000000000..dbeb18d62
--- /dev/null
+++ b/private/ntos/se/mp/sources
@@ -0,0 +1,29 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+NT_UP=0
+
+TARGETPATH=..\..\mpobj
+
+!include ..\sources.inc
diff --git a/private/ntos/se/nls/ntaudit.mc b/private/ntos/se/nls/ntaudit.mc
new file mode 100644
index 000000000..f49a33821
--- /dev/null
+++ b/private/ntos/se/nls/ntaudit.mc
@@ -0,0 +1,153 @@
+;/*++ BUILD Version: 0001 // Increment this if a change has global effects
+;
+;Copyright (c) 1991 Microsoft Corporation
+;
+;Module Name:
+;
+; ntaudit.mc
+;
+;Abstract:
+;
+; Constant definitions for the NT Audit Event Messages.
+;
+;Author:
+;
+; Jim Kelly (JimK) 30-Mar-1992
+;
+;Revision History:
+;
+;Notes:
+;
+; The .h and .res forms of this file are generated from the .mc
+; form of the file (private\ntos\se\nls\ntaudit.mc). Please make
+; all changes to the .mc form of the file.
+;
+;
+;
+;--*/
+;
+;#ifndef _NTAUDIT_
+;#define _NTAUDIT_
+;
+;/*lint -e767 */ // Don't complain about different definitions // winnt
+
+
+MessageIdTypedef=ULONG
+
+SeverityNames=(None=0x0)
+
+FacilityNames=(None=0x0)
+
+
+
+MessageId=0x0000
+ Language=English
+Unused message ID
+.
+;// Message ID 0 is unused - just used to flush out the diagram
+
+
+;
+;/////////////////////////////////////////////////////////////////////////
+;// //
+;// Logon Messages Follow //
+;// //
+;// //
+;/////////////////////////////////////////////////////////////////////////
+;
+
+;////////////////////////////////////////////////
+;//
+;// Module Catagory: SE_ADT_SUCCESSFUL_LOGON
+;//
+;// Event Type: Successful Logon
+;//
+;// Parameter Strings:
+;//
+;// String1 - User name
+;//
+;// String2 - LogonSessionLuid.HighPart (32-bit value)
+;//
+;// String3 - LogonSessionLuid.LowPart (32-bit value)
+;//
+
+MessageId=0x0001
+ SymbolicName=SE_EVENTID_SUCCESSFUL_LOGON
+ Language=English
+Successful Logon -
+ User: %S
+ Logon session ID: {%ld,%ld}.
+.
+
+
+;////////////////////////////////////////////////
+;//
+;// Module Catagory: SE_ADT_UNSUCCESSFUL_LOGON
+;//
+;// Event Type: Unknown user/password logon attempt
+;//
+;// Parameter Strings:
+;//
+;// String1 - User name
+;//
+;//
+;//
+
+MessageId=0x0002
+ SymbolicName=SE_EVENTID_UNKNOWN_USER_OR_PWD
+ Language=English
+Failed Logon -
+ User: %S
+ Reason: Unknown user name or password.
+.
+
+
+;////////////////////////////////////////////////
+;//
+;// Module Catagory: SE_ADT_UNSUCCESSFUL_LOGON
+;//
+;// Event Type: Time restriction logon failure
+;//
+;// Parameter Strings:
+;//
+;// String1 - User name
+;//
+;//
+;//
+
+MessageId=0x0003
+ SymbolicName=SE_EVENTID_ACCOUNT_TIME_RESTR
+ Language=English
+Failed Logon -
+ User: %S
+ Reason: Account time restriction violation.
+.
+
+
+;////////////////////////////////////////////////
+;//
+;// Module Catagory: SE_ADT_UNSUCCESSFUL_LOGON
+;//
+;// Event Type: Account Disabled
+;//
+;// Parameter Strings:
+;//
+;// String1 - User name
+;//
+;//
+;//
+
+MessageId=0x0004
+ SymbolicName=SE_EVENTID_ACCOUNT_DISABLED
+ Language=English
+Failed Logon -
+ User: %S
+ Reason: Account Disabled.
+.
+
+
+
+;/*lint +e767 */ // Resume checking for different macro definitions // winnt
+;
+;
+;#endif // _NTAUDIT_
diff --git a/private/ntos/se/privileg.c b/private/ntos/se/privileg.c
new file mode 100644
index 000000000..2257b2967
--- /dev/null
+++ b/private/ntos/se/privileg.c
@@ -0,0 +1,574 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ Privileg.c
+
+Abstract:
+
+ This Module implements the privilege check procedures.
+
+Author:
+
+ Robert Reichel (robertre) 26-Nov-90
+
+Environment:
+
+ Kernel Mode
+
+Revision History:
+
+--*/
+
+#include "tokenp.h"
+
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE,NtPrivilegeCheck)
+#pragma alloc_text(PAGE,SeCheckPrivilegedObject)
+#pragma alloc_text(PAGE,SepPrivilegeCheck)
+#pragma alloc_text(PAGE,SePrivilegeCheck)
+#pragma alloc_text(PAGE,SeSinglePrivilegeCheck)
+#endif
+
+
+BOOLEAN
+SepPrivilegeCheck(
+ IN PTOKEN Token,
+ IN OUT PLUID_AND_ATTRIBUTES RequiredPrivileges,
+ IN ULONG RequiredPrivilegeCount,
+ IN ULONG PrivilegeSetControl,
+ IN KPROCESSOR_MODE PreviousMode
+ )
+/*++
+
+Routine Description:
+
+ Worker routine for SePrivilegeCheck
+
+Arguments:
+
+ Token - The user's effective token.
+
+ RequiredPrivileges - A privilege set describing the required
+ privileges. The UsedForAccess bits will be set in any privilege
+ that is actually used (usually all of them).
+
+ RequiredPrivilegeCount - How many privileges are in the
+ RequiredPrivileges set.
+
+ PrivilegeSetControl - Describes how many privileges are required.
+
+ PreviousMode - The previous processor mode.
+
+Return Value:
+
+ Returns TRUE if requested privileges are granted, FALSE otherwise.
+
+--*/
+
+{
+ PLUID_AND_ATTRIBUTES CurrentRequiredPrivilege;
+ PLUID_AND_ATTRIBUTES CurrentTokenPrivilege;
+
+ BOOLEAN RequiredAll;
+
+ ULONG TokenPrivilegeCount;
+ ULONG MatchCount = 0;
+
+ ULONG i;
+ ULONG j;
+
+ PAGED_CODE();
+
+ //
+ // Take care of kernel callers first
+ //
+
+ if (PreviousMode == KernelMode) {
+
+ return(TRUE);
+
+ }
+
+ SepAcquireTokenReadLock( Token );
+
+ TokenPrivilegeCount = Token->PrivilegeCount;
+
+ //
+ // Save whether we require ALL of them or ANY
+ //
+
+ RequiredAll = (BOOLEAN)(PrivilegeSetControl & PRIVILEGE_SET_ALL_NECESSARY);
+
+ for ( i = 0 , CurrentRequiredPrivilege = RequiredPrivileges ;
+ i < RequiredPrivilegeCount ;
+ i++, CurrentRequiredPrivilege++ ) {
+
+ for ( j = 0, CurrentTokenPrivilege = Token->Privileges;
+ j < TokenPrivilegeCount ;
+ j++, CurrentTokenPrivilege++ ) {
+
+ if ((CurrentTokenPrivilege->Attributes & SE_PRIVILEGE_ENABLED) &&
+ (RtlEqualLuid(&CurrentTokenPrivilege->Luid,
+ &CurrentRequiredPrivilege->Luid))
+ ) {
+
+ CurrentRequiredPrivilege->Attributes |=
+ SE_PRIVILEGE_USED_FOR_ACCESS;
+ MatchCount++;
+ break; // start looking for next one
+ }
+
+ }
+
+ }
+
+ SepReleaseTokenReadLock( Token );
+
+ //
+ // If we wanted ANY and didn't get any, return failure.
+ //
+
+ if (!RequiredAll && (MatchCount == 0)) {
+
+ return (FALSE);
+
+ }
+
+ //
+ // If we wanted ALL and didn't get all, return failure.
+ //
+
+ if (RequiredAll && (MatchCount != RequiredPrivilegeCount)) {
+
+ return(FALSE);
+ }
+
+ return(TRUE);
+
+}
+
+
+
+
+BOOLEAN
+SePrivilegeCheck(
+ IN OUT PPRIVILEGE_SET RequiredPrivileges,
+ IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
+ IN KPROCESSOR_MODE AccessMode
+ )
+/*++
+
+Routine Description:
+
+ This routine checks to see if the token contains the specified
+ privileges.
+
+Arguments:
+
+ RequiredPrivileges - Points to a set of privileges. The subject's
+ security context is to be checked to see which of the specified
+ privileges are present. The results will be indicated in the
+ attributes associated with each privilege. Note that
+ flags in this parameter indicate whether all the privileges listed
+ are needed, or any of the privileges.
+
+ SubjectSecurityContext - A pointer to the subject's captured security
+ context.
+
+ AccessMode - Indicates the access mode to use for access check. One of
+ UserMode or KernelMode. If the mode is kernel, then all privileges
+ will be marked as being possessed by the subject, and successful
+ completion status is returned.
+
+
+Return Value:
+
+ BOOLEAN - TRUE if all specified privileges are held by the subject,
+ otherwise FALSE.
+
+
+--*/
+
+{
+ BOOLEAN Status;
+
+ PAGED_CODE();
+
+ //
+ // If we're impersonating a client, we have to be at impersonation level
+ // of SecurityImpersonation or above.
+ //
+
+ if ( (SubjectSecurityContext->ClientToken != NULL) &&
+ (SubjectSecurityContext->ImpersonationLevel < SecurityImpersonation)
+ ) {
+
+ return(FALSE);
+ }
+
+ //
+ // SepPrivilegeCheck locks the passed token for read access
+ //
+
+ Status = SepPrivilegeCheck(
+ EffectiveToken( SubjectSecurityContext ),
+ RequiredPrivileges->Privilege,
+ RequiredPrivileges->PrivilegeCount,
+ RequiredPrivileges->Control,
+ AccessMode
+ );
+
+ return(Status);
+}
+
+
+
+NTSTATUS
+NtPrivilegeCheck(
+ IN HANDLE ClientToken,
+ IN OUT PPRIVILEGE_SET RequiredPrivileges,
+ OUT PBOOLEAN Result
+ )
+
+/*++
+
+Routine Description:
+
+ This routine tests the caller's client's security context to see if it
+ contains the specified privileges.
+
+ This API requires the caller have SeTcbPrivilege privilege. The test
+ for this privilege is always against the primary token of the calling
+ process, not the impersonation token of the thread.
+
+
+Arguments:
+
+ ClientToken - A handle to a token object representing a client
+ attempting access. This handle must be obtained from a
+ communication session layer, such as from an LPC Port or Local
+ Named Pipe, to prevent possible security policy violations.
+
+ RequiredPrivileges - Points to a set of privileges. The client's
+ security context is to be checked to see which of the specified
+ privileges are present. The results will be indicated in the
+ attributes associated with each privilege. Note that
+ flags in this parameter indicate whether all the privileges listed
+ are needed, or any of the privileges.
+
+ Result - Receives a boolean flag indicating whether the client has all
+ the specified privileges or not. A value of TRUE indicates the
+ client has all the specified privileges. Otherwise a value of
+ FALSE is returned.
+
+
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the call completed successfully.
+
+ STATUS_PRIVILEGE_NOT_HELD - Indicates the caller does not have
+ sufficient privilege to use this privileged system service.
+
+--*/
+
+
+
+{
+ BOOLEAN BStatus;
+ KPROCESSOR_MODE PreviousMode;
+ NTSTATUS Status;
+ PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL;
+ PTOKEN Token;
+ ULONG CapturedPrivilegeCount;
+ ULONG CapturedPrivilegesLength;
+ ULONG ParameterLength;
+ ULONG PrivilegeSetControl;
+
+ PAGED_CODE();
+
+ PreviousMode = KeGetPreviousMode();
+
+ Status = ObReferenceObjectByHandle(
+ ClientToken, // Handle
+ TOKEN_QUERY, // DesiredAccess
+ SepTokenObjectType, // ObjectType
+ PreviousMode, // AccessMode
+ (PVOID *)&Token, // Object
+ NULL // GrantedAccess
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ return Status;
+
+ }
+
+ //
+ // If the passed token is an impersonation token, make sure
+ // it is at SecurityIdentification or above.
+ //
+
+ if (Token->TokenType == TokenImpersonation) {
+
+ if (Token->ImpersonationLevel < SecurityIdentification) {
+
+ ObDereferenceObject( (PVOID)Token );
+
+ return( STATUS_BAD_IMPERSONATION_LEVEL );
+
+ }
+ }
+
+ try {
+
+ //
+ // Capture passed Privilege Set
+ //
+
+ ProbeForWrite(
+ RequiredPrivileges,
+ sizeof(PRIVILEGE_SET),
+ sizeof(ULONG)
+ );
+
+ ParameterLength = (ULONG)sizeof(PRIVILEGE_SET) +
+ ((RequiredPrivileges->PrivilegeCount - ANYSIZE_ARRAY) *
+ (ULONG)sizeof(LUID_AND_ATTRIBUTES) );
+
+ ProbeForWrite(
+ RequiredPrivileges,
+ ParameterLength,
+ sizeof(ULONG)
+ );
+
+
+ ProbeForWriteBoolean(Result);
+
+ PrivilegeSetControl = RequiredPrivileges->Control;
+ CapturedPrivilegeCount = RequiredPrivileges->PrivilegeCount;
+
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ ObDereferenceObject( (PVOID)Token );
+ return GetExceptionCode();
+ }
+
+ Status = SeCaptureLuidAndAttributesArray(
+ (RequiredPrivileges->Privilege),
+ CapturedPrivilegeCount,
+ UserMode,
+ NULL, 0,
+ PagedPool,
+ TRUE,
+ &CapturedPrivileges,
+ &CapturedPrivilegesLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ ObDereferenceObject( (PVOID)Token );
+ return Status;
+ }
+
+ ASSERT(CapturedPrivileges != NULL);
+
+ BStatus = SepPrivilegeCheck(
+ Token, // Token,
+ CapturedPrivileges, // RequiredPrivileges,
+ CapturedPrivilegeCount, // RequiredPrivilegeCount,
+ PrivilegeSetControl, // PrivilegeSetControl
+ PreviousMode // PreviousMode
+ );
+
+ ObDereferenceObject( Token );
+
+
+ try {
+
+ //
+ // copy the modified privileges buffer back to user
+ //
+
+ RtlMoveMemory(
+ RequiredPrivileges->Privilege,
+ CapturedPrivileges,
+ CapturedPrivilegesLength
+ );
+
+ *Result = BStatus;
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ SeReleaseLuidAndAttributesArray(
+ CapturedPrivileges,
+ PreviousMode,
+ TRUE
+ );
+
+ return(GetExceptionCode());
+
+ }
+
+ SeReleaseLuidAndAttributesArray(
+ CapturedPrivileges,
+ PreviousMode,
+ TRUE
+ );
+
+ return( STATUS_SUCCESS );
+}
+
+
+
+BOOLEAN
+SeSinglePrivilegeCheck(
+ LUID PrivilegeValue,
+ KPROCESSOR_MODE PreviousMode
+ )
+
+/*++
+
+Routine Description:
+
+ This function will check for the passed privilege value in the
+ current context.
+
+Arguments:
+
+ PrivilegeValue - The value of the privilege being checked.
+
+
+Return Value:
+
+ TRUE - The current subject has the desired privilege.
+
+ FALSE - The current subject does not have the desired privilege.
+--*/
+
+{
+ BOOLEAN AccessGranted;
+ PRIVILEGE_SET RequiredPrivileges;
+ SECURITY_SUBJECT_CONTEXT SubjectSecurityContext;
+
+ PAGED_CODE();
+
+ //
+ // Make sure the caller has the privilege to make this
+ // call.
+ //
+
+ RequiredPrivileges.PrivilegeCount = 1;
+ RequiredPrivileges.Control = PRIVILEGE_SET_ALL_NECESSARY;
+ RequiredPrivileges.Privilege[0].Luid = PrivilegeValue;
+ RequiredPrivileges.Privilege[0].Attributes = 0;
+
+ SeCaptureSubjectContext( &SubjectSecurityContext );
+
+ AccessGranted = SePrivilegeCheck(
+ &RequiredPrivileges,
+ &SubjectSecurityContext,
+ PreviousMode
+ );
+
+ if ( PreviousMode != KernelMode ) {
+
+ SePrivilegedServiceAuditAlarm (
+ NULL, // BUGWARNING need service name
+ &SubjectSecurityContext,
+ &RequiredPrivileges,
+ AccessGranted
+ );
+ }
+
+
+ SeReleaseSubjectContext( &SubjectSecurityContext );
+
+ return( AccessGranted );
+
+}
+
+
+BOOLEAN
+SeCheckPrivilegedObject(
+ LUID PrivilegeValue,
+ HANDLE ObjectHandle,
+ ACCESS_MASK DesiredAccess,
+ KPROCESSOR_MODE PreviousMode
+ )
+
+/*++
+
+Routine Description:
+
+ This function will check for the passed privilege value in the
+ current context, and generate audits as appropriate.
+
+Arguments:
+
+ PrivilegeValue - The value of the privilege being checked.
+
+ Object - Specifies a pointer to the object being accessed.
+
+ ObjectHandle - Specifies the object handle being used.
+
+ DesiredAccess - The desired access mask, if any
+
+ PreviousMode - The previous processor mode
+
+
+Return Value:
+
+ TRUE - The current subject has the desired privilege.
+
+ FALSE - The current subject does not have the desired privilege.
+--*/
+
+{
+ BOOLEAN AccessGranted;
+ PRIVILEGE_SET RequiredPrivileges;
+ SECURITY_SUBJECT_CONTEXT SubjectSecurityContext;
+
+ PAGED_CODE();
+
+ //
+ // Make sure the caller has the privilege to make this
+ // call.
+ //
+
+ RequiredPrivileges.PrivilegeCount = 1;
+ RequiredPrivileges.Control = PRIVILEGE_SET_ALL_NECESSARY;
+ RequiredPrivileges.Privilege[0].Luid = PrivilegeValue;
+ RequiredPrivileges.Privilege[0].Attributes = 0;
+
+ SeCaptureSubjectContext( &SubjectSecurityContext );
+
+ AccessGranted = SePrivilegeCheck(
+ &RequiredPrivileges,
+ &SubjectSecurityContext,
+ PreviousMode
+ );
+
+ if ( PreviousMode != KernelMode ) {
+
+ SePrivilegeObjectAuditAlarm(
+ ObjectHandle,
+ &SubjectSecurityContext,
+ DesiredAccess,
+ &RequiredPrivileges,
+ AccessGranted,
+ PreviousMode
+ );
+
+ }
+
+
+ SeReleaseSubjectContext( &SubjectSecurityContext );
+
+ return( AccessGranted );
+
+}
diff --git a/private/ntos/se/rmaudit.c b/private/ntos/se/rmaudit.c
new file mode 100644
index 000000000..5d0accf52
--- /dev/null
+++ b/private/ntos/se/rmaudit.c
@@ -0,0 +1,221 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ rmaudit.c
+
+Abstract:
+
+ This module contains the Reference Monitor Auditing Command Workers.
+ These workers call functions in the Auditing sub-component to do the real
+ work.
+
+Author:
+
+ Scott Birrell (ScottBi) November 14,1991
+
+Environment:
+
+ Kernel mode only.
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntlsa.h>
+#include <ntos.h>
+#include <ntrmlsa.h>
+#include "sep.h"
+#include "adt.h"
+#include "adtp.h"
+#include "rmp.h"
+
+VOID
+SepRmSetAuditLogWrkr(
+ IN PRM_COMMAND_MESSAGE CommandMessage,
+ OUT PRM_REPLY_MESSAGE ReplyMessage
+ );
+
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE,SepRmSetAuditEventWrkr)
+#pragma alloc_text(PAGE,SepRmSetAuditLogWrkr)
+#endif
+
+
+
+VOID
+SepRmSetAuditEventWrkr(
+ IN PRM_COMMAND_MESSAGE CommandMessage,
+ OUT PRM_REPLY_MESSAGE ReplyMessage
+ )
+
+/*++
+
+Routine Description:
+
+ This function carries out the Reference Monitor Set Audit Event
+ Command. This command enables or disables auditing and optionally
+ sets the auditing events.
+
+
+Arguments:
+
+ CommandMessage - Pointer to structure containing RM command message
+ information consisting of an LPC PORT_MESSAGE structure followed
+ by the command number (RmSetAuditStateCommand) and a single command
+ parameter in structure form.
+
+ ReplyMessage - Pointer to structure containing RM reply message
+ information consisting of an LPC PORT_MESSAGE structure followed
+ by the command ReturnedStatus field in which a status code from the
+ command will be returned.
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+
+ PPOLICY_AUDIT_EVENT_OPTIONS EventAuditingOptions;
+ POLICY_AUDIT_EVENT_TYPE EventType;
+
+ PAGED_CODE();
+
+ SepAdtInitializeBounds();
+
+ ReplyMessage->ReturnedStatus = STATUS_SUCCESS;
+
+ //
+ // Strict check that command is correct one for this worker.
+ //
+
+ ASSERT( CommandMessage->CommandNumber == RmAuditSetCommand );
+
+ //
+ // Extract the AuditingMode flag and put it in the right place.
+ //
+
+ SepAdtAuditingEnabled = (((PLSARM_POLICY_AUDIT_EVENTS_INFO) CommandMessage->CommandParams)->
+ AuditingMode);
+
+ //
+ // For each element in the passed array, process changes to audit
+ // nothing, and then success or failure flags.
+ //
+
+ EventAuditingOptions = ((PLSARM_POLICY_AUDIT_EVENTS_INFO) CommandMessage->CommandParams)->
+ EventAuditingOptions;
+
+
+ for ( EventType=AuditEventMinType;
+ EventType <= AuditEventMaxType;
+ EventType++ ) {
+
+ SeAuditingState[EventType].AuditOnSuccess = FALSE;
+ SeAuditingState[EventType].AuditOnFailure = FALSE;
+
+ if ( EventAuditingOptions[EventType] & POLICY_AUDIT_EVENT_SUCCESS ) {
+
+ SeAuditingState[EventType].AuditOnSuccess = TRUE;
+ }
+
+ if ( EventAuditingOptions[EventType] & POLICY_AUDIT_EVENT_FAILURE ) {
+
+ SeAuditingState[EventType].AuditOnFailure = TRUE;
+ }
+ }
+
+ //
+ // Set the flag to indicate that we're auditing detailed events.
+ // This is merely a timesaver so we can skip auditing setup in
+ // time critical places like process creation.
+ //
+
+ //
+ // Despite what the UI may imply, we never audit failures for detailed events, since
+ // none of them can fail for security related reasons, and we're not interested in
+ // auditing out of memory errors and stuff like that. So just set this flag when
+ // they want to see successes and ignore the failure case.
+ //
+ // We may have to revisit this someday.
+ //
+
+ if ( SeAuditingState[AuditCategoryDetailedTracking].AuditOnSuccess && SepAdtAuditingEnabled ) {
+
+ SeDetailedAuditing = TRUE;
+
+ } else {
+
+ SeDetailedAuditing = FALSE;
+ }
+
+ return;
+}
+
+
+
+VOID
+SepRmSetAuditLogWrkr(
+ IN PRM_COMMAND_MESSAGE CommandMessage,
+ OUT PRM_REPLY_MESSAGE ReplyMessage
+ )
+
+/*++
+
+Routine Description:
+
+ This function carries out the Reference Monitor Set Audit Log
+ Command. This command stores parameters related to the Audit Log.
+
+Arguments:
+
+ CommandMessage - Pointer to structure containing RM command message
+ information consisting of an LPC PORT_MESSAGE structure followed
+ by the command number (RmSetAuditStateCommand) and a single command
+ parameter in structure form.
+
+ ReplyMessage - Pointer to structure containing RM reply message
+ information consisting of an LPC PORT_MESSAGE structure followed
+ by the command ReturnedStatus field in which a status code from the
+ command will be returned.
+
+Return Value:
+
+ None. A status code is returned in ReplyMessage->ReturnedStatus
+
+--*/
+
+{
+ //
+ // Strict check that command is correct one for this worker.
+ //
+
+/* BUGWARNING - SCOTTBI - Auditing is disabled
+
+ ASSERT( CommandMessage->CommandNumber == RmSetAuditLogCommand );
+
+*/
+
+ PAGED_CODE();
+
+#if DBG
+ DbgPrint("Security: RM Set Audit Log Command Received\n");
+#endif
+
+ //
+ // Call private function in Auditing Sub-component to do the work.
+ //
+
+ SepAdtSetAuditLogInformation(
+ (PPOLICY_AUDIT_LOG_INFO) CommandMessage->CommandParams
+ );
+
+ ReplyMessage->ReturnedStatus = STATUS_SUCCESS;
+}
+
diff --git a/private/ntos/se/rmlogon.c b/private/ntos/se/rmlogon.c
new file mode 100644
index 000000000..2a1c7a8c6
--- /dev/null
+++ b/private/ntos/se/rmlogon.c
@@ -0,0 +1,1137 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ rmlogon.c
+
+Abstract:
+
+ This module implements the kernel mode logon tracking performed by the
+ reference monitor. Logon tracking is performed by keeping a count of
+ how many tokens exist for each active logon in a system. When a logon
+ session's reference count drops to zero, the LSA is notified so that
+ authentication packages can clean up any related context data.
+
+
+Author:
+
+ Jim Kelly (JimK) 21-April-1991
+
+Environment:
+
+ Kernel mode only.
+
+Revision History:
+
+--*/
+
+//#define SEP_TRACK_LOGON_SESSION_REFS
+
+
+#include "rmp.h"
+#include <bugcodes.h>
+
+
+SEP_LOGON_SESSION_TERMINATED_NOTIFICATION
+SeFileSystemNotifyRoutinesHead = {0};
+
+
+////////////////////////////////////////////////////////////////////////////
+// //
+// Internally defined data types //
+// //
+////////////////////////////////////////////////////////////////////////////
+
+typedef struct _SEP_FILE_SYSTEM_NOTIFY_CONTEXT {
+ WORK_QUEUE_ITEM WorkItem;
+ LUID LogonId;
+} SEP_FILE_SYSTEM_NOTIFY_CONTEXT, *PSEP_FILE_SYSTEM_NOTIFY_CONTEXT;
+
+
+////////////////////////////////////////////////////////////////////////////
+// //
+// Internally defined routines //
+// //
+////////////////////////////////////////////////////////////////////////////
+
+NTSTATUS
+SepGetLogonSessionTrack(
+ IN PLUID LogonId
+ );
+
+
+VOID
+SepInformLsaOfDeletedLogon(
+ IN PLUID LogonId
+ );
+
+VOID
+SepInformFileSystemsOfDeletedLogon(
+ IN PLUID LogonId
+ );
+
+VOID
+SepNotifyFileSystems(
+ IN PVOID Context
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE,SeRegisterLogonSessionTerminatedRoutine)
+#pragma alloc_text(PAGE,SeUnregisterLogonSessionTerminatedRoutine)
+#pragma alloc_text(PAGE,SeMarkLogonSessionForTerminationNotification)
+#pragma alloc_text(PAGE,SepRmCreateLogonSessionWrkr)
+#pragma alloc_text(PAGE,SepRmDeleteLogonSessionWrkr)
+#pragma alloc_text(PAGE,SepReferenceLogonSession)
+#pragma alloc_text(PAGE,SepDeReferenceLogonSession)
+#pragma alloc_text(PAGE,SepCreateLogonSessionTrack)
+#pragma alloc_text(PAGE,SepDeleteLogonSessionTrack)
+#pragma alloc_text(PAGE,SepInformLsaOfDeletedLogon)
+#pragma alloc_text(PAGE,SepInformFileSystemsOfDeletedLogon)
+#pragma alloc_text(PAGE,SepNotifyFileSystems)
+#endif
+
+
+
+////////////////////////////////////////////////////////////////////////////
+// //
+// Local macros //
+// //
+////////////////////////////////////////////////////////////////////////////
+
+
+//
+// This macro is used to obtain an index into the logon session tracking
+// array given a logon session ID (a LUID).
+//
+
+#define SepLogonSessionIndex( PLogonId ) ( \
+ (PLogonId)->LowPart & SEP_LOGON_TRACK_INDEX_MASK \
+ )
+
+
+
+////////////////////////////////////////////////////////////////////////////
+// //
+// Exported Services //
+// //
+////////////////////////////////////////////////////////////////////////////
+
+VOID
+SepRmCreateLogonSessionWrkr(
+ IN PRM_COMMAND_MESSAGE CommandMessage,
+ OUT PRM_REPLY_MESSAGE ReplyMessage
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the dispatch routine for the LSA --> RM
+ "CreateLogonSession" call.
+
+ The arguments passed to this routine are defined by the
+ type SEP_RM_COMMAND_WORKER.
+
+
+Arguments:
+
+ CommandMessage - Points to structure containing RM command message
+ information consisting of an LPC PORT_MESSAGE structure followed
+ by the command number (RmComponentTestCommand) and a command-specific
+ body. The command-specific body of this parameter is a LUID of the
+ logon session to be created.
+
+ ReplyMessage - Pointer to structure containing LSA reply message
+ information consisting of an LPC PORT_MESSAGE structure followed
+ by the command ReturnedStatus field in which a status code from the
+ command will be returned.
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+
+ NTSTATUS Status;
+ LUID LogonId;
+
+ PAGED_CODE();
+
+ //
+ // Check that command is expected type
+ //
+
+ ASSERT( CommandMessage->CommandNumber == RmCreateLogonSession );
+
+
+ //
+ // Typecast the command parameter to what we expect.
+ //
+
+ LogonId = *((LUID UNALIGNED *) CommandMessage->CommandParams);
+
+
+
+ //
+ // Try to create the logon session tracking record
+ //
+
+ Status = SepCreateLogonSessionTrack( &LogonId );
+
+
+
+ //
+ // Set the reply status
+ //
+
+ ReplyMessage->ReturnedStatus = Status;
+
+
+ return;
+}
+
+
+
+VOID
+SepRmDeleteLogonSessionWrkr(
+ IN PRM_COMMAND_MESSAGE CommandMessage,
+ OUT PRM_REPLY_MESSAGE ReplyMessage
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the dispatch routine for the LSA --> RM
+ "DeleteLogonSession" call.
+
+ The arguments passed to this routine are defined by the
+ type SEP_RM_COMMAND_WORKER.
+
+
+Arguments:
+
+ CommandMessage - Points to structure containing RM command message
+ information consisting of an LPC PORT_MESSAGE structure followed
+ by the command number (RmComponentTestCommand) and a command-specific
+ body. The command-specific body of this parameter is a LUID of the
+ logon session to be created.
+
+ ReplyMessage - Pointer to structure containing LSA reply message
+ information consisting of an LPC PORT_MESSAGE structure followed
+ by the command ReturnedStatus field in which a status code from the
+ command will be returned.
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+
+ NTSTATUS Status;
+ LUID LogonId;
+
+ PAGED_CODE();
+
+ //
+ // Check that command is expected type
+ //
+
+ ASSERT( CommandMessage->CommandNumber == RmDeleteLogonSession );
+
+
+ //
+ // Typecast the command parameter to what we expect.
+ //
+
+ LogonId = *((LUID UNALIGNED *) CommandMessage->CommandParams);
+
+
+
+ //
+ // Try to create the logon session tracking record
+ //
+
+ Status = SepDeleteLogonSessionTrack( &LogonId );
+
+
+
+ //
+ // Set the reply status
+ //
+
+ ReplyMessage->ReturnedStatus = Status;
+
+
+ return;
+}
+
+
+
+NTSTATUS
+SepReferenceLogonSession(
+ IN PLUID LogonId
+ )
+
+/*++
+
+Routine Description:
+
+ This routine increments the reference count of a logon session
+ tracking record.
+
+
+
+Arguments:
+
+ LogonId - Pointer to the logon session ID whose logon track is
+ to be incremented.
+
+Return Value:
+
+ STATUS_SUCCESS - The reference count was successfully incremented.
+
+ STATUS_NO_SUCH_LOGON_SESSION - The specified logon session doesn't
+ exist in the reference monitor's database.
+
+--*/
+
+{
+
+ ULONG SessionArrayIndex;
+ PSEP_LOGON_SESSION_REFERENCES Previous, Current;
+
+#ifdef SEP_TRACK_LOGON_SESSION_REFS
+ ULONG Refs;
+#endif //SEP_TRACK_LOGON_SESSION_REFS
+
+ PAGED_CODE();
+
+ SessionArrayIndex = SepLogonSessionIndex( LogonId );
+
+ //
+ // Protect modification of reference monitor database
+ //
+
+ SepRmAcquireDbWriteLock();
+
+
+ //
+ // Now walk the list for our logon session array hash index.
+ //
+
+ Previous = (PSEP_LOGON_SESSION_REFERENCES)
+ ((PVOID)&SepLogonSessions[ SessionArrayIndex ]);
+ Current = Previous->Next;
+
+ while (Current != NULL) {
+
+ //
+ // If we found it, increment the reference count and return
+ //
+
+ if (RtlEqualLuid( LogonId, &Current->LogonId) ) {
+#ifdef SEP_TRACK_LOGON_SESSION_REFS
+ ULONG Refs;
+#endif //SEP_TRACK_LOGON_SESSION_REFS
+
+ Current->ReferenceCount += 1;
+
+#ifdef SEP_TRACK_LOGON_SESSION_REFS
+ Refs = Current->ReferenceCount;
+#endif //SEP_TRACK_LOGON_SESSION_REFS
+
+ SepRmReleaseDbWriteLock();
+
+#ifdef SEP_TRACK_LOGON_SESSION_REFS
+ DbgPrint("SE (rm): ++ logon session: (%d, %d) to %d by (%d, %d)\n",
+ LogonId->HighPart, LogonId->LowPart, Refs,
+ PsGetCurrentThread()->Cid.UniqueProcess,
+ PsGetCurrentThread()->Cid.UniqueThread);
+
+#endif //SEP_TRACK_LOGON_SESSION_REFS
+ return STATUS_SUCCESS;
+ }
+
+ Previous = Current;
+ Current = Current->Next;
+ }
+
+ SepRmReleaseDbWriteLock();
+
+ //
+ // Bad news, someone asked us to increment the reference count of
+ // a logon session we didn't know existed. This might be a new
+ // token being created, so return an error status and let the caller
+ // decide if it warrants a bug check or not.
+ //
+
+
+ return STATUS_NO_SUCH_LOGON_SESSION;
+
+
+
+}
+
+
+VOID
+SepDeReferenceLogonSession(
+ IN PLUID LogonId
+ )
+
+/*++
+
+Routine Description:
+
+ This routine decrements the reference count of a logon session
+ tracking record.
+
+ If the reference count is decremented to zero, then there is no
+ possibility for any more tokens to exist for the logon session.
+ In this case, the LSA is notified that a logon session has
+ terminated.
+
+
+
+Arguments:
+
+ LogonId - Pointer to the logon session ID whose logon track is
+ to be decremented.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ ULONG SessionArrayIndex;
+ PSEP_LOGON_SESSION_REFERENCES Previous, Current;
+
+#ifdef SEP_TRACK_LOGON_SESSION_REFS
+ ULONG Refs;
+#endif //SEP_TRACK_LOGON_SESSION_REFS
+
+ PAGED_CODE();
+
+ SessionArrayIndex = SepLogonSessionIndex( LogonId );
+
+ //
+ // Protect modification of reference monitor database
+ //
+
+ SepRmAcquireDbWriteLock();
+
+
+ //
+ // Now walk the list for our logon session array hash index.
+ //
+
+ Previous = (PSEP_LOGON_SESSION_REFERENCES)
+ ((PVOID)&SepLogonSessions[ SessionArrayIndex ]);
+ Current = Previous->Next;
+
+ while (Current != NULL) {
+
+ //
+ // If we found it, decrement the reference count and return
+ //
+
+ if (RtlEqualLuid( LogonId, &Current->LogonId) ) {
+ Current->ReferenceCount -= 1;
+ if (Current->ReferenceCount == 0) {
+
+ //
+ // Pull it from the list
+ //
+
+ Previous->Next = Current->Next;
+
+
+ //
+ // No longer need to protect our pointer to this
+ // record.
+ //
+
+ SepRmReleaseDbWriteLock();
+
+ //
+ // Asynchronoously inform file systems that this logon session
+ // is going away, if atleast one FS expressed interest in this
+ // logon session.
+ //
+
+ if (Current->Flags & SEP_TERMINATION_NOTIFY) {
+ SepInformFileSystemsOfDeletedLogon( LogonId );
+ }
+
+ //
+ // Deallocate the logon session track record.
+ //
+
+ ExFreePool( (PVOID)Current );
+
+
+#ifdef SEP_TRACK_LOGON_SESSION_REFS
+ DbgPrint("SE (rm): -- ** logon session: (%d, %d) to ZERO by (%d, %d)\n",
+ LogonId->HighPart, LogonId->LowPart,
+ PsGetCurrentThread()->Cid.UniqueProcess,
+ PsGetCurrentThread()->Cid.UniqueThread);
+
+#endif //SEP_TRACK_LOGON_SESSION_REFS
+
+ //
+ // Inform the LSA about the deletion of this logon session.
+ //
+
+ SepInformLsaOfDeletedLogon( LogonId );
+
+
+
+ return;
+
+ }
+
+ //
+ // reference count was incremented, but not to zero.
+ //
+
+#ifdef SEP_TRACK_LOGON_SESSION_REFS
+ Refs = Current->ReferenceCount;
+#endif //SEP_TRACK_LOGON_SESSION_REFS
+
+ SepRmReleaseDbWriteLock();
+
+#ifdef SEP_TRACK_LOGON_SESSION_REFS
+ DbgPrint("SE (rm): -- logon session: (%d, %d) to %d by (%d, %d)\n",
+ LogonId->HighPart, LogonId->LowPart, Refs,
+ PsGetCurrentThread()->Cid.UniqueProcess,
+ PsGetCurrentThread()->Cid.UniqueThread);
+#endif //SEP_TRACK_LOGON_SESSION_REFS
+
+ return;
+ }
+
+ Previous = Current;
+ Current = Current->Next;
+ }
+
+ SepRmReleaseDbWriteLock();
+
+ //
+ // Bad news, someone asked us to decrement the reference count of
+ // a logon session we didn't know existed.
+ //
+
+ KeBugCheck( DEREF_UNKNOWN_LOGON_SESSION );
+
+ return;
+
+}
+
+
+NTSTATUS
+SepCreateLogonSessionTrack(
+ IN PLUID LogonId
+ )
+
+/*++
+
+Routine Description:
+
+ This routine creates a new logon session tracking record.
+
+ This should only be called as a dispatch routine for a LSA->RM
+ call (and once during system initialization).
+
+ If the specified logon session already exists, then an error is returned.
+
+
+
+Arguments:
+
+ LogonId - Pointer to the logon session ID for which a new logon track is
+ to be created.
+
+Return Value:
+
+ STATUS_SUCCESS - The logon session track was created successfully.
+
+ STATUS_LOGON_SESSION_EXISTS - The logon session already exists.
+ A new one has not been created.
+
+--*/
+
+{
+
+ ULONG SessionArrayIndex;
+ PSEP_LOGON_SESSION_REFERENCES Previous, Current;
+ PSEP_LOGON_SESSION_REFERENCES LogonSessionTrack;
+
+ PAGED_CODE();
+
+ //
+ // Make sure we can allocate a new logon session track record
+ //
+
+ LogonSessionTrack = (PSEP_LOGON_SESSION_REFERENCES)
+ ExAllocatePoolWithTag(
+ PagedPool,
+ sizeof(SEP_LOGON_SESSION_REFERENCES),
+ 'sLeS'
+ );
+
+ if (LogonSessionTrack == NULL) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ LogonSessionTrack->LogonId = (*LogonId);
+ LogonSessionTrack->ReferenceCount = 0;
+
+
+
+ SessionArrayIndex = SepLogonSessionIndex( LogonId );
+
+ //
+ // Protect modification of reference monitor database
+ //
+
+ SepRmAcquireDbWriteLock();
+
+
+ //
+ // Now walk the list for our logon session array hash index
+ // looking for a duplicate logon session ID.
+ //
+
+ Previous = (PSEP_LOGON_SESSION_REFERENCES)
+ ((PVOID)&SepLogonSessions[ SessionArrayIndex ]);
+ Current = Previous->Next;
+
+ while (Current != NULL) {
+
+ if (RtlEqualLuid( LogonId, &Current->LogonId) ) {
+
+ //
+ // One already exists. Hmmm.
+ //
+
+ SepRmReleaseDbWriteLock();
+ ExFreePool(LogonSessionTrack);
+ return STATUS_LOGON_SESSION_EXISTS;
+
+ }
+
+ Previous = Current;
+ Current = Current->Next;
+ }
+
+
+ //
+ // Reached the end of the list without finding a duplicate.
+ // Add the new one.
+ //
+
+ LogonSessionTrack->Next = SepLogonSessions[ SessionArrayIndex ];
+ SepLogonSessions[ SessionArrayIndex ] = LogonSessionTrack;
+
+
+
+
+ SepRmReleaseDbWriteLock();
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+SepDeleteLogonSessionTrack(
+ IN PLUID LogonId
+ )
+
+/*++
+
+Routine Description:
+
+ This routine creates a new logon session tracking record.
+
+ This should only be called as a dispatch routine for a LSA->RM
+ call (and once during system initialization).
+
+ If the specified logon session already exists, then an error is returned.
+
+
+
+Arguments:
+
+ LogonId - Pointer to the logon session ID whose logon track is
+ to be deleted.
+
+Return Value:
+
+ STATUS_SUCCESS - The logon session track was deleted successfully.
+
+ STATUS_BAD_LOGON_SESSION_STATE - The logon session has a non-zero
+ reference count and can not be deleted.
+
+ STATUS_NO_SUCH_LOGON_SESSION - The specified logon session does not
+ exist.
+
+
+--*/
+
+{
+
+ ULONG SessionArrayIndex;
+ PSEP_LOGON_SESSION_REFERENCES Previous, Current;
+
+ PAGED_CODE();
+
+ SessionArrayIndex = SepLogonSessionIndex( LogonId );
+
+ //
+ // Protect modification of reference monitor database
+ //
+
+ SepRmAcquireDbWriteLock();
+
+
+ //
+ // Now walk the list for our logon session array hash index.
+ //
+
+ Previous = (PSEP_LOGON_SESSION_REFERENCES)
+ ((PVOID)&SepLogonSessions[ SessionArrayIndex ]);
+ Current = Previous->Next;
+
+ while (Current != NULL) {
+
+ //
+ // If we found it, make sure reference count is zero
+ //
+
+ if (RtlEqualLuid( LogonId, &Current->LogonId) ) {
+
+ if (Current->ReferenceCount == 0) {
+
+ //
+ // Pull it from the list
+ //
+
+ Previous->Next = Current->Next;
+
+
+ //
+ // No longer need to protect our pointer to this
+ // record.
+ //
+
+ SepRmReleaseDbWriteLock();
+
+
+ //
+ // Deallocate the logon session track record.
+ //
+
+ ExFreePool( (PVOID)Current );
+
+
+ return STATUS_SUCCESS;
+
+ }
+
+ //
+ // reference count was not zero. This is not considered
+ // a healthy situation. Return an error and let someone
+ // else declare the bug check.
+ //
+
+ SepRmReleaseDbWriteLock();
+ return STATUS_BAD_LOGON_SESSION_STATE;
+ }
+
+ Previous = Current;
+ Current = Current->Next;
+ }
+
+ SepRmReleaseDbWriteLock();
+
+ //
+ // Someone asked us to delete a logon session that isn't
+ // in the database.
+ //
+
+ return STATUS_NO_SUCH_LOGON_SESSION;
+
+}
+
+
+VOID
+SepInformLsaOfDeletedLogon(
+ IN PLUID LogonId
+ )
+
+/*++
+
+Routine Description:
+
+ This routine informs the LSA about the deletion of a logon session.
+
+ Note that we can not be guaranteed that we are in a whole (or wholesome)
+ thread, since we may be in the middle of process deletion and object
+ rundown. Therefore, we must queue the work off to a worker thread which
+ can then make an LPC call to the LSA.
+
+
+
+
+Arguments:
+
+ LogonId - Pointer to the logon session ID which has been deleted.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PSEP_LSA_WORK_ITEM DeleteLogonItem;
+
+ PAGED_CODE();
+
+ //
+ // Pass the LUID value along with the work queue item.
+ // Note that the worker thread is responsible for freeing the WorkItem data
+ // structure.
+ //
+
+ DeleteLogonItem = ExAllocatePoolWithTag( PagedPool, sizeof(SEP_LSA_WORK_ITEM), 'wLeS' );
+ if (DeleteLogonItem == NULL) {
+
+ //
+ // I don't know what to do here... we loose track of a logon session,
+ // but the system isn't really harmed in any way.
+ //
+
+ return;
+
+ }
+
+ DeleteLogonItem->CommandParams.LogonId = (*LogonId);
+ DeleteLogonItem->CommandNumber = LsapLogonSessionDeletedCommand;
+ DeleteLogonItem->CommandParamsLength = sizeof( LUID );
+ DeleteLogonItem->ReplyBuffer = NULL;
+ DeleteLogonItem->ReplyBufferLength = 0;
+ DeleteLogonItem->CleanupFunction = NULL;
+ DeleteLogonItem->CleanupParameter = 0;
+ DeleteLogonItem->Tag = SepDeleteLogon;
+ DeleteLogonItem->CommandParamsMemoryType = SepRmImmediateMemory;
+
+ if (!SepQueueWorkItem( DeleteLogonItem, TRUE )) {
+
+ ExFreePool( DeleteLogonItem );
+ }
+
+ return;
+
+}
+
+
+NTSTATUS
+SeRegisterLogonSessionTerminatedRoutine(
+ IN PSE_LOGON_SESSION_TERMINATED_ROUTINE CallbackRoutine
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called by file systems that are interested in being
+ notified when a logon session is being deleted.
+
+Arguments:
+
+ CallbackRoutine - Address of routine to call back when a logon session
+ is being deleted.
+
+Return Value:
+
+ STATUS_SUCCESS - Successfully registered routine
+
+ STATUS_INVALID_PARAMETER - CallbackRoutine is NULL
+
+ STATUS_INSUFFICIENT_RESOURCE - Unable to allocate list entry.
+
+--*/
+
+{
+ PSEP_LOGON_SESSION_TERMINATED_NOTIFICATION NewCallback;
+
+ PAGED_CODE();
+
+ if (CallbackRoutine == NULL) {
+ return( STATUS_INVALID_PARAMETER );
+ }
+
+ NewCallback = ExAllocatePoolWithTag(
+ PagedPool,
+ sizeof(SEP_LOGON_SESSION_TERMINATED_NOTIFICATION),
+ 'SFeS');
+
+ if (NewCallback == NULL) {
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ SepRmAcquireDbWriteLock();
+
+ NewCallback->Next = SeFileSystemNotifyRoutinesHead.Next;
+
+ NewCallback->CallbackRoutine = CallbackRoutine;
+
+ SeFileSystemNotifyRoutinesHead.Next = NewCallback;
+
+ SepRmReleaseDbWriteLock();
+
+ return( STATUS_SUCCESS );
+}
+
+
+NTSTATUS
+SeUnregisterLogonSessionTerminatedRoutine(
+ IN PSE_LOGON_SESSION_TERMINATED_ROUTINE CallbackRoutine
+ )
+
+/*++
+
+Routine Description:
+
+ This is the dual of SeRegisterLogonSessionTerminatedRoutine. A File System
+ *MUST* call this before it is unloaded.
+
+Arguments:
+
+ CallbackRoutine - Address of routine that was originally passed in to
+ SeRegisterLogonSessionTerminatedRoutine.
+
+Return Value:
+
+ STATUS_SUCCESS - Successfully removed callback routine
+
+ STATUS_INVALID_PARAMETER - CallbackRoutine is NULL
+
+ STATUS_NOT_FOUND - Didn't find and entry for CallbackRoutine
+
+--*/
+{
+ NTSTATUS Status;
+ PSEP_LOGON_SESSION_TERMINATED_NOTIFICATION PreviousEntry;
+ PSEP_LOGON_SESSION_TERMINATED_NOTIFICATION NotifyEntry;
+
+ PAGED_CODE();
+
+ if (CallbackRoutine == NULL) {
+ return( STATUS_INVALID_PARAMETER );
+ }
+
+ SepRmAcquireDbWriteLock();
+
+ for (PreviousEntry = &SeFileSystemNotifyRoutinesHead,
+ NotifyEntry = SeFileSystemNotifyRoutinesHead.Next;
+ NotifyEntry != NULL;
+ PreviousEntry = NotifyEntry,
+ NotifyEntry = NotifyEntry->Next) {
+
+ if (NotifyEntry->CallbackRoutine == CallbackRoutine)
+ break;
+
+ }
+
+ if (NotifyEntry != NULL) {
+
+ PreviousEntry->Next = NotifyEntry->Next;
+
+ ExFreePool( NotifyEntry );
+
+ Status = STATUS_SUCCESS;
+
+ } else {
+
+ Status = STATUS_NOT_FOUND;
+
+ }
+
+ SepRmReleaseDbWriteLock();
+
+ return( Status );
+
+}
+
+
+NTSTATUS
+SeMarkLogonSessionForTerminationNotification(
+ IN PLUID LogonId
+ )
+
+/*++
+
+Routine Description:
+
+ File systems that have registered for logon-termination notification
+ can mark logon sessions they are interested in for callback by calling
+ this routine.
+
+Arguments:
+
+ LogonId - The logon id for which the file system should be notified
+ when the logon session is terminated.
+
+Returns:
+
+ Nothing.
+
+--*/
+
+{
+
+ ULONG SessionArrayIndex;
+ PSEP_LOGON_SESSION_REFERENCES Previous, Current;
+
+ PAGED_CODE();
+
+ SessionArrayIndex = SepLogonSessionIndex( LogonId );
+
+ //
+ // Protect modification of reference monitor database
+ //
+
+ SepRmAcquireDbWriteLock();
+
+
+ //
+ // Now walk the list for our logon session array hash index.
+ //
+
+ Previous = (PSEP_LOGON_SESSION_REFERENCES)
+ ((PVOID)&SepLogonSessions[ SessionArrayIndex ]);
+ Current = Previous->Next;
+
+ while (Current != NULL) {
+
+ //
+ // If we found it, decrement the reference count and return
+ //
+
+ if (RtlEqualLuid( LogonId, &Current->LogonId) ) {
+ Current->Flags |= SEP_TERMINATION_NOTIFY;
+ break;
+ }
+
+ Previous = Current;
+ Current = Current->Next;
+ }
+
+ SepRmReleaseDbWriteLock();
+
+ return( (Current != NULL) ? STATUS_SUCCESS : STATUS_NOT_FOUND );
+
+}
+
+
+VOID
+SepInformFileSystemsOfDeletedLogon(
+ IN PLUID LogonId
+ )
+
+/*++
+
+Routine Description:
+
+ This routine informs interested file systems of a deleted logon.
+
+ Note that we can not be guaranteed that we are in a whole (or wholesome)
+ thread, since we may be in the middle of process deletion and object
+ rundown. Therefore, we must queue the work off to a worker thread.
+
+
+Arguments:
+
+ LogonId - Pointer to the logon session ID which has been deleted.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PSEP_FILE_SYSTEM_NOTIFY_CONTEXT FSNotifyContext;
+
+ PAGED_CODE();
+
+ FSNotifyContext = ExAllocatePoolWithTag(
+ NonPagedPool,
+ sizeof(SEP_FILE_SYSTEM_NOTIFY_CONTEXT),
+ 'SFeS');
+
+ if (FSNotifyContext == NULL) {
+
+ //
+ // I don't know what to do here... file systems will loose track of a
+ // logon session, but the system isn't really harmed in any way.
+ //
+
+ return;
+
+ }
+
+ FSNotifyContext->LogonId = *LogonId;
+
+ ExInitializeWorkItem( &FSNotifyContext->WorkItem,
+ (PWORKER_THREAD_ROUTINE) SepNotifyFileSystems,
+ (PVOID) FSNotifyContext);
+
+ ExQueueWorkItem( &FSNotifyContext->WorkItem, DelayedWorkQueue );
+
+}
+
+
+VOID
+SepNotifyFileSystems(
+ IN PVOID Context
+ )
+{
+ PSEP_FILE_SYSTEM_NOTIFY_CONTEXT FSNotifyContext =
+ (PSEP_FILE_SYSTEM_NOTIFY_CONTEXT) Context;
+
+ PSEP_LOGON_SESSION_TERMINATED_NOTIFICATION NextCallback;
+
+ PAGED_CODE();
+
+ //
+ // Protect modification of the list of FS callbacks.
+ //
+
+ SepRmAcquireDbReadLock();
+
+ NextCallback = SeFileSystemNotifyRoutinesHead.Next;
+
+ while (NextCallback != NULL) {
+
+ NextCallback->CallbackRoutine( &FSNotifyContext->LogonId );
+
+ NextCallback = NextCallback->Next;
+ }
+
+ SepRmReleaseDbReadLock();
+
+ ExFreePool( FSNotifyContext );
+}
+
diff --git a/private/ntos/se/rmmain.c b/private/ntos/se/rmmain.c
new file mode 100644
index 000000000..8ae60c8f1
--- /dev/null
+++ b/private/ntos/se/rmmain.c
@@ -0,0 +1,1294 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ rmmain.c
+
+Abstract:
+
+ Security Reference Monitor - Init, Control and State Change
+
+Author:
+
+ Scott Birrell (ScottBi) March 12, 1991
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntlsa.h>
+#include "sep.h"
+#include <zwapi.h>
+#include "rmp.h"
+#include "adt.h"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(INIT,SepRmInitPhase0)
+#pragma alloc_text(INIT,SeRmInitPhase1)
+#pragma alloc_text(PAGE,SepRmCommandServerThread)
+#pragma alloc_text(PAGE,SepRmCommandServerThreadInit)
+#pragma alloc_text(PAGE,SepRmComponentTestCommandWrkr)
+#pragma alloc_text(PAGE,SepRmSendCommandToLsaWrkr)
+#pragma alloc_text(PAGE,SepRmCallLsa)
+#endif
+
+
+
+
+
+//
+// Reference Monitor Command Worker Table
+//
+
+//
+// Keep this in sync with RM_COMMAND_NUMBER in ntrmlsa.h
+//
+
+SEP_RM_COMMAND_WORKER SepRmCommandDispatch[] = {
+ SepRmComponentTestCommandWrkr,
+ SepRmSetAuditEventWrkr,
+ SepRmSendCommandToLsaWrkr,
+ SepRmComponentTestCommandWrkr,
+ SepRmCreateLogonSessionWrkr,
+ SepRmDeleteLogonSessionWrkr
+ };
+
+
+BOOLEAN
+SeRmInitPhase1(
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called by Phase 1 System Initialization to initialize
+ the Security Reference Monitor. Note that initialization of the
+ Reference Monitor Global State has already been performed in Phase 0
+ initialization to allow access validation routines to operate without
+ having to check that Reference Monitor Initialization is complete.
+
+ The steps listed below are performed in this routine. The remainder
+ of Reference Monitor initialization requires the LSA subsystem to have run,
+ so that initialization is performed in a separate thread (the RM Command
+ Server Thread, see below), so that the present thread can create the
+ Session Manager which execs the LSA.
+
+ o Create the Reference Monitor Command LPC port. The LSA subsystem sends
+ commands (e.g. turn on auditing) which change the Reference Monitor
+ Global State.
+ o Create an Event for use in synchronizing with the LSA subsystem. The
+ LSA will signal the event when the portion of LSA initialization upon
+ with the Reference Monitor depends is complete. The Reference Monitor
+ uses another LPC port, called the LSA Command Port to send commands
+ to the LSA, so the RM must know that this port has been created before
+ trying to connect to it.
+ o Create the Reference Monitor Command Server Thread. This thread is
+ a permanent thread of the System Init process that fields the Reference
+ Monitor State Change commands described above.
+
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ BOOLEAN - TRUE if Rm Initialization (Phase 1) succeeded, else FALSE
+
+--*/
+
+{
+ NTSTATUS Status;
+ STRING RmCommandPortName;
+ UNICODE_STRING UnicodeRmCommandPortName;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ STRING LsaInitEventName;
+ UNICODE_STRING UnicodeLsaInitEventName;
+ OBJECT_ATTRIBUTES LsaInitEventObjectAttributes;
+ SECURITY_DESCRIPTOR LsaInitEventSecurityDescriptor;
+ ULONG AclSize;
+
+ PAGED_CODE();
+
+ //
+ // Create an LPC port called the Reference Monitor Command Port.
+ // This will be used by the LSA to send commands to the Reference
+ // Monitor to update its state data.
+ //
+
+ RtlInitString( &RmCommandPortName, "\\SeRmCommandPort" );
+ Status = RtlAnsiStringToUnicodeString(
+ &UnicodeRmCommandPortName,
+ &RmCommandPortName,
+ TRUE ); ASSERT( NT_SUCCESS(Status) );
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &UnicodeRmCommandPortName,
+ 0,
+ NULL,
+ NULL
+ );
+
+ Status = ZwCreatePort(
+ &SepRmState.RmCommandPortHandle,
+ &ObjectAttributes,
+ sizeof(SEP_RM_CONNECT_INFO),
+ sizeof(RM_COMMAND_MESSAGE),
+ sizeof(RM_COMMAND_MESSAGE) * 32
+ );
+ RtlFreeUnicodeString( &UnicodeRmCommandPortName );
+
+ if( !NT_SUCCESS(Status) ) {
+
+ KdPrint(("Security: Rm Create Command Port failed 0x%lx\n", Status));
+ return FALSE;
+ }
+
+ //
+ // Prepare to create an event for synchronizing with the LSA.
+ // First, build the Security Descriptor for the Init Event Object
+ //
+
+ Status = RtlCreateSecurityDescriptor(
+ &LsaInitEventSecurityDescriptor,
+ SECURITY_DESCRIPTOR_REVISION
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("Security: Creating Lsa Init Event Desc failed 0x%lx\n",
+ Status));
+ return FALSE;
+ }
+
+ //
+ // Allocate a temporary buffer from the paged pool. It is a fatal
+ // system error if the allocation fails since security cannot be
+ // enabled.
+ //
+
+ AclSize = sizeof(ACL) +
+ sizeof(ACCESS_ALLOWED_ACE) +
+ SeLengthSid(SeLocalSystemSid);
+ LsaInitEventSecurityDescriptor.Dacl =
+ ExAllocatePoolWithTag(PagedPool, AclSize, 'cAeS');
+
+ if (LsaInitEventSecurityDescriptor.Dacl == NULL) {
+
+ KdPrint(("Security LSA: Insufficient resources to initialize\n"));
+ return FALSE;
+ }
+
+ //
+ // Now create the Discretionary ACL within the Security Descriptor
+ //
+
+ Status = RtlCreateAcl(
+ LsaInitEventSecurityDescriptor.Dacl,
+ AclSize,
+ ACL_REVISION2
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("Security: Creating Lsa Init Event Dacl failed 0x%lx\n",
+ Status));
+ return FALSE;
+ }
+
+ //
+ // Now add an ACE giving GENERIC_ALL access to the User ID
+ //
+
+ Status = RtlAddAccessAllowedAce(
+ LsaInitEventSecurityDescriptor.Dacl,
+ ACL_REVISION2,
+ GENERIC_ALL,
+ SeLocalSystemSid
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("Security: Adding Lsa Init Event ACE failed 0x%lx\n",
+ Status));
+ return FALSE;
+ }
+
+ //
+ // Set up the Object Attributes for the Lsa Initialization Event
+ //
+
+ RtlInitString( &LsaInitEventName, "\\SeLsaInitEvent" );
+ Status = RtlAnsiStringToUnicodeString(
+ &UnicodeLsaInitEventName,
+ &LsaInitEventName,
+ TRUE ); ASSERT( NT_SUCCESS(Status) );
+ InitializeObjectAttributes(
+ &LsaInitEventObjectAttributes,
+ &UnicodeLsaInitEventName,
+ 0,
+ NULL,
+ &LsaInitEventSecurityDescriptor
+ );
+
+ //
+ // Create an event for use in synchronizing with the LSA. The LSA will
+ // signal this event when LSA initialization has reached the point
+ // where the LSA's Reference Monitor Server Port has been created.
+ //
+
+ Status = ZwCreateEvent(
+ &(SepRmState.LsaInitEventHandle),
+ EVENT_MODIFY_STATE,
+ &LsaInitEventObjectAttributes,
+ NotificationEvent,
+ FALSE);
+
+ RtlFreeUnicodeString( &UnicodeLsaInitEventName );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("Security: LSA init event creation failed.0x%xl\n",
+ Status));
+ return FALSE;
+ }
+
+ //
+ // Deallocate the pool memory used for the Init Event DACL
+ //
+
+ ExFreePool( LsaInitEventSecurityDescriptor.Dacl );
+
+ //
+ // Create a permanent thread of the Sysinit Process, called the
+ // Reference Monitor Server Thread. This thread is dedicated to
+ // receiving Reference Monitor commands and dispatching them.
+ //
+
+ Status = PsCreateSystemThread(
+ &SepRmState.SepRmThreadHandle,
+ THREAD_GET_CONTEXT |
+ THREAD_SET_CONTEXT |
+ THREAD_SET_INFORMATION,
+ NULL,
+ NULL,
+ NULL,
+ SepRmCommandServerThread,
+ NULL
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("Security: Rm Server Thread creation failed 0x%lx\n", Status));
+ return FALSE;
+ }
+
+ //
+ // Initialize data from the registry. This must go here because all other
+ // Se initialization takes place before the registry is initialized.
+ //
+
+ SepAdtInitializeCrashOnFail();
+ SepAdtInitializePrivilegeAuditing();
+
+ //
+ // Reference Monitor initialization is successful if we get to here.
+ //
+
+ ZwClose( SepRmState.SepRmThreadHandle );
+ SepRmState.SepRmThreadHandle = NULL;
+ return TRUE;
+}
+
+
+VOID
+SepRmCommandServerThread(
+ IN PVOID StartContext
+)
+
+/*++
+
+Routine Description:
+
+ This function is executed indefinitely by a dedicated permanent thread
+ of the Sysinit Process, called the Reference Monitor Server Thread.
+ This thread updates Reference Monitor Global State Data by dispatching
+ commands sent from the LSA through the the Reference Monitor LPC Command
+ Port. The following steps are repeated indefinitely:
+
+ o Initialize RM Command receive and reply buffer headers
+ o Perform remaining Reference Monitor initialization involving LSA
+ o Wait for RM command sent from LSA, send reply to previous command
+ (if any)
+ o Validate command
+ o Dispatch to command worker routine to execute command.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PRM_REPLY_MESSAGE Reply;
+ RM_COMMAND_MESSAGE CommandMessage;
+ RM_REPLY_MESSAGE ReplyMessage;
+
+ PAGED_CODE();
+
+ //
+ // Perform the rest of the Reference Monitor initialization, involving
+ // synchronization with the LSA or dependency on the LSA having run.
+ //
+
+ if (!SepRmCommandServerThreadInit()) {
+
+ KdPrint(("Security: Terminating Rm Command Server Thread\n"));
+ return;
+ }
+
+ //
+ // Initialize LPC port message header type and length fields for the
+ // received command message.
+ //
+
+ CommandMessage.MessageHeader.u2.ZeroInit = 0;
+ CommandMessage.MessageHeader.u1.s1.TotalLength =
+ (CSHORT) sizeof(RM_COMMAND_MESSAGE);
+ CommandMessage.MessageHeader.u1.s1.DataLength =
+ CommandMessage.MessageHeader.u1.s1.TotalLength -
+ (CSHORT) sizeof(PORT_MESSAGE);
+
+ //
+ // Initialize the LPC port message header type and data sizes for
+ // for the reply message.
+ //
+
+ ReplyMessage.MessageHeader.u2.ZeroInit = 0;
+ ReplyMessage.MessageHeader.u1.s1.TotalLength =
+ (CSHORT) sizeof(RM_COMMAND_MESSAGE);
+ ReplyMessage.MessageHeader.u1.s1.DataLength =
+ ReplyMessage.MessageHeader.u1.s1.TotalLength -
+ (CSHORT) sizeof(PORT_MESSAGE);
+
+ //
+ // First time through, there is no reply.
+ //
+
+ Reply = NULL;
+
+ //
+ // Now loop indefinitely, processing incoming Rm commands from the LSA.
+ //
+
+ for(;;) {
+
+ //
+ // Wait for Command, send reply to previous command (if any)
+ //
+
+ Status = ZwReplyWaitReceivePort(
+ SepRmState.RmCommandPortHandle,
+ NULL,
+ (PPORT_MESSAGE) Reply,
+ (PPORT_MESSAGE) &CommandMessage
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("Security: RM message receive from Lsa failed %lx\n",
+ Status));
+ return;
+ }
+
+ //
+ // Now dispatch to a routine to handle the command. Allow
+ // command errors to occur without bringing system down just now.
+ //
+
+ if ( CommandMessage.MessageHeader.u2.s2.Type == LPC_REQUEST ) {
+ (*(SepRmCommandDispatch[CommandMessage.CommandNumber]))
+ (&CommandMessage, &ReplyMessage);
+
+ //
+ // Initialize the client thread info and message id for the
+ // reply message. First time through, the reply message structure
+ // is not used.
+ //
+
+ ReplyMessage.MessageHeader.ClientId =
+ CommandMessage.MessageHeader.ClientId;
+ ReplyMessage.MessageHeader.MessageId =
+ CommandMessage.MessageHeader.MessageId;
+
+ Reply = &ReplyMessage;
+
+ } else {
+
+ Reply = NULL;
+ }
+ } // end_for
+
+ //
+ // Make compiler ferme la bouche
+ //
+
+ StartContext;
+}
+
+
+BOOLEAN
+SepRmCommandServerThreadInit(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This function performs initialization of the Reference Monitor Server
+ thread. The following steps are performed.
+
+ o Wait on the LSA signalling the event. When the event is signalled,
+ the LSA has already created the LSA Command Server LPC Port
+ o Close the LSA Init Event Handle. The event is not used again.
+ o Listen for the LSA to connect to the Port
+ o Accept the connection.
+ o Connect to the LSA Command Server LPC Port
+
+Arguments:
+
+ None.
+
+Return Value:
+
+--*/
+
+{
+ NTSTATUS Status;
+ UNICODE_STRING LsaCommandPortName;
+ PORT_MESSAGE ConnectionRequest;
+ SECURITY_QUALITY_OF_SERVICE DynamicQos;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ PORT_VIEW ClientView;
+ REMOTE_PORT_VIEW LsaClientView;
+ BOOLEAN BooleanStatus = TRUE;
+
+ PAGED_CODE();
+
+ //
+ // Save a pointer to our process so we can get back into this process
+ // to send commands to the LSA (using a handle to an LPC port created
+ // below).
+ //
+
+ SepRmLsaCallProcess = PsGetCurrentProcess();
+
+ ObReferenceObject(SepRmLsaCallProcess);
+
+ //
+ // Wait on the LSA signalling the event. This means that the LSA
+ // has created its command port, not that LSA initialization is
+ // complete.
+ //
+
+ Status = ZwWaitForSingleObject(
+ SepRmState.LsaInitEventHandle,
+ FALSE,
+ NULL);
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ KdPrint(("Security Rm Init: Waiting for LSA Init Event failed 0x%lx\n", Status));
+ goto RmCommandServerThreadInitError;
+ }
+
+ //
+ // Close the LSA Init Event Handle. The event is not used again.
+ //
+
+ ZwClose(SepRmState.LsaInitEventHandle);
+
+ //
+ // Listen for a connection to be made by the LSA to the Reference Monitor
+ // Command Port. This connection will be made by the LSA process.
+ //
+
+ ConnectionRequest.u1.s1.TotalLength = sizeof(ConnectionRequest);
+ ConnectionRequest.u1.s1.DataLength = (CSHORT)0;
+ Status = ZwListenPort(
+ SepRmState.RmCommandPortHandle,
+ &ConnectionRequest
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("Security Rm Init: Listen to Command Port failed 0x%lx\n",
+ Status));
+ goto RmCommandServerThreadInitError;
+ }
+
+ //
+ // Obtain a handle to the LSA process for use when auditing.
+ //
+
+ InitializeObjectAttributes( &ObjectAttributes, NULL, 0, NULL, NULL );
+
+ Status = ZwOpenProcess(
+ &SepLsaHandle,
+ PROCESS_VM_OPERATION | PROCESS_VM_WRITE,
+ &ObjectAttributes,
+ &ConnectionRequest.ClientId
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("Security Rm Init: Open Listen to Command Port failed 0x%lx\n",
+ Status));
+ goto RmCommandServerThreadInitError;
+ }
+
+ //
+ // Accept the connection made by the LSA process.
+ //
+
+ LsaClientView.Length = sizeof(LsaClientView);
+
+ Status = ZwAcceptConnectPort(
+ &SepRmState.RmCommandPortHandle,
+ NULL,
+ &ConnectionRequest,
+ TRUE,
+ NULL,
+ &LsaClientView
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("Security Rm Init: Accept Connect to Command Port failed 0x%lx\n",
+ Status));
+
+ goto RmCommandServerThreadInitError;
+ }
+
+ //
+ // Complete the connection.
+ //
+
+ Status = ZwCompleteConnectPort(SepRmState.RmCommandPortHandle);
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("Security Rm Init: Complete Connect to Command Port failed 0x%lx\n",
+ Status));
+ goto RmCommandServerThreadInitError;
+ }
+
+ //
+ // Set up the security quality of service parameters to use over the
+ // Lsa Command LPC port. Use the most efficient (least overhead) - which
+ // is dynamic rather than static tracking.
+ //
+
+ DynamicQos.ImpersonationLevel = SecurityImpersonation;
+ DynamicQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ DynamicQos.EffectiveOnly = TRUE;
+
+ //
+ // Create the section to be used as unnamed shared memory for
+ // communication between the RM and LSA.
+ //
+
+ SepRmState.LsaCommandPortSectionSize.LowPart = PAGE_SIZE;
+ SepRmState.LsaCommandPortSectionSize.HighPart = 0;
+
+ Status = ZwCreateSection(
+ &SepRmState.LsaCommandPortSectionHandle,
+ SECTION_ALL_ACCESS,
+ NULL, // ObjectAttributes
+ &SepRmState.LsaCommandPortSectionSize,
+ PAGE_READWRITE,
+ SEC_COMMIT,
+ NULL // FileHandle
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("Security Rm Init: Create Memory Section for LSA port failed: %X\n", Status));
+ goto RmCommandServerThreadInitError;
+ }
+
+ //
+ // Set up for a call to NtConnectPort and connect to the LSA port.
+ // This setup includes a description of the port memory section so that
+ // the LPC connection logic can make the section visible to both the
+ // client and server processes.
+ //
+
+ ClientView.Length = sizeof(ClientView);
+ ClientView.SectionHandle = SepRmState.LsaCommandPortSectionHandle;
+ ClientView.SectionOffset = 0;
+ ClientView.ViewSize = SepRmState.LsaCommandPortSectionSize.LowPart;
+ ClientView.ViewBase = 0;
+ ClientView.ViewRemoteBase = 0;
+
+ //
+ // Set up the security quality of service parameters to use over the
+ // port. Use dynamic tracking so that XACTSRV will impersonate the
+ // user that we are impersonating when we call NtRequestWaitReplyPort.
+ // If we used static tracking, XACTSRV would impersonate the context
+ // when the connection is made.
+ //
+
+ DynamicQos.ImpersonationLevel = SecurityImpersonation;
+ DynamicQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ DynamicQos.EffectiveOnly = TRUE;
+
+ //
+ // Connect to the Lsa Command LPC Port. This port is used to send
+ // commands from the RM to the LSA.
+ //
+
+ RtlInitUnicodeString( &LsaCommandPortName, L"\\SeLsaCommandPort" );
+
+ Status = ZwConnectPort(
+ &SepRmState.LsaCommandPortHandle,
+ &LsaCommandPortName,
+ &DynamicQos,
+ &ClientView,
+ NULL, // ServerView
+ NULL, // MaxMessageLength
+ NULL, // ConnectionInformation
+ NULL // ConnectionInformationLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ KdPrint(("Security Rm Init: Connect to LSA Port failed 0x%lx\n", Status));
+ goto RmCommandServerThreadInitError;
+ }
+
+ //
+ // Store information about the section so that we can create pointers
+ // meaningful to LSA.
+ //
+
+ SepRmState.RmViewPortMemory = ClientView.ViewBase;
+ SepRmState.LsaCommandPortMemoryDelta = (LONG)( (ULONG) ClientView.ViewRemoteBase -
+ (ULONG) ClientView.ViewBase );
+ SepRmState.LsaViewPortMemory = ClientView.ViewRemoteBase;
+
+/* BugWarning - ScottBi - probably don't need the resource
+
+ //
+ // Create the resource serializing access to the port. This
+ // resource prevents the port and the shared memory from being
+ // deleted while worker threads are processing requests.
+ //
+
+ if ( !SepRmState.LsaCommandPortResourceInitialized ) {
+
+ ExInitializeResource( &SepRmState.LsaCommandPortResource );
+ SepRmState.LsaCommandPortResourceInitialized = TRUE;
+ }
+
+ SepRmState.LsaCommandPortActive = TRUE;
+
+*/
+
+RmCommandServerThreadInitFinish:
+
+ //
+ // Dont need this section handle any more, even if returning
+ // success.
+ //
+
+ if ( SepRmState.LsaCommandPortSectionHandle != NULL ) {
+
+ NtClose( SepRmState.LsaCommandPortSectionHandle );
+ SepRmState.LsaCommandPortSectionHandle = NULL;
+ }
+
+ //
+ // The Reference Monitor Thread has successfully initialized.
+ //
+
+ return BooleanStatus;
+
+RmCommandServerThreadInitError:
+
+ if ( SepRmState.LsaCommandPortHandle != NULL ) {
+
+ NtClose( SepRmState.LsaCommandPortHandle );
+ SepRmState.LsaCommandPortHandle = NULL;
+ }
+
+ BooleanStatus = FALSE;
+ goto RmCommandServerThreadInitFinish;
+}
+
+
+
+VOID
+SepRmComponentTestCommandWrkr(
+ IN PRM_COMMAND_MESSAGE CommandMessage,
+ OUT PRM_REPLY_MESSAGE ReplyMessage
+ )
+
+/*++
+
+Routine Description:
+
+ BUGWARNING - Remove this command when other RM commands are implemented.
+ Until then, this command is the only way a CT can verify that
+ an RM command with parameters is sent correctly.
+
+ This function processes the Component Test RM command.
+ This is a temporary command that can be used to verify that the link
+ from RM to LSA is working. This command verifies that the link
+ is working by receiving a ULONG parameter and verifying that it
+ has the expected value.
+
+Arguments:
+
+ CommandMessage - Pointer to structure containing RM command message
+ information consisting of an LPC PORT_MESSAGE structure followed
+ by the command number (RmComponentTestCommand). This command
+ currently has one parameter, a fixed ulong value.
+
+ ReplyMessage - Pointer to structure containing LSA reply message
+ information consisting of an LPC PORT_MESSAGE structure followed
+ by the command ReturnedStatus field in which a status code from the
+ command will be returned.
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+ PAGED_CODE();
+
+ ReplyMessage->ReturnedStatus = STATUS_SUCCESS;
+
+ //
+ // Strict check that command is correct.
+ //
+
+ ASSERT( CommandMessage->CommandNumber == RmComponentTestCommand );
+
+ KdPrint(("Security: RM Component Test Command Received\n"));
+
+ //
+ // Verify that the parameter value passed is as expected.
+ //
+
+ if (*((ULONG *) CommandMessage->CommandParams) !=
+ RM_CT_COMMAND_PARAM_VALUE ) {
+
+ ReplyMessage->ReturnedStatus = STATUS_INVALID_PARAMETER;
+ }
+
+ return;
+}
+
+
+
+VOID
+SepRmSendCommandToLsaWrkr(
+ IN PRM_COMMAND_MESSAGE CommandMessage,
+ OUT PRM_REPLY_MESSAGE ReplyMessage
+ )
+
+/*++
+
+Routine Description:
+
+ This function carries out the special Rm Send Command To Lsa Command. This
+ command is used only by the ctlsarm component test which checks that
+ LSA to RM and RM to LSA communication via LPC is working.
+
+Arguments:
+
+ CommandMessage - Pointer to structure containing RM command message
+ information consisting of an LPC PORT_MESSAGE structure followed
+ by the command number (RmDisableAuditCommand), followed by the
+ command parameters. The parameters of this special command consists
+ of the Command Number of an LSA command and its parameters (if any).
+
+ ReplyMessage - Pointer to structure containing RM reply message
+ information consisting of an LPC PORT_MESSAGE structure followed
+ by the command ReturnedStatus field in which a status code from the
+ command will be returned.
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+ //
+ // Obtain a pointer to the LSA command's params, and the size of the
+ // params in bytes. If there are no params, set the pointer to NULL.
+ //
+
+ PVOID LsaCommandParams =
+ ((RM_SEND_COMMAND_TO_LSA_PARAMS *)
+ (CommandMessage->CommandParams))->LsaCommandParams;
+ ULONG LsaCommandParamsLength =
+ ((RM_SEND_COMMAND_TO_LSA_PARAMS *)
+ (CommandMessage->CommandParams))->LsaCommandParamsLength;
+
+ PAGED_CODE();
+
+ if (LsaCommandParamsLength == 0) {
+
+ LsaCommandParams = NULL;
+
+ }
+
+ //
+ // Strict check that command is correct one for this worker.
+ //
+
+ ASSERT( CommandMessage->CommandNumber == RmSendCommandToLsaCommand );
+
+ KdPrint(("Security: RM Send Command back to LSA Command Received\n"));
+
+ ReplyMessage->ReturnedStatus = STATUS_SUCCESS;
+
+// Status = SepRmCallLsa(
+// ((RM_SEND_COMMAND_TO_LSA_PARAMS *)
+// (CommandMessage->CommandParams))->LsaCommandNumber,
+// LsaCommandParams,
+// LsaCommandParamsLength,
+// NULL,
+// 0,
+// NULL,
+// NULL
+// );
+
+
+ return;
+
+}
+
+
+
+
+NTSTATUS
+SepRmCallLsa(
+ PSEP_WORK_ITEM SepWorkItem
+ )
+/*++
+
+Routine Description:
+
+ This function sends a command to the LSA via the LSA Reference Monitor
+ Server Command LPC Port. If the command has parameters, they will be
+ copied directly into a message structure and sent via LPC, therefore,
+ the supplied parameters may not contain any absolute pointers. A caller
+ must remove pointers by "marshalling" them into the buffer CommandParams.
+
+ This function will create a queue of requests. This is in order to allow
+ greater throughput for the majority if its callers. If a thread enters
+ this routine and finds the queue empty, it is the responsibility of that
+ thread to service all requests that come in while it is working until the
+ queue is empty again. Other threads that enter will simply hook their work
+ item onto the queue and exit.
+
+
+ To implement a new LSA command, do the following:
+ ================================================
+
+ (1) If the command takes no parameters, just call this routine directly
+ and provide an LSA worker routine called Lsap<command>Wrkr. See
+ file lsa\server\lsarm.c for examples
+
+ (2) If the command takes parameters, provide a routine called
+ SepRmSend<command>Command that takes the parameters in unmarshalled
+ form and calls SepRmCallLsa() with the command id, marshalled
+ parameters, length of marshalled parameters and pointer to
+ optional reply message. The marshalled parameters are free format:
+ the only restriction is that there must be no absolute address
+ pointers. These parameters are all placed in the passed LsaWorkItem
+ structure.
+
+ (3) In file private\inc\ntrmlsa.h, append a command name to the
+ enumerated type LSA_COMMAND_NUMBER defined in file
+ private\inc\ntrmlsa.h. Change the #define for LsapMaximumCommand
+ to reference the new command.
+
+ (4) Add the Lsap<command>Wrkr to the command dispatch table structure
+ LsapCommandDispatch[] in file lsarm.c.
+
+ (5) Add function prototypes to lsap.h and sep.h.
+
+
+Arguments:
+
+ LsaWorkItem - Supplies a pointer to an SE_LSA_WORK_ITEM containing the
+ information to be passed to LSA. This structure will be freed
+ asynchronously by some invocation of this routine, not necessarily
+ in the current context.
+
+ !THIS PARAMETER MUST BE ALLOCATED OUT OF NONPAGED POOL!
+
+Return Value:
+
+ NTSTATUS - Result Code. This is either a result code returned from
+ trying to send the command/receive the reply, or a status code
+ from the command itself.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ LSA_COMMAND_MESSAGE CommandMessage;
+ LSA_REPLY_MESSAGE ReplyMessage;
+ PSEP_LSA_WORK_ITEM WorkQueueItem;
+ ULONG LocalListLength = 0;
+ ULONG RegionSize;
+ PVOID CopiedCommandParams = NULL;
+ PVOID LsaViewCopiedCommandParams = NULL;
+
+ PAGED_CODE();
+
+#if 0
+ DbgPrint("Entering SepRmCallLsa\n");
+#endif
+
+ WorkQueueItem = SepWorkListHead();
+
+ KeAttachProcess( &SepRmLsaCallProcess->Pcb );
+
+ while ( WorkQueueItem ) {
+
+#if 0
+ DbgPrint("Got a work item from head of queue, processing\n");
+#endif
+
+ //
+ // Construct a message for LPC. First, fill in the message header
+ // fields for LPC, specifying the message type and data sizes for
+ // the outgoing CommandMessage and the incoming ReplyMessage.
+ //
+
+ CommandMessage.MessageHeader.u2.ZeroInit = 0;
+ CommandMessage.MessageHeader.u1.s1.TotalLength =
+ ((CSHORT) RM_COMMAND_MESSAGE_HEADER_SIZE +
+ (CSHORT) WorkQueueItem->CommandParamsLength);
+ CommandMessage.MessageHeader.u1.s1.DataLength =
+ CommandMessage.MessageHeader.u1.s1.TotalLength -
+ (CSHORT) sizeof(PORT_MESSAGE);
+
+ ReplyMessage.MessageHeader.u2.ZeroInit = 0;
+ ReplyMessage.MessageHeader.u1.s1.DataLength = (CSHORT) WorkQueueItem->ReplyBufferLength;
+ ReplyMessage.MessageHeader.u1.s1.TotalLength =
+ ReplyMessage.MessageHeader.u1.s1.DataLength +
+ (CSHORT) sizeof(PORT_MESSAGE);
+
+ //
+ // Next, fill in the header info needed by the LSA.
+ //
+
+ CommandMessage.CommandNumber = WorkQueueItem->CommandNumber;
+ ReplyMessage.ReturnedStatus = STATUS_SUCCESS;
+
+ //
+ // Set up the Command Parameters either in the LPC Command Message
+ // itself, in the preallocated Lsa shared memory block, or in a
+ // specially allocated block. The parameters are either
+ // immediate (i.e. in the WorkQueueItem itself, or are in a buffer
+ // pointed to by the address in the WorkQueueItem.
+ //
+
+ switch (WorkQueueItem->CommandParamsMemoryType) {
+
+ case SepRmImmediateMemory:
+
+ //
+ // The Command Parameters are in the CommandParams buffer
+ // in the Work Queue Item. Just copy them to the corresponding
+ // buffer in the CommandMessage buffer.
+ //
+
+ CommandMessage.CommandParamsMemoryType = SepRmImmediateMemory;
+
+ RtlMoveMemory(
+ CommandMessage.CommandParams,
+ &WorkQueueItem->CommandParams,
+ WorkQueueItem->CommandParamsLength
+ );
+
+ break;
+
+ case SepRmPagedPoolMemory:
+ case SepRmUnspecifiedMemory:
+
+ //
+ // The Command Parameters are contained in paged pool memory.
+ // Since this memory is is not accessible by the LSA, we must
+ // copy of them either to the LPC Command Message Block, or
+ // into LSA shared memory.
+ //
+
+ if (WorkQueueItem->CommandParamsLength <= LSA_MAXIMUM_COMMAND_PARAM_SIZE) {
+
+ //
+ // Parameters will fit into the LPC Command Message block.
+ //
+
+ CopiedCommandParams = CommandMessage.CommandParams;
+
+ RtlMoveMemory(
+ CopiedCommandParams,
+ WorkQueueItem->CommandParams.BaseAddress,
+ WorkQueueItem->CommandParamsLength
+ );
+
+ CommandMessage.CommandParamsMemoryType = SepRmImmediateMemory;
+
+ } else {
+
+ //
+ // Parameters too large for LPC Command Message block.
+ // If possible, copy them to the preallocated Lsa Shared
+ // Memory block. If they are too large to fit, copy them
+ // to an individually allocated chunk of Shared Virtual
+ // Memory.
+ //
+
+ if (WorkQueueItem->CommandParamsLength <= SEP_RM_LSA_SHARED_MEMORY_SIZE) {
+
+ RtlMoveMemory(
+ SepRmState.RmViewPortMemory,
+ WorkQueueItem->CommandParams.BaseAddress,
+ WorkQueueItem->CommandParamsLength
+ );
+
+ LsaViewCopiedCommandParams = SepRmState.LsaViewPortMemory;
+ CommandMessage.CommandParamsMemoryType = SepRmLsaCommandPortSharedMemory;
+
+ } else {
+
+ Status = SepAdtCopyToLsaSharedMemory(
+ SepLsaHandle,
+ WorkQueueItem->CommandParams.BaseAddress,
+ WorkQueueItem->CommandParamsLength,
+ &LsaViewCopiedCommandParams
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ //
+ // An error occurred, most likely in allocating
+ // shared virtual memory. For now, just ignore
+ // the error and discard the Audit Record. Later,
+ // we may consider generating a warning record
+ // indicating some records lost.
+ //
+
+ break;
+
+ }
+
+ CommandMessage.CommandParamsMemoryType = SepRmLsaCustomSharedMemory;
+ }
+
+ //
+ // Buffer has been successfully copied to a shared Lsa
+ // memory buffer. Place the address of the buffer valid in
+ // the LSA's process context in the Command Message.
+ //
+
+ *((PVOID *) CommandMessage.CommandParams) =
+ LsaViewCopiedCommandParams;
+
+ CommandMessage.MessageHeader.u1.s1.TotalLength =
+ ((CSHORT) RM_COMMAND_MESSAGE_HEADER_SIZE +
+ (CSHORT) sizeof( LsaViewCopiedCommandParams ));
+ CommandMessage.MessageHeader.u1.s1.DataLength =
+ CommandMessage.MessageHeader.u1.s1.TotalLength -
+ (CSHORT) sizeof(PORT_MESSAGE);
+ }
+
+ //
+ // Free input command params buffer if Paged Pool.
+ //
+
+ if (WorkQueueItem->CommandParamsMemoryType == SepRmPagedPoolMemory) {
+
+ ExFreePool( WorkQueueItem->CommandParams.BaseAddress );
+ }
+
+ break;
+
+ default:
+
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Send Message to the LSA via the LSA Server Command LPC Port.
+ // This must be done in the process in which the handle was created.
+ //
+
+ Status = ZwRequestWaitReplyPort(
+ SepRmState.LsaCommandPortHandle,
+ (PPORT_MESSAGE) &CommandMessage,
+ (PPORT_MESSAGE) &ReplyMessage
+ );
+
+ //
+ // If the command was successful, copy the data back to the output
+ // buffer.
+ //
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Move output from command (if any) to buffer. Note that this
+ // is done even if the command returns status, because some status
+ // values are not errors.
+ //
+
+ if (ARGUMENT_PRESENT(WorkQueueItem->ReplyBuffer)) {
+
+ RtlMoveMemory(
+ WorkQueueItem->ReplyBuffer,
+ ReplyMessage.ReplyBuffer,
+ WorkQueueItem->ReplyBufferLength
+ );
+ }
+
+ //
+ // Return status from command.
+ //
+
+ Status = ReplyMessage.ReturnedStatus;
+
+ if (!NT_SUCCESS(Status)) {
+ KdPrint(("Security: Command sent from RM to LSA returned 0x%lx\n",
+ Status));
+ }
+
+ } else {
+
+ KdPrint(("Security: Sending Command RM to LSA failed 0x%lx\n", Status));
+ }
+
+ //
+ // On return from the LPC call to the LSA, we expect the called
+ // LSA worker routine to have copied the Command Parameters
+ // buffer (if any). If a custom shared memory boffer was allocated,
+ // free it now.
+ //
+
+ if (CommandMessage.CommandParamsMemoryType == SepRmLsaCustomSharedMemory) {
+
+ RegionSize = 0;
+
+ Status = ZwFreeVirtualMemory(
+ SepLsaHandle,
+ (PVOID *) &CommandMessage.CommandParams,
+ &RegionSize,
+ MEM_RELEASE
+ );
+
+ ASSERT(NT_SUCCESS(Status));
+ }
+
+ }
+
+
+ //
+ // Clean up. We must call the cleanup functions on its parameter
+ // and then free the used WorkQueueItem itself.
+ //
+
+ if ( ARGUMENT_PRESENT( WorkQueueItem->CleanupFunction)) {
+
+ (WorkQueueItem->CleanupFunction)(WorkQueueItem->CleanupParameter);
+ }
+
+ //
+ // Determine if there is more work to do on this list
+ //
+
+ WorkQueueItem = SepDequeueWorkItem();
+#if 0
+ if ( WorkQueueItem ) {
+ DbgPrint("Got another item from list, going back\n");
+ } else {
+ DbgPrint("List is empty, leaving\n");
+ }
+#endif
+
+
+ }
+
+ KeDetachProcess();
+
+ if ( LocalListLength > SepLsaQueueLength ) {
+ SepLsaQueueLength = LocalListLength;
+ }
+
+ return Status;
+}
+
+
+
+
+
+BOOLEAN
+SepRmInitPhase0(
+ )
+
+/*++
+
+Routine Description:
+
+ This function performs Reference Monitor Phase 0 initialization.
+ This includes initializing the reference monitor database to a state
+ which allows access validation routines to operate (always granting
+ access) prior to the main init of the Reference Monitor in Phase 1
+ initialization, without having to check if the RM is initialized.
+
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ BOOLEAN - TRUE if successful, else FALSE
+
+--*/
+
+{
+
+ BOOLEAN CompletionStatus;
+
+ PAGED_CODE();
+
+ CompletionStatus = SepRmDbInitialization();
+
+ return CompletionStatus;
+}
diff --git a/private/ntos/se/rmp.h b/private/ntos/se/rmp.h
new file mode 100644
index 000000000..f20c6f657
--- /dev/null
+++ b/private/ntos/se/rmp.h
@@ -0,0 +1,229 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ rmp.h
+
+Abstract:
+
+ Security Reference Monitor Private Data Types, Functions and Defines
+
+Author:
+
+ Scott Birrell (ScottBi) March 12, 1991
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntlsa.h>
+#include "sep.h"
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Reference Monitor Private defines //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+
+//
+// Used to define the bounds of the array used to track logon session
+// reference counts.
+//
+
+#define SEP_LOGON_TRACK_INDEX_MASK (0x0000000FL)
+#define SEP_LOGON_TRACK_ARRAY_SIZE (0x00000010L)
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Reference Monitor Private Macros //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+//
+// acquire exclusive access to a token
+//
+
+#define SepRmAcquireDbReadLock() KeEnterCriticalRegion(); \
+ ExAcquireResourceShared(&SepRmDbLock, TRUE)
+
+#define SepRmAcquireDbWriteLock() KeEnterCriticalRegion(); \
+ ExAcquireResourceExclusive(&SepRmDbLock, TRUE)
+
+#define SepRmReleaseDbReadLock() ExReleaseResource(&SepRmDbLock); \
+ KeLeaveCriticalRegion()
+
+#define SepRmReleaseDbWriteLock() ExReleaseResource(&SepRmDbLock); \
+ KeLeaveCriticalRegion()
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Reference Monitor Private Data Types //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+#define SEP_RM_LSA_SHARED_MEMORY_SIZE ((ULONG) PAGE_SIZE)
+
+//
+// Reference Monitor Private Global State Data Structure
+//
+
+typedef struct _SEP_RM_STATE {
+
+ HANDLE LsaInitEventHandle;
+ HANDLE LsaCommandPortHandle;
+ HANDLE SepRmThreadHandle;
+ HANDLE RmCommandPortHandle;
+ ULONG AuditingEnabled;
+ LSA_OPERATIONAL_MODE OperationalMode;
+ HANDLE LsaCommandPortSectionHandle;
+ LARGE_INTEGER LsaCommandPortSectionSize;
+ PVOID LsaViewPortMemory;
+ PVOID RmViewPortMemory;
+ LONG LsaCommandPortMemoryDelta;
+ BOOLEAN LsaCommandPortResourceInitialized;
+ BOOLEAN LsaCommandPortActive;
+ ERESOURCE LsaCommandPortResource;
+
+} SEP_RM_STATE, *PSEP_RM_STATE;
+
+//
+// Reference Monitor Command Port Connection Info
+//
+
+typedef struct _SEP_RM_CONNECT_INFO {
+ ULONG ConnectInfo;
+} SEP_RM_CONNECT_INFO;
+
+typedef struct SEP_RM_CONNECT_INFO *PSEP_RM_CONNECT_INFO;
+
+
+//
+// Reference Monitor Command Table Entry Format
+//
+
+#define SEP_RM_COMMAND_MAX 4
+
+typedef VOID (*SEP_RM_COMMAND_WORKER)( PRM_COMMAND_MESSAGE, PRM_REPLY_MESSAGE );
+
+
+
+//
+// Each logon session active in the system has a corresponding record of
+// the following type...
+//
+
+typedef struct _SEP_LOGON_SESSION_REFERENCES {
+ struct _SEP_LOGON_SESSION_REFERENCES *Next;
+ LUID LogonId;
+ ULONG ReferenceCount;
+ ULONG Flags;
+} SEP_LOGON_SESSION_REFERENCES, *PSEP_LOGON_SESSION_REFERENCES;
+
+#define SEP_TERMINATION_NOTIFY 0x1
+
+//
+// File systems interested in being notified when a logon session is being
+// terminated register a callback routine. The following data structure
+// describes the callback routines.
+//
+// The global list of callback routines is pointed to by SeFileSystemNotifyRoutines.
+// This list is protected by the RM database lock.
+//
+
+typedef struct _SEP_LOGON_SESSION_TERMINATED_NOTIFICATION {
+ struct _SEP_LOGON_SESSION_TERMINATED_NOTIFICATION *Next;
+ PSE_LOGON_SESSION_TERMINATED_ROUTINE CallbackRoutine;
+} SEP_LOGON_SESSION_TERMINATED_NOTIFICATION, *PSEP_LOGON_SESSION_TERMINATED_NOTIFICATION;
+
+extern SEP_LOGON_SESSION_TERMINATED_NOTIFICATION
+SeFileSystemNotifyRoutinesHead;
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Reference Monitor Private Function Prototypes //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+BOOLEAN
+SepRmDbInitialization(
+ VOID
+ );
+
+VOID
+SepRmCommandServerThread(
+ IN PVOID StartContext
+ );
+
+BOOLEAN SepRmCommandServerThreadInit(
+ );
+
+VOID
+SepRmComponentTestCommandWrkr(
+ IN PRM_COMMAND_MESSAGE CommandMessage,
+ OUT PRM_REPLY_MESSAGE ReplyMessage
+ );
+
+VOID
+SepRmSetAuditEventWrkr(
+ IN PRM_COMMAND_MESSAGE CommandMessage,
+ OUT PRM_REPLY_MESSAGE ReplyMessage
+ );
+
+VOID
+SepRmSendCommandToLsaWrkr(
+ IN PRM_COMMAND_MESSAGE CommandMessage,
+ OUT PRM_REPLY_MESSAGE ReplyMessage
+ );
+
+VOID
+SepRmCreateLogonSessionWrkr(
+ IN PRM_COMMAND_MESSAGE CommandMessage,
+ OUT PRM_REPLY_MESSAGE ReplyMessage
+ );
+
+VOID
+SepRmDeleteLogonSessionWrkr(
+ IN PRM_COMMAND_MESSAGE CommandMessage,
+ OUT PRM_REPLY_MESSAGE ReplyMessage
+ ) ;
+
+
+NTSTATUS
+SepCreateLogonSessionTrack(
+ IN PLUID LogonId
+ );
+
+NTSTATUS
+SepDeleteLogonSessionTrack(
+ IN PLUID LogonId
+ );
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Reference Monitor Private Variables Declarations //
+// These variables are defined in rmvars.c //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+extern PEPROCESS SepRmLsaCallProcess;
+extern SEP_RM_STATE SepRmState;
+extern ERESOURCE SepRmDbLock;
+extern PSEP_LOGON_SESSION_REFERENCES *SepLogonSessions;
diff --git a/private/ntos/se/rmvars.c b/private/ntos/se/rmvars.c
new file mode 100644
index 000000000..8e9245c37
--- /dev/null
+++ b/private/ntos/se/rmvars.c
@@ -0,0 +1,179 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ rmvars.c
+
+Abstract:
+
+ This module contains the variables used to implement the run-time
+ reference monitor database.
+
+Author:
+
+ Jim Kelly (JimK) 2-Apr-1991
+
+Environment:
+
+ Kernel mode only.
+
+Revision History:
+
+--*/
+
+#include "rmp.h"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(INIT,SepRmDbInitialization)
+#endif
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// //
+// Read Only Reference Monitor Variables //
+// //
+////////////////////////////////////////////////////////////////////////////////
+
+
+//
+// The process within which the RM --> LSA command LPC port was established.
+// All calls from the reference monitor to the LSA must be made in this
+// process in order for the handle to be valid.
+
+PEPROCESS SepRmLsaCallProcess;
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// //
+// Read/Write Reference Monitor Variables //
+// //
+// Access to these variables is protected by the SepRmDbLock. //
+// //
+////////////////////////////////////////////////////////////////////////////////
+
+
+//
+// Resource Lock - This lock protects access to the modifiable fields of
+// the reference monitor database
+//
+
+ERESOURCE SepRmDbLock;
+
+
+//
+// State of the reference monitor
+//
+
+SEP_RM_STATE SepRmState;
+
+
+
+//
+// The following array is used as a hash bucket for tracking logon sessions.
+// The sequence number of logon LUIDs is ANDed with 0x0F and then used as an
+// index into this array. This entry in the array serves as a listhead of
+// logon session reference count records.
+//
+
+PSEP_LOGON_SESSION_REFERENCES *SepLogonSessions = NULL;
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Variable Initialization Routines //
+// //
+////////////////////////////////////////////////////////////////////////
+
+BOOLEAN
+SepRmDbInitialization(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function initializes the reference monitor in-memory database.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ TRUE if database successfully initialized.
+ FALSE if not successfully initialized.
+
+--*/
+{
+ NTSTATUS Status;
+ ULONG i;
+
+
+ //
+ // Create the reference monitor database lock
+ //
+ // Use SepRmAcquireDbReadLock()
+ // SepRmAcquireDbWriteLock()
+ // SepRmReleaseDbReadLock()
+ // SepRmReleaseDbWriteLock()
+ //
+ // to gain access to the reference monitor database.
+ //
+
+ ExInitializeResource(&SepRmDbLock);
+
+ //
+ // Initialize the Logon Session tracking array.
+ //
+
+ SepLogonSessions = ExAllocatePoolWithTag( PagedPool,
+ sizeof( PSEP_LOGON_SESSION_REFERENCES ) * SEP_LOGON_TRACK_ARRAY_SIZE,
+ 'SLeS'
+ );
+
+ if (SepLogonSessions == NULL) {
+ return( FALSE );
+ }
+
+ for (i=0;i<SEP_LOGON_TRACK_ARRAY_SIZE;i++) {
+
+ SepLogonSessions[ i ] = NULL;
+ }
+
+ //
+ // Now add in a record representing the system logon session.
+ //
+
+ Status = SepCreateLogonSessionTrack( &SeSystemAuthenticationId );
+ ASSERT( NT_SUCCESS(Status) );
+ if ( !NT_SUCCESS(Status)) {
+ return FALSE;
+ }
+
+
+
+
+ //
+ // The correct RM state will be set when the local security policy
+ // information is retrieved (by the LSA) and subsequently passed to
+ // the reference monitor later on in initialization. For now, initialize
+ // the state to something that will work for the remainder of
+ // system initialization.
+ //
+
+ SepRmState.AuditingEnabled = 0; // auditing state disabled.
+ SepRmState.OperationalMode = LSA_MODE_PASSWORD_PROTECTED;
+
+
+
+ return TRUE;
+
+
+}
diff --git a/private/ntos/se/seassign.c b/private/ntos/se/seassign.c
new file mode 100644
index 000000000..59a491e7e
--- /dev/null
+++ b/private/ntos/se/seassign.c
@@ -0,0 +1,1924 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ Seassign.c
+
+Abstract:
+
+ This Module implements the SeAssignSecurity procedure. For a description
+ of the pool allocation strategy please see the comments in semethod.c
+
+Author:
+
+ Gary Kimura (GaryKi) 9-Nov-1989
+
+Environment:
+
+ Kernel Mode
+
+Revision History:
+
+ Richard Ward (RichardW) 14-April-92
+ Robert Reichel (RobertRe) 28-February-95
+ Added Compound ACEs
+
+--*/
+
+
+#include "sep.h"
+#include "tokenp.h"
+#include "sertlp.h"
+#include "zwapi.h"
+
+
+
+//
+// Local macros and procedures
+//
+
+//
+// Macros to determine if an ACE contains one of the Creator SIDs
+//
+
+#define ContainsCreatorOwnerSid(Ace) ( \
+ RtlEqualSid( &((PKNOWN_ACE)( Ace ))->SidStart, SeCreatorOwnerSid ) \
+ )
+
+#define ContainsCreatorGroupSid(Ace) ( \
+ RtlEqualSid( &((PKNOWN_ACE)( Ace ))->SidStart, SeCreatorGroupSid ) \
+ )
+
+
+
+VOID
+SepApplyAclToObject (
+ IN PACL Acl,
+ IN PGENERIC_MAPPING GenericMapping
+ );
+
+NTSTATUS
+SepInheritAcl (
+ IN PACL Acl,
+ IN BOOLEAN IsDirectoryObject,
+ IN PSID OwnerSid,
+ IN PSID GroupSid,
+ IN PSID ServerSid OPTIONAL,
+ IN PSID ClientSid OPTIONAL,
+ IN PGENERIC_MAPPING GenericMapping,
+ IN POOL_TYPE PoolType,
+ OUT PACL *NewAcl
+ );
+
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE,SeAssignSecurity)
+#pragma alloc_text(PAGE,SeDeassignSecurity)
+#pragma alloc_text(PAGE,SepApplyAclToObject)
+#pragma alloc_text(PAGE,SepInheritAcl)
+#pragma alloc_text(PAGE,SeAssignWorldSecurityDescriptor)
+#pragma alloc_text(PAGE,SepDumpSecurityDescriptor)
+#pragma alloc_text(PAGE,SepPrintAcl)
+#pragma alloc_text(PAGE,SepPrintSid)
+#pragma alloc_text(PAGE,SepDumpTokenInfo)
+#pragma alloc_text(PAGE,SepSidTranslation)
+#endif
+
+
+//
+// These variables control whether security descriptors and token
+// information are dumped by their dump routines. This allows
+// selective turning on and off of debugging output by both program
+// control and via the kernel debugger.
+//
+
+#if DBG
+
+BOOLEAN SepDumpSD = FALSE;
+BOOLEAN SepDumpToken = FALSE;
+
+#endif
+
+
+NTSTATUS
+SeAssignSecurity (
+ IN PSECURITY_DESCRIPTOR ParentDescriptor OPTIONAL,
+ IN PSECURITY_DESCRIPTOR ExplicitDescriptor OPTIONAL,
+ OUT PSECURITY_DESCRIPTOR *NewDescriptor,
+ IN BOOLEAN IsDirectoryObject,
+ IN PSECURITY_SUBJECT_CONTEXT SubjectContext,
+ IN PGENERIC_MAPPING GenericMapping,
+ IN POOL_TYPE PoolType
+ )
+
+/*++
+
+Routine Description:
+
+ This routine assumes privilege checking HAS NOT yet been performed
+ and so will be performed by this routine.
+
+ This procedure is used to build a security descriptor for a new object
+ given the security descriptor of its parent directory and any originally
+ requested security for the object. The final security descriptor
+ returned to the caller may contain a mix of information, some explicitly
+ provided other from the new object's parent.
+
+ System and Discretionary ACL Assignment
+ ---------------------------------------
+
+ The assignment of system and discretionary ACLs is governed by the
+ logic illustrated in the following table (numbers in the cells refer
+ to comments in the code):
+
+ | Explicit | Explicit |
+ | (non-default) | Default | No
+ | Acl | Acl | Acl
+ | Specified | Specified | Specified
+ -------------+----------------+---------------+--------------
+ | (1)| (3)| (5)
+ Inheritable | Assign | Assign | Assign
+ Acl From | Specified | Inherited | Inherited
+ Parent | Acl | Acl | Acl
+ | | |
+ -------------+----------------+---------------+--------------
+ No | (2)| (4)| (6)
+ Inheritable | Assign | Assign | Assign
+ Acl From | Specified | Default | No Acl
+ Parent | Acl | Acl |
+ | | |
+ -------------+----------------+---------------+--------------
+
+ Note that an explicitly specified ACL, whether a default ACL or
+ not, may be empty or null.
+
+ If the caller is explicitly assigning a system acl, default or
+ non-default, the caller must either be a kernel mode client or
+ must be appropriately privileged.
+
+
+ Owner and Group Assignment
+ --------------------------
+
+ The assignment of the new object's owner and group is governed
+ by the following logic:
+
+ 1) If the passed security descriptor includes an owner, it
+ is assigned as the new object's owner. Otherwise, the
+ caller's token is looked in for the owner. Within the
+ token, if there is a default owner, it is assigned.
+ Otherwise, the caller's user ID is assigned.
+
+ 2) If the passed security descriptor includes a group, it
+ is assigned as the new object's group. Otherwise, the
+ caller's token is looked in for the group. Within the
+ token, if there is a default group, it is assigned.
+ Otherwise, the caller's primary group ID is assigned.
+
+
+
+Arguments:
+
+ ParentDescriptor - Optionally supplies the security descriptor of the
+ parent directory under which this new object is being created.
+
+ ExplicitDescriptor - Supplies the address of a pointer to the security
+ descriptor as specified by the user that is to be applied to
+ the new object.
+
+ NewDescriptor - Returns the actual security descriptor for the new
+ object that has been modified according to above rules.
+
+ IsDirectoryObject - Specifies if the new object is itself a directory
+ object. A value of TRUE indicates the object is a container of other
+ objects.
+
+ SubjectContext - Supplies the security context of the subject creating the
+ object. This is used to retrieve default security information for the
+ new object, such as default owner, primary group, and discretionary
+ access control.
+
+ GenericMapping - Supplies a pointer to an array of access mask values
+ denoting the mapping between each generic right to non-generic rights.
+
+ PoolType - Specifies the pool type to use to when allocating a new
+ security descriptor.
+
+Return Value:
+
+ STATUS_SUCCESS - indicates the operation was successful.
+
+ STATUS_INVALID_OWNER - The owner SID provided as the owner of the
+ target security descriptor is not one the caller is authorized
+ to assign as the owner of an object.
+
+ STATUS_PRIVILEGE_NOT_HELD - The caller does not have the privilege
+ necessary to explicitly assign the specified system ACL.
+ SeSecurityPrivilege privilege is needed to explicitly assign
+ system ACLs to objects.
+--*/
+
+{
+
+
+ KPROCESSOR_MODE RequestorMode;
+
+ SECURITY_DESCRIPTOR *CapturedDescriptor;
+ SECURITY_DESCRIPTOR InCaseOneNotPassed;
+ BOOLEAN SecurityDescriptorPassed;
+
+ NTSTATUS Status;
+
+ BOOLEAN RequestorCanAssignDescriptor = TRUE;
+
+ PACL NewSacl = NULL;
+ BOOLEAN NewSaclPresent = FALSE;
+ BOOLEAN NewSaclInherited = FALSE;
+
+ PACL NewDacl = NULL;
+ BOOLEAN NewDaclPresent = FALSE;
+ BOOLEAN NewDaclInherited = FALSE;
+
+ PACL ServerDacl = NULL;
+ BOOLEAN ServerDaclAllocated = FALSE;
+
+ PSID NewOwner = NULL;
+ PSID NewGroup = NULL;
+
+ BOOLEAN CleanUp = FALSE;
+ BOOLEAN SaclExplicitlyAssigned = FALSE;
+ BOOLEAN DaclExplicitlyAssigned = FALSE;
+ BOOLEAN OwnerExplicitlyAssigned = FALSE;
+
+ BOOLEAN ServerObject;
+ BOOLEAN DaclUntrusted;
+
+ BOOLEAN HasPrivilege;
+
+ PSID SubjectContextOwner;
+ PSID SubjectContextGroup;
+ PSID SubjectContextServerOwner;
+ PSID SubjectContextServerGroup;
+ PACL SubjectContextDacl;
+
+ ULONG AllocationSize;
+ ULONG NewOwnerSize;
+ ULONG NewGroupSize;
+ ULONG NewSaclSize;
+ ULONG NewDaclSize;
+
+ PCHAR Field;
+ PCHAR Base;
+
+ PAGED_CODE();
+
+ PoolType = PagedPool;
+
+ //
+ // The desired end result is to build a self-relative security descriptor.
+ // This means that a single block of memory will be allocated and all
+ // security information copied into it. To minimize work along the way,
+ // it is desirable to reference (rather than copy) each field as we
+ // determine its source. This can not be done with inherited ACLs, however,
+ // since they must be built from another ACL. So, explicitly assigned
+ // and defaulted SIDs and ACLs are just referenced until they are copied
+ // into the self-relative descriptor. Inherited ACLs are built in a
+ // temporary buffer which must be deallocated after being copied to the
+ // self-relative descriptor.
+ //
+
+
+ //
+ // Get the previous mode of the caller
+ //
+
+ RequestorMode = KeGetPreviousMode();
+
+ //
+ // If a security descriptor has been passed, capture it, otherwise
+ // cobble up a fake one to simplify the code that follows.
+ //
+
+ if (ARGUMENT_PRESENT(ExplicitDescriptor)) {
+
+ CapturedDescriptor = ExplicitDescriptor;
+ SecurityDescriptorPassed = TRUE;
+
+ } else {
+
+ //
+ // No descriptor passed, make a fake one
+ //
+
+ SecurityDescriptorPassed = FALSE;
+ RtlCreateSecurityDescriptor((PSECURITY_DESCRIPTOR)&InCaseOneNotPassed,
+ SECURITY_DESCRIPTOR_REVISION);
+ CapturedDescriptor = &InCaseOneNotPassed;
+
+ }
+
+#if DBG
+ SepDumpSecurityDescriptor( (PSECURITY_DESCRIPTOR)CapturedDescriptor,
+ "\nSeAssignSecurity: Input security descriptor = \n"
+ );
+
+ if (ARGUMENT_PRESENT( ParentDescriptor )) {
+ SepDumpSecurityDescriptor( (PSECURITY_DESCRIPTOR)ParentDescriptor,
+ "\nSeAssignSecurity: Parent security descriptor = \n"
+ );
+ }
+#endif // DBG
+ //
+ // Grab pointers to the default owner, primary group, and
+ // discretionary ACL.
+ //
+
+ //
+ // Lock the subject context for read access so that the pointers
+ // we copy out of it don't disappear on us at random
+ //
+
+ SeLockSubjectContext( SubjectContext );
+
+ SepGetDefaultsSubjectContext(
+ SubjectContext,
+ &SubjectContextOwner,
+ &SubjectContextGroup,
+ &SubjectContextServerOwner,
+ &SubjectContextServerGroup,
+ &SubjectContextDacl
+ );
+
+
+ if ( CapturedDescriptor->Control & SE_SERVER_SECURITY ) {
+ ServerObject = TRUE;
+ } else {
+ ServerObject = FALSE;
+ }
+
+ if ( CapturedDescriptor->Control & SE_DACL_UNTRUSTED ) {
+ DaclUntrusted = TRUE;
+ } else {
+ DaclUntrusted = FALSE;
+ }
+
+
+ if (!CleanUp) {
+
+ //
+ // Establish System Acl
+ //
+
+ if ( (CapturedDescriptor->Control & SE_SACL_PRESENT) &&
+ !(CapturedDescriptor->Control & SE_SACL_DEFAULTED) ) {
+
+ //
+ // Explicitly provided, not defaulted (Cases 1 and 2)
+ //
+
+ NewSacl = SepSaclAddrSecurityDescriptor(CapturedDescriptor);
+ NewSaclPresent = TRUE;
+ SaclExplicitlyAssigned = TRUE;
+
+ } else {
+
+ //
+ // See if there is an inheritable ACL (copy it if there is one.)
+ // This maps all ACEs for the target object type too.
+ //
+
+ Status = STATUS_SUCCESS;
+
+ if (ARGUMENT_PRESENT(ParentDescriptor) &&
+ NT_SUCCESS(Status = SepInheritAcl(
+ SepSaclAddrSecurityDescriptor(
+ ((SECURITY_DESCRIPTOR *)ParentDescriptor)
+ ),
+ IsDirectoryObject,
+ SubjectContextOwner,
+ SubjectContextGroup,
+ SubjectContextServerOwner,
+ SubjectContextServerGroup,
+ GenericMapping,
+ PoolType,
+ &NewSacl )
+ )) {
+
+ //
+ // There is an inheritable ACL from the parent. Assign
+ // it. (Cases 3 and 5)
+ //
+
+ NewSaclPresent = TRUE;
+ NewSaclInherited = TRUE;
+
+ } else if (!ARGUMENT_PRESENT(ParentDescriptor) || (Status == STATUS_NO_INHERITANCE)) {
+
+ //
+ // No inheritable ACL - check for a defaulted one
+ // (Cases 4 and 6)
+ //
+
+ if ( (CapturedDescriptor->Control & SE_SACL_PRESENT) &&
+ (CapturedDescriptor->Control & SE_SACL_DEFAULTED) ) {
+
+ //
+ // Reference the default ACL (case 4)
+ //
+
+ NewSacl = SepSaclAddrSecurityDescriptor(CapturedDescriptor);
+ NewSaclPresent = TRUE;
+
+ //
+ // Set SaclExplicitlyAssigned, because the caller
+ // must have SeSecurityPrivilege to do this. We
+ // will examine this flag and check for this privilege
+ // later.
+ //
+
+ SaclExplicitlyAssigned = TRUE;
+ }
+ } else {
+
+ //
+ // Some unusual error occured
+ //
+
+ CleanUp = TRUE;
+ }
+ }
+ }
+
+
+
+
+ if (!CleanUp) {
+
+ //
+ // Establish Discretionary Acl
+ //
+
+ if ( (CapturedDescriptor->Control & SE_DACL_PRESENT) &&
+ !(CapturedDescriptor->Control & SE_DACL_DEFAULTED) ) {
+
+ //
+ // Explicitly provided, not defaulted (Cases 1 and 2)
+ //
+
+ NewDacl = SepDaclAddrSecurityDescriptor(CapturedDescriptor);
+ NewDaclPresent = TRUE;
+ DaclExplicitlyAssigned = TRUE;
+
+ } else {
+
+ //
+ // See if there is an inheritable ACL (copy it if there is one.)
+ // This maps the ACEs to the target object type too.
+ //
+
+ Status = STATUS_SUCCESS;
+
+ if (ARGUMENT_PRESENT(ParentDescriptor) &&
+ NT_SUCCESS(Status = SepInheritAcl(
+ SepDaclAddrSecurityDescriptor(
+ ((SECURITY_DESCRIPTOR *)ParentDescriptor)
+ ),
+ IsDirectoryObject,
+ SubjectContextOwner,
+ SubjectContextGroup,
+ SubjectContextServerOwner,
+ SubjectContextServerGroup,
+ GenericMapping,
+ PoolType,
+ &NewDacl )
+ )) {
+
+ //
+ // There is an inheritable ACL from the parent. Assign
+ // it. (Cases 3 and 5)
+ //
+
+ NewDaclPresent = TRUE;
+ NewDaclInherited = TRUE;
+
+ } else if (!ARGUMENT_PRESENT(ParentDescriptor) || (Status == STATUS_NO_INHERITANCE)) {
+
+ //
+ // No inheritable ACL - check for a defaulted one in the
+ // security descriptor. If there isn't one there, then look
+ // for one in the subject's security context (Cases 4 and 6)
+ //
+
+ if ( (CapturedDescriptor->Control & SE_DACL_PRESENT) &&
+ (CapturedDescriptor->Control & SE_DACL_DEFAULTED) ) {
+
+ //
+ // reference the default ACL (Case 4)
+ //
+
+ NewDacl = SepDaclAddrSecurityDescriptor(CapturedDescriptor);
+ NewDaclPresent = TRUE;
+
+ //
+ // This counts as an explicit assignment.
+ //
+
+ DaclExplicitlyAssigned = TRUE;
+
+ } else {
+
+ if (ARGUMENT_PRESENT(SubjectContextDacl)) {
+
+ NewDacl = SubjectContextDacl;
+ NewDaclPresent = TRUE;
+ }
+ }
+
+ } else {
+
+ //
+ // Some unusual error occured
+ //
+
+ CleanUp = TRUE;
+ }
+ }
+ }
+
+
+ if (!CleanUp) {
+ //
+ // Establish an owner SID
+ //
+
+ if ((CapturedDescriptor->Owner) != NULL) {
+
+ //
+ // Use the specified owner
+ //
+
+ NewOwner = SepOwnerAddrSecurityDescriptor(CapturedDescriptor);
+ OwnerExplicitlyAssigned = TRUE;
+
+ } else {
+
+ //
+ // Pick up the default from the subject's security context.
+ //
+ // This does NOT constitute explicit assignment of owner
+ // and does not have to be checked as an ID that can be
+ // assigned as owner. This is because a default can not
+ // be established in a token unless the user of the token
+ // can assign it as an owner.
+ //
+
+ //
+ // If we've been asked to create a ServerObject, we need to
+ // make sure to pick up the new owner from the Primary token,
+ // not the client token. If we're not impersonating, they will
+ // end up being the same.
+ //
+
+ NewOwner = ServerObject ? SubjectContextServerOwner : SubjectContextOwner;
+ }
+ }
+
+
+
+ if (!CleanUp) {
+ //
+ // Establish a Group SID
+ //
+
+ if ((CapturedDescriptor->Group) != NULL) {
+
+ //
+ // Use the specified Group
+ //
+
+ NewGroup = SepGroupAddrSecurityDescriptor(CapturedDescriptor);
+
+ } else {
+
+ //
+ // Pick up the primary group from the subject's security context.
+ //
+ // If we're creating a Server object, use the group from the server
+ // context.
+ //
+
+ NewGroup = ServerObject ? SubjectContextServerGroup : SubjectContextGroup;
+ }
+ }
+
+
+ if (!CleanUp) {
+
+ //
+ // Now make sure that the caller has the right to assign
+ // everything in the descriptor. If requestor is kernel mode,
+ // then anything is legitimate. Otherwise, the requestor
+ // is subjected to privilege and restriction tests for some
+ // assignments.
+ //
+
+ if (RequestorMode == UserMode) {
+
+ //
+ // Anybody can assign any Discretionary ACL or group that they want to.
+ //
+
+ //
+ // See if the system ACL was explicitly specified
+ //
+
+ if (SaclExplicitlyAssigned) {
+
+ //
+ // Check for appropriate Privileges
+ // Audit/Alarm messages need to be generated due to the attempt
+ // to perform a privileged operation.
+ //
+
+ HasPrivilege = SeSinglePrivilegeCheck(
+ SeSecurityPrivilege,
+ RequestorMode
+ );
+
+ if (!HasPrivilege) {
+
+ RequestorCanAssignDescriptor = FALSE;
+ Status = STATUS_PRIVILEGE_NOT_HELD;
+ }
+
+ }
+
+ //
+ // See if the owner field is one the requestor can assign
+ //
+
+ if (OwnerExplicitlyAssigned) {
+
+
+ if (!SepValidOwnerSubjectContext(
+ SubjectContext,
+ NewOwner,
+ ServerObject)
+ ) {
+
+ RequestorCanAssignDescriptor = FALSE;
+ Status = STATUS_INVALID_OWNER;
+ }
+ }
+
+ if (DaclExplicitlyAssigned) {
+
+ //
+ // Perform analysis of compound ACEs to make sure they're all
+ // legitimate.
+ //
+
+ if (ServerObject) {
+
+ //
+ // Pass in the Server Owner as the default server SID.
+ //
+
+ Status = SepCreateServerAcl(
+ NewDacl,
+ DaclUntrusted,
+ SubjectContextServerOwner,
+ &ServerDacl,
+ &ServerDaclAllocated
+ );
+
+ if (!NT_SUCCESS( Status )) {
+
+ RequestorCanAssignDescriptor = FALSE;
+
+ } else {
+
+ NewDacl = ServerDacl;
+ }
+ }
+ }
+ }
+
+ if (RequestorCanAssignDescriptor) {
+
+ //
+ // Everything is assignable by the requestor.
+ // Calculate the memory needed to house all the information in
+ // a self-relative security descriptor.
+ //
+ // Also map the ACEs for application to the target object
+ // type, if they haven't already been mapped.
+ //
+
+ NewOwnerSize = (ULONG)LongAlign(SeLengthSid(NewOwner));
+
+ if (NewGroup != NULL) {
+ NewGroupSize = (ULONG)LongAlign(SeLengthSid(NewGroup));
+ } else {
+ NewGroupSize = 0;
+ }
+
+ if (NewSaclPresent && (NewSacl != NULL)) {
+ NewSaclSize = (ULONG)LongAlign(NewSacl->AclSize);
+ } else {
+ NewSaclSize = 0;
+ }
+
+ if (NewDaclPresent && (NewDacl != NULL)) {
+ NewDaclSize = (ULONG)LongAlign(NewDacl->AclSize);
+ } else {
+ NewDaclSize = 0;
+ }
+
+ AllocationSize = (ULONG)LongAlign(sizeof(SECURITY_DESCRIPTOR)) +
+ NewOwnerSize +
+ NewGroupSize +
+ NewSaclSize +
+ NewDaclSize;
+
+ //
+ // Allocate and initialize the security descriptor as
+ // self-relative form.
+ //
+
+ *NewDescriptor = (PSECURITY_DESCRIPTOR)ExAllocatePoolWithTag( PoolType, AllocationSize, 'dSeS');
+
+ if ((*NewDescriptor) == NULL) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ } else {
+
+ RtlCreateSecurityDescriptor(
+ (*NewDescriptor),
+ SECURITY_DESCRIPTOR_REVISION
+ );
+ ((SECURITY_DESCRIPTOR *)(*NewDescriptor))->Control |=
+ SE_SELF_RELATIVE;
+
+
+ Base = (PCHAR)(*NewDescriptor);
+ Field = Base + (ULONG)sizeof(SECURITY_DESCRIPTOR);
+
+ //
+ // Map and Copy in the Sacl
+ //
+
+ if (NewSaclPresent) {
+ ((SECURITY_DESCRIPTOR *)(*NewDescriptor))->Control |=
+ SE_SACL_PRESENT;
+ if (NewSacl != NULL) {
+ RtlMoveMemory( Field, NewSacl, NewSacl->AclSize );
+ if (!NewSaclInherited) {
+ SepApplyAclToObject( (PACL)Field, GenericMapping );
+ }
+ ((SECURITY_DESCRIPTOR *)(*NewDescriptor))->Sacl = (PACL)RtlPointerToOffset(Base,Field);
+ Field += NewSaclSize;
+ } else {
+ ((SECURITY_DESCRIPTOR *)(*NewDescriptor))->Sacl = NULL;
+ }
+
+ }
+
+ //
+ // Map and Copy in the Dacl
+ //
+
+ if (NewDaclPresent) {
+ ((SECURITY_DESCRIPTOR *)(*NewDescriptor))->Control |=
+ SE_DACL_PRESENT;
+ if (NewDacl != NULL) {
+ RtlMoveMemory( Field, NewDacl, NewDacl->AclSize );
+ if (!NewDaclInherited) {
+ SepApplyAclToObject( (PACL)Field, GenericMapping );
+ }
+ ((SECURITY_DESCRIPTOR *)(*NewDescriptor))->Dacl = (PACL)RtlPointerToOffset(Base,Field);
+ Field += NewDaclSize;
+ } else {
+ ((SECURITY_DESCRIPTOR *)(*NewDescriptor))->Dacl = NULL;
+ }
+ }
+
+
+ //
+ // Assign the owner
+ //
+
+ RtlMoveMemory( Field, NewOwner, SeLengthSid(NewOwner) );
+ ((SECURITY_DESCRIPTOR *)(*NewDescriptor))->Owner = (PSID)RtlPointerToOffset(Base,Field);
+ Field += NewOwnerSize;
+
+ if (NewGroup != NULL) {
+ RtlMoveMemory( Field, NewGroup, SeLengthSid(NewGroup) );
+ }
+ ((SECURITY_DESCRIPTOR *)(*NewDescriptor))->Group = (PSID)RtlPointerToOffset(Base,Field);
+
+ Status = STATUS_SUCCESS;
+ }
+
+ }
+ }
+
+ //
+ // If we allocated memory for a Server DACL, free it now.
+ //
+
+ if (ServerDaclAllocated) {
+ ExFreePool( ServerDacl );
+ }
+
+ //
+ // Either an error was encountered or the requestor the assignment has
+ // completed successfully. In either case, we have to clean up any
+ // memory.
+ //
+
+ SeUnlockSubjectContext( SubjectContext );
+
+ if (NewSaclInherited) {
+ ExFreePool( NewSacl );
+ }
+
+ if (NewDaclInherited) {
+ ExFreePool( NewDacl );
+ }
+
+#if DBG
+ SepDumpSecurityDescriptor( *NewDescriptor,
+ "SeAssignSecurity: Final security descriptor = \n"
+ );
+#endif
+
+ return Status;
+}
+
+
+NTSTATUS
+SeDeassignSecurity (
+ IN OUT PSECURITY_DESCRIPTOR *SecurityDescriptor
+ )
+
+/*++
+
+Routine Description:
+
+ This routine deallocates the memory associated with a security descriptor
+ that was assigned using SeAssignSecurity.
+
+
+Arguments:
+
+ SecurityDescriptor - Supplies the address of a pointer to the security
+ descriptor being deleted.
+
+Return Value:
+
+ STATUS_SUCCESS - The deallocation was successful.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ if ((*SecurityDescriptor) != NULL) {
+ ExFreePool( (*SecurityDescriptor) );
+ }
+
+ //
+ // And zero out the pointer to it for safety sake
+ //
+
+ (*SecurityDescriptor) = NULL;
+
+ return( STATUS_SUCCESS );
+
+}
+
+
+VOID
+SepApplyAclToObject (
+ IN PACL Acl,
+ IN PGENERIC_MAPPING GenericMapping
+ )
+
+/*++
+
+Routine Description:
+
+ This is a private routine that maps Access Masks of an ACL so that
+ they are applicable to the object type the ACL is being applied to.
+
+ Only known DSA ACEs are mapped. Unknown ACE types are ignored.
+
+ Only access types in the GenericAll mapping for the target object
+ type will be non-zero upon return.
+
+Arguments:
+
+ Acl - Supplies the acl being applied.
+
+ GenericMapping - Specifies the generic mapping to use.
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+//////////////////////////////////////////////////////////////////////////////
+// //
+// The logic in the ACL inheritance code must mirror the code for //
+// inheritance in the user mode runtime (in sertl.c). Do not make changes //
+// here without also making changes in that module. //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+ ULONG i;
+
+ PACE_HEADER Ace;
+
+ PAGED_CODE();
+
+ //
+ // First check if the acl is null
+ //
+
+ if (Acl == NULL) {
+
+ return;
+
+ }
+
+
+ //
+ // Now walk the ACL, mapping each ACE as we go.
+ //
+
+ for (i = 0, Ace = FirstAce(Acl);
+ i < Acl->AceCount;
+ i += 1, Ace = NextAce(Ace)) {
+
+ if (IsMSAceType( Ace )) {
+
+ RtlApplyAceToObject( Ace, GenericMapping );
+ }
+
+ }
+
+ return;
+}
+
+
+NTSTATUS
+SepInheritAcl (
+ IN PACL Acl,
+ IN BOOLEAN IsDirectoryObject,
+ IN PSID ClientOwnerSid,
+ IN PSID ClientGroupSid,
+ IN PSID ServerOwnerSid OPTIONAL,
+ IN PSID ServerGroupSid OPTIONAL,
+ IN PGENERIC_MAPPING GenericMapping,
+ IN POOL_TYPE PoolType,
+ OUT PACL *NewAcl
+ )
+
+/*++
+
+Routine Description:
+
+ This is a private routine that produces an inherited acl from
+ a parent acl according to the rules of inheritance
+
+Arguments:
+
+ Acl - Supplies the acl being inherited.
+
+ IsDirectoryObject - Specifies if the new acl is for a directory.
+
+ OwnerSid - Specifies the owner Sid to use.
+
+ GroupSid - Specifies the group SID to use.
+
+ ServerSid - Specifies the Server SID to use.
+
+ ClientSid - Specifies the Client SID to use.
+
+ GenericMapping - Specifies the generic mapping to use.
+
+ PoolType - Specifies the pool type for the new acl.
+
+ NewAcl - Receives a pointer to the new (inherited) acl.
+
+Return Value:
+
+ STATUS_SUCCESS - An inheritable ACL was successfully generated.
+
+ STATUS_NO_INHERITANCE - An inheritable ACL was not successfully generated.
+ This is a warning completion status.
+
+ STATUS_BAD_INHERITANCE_ACL - Indicates the acl built was not a valid ACL.
+ This can becaused by a number of things. One of the more probable
+ causes is the replacement of a CreatorId with an SID that didn't fit
+ into the ACE or ACL.
+
+ STATUS_UNKNOWN_REVISION - Indicates the source ACL is a revision that
+ is unknown to this routine.
+
+--*/
+
+{
+//////////////////////////////////////////////////////////////////////////////
+// //
+// The logic in the ACL inheritance code must mirror the code for //
+// inheritance in the user mode runtime (in sertl.c). Do not make changes //
+// here without also making changes in that module. //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+
+ NTSTATUS Status;
+ ULONG NewAclLength;
+
+ PAGED_CODE();
+
+ //
+ // First check if the acl is null
+ //
+
+ if (Acl == NULL) {
+
+ return STATUS_NO_INHERITANCE;
+ }
+
+ if (Acl->AclRevision != ACL_REVISION2 && Acl->AclRevision != ACL_REVISION3) {
+ return STATUS_UNKNOWN_REVISION;
+ }
+
+ //
+ // Generating an inheritable ACL is a two-pass operation.
+ // First you must see if there is anything to inherit, and if so,
+ // allocate enough room to hold it. then you must actually copy
+ // the generated ACEs.
+ //
+
+ Status = RtlpLengthInheritAcl(
+ Acl,
+ IsDirectoryObject,
+ ClientOwnerSid,
+ ClientGroupSid,
+ ServerOwnerSid,
+ ServerGroupSid,
+ GenericMapping,
+ &NewAclLength
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ return Status;
+ }
+ if (NewAclLength == 0) {
+ return STATUS_NO_INHERITANCE;
+ }
+
+ (*NewAcl) = (PACL)ExAllocatePoolWithTag( PoolType, NewAclLength, 'cAeS' );
+ if ((*NewAcl) == NULL) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCreateAcl( (*NewAcl), NewAclLength, Acl->AclRevision );
+
+ Status = RtlpGenerateInheritAcl(
+ Acl,
+ IsDirectoryObject,
+ ClientOwnerSid,
+ ClientGroupSid,
+ ServerOwnerSid,
+ ServerGroupSid,
+ GenericMapping,
+ (*NewAcl)
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ ExFreePool( (*NewAcl) );
+ }
+
+ return Status;
+}
+
+
+
+NTSTATUS
+SeAssignWorldSecurityDescriptor(
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN OUT PULONG Length,
+ IN PSECURITY_INFORMATION SecurityInformation
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called by the I/O system to properly initialize a
+ security descriptor for a FAT file. It will take a pointer to a
+ buffer containing an emptry security descriptor, and create in the
+ buffer a self-relative security descriptor with
+
+ Owner = WorldSid,
+
+ Group = WorldSid.
+
+ Thus, a FAT file is accessable to all.
+
+Arguments:
+
+ SecurityDescriptor - Supplies a pointer to a buffer in which will be
+ created a self-relative security descriptor as described above.
+
+ Length - The length in bytes of the buffer. If the length is too
+ small, it will contain the minimum size required upon exit.
+
+
+Return Value:
+
+ STATUS_BUFFER_TOO_SMALL - The buffer was not big enough to contain
+ the requested information.
+
+
+--*/
+
+{
+
+ PCHAR Field;
+ PCHAR Base;
+ ULONG WorldSidLength;
+ PISECURITY_DESCRIPTOR ISecurityDescriptor;
+ ULONG MinSize;
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ if ( !ARGUMENT_PRESENT( SecurityInformation )) {
+
+ return( STATUS_ACCESS_DENIED );
+ }
+
+ WorldSidLength = SeLengthSid( SeWorldSid );
+
+ MinSize = sizeof( SECURITY_DESCRIPTOR ) + 2 * WorldSidLength;
+
+ if ( *Length < MinSize ) {
+
+ *Length = MinSize;
+ return( STATUS_BUFFER_TOO_SMALL );
+ }
+
+ *Length = MinSize;
+
+ ISecurityDescriptor = (SECURITY_DESCRIPTOR *)SecurityDescriptor;
+
+ Status = RtlCreateSecurityDescriptor( ISecurityDescriptor,
+ SECURITY_DESCRIPTOR_REVISION );
+
+ if (!NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ Base = (PCHAR)(ISecurityDescriptor);
+ Field = Base + (ULONG)sizeof(SECURITY_DESCRIPTOR);
+
+ if ( *SecurityInformation & OWNER_SECURITY_INFORMATION ) {
+
+ RtlMoveMemory( Field, SeWorldSid, WorldSidLength );
+ ISecurityDescriptor->Owner = (PSID)RtlPointerToOffset(Base,Field);
+ Field += WorldSidLength;
+ }
+
+ if ( *SecurityInformation & GROUP_SECURITY_INFORMATION ) {
+
+ RtlMoveMemory( Field, SeWorldSid, WorldSidLength );
+ ISecurityDescriptor->Group = (PSID)RtlPointerToOffset(Base,Field);
+ }
+
+ if ( *SecurityInformation & DACL_SECURITY_INFORMATION ) {
+ SepSetControlBits( ISecurityDescriptor, SE_DACL_PRESENT );
+ }
+
+ if ( *SecurityInformation & SACL_SECURITY_INFORMATION ) {
+ SepSetControlBits( ISecurityDescriptor, SE_SACL_PRESENT );
+ }
+
+ SepSetControlBits( ISecurityDescriptor, SE_SELF_RELATIVE );
+
+ return( STATUS_SUCCESS );
+
+}
+
+
+
+NTSTATUS
+SepCreateServerAcl(
+ IN PACL Acl,
+ IN BOOLEAN AclUntrusted,
+ IN PSID ServerSid,
+ OUT PACL *ServerAcl,
+ OUT BOOLEAN *ServerAclAllocated
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes an ACL and converts it into a server ACL.
+ Currently, that means converting all of the GRANT ACEs into
+ Compount Grants, and if necessary sanitizing any Compound
+ Grants that are encountered.
+
+Arguments:
+
+
+
+Return Value:
+
+
+--*/
+
+{
+ USHORT RequiredSize = sizeof(ACL);
+ USHORT AceSizeAdjustment;
+ USHORT ServerSidSize;
+ PACE_HEADER Ace;
+ ULONG i;
+ PVOID Target;
+ PVOID AcePosition;
+ PSID UntrustedSid;
+ PSID ClientSid;
+ NTSTATUS Status;
+
+ if (Acl == NULL) {
+ *ServerAclAllocated = FALSE;
+ *ServerAcl = NULL;
+ return( STATUS_SUCCESS );
+ }
+
+ AceSizeAdjustment = sizeof( KNOWN_COMPOUND_ACE ) - sizeof( KNOWN_ACE );
+ ASSERT( sizeof( KNOWN_COMPOUND_ACE ) >= sizeof( KNOWN_ACE ) );
+
+ ServerSidSize = (USHORT)SeLengthSid( ServerSid );
+
+ //
+ // Do this in two passes. First, determine how big the final
+ // result is going to be, and then allocate the space and make
+ // the changes.
+ //
+
+ for (i = 0, Ace = FirstAce(Acl);
+ i < Acl->AceCount;
+ i += 1, Ace = NextAce(Ace)) {
+
+ //
+ // If it's an ACCESS_ALLOWED_ACE_TYPE, we'll need to add in the
+ // size of the Server SID.
+ //
+
+ if (Ace->AceType == ACCESS_ALLOWED_ACE_TYPE) {
+
+ //
+ // Simply add the size of the new Server SID plus whatever
+ // adjustment needs to be made to increase the size of the ACE.
+ //
+
+ RequiredSize += ( ServerSidSize + AceSizeAdjustment );
+
+ } else {
+
+ if (AclUntrusted && Ace->AceType == ACCESS_ALLOWED_COMPOUND_ACE_TYPE ) {
+
+ //
+ // Since the Acl is untrusted, we don't care what is in the
+ // server SID, we're going to replace it.
+ //
+
+ UntrustedSid = RtlCompoundAceServerSid( Ace );
+ if ((USHORT)SeLengthSid(UntrustedSid) > ServerSidSize) {
+ RequiredSize += ((USHORT)SeLengthSid(UntrustedSid) - ServerSidSize);
+ } else {
+ RequiredSize += (ServerSidSize - (USHORT)SeLengthSid(UntrustedSid));
+
+ }
+ }
+ }
+
+ RequiredSize += Ace->AceSize;
+ }
+
+ (*ServerAcl) = (PACL)ExAllocatePoolWithTag( PagedPool, RequiredSize, 'cAeS' );
+
+ if ((*ServerAcl) == NULL) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // Mark as allocated so caller knows to free it.
+ //
+
+ *ServerAclAllocated = TRUE;
+
+ Status = RtlCreateAcl( (*ServerAcl), RequiredSize, ACL_REVISION3 );
+ ASSERT( NT_SUCCESS( Status ));
+
+ for (i = 0, Ace = FirstAce(Acl), Target=FirstAce( *ServerAcl );
+ i < Acl->AceCount;
+ i += 1, Ace = NextAce(Ace)) {
+
+ //
+ // If it's an ACCESS_ALLOWED_ACE_TYPE, convert to a Server ACE.
+ //
+
+ if (Ace->AceType == ACCESS_ALLOWED_ACE_TYPE ||
+ (AclUntrusted && Ace->AceType == ACCESS_ALLOWED_COMPOUND_ACE_TYPE )) {
+
+ AcePosition = Target;
+
+ if (Ace->AceType == ACCESS_ALLOWED_ACE_TYPE) {
+ ClientSid = &((PKNOWN_ACE)Ace)->SidStart;
+ } else {
+ ClientSid = RtlCompoundAceClientSid( Ace );
+ }
+
+ //
+ // Copy up to the access mask.
+ //
+
+ RtlMoveMemory(
+ Target,
+ Ace,
+ FIELD_OFFSET(KNOWN_ACE, SidStart)
+ );
+
+ //
+ // Now copy the correct Server SID
+ //
+
+ Target = (PVOID)((ULONG)Target + (UCHAR)(FIELD_OFFSET(KNOWN_COMPOUND_ACE, SidStart)));
+
+ RtlMoveMemory(
+ Target,
+ ServerSid,
+ SeLengthSid(ServerSid)
+ );
+
+ Target = (PVOID)((ULONG)Target + (UCHAR)SeLengthSid(ServerSid));
+
+ //
+ // Now copy in the correct client SID. We can copy this right out of
+ // the original ACE.
+ //
+
+ RtlMoveMemory(
+ Target,
+ ClientSid,
+ SeLengthSid(ClientSid)
+ );
+
+ Target = (PVOID)((ULONG)Target + SeLengthSid(ClientSid));
+
+ //
+ // Set the size of the ACE accordingly
+ //
+
+ ((PKNOWN_COMPOUND_ACE)AcePosition)->Header.AceSize =
+ (USHORT)FIELD_OFFSET(KNOWN_COMPOUND_ACE, SidStart) +
+ (USHORT)SeLengthSid(ServerSid) +
+ (USHORT)SeLengthSid(ClientSid);
+
+ //
+ // Set the type
+ //
+
+ ((PKNOWN_COMPOUND_ACE)AcePosition)->Header.AceType = ACCESS_ALLOWED_COMPOUND_ACE_TYPE;
+ ((PKNOWN_COMPOUND_ACE)AcePosition)->CompoundAceType = COMPOUND_ACE_IMPERSONATION;
+
+ } else {
+
+ //
+ // Just copy the ACE as is.
+ //
+
+ RtlMoveMemory( Target, Ace, Ace->AceSize );
+
+ Target = (PVOID)((ULONG)Target + Ace->AceSize);
+ }
+ }
+
+ (*ServerAcl)->AceCount = Acl->AceCount;
+
+ return( STATUS_SUCCESS );
+}
+
+
+
+
+
+
+
+//
+// BUGWARNING The following routines should be in a debug only kernel, since
+// all they do is dump stuff to a debug terminal as appropriate. The same
+// goes for the declarations of the variables SepDumpSD and SepDumpToken
+//
+
+
+
+VOID
+SepDumpSecurityDescriptor(
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN PSZ TitleString
+ )
+
+/*++
+
+Routine Description:
+
+ Private routine to dump a security descriptor to the debug
+ screen.
+
+Arguments:
+
+ SecurityDescriptor - Supplies the security descriptor to be dumped.
+
+ TitleString - A null terminated string to print before dumping
+ the security descriptor.
+
+
+Return Value:
+
+ None.
+
+
+--*/
+{
+#if DBG
+ PISECURITY_DESCRIPTOR ISecurityDescriptor;
+ UCHAR Revision;
+ SECURITY_DESCRIPTOR_CONTROL Control;
+ PSID Owner;
+ PSID Group;
+ PACL Sacl;
+ PACL Dacl;
+
+ PAGED_CODE();
+
+
+ if (!SepDumpSD) {
+ return;
+ }
+
+ if (!ARGUMENT_PRESENT( SecurityDescriptor )) {
+ return;
+ }
+
+ DbgPrint(TitleString);
+
+ ISecurityDescriptor = ( PISECURITY_DESCRIPTOR )SecurityDescriptor;
+
+ Revision = ISecurityDescriptor->Revision;
+ Control = ISecurityDescriptor->Control;
+
+ Owner = SepOwnerAddrSecurityDescriptor( ISecurityDescriptor );
+ Group = SepGroupAddrSecurityDescriptor( ISecurityDescriptor );
+ Sacl = SepSaclAddrSecurityDescriptor( ISecurityDescriptor );
+ Dacl = SepDaclAddrSecurityDescriptor( ISecurityDescriptor );
+
+ DbgPrint("\nSECURITY DESCRIPTOR\n");
+
+ DbgPrint("Revision = %d\n",Revision);
+
+ //
+ // Print control info
+ //
+
+ if (Control & SE_OWNER_DEFAULTED) {
+ DbgPrint("Owner defaulted\n");
+ }
+ if (Control & SE_GROUP_DEFAULTED) {
+ DbgPrint("Group defaulted\n");
+ }
+ if (Control & SE_DACL_PRESENT) {
+ DbgPrint("Dacl present\n");
+ }
+ if (Control & SE_DACL_DEFAULTED) {
+ DbgPrint("Dacl defaulted\n");
+ }
+ if (Control & SE_SACL_PRESENT) {
+ DbgPrint("Sacl present\n");
+ }
+ if (Control & SE_SACL_DEFAULTED) {
+ DbgPrint("Sacl defaulted\n");
+ }
+ if (Control & SE_SELF_RELATIVE) {
+ DbgPrint("Self relative\n");
+ }
+ if (Control & SE_DACL_UNTRUSTED) {
+ DbgPrint("Dacl untrusted\n");
+ }
+ if (Control & SE_SERVER_SECURITY) {
+ DbgPrint("Server security\n");
+ }
+
+ DbgPrint("Owner ");
+ SepPrintSid( Owner );
+
+ DbgPrint("Group ");
+ SepPrintSid( Group );
+
+ DbgPrint("Sacl");
+ SepPrintAcl( Sacl );
+
+ DbgPrint("Dacl");
+ SepPrintAcl( Dacl );
+#endif
+}
+
+
+
+VOID
+SepPrintAcl (
+ IN PACL Acl
+ )
+
+/*++
+
+Routine Description:
+
+ This routine dumps via (DbgPrint) an Acl for debug purposes. It is
+ specialized to dump standard aces.
+
+Arguments:
+
+ Acl - Supplies the Acl to dump
+
+Return Value:
+
+ None
+
+--*/
+
+
+{
+#if DBG
+ ULONG i;
+ PKNOWN_ACE Ace;
+ BOOLEAN KnownType;
+
+ PAGED_CODE();
+
+ DbgPrint("@ %8lx\n", Acl);
+
+ //
+ // Check if the Acl is null
+ //
+
+ if (Acl == NULL) {
+
+ return;
+
+ }
+
+ //
+ // Dump the Acl header
+ //
+
+ DbgPrint(" Revision: %02x", Acl->AclRevision);
+ DbgPrint(" Size: %04x", Acl->AclSize);
+ DbgPrint(" AceCount: %04x\n", Acl->AceCount);
+
+ //
+ // Now for each Ace we want do dump it
+ //
+
+ for (i = 0, Ace = FirstAce(Acl);
+ i < Acl->AceCount;
+ i++, Ace = NextAce(Ace) ) {
+
+ //
+ // print out the ace header
+ //
+
+ DbgPrint("\n AceHeader: %08lx ", *(PULONG)Ace);
+
+ //
+ // special case on the standard ace types
+ //
+
+ if ((Ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE) ||
+ (Ace->Header.AceType == ACCESS_DENIED_ACE_TYPE) ||
+ (Ace->Header.AceType == SYSTEM_AUDIT_ACE_TYPE) ||
+ (Ace->Header.AceType == SYSTEM_ALARM_ACE_TYPE) ||
+ (Ace->Header.AceType == ACCESS_ALLOWED_COMPOUND_ACE_TYPE)) {
+
+ //
+ // The following array is indexed by ace types and must
+ // follow the allowed, denied, audit, alarm seqeuence
+ //
+
+ PCHAR AceTypes[] = { "Access Allowed",
+ "Access Denied ",
+ "System Audit ",
+ "System Alarm ",
+ "Compound Grant",
+ };
+
+ DbgPrint(AceTypes[Ace->Header.AceType]);
+ DbgPrint("\n Access Mask: %08lx ", Ace->Mask);
+ KnownType = TRUE;
+
+ } else {
+
+ DbgPrint(" Unknown Ace Type\n");
+ KnownType = FALSE;
+ }
+
+ DbgPrint("\n");
+
+ DbgPrint(" AceSize = %d\n",Ace->Header.AceSize);
+
+ DbgPrint(" Ace Flags = ");
+ if (Ace->Header.AceFlags & OBJECT_INHERIT_ACE) {
+ DbgPrint("OBJECT_INHERIT_ACE\n");
+ DbgPrint(" ");
+ }
+
+ if (Ace->Header.AceFlags & CONTAINER_INHERIT_ACE) {
+ DbgPrint("CONTAINER_INHERIT_ACE\n");
+ DbgPrint(" ");
+ }
+
+ if (Ace->Header.AceFlags & NO_PROPAGATE_INHERIT_ACE) {
+ DbgPrint("NO_PROPAGATE_INHERIT_ACE\n");
+ DbgPrint(" ");
+ }
+
+ if (Ace->Header.AceFlags & INHERIT_ONLY_ACE) {
+ DbgPrint("INHERIT_ONLY_ACE\n");
+ DbgPrint(" ");
+ }
+
+
+ if (Ace->Header.AceFlags & SUCCESSFUL_ACCESS_ACE_FLAG) {
+ DbgPrint("SUCCESSFUL_ACCESS_ACE_FLAG\n");
+ DbgPrint(" ");
+ }
+
+ if (Ace->Header.AceFlags & FAILED_ACCESS_ACE_FLAG) {
+ DbgPrint("FAILED_ACCESS_ACE_FLAG\n");
+ DbgPrint(" ");
+ }
+
+ DbgPrint("\n");
+
+ if (KnownType != TRUE) {
+ continue;
+ }
+
+ if (Ace->Header.AceType != ACCESS_ALLOWED_COMPOUND_ACE_TYPE) {
+ DbgPrint(" Sid = ");
+ SepPrintSid(&Ace->SidStart);
+ } else {
+ DbgPrint(" Server Sid = ");
+ SepPrintSid(RtlCompoundAceServerSid(Ace));
+ DbgPrint("\n Client Sid = ");
+ SepPrintSid(RtlCompoundAceClientSid( Ace ));
+ }
+ }
+#endif
+}
+
+
+
+VOID
+SepPrintSid(
+ IN PSID Sid
+ )
+
+/*++
+
+Routine Description:
+
+ Prints a formatted Sid
+
+Arguments:
+
+ Sid - Provides a pointer to the sid to be printed.
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+#if DBG
+ UCHAR i;
+ ULONG Tmp;
+ PISID ISid;
+ STRING AccountName;
+ UCHAR Buffer[128];
+
+ PAGED_CODE();
+
+ if (Sid == NULL) {
+ DbgPrint("Sid is NULL\n");
+ return;
+ }
+
+ Buffer[0] = 0;
+
+ AccountName.MaximumLength = 127;
+ AccountName.Length = 0;
+ AccountName.Buffer = (PVOID)&Buffer[0];
+
+ if (SepSidTranslation( Sid, &AccountName )) {
+
+ DbgPrint("%s ", AccountName.Buffer );
+ }
+
+ ISid = (PISID)Sid;
+
+ DbgPrint("S-%lu-", (USHORT)ISid->Revision );
+ if ( (ISid->IdentifierAuthority.Value[0] != 0) ||
+ (ISid->IdentifierAuthority.Value[1] != 0) ){
+ DbgPrint("0x%02hx%02hx%02hx%02hx%02hx%02hx",
+ (USHORT)ISid->IdentifierAuthority.Value[0],
+ (USHORT)ISid->IdentifierAuthority.Value[1],
+ (USHORT)ISid->IdentifierAuthority.Value[2],
+ (USHORT)ISid->IdentifierAuthority.Value[3],
+ (USHORT)ISid->IdentifierAuthority.Value[4],
+ (USHORT)ISid->IdentifierAuthority.Value[5] );
+ } else {
+ Tmp = (ULONG)ISid->IdentifierAuthority.Value[5] +
+ (ULONG)(ISid->IdentifierAuthority.Value[4] << 8) +
+ (ULONG)(ISid->IdentifierAuthority.Value[3] << 16) +
+ (ULONG)(ISid->IdentifierAuthority.Value[2] << 24);
+ DbgPrint("%lu", Tmp);
+ }
+
+
+ for (i=0;i<ISid->SubAuthorityCount ;i++ ) {
+ DbgPrint("-%lu", ISid->SubAuthority[i]);
+ }
+ DbgPrint("\n");
+#endif
+}
+
+
+
+
+VOID
+SepDumpTokenInfo(
+ IN PACCESS_TOKEN Token
+ )
+
+/*++
+
+Routine Description:
+
+ Prints interesting information in a token.
+
+Arguments:
+
+ Token - Provides the token to be examined.
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+#if DBG
+ ULONG UserAndGroupCount;
+ PSID_AND_ATTRIBUTES TokenSid;
+ ULONG i;
+ PTOKEN IToken;
+
+ PAGED_CODE();
+
+ if (!SepDumpToken) {
+ return;
+ }
+
+ IToken = (TOKEN *)Token;
+
+ UserAndGroupCount = IToken->UserAndGroupCount;
+
+ DbgPrint("\n\nToken User and Groups Array:\n\n");
+
+ for ( i = 0 , TokenSid = IToken->UserAndGroups;
+ i < UserAndGroupCount ;
+ i++, TokenSid++
+ ) {
+
+ SepPrintSid( TokenSid->Sid );
+
+ }
+#endif
+}
+
+
+
+BOOLEAN
+SepSidTranslation(
+ PSID Sid,
+ PSTRING AccountName
+ )
+
+/*++
+
+Routine Description:
+
+ This routine translates well-known SIDs into English names.
+
+Arguments:
+
+ Sid - Provides the sid to be examined.
+
+ AccountName - Provides a string buffer in which to place the
+ translated name.
+
+Return Value:
+
+ None
+
+--*/
+
+// AccountName is expected to have a large maximum length
+
+{
+ PAGED_CODE();
+
+ if (RtlEqualSid(Sid, SeWorldSid)) {
+ RtlInitString( AccountName, "WORLD ");
+ return(TRUE);
+ }
+
+ if (RtlEqualSid(Sid, SeLocalSid)) {
+ RtlInitString( AccountName, "LOCAL ");
+ return(TRUE);
+ }
+
+ if (RtlEqualSid(Sid, SeNetworkSid)) {
+ RtlInitString( AccountName, "NETWORK ");
+ return(TRUE);
+ }
+
+ if (RtlEqualSid(Sid, SeBatchSid)) {
+ RtlInitString( AccountName, "BATCH ");
+ return(TRUE);
+ }
+
+ if (RtlEqualSid(Sid, SeInteractiveSid)) {
+ RtlInitString( AccountName, "INTERACTIVE ");
+ return(TRUE);
+ }
+
+ if (RtlEqualSid(Sid, SeLocalSystemSid)) {
+ RtlInitString( AccountName, "SYSTEM ");
+ return(TRUE);
+ }
+
+ if (RtlEqualSid(Sid, SeCreatorOwnerSid)) {
+ RtlInitString( AccountName, "CREATOR_OWNER ");
+ return(TRUE);
+ }
+
+ if (RtlEqualSid(Sid, SeCreatorGroupSid)) {
+ RtlInitString( AccountName, "CREATOR_GROUP ");
+ return(TRUE);
+ }
+
+ if (RtlEqualSid(Sid, SeCreatorOwnerServerSid)) {
+ RtlInitString( AccountName, "CREATOR_OWNER_SERVER ");
+ return(TRUE);
+ }
+
+ if (RtlEqualSid(Sid, SeCreatorGroupServerSid)) {
+ RtlInitString( AccountName, "CREATOR_GROUP_SERVER ");
+ return(TRUE);
+ }
+
+ return(FALSE);
+}
+
+//
+// End debug only routines
+//
diff --git a/private/ntos/se/seastate.c b/private/ntos/se/seastate.c
new file mode 100644
index 000000000..5bdc56bcc
--- /dev/null
+++ b/private/ntos/se/seastate.c
@@ -0,0 +1,599 @@
+/*++
+
+Module Name:
+
+ SeAstate.c
+
+Abstract:
+
+ This Module implements the privilege check procedures.
+
+Author:
+
+ Robert Reichel (robertre) 20-March-90
+
+Environment:
+
+ Kernel Mode
+
+Revision History:
+
+ v1: robertre
+ new file, move Access State related routines here
+
+--*/
+
+#include "tokenp.h"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE,SeCreateAccessState)
+#pragma alloc_text(PAGE,SeDeleteAccessState)
+#pragma alloc_text(PAGE,SeAppendPrivileges)
+#pragma alloc_text(PAGE,SepConcatenatePrivileges)
+#endif
+
+
+//
+// Define logical sum of all generic accesses.
+//
+
+#define GENERIC_ACCESS (GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL)
+
+
+//
+// The PRIVILEGE_SET data structure includes an array including ANYSIZE_ARRAY
+// elements. This definition provides the size of an empty PRIVILEGE_SET
+// (i.e., one with no privileges in it).
+//
+
+#define SEP_PRIVILEGE_SET_HEADER_SIZE \
+ ((ULONG)sizeof(PRIVILEGE_SET) - \
+ (ANYSIZE_ARRAY * (ULONG)sizeof(LUID_AND_ATTRIBUTES)))
+
+
+
+
+
+#if 0
+NTSTATUS
+SeCreateAccessState(
+ IN PACCESS_STATE AccessState,
+ IN ACCESS_MASK DesiredAccess,
+ IN PGENERIC_MAPPING GenericMapping OPTIONAL
+ )
+
+/*++
+Routine Description:
+
+ This routine initializes an ACCESS_STATE structure. This consists
+ of:
+
+ - zeroing the entire structure
+
+ - mapping generic access types in the passed DesiredAccess
+ and putting it into the structure
+
+ - "capturing" the Subject Context, which must be held for the
+ duration of the access attempt (at least until auditing is performed).
+
+ - Allocating an Operation ID, which is an LUID that will be used
+ to associate different parts of the access attempt in the audit
+ log.
+
+Arguments:
+
+ AccessState - a pointer to the structure to be initialized.
+
+ DesiredAccess - Access mask containing the desired access
+
+ GenericMapping - Optionally supplies a pointer to a generic mapping
+ that may be used to map any generic access requests that may
+ have been passed in the DesiredAccess parameter.
+
+ Note that if this parameter is not supplied, it must be filled
+ in at some later point. The IO system does this in IopParseDevice.
+
+Return Value:
+
+ Error if the attempt to allocate an LUID fails.
+
+ Note that this error may be safely ignored if it is known that all
+ security checks will be performed with PreviousMode == KernelMode.
+ Know what you're doing if you choose to ignore this.
+
+--*/
+
+{
+
+ ACCESS_MASK MappedAccessMask;
+ PSECURITY_DESCRIPTOR InputSecurityDescriptor = NULL;
+ PAUX_ACCESS_DATA AuxData;
+
+ PAGED_CODE();
+
+ //
+ // Don't modify what he passed in
+ //
+
+ MappedAccessMask = DesiredAccess;
+
+ //
+ // Map generic access to object specific access iff generic access types
+ // are specified and a generic access mapping table is provided.
+ //
+
+ if ( ((DesiredAccess & GENERIC_ACCESS) != 0) &&
+ ARGUMENT_PRESENT(GenericMapping) ) {
+
+ RtlMapGenericMask(
+ &MappedAccessMask,
+ GenericMapping
+ );
+ }
+
+ RtlZeroMemory(AccessState, sizeof(ACCESS_STATE));
+
+ //
+ // Assume RtlZeroMemory has initialized these fields properly
+ //
+
+ ASSERT( AccessState->SecurityDescriptor == NULL );
+ ASSERT( AccessState->PrivilegesAllocated == FALSE );
+
+ AccessState->AuxData = ExAllocatePool( PagedPool, sizeof( AUX_ACCESS_DATA ));
+
+ if (AccessState->AuxData == NULL) {
+ return( STATUS_NO_MEMORY );
+ }
+
+ AuxData = (PAUX_ACCESS_DATA)AccessState->AuxData;
+
+ SeCaptureSubjectContext(&AccessState->SubjectSecurityContext);
+
+ if (((PTOKEN)EffectiveToken( &AccessState->SubjectSecurityContext ))->TokenFlags & TOKEN_HAS_TRAVERSE_PRIVILEGE ) {
+ AccessState->Flags = TOKEN_HAS_TRAVERSE_PRIVILEGE;
+ }
+
+ AccessState->RemainingDesiredAccess = MappedAccessMask;
+ AccessState->OriginalDesiredAccess = DesiredAccess;
+ AuxData->PrivilegesUsed = (PPRIVILEGE_SET)((ULONG)AccessState +
+ (FIELD_OFFSET(ACCESS_STATE, Privileges)));
+
+ ExAllocateLocallyUniqueId(&AccessState->OperationID);
+
+ if (ARGUMENT_PRESENT(GenericMapping)) {
+ AuxData->GenericMapping = *GenericMapping;
+ }
+
+ return( STATUS_SUCCESS );
+
+}
+
+#endif
+
+
+NTSTATUS
+SeCreateAccessState(
+ IN PACCESS_STATE AccessState,
+ IN PAUX_ACCESS_DATA AuxData,
+ IN ACCESS_MASK DesiredAccess,
+ IN PGENERIC_MAPPING GenericMapping OPTIONAL
+ )
+
+/*++
+Routine Description:
+
+ This routine initializes an ACCESS_STATE structure. This consists
+ of:
+
+ - zeroing the entire structure
+
+ - mapping generic access types in the passed DesiredAccess
+ and putting it into the structure
+
+ - "capturing" the Subject Context, which must be held for the
+ duration of the access attempt (at least until auditing is performed).
+
+ - Allocating an Operation ID, which is an LUID that will be used
+ to associate different parts of the access attempt in the audit
+ log.
+
+Arguments:
+
+ AccessState - a pointer to the structure to be initialized.
+
+ AuxData - Supplies a buffer big enough for an AuxData structure
+ so we don't have to allocate one.
+
+ DesiredAccess - Access mask containing the desired access
+
+ GenericMapping - Optionally supplies a pointer to a generic mapping
+ that may be used to map any generic access requests that may
+ have been passed in the DesiredAccess parameter.
+
+ Note that if this parameter is not supplied, it must be filled
+ in at some later point. The IO system does this in IopParseDevice.
+
+Return Value:
+
+ Error if the attempt to allocate an LUID fails.
+
+ Note that this error may be safely ignored if it is known that all
+ security checks will be performed with PreviousMode == KernelMode.
+ Know what you're doing if you choose to ignore this.
+
+--*/
+
+{
+
+ ACCESS_MASK MappedAccessMask;
+ PSECURITY_DESCRIPTOR InputSecurityDescriptor = NULL;
+
+ PAGED_CODE();
+
+ //
+ // Don't modify what he passed in
+ //
+
+ MappedAccessMask = DesiredAccess;
+
+ //
+ // Map generic access to object specific access iff generic access types
+ // are specified and a generic access mapping table is provided.
+ //
+
+ if ( ((DesiredAccess & GENERIC_ACCESS) != 0) &&
+ ARGUMENT_PRESENT(GenericMapping) ) {
+
+ RtlMapGenericMask(
+ &MappedAccessMask,
+ GenericMapping
+ );
+ }
+
+ RtlZeroMemory(AccessState, sizeof(ACCESS_STATE));
+
+ //
+ // Assume RtlZeroMemory has initialized these fields properly
+ //
+
+ ASSERT( AccessState->SecurityDescriptor == NULL );
+ ASSERT( AccessState->PrivilegesAllocated == FALSE );
+
+ AccessState->AuxData = AuxData;
+
+ SeCaptureSubjectContext(&AccessState->SubjectSecurityContext);
+
+ if (((PTOKEN)EffectiveToken( &AccessState->SubjectSecurityContext ))->TokenFlags & TOKEN_HAS_TRAVERSE_PRIVILEGE ) {
+ AccessState->Flags = TOKEN_HAS_TRAVERSE_PRIVILEGE;
+ }
+
+ AccessState->RemainingDesiredAccess = MappedAccessMask;
+ AccessState->OriginalDesiredAccess = DesiredAccess;
+ AuxData->PrivilegesUsed = (PPRIVILEGE_SET)((ULONG)AccessState +
+ (FIELD_OFFSET(ACCESS_STATE, Privileges)));
+
+ ExAllocateLocallyUniqueId(&AccessState->OperationID);
+
+ if (ARGUMENT_PRESENT(GenericMapping)) {
+ AuxData->GenericMapping = *GenericMapping;
+ }
+
+ return( STATUS_SUCCESS );
+
+}
+
+
+#if 0
+
+
+VOID
+SeDeleteAccessState(
+ PACCESS_STATE AccessState
+ )
+
+/*++
+
+Routine Description:
+
+ This routine deallocates any memory that may have been allocated as
+ part of constructing the access state (normally only for an excessive
+ number of privileges), and frees the Subject Context.
+
+Arguments:
+
+ AccessState - a pointer to the ACCESS_STATE structure to be
+ deallocated.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PAUX_ACCESS_DATA AuxData;
+
+ PAGED_CODE();
+
+ AuxData = (PAUX_ACCESS_DATA)AccessState->AuxData;
+
+ if (AccessState->PrivilegesAllocated) {
+ ExFreePool( (PVOID)AuxData->PrivilegesUsed );
+ }
+
+ if (AccessState->ObjectName.Buffer != NULL) {
+ ExFreePool(AccessState->ObjectName.Buffer);
+ }
+
+ if (AccessState->ObjectTypeName.Buffer != NULL) {
+ ExFreePool(AccessState->ObjectTypeName.Buffer);
+ }
+
+ ExFreePool( AuxData );
+
+ SeReleaseSubjectContext(&AccessState->SubjectSecurityContext);
+
+ return;
+}
+
+
+#endif
+
+VOID
+SeDeleteAccessState(
+ PACCESS_STATE AccessState
+ )
+
+/*++
+
+Routine Description:
+
+ This routine deallocates any memory that may have been allocated as
+ part of constructing the access state (normally only for an excessive
+ number of privileges), and frees the Subject Context.
+
+Arguments:
+
+ AccessState - a pointer to the ACCESS_STATE structure to be
+ deallocated.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PAUX_ACCESS_DATA AuxData;
+
+ PAGED_CODE();
+
+ AuxData = (PAUX_ACCESS_DATA)AccessState->AuxData;
+
+ if (AccessState->PrivilegesAllocated) {
+ ExFreePool( (PVOID)AuxData->PrivilegesUsed );
+ }
+
+ if (AccessState->ObjectName.Buffer != NULL) {
+ ExFreePool(AccessState->ObjectName.Buffer);
+ }
+
+ if (AccessState->ObjectTypeName.Buffer != NULL) {
+ ExFreePool(AccessState->ObjectTypeName.Buffer);
+ }
+
+ SeReleaseSubjectContext(&AccessState->SubjectSecurityContext);
+
+ return;
+}
+
+VOID
+SeSetAccessStateGenericMapping (
+ PACCESS_STATE AccessState,
+ PGENERIC_MAPPING GenericMapping
+ )
+
+/*++
+
+Routine Description:
+
+ This routine sets the GenericMapping field in an AccessState structure.
+ It must be called before access validation is performed if the GenericMapping
+ is not passed in when the AccessState structure is created.
+
+Arguments:
+
+ AccessState - a pointer to the ACCESS_STATE structure to be modified.
+
+ GenericMapping - a pointer to the GenericMapping to be copied into the AccessState.
+
+Return Value:
+
+
+--*/
+{
+ PAUX_ACCESS_DATA AuxData;
+
+ PAGED_CODE();
+
+ AuxData = (PAUX_ACCESS_DATA)AccessState->AuxData;
+
+ AuxData->GenericMapping = *GenericMapping;
+
+ return;
+}
+
+
+
+NTSTATUS
+SeAppendPrivileges(
+ PACCESS_STATE AccessState,
+ PPRIVILEGE_SET Privileges
+ )
+/*++
+
+Routine Description:
+
+ This routine takes a privilege set and adds it to the privilege set
+ imbedded in an ACCESS_STATE structure.
+
+ An AccessState may contain up to three imbedded privileges. To
+ add more, this routine will allocate a block of memory, copy
+ the current privileges into it, and append the new privilege
+ to that block. A bit is set in the AccessState indicating that
+ the pointer to the privilge set in the structure points to pool
+ memory and must be deallocated.
+
+Arguments:
+
+ AccessState - The AccessState structure representing the current
+ access attempt.
+
+ Privileges - A pointer to a privilege set to be added.
+
+Return Value:
+
+ STATUS_INSUFFICIENT_RESOURCES - an attempt to allocate pool memory
+ failed.
+
+--*/
+
+{
+ ULONG NewPrivilegeSetSize;
+ PPRIVILEGE_SET NewPrivilegeSet;
+ PAUX_ACCESS_DATA AuxData;
+
+ PAGED_CODE();
+
+ AuxData = (PAUX_ACCESS_DATA)AccessState->AuxData;
+
+ if (Privileges->PrivilegeCount + AuxData->PrivilegesUsed->PrivilegeCount >
+ INITIAL_PRIVILEGE_COUNT) {
+
+ //
+ // Compute the total size of the two privilege sets
+ //
+
+ NewPrivilegeSetSize = SepPrivilegeSetSize( Privileges ) +
+ SepPrivilegeSetSize( AuxData->PrivilegesUsed );
+
+ NewPrivilegeSet = ExAllocatePoolWithTag( PagedPool, NewPrivilegeSetSize, 'rPeS' );
+
+ if (NewPrivilegeSet == NULL) {
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+
+ RtlMoveMemory(
+ NewPrivilegeSet,
+ AuxData->PrivilegesUsed,
+ SepPrivilegeSetSize( AuxData->PrivilegesUsed )
+ );
+
+ //
+ // Note that this will adjust the privilege count in the
+ // structure for us.
+ //
+
+ SepConcatenatePrivileges(
+ NewPrivilegeSet,
+ NewPrivilegeSetSize,
+ Privileges
+ );
+
+ if (AccessState->PrivilegesAllocated) {
+ ExFreePool( AuxData->PrivilegesUsed );
+ }
+
+ AuxData->PrivilegesUsed = NewPrivilegeSet;
+
+ //
+ // Mark that we've allocated memory for the privilege set,
+ // so we know to free it when we're cleaning up.
+ //
+
+ AccessState->PrivilegesAllocated = TRUE;
+
+ } else {
+
+ //
+ // Note that this will adjust the privilege count in the
+ // structure for us.
+ //
+
+ SepConcatenatePrivileges(
+ AuxData->PrivilegesUsed,
+ sizeof(INITIAL_PRIVILEGE_SET),
+ Privileges
+ );
+
+ }
+
+ return( STATUS_SUCCESS );
+
+}
+
+
+VOID
+SepConcatenatePrivileges(
+ IN PPRIVILEGE_SET TargetPrivilegeSet,
+ IN ULONG TargetBufferSize,
+ IN PPRIVILEGE_SET SourcePrivilegeSet
+ )
+
+/*++
+
+Routine Description:
+
+ Takes two privilege sets and appends the second to the end of the
+ first.
+
+ There must be enough space left at the end of the first privilege
+ set to contain the second.
+
+Arguments:
+
+ TargetPrivilegeSet - Supplies a buffer containing a privilege set.
+ The buffer must be large enough to contain the second privilege
+ set.
+
+ TargetBufferSize - Supplies the size of the target buffer.
+
+ SourcePrivilegeSet - Supplies the privilege set to be copied
+ into the target buffer.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ PVOID Base;
+ PVOID Source;
+ ULONG Length;
+
+ PAGED_CODE();
+
+ ASSERT( ((ULONG)SepPrivilegeSetSize( TargetPrivilegeSet ) +
+ (ULONG)SepPrivilegeSetSize( SourcePrivilegeSet ) -
+ SEP_PRIVILEGE_SET_HEADER_SIZE ) <=
+ TargetBufferSize
+ );
+
+ Base = (PVOID)((ULONG)TargetPrivilegeSet + SepPrivilegeSetSize( TargetPrivilegeSet ));
+
+ Source = (PVOID) ((ULONG)SourcePrivilegeSet + SEP_PRIVILEGE_SET_HEADER_SIZE);
+
+ Length = SourcePrivilegeSet->PrivilegeCount * sizeof(LUID_AND_ATTRIBUTES);
+
+ RtlMoveMemory(
+ Base,
+ Source,
+ Length
+ );
+
+ TargetPrivilegeSet->PrivilegeCount += SourcePrivilegeSet->PrivilegeCount;
+
+}
diff --git a/private/ntos/se/seaudit.c b/private/ntos/se/seaudit.c
new file mode 100644
index 000000000..a0a90c39c
--- /dev/null
+++ b/private/ntos/se/seaudit.c
@@ -0,0 +1,4049 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ seaudit.c
+
+Abstract:
+
+ This Module implements the audit and alarm procedures.
+
+Author:
+
+ Robert Reichel (robertre) 26-Nov-90
+ Scott Birrell (ScottBi) 17-Jan-92
+
+Environment:
+
+ Kernel Mode
+
+Revision History:
+
+ Richard Ward (richardw) 14-Apr-92
+
+--*/
+
+#include "tokenp.h"
+#include "adt.h"
+#include "adtp.h"
+
+VOID
+SepProbeAndCaptureString_U (
+ IN PUNICODE_STRING SourceString,
+ OUT PUNICODE_STRING *DestString
+ );
+
+VOID
+SepFreeCapturedString(
+ IN PUNICODE_STRING CapturedString
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE,NtAccessCheckAndAuditAlarm)
+#pragma alloc_text(PAGE,NtCloseObjectAuditAlarm)
+#pragma alloc_text(PAGE,NtDeleteObjectAuditAlarm)
+#pragma alloc_text(PAGE,NtOpenObjectAuditAlarm)
+#pragma alloc_text(PAGE,NtPrivilegeObjectAuditAlarm)
+#pragma alloc_text(PAGE,NtPrivilegedServiceAuditAlarm)
+#pragma alloc_text(PAGE,SeAuditHandleCreation)
+#pragma alloc_text(PAGE,SeAuditingFileEvents)
+#pragma alloc_text(PAGE,SeCheckAuditPrivilege)
+#pragma alloc_text(PAGE,SeCloseObjectAuditAlarm)
+#pragma alloc_text(PAGE,SeDeleteObjectAuditAlarm)
+#pragma alloc_text(PAGE,SeCreateObjectAuditAlarm)
+#pragma alloc_text(PAGE,SeObjectReferenceAuditAlarm)
+#pragma alloc_text(PAGE,SeOpenObjectAuditAlarm)
+#pragma alloc_text(PAGE,SePrivilegeObjectAuditAlarm)
+#pragma alloc_text(PAGE,SePrivilegedServiceAuditAlarm)
+#pragma alloc_text(PAGE,SeTraverseAuditAlarm)
+#pragma alloc_text(PAGE,SepExamineSacl)
+#pragma alloc_text(PAGE,SepFilterPrivilegeAudits)
+#pragma alloc_text(PAGE,SepFreeCapturedString)
+#pragma alloc_text(PAGE,SepProbeAndCaptureString_U)
+#pragma alloc_text(PAGE,SepSinglePrivilegeCheck)
+#endif
+
+//
+// Flag to tell us if we are auditing shutdown events.
+//
+// Move this to seglobal.c
+//
+
+BOOLEAN SepAuditShutdownEvents = FALSE;
+
+
+//
+// Private useful routines
+//
+
+//
+// This routine is to be called to do simple checks of single privileges
+// against the passed token.
+//
+// DO NOT CALL THIS TO CHECK FOR SeTcbPrivilege SINCE THAT MUST
+// BE CHECKED AGAINST THE PRIMARY TOKEN ONLY!
+//
+
+BOOLEAN
+SepSinglePrivilegeCheck (
+ LUID DesiredPrivilege,
+ IN PACCESS_TOKEN Token,
+ IN KPROCESSOR_MODE PreviousMode
+ )
+
+/*++
+
+Routine Description:
+
+ Determines if the passed token has the passed privilege.
+
+Arguments:
+
+ DesiredPrivilege - The privilege to be tested for.
+
+ Token - The token being examined.
+
+ PreviousMode - The previous processor mode.
+
+Return Value:
+
+ Returns TRUE of the subject has the passed privilege, FALSE otherwise.
+
+--*/
+
+{
+
+ LUID_AND_ATTRIBUTES Privilege;
+ BOOLEAN Result;
+
+ PAGED_CODE();
+
+ //
+ // Don't let anyone call this to test for SeTcbPrivilege
+ //
+
+ ASSERT(!((DesiredPrivilege.LowPart == SeTcbPrivilege.LowPart) &&
+ (DesiredPrivilege.HighPart == SeTcbPrivilege.HighPart)));
+
+ Privilege.Luid = DesiredPrivilege;
+ Privilege.Attributes = 0;
+
+ Result = SepPrivilegeCheck(
+ Token,
+ &Privilege,
+ 1,
+ PRIVILEGE_SET_ALL_NECESSARY,
+ PreviousMode
+ );
+
+ return(Result);
+}
+
+
+BOOLEAN
+SeCheckAuditPrivilege (
+ IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
+ IN KPROCESSOR_MODE PreviousMode
+ )
+/*++
+
+Routine Description:
+
+ This routine specifically searches the primary token (rather than
+ the effective token) of the calling process for SeAuditPrivilege.
+ In order to do this it must call the underlying worker
+ SepPrivilegeCheck directly, to ensure that the correct token is
+ searched
+
+Arguments:
+
+ SubjectSecurityContext - The subject being examined.
+
+ PreviousMode - The previous processor mode.
+
+Return Value:
+
+ Returns TRUE if the subject has SeAuditPrivilege, FALSE otherwise.
+
+--*/
+{
+
+ PRIVILEGE_SET RequiredPrivileges;
+ BOOLEAN AccessGranted;
+
+ PAGED_CODE();
+
+ RequiredPrivileges.PrivilegeCount = 1;
+ RequiredPrivileges.Control = PRIVILEGE_SET_ALL_NECESSARY;
+ RequiredPrivileges.Privilege[0].Luid = SeAuditPrivilege;
+ RequiredPrivileges.Privilege[0].Attributes = 0;
+
+ AccessGranted = SepPrivilegeCheck(
+ SubjectSecurityContext->PrimaryToken, // token
+ RequiredPrivileges.Privilege, // privilege set
+ RequiredPrivileges.PrivilegeCount, // privilege count
+ PRIVILEGE_SET_ALL_NECESSARY, // privilege control
+ PreviousMode // previous mode
+ );
+
+ if ( PreviousMode != KernelMode ) {
+
+ SePrivilegedServiceAuditAlarm (
+ NULL, // BUGWARNING need service name
+ SubjectSecurityContext,
+ &RequiredPrivileges,
+ AccessGranted
+ );
+ }
+
+ return( AccessGranted );
+}
+
+
+VOID
+SepProbeAndCaptureString_U (
+ IN PUNICODE_STRING SourceString,
+ OUT PUNICODE_STRING *DestString
+ )
+/*++
+
+Routine Description:
+
+ Helper routine to probe and capture a Unicode string argument.
+
+ This routine may fail due to lack of memory, in which case,
+ it will return a NULL pointer in the output parameter.
+
+Arguments:
+
+ SourceString - Pointer to a Unicode string to be captured.
+
+ DestString - Returns a pointer to a captured Unicode string. This
+ will be one contiguous structure, and thus may be freed by
+ a single call to ExFreePool().
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PAGED_CODE();
+
+ ProbeForRead( SourceString, sizeof( UNICODE_STRING ), sizeof( ULONG ) );
+
+ *DestString = NULL;
+
+ //
+ // Probe the pointer to the buffer to ensure that the
+ // characters in the buffer are readable by the caller.
+ // If so, then try to allocate a pool buffer and copy the
+ // buffer into it.
+ //
+
+ ProbeForRead((PVOID)SourceString->Buffer,
+ SourceString->Length,
+ sizeof(WCHAR));
+
+
+ *DestString = ExAllocatePoolWithTag( PagedPool,
+ sizeof( UNICODE_STRING ) + SourceString->MaximumLength,
+ 'sUeS'
+ );
+
+ if ( *DestString == NULL ) {
+ return;
+ }
+
+ (*DestString)->Buffer = (PWSTR)((PCHAR)(*DestString) + sizeof( UNICODE_STRING ));
+ (*DestString)->MaximumLength = SourceString->MaximumLength;
+
+ RtlCopyUnicodeString(
+ *DestString,
+ SourceString
+ );
+
+ return;
+}
+
+
+VOID
+SepFreeCapturedString(
+ IN PUNICODE_STRING CapturedString
+ )
+
+/*++
+
+Routine Description:
+
+ Frees a string captured by SepProbeAndCaptureString.
+
+Arguments:
+
+ CapturedString - Supplies a pointer to a string previously captured
+ by SepProbeAndCaptureString.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ ExFreePool( CapturedString );
+ return;
+}
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Privileged Object Audit Alarms //
+// //
+////////////////////////////////////////////////////////////////////////
+
+
+NTSTATUS
+NtPrivilegeObjectAuditAlarm (
+ IN PUNICODE_STRING SubsystemName,
+ IN PVOID HandleId,
+ IN HANDLE ClientToken,
+ IN ACCESS_MASK DesiredAccess,
+ IN PPRIVILEGE_SET Privileges,
+ IN BOOLEAN AccessGranted
+ )
+/*++
+
+Routine Description:
+
+ This routine is used to generate audit and alarm messages when an
+ attempt is made to perform privileged operations on a protected
+ subsystem object after the object is already opened. This routine may
+ result in several messages being generated and sent to Port objects.
+ This may result in a significant latency before returning. Design of
+ routines that must call this routine must take this potential latency
+ into account. This may have an impact on the approach taken for data
+ structure mutex locking, for example.
+
+ This API requires the caller have SeAuditPrivilege privilege. The test
+ for this privilege is always against the primary token of the calling
+ process, allowing the caller to be impersonating a client during the
+ call with no ill effects.
+
+Arguments:
+
+ SubsystemName - Supplies a name string identifying the subsystem
+ calling the routine.
+
+ HandleId - A unique value representing the client's handle to the
+ object.
+
+ ClientToken - A handle to a token object representing the client that
+ requested the operation. This handle must be obtained from a
+ communication session layer, such as from an LPC Port or Local
+ Named Pipe, to prevent possible security policy violations.
+
+ DesiredAccess - The desired access mask. This mask must have been
+ previously mapped to contain no generic accesses.
+
+ Privileges - The set of privileges required for the requested
+ operation. Those privileges that were held by the subject are
+ marked using the UsedForAccess flag of the attributes
+ associated with each privilege.
+
+ AccessGranted - Indicates whether the requested access was granted or
+ not. A value of TRUE indicates the access was granted. A value of
+ FALSE indicates the access was not granted.
+
+Return value:
+
+--*/
+{
+
+ KPROCESSOR_MODE PreviousMode;
+ PUNICODE_STRING CapturedSubsystemName = NULL;
+ PPRIVILEGE_SET CapturedPrivileges = NULL;
+ ULONG PrivilegeParameterLength;
+ SECURITY_SUBJECT_CONTEXT SubjectSecurityContext;
+ BOOLEAN Result;
+ PTOKEN Token;
+ NTSTATUS Status;
+ BOOLEAN AuditPerformed;
+
+ PAGED_CODE();
+
+ PreviousMode = KeGetPreviousMode();
+
+ ASSERT(PreviousMode != KernelMode);
+
+ Status = ObReferenceObjectByHandle(
+ ClientToken, // Handle
+ TOKEN_QUERY, // DesiredAccess
+ SepTokenObjectType, // ObjectType
+ PreviousMode, // AccessMode
+ (PVOID *)&Token, // Object
+ NULL // GrantedAccess
+ );
+
+ if (!NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ //
+ // If the passed token is an impersonation token, make sure
+ // it is at SecurityIdentification or above.
+ //
+
+ if (Token->TokenType == TokenImpersonation) {
+
+ if (Token->ImpersonationLevel < SecurityIdentification) {
+
+ ObDereferenceObject( (PVOID)Token );
+
+ return( STATUS_BAD_IMPERSONATION_LEVEL );
+
+ }
+ }
+
+// //
+// // Make sure the passed token is an impersonation token...
+// //
+//
+// if (Token->TokenType != TokenImpersonation) {
+//
+// ObDereferenceObject( (PVOID)Token );
+//
+// return( STATUS_NO_IMPERSONATION_TOKEN );
+//
+// }
+//
+// //
+// // ...and at a high enough impersonation level
+// //
+//
+// if (Token->ImpersonationLevel < SecurityIdentification) {
+//
+// ObDereferenceObject( (PVOID)Token );
+//
+// return( STATUS_BAD_IMPERSONATION_LEVEL );
+//
+// }
+
+ //
+ // Check for SeAuditPrivilege
+ //
+
+ SeCaptureSubjectContext ( &SubjectSecurityContext );
+
+ Result = SeCheckAuditPrivilege (
+ &SubjectSecurityContext,
+ PreviousMode
+ );
+
+ if (!Result) {
+
+ ObDereferenceObject( (PVOID)Token );
+ SeReleaseSubjectContext ( &SubjectSecurityContext );
+ return(STATUS_PRIVILEGE_NOT_HELD);
+
+ }
+
+ try {
+
+ SepProbeAndCaptureString_U ( SubsystemName,
+ &CapturedSubsystemName );
+
+ ProbeForRead(
+ Privileges,
+ sizeof(PRIVILEGE_SET),
+ sizeof(ULONG)
+ );
+
+ PrivilegeParameterLength = (ULONG)sizeof(PRIVILEGE_SET) +
+ ((Privileges->PrivilegeCount - ANYSIZE_ARRAY) *
+ (ULONG)sizeof(LUID_AND_ATTRIBUTES) );
+
+ ProbeForRead(
+ Privileges,
+ PrivilegeParameterLength,
+ sizeof(ULONG)
+ );
+
+ CapturedPrivileges = ExAllocatePoolWithTag( PagedPool,
+ PrivilegeParameterLength,
+ 'rPeS'
+ );
+
+ if (CapturedPrivileges != NULL) {
+
+ RtlMoveMemory ( CapturedPrivileges,
+ Privileges,
+ PrivilegeParameterLength );
+ }
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ if (CapturedPrivileges != NULL) {
+ ExFreePool( CapturedPrivileges );
+ }
+
+ if (CapturedSubsystemName != NULL) {
+ SepFreeCapturedString ( CapturedSubsystemName );
+ }
+
+ SeReleaseSubjectContext ( &SubjectSecurityContext );
+
+ ObDereferenceObject( (PVOID)Token );
+
+ return GetExceptionCode();
+
+ }
+
+ //
+ // No need to lock the token, because the only thing we're going
+ // to reference in it is the User's Sid, which cannot be changed.
+ //
+
+ //
+ // SepPrivilegeObjectAuditAlarm will check the global flags
+ // to determine if we're supposed to be auditing here.
+ //
+
+ AuditPerformed = SepAdtPrivilegeObjectAuditAlarm (
+ CapturedSubsystemName,
+ HandleId,
+ Token, // ClientToken
+ SubjectSecurityContext.PrimaryToken, // PrimaryToken
+ SubjectSecurityContext.ProcessAuditId,
+ DesiredAccess,
+ CapturedPrivileges,
+ AccessGranted
+ );
+
+ if (CapturedPrivileges != NULL) {
+ ExFreePool( CapturedPrivileges );
+ }
+
+ if (CapturedSubsystemName != NULL) {
+ SepFreeCapturedString ( CapturedSubsystemName );
+ }
+
+ SeReleaseSubjectContext ( &SubjectSecurityContext );
+
+ ObDereferenceObject( (PVOID)Token );
+
+ return(STATUS_SUCCESS);
+}
+
+
+VOID
+SePrivilegeObjectAuditAlarm(
+ IN HANDLE Handle,
+ IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
+ IN ACCESS_MASK DesiredAccess,
+ IN PPRIVILEGE_SET Privileges,
+ IN BOOLEAN AccessGranted,
+ IN KPROCESSOR_MODE AccessMode
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used by object methods that perform privileged
+ operations to generate audit and alarm messages related to the use
+ of privileges, or attempts to use privileges.
+
+Arguments:
+
+ Object - Address of the object accessed. This value will not be
+ used as a pointer (referenced). It is necessary only to enter
+ into log messages.
+
+ Handle - Provides the handle value assigned for the open.
+
+ SecurityDescriptor - A pointer to the security descriptor of the
+ object being accessed.
+
+ SubjectSecurityContext - A pointer to the captured security
+ context of the subject attempting to open the object.
+
+ DesiredAccess - The desired access mask. This mask must have been
+ previously mapped to contain no generic accesses.
+
+ Privileges - Points to a set of privileges required for the access
+ attempt. Those privileges that were held by the subject are
+ marked using the UsedForAccess flag of the PRIVILEGE_ATTRIBUTES
+ associated with each privilege.
+
+ AccessGranted - Indicates whether the access was granted or
+ denied. A value of TRUE indicates the access was allowed. A
+ value of FALSE indicates the access was denied.
+
+ AccessMode - Indicates the access mode used for the access check.
+ Messages will not be generated by kernel mode accesses.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ BOOLEAN AuditPerformed;
+
+ PAGED_CODE();
+
+ if (AccessMode != KernelMode) {
+
+ AuditPerformed = SepAdtPrivilegeObjectAuditAlarm (
+ &SeSubsystemName,
+ Handle,
+ SubjectSecurityContext->ClientToken,
+ SubjectSecurityContext->PrimaryToken,
+ SubjectSecurityContext->ProcessAuditId,
+ DesiredAccess,
+ Privileges,
+ AccessGranted
+ );
+ }
+}
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Privileged Service Audit Alarms //
+// //
+////////////////////////////////////////////////////////////////////////
+
+
+NTSTATUS
+NtPrivilegedServiceAuditAlarm (
+ IN PUNICODE_STRING SubsystemName,
+ IN PUNICODE_STRING ServiceName,
+ IN HANDLE ClientToken,
+ IN PPRIVILEGE_SET Privileges,
+ IN BOOLEAN AccessGranted
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to generate audit and alarm messages when an
+ attempt is made to perform privileged system service operations. This
+ routine may result in several messages being generated and sent to Port
+ objects. This may result in a significant latency before returning.
+ Design of routines that must call this routine must take this potential
+ latency into account. This may have an impact on the approach taken
+ for data structure mutex locking, for example.
+
+ This API requires the caller have SeAuditPrivilege privilege. The test
+ for this privilege is always against the primary token of the calling
+ process, allowing the caller to be impersonating a client during the
+ call with no ill effects
+
+Arguments:
+
+ SubsystemName - Supplies a name string identifying the subsystem
+ calling the routine.
+
+ ServiceName - Supplies a name of the privileged subsystem service. For
+ example, "RESET RUNTIME LOCAL SECURITY POLICY" might be specified
+ by a Local Security Authority service used to update the local
+ security policy database.
+
+ ClientToken - A handle to a token object representing the client that
+ requested the operation. This handle must be obtained from a
+ communication session layer, such as from an LPC Port or Local
+ Named Pipe, to prevent possible security policy violations.
+
+ Privileges - Points to a set of privileges required to perform the
+ privileged operation. Those privileges that were held by the
+ subject are marked using the UsedForAccess flag of the
+ attributes associated with each privilege.
+
+ AccessGranted - Indicates whether the requested access was granted or
+ not. A value of TRUE indicates the access was granted. A value of
+ FALSE indicates the access was not granted.
+
+Return value:
+
+--*/
+
+{
+
+ PPRIVILEGE_SET CapturedPrivileges = NULL;
+ ULONG PrivilegeParameterLength = 0;
+ BOOLEAN Result;
+ SECURITY_SUBJECT_CONTEXT SubjectSecurityContext;
+ KPROCESSOR_MODE PreviousMode;
+ PUNICODE_STRING CapturedSubsystemName = NULL;
+ PUNICODE_STRING CapturedServiceName = NULL;
+ NTSTATUS Status;
+ PTOKEN Token;
+
+ PAGED_CODE();
+
+ PreviousMode = KeGetPreviousMode();
+
+ ASSERT(PreviousMode != KernelMode);
+
+ Status = ObReferenceObjectByHandle(
+ ClientToken, // Handle
+ TOKEN_QUERY, // DesiredAccess
+ SepTokenObjectType, // ObjectType
+ PreviousMode, // AccessMode
+ (PVOID *)&Token, // Object
+ NULL // GrantedAccess
+ );
+
+ if ( !NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ //
+ // If the passed token is an impersonation token, make sure
+ // it is at SecurityIdentification or above.
+ //
+
+ if (Token->TokenType == TokenImpersonation) {
+
+ if (Token->ImpersonationLevel < SecurityIdentification) {
+
+ ObDereferenceObject( (PVOID)Token );
+
+ return( STATUS_BAD_IMPERSONATION_LEVEL );
+
+ }
+ }
+
+// //
+// // Make sure the passed token is an impersonation token...
+// //
+//
+// if (Token->TokenType != TokenImpersonation) {
+//
+// ObDereferenceObject( (PVOID)Token );
+//
+// return( STATUS_NO_IMPERSONATION_TOKEN );
+//
+// }
+//
+// //
+// // ...and at a high enough impersonation level
+// //
+//
+// if (Token->ImpersonationLevel < SecurityIdentification) {
+//
+// ObDereferenceObject( (PVOID)Token );
+//
+// return( STATUS_BAD_IMPERSONATION_LEVEL );
+//
+// }
+
+ //
+ // Check for SeAuditPrivilege
+ //
+
+ SeCaptureSubjectContext ( &SubjectSecurityContext );
+
+ Result = SeCheckAuditPrivilege (
+ &SubjectSecurityContext,
+ PreviousMode
+ );
+
+ if (!Result) {
+
+ ObDereferenceObject( (PVOID)Token );
+
+ SeReleaseSubjectContext ( &SubjectSecurityContext );
+
+ return(STATUS_PRIVILEGE_NOT_HELD);
+ }
+
+ try {
+
+ if ( ARGUMENT_PRESENT( SubsystemName )) {
+ SepProbeAndCaptureString_U ( SubsystemName,
+ &CapturedSubsystemName );
+ }
+
+ if ( ARGUMENT_PRESENT( ServiceName )) {
+ SepProbeAndCaptureString_U ( ServiceName,
+ &CapturedServiceName );
+
+ }
+
+ ProbeForRead(
+ Privileges,
+ sizeof(PRIVILEGE_SET),
+ sizeof(ULONG)
+ );
+
+ PrivilegeParameterLength = (ULONG)sizeof(PRIVILEGE_SET) +
+ ((Privileges->PrivilegeCount - ANYSIZE_ARRAY) *
+ (ULONG)sizeof(LUID_AND_ATTRIBUTES) );
+
+ ProbeForRead(
+ Privileges,
+ PrivilegeParameterLength,
+ sizeof(ULONG)
+ );
+
+ CapturedPrivileges = ExAllocatePoolWithTag( PagedPool,
+ PrivilegeParameterLength,
+ 'rPeS'
+ );
+
+ //
+ // If ExAllocatePool has failed, too bad. Carry on and do as much of the
+ // audit as we can.
+ //
+
+ if (CapturedPrivileges != NULL) {
+
+ RtlMoveMemory ( CapturedPrivileges,
+ Privileges,
+ PrivilegeParameterLength );
+
+ }
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ if (CapturedSubsystemName != NULL) {
+ SepFreeCapturedString ( CapturedSubsystemName );
+ }
+
+ if (CapturedServiceName != NULL) {
+ SepFreeCapturedString ( CapturedServiceName );
+ }
+
+ if (CapturedPrivileges != NULL) {
+ ExFreePool ( CapturedPrivileges );
+ }
+
+ SeReleaseSubjectContext ( &SubjectSecurityContext );
+
+ ObDereferenceObject( (PVOID)Token );
+
+ return GetExceptionCode();
+
+ }
+
+ //
+ // The AuthenticationId is in the read-only part of the token,
+ // so we may reference it without having the token read-locked.
+ //
+
+ SepAdtPrivilegedServiceAuditAlarm ( CapturedSubsystemName,
+ CapturedServiceName,
+ Token,
+ SubjectSecurityContext.PrimaryToken,
+ CapturedPrivileges,
+ AccessGranted );
+
+ if (CapturedSubsystemName != NULL) {
+ SepFreeCapturedString ( CapturedSubsystemName );
+ }
+
+ if (CapturedServiceName != NULL) {
+ SepFreeCapturedString ( CapturedServiceName );
+ }
+
+ if (CapturedPrivileges != NULL) {
+ ExFreePool ( CapturedPrivileges );
+ }
+
+ ObDereferenceObject( (PVOID)Token );
+
+ SeReleaseSubjectContext ( &SubjectSecurityContext );
+
+ return(STATUS_SUCCESS);
+}
+
+
+VOID
+SePrivilegedServiceAuditAlarm (
+ IN PUNICODE_STRING ServiceName,
+ IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
+ IN PPRIVILEGE_SET Privileges,
+ IN BOOLEAN AccessGranted
+ )
+/*++
+
+Routine Description:
+
+ This routine is to be called whenever a privileged system service
+ is attempted. It should be called immediately after the privilege
+ check regardless of whether or not the test succeeds.
+
+Arguments:
+
+ ServiceName - Supplies the name of the privileged system service.
+
+ SubjectSecurityContext - The subject security context representing
+ the caller of the system service.
+
+ Privileges - Supplies a privilge set containing the privilege(s)
+ required for the access.
+
+ AccessGranted - Supplies the results of the privilege test.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PTOKEN Token;
+
+ PAGED_CODE();
+
+ if ( SepAdtAuditThisEvent( AuditCategoryPrivilegeUse, &AccessGranted ) &&
+ SepFilterPrivilegeAudits( Privileges )) {
+
+ Token = (PTOKEN)EffectiveToken( SubjectSecurityContext );
+
+ if ( RtlEqualSid( SeLocalSystemSid, SepTokenUserSid( Token ))) {
+ return;
+ }
+
+ SepAdtPrivilegedServiceAuditAlarm (
+ &SeSubsystemName,
+ ServiceName,
+ SubjectSecurityContext->ClientToken,
+ SubjectSecurityContext->PrimaryToken,
+ Privileges,
+ AccessGranted
+ );
+ }
+
+ return;
+}
+
+
+NTSTATUS
+NtAccessCheckAndAuditAlarm (
+ IN PUNICODE_STRING SubsystemName,
+ IN PVOID HandleId,
+ IN PUNICODE_STRING ObjectTypeName,
+ IN PUNICODE_STRING ObjectName,
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN ACCESS_MASK DesiredAccess,
+ IN PGENERIC_MAPPING GenericMapping,
+ IN BOOLEAN ObjectCreation,
+ OUT PACCESS_MASK GrantedAccess,
+ OUT PNTSTATUS AccessStatus,
+ OUT PBOOLEAN GenerateOnClose
+ )
+/*++
+
+Routine Description:
+
+ This system service is used to perform both an access validation and
+ generate the corresponding audit and alarm messages. This service may
+ only be used by a protected server that chooses to impersonate its
+ client and thereby specifies the client security context implicitly.
+
+Arguments:
+
+ SubsystemName - Supplies a name string identifying the subsystem
+ calling the routine.
+
+ HandleId - A unique value that will be used to represent the client's
+ handle to the object. This value is ignored (and may be re-used)
+ if the access is denied.
+
+ ObjectTypeName - Supplies the name of the type of the object being
+ created or accessed.
+
+ ObjectName - Supplies the name of the object being created or accessed.
+
+ SecurityDescriptor - A pointer to the Security Descriptor against which
+ acccess is to be checked.
+
+ DesiredAccess - The desired acccess mask. This mask must have been
+ previously mapped to contain no generic accesses.
+
+ GenericMapping - Supplies a pointer to the generic mapping associated
+ with this object type.
+
+ ObjectCreation - A boolean flag indicated whether the access will
+ result in a new object being created if granted. A value of TRUE
+ indicates an object will be created, FALSE indicates an existing
+ object will be opened.
+
+ GrantedAccess - Receives a masking indicating which accesses have been
+ granted.
+
+ AccessStatus - Receives an indication of the success or failure of the
+ access check. If access is granted, STATUS_SUCCESS is returned.
+ If access is denied, a value appropriate for return to the client
+ is returned. This will be STATUS_ACCESS_DENIED or, when mandatory
+ access controls are implemented, STATUS_OBJECT_NOT_FOUND.
+
+ GenerateOnClose - Points to a boolean that is set by the audity
+ generation routine and must be passed to NtCloseObjectAuditAlarm
+ when the object handle is closed.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the call completed successfully. In this
+ case, ClientStatus receives the result of the access check.
+
+ STATUS_PRIVILEGE_NOT_HELD - Indicates the caller does not have
+ sufficient privilege to use this privileged system service.
+
+--*/
+
+{
+
+ SECURITY_SUBJECT_CONTEXT SubjectSecurityContext;
+
+ NTSTATUS Status;
+
+ ACCESS_MASK LocalGrantedAccess = (ACCESS_MASK)0;
+ BOOLEAN LocalGenerateOnClose = FALSE;
+
+ KPROCESSOR_MODE PreviousMode;
+
+ PUNICODE_STRING CapturedSubsystemName = (PUNICODE_STRING) NULL;
+ PUNICODE_STRING CapturedObjectTypeName = (PUNICODE_STRING) NULL;
+ PUNICODE_STRING CapturedObjectName = (PUNICODE_STRING) NULL;
+ PSECURITY_DESCRIPTOR CapturedSecurityDescriptor = (PSECURITY_DESCRIPTOR) NULL;
+
+ ACCESS_MASK PreviouslyGrantedAccess = (ACCESS_MASK)0;
+ GENERIC_MAPPING LocalGenericMapping;
+
+ PPRIVILEGE_SET PrivilegeSet = NULL;
+
+ BOOLEAN Result;
+
+ BOOLEAN AccessGranted = FALSE;
+ BOOLEAN GenerateAudit = FALSE;
+ BOOLEAN GenerateAlarm = FALSE;
+ LUID OperationId;
+ BOOLEAN AuditPerformed;
+
+ PAGED_CODE();
+
+ PreviousMode = KeGetPreviousMode();
+
+ ASSERT( PreviousMode != KernelMode );
+
+ //
+ // Capture the subject Context
+ //
+
+ SeCaptureSubjectContext ( &SubjectSecurityContext );
+
+ //
+ // Make sure we're impersonating a client...
+ //
+
+ if ( (SubjectSecurityContext.ClientToken == NULL) ) {
+
+ SeReleaseSubjectContext ( &SubjectSecurityContext );
+ return(STATUS_NO_IMPERSONATION_TOKEN);
+
+ }
+
+ //
+ // ...and at a high enough impersonation level
+ //
+
+ if (SubjectSecurityContext.ImpersonationLevel < SecurityIdentification) {
+
+ SeReleaseSubjectContext ( &SubjectSecurityContext );
+ return(STATUS_BAD_IMPERSONATION_LEVEL);
+
+ }
+
+ try {
+
+ ProbeForWriteUlong((PULONG)AccessStatus);
+ ProbeForWriteUlong((PULONG)GrantedAccess);
+
+ ProbeForRead(
+ GenericMapping,
+ sizeof(GENERIC_MAPPING),
+ sizeof(ULONG)
+ );
+
+ LocalGenericMapping = *GenericMapping;
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ SeReleaseSubjectContext( &SubjectSecurityContext );
+ return( GetExceptionCode() );
+
+ }
+
+ //
+ // Check for SeAuditPrivilege
+ //
+
+ Result = SeCheckAuditPrivilege (
+ &SubjectSecurityContext,
+ PreviousMode
+ );
+
+ if (!Result) {
+
+ SeReleaseSubjectContext ( &SubjectSecurityContext );
+
+ try {
+
+ *AccessStatus = STATUS_ACCESS_DENIED;
+ *GrantedAccess = (ACCESS_MASK)0;
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ return( GetExceptionCode() );
+
+ }
+
+ return(STATUS_PRIVILEGE_NOT_HELD);
+ }
+
+ if (DesiredAccess &
+ ( GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL )) {
+
+ SeReleaseSubjectContext ( &SubjectSecurityContext );
+
+ return(STATUS_GENERIC_NOT_MAPPED);
+ }
+
+ //
+ // Capture the passed security descriptor.
+ //
+ // SeCaptureSecurityDescriptor probes the input security descriptor,
+ // so we don't have to
+ //
+
+ Status = SeCaptureSecurityDescriptor (
+ SecurityDescriptor,
+ PreviousMode,
+ PagedPool,
+ FALSE,
+ &CapturedSecurityDescriptor
+ );
+
+ if (!NT_SUCCESS(Status) || CapturedSecurityDescriptor == NULL) {
+
+ SeReleaseSubjectContext ( &SubjectSecurityContext );
+
+ try {
+
+ *AccessStatus = Status;
+ *GrantedAccess = (ACCESS_MASK)0;
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ return( GetExceptionCode() );
+
+ }
+
+ if ( CapturedSecurityDescriptor == NULL ) {
+
+ return( STATUS_INVALID_SECURITY_DESCR );
+ }
+
+ return( STATUS_SUCCESS );
+ }
+
+ //
+ // A valid security descriptor must have an owner and a group
+ //
+
+ if ( SepOwnerAddrSecurityDescriptor(
+ (PISECURITY_DESCRIPTOR)CapturedSecurityDescriptor
+ ) == NULL ||
+ SepGroupAddrSecurityDescriptor(
+ (PISECURITY_DESCRIPTOR)CapturedSecurityDescriptor
+ ) == NULL ) {
+
+ SeReleaseSubjectContext ( &SubjectSecurityContext );
+
+ SeReleaseSecurityDescriptor (
+ CapturedSecurityDescriptor,
+ PreviousMode,
+ FALSE
+ );
+
+ return( STATUS_INVALID_SECURITY_DESCR );
+ }
+
+ //
+ // Probe and capture the STRING arguments
+ //
+
+ try {
+
+ ProbeForWriteBoolean(GenerateOnClose);
+
+ SepProbeAndCaptureString_U ( SubsystemName, &CapturedSubsystemName );
+
+ SepProbeAndCaptureString_U ( ObjectTypeName, &CapturedObjectTypeName );
+
+ SepProbeAndCaptureString_U ( ObjectName, &CapturedObjectName );
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ SeReleaseSubjectContext ( &SubjectSecurityContext );
+
+ if (CapturedSubsystemName != NULL) {
+ SepFreeCapturedString( CapturedSubsystemName );
+ }
+
+ if (CapturedObjectTypeName != NULL) {
+ SepFreeCapturedString( CapturedObjectTypeName );
+ }
+
+ if (CapturedObjectName != NULL) {
+ SepFreeCapturedString( CapturedObjectName );
+ }
+
+ SeReleaseSecurityDescriptor (
+ CapturedSecurityDescriptor,
+ PreviousMode,
+ FALSE
+ );
+
+ return( GetExceptionCode() );
+
+ }
+
+ //
+ // See if anything (or everything) in the desired access can be
+ // satisfied by privileges.
+ //
+
+ Status = SePrivilegePolicyCheck(
+ &DesiredAccess,
+ &PreviouslyGrantedAccess,
+ &SubjectSecurityContext,
+ NULL,
+ &PrivilegeSet,
+ PreviousMode
+ );
+
+ SeLockSubjectContext( &SubjectSecurityContext );
+
+ if (NT_SUCCESS( Status )) {
+
+ //
+ // If the user in the token is the owner of the object, we
+ // must automatically grant ReadControl and WriteDac access
+ // if desired. If the DesiredAccess mask is empty after
+ // these bits are turned off, we don't have to do any more
+ // access checking (ref section 4, DSA ACL Arch)
+ //
+
+ if ( DesiredAccess & (WRITE_DAC | READ_CONTROL | MAXIMUM_ALLOWED) ) {
+
+ if (SepTokenIsOwner( SubjectSecurityContext.ClientToken, CapturedSecurityDescriptor, TRUE )) {
+
+ if ( DesiredAccess & MAXIMUM_ALLOWED ) {
+
+ PreviouslyGrantedAccess |= ( WRITE_DAC | READ_CONTROL );
+
+ } else {
+
+ PreviouslyGrantedAccess |= (DesiredAccess & (WRITE_DAC | READ_CONTROL));
+ }
+
+ DesiredAccess &= ~(WRITE_DAC | READ_CONTROL);
+ }
+
+ }
+
+ if (DesiredAccess == 0) {
+
+ LocalGrantedAccess = PreviouslyGrantedAccess;
+ AccessGranted = TRUE;
+ Status = STATUS_SUCCESS;
+
+ } else {
+
+ //
+ // Finally, do the access check
+ //
+
+ AccessGranted = SepAccessCheck ( CapturedSecurityDescriptor,
+ SubjectSecurityContext.PrimaryToken,
+ SubjectSecurityContext.ClientToken,
+ DesiredAccess,
+ &LocalGenericMapping,
+ PreviouslyGrantedAccess,
+ PreviousMode,
+ &LocalGrantedAccess,
+ NULL,
+ &Status
+ );
+
+ }
+ }
+
+ //
+ // sound the alarms...
+ //
+
+ if ( SepAdtAuditThisEvent( AuditCategoryObjectAccess, &AccessGranted )) {
+
+ SepExamineSacl(
+ SepSaclAddrSecurityDescriptor( (PISECURITY_DESCRIPTOR)CapturedSecurityDescriptor ),
+ EffectiveToken( &SubjectSecurityContext ),
+ (AccessGranted ? LocalGrantedAccess : (DesiredAccess | PreviouslyGrantedAccess)),
+ AccessGranted,
+ &GenerateAudit,
+ &GenerateAlarm
+ );
+
+ }
+
+ if (GenerateAudit || GenerateAlarm) {
+
+ //
+ // Save this to a local here, so we don't
+ // have to risk accessing user memory and
+ // potentially having to exit before the audit
+ //
+
+ if ( AccessGranted ) {
+
+ //
+ // SAM calls NtCloseObjectAuditAlarm despite the fact that it may not
+ // have successfully opened the object, causing a spurious close audit.
+ // Since no one should rely on this anyway if their access attempt
+ // failed, make sure it's false and SAM will work properly.
+ //
+
+ LocalGenerateOnClose = TRUE;
+ }
+
+ ExAllocateLocallyUniqueId( &OperationId );
+
+ AuditPerformed = SepAdtOpenObjectAuditAlarm (
+ CapturedSubsystemName,
+ AccessGranted ? &HandleId : NULL, // Don't audit handle if failure
+ CapturedObjectTypeName,
+ 0, // IN PVOID Object OPTIONAL,
+ CapturedObjectName,
+ SubjectSecurityContext.ClientToken,
+ SubjectSecurityContext.PrimaryToken,
+ DesiredAccess,
+ LocalGrantedAccess,
+ &OperationId,
+ PrivilegeSet,
+ ObjectCreation,
+ AccessGranted,
+ GenerateAudit,
+ GenerateAlarm,
+ PsProcessAuditId( PsGetCurrentProcess() )
+ );
+ } else {
+
+ //
+ // We didn't generate an audit due to the SACL. If privileges were used, we need
+ // to audit that.
+ //
+
+ if ( PrivilegeSet != NULL ) {
+
+ if ( SepAdtAuditThisEvent( AuditCategoryPrivilegeUse, &AccessGranted) ) {
+
+ AuditPerformed = SepAdtPrivilegeObjectAuditAlarm ( CapturedSubsystemName,
+ &HandleId,
+ SubjectSecurityContext.ClientToken,
+ SubjectSecurityContext.PrimaryToken,
+ PsProcessAuditId( PsGetCurrentProcess() ),
+ DesiredAccess,
+ PrivilegeSet,
+ AccessGranted
+ );
+
+ //
+ // We don't want close audits to be generated. May need to revisit this.
+ //
+
+ LocalGenerateOnClose = FALSE;
+ }
+ }
+ }
+
+ SeUnlockSubjectContext( &SubjectSecurityContext );
+
+ //
+ // Free any privileges allocated as part of the access check
+ //
+
+ if (PrivilegeSet != NULL) {
+
+ ExFreePool( PrivilegeSet );
+ }
+
+ SeReleaseSubjectContext ( &SubjectSecurityContext );
+
+ SeReleaseSecurityDescriptor ( CapturedSecurityDescriptor,
+ PreviousMode,
+ FALSE );
+
+ if (CapturedSubsystemName != NULL) {
+ SepFreeCapturedString( CapturedSubsystemName );
+ }
+
+ if (CapturedObjectTypeName != NULL) {
+ SepFreeCapturedString( CapturedObjectTypeName );
+ }
+
+ if (CapturedObjectName != NULL) {
+ SepFreeCapturedString( CapturedObjectName );
+ }
+
+ try {
+ *AccessStatus = Status;
+ *GrantedAccess = LocalGrantedAccess;
+ *GenerateOnClose = LocalGenerateOnClose;
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ return( GetExceptionCode() );
+ }
+
+ return(STATUS_SUCCESS);
+}
+
+
+NTSTATUS
+NtOpenObjectAuditAlarm (
+ IN PUNICODE_STRING SubsystemName,
+ IN PVOID HandleId OPTIONAL,
+ IN PUNICODE_STRING ObjectTypeName,
+ IN PUNICODE_STRING ObjectName,
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor OPTIONAL,
+ IN HANDLE ClientToken,
+ IN ACCESS_MASK DesiredAccess,
+ IN ACCESS_MASK GrantedAccess,
+ IN PPRIVILEGE_SET Privileges OPTIONAL,
+ IN BOOLEAN ObjectCreation,
+ IN BOOLEAN AccessGranted,
+ OUT PBOOLEAN GenerateOnClose
+ )
+/*++
+
+ Routine Description:
+
+ This routine is used to generate audit and alarm messages when an
+ attempt is made to access an existing protected subsystem object or
+ create a new one. This routine may result in several messages being
+ generated and sent to Port objects. This may result in a significant
+ latency before returning. Design of routines that must call this
+ routine must take this potential latency into account. This may have
+ an impact on the approach taken for data structure mutex locking, for
+ example.
+
+ This routine may not be able to generate a complete audit record
+ due to memory restrictions.
+
+ This API requires the caller have SeAuditPrivilege privilege. The test
+ for this privilege is always against the primary token of the calling
+ process, not the impersonation token of the thread.
+
+Arguments:
+
+ SubsystemName - Supplies a name string identifying the
+ subsystem calling the routine.
+
+ HandleId - A unique value representing the client's handle to the
+ object. If the access attempt was not successful (AccessGranted is
+ FALSE), then this parameter is ignored.
+
+ ObjectTypeName - Supplies the name of the type of object being
+ accessed.
+
+ ObjectName - Supplies the name of the object the client
+ accessed or attempted to access.
+
+ SecurityDescriptor - An optional pointer to the security descriptor of
+ the object being accessed.
+
+ ClientToken - A handle to a token object representing the client that
+ requested the operation. This handle must be obtained from a
+ communication session layer, such as from an LPC Port or Local
+ Named Pipe, to prevent possible security policy violations.
+
+ DesiredAccess - The desired access mask. This mask must have been
+ previously mapped to contain no generic accesses.
+
+ GrantedAccess - The mask of accesses that were actually granted.
+
+ Privileges - Optionally points to a set of privileges that were
+ required for the access attempt. Those privileges that were held
+ by the subject are marked using the UsedForAccess flag of the
+ attributes associated with each privilege.
+
+ ObjectCreation - A boolean flag indicating whether the access will
+ result in a new object being created if granted. A value of TRUE
+ indicates an object will be created, FALSE indicates an existing
+ object will be opened.
+
+ AccessGranted - Indicates whether the requested access was granted or
+ not. A value of TRUE indicates the access was granted. A value of
+ FALSE indicates the access was not granted.
+
+ GenerateOnClose - Points to a boolean that is set by the audit
+ generation routine and must be passed to NtCloseObjectAuditAlarm()
+ when the object handle is closed.
+
+Return Value:
+
+--*/
+{
+
+ KPROCESSOR_MODE PreviousMode;
+ ULONG PrivilegeParameterLength;
+ PUNICODE_STRING CapturedSubsystemName = (PUNICODE_STRING) NULL;
+ PUNICODE_STRING CapturedObjectTypeName = (PUNICODE_STRING) NULL;
+ PUNICODE_STRING CapturedObjectName = (PUNICODE_STRING) NULL;
+ PSECURITY_DESCRIPTOR CapturedSecurityDescriptor = (PSECURITY_DESCRIPTOR) NULL;
+ PPRIVILEGE_SET CapturedPrivileges = NULL;
+ BOOLEAN LocalGenerateOnClose = FALSE;
+ SECURITY_SUBJECT_CONTEXT SubjectSecurityContext;
+ BOOLEAN Result;
+ NTSTATUS Status;
+ BOOLEAN GenerateAudit = FALSE;
+ BOOLEAN GenerateAlarm = FALSE;
+ PLUID ClientAuthenticationId = NULL;
+ HANDLE CapturedHandleId = NULL;
+ BOOLEAN AuditPerformed;
+
+ PTOKEN Token;
+
+ PAGED_CODE();
+
+ PreviousMode = KeGetPreviousMode();
+
+ ASSERT( PreviousMode != KernelMode );
+
+ Status = ObReferenceObjectByHandle( ClientToken, // Handle
+ TOKEN_QUERY, // DesiredAccess
+ SepTokenObjectType, // ObjectType
+ PreviousMode, // AccessMode
+ (PVOID *)&Token, // Object
+ NULL // GrantedAccess
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ return( Status );
+ }
+
+ //
+ // If the passed token is an impersonation token, make sure
+ // it is at SecurityIdentification or above.
+ //
+
+ if (Token->TokenType == TokenImpersonation) {
+
+ if (Token->ImpersonationLevel < SecurityIdentification) {
+
+ ObDereferenceObject( (PVOID)Token );
+
+ return( STATUS_BAD_IMPERSONATION_LEVEL );
+
+ }
+ }
+
+ //
+ // Check for SeAuditPrivilege. This must be tested against
+ // the caller's primary token.
+ //
+
+ SeCaptureSubjectContext ( &SubjectSecurityContext );
+
+ Result = SeCheckAuditPrivilege (
+ &SubjectSecurityContext,
+ PreviousMode
+ );
+
+ if (!Result) {
+
+ ObDereferenceObject( (PVOID)Token );
+
+ SeReleaseSubjectContext ( &SubjectSecurityContext );
+
+ return(STATUS_PRIVILEGE_NOT_HELD);
+ }
+
+ //
+ // This will just return NULL if the input descriptor is NULL
+ //
+
+ Status =
+ SeCaptureSecurityDescriptor ( SecurityDescriptor,
+ PreviousMode,
+ PagedPool,
+ FALSE,
+ &CapturedSecurityDescriptor
+ );
+
+ //
+ // At this point in time, if there's no security descriptor, there's
+ // nothing to do. Return success.
+ //
+
+ if (!NT_SUCCESS( Status ) || CapturedSecurityDescriptor == NULL) {
+
+ ObDereferenceObject( (PVOID)Token );
+
+ SeReleaseSubjectContext ( &SubjectSecurityContext );
+
+ return( Status );
+ }
+
+ try {
+
+ if (ARGUMENT_PRESENT(Privileges)) {
+
+ ProbeForRead(
+ Privileges,
+ sizeof(PRIVILEGE_SET),
+ sizeof(ULONG)
+ );
+
+ PrivilegeParameterLength = (ULONG)sizeof(PRIVILEGE_SET) +
+ ((Privileges->PrivilegeCount - ANYSIZE_ARRAY) *
+ (ULONG)sizeof(LUID_AND_ATTRIBUTES) );
+
+ ProbeForRead(
+ Privileges,
+ PrivilegeParameterLength,
+ sizeof(ULONG)
+ );
+
+ CapturedPrivileges = ExAllocatePoolWithTag( PagedPool,
+ PrivilegeParameterLength,
+ 'rPeS'
+ );
+
+ if (CapturedPrivileges != NULL) {
+
+ RtlMoveMemory ( CapturedPrivileges,
+ Privileges,
+ PrivilegeParameterLength );
+ } else {
+
+ SeReleaseSecurityDescriptor ( CapturedSecurityDescriptor,
+ PreviousMode,
+ FALSE );
+
+ ObDereferenceObject( (PVOID)Token );
+ SeReleaseSubjectContext ( &SubjectSecurityContext );
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+
+ }
+
+ if (ARGUMENT_PRESENT( HandleId )) {
+
+ ProbeForRead( (PHANDLE)HandleId, sizeof(PVOID), sizeof(PVOID) );
+ CapturedHandleId = *(PHANDLE)HandleId;
+ }
+
+ ProbeForWriteBoolean(GenerateOnClose);
+
+ //
+ // Probe and Capture the parameter strings.
+ // If we run out of memory attempting to capture
+ // the strings, the returned pointer will be
+ // NULL and we will continue with the audit.
+ //
+
+ SepProbeAndCaptureString_U ( SubsystemName,
+ &CapturedSubsystemName );
+
+ SepProbeAndCaptureString_U ( ObjectTypeName,
+ &CapturedObjectTypeName );
+
+ SepProbeAndCaptureString_U ( ObjectName,
+ &CapturedObjectName );
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ if (CapturedSubsystemName != NULL) {
+ SepFreeCapturedString( CapturedSubsystemName );
+ }
+
+ if (CapturedObjectTypeName != NULL) {
+ SepFreeCapturedString( CapturedObjectTypeName );
+ }
+
+ if (CapturedObjectName != NULL) {
+ SepFreeCapturedString( CapturedObjectName );
+ }
+
+ if (CapturedPrivileges != NULL) {
+ ExFreePool( CapturedPrivileges );
+ }
+
+ if (CapturedSecurityDescriptor != NULL) {
+
+ SeReleaseSecurityDescriptor ( CapturedSecurityDescriptor,
+ PreviousMode,
+ FALSE );
+ }
+
+ ObDereferenceObject( (PVOID)Token );
+
+ SeReleaseSubjectContext ( &SubjectSecurityContext );
+
+ return GetExceptionCode();
+
+ }
+
+ if ( SepAdtAuditThisEvent( AuditCategoryObjectAccess, &AccessGranted) ) {
+
+ SepExamineSacl(
+ SepSaclAddrSecurityDescriptor( (PISECURITY_DESCRIPTOR)CapturedSecurityDescriptor ),
+ Token,
+ DesiredAccess | GrantedAccess,
+ AccessGranted,
+ &GenerateAudit,
+ &GenerateAlarm
+ );
+
+ if (GenerateAudit || GenerateAlarm) {
+
+ //
+ // Take a read lock on the token, because we're going to extract
+ // the user's Sid from it.
+ //
+
+ LocalGenerateOnClose = TRUE;
+
+ AuditPerformed = SepAdtOpenObjectAuditAlarm ( CapturedSubsystemName,
+ ARGUMENT_PRESENT(HandleId) ? (PVOID)&CapturedHandleId : NULL,
+ CapturedObjectTypeName,
+ NULL,
+ CapturedObjectName,
+ Token,
+ SubjectSecurityContext.PrimaryToken,
+ DesiredAccess,
+ GrantedAccess,
+ NULL,
+ CapturedPrivileges,
+ ObjectCreation,
+ AccessGranted,
+ GenerateAudit,
+ GenerateAlarm,
+ PsProcessAuditId( PsGetCurrentProcess() )
+ );
+
+ LocalGenerateOnClose = AuditPerformed;
+ }
+ }
+
+ if ( !(GenerateAudit || GenerateAlarm) ) {
+
+ //
+ // We didn't attempt to generate an audit above, so if privileges were used,
+ // see if we should generate an audit here.
+ //
+
+ if ( ARGUMENT_PRESENT(Privileges) ) {
+
+ if ( SepAdtAuditThisEvent( AuditCategoryPrivilegeUse, &AccessGranted) ) {
+
+ AuditPerformed = SepAdtPrivilegeObjectAuditAlarm ( CapturedSubsystemName,
+ CapturedHandleId,
+ Token,
+ SubjectSecurityContext.PrimaryToken,
+ PsProcessAuditId( PsGetCurrentProcess() ),
+ DesiredAccess,
+ CapturedPrivileges,
+ AccessGranted
+ );
+ //
+ // If we generate an audit due to use of privilege, don't set generate on close,
+ // because then we'll have a close audit without a corresponding open audit.
+ //
+
+ LocalGenerateOnClose = FALSE;
+ }
+ }
+ }
+
+ if (CapturedSecurityDescriptor != NULL) {
+
+ SeReleaseSecurityDescriptor ( CapturedSecurityDescriptor,
+ PreviousMode,
+ FALSE );
+ }
+
+ if (CapturedSubsystemName != NULL) {
+ SepFreeCapturedString( CapturedSubsystemName );
+ }
+
+ if (CapturedObjectTypeName != NULL) {
+ SepFreeCapturedString( CapturedObjectTypeName );
+ }
+
+ if (CapturedObjectName != NULL) {
+ SepFreeCapturedString( CapturedObjectName );
+ }
+
+ if (CapturedPrivileges != NULL) {
+ ExFreePool( CapturedPrivileges );
+ }
+
+ ObDereferenceObject( (PVOID)Token );
+
+ SeReleaseSubjectContext ( &SubjectSecurityContext );
+
+ try {
+
+ *GenerateOnClose = LocalGenerateOnClose;
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ return GetExceptionCode();
+ }
+
+ return(STATUS_SUCCESS);
+}
+
+
+
+NTSTATUS
+NtCloseObjectAuditAlarm (
+ IN PUNICODE_STRING SubsystemName,
+ IN PVOID HandleId,
+ IN BOOLEAN GenerateOnClose
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to generate audit and alarm messages when a handle
+ to a protected subsystem object is deleted. This routine may result in
+ several messages being generated and sent to Port objects. This may
+ result in a significant latency before returning. Design of routines
+ that must call this routine must take this potential latency into
+ account. This may have an impact on the approach taken for data
+ structure mutex locking, for example.
+
+ This API requires the caller have SeAuditPrivilege privilege. The test
+ for this privilege is always against the primary token of the calling
+ process, allowing the caller to be impersonating a client during the
+ call with no ill effects.
+
+Arguments:
+
+ SubsystemName - Supplies a name string identifying the subsystem
+ calling the routine.
+
+ HandleId - A unique value representing the client's handle to the
+ object.
+
+ GenerateOnClose - Is a boolean value returned from a corresponding
+ NtAccessCheckAndAuditAlarm() call or NtOpenObjectAuditAlarm() call
+ when the object handle was created.
+
+Return value:
+
+--*/
+
+{
+ BOOLEAN Result;
+ SECURITY_SUBJECT_CONTEXT SubjectSecurityContext;
+ KPROCESSOR_MODE PreviousMode;
+ PUNICODE_STRING CapturedSubsystemName = NULL;
+ PSID UserSid;
+ PSID CapturedUserSid;
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ PreviousMode = KeGetPreviousMode();
+
+ ASSERT(PreviousMode != KernelMode);
+
+ if (!GenerateOnClose) {
+ return( STATUS_SUCCESS );
+ }
+
+ //
+ // Check for SeAuditPrivilege
+ //
+
+ SeCaptureSubjectContext ( &SubjectSecurityContext );
+
+ Result = SeCheckAuditPrivilege (
+ &SubjectSecurityContext,
+ PreviousMode
+ );
+
+ if (!Result) {
+
+ SeReleaseSubjectContext ( &SubjectSecurityContext );
+ return(STATUS_PRIVILEGE_NOT_HELD);
+ }
+
+ UserSid = SepTokenUserSid( EffectiveToken (&SubjectSecurityContext));
+
+ CapturedUserSid = ExAllocatePoolWithTag(
+ PagedPool,
+ SeLengthSid( UserSid ),
+ 'iSeS'
+ );
+
+ if ( CapturedUserSid == NULL ) {
+ SeReleaseSubjectContext ( &SubjectSecurityContext );
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ Status = RtlCopySid (
+ SeLengthSid( UserSid ),
+ CapturedUserSid,
+ UserSid
+ );
+
+ ASSERT( NT_SUCCESS( Status ));
+
+ SeReleaseSubjectContext ( &SubjectSecurityContext );
+
+ try {
+
+ SepProbeAndCaptureString_U ( SubsystemName,
+ &CapturedSubsystemName );
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ if ( CapturedSubsystemName != NULL ) {
+ SepFreeCapturedString( CapturedSubsystemName );
+ }
+
+ ExFreePool( CapturedUserSid );
+ return GetExceptionCode();
+
+ }
+
+ //
+ // This routine will check to see if auditing is enabled
+ //
+
+ SepAdtCloseObjectAuditAlarm ( CapturedSubsystemName,
+ HandleId,
+ NULL,
+ CapturedUserSid,
+ SepTokenAuthenticationId( EffectiveToken( &SubjectSecurityContext ))
+ );
+
+ SepFreeCapturedString( CapturedSubsystemName );
+
+ ExFreePool( CapturedUserSid );
+
+ return(STATUS_SUCCESS);
+}
+
+
+NTSTATUS
+NtDeleteObjectAuditAlarm (
+ IN PUNICODE_STRING SubsystemName,
+ IN PVOID HandleId,
+ IN BOOLEAN GenerateOnClose
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to generate audit and alarm messages when an object
+ in a protected subsystem object is deleted. This routine may result in
+ several messages being generated and sent to Port objects. This may
+ result in a significant latency before returning. Design of routines
+ that must call this routine must take this potential latency into
+ account. This may have an impact on the approach taken for data
+ structure mutex locking, for example.
+
+ This API requires the caller have SeAuditPrivilege privilege. The test
+ for this privilege is always against the primary token of the calling
+ process, allowing the caller to be impersonating a client during the
+ call with no ill effects.
+
+Arguments:
+
+ SubsystemName - Supplies a name string identifying the subsystem
+ calling the routine.
+
+ HandleId - A unique value representing the client's handle to the
+ object.
+
+ GenerateOnClose - Is a boolean value returned from a corresponding
+ NtAccessCheckAndAuditAlarm() call or NtOpenObjectAuditAlarm() call
+ when the object handle was created.
+
+Return value:
+
+--*/
+
+{
+ BOOLEAN Result;
+ SECURITY_SUBJECT_CONTEXT SubjectSecurityContext;
+ KPROCESSOR_MODE PreviousMode;
+ PUNICODE_STRING CapturedSubsystemName = NULL;
+ PSID UserSid;
+ PSID CapturedUserSid;
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ PreviousMode = KeGetPreviousMode();
+
+ ASSERT(PreviousMode != KernelMode);
+
+ if (!GenerateOnClose) {
+ return( STATUS_SUCCESS );
+ }
+
+ //
+ // Check for SeAuditPrivilege
+ //
+
+ SeCaptureSubjectContext ( &SubjectSecurityContext );
+
+ Result = SeCheckAuditPrivilege (
+ &SubjectSecurityContext,
+ PreviousMode
+ );
+
+ if (!Result) {
+
+ SeReleaseSubjectContext ( &SubjectSecurityContext );
+ return(STATUS_PRIVILEGE_NOT_HELD);
+ }
+
+ UserSid = SepTokenUserSid( EffectiveToken (&SubjectSecurityContext));
+
+ CapturedUserSid = ExAllocatePoolWithTag(
+ PagedPool,
+ SeLengthSid( UserSid ),
+ 'iSeS'
+ );
+
+ if ( CapturedUserSid == NULL ) {
+ SeReleaseSubjectContext ( &SubjectSecurityContext );
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ Status = RtlCopySid (
+ SeLengthSid( UserSid ),
+ CapturedUserSid,
+ UserSid
+ );
+
+ ASSERT( NT_SUCCESS( Status ));
+
+ SeReleaseSubjectContext ( &SubjectSecurityContext );
+
+ try {
+
+ SepProbeAndCaptureString_U ( SubsystemName,
+ &CapturedSubsystemName );
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ if ( CapturedSubsystemName != NULL ) {
+ SepFreeCapturedString( CapturedSubsystemName );
+ }
+
+ ExFreePool( CapturedUserSid );
+ return GetExceptionCode();
+
+ }
+
+ //
+ // This routine will check to see if auditing is enabled
+ //
+
+ SepAdtDeleteObjectAuditAlarm ( CapturedSubsystemName,
+ HandleId,
+ NULL,
+ CapturedUserSid,
+ SepTokenAuthenticationId( EffectiveToken( &SubjectSecurityContext ))
+ );
+
+ SepFreeCapturedString( CapturedSubsystemName );
+
+ ExFreePool( CapturedUserSid );
+
+ return(STATUS_SUCCESS);
+}
+
+
+VOID
+SeOpenObjectAuditAlarm (
+ IN PUNICODE_STRING ObjectTypeName,
+ IN PVOID Object OPTIONAL,
+ IN PUNICODE_STRING AbsoluteObjectName OPTIONAL,
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN PACCESS_STATE AccessState,
+ IN BOOLEAN ObjectCreated,
+ IN BOOLEAN AccessGranted,
+ IN KPROCESSOR_MODE AccessMode,
+ OUT PBOOLEAN GenerateOnClose
+ )
+/*++
+
+Routine Description:
+
+ SeOpenObjectAuditAlarm is used by the object manager that open objects
+ to generate any necessary audit or alarm messages. The open may be to
+ existing objects or for newly created objects. No messages will be
+ generated for Kernel mode accesses.
+
+ This routine is used to generate audit and alarm messages when an
+ attempt is made to open an object.
+
+ This routine may result in several messages being generated and sent to
+ Port objects. This may result in a significant latency before
+ returning. Design of routines that must call this routine must take
+ this potential latency into account. This may have an impact on the
+ approach taken for data structure mutex locking, for example.
+
+Arguments:
+
+ ObjectTypeName - Supplies the name of the type of object being
+ accessed. This must be the same name provided to the
+ ObCreateObjectType service when the object type was created.
+
+ Object - Address of the object accessed. This value will not be used
+ as a pointer (referenced). It is necessary only to enter into log
+ messages. If the open was not successful, then this argument is
+ ignored. Otherwise, it must be provided.
+
+ AbsoluteObjectName - Supplies the name of the object being accessed.
+ If the object doesn't have a name, then this field is left null.
+ Otherwise, it must be provided.
+
+ SecurityDescriptor - A pointer to the security descriptor of the
+ object being accessed.
+
+ AccessState - A pointer to an access state structure containing the
+ subject context, the remaining desired access types, the granted
+ access types, and optionally a privilege set to indicate which
+ privileges were used to permit the access.
+
+ ObjectCreated - A boolean flag indicating whether the access resulted
+ in a new object being created. A value of TRUE indicates an object
+ was created, FALSE indicates an existing object was opened.
+
+ AccessGranted - Indicates if the access was granted or denied based on
+ the access check or privilege check.
+
+ AccessMode - Indicates the access mode used for the access check. One
+ of UserMode or KernelMode. Messages will not be generated by
+ kernel mode accesses.
+
+ GenerateOnClose - Points to a boolean that is set by the audit
+ generation routine and must be passed to SeCloseObjectAuditAlarm()
+ when the object handle is closed.
+
+Return value:
+
+ None.
+
+--*/
+{
+ BOOLEAN GenerateAudit = FALSE;
+ BOOLEAN GenerateAlarm = FALSE;
+ ACCESS_MASK RequestedAccess;
+ POBJECT_NAME_INFORMATION ObjectNameInfo = NULL;
+ PUNICODE_STRING ObjectTypeNameInfo = NULL;
+ PUNICODE_STRING ObjectName = NULL;
+ PUNICODE_STRING LocalObjectTypeName = NULL;
+ PLUID PrimaryAuthenticationId = NULL;
+ PLUID ClientAuthenticationId = NULL;
+ BOOLEAN AuditPrivileges = FALSE;
+ BOOLEAN AuditPerformed;
+ PTOKEN Token;
+ ACCESS_MASK MappedGrantMask = (ACCESS_MASK)0;
+ ACCESS_MASK MappedDenyMask = (ACCESS_MASK)0;
+ PAUX_ACCESS_DATA AuxData;
+
+ PAGED_CODE();
+
+ if ( AccessMode == KernelMode ) {
+ return;
+ }
+
+ AuxData = (PAUX_ACCESS_DATA)AccessState->AuxData;
+
+ Token = EffectiveToken( &AccessState->SubjectSecurityContext );
+
+ if (ARGUMENT_PRESENT(Token->AuditData)) {
+
+ MappedGrantMask = Token->AuditData->GrantMask;
+
+ RtlMapGenericMask(
+ &MappedGrantMask,
+ &AuxData->GenericMapping
+ );
+
+ MappedDenyMask = Token->AuditData->DenyMask;
+
+ RtlMapGenericMask(
+ &MappedDenyMask,
+ &AuxData->GenericMapping
+ );
+ }
+
+ if (SecurityDescriptor != NULL) {
+
+ RequestedAccess = AccessState->RemainingDesiredAccess |
+ AccessState->PreviouslyGrantedAccess;
+
+ if ( SepAdtAuditThisEvent( AuditCategoryObjectAccess, &AccessGranted )) {
+
+ if ( RequestedAccess & (AccessGranted ? MappedGrantMask : MappedDenyMask)) {
+
+ GenerateAudit = TRUE;
+
+ } else {
+
+ SepExamineSacl(
+ SepSaclAddrSecurityDescriptor( (PISECURITY_DESCRIPTOR)SecurityDescriptor ),
+ Token,
+ RequestedAccess,
+ AccessGranted,
+ &GenerateAudit,
+ &GenerateAlarm
+ );
+ }
+
+ //
+ // Only generate an audit on close of we're auditing from SACL
+ // settings.
+ //
+
+ if (GenerateAudit) {
+ *GenerateOnClose = TRUE;
+ }
+ }
+ }
+
+ //
+ // If we don't generate an audit via the SACL, see if we need to generate
+ // one for privilege use.
+ //
+ // Note that we only audit privileges successfully used to open objects,
+ // so we don't care about a failed privilege use here. Therefore, only
+ // do this test of access has been granted.
+ //
+
+ if (!GenerateAudit && (AccessGranted == TRUE)) {
+
+ if ( SepAdtAuditThisEvent( AuditCategoryPrivilegeUse, &AccessGranted )) {
+
+ if ((AuxData->PrivilegesUsed != NULL) &&
+ (AuxData->PrivilegesUsed->PrivilegeCount > 0) ) {
+
+ //
+ // Make sure these are actually privileges that we want to audit
+ //
+
+ if (SepFilterPrivilegeAudits( AuxData->PrivilegesUsed )) {
+
+ GenerateAudit = TRUE;
+
+ //
+ // When we finally try to generate this audit, this flag
+ // will tell us that we need to audit the fact that we
+ // used a privilege, as opposed to audit due to the SACL.
+ //
+
+ AccessState->AuditPrivileges = TRUE;
+ }
+ }
+ }
+ }
+
+ //
+ // Set up either to generate an audit (if the access check has failed), or save
+ // the stuff that we're going to audit later into the AccessState structure.
+ //
+
+ if (GenerateAudit || GenerateAlarm) {
+
+ AccessState->GenerateAudit = TRUE;
+
+ //
+ // Figure out what we've been passed, and obtain as much
+ // missing information as possible.
+ //
+
+ if ( !ARGUMENT_PRESENT( AbsoluteObjectName )) {
+
+ if ( ARGUMENT_PRESENT( Object )) {
+
+ ObjectNameInfo = SepQueryNameString( Object );
+
+ if ( ObjectNameInfo != NULL ) {
+
+ ObjectName = &ObjectNameInfo->Name;
+ }
+ }
+
+ } else {
+
+ ObjectName = AbsoluteObjectName;
+ }
+
+ if ( !ARGUMENT_PRESENT( ObjectTypeName )) {
+
+ if ( ARGUMENT_PRESENT( Object )) {
+
+ ObjectTypeNameInfo = SepQueryTypeString( Object );
+
+ if ( ObjectTypeNameInfo != NULL ) {
+
+ LocalObjectTypeName = ObjectTypeNameInfo;
+ }
+ }
+
+ } else {
+
+ LocalObjectTypeName = ObjectTypeName;
+ }
+
+ //
+ // If the access attempt failed, do the audit here. If it succeeded,
+ // we'll do the audit later, when the handle is allocated.
+ //
+ //
+
+ if (!AccessGranted) {
+
+ AuditPerformed = SepAdtOpenObjectAuditAlarm ( &SeSubsystemName,
+ NULL,
+ LocalObjectTypeName,
+ NULL,
+ ObjectName,
+ AccessState->SubjectSecurityContext.ClientToken,
+ AccessState->SubjectSecurityContext.PrimaryToken,
+ AccessState->OriginalDesiredAccess,
+ AccessState->PreviouslyGrantedAccess,
+ &AccessState->OperationID,
+ AuxData->PrivilegesUsed,
+ FALSE,
+ FALSE,
+ TRUE,
+ FALSE,
+ AccessState->SubjectSecurityContext.ProcessAuditId );
+ } else {
+
+ //
+ // Copy all the stuff we're going to need into the
+ // AccessState and return.
+ //
+
+ if ( ObjectName != NULL ) {
+
+ AccessState->ObjectName.Buffer = ExAllocatePool( PagedPool,ObjectName->MaximumLength );
+ if (AccessState->ObjectName.Buffer != NULL) {
+
+ AccessState->ObjectName.MaximumLength = ObjectName->MaximumLength;
+ RtlCopyUnicodeString( &AccessState->ObjectName, ObjectName );
+ }
+ }
+
+ if ( LocalObjectTypeName != NULL ) {
+
+ AccessState->ObjectTypeName.Buffer = ExAllocatePool( PagedPool, LocalObjectTypeName->MaximumLength );
+ if (AccessState->ObjectTypeName.Buffer != NULL) {
+
+ AccessState->ObjectTypeName.MaximumLength = LocalObjectTypeName->MaximumLength;
+ RtlCopyUnicodeString( &AccessState->ObjectTypeName, LocalObjectTypeName );
+ }
+ }
+ }
+
+ if ( ObjectNameInfo != NULL ) {
+
+ ExFreePool( ObjectNameInfo );
+ }
+
+ if ( ObjectTypeNameInfo != NULL ) {
+
+ ExFreePool( ObjectTypeNameInfo );
+ }
+ }
+
+ return;
+}
+
+
+VOID
+SeOpenObjectForDeleteAuditAlarm (
+ IN PUNICODE_STRING ObjectTypeName,
+ IN PVOID Object OPTIONAL,
+ IN PUNICODE_STRING AbsoluteObjectName OPTIONAL,
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN PACCESS_STATE AccessState,
+ IN BOOLEAN ObjectCreated,
+ IN BOOLEAN AccessGranted,
+ IN KPROCESSOR_MODE AccessMode,
+ OUT PBOOLEAN GenerateOnClose
+ )
+/*++
+
+Routine Description:
+
+ SeOpenObjectForDeleteAuditAlarm is used by the object manager that open
+ objects to generate any necessary audit or alarm messages. The open may
+ be to existing objects or for newly created objects. No messages will be
+ generated for Kernel mode accesses.
+
+ This routine is used to generate audit and alarm messages when an
+ attempt is made to open an object with the intent to delete it.
+ Specifically, this is used by file systems when the flag
+ FILE_DELETE_ON_CLOSE is specified.
+
+ This routine may result in several messages being generated and sent to
+ Port objects. This may result in a significant latency before
+ returning. Design of routines that must call this routine must take
+ this potential latency into account. This may have an impact on the
+ approach taken for data structure mutex locking, for example.
+
+Arguments:
+
+ ObjectTypeName - Supplies the name of the type of object being
+ accessed. This must be the same name provided to the
+ ObCreateObjectType service when the object type was created.
+
+ Object - Address of the object accessed. This value will not be used
+ as a pointer (referenced). It is necessary only to enter into log
+ messages. If the open was not successful, then this argument is
+ ignored. Otherwise, it must be provided.
+
+ AbsoluteObjectName - Supplies the name of the object being accessed.
+ If the object doesn't have a name, then this field is left null.
+ Otherwise, it must be provided.
+
+ SecurityDescriptor - A pointer to the security descriptor of the
+ object being accessed.
+
+ AccessState - A pointer to an access state structure containing the
+ subject context, the remaining desired access types, the granted
+ access types, and optionally a privilege set to indicate which
+ privileges were used to permit the access.
+
+ ObjectCreated - A boolean flag indicating whether the access resulted
+ in a new object being created. A value of TRUE indicates an object
+ was created, FALSE indicates an existing object was opened.
+
+ AccessGranted - Indicates if the access was granted or denied based on
+ the access check or privilege check.
+
+ AccessMode - Indicates the access mode used for the access check. One
+ of UserMode or KernelMode. Messages will not be generated by
+ kernel mode accesses.
+
+ GenerateOnClose - Points to a boolean that is set by the audit
+ generation routine and must be passed to SeCloseObjectAuditAlarm()
+ when the object handle is closed.
+
+Return value:
+
+ None.
+
+--*/
+{
+ BOOLEAN GenerateAudit = FALSE;
+ BOOLEAN GenerateAlarm = FALSE;
+ ACCESS_MASK RequestedAccess;
+ POBJECT_NAME_INFORMATION ObjectNameInfo = NULL;
+ PUNICODE_STRING ObjectTypeNameInfo = NULL;
+ PUNICODE_STRING ObjectName = NULL;
+ PUNICODE_STRING LocalObjectTypeName = NULL;
+ PLUID PrimaryAuthenticationId = NULL;
+ PLUID ClientAuthenticationId = NULL;
+ BOOLEAN AuditPrivileges = FALSE;
+ BOOLEAN AuditPerformed;
+ PTOKEN Token;
+ ACCESS_MASK MappedGrantMask = (ACCESS_MASK)0;
+ ACCESS_MASK MappedDenyMask = (ACCESS_MASK)0;
+ PAUX_ACCESS_DATA AuxData;
+
+ PAGED_CODE();
+
+ if ( AccessMode == KernelMode ) {
+ return;
+ }
+
+ AuxData = (PAUX_ACCESS_DATA)AccessState->AuxData;
+
+ Token = EffectiveToken( &AccessState->SubjectSecurityContext );
+
+ if (ARGUMENT_PRESENT(Token->AuditData)) {
+
+ MappedGrantMask = Token->AuditData->GrantMask;
+
+ RtlMapGenericMask(
+ &MappedGrantMask,
+ &AuxData->GenericMapping
+ );
+
+ MappedDenyMask = Token->AuditData->DenyMask;
+
+ RtlMapGenericMask(
+ &MappedDenyMask,
+ &AuxData->GenericMapping
+ );
+ }
+
+ if (SecurityDescriptor != NULL) {
+
+ RequestedAccess = AccessState->RemainingDesiredAccess |
+ AccessState->PreviouslyGrantedAccess;
+
+ if ( SepAdtAuditThisEvent( AuditCategoryObjectAccess, &AccessGranted )) {
+
+ if ( RequestedAccess & (AccessGranted ? MappedGrantMask : MappedDenyMask)) {
+
+ GenerateAudit = TRUE;
+
+ } else {
+
+ SepExamineSacl(
+ SepSaclAddrSecurityDescriptor( (PISECURITY_DESCRIPTOR)SecurityDescriptor ),
+ Token,
+ RequestedAccess,
+ AccessGranted,
+ &GenerateAudit,
+ &GenerateAlarm
+ );
+ }
+
+ //
+ // Only generate an audit on close of we're auditing from SACL
+ // settings.
+ //
+
+ if (GenerateAudit) {
+ *GenerateOnClose = TRUE;
+ }
+ }
+ }
+
+ //
+ // If we don't generate an audit via the SACL, see if we need to generate
+ // one for privilege use.
+ //
+ // Note that we only audit privileges successfully used to open objects,
+ // so we don't care about a failed privilege use here. Therefore, only
+ // do this test of access has been granted.
+ //
+
+ if (!GenerateAudit && (AccessGranted == TRUE)) {
+
+ if ( SepAdtAuditThisEvent( AuditCategoryPrivilegeUse, &AccessGranted )) {
+
+ if ((AuxData->PrivilegesUsed != NULL) &&
+ (AuxData->PrivilegesUsed->PrivilegeCount > 0) ) {
+
+ //
+ // Make sure these are actually privileges that we want to audit
+ //
+
+ if (SepFilterPrivilegeAudits( AuxData->PrivilegesUsed )) {
+
+ GenerateAudit = TRUE;
+
+ //
+ // When we finally try to generate this audit, this flag
+ // will tell us that we need to audit the fact that we
+ // used a privilege, as opposed to audit due to the SACL.
+ //
+
+ AccessState->AuditPrivileges = TRUE;
+ }
+ }
+ }
+ }
+
+ //
+ // Set up either to generate an audit (if the access check has failed), or save
+ // the stuff that we're going to audit later into the AccessState structure.
+ //
+
+ if (GenerateAudit || GenerateAlarm) {
+
+ AccessState->GenerateAudit = TRUE;
+
+ //
+ // Figure out what we've been passed, and obtain as much
+ // missing information as possible.
+ //
+
+ if ( !ARGUMENT_PRESENT( AbsoluteObjectName )) {
+
+ if ( ARGUMENT_PRESENT( Object )) {
+
+ ObjectNameInfo = SepQueryNameString( Object );
+
+ if ( ObjectNameInfo != NULL ) {
+
+ ObjectName = &ObjectNameInfo->Name;
+ }
+ }
+
+ } else {
+
+ ObjectName = AbsoluteObjectName;
+ }
+
+ if ( !ARGUMENT_PRESENT( ObjectTypeName )) {
+
+ if ( ARGUMENT_PRESENT( Object )) {
+
+ ObjectTypeNameInfo = SepQueryTypeString( Object );
+
+ if ( ObjectTypeNameInfo != NULL ) {
+
+ LocalObjectTypeName = ObjectTypeNameInfo;
+ }
+ }
+
+ } else {
+
+ LocalObjectTypeName = ObjectTypeName;
+ }
+
+ //
+ // If the access attempt failed, do the audit here. If it succeeded,
+ // we'll do the audit later, when the handle is allocated.
+ //
+ //
+
+ if (!AccessGranted) {
+
+ AuditPerformed = SepAdtOpenObjectAuditAlarm ( &SeSubsystemName,
+ NULL,
+ LocalObjectTypeName,
+ NULL,
+ ObjectName,
+ AccessState->SubjectSecurityContext.ClientToken,
+ AccessState->SubjectSecurityContext.PrimaryToken,
+ AccessState->OriginalDesiredAccess,
+ AccessState->PreviouslyGrantedAccess,
+ &AccessState->OperationID,
+ AuxData->PrivilegesUsed,
+ FALSE,
+ FALSE,
+ TRUE,
+ FALSE,
+ AccessState->SubjectSecurityContext.ProcessAuditId );
+ } else {
+
+ //
+ // Generate the delete audit first
+ //
+
+ SepAdtOpenObjectForDeleteAuditAlarm ( &SeSubsystemName,
+ NULL,
+ LocalObjectTypeName,
+ NULL,
+ ObjectName,
+ AccessState->SubjectSecurityContext.ClientToken,
+ AccessState->SubjectSecurityContext.PrimaryToken,
+ AccessState->OriginalDesiredAccess,
+ AccessState->PreviouslyGrantedAccess,
+ &AccessState->OperationID,
+ AuxData->PrivilegesUsed,
+ FALSE,
+ TRUE,
+ TRUE,
+ FALSE,
+ AccessState->SubjectSecurityContext.ProcessAuditId );
+
+ //
+ // Copy all the stuff we're going to need into the
+ // AccessState and return.
+ //
+
+ if ( ObjectName != NULL ) {
+
+ AccessState->ObjectName.Buffer = ExAllocatePool( PagedPool,ObjectName->MaximumLength );
+ if (AccessState->ObjectName.Buffer != NULL) {
+
+ AccessState->ObjectName.MaximumLength = ObjectName->MaximumLength;
+ RtlCopyUnicodeString( &AccessState->ObjectName, ObjectName );
+ }
+ }
+
+ if ( LocalObjectTypeName != NULL ) {
+
+ AccessState->ObjectTypeName.Buffer = ExAllocatePool( PagedPool, LocalObjectTypeName->MaximumLength );
+ if (AccessState->ObjectTypeName.Buffer != NULL) {
+
+ AccessState->ObjectTypeName.MaximumLength = LocalObjectTypeName->MaximumLength;
+ RtlCopyUnicodeString( &AccessState->ObjectTypeName, LocalObjectTypeName );
+ }
+ }
+ }
+
+ if ( ObjectNameInfo != NULL ) {
+
+ ExFreePool( ObjectNameInfo );
+ }
+
+ if ( ObjectTypeNameInfo != NULL ) {
+
+ ExFreePool( ObjectTypeNameInfo );
+ }
+ }
+
+ return;
+}
+
+#if 0
+
+VOID
+SeOpenObjectForDeleteAuditAlarm (
+ IN PUNICODE_STRING ObjectTypeName,
+ IN PVOID Object OPTIONAL,
+ IN PUNICODE_STRING AbsoluteObjectName OPTIONAL,
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN PACCESS_STATE AccessState,
+ IN BOOLEAN ObjectCreated,
+ IN BOOLEAN AccessGranted,
+ IN KPROCESSOR_MODE AccessMode,
+ OUT PBOOLEAN GenerateOnClose
+ )
+/*++
+
+Routine Description:
+
+ SeOpenObjectAuditForDeleteAlarm is used by the file systems for files
+ that are opened with the FILE_DELETE_ON_CLOSE bit specified. Since a
+ handle may not be created, it is important to generate the deletiong
+ audit when the file is opened. No messages will be
+ generated for Kernel mode accesses.
+
+ This routine may result in several messages being generated and sent to
+ Port objects. This may result in a significant latency before
+ returning. Design of routines that must call this routine must take
+ this potential latency into account. This may have an impact on the
+ approach taken for data structure mutex locking, for example.
+
+Arguments:
+
+ ObjectTypeName - Supplies the name of the type of object being
+ accessed. This must be the same name provided to the
+ ObCreateObjectType service when the object type was created.
+
+ Object - Address of the object accessed. This value will not be used
+ as a pointer (referenced). It is necessary only to enter into log
+ messages. If the open was not successful, then this argument is
+ ignored. Otherwise, it must be provided.
+
+ AbsoluteObjectName - Supplies the name of the object being accessed.
+ If the object doesn't have a name, then this field is left null.
+ Otherwise, it must be provided.
+
+ SecurityDescriptor - A pointer to the security descriptor of the
+ object being accessed.
+
+ AccessState - A pointer to an access state structure containing the
+ subject context, the remaining desired access types, the granted
+ access types, and optionally a privilege set to indicate which
+ privileges were used to permit the access.
+
+ ObjectCreated - A boolean flag indicating whether the access resulted
+ in a new object being created. A value of TRUE indicates an object
+ was created, FALSE indicates an existing object was opened.
+
+ AccessGranted - Indicates if the access was granted or denied based on
+ the access check or privilege check.
+
+ AccessMode - Indicates the access mode used for the access check. One
+ of UserMode or KernelMode. Messages will not be generated by
+ kernel mode accesses.
+
+ GenerateOnClose - Points to a boolean that is set by the audit
+ generation routine and must be passed to SeCloseObjectAuditAlarm()
+ when the object handle is closed.
+
+Return value:
+
+ None.
+
+--*/
+{
+ BOOLEAN GenerateAudit = FALSE;
+ BOOLEAN GenerateAlarm = FALSE;
+ ACCESS_MASK RequestedAccess;
+ POBJECT_NAME_INFORMATION ObjectNameInfo = NULL;
+ PUNICODE_STRING ObjectTypeNameInfo = NULL;
+ PUNICODE_STRING ObjectName = NULL;
+ PUNICODE_STRING LocalObjectTypeName = NULL;
+ PLUID PrimaryAuthenticationId = NULL;
+ PLUID ClientAuthenticationId = NULL;
+ BOOLEAN AuditPrivileges = FALSE;
+ PTOKEN Token;
+ ACCESS_MASK MappedGrantMask = (ACCESS_MASK)0;
+ ACCESS_MASK MappedDenyMask = (ACCESS_MASK)0;
+ PAUX_ACCESS_DATA AuxData;
+
+ PAGED_CODE();
+
+ if ( AccessMode == KernelMode ) {
+ return;
+ }
+
+ AuxData = (PAUX_ACCESS_DATA)AccessState->AuxData;
+
+ Token = EffectiveToken( &AccessState->SubjectSecurityContext );
+
+ if (ARGUMENT_PRESENT(Token->AuditData)) {
+
+ MappedGrantMask = Token->AuditData->GrantMask;
+
+ RtlMapGenericMask(
+ &MappedGrantMask,
+ &AuxData->GenericMapping
+ );
+
+ MappedDenyMask = Token->AuditData->DenyMask;
+
+ RtlMapGenericMask(
+ &MappedDenyMask,
+ &AuxData->GenericMapping
+ );
+ }
+
+ if (SecurityDescriptor != NULL) {
+
+ RequestedAccess = AccessState->RemainingDesiredAccess |
+ AccessState->PreviouslyGrantedAccess;
+
+ if ( SepAdtAuditThisEvent( AuditCategoryObjectAccess, &AccessGranted )) {
+
+ if ( RequestedAccess & (AccessGranted ? MappedGrantMask : MappedDenyMask)) {
+
+ GenerateAudit = TRUE;
+
+ } else {
+
+ SepExamineSacl(
+ SepSaclAddrSecurityDescriptor( (PISECURITY_DESCRIPTOR)SecurityDescriptor ),
+ Token,
+ RequestedAccess,
+ AccessGranted,
+ &GenerateAudit,
+ &GenerateAlarm
+ );
+ }
+
+ //
+ // Only generate an audit on close of we're auditing from SACL
+ // settings.
+ //
+
+ if (GenerateAudit) {
+ *GenerateOnClose = TRUE;
+ }
+ }
+ }
+
+ //
+ // If we don't generate an audit via the SACL, see if we need to generate
+ // one for privilege use.
+ //
+ // Note that we only audit privileges successfully used to open objects,
+ // so we don't care about a failed privilege use here. Therefore, only
+ // do this test of access has been granted.
+ //
+
+ if (!GenerateAudit && (AccessGranted == TRUE)) {
+
+ if ( SepAdtAuditThisEvent( AuditCategoryPrivilegeUse, &AccessGranted )) {
+
+ if ((AuxData->PrivilegesUsed != NULL) &&
+ (AuxData->PrivilegesUsed->PrivilegeCount > 0) ) {
+
+ //
+ // Make sure these are actually privileges that we want to audit
+ //
+
+ if (SepFilterPrivilegeAudits( AuxData->PrivilegesUsed )) {
+
+ GenerateAudit = TRUE;
+
+ //
+ // When we finally try to generate this audit, this flag
+ // will tell us that we need to audit the fact that we
+ // used a privilege, as opposed to audit due to the SACL.
+ //
+
+ AccessState->AuditPrivileges = TRUE;
+ }
+ }
+ }
+ }
+
+ //
+ // Set up either to generate an audit (if the access check has failed), or save
+ // the stuff that we're going to audit later into the AccessState structure.
+ //
+
+ if (GenerateAudit || GenerateAlarm) {
+
+ AccessState->GenerateAudit = TRUE;
+
+ //
+ // Figure out what we've been passed, and obtain as much
+ // missing information as possible.
+ //
+
+ if ( !ARGUMENT_PRESENT( AbsoluteObjectName )) {
+
+ if ( ARGUMENT_PRESENT( Object )) {
+
+ ObjectNameInfo = SepQueryNameString( Object );
+
+ if ( ObjectNameInfo != NULL ) {
+
+ ObjectName = &ObjectNameInfo->Name;
+ }
+ }
+
+ } else {
+
+ ObjectName = AbsoluteObjectName;
+ }
+
+ if ( !ARGUMENT_PRESENT( ObjectTypeName )) {
+
+ if ( ARGUMENT_PRESENT( Object )) {
+
+ ObjectTypeNameInfo = SepQueryTypeString( Object );
+
+ if ( ObjectTypeNameInfo != NULL ) {
+
+ LocalObjectTypeName = ObjectTypeNameInfo;
+ }
+ }
+
+ } else {
+
+ LocalObjectTypeName = ObjectTypeName;
+ }
+
+ //
+ // Do the audit here.
+ //
+
+ //
+ // BUGBUG: call SepAdtOpenObjectForDeleteAuditAlarm here
+ // instead.
+ //
+
+ SepAdtOpenObjectAuditAlarm ( &SeSubsystemName,
+ NULL,
+ LocalObjectTypeName,
+ NULL,
+ ObjectName,
+ AccessState->SubjectSecurityContext.ClientToken,
+ AccessState->SubjectSecurityContext.PrimaryToken,
+ AccessState->OriginalDesiredAccess,
+ AccessState->PreviouslyGrantedAccess,
+ &AccessState->OperationID,
+ AuxData->PrivilegesUsed,
+ FALSE,
+ FALSE,
+ TRUE,
+ FALSE,
+ AccessState->SubjectSecurityContext.ProcessAuditId );
+
+ if ( ObjectNameInfo != NULL ) {
+
+ ExFreePool( ObjectNameInfo );
+ }
+
+ if ( ObjectTypeNameInfo != NULL ) {
+
+ ExFreePool( ObjectTypeNameInfo );
+ }
+ }
+
+ return;
+}
+#endif
+
+
+VOID
+SeTraverseAuditAlarm(
+ IN PLUID OperationID,
+ IN PVOID DirectoryObject,
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
+ IN BOOLEAN SubjectContextLocked,
+ IN ACCESS_MASK TraverseAccess,
+ IN PPRIVILEGE_SET Privileges OPTIONAL,
+ IN BOOLEAN AccessGranted,
+ IN KPROCESSOR_MODE AccessMode
+ )
+/*++
+
+Routine Description:
+
+ This routine is called to audit directory traverse operations
+ specifically. It should be called by parse procedures as they traverse
+ directories as part of their operation.
+
+Arguments:
+
+ OperationID - LUID identifying the operation in progress
+
+ DirectoryObject - Pointer to the directory being traversed.
+
+ SecurityDescriptor - The security descriptor (if any) attached to the
+ directory being traversed.
+
+ SubjectSecurityContext - Security context of the client.
+
+ SubjectContextLocked - Supplies whether the SubjectContext is locked
+ for shared access.
+
+ TraverseAccess - Mask to indicate the traverse access for this object
+ type.
+
+ Privileges - Optional parameter to indicate any privilges that the
+ subject may have used to gain access to the object.
+
+ AccessGranted - Indicates if the access was granted or denied based on
+ the access check or privilege check.
+
+ AccessMode - Indicates the access mode used for the access check. One
+ of UserMode or KernelMode. Messages will not be generated by
+ kernel mode accesses.
+
+Return value:
+
+ None.
+
+--*/
+
+{
+ PAGED_CODE();
+
+#if 0
+ BOOLEAN GenerateAudit = FALSE;
+ BOOLEAN GenerateAlarm = FALSE;
+
+ if (AccessMode == KernelMode) {
+ return;
+ }
+
+ if ((SeAuditingState[AuditEventTraverse].AuditOnSuccess && AccessGranted) ||
+ SeAuditingState[AuditEventTraverse].AuditOnFailure && !AccessGranted) {
+
+ if ( SecurityDescriptor != NULL ) {
+
+ if ( !SubjectContextLocked ) {
+ SeLockSubjectContext( SubjectSecurityContext );
+ }
+
+ SepExamineSacl(
+ SepSaclAddrSecurityDescriptor( (PISECURITY_DESCRIPTOR)SecurityDescriptor ),
+ EffectiveToken( SubjectSecurityContext ),
+ TraverseAccess,
+ AccessGranted,
+ &GenerateAudit,
+ &GenerateAlarm
+ );
+
+ if (GenerateAudit || GenerateAlarm) {
+
+ SepAdtTraverseAuditAlarm(
+ OperationID,
+ DirectoryObject,
+ SepTokenUserSid(EffectiveToken( SubjectSecurityContext )),
+ SepTokenAuthenticationId(EffectiveToken( SubjectSecurityContext )),
+ TraverseAccess,
+ Privileges,
+ AccessGranted,
+ GenerateAudit,
+ GenerateAlarm
+ );
+ }
+
+ if ( !SubjectContextLocked ) {
+ SeUnlockSubjectContext( SubjectSecurityContext );
+ }
+ }
+ }
+
+#endif
+
+ return;
+}
+
+//
+#if 0
+
+VOID
+SeCreateInstanceAuditAlarm(
+ IN PLUID OperationID OPTIONAL,
+ IN PVOID Object,
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
+ IN ACCESS_MASK DesiredAccess,
+ IN PPRIVILEGE_SET Privileges OPTIONAL,
+ IN BOOLEAN AccessGranted,
+ IN KPROCESSOR_MODE AccessMode
+ )
+
+/*++
+
+Routine Description:
+
+ description-of-function.
+
+Arguments:
+
+ argument-name - Supplies | Returns description of argument.
+ .
+ .
+
+Return Value:
+
+ return-value - Description of conditions needed to return value. - or -
+ None.
+
+--*/
+
+{
+ BOOLEAN GenerateAudit = FALSE;
+ BOOLEAN GenerateAlarm = FALSE;
+
+ if ( AccessMode == KernelMode ) {
+ return;
+ }
+
+ if ( SepAdtAuditThisEvent( AuditCategoryObjectAccess, &AccessGranted )) {
+
+ if ( SecurityDescriptor != NULL ) {
+
+ SepExamineSacl(
+ SepSaclAddrSecurityDescriptor( (PISECURITY_DESCRIPTOR)SecurityDescriptor ),
+ EffectiveToken( SubjectSecurityContext ),
+ DesiredAccess,
+ AccessGranted,
+ &GenerateAudit,
+ &GenerateAlarm
+ );
+
+ if ( GenerateAudit || GenerateAlarm ) {
+
+ SepAdtCreateInstanceAuditAlarm(
+ OperationID,
+ Object,
+ SepTokenUserSid(EffectiveToken( SubjectSecurityContext )),
+ SepTokenAuthenticationId(EffectiveToken( SubjectSecurityContext )),
+ DesiredAccess,
+ Privileges,
+ AccessGranted,
+ GenerateAudit,
+ GenerateAlarm
+ );
+
+ }
+ }
+ }
+
+ return;
+
+}
+
+#endif
+
+
+VOID
+SeCreateObjectAuditAlarm(
+ IN PLUID OperationID OPTIONAL,
+ IN PVOID DirectoryObject,
+ IN PUNICODE_STRING ComponentName,
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
+ IN ACCESS_MASK DesiredAccess,
+ IN PPRIVILEGE_SET Privileges OPTIONAL,
+ IN BOOLEAN AccessGranted,
+ OUT PBOOLEAN AuditPerformed,
+ IN KPROCESSOR_MODE AccessMode
+ )
+
+/*++
+
+Routine Description:
+
+ Audits the creation of an object in a directory.
+
+Arguments:
+
+ OperationID - Optionally supplies the LUID representing the operation
+ id for this operation.
+
+ DirectoryObject - Provides a pointer to the directory object being
+ examined.
+
+ ComponentName - Provides a pointer to a Unicode string containing the
+ relative name of the object being created.
+
+ SecurityDescriptor - The security descriptor for the passed direcctory.
+
+ SubjectSecurityContext - The current subject context.
+
+ DesiredAccess - The desired access to the directory.
+
+ Privileges - Returns any privileges that were used for the access attempt.
+
+ AccessGranted - Returns whether or not the access was successful.
+
+ AuditPerformed - Returns whether or not auditing was performed.
+
+ AccessMode - The previous mode.
+
+Return Value:
+
+ return-value - Description of conditions needed to return value. - or -
+ None.
+
+--*/
+
+{
+ PAGED_CODE();
+#if 0
+
+ BOOLEAN GenerateAudit = FALSE;
+ BOOLEAN GenerateAlarm = FALSE;
+ PUNICODE_STRING DirectoryName;
+ POBJECT_NAME_INFORMATION ObjectNameInformation = NULL;
+
+ UNREFERENCED_PARAMETER( DirectoryObject );
+ UNREFERENCED_PARAMETER( Privileges );
+
+ if (AccessMode == KernelMode) {
+ return;
+ }
+
+ if ( SecurityDescriptor != NULL ) {
+
+ if ( SepAdtAuditThisEvent( AuditCategoryObjectAccess, &AccessGranted )) {
+
+ SepExamineSacl(
+ SepSaclAddrSecurityDescriptor( (PISECURITY_DESCRIPTOR)SecurityDescriptor ),
+ EffectiveToken( SubjectSecurityContext ),
+ DesiredAccess,
+ AccessGranted,
+ &GenerateAudit,
+ &GenerateAlarm
+ );
+
+ if ( GenerateAudit || GenerateAlarm ) {
+
+ //
+ // Call ob for the name of the directory.
+ //
+
+ ObjectNameInformation = SepQueryNameString( DirectoryObject );
+
+ if ( ObjectNameInformation != NULL ) {
+
+ DirectoryName = &ObjectNameInformation->Name;
+ }
+
+ SepAdtCreateObjectAuditAlarm(
+ OperationID,
+ DirectoryName,
+ ComponentName,
+ SepTokenUserSid(EffectiveToken( SubjectSecurityContext )),
+ SepTokenAuthenticationId( EffectiveToken( SubjectSecurityContext )),
+ DesiredAccess,
+ AccessGranted,
+ GenerateAudit,
+ GenerateAlarm
+ );
+
+ *AuditPerformed = TRUE;
+
+ if ( DirectoryName != NULL ) {
+
+ ExFreePool( DirectoryName );
+ }
+ }
+ }
+ }
+
+#endif
+
+ return;
+}
+
+
+VOID
+SeObjectReferenceAuditAlarm(
+ IN PLUID OperationID OPTIONAL,
+ IN PVOID Object,
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
+ IN ACCESS_MASK DesiredAccess,
+ IN PPRIVILEGE_SET Privileges OPTIONAL,
+ IN BOOLEAN AccessGranted,
+ IN KPROCESSOR_MODE AccessMode
+ )
+
+/*++
+
+Routine Description:
+
+ description-of-function.
+
+Arguments:
+
+ argument-name - Supplies | Returns description of argument.
+ .
+ .
+
+Return Value:
+
+ return-value - Description of conditions needed to return value. - or -
+ None.
+
+--*/
+
+{
+ BOOLEAN GenerateAudit = FALSE;
+ BOOLEAN GenerateAlarm = FALSE;
+
+ PAGED_CODE();
+
+ if (AccessMode == KernelMode) {
+ return;
+ }
+
+ if ( SecurityDescriptor != NULL ) {
+
+ if ( SepAdtAuditThisEvent( AuditCategoryDetailedTracking, &AccessGranted )) {
+
+ SepExamineSacl(
+ SepSaclAddrSecurityDescriptor( (PISECURITY_DESCRIPTOR)SecurityDescriptor ),
+ EffectiveToken( SubjectSecurityContext ),
+ DesiredAccess,
+ AccessGranted,
+ &GenerateAudit,
+ &GenerateAlarm
+ );
+
+ if ( GenerateAudit || GenerateAlarm ) {
+
+ SepAdtObjectReferenceAuditAlarm(
+ OperationID,
+ Object,
+ SubjectSecurityContext,
+ DesiredAccess,
+ Privileges,
+ AccessGranted,
+ GenerateAudit,
+ GenerateAlarm
+ );
+ }
+ }
+ }
+
+ return;
+
+}
+
+//
+#if 0
+
+VOID
+SeImplicitObjectAuditAlarm(
+ IN PLUID OperationID OPTIONAL,
+ IN PVOID Object,
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
+ IN ACCESS_MASK DesiredAccess,
+ IN PPRIVILEGE_SET Privileges OPTIONAL,
+ IN BOOLEAN AccessGranted,
+ IN KPROCESSOR_MODE AccessMode
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to generate audit messages for implicit
+ references to objects. This includes both objects that may
+ be referenced without either a name or a handle (such as
+ the current process), and references to objects where access
+ checking is being performed but the object is not being
+ opened or created.
+
+ For example, the file system may wish to determine if a
+ user has a FILE_LIST_DIRECTORY access to a directory, but
+ it is not opening the directory or creating a handle to it.
+
+Arguments:
+
+ OperationID - If this reference is part of another, more complex
+ object access, pass in the OperationID corresponding to that access.
+
+ Object - The object being accessed.
+
+ SecurityDescriptor - The security descriptor of the object being accessed.
+
+ SubjectSecurityContext - The current subject context.
+
+ DesiredAccess - Access mask describing the desired access to the object.
+
+ Privileges - Any privileges that have been invoked as part of gaining
+ access to this object.
+
+ AccessGranted - Boolean describing whether access was granted or not.
+
+ AccessMode - Previous processor mode.
+
+Return value:
+
+ None.
+
+--*/
+{
+ BOOLEAN GenerateAudit = FALSE;
+ BOOLEAN GenerateAlarm = FALSE;
+
+ if (AccessMode == KernelMode) {
+ return;
+ }
+
+ if ( SecurityDescriptor != NULL ) {
+
+ if ((SeAuditingState[AuditEventImplicitAccess].AuditOnSuccess && AccessGranted) ||
+ SeAuditingState[AuditEventImplicitAccess].AuditOnFailure && !AccessGranted) {
+
+ SepExamineSacl(
+ SepSaclAddrSecurityDescriptor( (PISECURITY_DESCRIPTOR)SecurityDescriptor ),
+ EffectiveToken( SubjectSecurityContext ),
+ DesiredAccess,
+ AccessGranted,
+ &GenerateAudit,
+ &GenerateAlarm
+ );
+
+ if ( GenerateAudit || GenerateAlarm ) {
+
+ SepAdtImplicitObjectAuditAlarm(
+ OperationID,
+ Object,
+ SepTokenUserSid(EffectiveToken( SubjectSecurityContext )),
+ DesiredAccess,
+ Privileges,
+ AccessGranted,
+ GenerateAudit,
+ GenerateAlarm
+ );
+ }
+ }
+ }
+
+ return;
+
+}
+
+#endif
+
+
+VOID
+SeAuditHandleCreation(
+ IN PACCESS_STATE AccessState,
+ IN HANDLE Handle
+ )
+
+/*++
+
+Routine Description:
+
+ This function audits the creation of a handle.
+
+ It will examine the AuditHandleCreation field in the passed AccessState,
+ which will indicate whether auditing was performed when the object
+ was found or created.
+
+ This routine is necessary because object name decoding and handle
+ allocation occur in widely separate places, preventing us from
+ auditing everything at once.
+
+Arguments:
+
+ AccessState - Supplies a pointer to the AccessState structure
+ representing this access attempt.
+
+ Handle - The newly allocated handle value.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ BOOLEAN AuditPerformed;
+ PAUX_ACCESS_DATA AuxData;
+
+ PAGED_CODE();
+
+ AuxData = (PAUX_ACCESS_DATA)AccessState->AuxData;
+
+ if ( AccessState->GenerateAudit ) {
+
+ if ( AccessState->AuditPrivileges ) {
+
+ AuditPerformed = SepAdtPrivilegeObjectAuditAlarm (
+ &SeSubsystemName,
+ Handle,
+ (PTOKEN)AccessState->SubjectSecurityContext.ClientToken,
+ (PTOKEN)AccessState->SubjectSecurityContext.PrimaryToken,
+ &AccessState->SubjectSecurityContext.ProcessAuditId,
+ AccessState->PreviouslyGrantedAccess,
+ AuxData->PrivilegesUsed,
+ TRUE
+ );
+ } else {
+
+ AuditPerformed = SepAdtOpenObjectAuditAlarm ( &SeSubsystemName,
+ &Handle,
+ &AccessState->ObjectTypeName,
+ NULL,
+ &AccessState->ObjectName,
+ AccessState->SubjectSecurityContext.ClientToken,
+ AccessState->SubjectSecurityContext.PrimaryToken,
+ AccessState->OriginalDesiredAccess,
+ AccessState->PreviouslyGrantedAccess,
+ &AccessState->OperationID,
+ AuxData->PrivilegesUsed,
+ FALSE,
+ TRUE,
+ TRUE,
+ FALSE,
+ AccessState->SubjectSecurityContext.ProcessAuditId );
+ }
+ }
+
+ //
+ // If we generated an 'open' audit, make sure we generate a close
+ //
+
+ AccessState->GenerateOnClose = AuditPerformed;
+
+ return;
+}
+
+
+VOID
+SeCloseObjectAuditAlarm(
+ IN PVOID Object,
+ IN HANDLE Handle,
+ IN BOOLEAN GenerateOnClose
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to generate audit and alarm messages when a handle
+ to an object is deleted.
+
+ This routine may result in several messages being generated and sent to
+ Port objects. This may result in a significant latency before
+ returning. Design of routines that must call this routine must take
+ this potential latency into account. This may have an impact on the
+ approach taken for data structure mutex locking, for example.
+
+Arguments:
+
+ Object - Address of the object being accessed. This value will not be
+ used as a pointer (referenced). It is necessary only to enter into
+ log messages.
+
+ Handle - Supplies the handle value assigned to the open.
+
+ GenerateOnClose - Is a boolean value returned from a corresponding
+ SeOpenObjectAuditAlarm() call when the object handle was created.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ SECURITY_SUBJECT_CONTEXT SubjectSecurityContext;
+ PSID UserSid;
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ if (GenerateOnClose) {
+
+ SeCaptureSubjectContext ( &SubjectSecurityContext );
+
+ UserSid = SepTokenUserSid( EffectiveToken (&SubjectSecurityContext));
+
+
+ SepAdtCloseObjectAuditAlarm (
+ &SeSubsystemName,
+ (PVOID)Handle,
+ Object,
+ UserSid,
+ SepTokenAuthenticationId( EffectiveToken (&SubjectSecurityContext))
+ );
+
+ SeReleaseSubjectContext ( &SubjectSecurityContext );
+ }
+
+ return;
+}
+
+
+VOID
+SeDeleteObjectAuditAlarm(
+ IN PVOID Object,
+ IN HANDLE Handle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to generate audit and alarm messages when an object
+ is marked for deletion.
+
+ This routine may result in several messages being generated and sent to
+ Port objects. This may result in a significant latency before
+ returning. Design of routines that must call this routine must take
+ this potential latency into account. This may have an impact on the
+ approach taken for data structure mutex locking, for example.
+
+Arguments:
+
+ Object - Address of the object being accessed. This value will not be
+ used as a pointer (referenced). It is necessary only to enter into
+ log messages.
+
+ Handle - Supplies the handle value assigned to the open.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ SECURITY_SUBJECT_CONTEXT SubjectSecurityContext;
+ PSID UserSid;
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ SeCaptureSubjectContext ( &SubjectSecurityContext );
+
+ UserSid = SepTokenUserSid( EffectiveToken (&SubjectSecurityContext));
+
+
+
+ SepAdtDeleteObjectAuditAlarm (
+ &SeSubsystemName,
+ (PVOID)Handle,
+ Object,
+ UserSid,
+ SepTokenAuthenticationId( EffectiveToken (&SubjectSecurityContext))
+ );
+
+ SeReleaseSubjectContext ( &SubjectSecurityContext );
+
+ return;
+}
+
+
+VOID
+SepExamineSacl(
+ IN PACL Sacl,
+ IN PACCESS_TOKEN Token,
+ IN ACCESS_MASK DesiredAccess,
+ IN BOOLEAN AccessGranted,
+ OUT PBOOLEAN GenerateAudit,
+ OUT PBOOLEAN GenerateAlarm
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will examine the passed Sacl and determine what
+ if any action is required based its contents.
+
+ Note that this routine is not aware of any system state, ie,
+ whether or not auditing is currently enabled for either the
+ system or this particular object type.
+
+Arguments:
+
+ Sacl - Supplies a pointer to the Sacl to be examined.
+
+ Token - Supplies the effective token of the caller
+
+ AccessGranted - Supplies whether or not the access attempt
+ was successful.
+
+ GenerateAudit - Returns a boolean indicating whether or not
+ we should generate an audit.
+
+ GenerateAlarm - Returns a boolean indiciating whether or not
+ we should generate an alarm.
+
+Return Value:
+
+ STATUS_SUCCESS - The operation completed successfully.
+
+--*/
+
+{
+
+ ULONG i;
+ PVOID Ace;
+ ULONG AceCount;
+ ACCESS_MASK AccessMask;
+ UCHAR AceFlags;
+ BOOLEAN FailedMaximumAllowed;
+
+ PAGED_CODE();
+
+ *GenerateAudit = FALSE;
+ *GenerateAlarm = FALSE;
+
+ //
+ // If we failed an attempt to open an object for ONLY maximumum allowed,
+ // then we generate an audit if ANY ACCESS_DENIED audit matching this
+ // user's list of sids is found
+ //
+
+ FailedMaximumAllowed = FALSE;
+ if (!AccessGranted && (DesiredAccess & MAXIMUM_ALLOWED)) {
+ FailedMaximumAllowed = TRUE;
+ }
+
+ //
+ // If the Sacl is null, do nothing and return
+ //
+
+ if (Sacl == NULL) {
+
+ return;
+ }
+
+ AceCount = Sacl->AceCount;
+
+ if (AceCount == 0) {
+ return;
+ }
+
+ //
+ // Iterate through the ACEs on the Sacl until either we reach
+ // the end or discover that we have to take all possible actions,
+ // in which case it doesn't pay to look any further
+ //
+
+ for ( i = 0, Ace = FirstAce( Sacl ) ;
+ (i < AceCount) && !(*GenerateAudit && *GenerateAlarm);
+ i++, Ace = NextAce( Ace ) ) {
+
+ if ( !(((PACE_HEADER)Ace)->AceFlags & INHERIT_ONLY_ACE)) {
+
+ if ( (((PACE_HEADER)Ace)->AceType == SYSTEM_AUDIT_ACE_TYPE) ) {
+
+ if ( SepSidInToken( (PACCESS_TOKEN)Token, &((PSYSTEM_AUDIT_ACE)Ace)->SidStart ) ) {
+
+ AccessMask = ((PSYSTEM_AUDIT_ACE)Ace)->Mask;
+ AceFlags = ((PACE_HEADER)Ace)->AceFlags;
+
+ if ( AccessMask & DesiredAccess ) {
+
+ if (((AceFlags & SUCCESSFUL_ACCESS_ACE_FLAG) && AccessGranted) ||
+ ((AceFlags & FAILED_ACCESS_ACE_FLAG) && !AccessGranted)) {
+
+ *GenerateAudit = TRUE;
+ }
+ } else if ( FailedMaximumAllowed && (AceFlags & FAILED_ACCESS_ACE_FLAG) ) {
+ *GenerateAudit = TRUE;
+ }
+ }
+
+ continue;
+ }
+
+ if ( (((PACE_HEADER)Ace)->AceType == SYSTEM_ALARM_ACE_TYPE) ) {
+
+ if ( SepSidInToken( (PACCESS_TOKEN)Token, &((PSYSTEM_ALARM_ACE)Ace)->SidStart ) ) {
+
+ AccessMask = ((PSYSTEM_ALARM_ACE)Ace)->Mask;
+
+ if ( AccessMask & DesiredAccess ) {
+
+ AceFlags = ((PACE_HEADER)Ace)->AceFlags;
+
+ if (((AceFlags & SUCCESSFUL_ACCESS_ACE_FLAG) && AccessGranted) ||
+ ((AceFlags & FAILED_ACCESS_ACE_FLAG) && !AccessGranted)) {
+
+ *GenerateAlarm = TRUE;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return;
+}
+
+
+/******************************************************************************
+* *
+* The following list of privileges is checked at high frequency *
+* during normal operation, and tend to clog up the audit log when *
+* privilege auditing is enabled. The use of these privileges will *
+* not be audited when they are checked singly or in combination with *
+* each other. *
+* *
+* When adding new privileges, be careful to preserve the NULL *
+* privilege pointer marking the end of the array. *
+* *
+* Be sure to update the corresponding array in LSA when adding new *
+* privileges to this list (LsaFilterPrivileges). *
+* *
+******************************************************************************/
+
+PLUID *SepFilterPrivileges = NULL;
+
+PLUID SepFilterPrivilegesLong[] =
+ {
+ &SeChangeNotifyPrivilege,
+ &SeAuditPrivilege,
+ &SeCreateTokenPrivilege,
+ &SeAssignPrimaryTokenPrivilege,
+ &SeBackupPrivilege,
+ &SeRestorePrivilege,
+ &SeDebugPrivilege,
+ NULL
+ };
+
+/******************************************************************************
+* *
+* The following list of privileges is the same as the above list, except *
+* is missing backup and restore privileges. This allows for auditing *
+* the use of those privileges at the time they are used. *
+* *
+* The use of this list or the one above is determined by settings in *
+* the registry. *
+* *
+******************************************************************************/
+
+PLUID SepFilterPrivilegesShort[] =
+ {
+ &SeChangeNotifyPrivilege,
+ &SeAuditPrivilege,
+ &SeCreateTokenPrivilege,
+ &SeAssignPrimaryTokenPrivilege,
+ &SeDebugPrivilege,
+ NULL
+ };
+
+BOOLEAN
+SepInitializePrivilegeFilter(
+ BOOLEAN Verbose
+ )
+/*++
+
+Routine Description:
+
+ Initializes SepFilterPrivileges for either normal or verbose auditing.
+
+Arguments:
+
+ Verbose - Whether we want to filter by the short or long privileges
+ list. Verbose == TRUE means use the short list.
+
+Return Value:
+
+ TRUE for success, FALSE for failure
+
+--*/
+{
+ if (Verbose) {
+ SepFilterPrivileges = SepFilterPrivilegesShort;
+ } else {
+ SepFilterPrivileges = SepFilterPrivilegesLong;
+ }
+
+ return( TRUE );
+}
+
+
+BOOLEAN
+SepFilterPrivilegeAudits(
+ IN PPRIVILEGE_SET PrivilegeSet
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will filter out a list of privileges as listed in the
+ SepFilterPrivileges array.
+
+Arguments:
+
+ Privileges - The privilege set to be audited
+
+Return Value:
+
+ FALSE means that this use of privilege is not to be audited.
+ TRUE means that the audit should continue normally.
+
+--*/
+
+{
+ PLUID *Privilege;
+ ULONG Match = 0;
+ ULONG i;
+
+ PAGED_CODE();
+
+ if ( !ARGUMENT_PRESENT(PrivilegeSet) ||
+ (PrivilegeSet->PrivilegeCount == 0) ) {
+ return( FALSE );
+ }
+
+ for (i=0; i<PrivilegeSet->PrivilegeCount; i++) {
+
+ Privilege = SepFilterPrivileges;
+
+ do {
+
+ if ( RtlEqualLuid( &PrivilegeSet->Privilege[i].Luid, *Privilege )) {
+
+ Match++;
+ break;
+ }
+
+ } while ( *++Privilege != NULL );
+ }
+
+ if ( Match == PrivilegeSet->PrivilegeCount ) {
+
+ return( FALSE );
+
+ } else {
+
+ return( TRUE );
+ }
+}
+
+
+BOOLEAN
+SeAuditingFileOrGlobalEvents(
+ IN BOOLEAN AccessGranted,
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is to be called by a file system to quickly determine
+ if we are auditing file open events. This allows the file system
+ to avoid the often considerable setup involved in generating an audit.
+
+Arguments:
+
+ AccessGranted - Supplies whether the access attempt was successful
+ or a failure.
+
+Return Value:
+
+ Boolean - TRUE if events of type AccessGranted are being audited, FALSE
+ otherwise.
+
+--*/
+
+{
+ PISECURITY_DESCRIPTOR ISecurityDescriptor = (PISECURITY_DESCRIPTOR) SecurityDescriptor;
+
+ PAGED_CODE();
+
+ if ( ((PTOKEN)EffectiveToken( SubjectSecurityContext ))->AuditData != NULL) {
+ return( TRUE );
+ }
+
+ if ( SepSaclAddrSecurityDescriptor( ISecurityDescriptor ) == NULL ) {
+
+ return( FALSE );
+ }
+
+ return( SepAdtAuditThisEvent( AuditCategoryObjectAccess, &AccessGranted ) );
+}
+
+
+BOOLEAN
+SeAuditingFileEvents(
+ IN BOOLEAN AccessGranted,
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is to be called by a file system to quickly determine
+ if we are auditing file open events. This allows the file system
+ to avoid the often considerable setup involved in generating an audit.
+
+Arguments:
+
+ AccessGranted - Supplies whether the access attempt was successful
+ or a failure.
+
+Return Value:
+
+ Boolean - TRUE if events of type AccessGranted are being audited, FALSE
+ otherwise.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ UNREFERENCED_PARAMETER( SecurityDescriptor );
+
+ return( SepAdtAuditThisEvent( AuditCategoryObjectAccess, &AccessGranted ) );
+}
diff --git a/private/ntos/se/seclient.c b/private/ntos/se/seclient.c
new file mode 100644
index 000000000..8f330cc9d
--- /dev/null
+++ b/private/ntos/se/seclient.c
@@ -0,0 +1,633 @@
+
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ seclient.c
+
+Abstract:
+
+ This module implements routines providing client impersonation to
+ communication session layers (such as LPC Ports).
+
+ WARNING: The following notes apply to the use of these services:
+
+ (1) No synchronization of operations to a security context block are
+ performed by these services. The caller of these services must
+ ensure that use of an individual security context block is
+ serialized to prevent simultaneous, incompatible updates.
+
+ (2) Any or all of these services may create, open, or operate on a
+ token object. This may result in a mutex being acquired at
+ MUTEXT_LEVEL_SE_TOKEN level. The caller must ensure that no
+ mutexes are held at levels that conflict with this action.
+
+
+Author:
+
+ Jim Kelly (JimK) 1-August-1990
+
+Environment:
+
+ Kernel mode only.
+
+Revision History:
+
+--*/
+
+
+#include "sep.h"
+#include "seopaque.h"
+
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE,SeCreateClientSecurity)
+#pragma alloc_text(PAGE,SeUpdateClientSecurity)
+#pragma alloc_text(PAGE,SeImpersonateClient)
+#endif
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Routines //
+// //
+////////////////////////////////////////////////////////////////////////
+
+
+NTSTATUS
+SeCreateClientSecurity (
+ IN PETHREAD ClientThread,
+ IN PSECURITY_QUALITY_OF_SERVICE ClientSecurityQos,
+ IN BOOLEAN ServerIsRemote,
+ OUT PSECURITY_CLIENT_CONTEXT ClientContext
+ )
+
+/*++
+
+Routine Description:
+
+ This service initializes a context block to represent a client's
+ security context. This may simply result in a reference to the
+ client's token, or may cause the client's token to be duplicated,
+ depending upon the security quality of service information specified.
+
+ NOTE
+
+ The code in this routine is optimized for DYNAMIC context
+ tracking. This is only mode in which direct access to a
+ caller's token is allowed, and the mode expected to be used
+ most often. STATIC context tracking always requires the
+ caller's token to be copied.
+
+
+Arguments:
+
+ ClientThread - Points to the client's thread. This is used to
+ locate the client's security context (token).
+
+ ClientSecurityQos - Points to the security quality of service
+ parameters specified by the client for this communication
+ session.
+
+ ServerIsRemote - Provides an indication as to whether the session
+ this context block is being used for is an inter-system
+ session or intra-system session. This is reconciled with the
+ impersonation level of the client thread's token (in case the
+ client has a client of his own that didn't specify delegation).
+
+ ClientContext - Points to the client security context block to be
+ initialized.
+
+
+Return Value:
+
+ STATUS_SUCCESS - The service completed successfully.
+
+ STATUS_BAD_IMPERSONATION_LEVEL - The client is currently
+ impersonating either an Anonymous or Identification level
+ token, which can not be passed on for use by another server.
+ This status may also be returned if the security context
+ block is for an inter-system communication session and the
+ client thread is impersonating a client of its own using
+ other than delegation impersonation level.
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PACCESS_TOKEN Token;
+ TOKEN_TYPE TokenType;
+ BOOLEAN ThreadEffectiveOnly;
+ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
+ PACCESS_TOKEN DuplicateToken;
+
+ PAGED_CODE();
+
+ //
+ // Gain access to the client thread's effective token
+ //
+
+ Token = PsReferenceEffectiveToken(
+ ClientThread,
+ &TokenType,
+ &ThreadEffectiveOnly,
+ &ImpersonationLevel
+ );
+
+
+ //
+ // Make sure the client is not trying to abuse use of a
+ // client of its own by attempting an invalid impersonation.
+ // Also set the ClientContext->DirectAccessEffectiveOnly flag
+ // appropriately if the impersonation is legitimate. The
+ // DirectAccessEffectiveOnly flag value will end up being ignored
+ // if STATIC mode is requested, but this is the most efficient
+ // place to calculate it, and we are optimizing for DYNAMIC mode.
+ //
+
+ if (TokenType == TokenImpersonation) {
+
+ if ( ClientSecurityQos->ImpersonationLevel > ImpersonationLevel) {
+
+ PsDereferenceImpersonationToken( Token );
+ return STATUS_BAD_IMPERSONATION_LEVEL;
+
+ }
+
+
+ if ( SepBadImpersonationLevel(ImpersonationLevel,ServerIsRemote)) {
+
+ PsDereferenceImpersonationToken( Token );
+ return STATUS_BAD_IMPERSONATION_LEVEL;
+
+ } else {
+
+ //
+ // TokenType is TokenImpersonation and the impersonation is legit.
+ // Set the DirectAccessEffectiveOnly flag to be the minimum of
+ // the current thread value and the caller specified value.
+ //
+
+ ClientContext->DirectAccessEffectiveOnly =
+ ( (ThreadEffectiveOnly || (ClientSecurityQos->EffectiveOnly)) ?
+ TRUE : FALSE );
+ }
+
+ } else {
+
+ //
+ // TokenType is TokenPrimary. In this case, the client specified
+ // EffectiveOnly value is always used.
+ //
+
+ ClientContext->DirectAccessEffectiveOnly =
+ ClientSecurityQos->EffectiveOnly;
+ }
+
+
+
+ //
+ // Copy the token if necessary (i.e., static tracking requested)
+ //
+
+ if (ClientSecurityQos->ContextTrackingMode == SECURITY_STATIC_TRACKING) {
+
+ ClientContext->DirectlyAccessClientToken = FALSE;
+
+ Status = SeCopyClientToken(
+ Token,
+ ClientSecurityQos->ImpersonationLevel,
+ KernelMode,
+ &DuplicateToken
+ );
+
+
+ if ( NT_SUCCESS(Status) ) {
+ ObDeleteCapturedInsertInfo(DuplicateToken);
+ }
+ //
+ // No longer need the pointer to the client's token
+ //
+
+ if (TokenType == TokenPrimary) {
+ PsDereferencePrimaryToken( Token );
+ } else {
+ PsDereferenceImpersonationToken( Token );
+ }
+
+ Token = DuplicateToken;
+
+
+ //
+ // If there was an error, we're done.
+ //
+ if (!NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+ } else {
+
+ ClientContext->DirectlyAccessClientToken = TRUE;
+
+ if (ServerIsRemote) {
+ //
+ // Get a copy of the client token's control information
+ // so that we can tell if it changes in the future.
+ //
+
+ SeGetTokenControlInformation( Token,
+ &ClientContext->ClientTokenControl
+ );
+
+ }
+
+ }
+
+
+
+ ClientContext->SecurityQos.Length =
+ (ULONG)sizeof(SECURITY_QUALITY_OF_SERVICE);
+
+ ClientContext->SecurityQos.ImpersonationLevel =
+ ClientSecurityQos->ImpersonationLevel;
+
+ ClientContext->SecurityQos.ContextTrackingMode =
+ ClientSecurityQos->ContextTrackingMode;
+
+ ClientContext->SecurityQos.EffectiveOnly =
+ ClientSecurityQos->EffectiveOnly;
+
+ ClientContext->ServerIsRemote = ServerIsRemote;
+
+ ClientContext->ClientToken = Token;
+
+ return STATUS_SUCCESS;
+
+
+
+}
+
+
+
+#if SAVE_FOR_PRODUCT_2
+
+
+
+
+NTSTATUS
+SeUpdateClientSecurity(
+ IN PETHREAD ClientThread,
+ IN OUT PSECURITY_CLIENT_CONTEXT ClientContext,
+ OUT PBOOLEAN ChangesMade,
+ OUT PBOOLEAN NewToken
+ )
+
+/*++
+
+Routine Description:
+
+ This service is used to update a client security context block
+ based upon the client's current security context and the security
+ quality of service parameters specified when the security block
+ was created. If the SecurityContextTracking specified when the
+ context block was created indicated static tracking, then no
+ change will be made to the context block. Otherwise, a change may
+ be made.
+
+
+ An indication of whether any changes were made is returned to the
+ caller. This may be used by communication session layers
+ providing remote communications to decide whether or not to send
+ an updated security context to the remote server's node. It may
+ also be used by a server session layer to decide whether or not to
+ inform a server that a previously obtained handle to a token no
+ longer represents the current security context.
+
+
+Arguments:
+
+ ClientThread - Points to the client's thread. This is used to
+ locate the security context to synchronize with.
+
+ ClientContext - Points to client security context block to be
+ updated.
+
+ ChangesMade - Receives an indication as to whether any changes to
+ the client's security context had been made since the last
+ time the security context block was synchronized. This will
+ always be FALSE if static security tracking is in effect.
+
+ NewToken - Receives an indication as to whether the same token
+ is used to represent the client's current context, or whether
+ the context now points to a new token. If the client's token
+ is directly referenced, then this indicates the client changed
+ tokens (and the new one is now referenced). If the client's token
+ isn't directly referenced, then this indicates it was necessary
+ to delete one token and create another one. This will always be
+ FALSE if static security tracking is in effect.
+
+
+Return Value:
+
+ STATUS_SUCCESS - The service completed successfully.
+
+ STATUS_BAD_IMPERSONATION_LEVEL - The client is currently
+ impersonating either an Anonymous or Identification level
+ token, which can not be passed on for use by another server.
+ This status may also be returned if the security context
+ block is for an inter-system communication session and the
+ client thread is impersonating a client of its own using
+ other than delegation impersonation level.
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ PACCESS_TOKEN Token;
+ TOKEN_TYPE TokenType;
+ BOOLEAN ThreadEffectiveOnly;
+ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
+ PACCESS_TOKEN DuplicateToken;
+ TOKEN_CONTROL TokenControl;
+
+ PAGED_CODE();
+
+ if (ClientContext->SecurityQos.ContextTrackingMode ==
+ SECURITY_STATIC_TRACKING) {
+
+ (*NewToken) = FALSE;
+ (*ChangesMade) = FALSE;
+ return STATUS_SUCCESS;
+
+ }
+
+
+ //////////////////////////////////////////////
+ // //
+ // Optimize for the directly accessed token //
+ // //
+ //////////////////////////////////////////////
+
+
+
+ //
+ // Gain access to the client thread's effective token
+ //
+
+ Token = PsReferenceEffectiveToken(
+ ClientThread,
+ &TokenType,
+ &ThreadEffectiveOnly,
+ &ImpersonationLevel
+ );
+
+
+
+ //
+ // See if the token is the same.
+ //
+
+
+ SeGetTokenControlInformation( Token, &TokenControl );
+
+ if ( SeSameToken( &TokenControl,
+ &ClientContext->ClientTokenControl) ) {
+
+ (*NewToken = FALSE);
+
+
+ //
+ // Same token.
+ // Is it unmodified?
+ //
+
+ if ( (TokenControl.ModifiedId.HighPart ==
+ ClientContext->ClientTokenControl.ModifiedId.HighPart) &&
+ (TokenControl.ModifiedId.LowPart ==
+ ClientContext->ClientTokenControl.ModifiedId.LowPart) ) {
+
+ //
+ // Yup. No changes necessary.
+ //
+
+ if (TokenType == TokenPrimary) {
+ PsDereferencePrimaryToken( Token );
+ } else {
+ PsDereferenceImpersonationToken( Token );
+ }
+
+ (*ChangesMade) = FALSE;
+ return STATUS_SUCCESS;
+
+ } else {
+
+ //
+ // Same token, but it has been modified.
+ // If we are directly accessing the token, then we can
+ // just indicate it has changed and return. Otherwise
+ // we have to actually update our copy of the token.
+ //
+
+ (*ChangesMade) = TRUE;
+ if (ClientContext->DirectlyAccessClientToken) {
+
+ if (TokenType == TokenPrimary) {
+ PsDereferencePrimaryToken( Token );
+ } else {
+ PsDereferenceImpersonationToken( Token );
+ }
+
+ //
+ // Save the new modified count and whether or not
+ // the token is for effective use only
+ //
+
+ ClientContext->ClientTokenControl.ModifiedId =
+ TokenControl.ModifiedId;
+ ClientContext->DirectAccessEffectiveOnly =
+ ( (ThreadEffectiveOnly || (ClientContext->SecurityQos.EffectiveOnly)) ?
+ TRUE : FALSE );
+
+ return STATUS_SUCCESS;
+ } else {
+
+ //
+ // There is a possibility for a fair performance gain here
+ // by just updating the existing token to match its origin.
+ // However, it isn't clear that this case is ever really
+ // used, so the effort and complexity is avoided at this time.
+ // If it is found that this case is used, then this code
+ // can be added.
+ //
+ // Instead, we just fall through to the case of completely
+ // different tokens below.
+ //
+ }
+ }
+ }
+
+
+ //
+ // Not the same token, or the same token has changed.
+ // In either case, we're going to create a new copy of the token
+ // and dump the old copy.
+ //
+ // Make sure the current impersonation situation is legitimate.
+ //
+
+ (*NewToken) = TRUE;
+ (*ChangesMade) = TRUE;
+ if (TokenType == TokenImpersonation) {
+ if ( SepBadImpersonationLevel(ImpersonationLevel,
+ ClientContext->ServerIsRemote)) {
+
+ PsDereferenceImpersonationToken( Token );
+ return STATUS_BAD_IMPERSONATION_LEVEL;
+ }
+ }
+
+
+ //
+ // Copy the token
+ //
+
+
+
+ Status = SeCopyClientToken(
+ Token,
+ ClientContext->SecurityQos.ImpersonationLevel,
+ KernelMode,
+ &DuplicateToken
+ );
+
+
+ //
+ // No longer need the pointer to the client's effective token
+ //
+
+ if (TokenType == TokenPrimary) {
+ PsDereferencePrimaryToken( Token );
+ } else {
+ PsDereferenceImpersonationToken( Token );
+ }
+
+
+
+ //
+ // If there was an error, we're done.
+ //
+ if (!NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+
+ //
+ // Otherwise, replace the current token with the new one.
+ //
+
+ Token = ClientContext->ClientToken;
+ ClientContext->ClientToken = DuplicateToken;
+ ClientContext->DirectlyAccessClientToken = FALSE;
+
+ if (SeTokenType( Token ) == TokenPrimary) {
+ PsDereferencePrimaryToken( Token );
+ } else {
+ PsDereferenceImpersonationToken( Token );
+ }
+
+
+ //
+ // Get a copy of the current token's control information
+ // so that we can tell if it changes in the future.
+ //
+
+ SeGetTokenControlInformation( DuplicateToken,
+ &ClientContext->ClientTokenControl
+ );
+
+
+ return STATUS_SUCCESS;
+
+}
+
+
+#endif
+
+
+
+
+VOID
+SeImpersonateClient(
+ IN PSECURITY_CLIENT_CONTEXT ClientContext,
+ IN PETHREAD ServerThread OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This service is used to cause the calling thread to impersonate a
+ client. The client security context in ClientContext is assumed to
+ be up to date.
+
+
+Arguments:
+
+ ClientContext - Points to client security context block.
+
+ ServerThread - (Optional) Specifies the thread which is to be made to
+ impersonate the client. If not specified, the calling thread is
+ used.
+
+
+Return Value:
+
+ None.
+
+
+--*/
+
+
+{
+
+ BOOLEAN EffectiveValueToUse;
+ PETHREAD Thread;
+
+ PAGED_CODE();
+
+ if (ClientContext->DirectlyAccessClientToken) {
+ EffectiveValueToUse = ClientContext->DirectAccessEffectiveOnly;
+ } else {
+ EffectiveValueToUse = ClientContext->SecurityQos.EffectiveOnly;
+ }
+
+
+
+ //
+ // if a ServerThread wasn't specified, then default to the current
+ // thread.
+ //
+
+ if (!ARGUMENT_PRESENT(ServerThread)) {
+ Thread = PsGetCurrentThread();
+ } else {
+ Thread = ServerThread;
+ }
+
+
+
+ //
+ // Assign the context to the calling thread
+ //
+
+ PsImpersonateClient( Thread,
+ ClientContext->ClientToken,
+ TRUE,
+ EffectiveValueToUse,
+ ClientContext->SecurityQos.ImpersonationLevel
+ );
+
+}
diff --git a/private/ntos/se/seglobal.c b/private/ntos/se/seglobal.c
new file mode 100644
index 000000000..1526aac8b
--- /dev/null
+++ b/private/ntos/se/seglobal.c
@@ -0,0 +1,965 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ seglobal.c
+
+Abstract:
+
+ This module contains the global variables used and exported by the security
+ component.
+
+Author:
+
+ Jim Kelly (JimK) 5-Aug-1990
+
+Environment:
+
+ Kernel mode only.
+
+Revision History:
+
+
+--*/
+
+#include "sep.h"
+#include "adt.h"
+#include "seopaque.h"
+
+VOID
+SepInitializePrivilegeSets( VOID );
+
+
+VOID
+SepInitSystemDacls( VOID );
+
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(INIT,SepVariableInitialization)
+#pragma alloc_text(INIT,SepInitializePrivilegeSets)
+#pragma alloc_text(INIT,SepInitSystemDacls)
+#pragma alloc_text(INIT,SepInitializeWorkList)
+#pragma alloc_text(PAGE,SepAssemblePrivileges)
+#endif
+
+#ifdef SE_DIAGNOSTICS_ENABLED
+
+//
+// Used to control the active SE diagnostic support provided
+//
+
+ULONG SeGlobalFlag = 0;
+
+#endif // SE_DIAGNOSTICS_ENABLED
+
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Global, READ ONLY, Security variables //
+// //
+////////////////////////////////////////////////////////////////////////
+
+//
+// Authentication ID and source name used for system processes
+//
+
+TOKEN_SOURCE SeSystemTokenSource = {"*SYSTEM*", 0};
+LUID SeSystemAuthenticationId = SYSTEM_LUID;
+
+
+//
+// Universal well known SIDs
+//
+
+PSID SeNullSid;
+PSID SeWorldSid;
+PSID SeLocalSid;
+PSID SeCreatorOwnerSid;
+PSID SeCreatorGroupSid;
+PSID SeCreatorGroupServerSid;
+PSID SeCreatorOwnerServerSid;
+
+//
+// Sids defined by NT
+//
+
+PSID SeNtAuthoritySid;
+
+PSID SeDialupSid;
+PSID SeNetworkSid;
+PSID SeBatchSid;
+PSID SeInteractiveSid;
+PSID SeServiceSid;
+PSID SeLocalSystemSid;
+PSID SeAliasAdminsSid;
+PSID SeAliasUsersSid;
+PSID SeAliasGuestsSid;
+PSID SeAliasPowerUsersSid;
+PSID SeAliasAccountOpsSid;
+PSID SeAliasSystemOpsSid;
+PSID SeAliasPrintOpsSid;
+PSID SeAliasBackupOpsSid;
+
+
+//
+// System default DACLs & Security Descriptors
+//
+// SePublicDefaultDacl - Protects objects so that WORLD:E, Admins:ALL, System: ALL.
+// Not inherited by sub-objects.
+//
+// SePublicOpenDacl - Protects so that WORLD can RWE and Admins: All.
+// Not inherited by sub-objects.
+//
+// SeSystemDefaultDacl - Protects objects so that SYSTEM & ADMIN can use them.
+// Not inherited by subobjects.
+//
+
+PSECURITY_DESCRIPTOR SePublicDefaultSd;
+SECURITY_DESCRIPTOR SepPublicDefaultSd;
+PSECURITY_DESCRIPTOR SePublicOpenSd;
+SECURITY_DESCRIPTOR SepPublicOpenSd;
+PSECURITY_DESCRIPTOR SeSystemDefaultSd;
+SECURITY_DESCRIPTOR SepSystemDefaultSd;
+
+PACL SePublicDefaultDacl;
+PACL SePublicOpenDacl;
+PACL SeSystemDefaultDacl;
+
+//
+// Sid of primary domain, and admin account in that domain
+//
+
+PSID SepPrimaryDomainSid;
+PSID SepPrimaryDomainAdminSid;
+
+
+
+//
+// Well known privilege values
+//
+
+
+LUID SeCreateTokenPrivilege;
+LUID SeAssignPrimaryTokenPrivilege;
+LUID SeLockMemoryPrivilege;
+LUID SeIncreaseQuotaPrivilege;
+LUID SeUnsolicitedInputPrivilege;
+LUID SeTcbPrivilege;
+LUID SeSecurityPrivilege;
+LUID SeTakeOwnershipPrivilege;
+LUID SeLoadDriverPrivilege;
+LUID SeCreatePagefilePrivilege;
+LUID SeIncreaseBasePriorityPrivilege;
+LUID SeSystemProfilePrivilege;
+LUID SeSystemtimePrivilege;
+LUID SeProfileSingleProcessPrivilege;
+LUID SeCreatePermanentPrivilege;
+LUID SeBackupPrivilege;
+LUID SeRestorePrivilege;
+LUID SeShutdownPrivilege;
+LUID SeDebugPrivilege;
+LUID SeAuditPrivilege;
+LUID SeSystemEnvironmentPrivilege;
+LUID SeChangeNotifyPrivilege;
+LUID SeRemoteShutdownPrivilege;
+
+
+//
+// Define the following structures for export from the kernel.
+// This will allow us to export pointers to these structures
+// rather than a pointer for each element in the structure.
+//
+
+PSE_EXPORTS SeExports;
+SE_EXPORTS SepExports;
+
+
+static SID_IDENTIFIER_AUTHORITY SepNullSidAuthority = SECURITY_NULL_SID_AUTHORITY;
+static SID_IDENTIFIER_AUTHORITY SepWorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY;
+static SID_IDENTIFIER_AUTHORITY SepLocalSidAuthority = SECURITY_LOCAL_SID_AUTHORITY;
+static SID_IDENTIFIER_AUTHORITY SepCreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;
+static SID_IDENTIFIER_AUTHORITY SepNtAuthority = SECURITY_NT_AUTHORITY;
+
+
+//
+// Some variables we are going to use to help speed up access
+// checking.
+//
+
+static ULONG SinglePrivilegeSetSize;
+static ULONG DoublePrivilegeSetSize;
+
+static PPRIVILEGE_SET SepSystemSecurityPrivilegeSet;
+static PPRIVILEGE_SET SepTakeOwnershipPrivilegeSet;
+static PPRIVILEGE_SET SepDoublePrivilegeSet;
+
+
+//
+// Array containing information describing what is to be audited
+//
+
+SE_AUDITING_STATE SeAuditingState[POLICY_AUDIT_EVENT_TYPE_COUNT] =
+ {
+ { FALSE, FALSE },
+ { FALSE, FALSE },
+ { FALSE, FALSE },
+ { FALSE, FALSE },
+ { FALSE, FALSE },
+ { FALSE, FALSE },
+ { FALSE, FALSE }
+ };
+
+//
+// Boolean indicating whether or not auditing is enabled for the system
+//
+
+BOOLEAN SepAdtAuditingEnabled = FALSE;
+
+//
+// Boolean to hold whether or not the user wants the system to crash when
+// an audit fails.
+//
+
+BOOLEAN SepCrashOnAuditFail = FALSE;
+
+//
+// Handle to the LSA process
+//
+
+HANDLE SepLsaHandle;
+
+//
+// Boolean indicating that we're auditing detailed events
+// such as process creation.
+//
+
+BOOLEAN SeDetailedAuditing = FALSE;
+
+UNICODE_STRING SeSubsystemName;
+
+
+//
+// Mutex protecting the queue of work being passed to LSA
+//
+
+ERESOURCE SepLsaQueueLock;
+
+//
+// Doubly linked list of work items queued to worker threads.
+//
+
+LIST_ENTRY SepLsaQueue;
+
+//
+// Count to tell us how long the queue gets in SepRmCallLsa
+//
+
+ULONG SepLsaQueueLength = 0;
+
+SEP_WORK_ITEM SepExWorkItem;
+
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Variable Initialization Routines //
+// //
+////////////////////////////////////////////////////////////////////////
+
+BOOLEAN
+SepVariableInitialization()
+/*++
+
+Routine Description:
+
+ This function initializes the global variables used by and exposed
+ by security.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ TRUE if variables successfully initialized.
+ FALSE if not successfully initialized.
+
+--*/
+{
+
+ ULONG SidWithZeroSubAuthorities;
+ ULONG SidWithOneSubAuthority;
+ ULONG SidWithTwoSubAuthorities;
+ ULONG SidWithThreeSubAuthorities;
+
+ SID_IDENTIFIER_AUTHORITY NullSidAuthority;
+ SID_IDENTIFIER_AUTHORITY WorldSidAuthority;
+ SID_IDENTIFIER_AUTHORITY LocalSidAuthority;
+ SID_IDENTIFIER_AUTHORITY CreatorSidAuthority;
+ SID_IDENTIFIER_AUTHORITY SeNtAuthority;
+
+ PAGED_CODE();
+
+ NullSidAuthority = SepNullSidAuthority;
+ WorldSidAuthority = SepWorldSidAuthority;
+ LocalSidAuthority = SepLocalSidAuthority;
+ CreatorSidAuthority = SepCreatorSidAuthority;
+ SeNtAuthority = SepNtAuthority;
+
+
+ //
+ // The following SID sizes need to be allocated
+ //
+
+ SidWithZeroSubAuthorities = RtlLengthRequiredSid( 0 );
+ SidWithOneSubAuthority = RtlLengthRequiredSid( 1 );
+ SidWithTwoSubAuthorities = RtlLengthRequiredSid( 2 );
+ SidWithThreeSubAuthorities = RtlLengthRequiredSid( 3 );
+
+ //
+ // Allocate and initialize the universal SIDs
+ //
+
+ SeNullSid = (PSID)ExAllocatePoolWithTag(PagedPool,SidWithOneSubAuthority,'iSeS');
+ SeWorldSid = (PSID)ExAllocatePoolWithTag(PagedPool,SidWithOneSubAuthority,'iSeS');
+ SeLocalSid = (PSID)ExAllocatePoolWithTag(PagedPool,SidWithOneSubAuthority,'iSeS');
+ SeCreatorOwnerSid = (PSID)ExAllocatePoolWithTag(PagedPool,SidWithOneSubAuthority,'iSeS');
+ SeCreatorGroupSid = (PSID)ExAllocatePoolWithTag(PagedPool,SidWithOneSubAuthority,'iSeS');
+ SeCreatorOwnerServerSid = (PSID)ExAllocatePoolWithTag(PagedPool,SidWithOneSubAuthority,'iSeS');
+ SeCreatorGroupServerSid = (PSID)ExAllocatePoolWithTag(PagedPool,SidWithOneSubAuthority,'iSeS');
+
+ //
+ // Fail initialization if we didn't get enough memory for the universal
+ // SIDs.
+ //
+
+ if ( (SeNullSid == NULL) ||
+ (SeWorldSid == NULL) ||
+ (SeLocalSid == NULL) ||
+ (SeCreatorOwnerSid == NULL) ||
+ (SeCreatorGroupSid == NULL) ||
+ (SeCreatorOwnerServerSid == NULL ) ||
+ (SeCreatorGroupServerSid == NULL )
+ ) {
+
+ return( FALSE );
+ }
+
+ RtlInitializeSid( SeNullSid, &NullSidAuthority, 1 );
+ RtlInitializeSid( SeWorldSid, &WorldSidAuthority, 1 );
+ RtlInitializeSid( SeLocalSid, &LocalSidAuthority, 1 );
+ RtlInitializeSid( SeCreatorOwnerSid, &CreatorSidAuthority, 1 );
+ RtlInitializeSid( SeCreatorGroupSid, &CreatorSidAuthority, 1 );
+ RtlInitializeSid( SeCreatorOwnerServerSid, &CreatorSidAuthority, 1 );
+ RtlInitializeSid( SeCreatorGroupServerSid, &CreatorSidAuthority, 1 );
+
+ *(RtlSubAuthoritySid( SeNullSid, 0 )) = SECURITY_NULL_RID;
+ *(RtlSubAuthoritySid( SeWorldSid, 0 )) = SECURITY_WORLD_RID;
+ *(RtlSubAuthoritySid( SeLocalSid, 0 )) = SECURITY_LOCAL_RID;
+ *(RtlSubAuthoritySid( SeCreatorOwnerSid, 0 )) = SECURITY_CREATOR_OWNER_RID;
+ *(RtlSubAuthoritySid( SeCreatorGroupSid, 0 )) = SECURITY_CREATOR_GROUP_RID;
+ *(RtlSubAuthoritySid( SeCreatorOwnerServerSid, 0 )) = SECURITY_CREATOR_OWNER_SERVER_RID;
+ *(RtlSubAuthoritySid( SeCreatorGroupServerSid, 0 )) = SECURITY_CREATOR_GROUP_SERVER_RID;
+
+ //
+ // Allocate and initialize the NT defined SIDs
+ //
+
+ SeNtAuthoritySid = (PSID)ExAllocatePoolWithTag(PagedPool,SidWithZeroSubAuthorities,'iSeS');
+ SeDialupSid = (PSID)ExAllocatePoolWithTag(PagedPool,SidWithOneSubAuthority,'iSeS');
+ SeNetworkSid = (PSID)ExAllocatePoolWithTag(PagedPool,SidWithOneSubAuthority,'iSeS');
+ SeBatchSid = (PSID)ExAllocatePoolWithTag(PagedPool,SidWithOneSubAuthority,'iSeS');
+ SeInteractiveSid = (PSID)ExAllocatePoolWithTag(PagedPool,SidWithOneSubAuthority,'iSeS');
+ SeServiceSid = (PSID)ExAllocatePoolWithTag(PagedPool,SidWithOneSubAuthority,'iSeS');
+ SeLocalSystemSid = (PSID)ExAllocatePoolWithTag(PagedPool,SidWithOneSubAuthority,'iSeS');
+
+ SeAliasAdminsSid = (PSID)ExAllocatePoolWithTag(PagedPool,SidWithTwoSubAuthorities,'iSeS');
+ SeAliasUsersSid = (PSID)ExAllocatePoolWithTag(PagedPool,SidWithTwoSubAuthorities,'iSeS');
+ SeAliasGuestsSid = (PSID)ExAllocatePoolWithTag(PagedPool,SidWithTwoSubAuthorities,'iSeS');
+ SeAliasPowerUsersSid = (PSID)ExAllocatePoolWithTag(PagedPool,SidWithTwoSubAuthorities,'iSeS');
+ SeAliasAccountOpsSid = (PSID)ExAllocatePoolWithTag(PagedPool,SidWithTwoSubAuthorities,'iSeS');
+ SeAliasSystemOpsSid = (PSID)ExAllocatePoolWithTag(PagedPool,SidWithTwoSubAuthorities,'iSeS');
+ SeAliasPrintOpsSid = (PSID)ExAllocatePoolWithTag(PagedPool,SidWithTwoSubAuthorities,'iSeS');
+ SeAliasBackupOpsSid = (PSID)ExAllocatePoolWithTag(PagedPool,SidWithTwoSubAuthorities,'iSeS');
+
+ //
+ // Fail initialization if we didn't get enough memory for the NT SIDs.
+ //
+
+ if ( (SeNtAuthoritySid == NULL) ||
+ (SeDialupSid == NULL) ||
+ (SeNetworkSid == NULL) ||
+ (SeBatchSid == NULL) ||
+ (SeInteractiveSid == NULL) ||
+ (SeServiceSid == NULL) ||
+ (SeLocalSystemSid == NULL) ||
+ (SeAliasAdminsSid == NULL) ||
+ (SeAliasUsersSid == NULL) ||
+ (SeAliasGuestsSid == NULL) ||
+ (SeAliasPowerUsersSid == NULL) ||
+ (SeAliasAccountOpsSid == NULL) ||
+ (SeAliasSystemOpsSid == NULL) ||
+ (SeAliasPrintOpsSid == NULL) ||
+ (SeAliasBackupOpsSid == NULL)
+ ) {
+
+ return( FALSE );
+ }
+
+ RtlInitializeSid( SeNtAuthoritySid, &SeNtAuthority, 0 );
+ RtlInitializeSid( SeDialupSid, &SeNtAuthority, 1 );
+ RtlInitializeSid( SeNetworkSid, &SeNtAuthority, 1 );
+ RtlInitializeSid( SeBatchSid, &SeNtAuthority, 1 );
+ RtlInitializeSid( SeInteractiveSid, &SeNtAuthority, 1 );
+ RtlInitializeSid( SeServiceSid, &SeNtAuthority, 1 );
+ RtlInitializeSid( SeLocalSystemSid, &SeNtAuthority, 1 );
+
+ RtlInitializeSid( SeAliasAdminsSid, &SeNtAuthority, 2);
+ RtlInitializeSid( SeAliasUsersSid, &SeNtAuthority, 2);
+ RtlInitializeSid( SeAliasGuestsSid, &SeNtAuthority, 2);
+ RtlInitializeSid( SeAliasPowerUsersSid, &SeNtAuthority, 2);
+ RtlInitializeSid( SeAliasAccountOpsSid, &SeNtAuthority, 2);
+ RtlInitializeSid( SeAliasSystemOpsSid, &SeNtAuthority, 2);
+ RtlInitializeSid( SeAliasPrintOpsSid, &SeNtAuthority, 2);
+ RtlInitializeSid( SeAliasBackupOpsSid, &SeNtAuthority, 2);
+
+ *(RtlSubAuthoritySid( SeDialupSid, 0 )) = SECURITY_DIALUP_RID;
+ *(RtlSubAuthoritySid( SeNetworkSid, 0 )) = SECURITY_NETWORK_RID;
+ *(RtlSubAuthoritySid( SeBatchSid, 0 )) = SECURITY_BATCH_RID;
+ *(RtlSubAuthoritySid( SeInteractiveSid, 0 )) = SECURITY_INTERACTIVE_RID;
+ *(RtlSubAuthoritySid( SeServiceSid, 0 )) = SECURITY_SERVICE_RID;
+ *(RtlSubAuthoritySid( SeLocalSystemSid, 0 )) = SECURITY_LOCAL_SYSTEM_RID;
+
+
+ *(RtlSubAuthoritySid( SeAliasAdminsSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID;
+ *(RtlSubAuthoritySid( SeAliasUsersSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID;
+ *(RtlSubAuthoritySid( SeAliasGuestsSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID;
+ *(RtlSubAuthoritySid( SeAliasPowerUsersSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID;
+ *(RtlSubAuthoritySid( SeAliasAccountOpsSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID;
+ *(RtlSubAuthoritySid( SeAliasSystemOpsSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID;
+ *(RtlSubAuthoritySid( SeAliasPrintOpsSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID;
+ *(RtlSubAuthoritySid( SeAliasBackupOpsSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID;
+
+ *(RtlSubAuthoritySid( SeAliasAdminsSid, 1 )) = DOMAIN_ALIAS_RID_ADMINS;
+ *(RtlSubAuthoritySid( SeAliasUsersSid, 1 )) = DOMAIN_ALIAS_RID_USERS;
+ *(RtlSubAuthoritySid( SeAliasGuestsSid, 1 )) = DOMAIN_ALIAS_RID_GUESTS;
+ *(RtlSubAuthoritySid( SeAliasPowerUsersSid, 1 )) = DOMAIN_ALIAS_RID_POWER_USERS;
+ *(RtlSubAuthoritySid( SeAliasAccountOpsSid, 1 )) = DOMAIN_ALIAS_RID_ACCOUNT_OPS;
+ *(RtlSubAuthoritySid( SeAliasSystemOpsSid, 1 )) = DOMAIN_ALIAS_RID_SYSTEM_OPS;
+ *(RtlSubAuthoritySid( SeAliasPrintOpsSid, 1 )) = DOMAIN_ALIAS_RID_PRINT_OPS;
+ *(RtlSubAuthoritySid( SeAliasBackupOpsSid, 1 )) = DOMAIN_ALIAS_RID_BACKUP_OPS;
+
+
+
+ //
+ // Initialize system default dacl
+ //
+
+ SepInitSystemDacls();
+
+
+ //
+ // Initialize the well known privilege values
+ //
+
+ SeCreateTokenPrivilege =
+ RtlConvertLongToLuid(SE_CREATE_TOKEN_PRIVILEGE);
+ SeAssignPrimaryTokenPrivilege =
+ RtlConvertLongToLuid(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE);
+ SeLockMemoryPrivilege =
+ RtlConvertLongToLuid(SE_LOCK_MEMORY_PRIVILEGE);
+ SeIncreaseQuotaPrivilege =
+ RtlConvertLongToLuid(SE_INCREASE_QUOTA_PRIVILEGE);
+ SeUnsolicitedInputPrivilege =
+ RtlConvertLongToLuid(SE_UNSOLICITED_INPUT_PRIVILEGE);
+ SeTcbPrivilege =
+ RtlConvertLongToLuid(SE_TCB_PRIVILEGE);
+ SeSecurityPrivilege =
+ RtlConvertLongToLuid(SE_SECURITY_PRIVILEGE);
+ SeTakeOwnershipPrivilege =
+ RtlConvertLongToLuid(SE_TAKE_OWNERSHIP_PRIVILEGE);
+ SeLoadDriverPrivilege =
+ RtlConvertLongToLuid(SE_LOAD_DRIVER_PRIVILEGE);
+ SeCreatePagefilePrivilege =
+ RtlConvertLongToLuid(SE_CREATE_PAGEFILE_PRIVILEGE);
+ SeIncreaseBasePriorityPrivilege =
+ RtlConvertLongToLuid(SE_INC_BASE_PRIORITY_PRIVILEGE);
+ SeSystemProfilePrivilege =
+ RtlConvertLongToLuid(SE_SYSTEM_PROFILE_PRIVILEGE);
+ SeSystemtimePrivilege =
+ RtlConvertLongToLuid(SE_SYSTEMTIME_PRIVILEGE);
+ SeProfileSingleProcessPrivilege =
+ RtlConvertLongToLuid(SE_PROF_SINGLE_PROCESS_PRIVILEGE);
+ SeCreatePermanentPrivilege =
+ RtlConvertLongToLuid(SE_CREATE_PERMANENT_PRIVILEGE);
+ SeBackupPrivilege =
+ RtlConvertLongToLuid(SE_BACKUP_PRIVILEGE);
+ SeRestorePrivilege =
+ RtlConvertLongToLuid(SE_RESTORE_PRIVILEGE);
+ SeShutdownPrivilege =
+ RtlConvertLongToLuid(SE_SHUTDOWN_PRIVILEGE);
+ SeDebugPrivilege =
+ RtlConvertLongToLuid(SE_DEBUG_PRIVILEGE);
+ SeAuditPrivilege =
+ RtlConvertLongToLuid(SE_AUDIT_PRIVILEGE);
+ SeSystemEnvironmentPrivilege =
+ RtlConvertLongToLuid(SE_SYSTEM_ENVIRONMENT_PRIVILEGE);
+ SeChangeNotifyPrivilege =
+ RtlConvertLongToLuid(SE_CHANGE_NOTIFY_PRIVILEGE);
+ SeRemoteShutdownPrivilege =
+ RtlConvertLongToLuid(SE_REMOTE_SHUTDOWN_PRIVILEGE);
+
+
+
+ //
+ // Initialize the SeExports structure for exporting all
+ // of the information we've created out of the kernel.
+ //
+
+ //
+ // Package these together for export
+ //
+
+
+ SepExports.SeNullSid = SeNullSid;
+ SepExports.SeWorldSid = SeWorldSid;
+ SepExports.SeLocalSid = SeLocalSid;
+ SepExports.SeCreatorOwnerSid = SeCreatorOwnerSid;
+ SepExports.SeCreatorGroupSid = SeCreatorGroupSid;
+
+
+ SepExports.SeNtAuthoritySid = SeNtAuthoritySid;
+ SepExports.SeDialupSid = SeDialupSid;
+ SepExports.SeNetworkSid = SeNetworkSid;
+ SepExports.SeBatchSid = SeBatchSid;
+ SepExports.SeInteractiveSid = SeInteractiveSid;
+ SepExports.SeLocalSystemSid = SeLocalSystemSid;
+ SepExports.SeAliasAdminsSid = SeAliasAdminsSid;
+ SepExports.SeAliasUsersSid = SeAliasUsersSid;
+ SepExports.SeAliasGuestsSid = SeAliasGuestsSid;
+ SepExports.SeAliasPowerUsersSid = SeAliasPowerUsersSid;
+ SepExports.SeAliasAccountOpsSid = SeAliasAccountOpsSid;
+ SepExports.SeAliasSystemOpsSid = SeAliasSystemOpsSid;
+ SepExports.SeAliasPrintOpsSid = SeAliasPrintOpsSid;
+ SepExports.SeAliasBackupOpsSid = SeAliasBackupOpsSid;
+
+
+
+ SepExports.SeCreateTokenPrivilege = SeCreateTokenPrivilege;
+ SepExports.SeAssignPrimaryTokenPrivilege = SeAssignPrimaryTokenPrivilege;
+ SepExports.SeLockMemoryPrivilege = SeLockMemoryPrivilege;
+ SepExports.SeIncreaseQuotaPrivilege = SeIncreaseQuotaPrivilege;
+ SepExports.SeUnsolicitedInputPrivilege = SeUnsolicitedInputPrivilege;
+ SepExports.SeTcbPrivilege = SeTcbPrivilege;
+ SepExports.SeSecurityPrivilege = SeSecurityPrivilege;
+ SepExports.SeTakeOwnershipPrivilege = SeTakeOwnershipPrivilege;
+ SepExports.SeLoadDriverPrivilege = SeLoadDriverPrivilege;
+ SepExports.SeCreatePagefilePrivilege = SeCreatePagefilePrivilege;
+ SepExports.SeIncreaseBasePriorityPrivilege = SeIncreaseBasePriorityPrivilege;
+ SepExports.SeSystemProfilePrivilege = SeSystemProfilePrivilege;
+ SepExports.SeSystemtimePrivilege = SeSystemtimePrivilege;
+ SepExports.SeProfileSingleProcessPrivilege = SeProfileSingleProcessPrivilege;
+ SepExports.SeCreatePermanentPrivilege = SeCreatePermanentPrivilege;
+ SepExports.SeBackupPrivilege = SeBackupPrivilege;
+ SepExports.SeRestorePrivilege = SeRestorePrivilege;
+ SepExports.SeShutdownPrivilege = SeShutdownPrivilege;
+ SepExports.SeDebugPrivilege = SeDebugPrivilege;
+ SepExports.SeAuditPrivilege = SeAuditPrivilege;
+ SepExports.SeSystemEnvironmentPrivilege = SeSystemEnvironmentPrivilege;
+ SepExports.SeChangeNotifyPrivilege = SeChangeNotifyPrivilege;
+ SepExports.SeRemoteShutdownPrivilege = SeRemoteShutdownPrivilege;
+
+ SeExports = &SepExports;
+
+ //
+ // Initialize frequently used privilege sets to speed up access
+ // validation.
+ //
+
+ SepInitializePrivilegeSets();
+
+ return TRUE;
+
+}
+
+VOID
+SepInitSystemDacls( VOID )
+/*++
+
+Routine Description:
+
+ This function initializes the system's default dacls & security
+ descriptors.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+
+--*/
+{
+
+ NTSTATUS
+ Status;
+
+ ULONG
+ PublicLength,
+ SystemLength,
+ PublicOpenLength;
+
+
+ PAGED_CODE();
+
+ //
+ // Set up a default ACLs
+ //
+ // Public: WORLD:execute, SYSTEM:all, ADMINS:all
+ // Public Open: WORLD:(Read|Write|Execute), ADMINS:(all), SYSTEM:all
+ // System: SYSTEM:all, ADMINS:(read|execute|read_control)
+
+ SystemLength = (ULONG)sizeof(ACL) +
+ (2*((ULONG)sizeof(ACCESS_ALLOWED_ACE))) +
+ SeLengthSid( SeLocalSystemSid ) +
+ SeLengthSid( SeAliasAdminsSid ) +
+ 8; // The 8 is just for good measure
+
+ PublicLength = SystemLength +
+ ((ULONG)sizeof(ACCESS_ALLOWED_ACE)) +
+ SeLengthSid( SeWorldSid );
+
+ PublicOpenLength = PublicLength;
+
+
+ SePublicDefaultDacl = (PACL)ExAllocatePoolWithTag(PagedPool, PublicLength, 'cAeS');
+ SePublicOpenDacl = (PACL)ExAllocatePoolWithTag(PagedPool, PublicOpenLength, 'cAeS');
+ SeSystemDefaultDacl = (PACL)ExAllocatePoolWithTag(PagedPool, SystemLength, 'cAeS');
+ ASSERT(SePublicDefaultDacl != NULL);
+ ASSERT(SePublicOpenDacl != NULL);
+ ASSERT(SeSystemDefaultDacl != NULL);
+
+
+
+ Status = RtlCreateAcl( SePublicDefaultDacl, PublicLength, ACL_REVISION2);
+ ASSERT( NT_SUCCESS(Status) );
+ Status = RtlCreateAcl( SePublicOpenDacl, PublicOpenLength, ACL_REVISION2);
+ ASSERT( NT_SUCCESS(Status) );
+ Status = RtlCreateAcl( SeSystemDefaultDacl, SystemLength, ACL_REVISION2);
+ ASSERT( NT_SUCCESS(Status) );
+
+
+ //
+ // WORLD access (Public DACLs only)
+ //
+
+ Status = RtlAddAccessAllowedAce (
+ SePublicDefaultDacl,
+ ACL_REVISION2,
+ GENERIC_EXECUTE,
+ SeWorldSid
+ );
+ ASSERT( NT_SUCCESS(Status) );
+
+
+ Status = RtlAddAccessAllowedAce (
+ SePublicOpenDacl,
+ ACL_REVISION2,
+ (GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE),
+ SeWorldSid
+ );
+ ASSERT( NT_SUCCESS(Status) );
+
+
+
+ //
+ // SYSTEM access (PublicDefault, PublicOpen, and SystemDefault)
+ //
+
+
+ Status = RtlAddAccessAllowedAce (
+ SePublicDefaultDacl,
+ ACL_REVISION2,
+ GENERIC_ALL,
+ SeLocalSystemSid
+ );
+ ASSERT( NT_SUCCESS(Status) );
+
+
+ Status = RtlAddAccessAllowedAce (
+ SePublicOpenDacl,
+ ACL_REVISION2,
+ GENERIC_ALL,
+ SeLocalSystemSid
+ );
+ ASSERT( NT_SUCCESS(Status) );
+
+
+ Status = RtlAddAccessAllowedAce (
+ SeSystemDefaultDacl,
+ ACL_REVISION2,
+ GENERIC_ALL,
+ SeLocalSystemSid
+ );
+ ASSERT( NT_SUCCESS(Status) );
+
+ //
+ // ADMINISTRATORS access (PublicDefault, PublicOpen, and SystemDefault)
+ //
+
+ Status = RtlAddAccessAllowedAce (
+ SePublicDefaultDacl,
+ ACL_REVISION2,
+ GENERIC_ALL,
+ SeAliasAdminsSid
+ );
+ ASSERT( NT_SUCCESS(Status) );
+
+
+ Status = RtlAddAccessAllowedAce (
+ SePublicOpenDacl,
+ ACL_REVISION2,
+ GENERIC_ALL,
+ SeAliasAdminsSid
+ );
+ ASSERT( NT_SUCCESS(Status) );
+
+
+ Status = RtlAddAccessAllowedAce (
+ SeSystemDefaultDacl,
+ ACL_REVISION2,
+ GENERIC_READ | GENERIC_EXECUTE | READ_CONTROL,
+ SeAliasAdminsSid
+ );
+ ASSERT( NT_SUCCESS(Status) );
+
+
+
+ //
+ // Now initialize security descriptors
+ // that export this protection
+ //
+
+
+ SePublicDefaultSd = (PSECURITY_DESCRIPTOR)&SepPublicDefaultSd;
+ Status = RtlCreateSecurityDescriptor(
+ SePublicDefaultSd,
+ SECURITY_DESCRIPTOR_REVISION1
+ );
+ ASSERT( NT_SUCCESS(Status) );
+ Status = RtlSetDaclSecurityDescriptor(
+ SePublicDefaultSd,
+ TRUE, // DaclPresent
+ SePublicDefaultDacl,
+ FALSE // DaclDefaulted
+ );
+ ASSERT( NT_SUCCESS(Status) );
+
+
+ SePublicOpenSd = (PSECURITY_DESCRIPTOR)&SepPublicOpenSd;
+ Status = RtlCreateSecurityDescriptor(
+ SePublicOpenSd,
+ SECURITY_DESCRIPTOR_REVISION1
+ );
+ ASSERT( NT_SUCCESS(Status) );
+ Status = RtlSetDaclSecurityDescriptor(
+ SePublicOpenSd,
+ TRUE, // DaclPresent
+ SePublicOpenDacl,
+ FALSE // DaclDefaulted
+ );
+ ASSERT( NT_SUCCESS(Status) );
+
+
+ SeSystemDefaultSd = (PSECURITY_DESCRIPTOR)&SepSystemDefaultSd;
+ Status = RtlCreateSecurityDescriptor(
+ SeSystemDefaultSd,
+ SECURITY_DESCRIPTOR_REVISION1
+ );
+ ASSERT( NT_SUCCESS(Status) );
+ Status = RtlSetDaclSecurityDescriptor(
+ SeSystemDefaultSd,
+ TRUE, // DaclPresent
+ SeSystemDefaultDacl,
+ FALSE // DaclDefaulted
+ );
+ ASSERT( NT_SUCCESS(Status) );
+
+
+ return;
+
+}
+
+
+VOID
+SepInitializePrivilegeSets( VOID )
+/*++
+
+Routine Description:
+
+ This routine is called once during system initialization to pre-allocate
+ and initialize some commonly used privilege sets.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PAGED_CODE();
+
+ SinglePrivilegeSetSize = sizeof( PRIVILEGE_SET );
+ DoublePrivilegeSetSize = sizeof( PRIVILEGE_SET ) +
+ (ULONG)sizeof( LUID_AND_ATTRIBUTES );
+
+ SepSystemSecurityPrivilegeSet = ExAllocatePoolWithTag( PagedPool, SinglePrivilegeSetSize, 'rPeS' );
+ SepTakeOwnershipPrivilegeSet = ExAllocatePoolWithTag( PagedPool, SinglePrivilegeSetSize, 'rPeS' );
+ SepDoublePrivilegeSet = ExAllocatePoolWithTag( PagedPool, DoublePrivilegeSetSize, 'rPeS' );
+
+ SepSystemSecurityPrivilegeSet->PrivilegeCount = 1;
+ SepSystemSecurityPrivilegeSet->Control = 0;
+ SepSystemSecurityPrivilegeSet->Privilege[0].Luid = SeSecurityPrivilege;
+ SepSystemSecurityPrivilegeSet->Privilege[0].Attributes = SE_PRIVILEGE_USED_FOR_ACCESS;
+
+ SepTakeOwnershipPrivilegeSet->PrivilegeCount = 1;
+ SepTakeOwnershipPrivilegeSet->Control = 0;
+ SepTakeOwnershipPrivilegeSet->Privilege[0].Luid = SeTakeOwnershipPrivilege;
+ SepTakeOwnershipPrivilegeSet->Privilege[0].Attributes = SE_PRIVILEGE_USED_FOR_ACCESS;
+
+ SepDoublePrivilegeSet->PrivilegeCount = 2;
+ SepDoublePrivilegeSet->Control = 0;
+
+ SepDoublePrivilegeSet->Privilege[0].Luid = SeSecurityPrivilege;
+ SepDoublePrivilegeSet->Privilege[0].Attributes = SE_PRIVILEGE_USED_FOR_ACCESS;
+
+ SepDoublePrivilegeSet->Privilege[1].Luid = SeTakeOwnershipPrivilege;
+ SepDoublePrivilegeSet->Privilege[1].Attributes = SE_PRIVILEGE_USED_FOR_ACCESS;
+
+}
+
+
+
+VOID
+SepAssemblePrivileges(
+ IN ULONG PrivilegeCount,
+ IN BOOLEAN SystemSecurity,
+ IN BOOLEAN WriteOwner,
+ OUT PPRIVILEGE_SET *Privileges
+ )
+/*++
+
+Routine Description:
+
+ This routine takes the results of the various privilege checks
+ in SeAccessCheck and returns an appropriate privilege set.
+
+Arguments:
+
+ PrivilegeCount - The number of privileges granted.
+
+ SystemSecurity - Provides a boolean indicating whether to put
+ SeSecurityPrivilege into the output privilege set.
+
+ WriteOwner - Provides a boolean indicating whether to put
+ SeTakeOwnershipPrivilege into the output privilege set.
+
+ Privileges - Supplies a pointer that will return the privilege
+ set. Should be freed with ExFreePool when no longer needed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PPRIVILEGE_SET PrivilegeSet;
+ ULONG SizeRequired;
+
+ PAGED_CODE();
+
+ ASSERT( (PrivilegeCount != 0) && (PrivilegeCount <= 2) );
+
+ if ( !ARGUMENT_PRESENT( Privileges ) ) {
+ return;
+ }
+
+ if ( PrivilegeCount == 1 ) {
+
+ SizeRequired = SinglePrivilegeSetSize;
+
+ if ( SystemSecurity ) {
+
+ PrivilegeSet = SepSystemSecurityPrivilegeSet;
+
+ } else {
+
+ ASSERT( WriteOwner );
+
+ PrivilegeSet = SepTakeOwnershipPrivilegeSet;
+ }
+
+ } else {
+
+ SizeRequired = DoublePrivilegeSetSize;
+ PrivilegeSet = SepDoublePrivilegeSet;
+ }
+
+ *Privileges = ExAllocatePoolWithTag( PagedPool, SizeRequired, 'rPeS' );
+
+ if ( *Privileges != NULL ) {
+
+ RtlMoveMemory (
+ *Privileges,
+ PrivilegeSet,
+ SizeRequired
+ );
+ }
+}
+
+
+
+
+
+BOOLEAN
+SepInitializeWorkList(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Initializes the mutex and list head used to queue work from the
+ Executive to LSA. This mechanism operates on top of the normal ExWorkerThread
+ mechanism by capturing the first thread to perform LSA work and keeping it
+ until all the current work is done.
+
+ The reduces the number of worker threads that are blocked on I/O to LSA.
+
+Arguments:
+
+ None.
+
+
+Return Value:
+
+ TRUE if successful, FALSE otherwise.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ ExInitializeResource(&SepLsaQueueLock);
+ InitializeListHead(&SepLsaQueue);
+ return( TRUE );
+}
diff --git a/private/ntos/se/seinit.c b/private/ntos/se/seinit.c
new file mode 100644
index 000000000..0855e5eed
--- /dev/null
+++ b/private/ntos/se/seinit.c
@@ -0,0 +1,303 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ seinit.c
+
+Abstract:
+
+ Executive security components Initialization.
+
+Author:
+
+ Jim Kelly (JimK) 10-May-1990
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include "sep.h"
+#include "tokenp.h"
+#include "adt.h"
+#include <string.h>
+
+//
+// Security Database Constants
+//
+
+#define SEP_INITIAL_KEY_COUNT 15
+#define SEP_INITIAL_LEVEL_COUNT 6L
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(INIT,SeInitSystem)
+#pragma alloc_text(INIT,SepInitializationPhase0)
+#pragma alloc_text(INIT,SepInitializationPhase1)
+#endif
+
+BOOLEAN
+SeInitSystem( VOID )
+
+/*++
+
+Routine Description:
+
+ Perform security related system initialization functions.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ TRUE - Initialization succeeded.
+
+ FALSE - Initialization failed.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ switch ( InitializationPhase ) {
+
+ case 0 :
+ return SepInitializationPhase0();
+ case 1 :
+ return SepInitializationPhase1();
+ default:
+ KeBugCheck(UNEXPECTED_INITIALIZATION_CALL);
+ }
+}
+
+
+BOOLEAN
+SepInitializationPhase0( VOID )
+
+/*++
+
+Routine Description:
+
+ Perform phase 0 security initialization.
+
+ This includes:
+
+ - Initialize LUID allocation
+ - Initialize security global variables
+ - initialize the token object.
+ - Initialize the necessary security components of the boot thread/process
+
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ TRUE - Initialization was successful.
+
+ FALSE - Initialization Failed.
+
+--*/
+
+{
+
+ PAGED_CODE();
+
+ //
+ // LUID allocation services are needed by security prior to phase 0
+ // Executive initialization. So, LUID initialization is performed
+ // here
+ //
+
+ if (ExLuidInitialization() == FALSE) {
+ KdPrint(("Security: Locally Unique ID initialization failed.\n"));
+ return FALSE;
+ }
+
+ //
+ // Initialize security global variables
+ //
+
+ if (!SepVariableInitialization()) {
+ KdPrint(("Security: Global variable initialization failed.\n"));
+ return FALSE;
+ }
+
+ //
+ // Perform Phase 0 Reference Monitor Initialization.
+ //
+
+ if (!SepRmInitPhase0()) {
+ KdPrint(("Security: Ref Mon state initialization failed.\n"));
+ return FALSE;
+ }
+
+ //
+ // Initialize the token object type.
+ //
+
+ if (!SepTokenInitialization()) {
+ KdPrint(("Security: Token object initialization failed.\n"));
+ return FALSE;
+ }
+
+// //
+// // Initialize auditing structures
+// //
+//
+// if (!SepAdtInitializePhase0()) {
+// KdPrint(("Security: Auditing initialization failed.\n"));
+// return FALSE;
+// }
+//
+ //
+ // Initialize SpinLock and list for the LSA worker thread
+ //
+
+ //
+ // Initialize the work queue spinlock, list head, and semaphore
+ // for each of the work queues.
+ //
+
+ if (!SepInitializeWorkList()) {
+ KdPrint(("Security: Unable to initialize work queue\n"));
+ return FALSE;
+ }
+
+ //
+ // Initialize the security fields of the boot thread.
+ //
+
+ PsGetCurrentProcess()->Token = SeMakeSystemToken();
+ PsGetCurrentThread()->ImpersonationInfo = NULL;
+ PsGetCurrentThread()->ActiveImpersonationInfo = FALSE;
+
+ return TRUE;
+}
+
+
+BOOLEAN
+SepInitializationPhase1( VOID )
+
+/*++
+
+Routine Description:
+
+ Perform phase 1 security initialization.
+
+ This includes:
+
+ - Create an object directory for security related objects.
+ (\Security).
+
+ - Create an event to be signalled after the LSA has initialized.
+ (\Security\LSA_Initialized)
+
+
+
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ TRUE - Initialization was successful.
+
+ FALSE - Initialization Failed.
+
+--*/
+
+{
+
+ NTSTATUS Status;
+ STRING Name;
+ UNICODE_STRING UnicodeName;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ HANDLE SecurityRoot, TemporaryHandle;
+
+ PAGED_CODE();
+
+ //
+ // Create the security object directory.
+ //
+
+ RtlInitString( &Name, "\\Security" );
+ Status = RtlAnsiStringToUnicodeString(
+ &UnicodeName,
+ &Name,
+ TRUE ); ASSERT( NT_SUCCESS(Status) );
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &UnicodeName,
+ (OBJ_PERMANENT | OBJ_CASE_INSENSITIVE),
+ NULL,
+ NULL
+ );
+
+ Status = NtCreateDirectoryObject(
+ &SecurityRoot,
+ DIRECTORY_ALL_ACCESS,
+ &ObjectAttributes
+ );
+ RtlFreeUnicodeString( &UnicodeName );
+ ASSERTMSG("Security root object directory creation failed.",NT_SUCCESS(Status));
+
+
+
+ //
+ // Create an event in the security directory
+ //
+
+ RtlInitString( &Name, "LSA_AUTHENTICATION_INITIALIZED" );
+ Status = RtlAnsiStringToUnicodeString(
+ &UnicodeName,
+ &Name,
+ TRUE ); ASSERT( NT_SUCCESS(Status) );
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &UnicodeName,
+ (OBJ_PERMANENT | OBJ_CASE_INSENSITIVE),
+ SecurityRoot,
+ SePublicDefaultSd
+ );
+
+ Status = NtCreateEvent(
+ &TemporaryHandle,
+ GENERIC_WRITE,
+ &ObjectAttributes,
+ NotificationEvent,
+ FALSE
+ );
+ RtlFreeUnicodeString( &UnicodeName );
+ ASSERTMSG("LSA Initialization Event Creation Failed.",NT_SUCCESS(Status));
+
+ Status = NtClose( SecurityRoot );
+ ASSERTMSG("Security object directory handle closure Failed.",NT_SUCCESS(Status));
+ Status = NtClose( TemporaryHandle );
+ ASSERTMSG("LSA Initialization Event handle closure Failed.",NT_SUCCESS(Status));
+
+ //
+ // Initialize auditing structures
+ //
+
+ if (!SepAdtInitializePhase1()) {
+ KdPrint(("Security: Auditing initialization failed.\n"));
+ return FALSE;
+ }
+
+
+#ifndef SETEST
+
+ return TRUE;
+
+#else
+
+ return SepDevelopmentTest();
+
+#endif //SETEST
+
+}
diff --git a/private/ntos/se/semethod.c b/private/ntos/se/semethod.c
new file mode 100644
index 000000000..9033f4f58
--- /dev/null
+++ b/private/ntos/se/semethod.c
@@ -0,0 +1,1279 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ Semethod.c
+
+Abstract:
+
+ This Module implements the SeDefaultObjectMethod procedure. This
+ procedure and SeAssignSecurity are the only two procedures that will
+ place a security descriptor on an object. Therefore they must understand
+ and agree on how a descriptor is allocated from pool so that they can
+ deallocate and reallocate pool as necessary. Any security descriptor
+ that is attached to an object by these procedures has the following
+ pool allocation plan.
+
+ 1. if the objects security descriptor is null then there is no pool
+ allocated
+
+ 2. otherwise there is at least one pool allocation for the security
+ descriptor header. if it's acl fields are null then there are no
+ other pool allocations (this should never happen).
+
+ 3. There is a separate pool allocation for each acl in the descriptor.
+ So a maximum of three pool allocations can occur for each attached
+ security descriptor.
+
+ 4 Everytime an acl is replace in a descriptor we see if we can use
+ the old acl and if so then we try and keep the acl size as large
+ as possible.
+
+ Note that this is different from the algorithm used to capture
+ a security descriptor (which puts everything in one pool allocation).
+ Also note that this can be easily optimized at a later time (if necessary)
+ to use only one allocation.
+
+
+
+Author:
+
+ Gary Kimura (GaryKi) 9-Nov-1989
+ Jim Kelly (JimK) 10-May-1990
+
+Environment:
+
+ Kernel Mode
+
+Revision History:
+
+
+--*/
+
+#include "sep.h"
+
+NTSTATUS
+SepDefaultDeleteMethod (
+ IN OUT PSECURITY_DESCRIPTOR *ObjectsSecurityDescriptor
+ );
+
+
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE,SeSetSecurityAccessMask)
+#pragma alloc_text(PAGE,SeQuerySecurityAccessMask)
+#pragma alloc_text(PAGE,SeDefaultObjectMethod)
+#pragma alloc_text(PAGE,SeSetSecurityDescriptorInfo)
+#pragma alloc_text(PAGE,SeQuerySecurityDescriptorInfo)
+#pragma alloc_text(PAGE,SepDefaultDeleteMethod)
+#endif
+
+
+
+
+VOID
+SeSetSecurityAccessMask(
+ IN SECURITY_INFORMATION SecurityInformation,
+ OUT PACCESS_MASK DesiredAccess
+ )
+
+/*++
+
+Routine Description:
+
+ This routine builds an access mask representing the accesses necessary
+ to set the object security information specified in the SecurityInformation
+ parameter. While it is not difficult to determine this information,
+ the use of a single routine to generate it will ensure minimal impact
+ when the security information associated with an object is extended in
+ the future (to include mandatory access control information).
+
+Arguments:
+
+ SecurityInformation - Identifies the object's security information to be
+ modified.
+
+ DesiredAccess - Points to an access mask to be set to represent the
+ accesses necessary to modify the information specified in the
+ SecurityInformation parameter.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PAGED_CODE();
+
+ //
+ // Figure out accesses needed to perform the indicated operation(s).
+ //
+
+ (*DesiredAccess) = 0;
+
+ if ((SecurityInformation & OWNER_SECURITY_INFORMATION) ||
+ (SecurityInformation & GROUP_SECURITY_INFORMATION) ) {
+ (*DesiredAccess) |= WRITE_OWNER;
+ }
+
+ if (SecurityInformation & DACL_SECURITY_INFORMATION) {
+ (*DesiredAccess) |= WRITE_DAC;
+ }
+
+ if (SecurityInformation & SACL_SECURITY_INFORMATION) {
+ (*DesiredAccess) |= ACCESS_SYSTEM_SECURITY;
+ }
+
+ return;
+
+}
+
+
+VOID
+SeQuerySecurityAccessMask(
+ IN SECURITY_INFORMATION SecurityInformation,
+ OUT PACCESS_MASK DesiredAccess
+ )
+
+/*++
+
+Routine Description:
+
+ This routine builds an access mask representing the accesses necessary
+ to query the object security information specified in the
+ SecurityInformation parameter. While it is not difficult to determine
+ this information, the use of a single routine to generate it will ensure
+ minimal impact when the security information associated with an object is
+ extended in the future (to include mandatory access control information).
+
+Arguments:
+
+ SecurityInformation - Identifies the object's security information to be
+ queried.
+
+ DesiredAccess - Points to an access mask to be set to represent the
+ accesses necessary to query the information specified in the
+ SecurityInformation parameter.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ //
+ // Figure out accesses needed to perform the indicated operation(s).
+ //
+
+ (*DesiredAccess) = 0;
+
+ if ((SecurityInformation & OWNER_SECURITY_INFORMATION) ||
+ (SecurityInformation & GROUP_SECURITY_INFORMATION) ||
+ (SecurityInformation & DACL_SECURITY_INFORMATION)) {
+ (*DesiredAccess) |= READ_CONTROL;
+ }
+
+ if ((SecurityInformation & SACL_SECURITY_INFORMATION)) {
+ (*DesiredAccess) |= ACCESS_SYSTEM_SECURITY;
+ }
+
+ return;
+
+}
+
+
+
+NTSTATUS
+SeDefaultObjectMethod (
+ IN PVOID Object,
+ IN SECURITY_OPERATION_CODE OperationCode,
+ IN PSECURITY_INFORMATION SecurityInformation,
+ IN OUT PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN OUT PULONG CapturedLength,
+ IN OUT PSECURITY_DESCRIPTOR *ObjectsSecurityDescriptor,
+ IN POOL_TYPE PoolType,
+ IN PGENERIC_MAPPING GenericMapping
+ )
+
+/*++
+
+Routine Description:
+
+ This is the default security method for objects. It is responsible
+ for either retrieving, setting, and deleting the security descriptor of
+ an object. It is not used to assign the original security descriptor
+ to an object (use SeAssignSecurity for that purpose).
+
+
+ IT IS ASSUMED THAT THE OBJECT MANAGER HAS ALREADY DONE THE ACCESS
+ VALIDATIONS NECESSARY TO ALLOW THE REQUESTED OPERATIONS TO BE PERFORMED.
+
+Arguments:
+
+ Object - Supplies a pointer to the object being used.
+
+ OperationCode - Indicates if the operation is for setting, querying, or
+ deleting the object's security descriptor.
+
+ SecurityInformation - Indicates which security information is being
+ queried or set. This argument is ignored for the delete operation.
+
+ SecurityDescriptor - The meaning of this parameter depends on the
+ OperationCode:
+
+ QuerySecurityDescriptor - For the query operation this supplies the
+ buffer to copy the descriptor into. The security descriptor is
+ assumed to have been probed up to the size passed in in Length.
+ Since it still points into user space, it must always be
+ accessed in a try clause in case it should suddenly disappear.
+
+ SetSecurityDescriptor - For a set operation this supplies the
+ security descriptor to copy into the object. The security
+ descriptor must be captured before this routine is called.
+
+ DeleteSecurityDescriptor - It is ignored when deleting a security
+ descriptor.
+
+ AssignSecurityDescriptor - For assign operations this is the
+ security descriptor that will be assigned to the object.
+ It is assumed to be in kernel space, and is therefore not
+ probed or captured.
+
+ CapturedLength - For the query operation this specifies the length, in
+ bytes, of the security descriptor buffer, and upon return contains
+ the number of bytes needed to store the descriptor. If the length
+ needed is greater than the length supplied the operation will fail.
+ It is ignored in the set and delete operation.
+
+ This parameter is assumed to be captured and probed as appropriate.
+
+ ObjectsSecurityDescriptor - For the Set operation this supplies the address
+ of a pointer to the object's current security descriptor. This routine
+ will either modify the security descriptor in place or allocate a new
+ security descriptor and use this variable to indicate its new location.
+ For the query operation it simply supplies the security descriptor
+ being queried. The caller is responsible for freeing the old security
+ descriptor.
+
+ PoolType - For the set operation this specifies the pool type to use if
+ a new security descriptor needs to be allocated. It is ignored
+ in the query and delete operation.
+
+ the mapping of generic to specific/standard access types for the object
+ being accessed. This mapping structure is expected to be safe to
+ access (i.e., captured if necessary) prior to be passed to this routine.
+
+Return Value:
+
+ NTSTATUS - STATUS_SUCCESS if the operation is successful and an
+ appropriate error status otherwise.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ //
+ // If the object's security descriptor is null, then object is not
+ // one that has security information associated with it. Return
+ // an error.
+ //
+
+ //
+ // Make sure the common parts of our input are proper
+ //
+
+ ASSERT( (OperationCode == SetSecurityDescriptor) ||
+ (OperationCode == QuerySecurityDescriptor) ||
+ (OperationCode == AssignSecurityDescriptor) ||
+ (OperationCode == DeleteSecurityDescriptor) );
+
+ //
+ // This routine simply cases off of the operation code to decide
+ // which support routine to call
+ //
+
+ switch (OperationCode) {
+
+ case SetSecurityDescriptor:
+
+ ASSERT( (PoolType == PagedPool) || (PoolType == NonPagedPool) );
+
+ return ObSetSecurityDescriptorInfo( Object,
+ SecurityInformation,
+ SecurityDescriptor,
+ ObjectsSecurityDescriptor,
+ PoolType,
+ GenericMapping
+ );
+
+
+
+ case QuerySecurityDescriptor:
+
+ //
+ // check the rest of our input and call the default query security
+ // method
+ //
+
+ ASSERT( CapturedLength != NULL );
+
+ return SeQuerySecurityDescriptorInfo( SecurityInformation,
+ SecurityDescriptor,
+ CapturedLength,
+ ObjectsSecurityDescriptor );
+
+ case DeleteSecurityDescriptor:
+
+ //
+ // call the default delete security method
+ //
+
+ return SepDefaultDeleteMethod( ObjectsSecurityDescriptor );
+
+ case AssignSecurityDescriptor:
+
+ ObAssignObjectSecurityDescriptor( Object, SecurityDescriptor, PoolType );
+ return( STATUS_SUCCESS );
+
+ default:
+
+ //
+ // Bugcheck on any other operation code, We won't get here if
+ // the earlier asserts are still checked.
+ //
+
+ KeBugCheck( SECURITY_SYSTEM );
+
+ }
+
+}
+
+
+
+
+NTSTATUS
+SeSetSecurityDescriptorInfo (
+ IN PVOID Object OPTIONAL,
+ IN PSECURITY_INFORMATION SecurityInformation,
+ IN PSECURITY_DESCRIPTOR ModificationDescriptor,
+ IN OUT PSECURITY_DESCRIPTOR *ObjectsSecurityDescriptor,
+ IN POOL_TYPE PoolType,
+ IN PGENERIC_MAPPING GenericMapping
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will set an object's security descriptor. The input
+ security descriptor must be previously captured.
+
+Arguments:
+
+ Object - Optionally supplies the object whose security is
+ being adjusted. This is used to update security quota
+ information.
+
+ SecurityInformation - Indicates which security information is
+ to be applied to the object. The value(s) to be assigned are
+ passed in the SecurityDescriptor parameter.
+
+ ModificationDescriptor - Supplies the input security descriptor to be
+ applied to the object. The caller of this routine is expected
+ to probe and capture the passed security descriptor before calling
+ and release it after calling.
+
+ ObjectsSecurityDescriptor - Supplies the address of a pointer to
+ the objects security descriptor that is going to be altered by
+ this procedure. This structure must be deallocated by the caller.
+
+ PoolType - Specifies the type of pool to allocate for the objects
+ security descriptor.
+
+ GenericMapping - This argument provides the mapping of generic to
+ specific/standard access types for the object being accessed.
+ This mapping structure is expected to be safe to access
+ (i.e., captured if necessary) prior to be passed to this routine.
+
+Return Value:
+
+ NTSTATUS - STATUS_SUCCESS if successful and an appropriate error
+ value otherwise.
+
+--*/
+
+{
+ BOOLEAN NewGroupPresent = FALSE;
+ BOOLEAN NewSaclPresent = FALSE;
+ BOOLEAN NewDaclPresent = FALSE;
+ BOOLEAN NewOwnerPresent = FALSE;
+
+ PCHAR Field;
+ PCHAR Base;
+
+ SECURITY_DESCRIPTOR *NewDescriptor;
+
+ NTSTATUS Status;
+
+ PSID NewGroup;
+ PSID NewOwner;
+
+ PACL NewDacl;
+ PACL NewSacl;
+ PACL ServerDacl;
+
+ ULONG NewDaclSize;
+ ULONG NewSaclSize;
+ ULONG NewOwnerSize;
+ ULONG NewGroupSize;
+ ULONG AllocationSize;
+
+ SECURITY_SUBJECT_CONTEXT SubjectContext;
+
+ BOOLEAN ServerObject;
+ BOOLEAN DaclUntrusted;
+ BOOLEAN ServerDaclAllocated = FALSE;
+
+ PSID SubjectContextOwner;
+ PSID SubjectContextGroup;
+ PSID SubjectContextServerOwner;
+ PSID SubjectContextServerGroup;
+ PACL SubjectContextDacl;
+
+
+ //
+ // Typecast to internal representation of security descriptor.
+ // Note that the internal one is not a pointer to a pointer.
+ // It is just a pointer to a security descriptor.
+ //
+
+ SECURITY_DESCRIPTOR *IModificationDescriptor =
+ (SECURITY_DESCRIPTOR *)ModificationDescriptor;
+ SECURITY_DESCRIPTOR *IObjectsSecurityDescriptor =
+ (SECURITY_DESCRIPTOR *)*ObjectsSecurityDescriptor;
+
+ PAGED_CODE();
+
+ //
+ // Make sure the object already has a security descriptor.
+ // Objects that 'may' have security descriptors 'must' have security
+ // descriptors. If this one doesn't already have one, then we can't
+ // assign one to it.
+ //
+
+ if ((*ObjectsSecurityDescriptor) == NULL) {
+ return(STATUS_NO_SECURITY_ON_OBJECT);
+ }
+
+ ASSERT (IObjectsSecurityDescriptor != NULL);
+
+ //
+ // Validate that the provided SD is in self-relative form
+ //
+
+ if ( !SepAreControlBitsSet(IObjectsSecurityDescriptor, SE_SELF_RELATIVE) ) {
+ return( STATUS_BAD_DESCRIPTOR_FORMAT );
+ }
+
+ //
+ // Check to see if we need to edit the passed acl
+ // either because we're creating a server object, or because
+ // we were passed an untrusted ACL.
+ //
+
+ if ( SepAreControlBitsSet(IModificationDescriptor, SE_SERVER_SECURITY)) {
+ ServerObject = TRUE;
+ } else {
+ ServerObject = FALSE;
+ }
+
+ if ( SepAreControlBitsSet(IModificationDescriptor, SE_DACL_UNTRUSTED)) {
+ DaclUntrusted = TRUE;
+ } else {
+ DaclUntrusted = FALSE;
+ }
+
+ //
+ // The basic algorithm of setting a security descriptor is to
+ // figure out where each component of the object's resultant
+ // security descriptor is to come from: the original security
+ // descriptor, or the new one.
+ //
+
+
+ //
+ // Copy the system acl if specified
+ //
+
+ if (((*SecurityInformation) & SACL_SECURITY_INFORMATION)) {
+
+ NewSacl = SepSaclAddrSecurityDescriptor( IModificationDescriptor );
+ NewSaclPresent = TRUE;
+
+ } else {
+
+ NewSacl = SepSaclAddrSecurityDescriptor( IObjectsSecurityDescriptor );
+ }
+
+ //
+ // Copy the Discretionary acl if specified
+ //
+
+ if (((*SecurityInformation) & DACL_SECURITY_INFORMATION)) {
+
+ NewDacl = SepDaclAddrSecurityDescriptor( IModificationDescriptor );
+ NewDaclPresent = TRUE;
+
+ if (ServerObject) {
+
+ SeCaptureSubjectContext( &SubjectContext );
+
+ SepGetDefaultsSubjectContext(
+ &SubjectContext,
+ &SubjectContextOwner,
+ &SubjectContextGroup,
+ &SubjectContextServerOwner,
+ &SubjectContextServerGroup,
+ &SubjectContextDacl
+ );
+
+ Status = SepCreateServerAcl(
+ NewDacl,
+ DaclUntrusted,
+ SubjectContextServerOwner,
+ &ServerDacl,
+ &ServerDaclAllocated
+ );
+
+ SeReleaseSubjectContext( &SubjectContext );
+
+ if (!NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ NewDacl = ServerDacl;
+ }
+
+ } else {
+
+ NewDacl = SepDaclAddrSecurityDescriptor( IObjectsSecurityDescriptor );
+ }
+
+ //
+ // Copy the Owner SID if specified
+ //
+
+ //
+ // if he's setting the owner field, make sure he's
+ // allowed to set that value as an owner.
+ //
+
+ if (((*SecurityInformation) & OWNER_SECURITY_INFORMATION)) {
+
+ SeCaptureSubjectContext( &SubjectContext );
+
+ NewOwner = SepOwnerAddrSecurityDescriptor( IModificationDescriptor );
+ NewOwnerPresent = TRUE;
+
+ if (!SepValidOwnerSubjectContext( &SubjectContext, NewOwner, ServerObject ) ) {
+
+ SeReleaseSubjectContext( &SubjectContext );
+ return( STATUS_INVALID_OWNER );
+
+ } else {
+
+ SeReleaseSubjectContext( &SubjectContext );
+ }
+
+ } else {
+
+ NewOwner = SepOwnerAddrSecurityDescriptor ( IObjectsSecurityDescriptor );
+ }
+
+ ASSERT( NewOwner != NULL );
+
+ //
+ // Copy the Group SID if specified
+ //
+
+ if (((*SecurityInformation) & GROUP_SECURITY_INFORMATION)) {
+
+ NewGroup = SepGroupAddrSecurityDescriptor(IModificationDescriptor);
+ NewGroupPresent = TRUE;
+
+ } else {
+
+ NewGroup = SepGroupAddrSecurityDescriptor( IObjectsSecurityDescriptor );
+ }
+
+ if (NewGroup != NULL) {
+ if (!RtlValidSid( NewGroup )) {
+ return( STATUS_INVALID_PRIMARY_GROUP );
+ }
+ }
+
+ //
+ // Everything is assignable by the requestor.
+ // Calculate the memory needed to house all the information in
+ // a self-relative security descriptor.
+ //
+ // Also map the ACEs for application to the target object
+ // type, if they haven't already been mapped.
+ //
+
+ NewOwnerSize = (ULONG)LongAlign(SeLengthSid(NewOwner));
+
+ if (NewGroup != NULL) {
+ NewGroupSize = (ULONG)LongAlign(SeLengthSid(NewGroup));
+ } else {
+ NewGroupSize = 0;
+ }
+
+
+ if (NewSacl != NULL) {
+ NewSaclSize = (ULONG)LongAlign(NewSacl->AclSize);
+ } else {
+ NewSaclSize = 0;
+ }
+
+ if (NewDacl !=NULL) {
+ NewDaclSize = (ULONG)LongAlign(NewDacl->AclSize);
+ } else {
+ NewDaclSize = 0;
+ }
+
+ AllocationSize = (ULONG)LongAlign(sizeof(SECURITY_DESCRIPTOR)) +
+ NewOwnerSize +
+ NewGroupSize +
+ NewSaclSize +
+ NewDaclSize;
+
+ //
+ // Allocate and initialize the security descriptor as
+ // self-relative form.
+ //
+
+ NewDescriptor = (SECURITY_DESCRIPTOR *)
+ ExAllocatePoolWithTag(PoolType, AllocationSize, 'dSeS');
+
+ if (NewDescriptor == NULL) {
+ return( STATUS_NO_MEMORY );
+ }
+
+ Status = RtlCreateSecurityDescriptor(
+ NewDescriptor,
+ SECURITY_DESCRIPTOR_REVISION
+ );
+
+ ASSERT( NT_SUCCESS( Status ) );
+
+ //
+ // We must check to make sure that the Group and Dacl size
+ // do not exceed the quota preallocated for this object's
+ // security when it was created.
+ //
+ // Update SeComputeSecurityQuota if this changes.
+ //
+
+
+ if (ARGUMENT_PRESENT( Object )) {
+
+ Status = ObValidateSecurityQuota(
+ Object,
+ NewGroupSize + NewDaclSize
+ );
+
+ if (!NT_SUCCESS( Status )) {
+
+ //
+ // The new information is too big.
+ //
+
+ ExFreePool( NewDescriptor );
+ return( Status );
+ }
+
+ }
+
+ SepSetControlBits( NewDescriptor, SE_SELF_RELATIVE );
+
+ Base = (PCHAR)NewDescriptor;
+ Field = Base + (ULONG)sizeof(SECURITY_DESCRIPTOR);
+
+ //
+ // Map and Copy in the Sacl
+ //
+
+ //
+ // if new item {
+ // PRESENT=TRUE
+ // DEFAULTED=FALSE
+ // if (new item == NULL) {
+ // set new pointer to NULL
+ // } else {
+ // copy into new SD
+ // }
+ // } else {
+ // copy PRESENT bit
+ // copy DEFAULTED bit
+ // if (new item == NULL) {
+ // set new pointer to NULL
+ // } else {
+ // copy old one into new SD
+ // }
+ // }
+ //
+
+
+
+ if (NewSacl == NULL) {
+ NewDescriptor->Sacl = NULL;
+
+ } else {
+
+ RtlMoveMemory( Field, NewSacl, NewSacl->AclSize );
+ NewDescriptor->Sacl = (PACL)RtlPointerToOffset(Base,Field);
+ SepApplyAclToObject( (PACL)Field, GenericMapping );
+ Field += NewSaclSize;
+ }
+
+
+
+ if (NewSaclPresent) {
+
+ //
+ // defaulted bit is off already
+ //
+
+ SepSetControlBits( NewDescriptor, SE_SACL_PRESENT );
+
+ } else {
+
+ //
+ // Propagate the SE_SACL_DEFAULTED and SE_SACL_PRESENT
+ // bits from the old security descriptor into the new
+ // one.
+ //
+
+ SepPropagateControlBits(
+ NewDescriptor,
+ IObjectsSecurityDescriptor,
+ SE_SACL_DEFAULTED | SE_SACL_PRESENT
+ );
+
+ }
+
+ //
+ // Fill in Dacl field in new SD
+ //
+
+ if (NewDacl == NULL) {
+ NewDescriptor->Dacl = NULL;
+
+ } else {
+
+ RtlMoveMemory( Field, NewDacl, NewDacl->AclSize );
+ NewDescriptor->Dacl = (PACL)RtlPointerToOffset(Base,Field);
+ SepApplyAclToObject( (PACL)Field, GenericMapping );
+ Field += NewDaclSize;
+ }
+
+ if (NewDaclPresent) {
+
+ //
+ // defaulted bit is off already
+ //
+
+ SepSetControlBits( NewDescriptor, SE_DACL_PRESENT );
+
+ } else {
+
+ //
+ // Propagate the SE_DACL_DEFAULTED and SE_DACL_PRESENT
+ // bits from the old security descriptor into the new
+ // one.
+ //
+
+ SepPropagateControlBits(
+ NewDescriptor,
+ IObjectsSecurityDescriptor,
+ SE_DACL_DEFAULTED | SE_DACL_PRESENT
+ );
+
+ }
+
+ //
+ // If we allocated memory for a server acl, we can free it now.
+ //
+
+ if (ServerDaclAllocated) {
+ ExFreePool( NewDacl );
+ }
+
+ //
+ // Fill in Owner field in new SD
+ //
+
+ RtlMoveMemory( Field, NewOwner, SeLengthSid(NewOwner) );
+ NewDescriptor->Owner = (PSID)RtlPointerToOffset(Base,Field);
+ Field += NewOwnerSize;
+
+ if (!NewOwnerPresent) {
+
+ //
+ // Propagate the SE_OWNER_DEFAULTED bit from the old SD.
+ // If a new owner is being assigned, we want to leave
+ // SE_OWNER_DEFAULTED off, which means leave it alone.
+ //
+
+ SepPropagateControlBits(
+ NewDescriptor,
+ IObjectsSecurityDescriptor,
+ SE_OWNER_DEFAULTED
+ );
+
+ } else {
+ ASSERT( !SepAreControlBitsSet( NewDescriptor, SE_OWNER_DEFAULTED ) );
+ }
+
+
+ //
+ // Fill in Group field in new SD
+ //
+
+ RtlMoveMemory( Field, NewGroup, SeLengthSid(NewGroup) );
+ NewDescriptor->Group = (PSID)RtlPointerToOffset(Base,Field);
+
+ if (!NewGroupPresent) {
+
+ //
+ // Propagate the SE_GROUP_DEFAULTED bit from the old SD
+ // If a new owner is being assigned, we want to leave
+ // SE_GROUP_DEFAULTED off, which means leave it alone.
+ //
+
+ SepPropagateControlBits(
+ NewDescriptor,
+ IObjectsSecurityDescriptor,
+ SE_GROUP_DEFAULTED
+ );
+ } else {
+ ASSERT( !SepAreControlBitsSet( NewDescriptor, SE_GROUP_DEFAULTED ) );
+
+ }
+
+// //
+// // Free old descriptor
+// //
+//
+// ExFreePool( IObjectsSecurityDescriptor );
+
+ *ObjectsSecurityDescriptor = (PSECURITY_DESCRIPTOR)NewDescriptor;
+
+ //
+ // and now we can return to our caller
+ //
+
+ return STATUS_SUCCESS;
+
+}
+
+
+
+NTSTATUS
+SeQuerySecurityDescriptorInfo (
+ IN PSECURITY_INFORMATION SecurityInformation,
+ OUT PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN OUT PULONG Length,
+ IN PSECURITY_DESCRIPTOR *ObjectsSecurityDescriptor
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will extract the desired information from the
+ passed security descriptor and return the information in
+ the passed buffer as a security descriptor in self-relative
+ format.
+
+Arguments:
+
+ SecurityInformation - Specifies what information is being queried.
+
+ SecurityDescriptor - Supplies the buffer to output the requested
+ information into.
+
+ This buffer has been probed only to the size indicated by
+ the Length parameter. Since it still points into user space,
+ it must always be accessed in a try clause.
+
+ Length - Supplies the address of a variable containing the length of
+ the security descriptor buffer. Upon return this variable will
+ contain the length needed to store the requested information.
+
+ ObjectsSecurityDescriptor - Supplies the address of a pointer to
+ the objects security descriptor. The passed security descriptor
+ must be in self-relative format.
+
+Return Value:
+
+ NTSTATUS - STATUS_SUCCESS if successful and an appropriate error value
+ otherwise
+
+--*/
+
+{
+ ULONG BufferLength;
+
+ ULONG Size;
+ ULONG OwnerLength;
+ ULONG GroupLength;
+ ULONG DaclLength;
+ ULONG SaclLength;
+ PUCHAR NextFree;
+ SECURITY_DESCRIPTOR IObjectSecurity;
+
+ //
+ // Note that IObjectSecurity is not a pointer to a pointer
+ // like ObjectsSecurityDescriptor is.
+ //
+
+ SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;
+
+ PAGED_CODE();
+
+ //
+ // We will be accessing user memory throughout this routine,
+ // therefore do everything in a try-except clause.
+ //
+
+ try {
+
+ BufferLength = *Length;
+
+ //
+ // Check if the object's descriptor is null, and if it is then
+ // we only need to return a blank security descriptor record
+ //
+
+ if (*ObjectsSecurityDescriptor == NULL) {
+
+ *Length = sizeof(SECURITY_DESCRIPTOR);
+
+ //
+ // Now make sure it's large enough for the security descriptor
+ // record
+ //
+
+ if (BufferLength < sizeof(SECURITY_DESCRIPTOR)) {
+
+ return STATUS_BUFFER_TOO_SMALL;
+
+ }
+
+ //
+ // It's large enough to make a blank security descriptor record
+ //
+ // Note that this parameter has been probed for write by the
+ // object manager, however, we still have to be careful when
+ // writing to it.
+ //
+
+ //
+ // We do not have to probe this here, because the object
+ // manager has probed it for length=BufferLength, which we
+ // know at this point is at least as large as a security
+ // descriptor.
+ //
+
+ RtlCreateSecurityDescriptor( SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION );
+
+ //
+ // Mark it as self-relative
+ //
+
+ SepSetControlBits( ISecurityDescriptor, SE_SELF_RELATIVE );
+
+ //
+ // And return to our caller
+ //
+
+ return STATUS_SUCCESS;
+
+ }
+
+ //
+ // Create an absolute format SD on the stack pointing into
+ // user space to simplify the following code
+ //
+
+ RtlMoveMemory( (&IObjectSecurity),
+ *ObjectsSecurityDescriptor,
+ sizeof(SECURITY_DESCRIPTOR) );
+
+ IObjectSecurity.Owner = SepOwnerAddrSecurityDescriptor(
+ (SECURITY_DESCRIPTOR *) *ObjectsSecurityDescriptor );
+ IObjectSecurity.Group = SepGroupAddrSecurityDescriptor(
+ (SECURITY_DESCRIPTOR *) *ObjectsSecurityDescriptor );
+ IObjectSecurity.Dacl = SepDaclAddrSecurityDescriptor(
+ (SECURITY_DESCRIPTOR *) *ObjectsSecurityDescriptor );
+ IObjectSecurity.Sacl = SepSaclAddrSecurityDescriptor(
+ (SECURITY_DESCRIPTOR *) *ObjectsSecurityDescriptor );
+
+ IObjectSecurity.Control &= ~SE_SELF_RELATIVE;
+
+ //
+ // This is not a blank descriptor so we need to determine the size
+ // needed to store the requested information. It is the size of the
+ // descriptor record plus the size of each requested component.
+ //
+
+ Size = sizeof(SECURITY_DESCRIPTOR);
+
+ if ( (((*SecurityInformation) & OWNER_SECURITY_INFORMATION)) &&
+ (IObjectSecurity.Owner != NULL) ) {
+
+ OwnerLength = SeLengthSid( IObjectSecurity.Owner );
+ Size += (ULONG)LongAlign(OwnerLength);
+
+ }
+
+ if ( (((*SecurityInformation) & GROUP_SECURITY_INFORMATION)) &&
+ (IObjectSecurity.Group != NULL) ) {
+
+ GroupLength = SeLengthSid( IObjectSecurity.Group );
+ Size += (ULONG)LongAlign(GroupLength);
+
+ }
+
+ if ( (((*SecurityInformation) & DACL_SECURITY_INFORMATION)) &&
+ (IObjectSecurity.Control & SE_DACL_PRESENT) &&
+ (IObjectSecurity.Dacl != NULL) ) {
+
+
+ DaclLength = (ULONG)LongAlign((IObjectSecurity.Dacl)->AclSize);
+ Size += DaclLength;
+
+ }
+
+ if ( (((*SecurityInformation) & SACL_SECURITY_INFORMATION)) &&
+ (IObjectSecurity.Control & SE_SACL_PRESENT) &&
+ (IObjectSecurity.Sacl != NULL) ) {
+
+ SaclLength = (ULONG)LongAlign((IObjectSecurity.Sacl)->AclSize);
+ Size += SaclLength;
+
+ }
+
+ //
+ // Tell the caller how much space this will require
+ // (whether we actually fit or not)
+ //
+
+ *Length = Size;
+
+ //
+ // Now make sure the size is less than or equal to the length
+ // we were passed
+ //
+
+ if (Size > BufferLength) {
+
+ return STATUS_BUFFER_TOO_SMALL;
+
+ }
+
+ //
+ // The length is fine.
+ //
+ // Fill in the length and flags part of the security descriptor.
+ // The real addresses of each acl will be filled in later when we
+ // copy the ACLs over.
+ //
+ // Note that we only set a flag in the descriptor if the information
+ // was requested, which is a simple copy of the requested information
+ // input variable
+ //
+ // The output buffer has already been probed to the passed size,
+ // so we can just write to it.
+ //
+
+ RtlCreateSecurityDescriptor( SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION );
+
+ //
+ // Mark the returned Security Descriptor as being in
+ // self-relative format
+ //
+
+ SepSetControlBits( ISecurityDescriptor, SE_SELF_RELATIVE );
+
+ //
+ // NextFree is used to point to the next free spot in the
+ // returned security descriptor.
+ //
+
+ NextFree = LongAlign((PUCHAR)SecurityDescriptor +
+ sizeof(SECURITY_DESCRIPTOR));
+
+ //
+ // Copy the Owner SID if necessary and update the NextFree pointer,
+ // keeping it longword aligned.
+ //
+
+ if ( ((*SecurityInformation) & OWNER_SECURITY_INFORMATION) &&
+ ((IObjectSecurity.Owner) != NULL) ) {
+
+ RtlMoveMemory( NextFree,
+ IObjectSecurity.Owner,
+ OwnerLength );
+
+ ISecurityDescriptor->Owner = (PACL)((PUCHAR)NextFree - (PUCHAR)SecurityDescriptor);
+
+ SepPropagateControlBits(
+ ISecurityDescriptor,
+ &IObjectSecurity,
+ SE_OWNER_DEFAULTED
+ );
+
+ NextFree += (ULONG)LongAlign(OwnerLength);
+
+ }
+
+
+ //
+ // Copy the Group SID if necessary and update the NextFree pointer,
+ // keeping it longword aligned.
+ //
+
+ if ( ((*SecurityInformation) & GROUP_SECURITY_INFORMATION) &&
+ (IObjectSecurity.Group != NULL) ) {
+
+ RtlMoveMemory( NextFree,
+ IObjectSecurity.Group,
+ GroupLength );
+
+ ISecurityDescriptor->Group = (PACL)((PUCHAR)NextFree - (PUCHAR)SecurityDescriptor);
+
+ SepPropagateControlBits(
+ ISecurityDescriptor,
+ &IObjectSecurity,
+ SE_GROUP_DEFAULTED
+ );
+
+ NextFree += (ULONG)LongAlign(GroupLength);
+
+ }
+
+
+ //
+ // Set discretionary acl information if requested.
+ // If not set in object's security,
+ // then everything is already set properly.
+ //
+
+ if ( ((*SecurityInformation) & DACL_SECURITY_INFORMATION) &&
+ (IObjectSecurity.Control & SE_DACL_PRESENT) ) {
+
+ SepPropagateControlBits(
+ ISecurityDescriptor,
+ &IObjectSecurity,
+ SE_DACL_PRESENT | SE_DACL_DEFAULTED
+ );
+
+ //
+ // Copy the acl if non-null and update the NextFree pointer,
+ // keeping it longword aligned.
+ //
+
+ if (IObjectSecurity.Dacl != NULL) {
+
+ RtlMoveMemory( NextFree,
+ IObjectSecurity.Dacl,
+ (IObjectSecurity.Dacl)->AclSize );
+
+ ISecurityDescriptor->Dacl = (PACL)((PUCHAR)NextFree - (PUCHAR)SecurityDescriptor);
+
+ NextFree += DaclLength;
+
+ }
+ }
+
+
+ //
+ // Set system acl information if requested.
+ // If not set in object's security,
+ // then everything is already set properly.
+ //
+
+ if ( (((*SecurityInformation) & SACL_SECURITY_INFORMATION)) &&
+ (IObjectSecurity.Control & SE_SACL_PRESENT) ) {
+
+ SepPropagateControlBits(
+ ISecurityDescriptor,
+ &IObjectSecurity,
+ SE_SACL_PRESENT | SE_SACL_DEFAULTED
+ );
+
+ //
+ // Copy the acl if non-null and update the NextFree pointer,
+ // keeping it longword aligned.
+ //
+
+ if (IObjectSecurity.Sacl != NULL) {
+
+ RtlMoveMemory( NextFree,
+ IObjectSecurity.Sacl,
+ (IObjectSecurity.Sacl)->AclSize );
+
+ ISecurityDescriptor->Sacl = (PACL)((PUCHAR)NextFree - (PUCHAR)SecurityDescriptor);
+
+ }
+ }
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ return(GetExceptionCode());
+ }
+
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+SepDefaultDeleteMethod (
+ IN OUT PSECURITY_DESCRIPTOR *ObjectsSecurityDescriptor
+ )
+
+/*++
+
+Routine Description:
+
+ This is a private procedure to delete the security descriptor for
+ an object. It cleans up any pool allocations that have occured
+ as part of the descriptor.
+
+Arguments:
+
+ ObjectsSecurityDescriptor - Supplies the address of a pointer
+ to the security descriptor being deleted.
+
+Return Value:
+
+ NTSTATUS - STATUS_SUCCESS
+
+--*/
+
+{
+ PAGED_CODE();
+
+ return (ObDeassignSecurity ( ObjectsSecurityDescriptor ));
+}
diff --git a/private/ntos/se/sep.c b/private/ntos/se/sep.c
new file mode 100644
index 000000000..9101f6af6
--- /dev/null
+++ b/private/ntos/se/sep.c
@@ -0,0 +1,231 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ Sep.c
+
+Abstract:
+
+ This Module implements the private security routine that are defined
+ in sep.h
+
+Author:
+
+ Gary Kimura (GaryKi) 9-Nov-1989
+
+Environment:
+
+ Kernel Mode
+
+Revision History:
+
+--*/
+
+#include "sep.h"
+#include "seopaque.h"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE,SepCheckAcl)
+#endif
+
+
+#define LongAligned( ptr ) (LongAlign(ptr) == ((PVOID)(ptr)))
+#define WordAligned( ptr ) (WordAlign(ptr) == ((PVOID)(ptr)))
+
+
+BOOLEAN
+SepCheckAcl (
+ IN PACL Acl,
+ IN ULONG Length
+ )
+
+/*++
+
+Routine Description:
+
+ This is a private routine that checks that an acl is well formed.
+
+Arguments:
+
+ Acl - Supplies the acl to check
+
+ Length - Supplies the real size of the acl. The internal acl size
+ must agree.
+
+Return Value:
+
+ BOOLEAN - TRUE if the acl is well formed and FALSE otherwise
+
+--*/
+{
+ PACE_HEADER Ace;
+ PISID Sid;
+ PISID Sid2;
+ ULONG i;
+ UCHAR AclRevision = ACL_REVISION2;
+
+ if (!ValidAclRevision(Acl)) {
+ return(FALSE);
+ }
+
+
+ if (!LongAligned(Acl->AclSize)) {
+ return(FALSE);
+ }
+
+ //
+ // Validate all of the ACEs.
+ //
+
+ Ace = ((PVOID)((PUCHAR)(Acl) + sizeof(ACL)));
+
+ for (i = 0; i < Acl->AceCount; i++) {
+
+ //
+ // Check to make sure we haven't overrun the Acl buffer
+ // with our ace pointer. Make sure the ACE_HEADER is in
+ // the ACL also.
+ //
+
+ if ((PUCHAR)Ace + sizeof(ACE_HEADER) >= ((PUCHAR)Acl + Acl->AclSize)) {
+ return(FALSE);
+ }
+
+ if (!WordAligned(&Ace->AceSize)) {
+ return(FALSE);
+ }
+
+ if ((PUCHAR)Ace + Ace->AceSize > ((PUCHAR)Acl + Acl->AclSize)) {
+ return(FALSE);
+ }
+
+ //
+ // It is now safe to reference fields in the ACE header.
+ //
+
+ //
+ // The ACE header fits into the ACL, if this is a known type of ACE,
+ // make sure the SID is within the bounds of the ACE
+ //
+
+ if (IsKnownAceType(Ace)) {
+
+ if (!LongAligned(Ace->AceSize)) {
+ return(FALSE);
+ }
+
+ if (Ace->AceSize < sizeof(KNOWN_ACE) - sizeof(ULONG) + sizeof(SID)) {
+ return(FALSE);
+ }
+
+ //
+ // It's now safe to reference the parts of the SID structure, though
+ // not the SID itself.
+ //
+
+ Sid = (PISID) & (((PKNOWN_ACE)Ace)->SidStart);
+
+ if (Sid->Revision != SID_REVISION) {
+ return(FALSE);
+ }
+
+ if (Sid->SubAuthorityCount > SID_MAX_SUB_AUTHORITIES) {
+ return(FALSE);
+ }
+
+ //
+ // SeLengthSid computes the size of the SID based on the subauthority count,
+ // so it is safe to use even though we don't know that the body of the SID
+ // is safe to reference.
+ //
+
+ if (Ace->AceSize < sizeof(KNOWN_ACE) - sizeof(ULONG) + SeLengthSid( Sid )) {
+ return(FALSE);
+ }
+
+ } else {
+
+ //
+ // If it's a compound ACE, then perform roughly the same set of tests, but
+ // check the validity of both SIDs.
+ //
+
+ if (IsCompoundAceType(Ace)) {
+
+ //
+ // Save away the fact that we saw a compound ACE while traversing the ACL.
+ //
+
+ AclRevision = ACL_REVISION3;
+
+ if (!LongAligned(Ace->AceSize)) {
+ return(FALSE);
+ }
+
+ if (Ace->AceSize < sizeof(KNOWN_COMPOUND_ACE) - sizeof(ULONG) + sizeof(SID)) {
+ return(FALSE);
+ }
+
+ //
+ // The only currently defined Compound ACE is an Impersonation ACE.
+ //
+
+ if (((PKNOWN_COMPOUND_ACE)Ace)->CompoundAceType != COMPOUND_ACE_IMPERSONATION) {
+ return(FALSE);
+ }
+
+ //
+ // Examine the first SID and make sure it's structurally valid,
+ // and it lies within the boundaries of the ACE.
+ //
+
+ Sid = (PISID) & (((PKNOWN_COMPOUND_ACE)Ace)->SidStart);
+
+ if (Sid->Revision != SID_REVISION) {
+ return(FALSE);
+ }
+
+ if (Sid->SubAuthorityCount > SID_MAX_SUB_AUTHORITIES) {
+ return(FALSE);
+ }
+
+ //
+ // Compound ACEs contain two SIDs. Make sure this ACE is large enough to contain
+ // not only the first SID, but the body of the 2nd.
+ //
+
+ if (Ace->AceSize < sizeof(KNOWN_COMPOUND_ACE) - sizeof(ULONG) + SeLengthSid( Sid ) + sizeof(SID)) {
+ return(FALSE);
+ }
+
+ //
+ // It is safe to reference the interior of the 2nd SID.
+ //
+
+ Sid2 = (PISID) ((PUCHAR)Sid + SeLengthSid( Sid ));
+
+ if (Sid2->Revision != SID_REVISION) {
+ return(FALSE);
+ }
+
+ if (Sid2->SubAuthorityCount > SID_MAX_SUB_AUTHORITIES) {
+ return(FALSE);
+ }
+
+ if (Ace->AceSize < sizeof(KNOWN_COMPOUND_ACE) - sizeof(ULONG) + SeLengthSid( Sid ) + SeLengthSid( Sid2 )) {
+ return(FALSE);
+ }
+ }
+ }
+
+ //
+ // And move Ace to the next ace position
+ //
+
+ Ace = ((PVOID)((PUCHAR)(Ace) + ((PACE_HEADER)(Ace))->AceSize));
+ }
+
+ return(TRUE);
+}
diff --git a/private/ntos/se/sep.h b/private/ntos/se/sep.h
new file mode 100644
index 000000000..7ad0d6cbd
--- /dev/null
+++ b/private/ntos/se/sep.h
@@ -0,0 +1,741 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sep.h
+
+Abstract:
+
+ This module contains the internal (private) declarations needed by the
+ Kernel mode security routines.
+
+Author:
+
+ Gary Kimura (GaryKi) 31-Mar-1989
+ Jim Kelly (JimK) 2-Mar-1990
+
+Revision History:
+
+
+--*/
+
+#ifndef _SEP_
+#define _SEP_
+
+#include "ntos.h"
+#include <ntrmlsa.h>
+#include "seopaque.h"
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// SE Diagnostics //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+
+
+#if DBG
+#define SE_DIAGNOSTICS_ENABLED 1
+#endif // DBG
+
+
+//
+// These definitions are useful diagnostics aids
+//
+
+#if SE_DIAGNOSTICS_ENABLED
+
+//
+// Test for enabled diagnostic
+//
+
+#define IF_SE_GLOBAL( FlagName ) \
+ if (SeGlobalFlag & (SE_DIAG_##FlagName))
+
+//
+// Diagnostics print statement
+//
+
+#define SeDiagPrint( FlagName, _Text_ ) \
+ IF_SE_GLOBAL( FlagName ) \
+ DbgPrint _Text_
+
+
+#else
+
+//
+// diagnostics not enabled - No diagnostics included in build
+//
+
+
+//
+// Test for diagnostics enabled
+//
+
+#define IF_SE_GLOBAL( FlagName ) if (FALSE)
+
+//
+// Diagnostics print statement (expands to no-op)
+//
+
+#define SeDiagPrint( FlagName, _Text_ ) ;
+
+#endif // SE_DIAGNOSTICS_ENABLED
+
+
+
+
+//
+// The following flags enable or disable various diagnostic
+// capabilities within SE code. These flags are set in
+// SeGlobalFlag (only available within a DBG system).
+//
+// SD_TRACKING - Display information about create/deletion of
+// shared security descriptors
+//
+//
+
+#define SE_DIAG_SD_TRACKING ((ULONG) 0x00000001L)
+
+
+
+
+
+//
+// Control flag manipulation macros
+//
+
+//
+// Macro to query whether or not control flags ALL on
+// or not (ie, returns FALSE if any of the flags are not set)
+//
+
+#define SepAreFlagsSet( Mask, Bits ) \
+ ( \
+ ((Mask) & ( Bits )) == ( Bits ) \
+ )
+
+//
+// Macro to set the specified control bits in the given Security Descriptor
+//
+
+#define SepSetFlags( Mask, Bits ) \
+ ( \
+ ( Mask ) |= ( Bits ) \
+ )
+
+//
+// Macro to clear the passed control bits in the given Security Descriptor
+//
+
+#define SepClearFlags( Mask, Bits ) \
+ ( \
+ ( Mask ) &= ~( Bits ) \
+ )
+
+
+
+//
+// Macros for calculating the address of the components of a security
+// descriptor. This will calculate the address of the field regardless
+// of whether the security descriptor is absolute or self-relative form.
+// A null value indicates the specified field is not present in the
+// security descriptor.
+//
+
+#define SepOwnerAddrSecurityDescriptor( SD ) \
+ ( ((SD)->Owner == NULL) ? (PSID)NULL : \
+ ( ((SD)->Control & SE_SELF_RELATIVE) ? \
+ (PSID)RtlOffsetToPointer((SD), (SD)->Owner) : \
+ (PSID)((SD)->Owner) \
+ ) \
+ )
+
+#define SepGroupAddrSecurityDescriptor( SD ) \
+ ( ((SD)->Group == NULL) ? (PSID)NULL : \
+ ( ((SD)->Control & SE_SELF_RELATIVE) ? \
+ (PSID)RtlOffsetToPointer((SD), (SD)->Group) : \
+ (PSID)((SD)->Group) \
+ ) \
+ )
+
+#define SepSaclAddrSecurityDescriptor( SD ) \
+ ( (!((SD)->Control & SE_SACL_PRESENT) || ((SD)->Sacl == NULL) ) ? \
+ (PACL)NULL : \
+ ( ((SD)->Control & SE_SELF_RELATIVE) ? \
+ (PACL)RtlOffsetToPointer((SD), (SD)->Sacl) : \
+ (PACL)((SD)->Sacl) \
+ ) \
+ )
+
+#define SepDaclAddrSecurityDescriptor( SD ) \
+ ( (!((SD)->Control & SE_DACL_PRESENT) || ((SD)->Dacl == NULL) ) ? \
+ (PACL)NULL : \
+ ( ((SD)->Control & SE_SELF_RELATIVE) ? \
+ (PACL)RtlOffsetToPointer((SD), (SD)->Dacl) : \
+ (PACL)((SD)->Dacl) \
+ ) \
+ )
+
+
+//
+// Macro to copy the state of the passed bits from the old security
+// descriptor (OldSD) into the Control field of the new one (NewSD)
+//
+
+#define SepPropagateControlBits( NewSD, OldSD, Bits ) \
+ ( NewSD )->Control |= \
+ ( \
+ ( OldSD )->Control & ( Bits ) \
+ )
+
+//
+// Macro to query whether or not the passed set of bits are ALL on
+// or not (ie, returns FALSE if some are on and not others)
+//
+
+#define SepAreControlBitsSet( SD, Bits ) \
+ ( \
+ (( SD )->Control & ( Bits )) == ( Bits ) \
+ )
+
+//
+// Macro to set the passed control bits in the given Security Descriptor
+//
+
+#define SepSetControlBits( SD, Bits ) \
+ ( \
+ ( SD )->Control |= ( Bits ) \
+ )
+
+//
+// Macro to clear the passed control bits in the given Security Descriptor
+//
+
+#define SepClearControlBits( SD, Bits ) \
+ ( \
+ ( SD )->Control &= ~( Bits ) \
+ )
+
+
+//
+// Macro to determine the size of a PRIVILEGE_SET
+//
+
+#define SepPrivilegeSetSize( PrivilegeSet ) \
+ ( ( PrivilegeSet ) == NULL ? 0 : \
+ ((( PrivilegeSet )->PrivilegeCount > 0) \
+ ? \
+ ((ULONG)sizeof(PRIVILEGE_SET) + \
+ ( \
+ (( PrivilegeSet )->PrivilegeCount - ANYSIZE_ARRAY) * \
+ (ULONG)sizeof(LUID_AND_ATTRIBUTES) \
+ ) \
+ ) \
+ : ((ULONG)sizeof(PRIVILEGE_SET) - (ULONG)sizeof(LUID_AND_ATTRIBUTES)) \
+ ))
+
+
+//
+// Return the effective token from a SecurityContext
+//
+
+#define EffectiveToken( SubjectSecurityContext ) ( \
+ (SubjectSecurityContext)->ClientToken ? \
+ (SubjectSecurityContext)->ClientToken : \
+ (SubjectSecurityContext)->PrimaryToken \
+ ) \
+
+
+//
+// Return a pointer to the Sid of the User of a given token
+//
+
+#define SepTokenUserSid( Token ) ((PTOKEN)(Token))->UserAndGroups->Sid
+
+
+//
+// Return the AuthenticationId from a given token
+//
+
+#define SepTokenAuthenticationId( Token ) (((PTOKEN)(Token))->AuthenticationId)
+
+
+
+//
+//
+// BOOLEAN
+// SepBadImpersonationLevel(
+// IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
+// IN BOOLEAN ServerIsRemote
+// )
+//
+// Routine Description:
+//
+// Determine whether a client is trying to impersonate innappropriately
+// This routine should only be called if a thread requesting impersonation
+// is itself already impersonating a client of its own. This routine
+// indicates whether the client is attempting to violate the level of
+// impersonation granted to it by its client.
+//
+// Arguments:
+//
+// ImpersonationLevel - Is the impersonation level of the client's
+// effective token.
+//
+// ServerIsRemote - Is a boolean flag indicating whether the client
+// is requesting impersonation services to a remote system. TRUE
+// indicates the session is a remote session, FALSE indicates the
+// session is a local session. Delegation level is necessary to
+// achieve a remote session.
+//
+// Return Value:
+//
+// TRUE - Indicates that the impersonation level of the client's client
+// token is innapropriate for the attempted impersonation.
+// An error (STATUS_BAD_IMPERSONATION_LEVEL) should be generated.
+//
+// FALSE - Indicates the impersonation attempt is not bad, and should
+// be allowed.
+//
+//
+
+#define SepBadImpersonationLevel(IL,SIR) (( \
+ ((IL) == SecurityAnonymous) || ((IL) == SecurityIdentification) || \
+ ( (SIR) && ((IL) != SecurityDelegation) ) \
+ ) ? TRUE : FALSE )
+
+
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// Private Data types //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+
+extern HANDLE SepLsaHandle;
+
+extern BOOLEAN SepAuditShutdownEvents;
+
+//
+// Spinlock protecting the queue of work being passed to LSA
+//
+
+extern ERESOURCE SepLsaQueueLock;
+
+extern ULONG SepLsaQueueLength;
+
+//
+// Doubly linked list of work items queued to worker threads.
+//
+
+extern LIST_ENTRY SepLsaQueue;
+
+
+
+#define SepLockLsaQueue() ExAcquireResourceExclusive(&SepLsaQueueLock, TRUE)
+
+#define SepUnlockLsaQueue() ExReleaseResource(&SepLsaQueueLock)
+
+#define SepWorkListHead() ((PSEP_LSA_WORK_ITEM)(&SepLsaQueue)->Flink)
+
+#ifndef ExAllocatePool
+#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,' eS')
+#endif
+#ifndef ExAllocatePoolWithQuota
+#define ExAllocatePoolWithQuota(a,b) ExAllocatePoolWithQuotaTag(a,b,' eS')
+#endif
+
+typedef
+VOID
+(*PSEP_LSA_WORKER_CLEANUP_ROUTINE)(
+ IN PVOID Parameter
+ );
+
+
+typedef enum _SEP_LSA_WORK_ITEM_TAG {
+ SepDeleteLogon,
+ SepAuditRecord
+} SEP_LSA_WORK_ITEM_TAG, *PSEP_LSA_WORK_ITEM_TAG;
+
+
+
+
+
+typedef struct _SEP_LSA_WORK_ITEM {
+
+ //
+ // This field must be the first field of this structure
+ //
+
+ LIST_ENTRY List;
+
+ //
+ // Command Params Memory type
+ //
+
+ SEP_RM_LSA_MEMORY_TYPE CommandParamsMemoryType;
+
+ //
+ // Tag describing what kind of structure we've got
+ //
+
+ SEP_LSA_WORK_ITEM_TAG Tag;
+
+ //
+ // The following union contains the data to be passed
+ // to LSA.
+ //
+
+ union {
+
+ PVOID BaseAddress;
+ LUID LogonId;
+
+ } CommandParams;
+
+ //
+ // These fields must be filled in by the caller of SepRmCallLsa
+ //
+
+ LSA_COMMAND_NUMBER CommandNumber;
+ ULONG CommandParamsLength;
+ PVOID ReplyBuffer;
+ ULONG ReplyBufferLength;
+
+ //
+ // CleanupFunction (if specified) will be called with CleanupParameter
+ // as its argument before the SEP_LSA_WORK_ITEM is freed by SepRmCallLsa
+ //
+
+ PSEP_LSA_WORKER_CLEANUP_ROUTINE CleanupFunction;
+ PVOID CleanupParameter;
+
+} SEP_LSA_WORK_ITEM, *PSEP_LSA_WORK_ITEM;
+
+
+typedef struct _SEP_WORK_ITEM {
+
+ WORK_QUEUE_ITEM WorkItem;
+
+} SEP_WORK_ITEM, *PSEP_WORK_ITEM;
+
+extern SEP_WORK_ITEM SepExWorkItem;
+
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// Private Routines //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+BOOLEAN
+SepDevelopmentTest( VOID ); //Used only for development testing
+
+
+BOOLEAN
+SepInitializationPhase0( VOID );
+
+BOOLEAN
+SepInitializationPhase1( VOID );
+
+BOOLEAN
+SepVariableInitialization( VOID );
+
+NTSTATUS
+SepCreateToken(
+ OUT PHANDLE TokenHandle,
+ IN KPROCESSOR_MODE RequestorMode,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
+ IN TOKEN_TYPE TokenType,
+ IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel OPTIONAL,
+ IN PLUID AuthenticationId,
+ IN PLARGE_INTEGER ExpirationTime,
+ IN PSID_AND_ATTRIBUTES User,
+ IN ULONG GroupCount,
+ IN PSID_AND_ATTRIBUTES Groups,
+ IN ULONG GroupsLength,
+ IN ULONG PrivilegeCount,
+ IN PLUID_AND_ATTRIBUTES Privileges,
+ IN ULONG PrivilegesLength,
+ IN PSID Owner OPTIONAL,
+ IN PSID PrimaryGroup,
+ IN PACL DefaultDacl OPTIONAL,
+ IN PTOKEN_SOURCE TokenSource,
+ IN BOOLEAN SystemToken,
+ IN PSECURITY_TOKEN_PROXY_DATA ProxyData OPTIONAL,
+ IN PSECURITY_TOKEN_AUDIT_DATA AuditData OPTIONAL
+ );
+
+
+
+NTSTATUS
+SepReferenceLogonSession(
+ IN PLUID LogonId
+ );
+
+VOID
+SepDeReferenceLogonSession(
+ IN PLUID LogonId
+ );
+
+
+
+VOID
+SepLockSubjectContext(
+ IN PSECURITY_SUBJECT_CONTEXT SubjectContext
+ );
+
+VOID
+SepFreeSubjectContext(
+ IN PSECURITY_SUBJECT_CONTEXT SubjectContext
+ );
+
+VOID
+SepGetDefaultsSubjectContext(
+ IN PSECURITY_SUBJECT_CONTEXT SubjectContext,
+ OUT PSID *Owner,
+ OUT PSID *Group,
+ OUT PSID *ServerOwner,
+ OUT PSID *ServerGroup,
+ OUT PACL *Dacl
+ );
+
+
+BOOLEAN
+SepValidOwnerSubjectContext(
+ IN PSECURITY_SUBJECT_CONTEXT SubjectContext,
+ IN PSID Owner,
+ IN BOOLEAN ServerObject
+ );
+
+
+BOOLEAN
+SepCheckAcl (
+ IN PACL Acl,
+ IN ULONG Length
+ );
+
+
+BOOLEAN
+SepAuditAlarm (
+ IN PUNICODE_STRING SubsystemName,
+ IN PVOID HandleId,
+ IN PUNICODE_STRING ObjectTypeName,
+ IN PUNICODE_STRING ObjectName,
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN ACCESS_MASK DesiredAccess,
+ IN BOOLEAN ObjectCreation,
+ IN ACCESS_MASK GrantedAccess,
+ OUT PBOOLEAN GenerateOnClose
+ );
+
+BOOLEAN
+SepSinglePrivilegeCheck (
+ LUID DesiredPrivilege,
+ IN PACCESS_TOKEN EffectiveToken,
+ IN KPROCESSOR_MODE PreviousMode
+ );
+
+
+
+NTSTATUS
+SepRmCallLsa(
+ PSEP_WORK_ITEM SepWorkItem
+ );
+
+
+
+BOOLEAN
+SepInitializeWorkList(
+ VOID
+ );
+
+
+
+BOOLEAN
+SepRmInitPhase0(
+ );
+
+
+VOID
+SepApplyAclToObject (
+ IN PACL Acl,
+ IN PGENERIC_MAPPING GenericMapping
+ );
+
+VOID
+SepConcatenatePrivileges(
+ IN PPRIVILEGE_SET TargetPrivilegeSet,
+ IN ULONG TargetBufferSize,
+ IN PPRIVILEGE_SET SourcePrivilegeSet
+ );
+
+BOOLEAN
+SepTokenIsOwner(
+ IN PACCESS_TOKEN Token,
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN BOOLEAN TokenLocked
+ );
+
+VOID
+SepPrintAcl (
+ IN PACL Acl
+ );
+
+VOID
+SepPrintSid(
+ IN PSID Sid
+ );
+
+VOID
+SepDumpSecurityDescriptor(
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN PSZ TitleString
+ );
+
+BOOLEAN
+SepSidTranslation(
+ PSID Sid,
+ PSTRING AccountName
+ );
+
+VOID
+SepDumpTokenInfo(
+ IN PACCESS_TOKEN Token
+ );
+
+VOID
+SepDumpString(
+ IN PUNICODE_STRING String
+ );
+
+BOOLEAN
+SepSidInToken (
+ IN PACCESS_TOKEN Token,
+ IN PSID Sid
+ );
+
+
+BOOLEAN
+SepValidateAce (
+ IN PVOID Ace,
+ IN PACL Dacl
+ );
+
+VOID
+SepExamineSacl(
+ IN PACL Sacl,
+ IN PACCESS_TOKEN Token,
+ IN ACCESS_MASK DesiredAccess,
+ IN BOOLEAN AccessGranted,
+ OUT PBOOLEAN GenerateAudit,
+ OUT PBOOLEAN GenerateAlarm
+ );
+
+NTSTATUS
+SepCreateServerAcl(
+ IN PACL NewDacl,
+ IN BOOLEAN DaclUntrusted,
+ IN PSID ServerSid,
+ OUT PACL *ServerDacl,
+ OUT BOOLEAN *ServerDaclAllocated
+ );
+
+
+VOID
+SepCopyString (
+ IN PUNICODE_STRING SourceString,
+ OUT PUNICODE_STRING *DestString
+ );
+
+VOID
+SepAssemblePrivileges(
+ IN ULONG PrivilegeCount,
+ IN BOOLEAN SystemSecurity,
+ IN BOOLEAN WriteOwner,
+ OUT PPRIVILEGE_SET *Privileges
+ );
+
+
+PUNICODE_STRING
+SepQueryTypeString(
+ IN PVOID Object
+ );
+
+
+POBJECT_NAME_INFORMATION
+SepQueryNameString(
+ IN PVOID Object
+ );
+
+BOOLEAN
+SepFilterPrivilegeAudits(
+ IN PPRIVILEGE_SET PrivilegeSet
+ );
+
+BOOLEAN
+SepQueueWorkItem(
+ IN PSEP_LSA_WORK_ITEM LsaWorkItem,
+ IN BOOLEAN ForceQueue
+ );
+
+PSEP_LSA_WORK_ITEM
+SepDequeueWorkItem(
+ VOID
+ );
+
+VOID
+SepAdtGenerateDiscardAudit(
+ VOID
+ );
+
+BOOLEAN
+SepAdtValidateAuditBounds(
+ ULONG Upper,
+ ULONG Lower
+ );
+
+NTSTATUS
+SepAdtInitializeCrashOnFail(
+ VOID
+ );
+
+BOOLEAN
+SepAdtInitializePrivilegeAuditing(
+ VOID
+ );
+
+NTSTATUS
+SepCopyProxyData (
+ OUT PSECURITY_TOKEN_PROXY_DATA * DestProxyData,
+ IN PSECURITY_TOKEN_PROXY_DATA SourceProxyData
+ );
+
+VOID
+SepFreeProxyData (
+ IN PSECURITY_TOKEN_PROXY_DATA ProxyData
+ );
+
+NTSTATUS
+SepProbeAndCaptureQosData(
+ IN PSECURITY_ADVANCED_QUALITY_OF_SERVICE CapturedSecurityQos
+ );
+
+#endif // _SEP_
diff --git a/private/ntos/se/sepaudit.c b/private/ntos/se/sepaudit.c
new file mode 100644
index 000000000..6fb6fc17d
--- /dev/null
+++ b/private/ntos/se/sepaudit.c
@@ -0,0 +1,2629 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sepaudit.c
+
+Abstract:
+
+ This Module implements the audit and alarm procedures that are
+ private to the security component.
+
+Author:
+
+ Robert Reichel (robertre) September 10, 1991
+
+Environment:
+
+ Kernel Mode
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntlsa.h>
+#include <msaudite.h>
+#include "tokenp.h"
+#include "adt.h"
+#include "adtp.h"
+
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE,SeAuditHandleDuplication)
+// #pragma alloc_text(PAGE,SepAdtAuditThisEvent)
+#pragma alloc_text(PAGE,SepAdtPrivilegeObjectAuditAlarm)
+#pragma alloc_text(PAGE,SepAdtPrivilegedServiceAuditAlarm)
+#pragma alloc_text(PAGE,SepAdtOpenObjectAuditAlarm)
+#pragma alloc_text(PAGE,SepAdtOpenObjectForDeleteAuditAlarm)
+#pragma alloc_text(PAGE,SepAdtHandleAuditAlarm)
+#pragma alloc_text(PAGE,SepAdtObjectReferenceAuditAlarm)
+#pragma alloc_text(PAGE,SepQueryNameString)
+#pragma alloc_text(PAGE,SepQueryTypeString)
+#pragma alloc_text(PAGE,SeAuditProcessCreation)
+#pragma alloc_text(PAGE,SeAuditProcessExit)
+#pragma alloc_text(PAGE,SepAdtGenerateDiscardAudit)
+#endif
+
+
+#define SepSetParmTypeSid( AuditParameters, Index, Sid ) \
+ { \
+ (AuditParameters).Parameters[(Index)].Type = SeAdtParmTypeSid; \
+ (AuditParameters).Parameters[(Index)].Length = RtlLengthSid( (Sid) ); \
+ (AuditParameters).Parameters[(Index)].Address = (Sid); \
+ }
+
+
+#define SepSetParmTypeString( AuditParameters, Index, String ) \
+ { \
+ (AuditParameters).Parameters[(Index)].Type = SeAdtParmTypeString; \
+ (AuditParameters).Parameters[(Index)].Length = \
+ sizeof(UNICODE_STRING)+(String)->Length; \
+ (AuditParameters).Parameters[(Index)].Address = (String); \
+ }
+
+
+#define SepSetParmTypeFileSpec( AuditParameters, Index, String ) \
+ { \
+ (AuditParameters).Parameters[(Index)].Type = SeAdtParmTypeFileSpec; \
+ (AuditParameters).Parameters[(Index)].Length = \
+ sizeof(UNICODE_STRING)+(String)->Length; \
+ (AuditParameters).Parameters[(Index)].Address = (String); \
+ }
+
+#define SepSetParmTypeUlong( AuditParameters, Index, Ulong ) \
+ { \
+ (AuditParameters).Parameters[(Index)].Type = SeAdtParmTypeUlong; \
+ (AuditParameters).Parameters[(Index)].Length = sizeof( (Ulong) ); \
+ (AuditParameters).Parameters[(Index)].Data[0] = (ULONG)(Ulong); \
+ }
+
+#define SepSetParmTypeNoLogon( AuditParameters, Index ) \
+ { \
+ (AuditParameters).Parameters[(Index)].Type = SeAdtParmTypeNoLogonId; \
+ }
+
+#define SepSetParmTypeLogonId( AuditParameters, Index, LogonId ) \
+ { \
+ LUID UNALIGNED * TmpLuid; \
+ \
+ (AuditParameters).Parameters[(Index)].Type = SeAdtParmTypeLogonId; \
+ (AuditParameters).Parameters[(Index)].Length = sizeof( (LogonId) ); \
+ TmpLuid = (LUID UNALIGNED *)(&(AuditParameters).Parameters[(Index)].Data[0]); \
+ *TmpLuid = (LogonId); \
+ }
+
+#define SepSetParmTypeAccessMask( AuditParameters, Index, AccessMask, ObjectTypeIndex ) \
+ { \
+ (AuditParameters).Parameters[(Index)].Type = SeAdtParmTypeAccessMask; \
+ (AuditParameters).Parameters[(Index)].Length = sizeof( ACCESS_MASK ); \
+ (AuditParameters).Parameters[(Index)].Data[0] = (AccessMask); \
+ (AuditParameters).Parameters[(Index)].Data[1] = (ObjectTypeIndex); \
+ }
+
+#define SepSetParmTypePrivileges( AuditParameters, Index, Privileges ) \
+ { \
+ (AuditParameters).Parameters[(Index)].Type = SeAdtParmTypePrivs; \
+ (AuditParameters).Parameters[(Index)].Length = SepPrivilegeSetSize( (Privileges) ); \
+ (AuditParameters).Parameters[(Index)].Address = (Privileges); \
+ }
+
+
+
+BOOLEAN
+SepAdtPrivilegeObjectAuditAlarm (
+ IN PUNICODE_STRING CapturedSubsystemName OPTIONAL,
+ IN PVOID HandleId,
+ IN PTOKEN ClientToken OPTIONAL,
+ IN PTOKEN PrimaryToken,
+ IN PVOID ProcessId,
+ IN ACCESS_MASK DesiredAccess,
+ IN PPRIVILEGE_SET CapturedPrivileges,
+ IN BOOLEAN AccessGranted
+ )
+
+/*++
+
+Routine Description:
+
+ Implements NtPrivilegeObjectAuditAlarm after parameters have been
+ captured.
+
+ This routine is used to generate audit and alarm messages when an
+ attempt is made to perform privileged operations on a protected
+ subsystem object after the object is already opened. This routine may
+ result in several messages being generated and sent to Port objects.
+ This may result in a significant latency before returning. Design of
+ routines that must call this routine must take this potential latency
+ into account. This may have an impact on the approach taken for data
+ structure mutex locking, for example.
+
+ This API requires the caller have SeTcbPrivilege privilege. The test
+ for this privilege is always against the primary token of the calling
+ process, allowing the caller to be impersonating a client during the
+ call with no ill effects.
+
+ This routine will create an SE_ADT_PARAMETERS array organized as follows:
+
+ Parameter[0] - User Sid
+
+ Parameter[1] - Subsystem name (if available)
+
+ Parameter[2] - New handle ID
+
+ Parameter[3] - Subject's process id
+
+ Parameter[4] - Subject's primary authentication ID
+
+ Parameter[5] - Subject's client authentication ID
+
+ Parameter[6] - Privileges used for open
+
+Arguments:
+
+ CapturedSubsystemName - Supplies a name string identifying the
+ subsystem calling the routine.
+
+ HandleId - A unique value representing the client's handle to the
+ object.
+
+ ClientToken - Optionally provides a pointer to the client token
+ (only if the caller is currently impersonating)
+
+ PrimaryToken - Provides a pointer to the caller's primary token.
+
+ DesiredAccess - The desired access mask. This mask must have been
+ previously mapped to contain no generic accesses.
+
+ CapturedPrivileges - The set of privileges required for the requested
+ operation. Those privileges that were held by the subject are
+ marked using the UsedForAccess flag of the attributes
+ associated with each privilege.
+
+ AccessGranted - Indicates whether the requested access was granted or
+ not. A value of TRUE indicates the access was granted. A value of
+ FALSE indicates the access was not granted.
+
+Return value:
+
+--*/
+{
+ SE_ADT_PARAMETER_ARRAY AuditParameters;
+ PSID CapturedUserSid;
+ LUID ClientAuthenticationId;
+ LUID PrimaryAuthenticationId;
+
+ PAGED_CODE();
+
+ //
+ // Determine if we are auditing the use of privileges
+ //
+
+ if ( SepAdtAuditThisEvent( AuditCategoryPrivilegeUse, &AccessGranted ) &&
+ SepFilterPrivilegeAudits( CapturedPrivileges )) {
+
+ if ( ARGUMENT_PRESENT( ClientToken )) {
+
+ CapturedUserSid = SepTokenUserSid( ClientToken );
+ ClientAuthenticationId = SepTokenAuthenticationId( ClientToken );
+
+ } else {
+
+ CapturedUserSid = SepTokenUserSid( PrimaryToken );
+ }
+
+ if ( RtlEqualSid( SeLocalSystemSid, CapturedUserSid )) {
+
+ return (FALSE);
+ }
+
+ PrimaryAuthenticationId = SepTokenAuthenticationId( PrimaryToken );
+
+ //
+ // A completely zero'd entry will be interpreted
+ // as a "null string" or not supplied parameter.
+ //
+ // Initializing the entire array up front will allow
+ // us to avoid filling in each not supplied entry.
+ //
+
+ RtlZeroMemory (
+ (PVOID) &AuditParameters,
+ sizeof( AuditParameters )
+ );
+
+ ASSERT( SeAdtParmTypeNone == 0 );
+
+ AuditParameters.CategoryId = SE_CATEGID_PRIVILEGE_USE;
+ AuditParameters.AuditId = SE_AUDITID_PRIVILEGED_OBJECT;
+ AuditParameters.ParameterCount = 0;
+
+ if ( AccessGranted ) {
+
+ AuditParameters.Type = EVENTLOG_AUDIT_SUCCESS;
+
+ } else {
+
+ AuditParameters.Type = EVENTLOG_AUDIT_FAILURE;
+ }
+
+ //
+ // Parameter[0] - User Sid
+ //
+
+ SepSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, CapturedUserSid );
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[1] - Subsystem name (if available)
+ //
+
+ SepSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, CapturedSubsystemName );
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[1] - Subsystem name (if available)
+ //
+
+ SepSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, CapturedSubsystemName );
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[2] - New handle ID
+ //
+
+ SepSetParmTypeUlong( AuditParameters, AuditParameters.ParameterCount, HandleId );
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[3] - Subject's process id
+ //
+
+ SepSetParmTypeUlong( AuditParameters, AuditParameters.ParameterCount, ProcessId );
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[4] - Subject's primary authentication ID
+ //
+
+ SepSetParmTypeLogonId( AuditParameters, AuditParameters.ParameterCount, PrimaryAuthenticationId );
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[5] - Subject's client authentication ID
+ //
+
+ if ( ARGUMENT_PRESENT( ClientToken )) {
+
+ SepSetParmTypeLogonId( AuditParameters, AuditParameters.ParameterCount, ClientAuthenticationId );
+
+ } else {
+
+ SepSetParmTypeNoLogon( AuditParameters, AuditParameters.ParameterCount );
+ }
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[6] - Privileges used for open
+ //
+
+ if ( (CapturedPrivileges != NULL) && (CapturedPrivileges->PrivilegeCount > 0) ) {
+
+ SepSetParmTypePrivileges( AuditParameters, AuditParameters.ParameterCount, CapturedPrivileges );
+ }
+
+ AuditParameters.ParameterCount++;
+
+ SepAdtLogAuditRecord( &AuditParameters );
+
+ return( TRUE );
+
+ }
+
+ return( FALSE );
+}
+
+
+VOID
+SepAdtPrivilegedServiceAuditAlarm (
+ IN PUNICODE_STRING CapturedSubsystemName,
+ IN PUNICODE_STRING CapturedServiceName,
+ IN PTOKEN ClientToken OPTIONAL,
+ IN PTOKEN PrimaryToken,
+ IN PPRIVILEGE_SET CapturedPrivileges,
+ IN BOOLEAN AccessGranted
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is the active part of NtPrivilegedServiceAuditAlarm.
+
+ This routine is used to generate audit and alarm messages when an
+ attempt is made to perform privileged system service operations. This
+ routine may result in several messages being generated and sent to Port
+ objects. This may result in a significant latency before returning.
+ Design of routines that must call this routine must take this potential
+ latency into account. This may have an impact on the approach taken
+ for data structure mutex locking, for example.
+
+ This API requires the caller have SeTcbPrivilege privilege. The test
+ for this privilege is always against the primary token of the calling
+ process, allowing the caller to be impersonating a client during the
+ call with no ill effects. The test for this privilege is assumed to
+ have occurred at a higher level.
+
+ This routine will create an SE_ADT_PARAMETERS array organized as follows:
+
+ Parameter[0] - User Sid
+
+ Parameter[1] - Subsystem name (if available)
+
+ Parameter[2] - Subject's primary authentication ID
+
+ Parameter[3] - Subject's client authentication ID
+
+ Parameter[4] - Privileges used for open
+
+Arguments:
+
+ SubsystemName - Supplies a name string identifying the subsystem
+ calling the routine.
+
+ ServiceName - Supplies a name of the privileged subsystem service. For
+ example, "RESET RUNTIME LOCAL SECURITY POLICY" might be specified
+ by a Local Security Authority service used to update the local
+ security policy database.
+
+ ClientToken - Optionally provides a pointer to the client token
+ (only if the caller is currently impersonating)
+
+ PrimaryToken - Provides a pointer to the caller's primary token.
+
+ Privileges - Points to a set of privileges required to perform the
+ privileged operation. Those privileges that were held by the
+ subject are marked using the UsedForAccess flag of the
+ attributes associated with each privilege.
+
+ AccessGranted - Indicates whether the requested access was granted or
+ not. A value of TRUE indicates the access was granted. A value of
+ FALSE indicates the access was not granted.
+
+
+Return value:
+
+
+--*/
+
+{
+
+ SE_ADT_PARAMETER_ARRAY AuditParameters;
+ PSID CapturedUserSid;
+ LUID ClientAuthenticationId;
+ LUID PrimaryAuthenticationId;
+ PUNICODE_STRING SubsystemName;
+
+ PAGED_CODE();
+
+ //
+ // Determine if we are auditing privileged services
+ //
+
+ if ( SepAdtAuditThisEvent( AuditCategoryPrivilegeUse, &AccessGranted )) {
+
+ if ( ARGUMENT_PRESENT( ClientToken )) {
+
+ CapturedUserSid = SepTokenUserSid( ClientToken );
+ ClientAuthenticationId = SepTokenAuthenticationId( ClientToken );
+
+ } else {
+
+ CapturedUserSid = SepTokenUserSid( PrimaryToken );
+ }
+
+ PrimaryAuthenticationId = SepTokenAuthenticationId( PrimaryToken );
+
+ if ( !ARGUMENT_PRESENT( CapturedSubsystemName )) {
+
+ SubsystemName = &SeSubsystemName;
+
+ } else {
+
+ SubsystemName = CapturedSubsystemName;
+ }
+
+ //
+ // A completely zero'd entry will be interpreted
+ // as a "null string" or not supplied parameter.
+ //
+ // Initializing the entire array up front will allow
+ // us to avoid filling in each not supplied entry.
+ //
+
+ RtlZeroMemory (
+ (PVOID) &AuditParameters,
+ sizeof( AuditParameters )
+ );
+
+ ASSERT( SeAdtParmTypeNone == 0 );
+
+ AuditParameters.CategoryId = SE_CATEGID_PRIVILEGE_USE;
+ AuditParameters.AuditId = SE_AUDITID_PRIVILEGED_SERVICE;
+ AuditParameters.ParameterCount = 0;
+
+ if ( AccessGranted ) {
+
+ AuditParameters.Type = EVENTLOG_AUDIT_SUCCESS;
+
+ } else {
+
+ AuditParameters.Type = EVENTLOG_AUDIT_FAILURE;
+ }
+
+
+ //
+ // Parameter[0] - User Sid
+ //
+
+ SepSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, CapturedUserSid );
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[1] - Subsystem name (if available)
+ //
+
+ SepSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, SubsystemName );
+
+ AuditParameters.ParameterCount++;
+
+
+ //
+ // Parameter[2] - Server
+ //
+
+ SepSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, SubsystemName );
+
+ AuditParameters.ParameterCount++;
+
+
+ //
+ // Parameter[3] - Service name (if available)
+ //
+
+ if ( ARGUMENT_PRESENT( CapturedServiceName )) {
+
+ SepSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, CapturedServiceName );
+ }
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[3] - Subject's primary authentication ID
+ //
+
+
+ SepSetParmTypeLogonId( AuditParameters, AuditParameters.ParameterCount, PrimaryAuthenticationId );
+
+ AuditParameters.ParameterCount++;
+
+
+ //
+ // Parameter[4] - Subject's client authentication ID
+ //
+
+ if ( ARGUMENT_PRESENT( ClientToken )) {
+
+ SepSetParmTypeLogonId( AuditParameters, AuditParameters.ParameterCount, ClientAuthenticationId );
+
+ } else {
+
+ SepSetParmTypeNoLogon( AuditParameters, AuditParameters.ParameterCount );
+ }
+
+ AuditParameters.ParameterCount++;
+
+
+ //
+ // Parameter[5] - Privileges used for open
+ //
+
+
+ if ( (CapturedPrivileges != NULL) && (CapturedPrivileges->PrivilegeCount > 0) ) {
+
+ SepSetParmTypePrivileges( AuditParameters, AuditParameters.ParameterCount, CapturedPrivileges );
+ }
+
+ AuditParameters.ParameterCount++;
+
+
+ SepAdtLogAuditRecord( &AuditParameters );
+
+ }
+
+}
+
+
+
+
+
+
+BOOLEAN
+SepAdtOpenObjectAuditAlarm (
+ IN PUNICODE_STRING CapturedSubsystemName,
+ IN PVOID *HandleId OPTIONAL,
+ IN PUNICODE_STRING CapturedObjectTypeName,
+ IN PVOID Object OPTIONAL,
+ IN PUNICODE_STRING CapturedObjectName OPTIONAL,
+ IN PTOKEN ClientToken OPTIONAL,
+ IN PTOKEN PrimaryToken,
+ IN ACCESS_MASK DesiredAccess,
+ IN ACCESS_MASK GrantedAccess,
+ IN PLUID OperationId,
+ IN PPRIVILEGE_SET CapturedPrivileges OPTIONAL,
+ IN BOOLEAN ObjectCreated,
+ IN BOOLEAN AccessGranted,
+ IN BOOLEAN GenerateAudit,
+ IN BOOLEAN GenerateAlarm,
+ IN HANDLE ProcessID
+ )
+
+/*++
+
+ Routine Description:
+
+ Implements NtOpenObjectAuditAlarm after parameters have been captured.
+
+ This routine is used to generate audit and alarm messages when an
+ attempt is made to access an existing protected subsystem object or
+ create a new one. This routine may result in several messages being
+ generated and sent to Port objects. This may result in a significant
+ latency before returning. Design of routines that must call this
+ routine must take this potential latency into account. This may have
+ an impact on the approach taken for data structure mutex locking, for
+ example. This API requires the caller have SeTcbPrivilege privilege.
+ The test for this privilege is always against the primary token of the
+ calling process, not the impersonation token of the thread.
+
+
+ This routine will create an SE_ADT_PARAMETERS array organized as follows:
+
+ Parameter[0] - User Sid
+
+ Parameter[1] - Subsystem name (if available)
+
+ Parameter[2] - Server name (if available)
+
+ Parameter[3] - Object Type Name
+
+ Parameter[4] - Object Name
+
+ Parameter[5] - New handle ID
+
+ Parameter[6] - Subject's process id
+
+ Parameter[7] - Subject's primary authentication ID
+
+ Parameter[8] - Subject's client authentication ID
+
+ Parameter[9] - DesiredAccess mask
+
+ Parameter[10] - Privileges used for open
+
+Arguments:
+
+ CapturedSubsystemName - Supplies a name string identifying the
+ subsystem calling the routine.
+
+ HandleId - A unique value representing the client's handle to the
+ object. If the access attempt was not successful (AccessGranted is
+ FALSE), then this parameter is ignored.
+
+ CapturedObjectTypeName - Supplies the name of the type of object being
+ accessed.
+
+ CapturedObjectName - Supplies the name of the object the client
+ accessed or attempted to access.
+
+ CapturedSecurityDescriptor - A pointer to the security descriptor of
+ the object being accessed.
+
+ ClientToken - Optionally provides a pointer to the client token
+ (only if the caller is currently impersonating)
+
+ PrimaryToken - Provides a pointer to the caller's primary token.
+
+ DesiredAccess - The desired access mask. This mask must have been
+ previously mapped to contain no generic accesses.
+
+ GrantedAccess - The mask of accesses that were actually granted.
+
+ CapturedPrivileges - Optionally points to a set of privileges that were
+ required for the access attempt. Those privileges that were held
+ by the subject are marked using the UsedForAccess flag of the
+ attributes associated with each privilege.
+
+ ObjectCreation - A boolean flag indicating whether the access will
+ result in a new object being created if granted. A value of TRUE
+ indicates an object will be created, FALSE indicates an existing
+ object will be opened.
+
+ AccessGranted - Indicates whether the requested access was granted or
+ not. A value of TRUE indicates the access was granted. A value of
+ FALSE indicates the access was not granted.
+
+ GenerateOnClose - Points to a boolean that is set by the audit
+ generation routine and must be passed to NtCloseObjectAuditAlarm()
+ when the object handle is closed.
+
+ GenerateAudit - Indicates if we should generate an audit for this operation.
+
+ GenerateAlarm - Indicates if we should generate an alarm for this operation.
+
+Return Value:
+
+ Returns TRUE if audit is generated, FALSE otherwise.
+
+--*/
+
+{
+ SE_ADT_PARAMETER_ARRAY AuditParameters;
+ ULONG ObjectTypeIndex;
+ PSID CapturedUserSid;
+ LUID PrimaryAuthenticationId;
+ LUID ClientAuthenticationId;
+
+ PAGED_CODE();
+
+ if ( ARGUMENT_PRESENT( ClientToken )) {
+
+ CapturedUserSid = SepTokenUserSid( ClientToken );
+ ClientAuthenticationId = SepTokenAuthenticationId( ClientToken );
+
+ } else {
+
+ CapturedUserSid = SepTokenUserSid( PrimaryToken );
+ }
+
+ PrimaryAuthenticationId = SepTokenAuthenticationId( PrimaryToken );
+
+ //
+ // A completely zero'd entry will be interpreted
+ // as a "null string" or not supplied parameter.
+ //
+ // Initializing the entire array up front will allow
+ // us to avoid filling in each not supplied entry.
+ //
+
+ RtlZeroMemory (
+ (PVOID) &AuditParameters,
+ sizeof( AuditParameters )
+ );
+
+ ASSERT( SeAdtParmTypeNone == 0 );
+
+ AuditParameters.CategoryId = SE_CATEGID_OBJECT_ACCESS;
+ AuditParameters.AuditId = SE_AUDITID_OPEN_HANDLE;
+ AuditParameters.ParameterCount = 0;
+
+ if ( AccessGranted ) {
+
+ AuditParameters.Type = EVENTLOG_AUDIT_SUCCESS;
+
+ } else {
+
+ AuditParameters.Type = EVENTLOG_AUDIT_FAILURE;
+ }
+
+ //
+ // Parameter[0] - User Sid
+ //
+
+ SepSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, CapturedUserSid );
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[1] - Subsystem name (if available)
+ //
+
+ SepSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, CapturedSubsystemName );
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[2] - Object Server (if available)
+ //
+
+ if ( ARGUMENT_PRESENT( CapturedSubsystemName )) {
+
+ SepSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, CapturedSubsystemName );
+ }
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[3] - Object Type Name
+ //
+
+ if ( ARGUMENT_PRESENT( CapturedObjectTypeName )) {
+
+ SepSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, CapturedObjectTypeName );
+ ObjectTypeIndex = AuditParameters.ParameterCount;
+ }
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[4] - Object Name
+ //
+
+ if ( ARGUMENT_PRESENT( CapturedObjectName )) {
+
+ SepSetParmTypeFileSpec( AuditParameters, AuditParameters.ParameterCount, CapturedObjectName );
+ }
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[5] - New handle ID
+ //
+
+ if ( ARGUMENT_PRESENT( HandleId )) {
+
+ SepSetParmTypeUlong( AuditParameters, AuditParameters.ParameterCount, *HandleId );
+ }
+
+ AuditParameters.ParameterCount++;
+
+ if ( ARGUMENT_PRESENT( OperationId )) {
+
+ SepSetParmTypeUlong( AuditParameters, AuditParameters.ParameterCount, (*OperationId).HighPart );
+
+ AuditParameters.ParameterCount++;
+
+ SepSetParmTypeUlong( AuditParameters, AuditParameters.ParameterCount, (*OperationId).LowPart );
+
+ AuditParameters.ParameterCount++;
+
+ } else {
+
+ AuditParameters.ParameterCount += 2;
+ }
+
+ //
+ // Parameter[6] - Subject's process id
+ //
+
+ SepSetParmTypeUlong( AuditParameters, AuditParameters.ParameterCount, ProcessID );
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[7] - Subject's primary authentication ID
+ //
+
+ SepSetParmTypeLogonId( AuditParameters, AuditParameters.ParameterCount, PrimaryAuthenticationId );
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[8] - Subject's client authentication ID
+ //
+
+ if ( ARGUMENT_PRESENT( ClientToken )) {
+
+ SepSetParmTypeLogonId( AuditParameters, AuditParameters.ParameterCount, ClientAuthenticationId );
+
+ } else {
+
+ SepSetParmTypeNoLogon( AuditParameters, AuditParameters.ParameterCount );
+ }
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[9] - DesiredAccess mask
+ //
+
+ if ( AccessGranted ) {
+
+ SepSetParmTypeAccessMask( AuditParameters, AuditParameters.ParameterCount, GrantedAccess, ObjectTypeIndex );
+
+ } else {
+
+ SepSetParmTypeAccessMask( AuditParameters, AuditParameters.ParameterCount, DesiredAccess, ObjectTypeIndex );
+ }
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[10] - Privileges used for open
+ //
+
+ if ( (CapturedPrivileges != NULL) && (CapturedPrivileges->PrivilegeCount > 0) ) {
+
+ SepSetParmTypePrivileges( AuditParameters, AuditParameters.ParameterCount, CapturedPrivileges );
+ }
+
+ AuditParameters.ParameterCount++;
+
+ SepAdtLogAuditRecord( &AuditParameters );
+
+ return( TRUE );
+}
+
+
+BOOLEAN
+SepAdtOpenObjectForDeleteAuditAlarm (
+ IN PUNICODE_STRING CapturedSubsystemName,
+ IN PVOID *HandleId OPTIONAL,
+ IN PUNICODE_STRING CapturedObjectTypeName,
+ IN PVOID Object OPTIONAL,
+ IN PUNICODE_STRING CapturedObjectName OPTIONAL,
+ IN PTOKEN ClientToken OPTIONAL,
+ IN PTOKEN PrimaryToken,
+ IN ACCESS_MASK DesiredAccess,
+ IN ACCESS_MASK GrantedAccess,
+ IN PLUID OperationId,
+ IN PPRIVILEGE_SET CapturedPrivileges OPTIONAL,
+ IN BOOLEAN ObjectCreated,
+ IN BOOLEAN AccessGranted,
+ IN BOOLEAN GenerateAudit,
+ IN BOOLEAN GenerateAlarm,
+ IN HANDLE ProcessID
+ )
+
+/*++
+
+ Routine Description:
+
+ Implements SeOpenObjectForDeleteAuditAlarm after parameters have been
+ captured.
+
+ This routine is used to generate audit and alarm messages when an
+ attempt is made to access an existing protected subsystem object or
+ create a new one. This routine may result in several messages being
+ generated and sent to Port objects. This may result in a significant
+ latency before returning. Design of routines that must call this
+ routine must take this potential latency into account. This may have
+ an impact on the approach taken for data structure mutex locking, for
+ example. This API requires the caller have SeTcbPrivilege privilege.
+ The test for this privilege is always against the primary token of the
+ calling process, not the impersonation token of the thread.
+
+
+ This routine will create an SE_ADT_PARAMETERS array organized as follows:
+
+ Parameter[0] - User Sid
+
+ Parameter[1] - Subsystem name (if available)
+
+ Parameter[2] - Server name (if available)
+
+ Parameter[3] - Object Type Name
+
+ Parameter[4] - Object Name
+
+ Parameter[5] - New handle ID
+
+ Parameter[6] - Subject's process id
+
+ Parameter[7] - Subject's primary authentication ID
+
+ Parameter[8] - Subject's client authentication ID
+
+ Parameter[9] - DesiredAccess mask
+
+ Parameter[10] - Privileges used for open
+
+Arguments:
+
+ CapturedSubsystemName - Supplies a name string identifying the
+ subsystem calling the routine.
+
+ HandleId - A unique value representing the client's handle to the
+ object. If the access attempt was not successful (AccessGranted is
+ FALSE), then this parameter is ignored.
+
+ CapturedObjectTypeName - Supplies the name of the type of object being
+ accessed.
+
+ CapturedObjectName - Supplies the name of the object the client
+ accessed or attempted to access.
+
+ CapturedSecurityDescriptor - A pointer to the security descriptor of
+ the object being accessed.
+
+ ClientToken - Optionally provides a pointer to the client token
+ (only if the caller is currently impersonating)
+
+ PrimaryToken - Provides a pointer to the caller's primary token.
+
+ DesiredAccess - The desired access mask. This mask must have been
+ previously mapped to contain no generic accesses.
+
+ GrantedAccess - The mask of accesses that were actually granted.
+
+ CapturedPrivileges - Optionally points to a set of privileges that were
+ required for the access attempt. Those privileges that were held
+ by the subject are marked using the UsedForAccess flag of the
+ attributes associated with each privilege.
+
+ ObjectCreation - A boolean flag indicating whether the access will
+ result in a new object being created if granted. A value of TRUE
+ indicates an object will be created, FALSE indicates an existing
+ object will be opened.
+
+ AccessGranted - Indicates whether the requested access was granted or
+ not. A value of TRUE indicates the access was granted. A value of
+ FALSE indicates the access was not granted.
+
+ GenerateOnClose - Points to a boolean that is set by the audit
+ generation routine and must be passed to NtCloseObjectAuditAlarm()
+ when the object handle is closed.
+
+ GenerateAudit - Indicates if we should generate an audit for this operation.
+
+ GenerateAlarm - Indicates if we should generate an alarm for this operation.
+
+Return Value:
+
+ Returns TRUE if audit is generated, FALSE otherwise.
+
+--*/
+
+{
+ SE_ADT_PARAMETER_ARRAY AuditParameters;
+ ULONG ObjectTypeIndex;
+ PSID CapturedUserSid;
+ LUID PrimaryAuthenticationId;
+ LUID ClientAuthenticationId;
+
+ PAGED_CODE();
+
+ if ( ARGUMENT_PRESENT( ClientToken )) {
+
+ CapturedUserSid = SepTokenUserSid( ClientToken );
+ ClientAuthenticationId = SepTokenAuthenticationId( ClientToken );
+
+ } else {
+
+ CapturedUserSid = SepTokenUserSid( PrimaryToken );
+ }
+
+ PrimaryAuthenticationId = SepTokenAuthenticationId( PrimaryToken );
+
+ //
+ // A completely zero'd entry will be interpreted
+ // as a "null string" or not supplied parameter.
+ //
+ // Initializing the entire array up front will allow
+ // us to avoid filling in each not supplied entry.
+ //
+
+ RtlZeroMemory (
+ (PVOID) &AuditParameters,
+ sizeof( AuditParameters )
+ );
+
+ ASSERT( SeAdtParmTypeNone == 0 );
+
+ AuditParameters.CategoryId = SE_CATEGID_OBJECT_ACCESS;
+ AuditParameters.AuditId = SE_AUDITID_OPEN_OBJECT_FOR_DELETE;
+ AuditParameters.ParameterCount = 0;
+
+ if ( AccessGranted ) {
+
+ AuditParameters.Type = EVENTLOG_AUDIT_SUCCESS;
+
+ } else {
+
+ AuditParameters.Type = EVENTLOG_AUDIT_FAILURE;
+ }
+
+ //
+ // Parameter[0] - User Sid
+ //
+
+ SepSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, CapturedUserSid );
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[1] - Subsystem name (if available)
+ //
+
+ SepSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, CapturedSubsystemName );
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[2] - Object Server (if available)
+ //
+
+ if ( ARGUMENT_PRESENT( CapturedSubsystemName )) {
+
+ SepSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, CapturedSubsystemName );
+ }
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[3] - Object Type Name
+ //
+
+ if ( ARGUMENT_PRESENT( CapturedObjectTypeName )) {
+
+ SepSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, CapturedObjectTypeName );
+ ObjectTypeIndex = AuditParameters.ParameterCount;
+ }
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[4] - Object Name
+ //
+
+ if ( ARGUMENT_PRESENT( CapturedObjectName )) {
+
+ SepSetParmTypeFileSpec( AuditParameters, AuditParameters.ParameterCount, CapturedObjectName );
+ }
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[5] - New handle ID
+ //
+
+ if ( ARGUMENT_PRESENT( HandleId )) {
+
+ SepSetParmTypeUlong( AuditParameters, AuditParameters.ParameterCount, *HandleId );
+ }
+
+ AuditParameters.ParameterCount++;
+
+ if ( ARGUMENT_PRESENT( OperationId )) {
+
+ SepSetParmTypeUlong( AuditParameters, AuditParameters.ParameterCount, (*OperationId).HighPart );
+
+ AuditParameters.ParameterCount++;
+
+ SepSetParmTypeUlong( AuditParameters, AuditParameters.ParameterCount, (*OperationId).LowPart );
+
+ AuditParameters.ParameterCount++;
+
+ } else {
+
+ AuditParameters.ParameterCount += 2;
+ }
+
+ //
+ // Parameter[6] - Subject's process id
+ //
+
+ SepSetParmTypeUlong( AuditParameters, AuditParameters.ParameterCount, ProcessID );
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[7] - Subject's primary authentication ID
+ //
+
+ SepSetParmTypeLogonId( AuditParameters, AuditParameters.ParameterCount, PrimaryAuthenticationId );
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[8] - Subject's client authentication ID
+ //
+
+ if ( ARGUMENT_PRESENT( ClientToken )) {
+
+ SepSetParmTypeLogonId( AuditParameters, AuditParameters.ParameterCount, ClientAuthenticationId );
+
+ } else {
+
+ SepSetParmTypeNoLogon( AuditParameters, AuditParameters.ParameterCount );
+ }
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[9] - DesiredAccess mask
+ //
+
+ if ( AccessGranted ) {
+
+ SepSetParmTypeAccessMask( AuditParameters, AuditParameters.ParameterCount, GrantedAccess, ObjectTypeIndex );
+
+ } else {
+
+ SepSetParmTypeAccessMask( AuditParameters, AuditParameters.ParameterCount, DesiredAccess, ObjectTypeIndex );
+ }
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[10] - Privileges used for open
+ //
+
+ if ( (CapturedPrivileges != NULL) && (CapturedPrivileges->PrivilegeCount > 0) ) {
+
+ SepSetParmTypePrivileges( AuditParameters, AuditParameters.ParameterCount, CapturedPrivileges );
+ }
+
+ AuditParameters.ParameterCount++;
+
+ SepAdtLogAuditRecord( &AuditParameters );
+
+ return( TRUE );
+}
+
+
+
+
+VOID
+SepAdtCloseObjectAuditAlarm (
+ IN PUNICODE_STRING CapturedSubsystemName,
+ IN PVOID HandleId,
+ IN PVOID Object,
+ IN PSID UserSid,
+ IN LUID AuthenticationId
+ )
+
+/*++
+
+Routine Description:
+
+ This routine implements NtCloseObjectAuditAlarm after parameters have
+ been captured.
+
+ This routine is used to generate audit and alarm messages when a handle
+ to a protected subsystem object is deleted. This routine may result in
+ several messages being generated and sent to Port objects. This may
+ result in a significant latency before returning. Design of routines
+ that must call this routine must take this potential latency into
+ account. This may have an impact on the approach taken for data
+ structure mutex locking, for example.
+
+ This API requires the caller have SeTcbPrivilege privilege. The test
+ for this privilege is always against the primary token of the calling
+ process, allowing the caller to be impersonating a client during the
+ call with no ill effects. It is assumed that this privilege has been
+ tested at a higher level.
+
+ This routine will create an SE_ADT_PARAMETERS array organized as follows:
+
+ Parameter[0] - User Sid
+
+ Parameter[1] - Subsystem name (if available)
+
+ Parameter[2] - New handle ID
+
+ Parameter[3] - Subject's process id
+
+Arguments:
+
+ CapturedSubsystemName - Supplies a name string identifying the
+ subsystem calling the routine.
+
+ HandleId - A unique value representing the client's handle to the
+ object.
+
+ Object - The address of the object being closed
+
+ UserSid - The Sid identifying the current caller.
+
+
+
+Return value:
+
+ None.
+
+
+--*/
+
+{
+
+ SE_ADT_PARAMETER_ARRAY AuditParameters;
+ BOOLEAN AccessGranted = TRUE;
+ HANDLE ProcessId;
+
+ PAGED_CODE();
+
+ if ( SepAdtAuditThisEvent( AuditCategoryObjectAccess, &AccessGranted ) ) {
+
+ //
+ // A completely zero'd entry will be interpreted
+ // as a "null string" or not supplied parameter.
+ //
+ // Initializing the entire array up front will allow
+ // us to avoid filling in each not supplied entry.
+ //
+
+ RtlZeroMemory (
+ (PVOID) &AuditParameters,
+ sizeof( AuditParameters )
+ );
+
+ ASSERT( SeAdtParmTypeNone == 0 );
+
+ AuditParameters.CategoryId = SE_CATEGID_OBJECT_ACCESS;
+ AuditParameters.AuditId = SE_AUDITID_CLOSE_HANDLE;
+ AuditParameters.ParameterCount = 0;
+ AuditParameters.Type = EVENTLOG_AUDIT_SUCCESS;
+
+
+ //
+ // Parameter[0] - User Sid
+ //
+
+ SepSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, UserSid );
+
+ AuditParameters.ParameterCount++;
+
+
+ //
+ // Parameter[1] - Subsystem name (if available)
+ //
+
+ if ( ARGUMENT_PRESENT( CapturedSubsystemName )) {
+
+ SepSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, CapturedSubsystemName );
+ }
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[2] - Subsystem name (if available)
+ //
+
+ if ( ARGUMENT_PRESENT( CapturedSubsystemName )) {
+
+ SepSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, CapturedSubsystemName );
+ }
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[3] - New handle ID
+ //
+
+ SepSetParmTypeUlong( AuditParameters, AuditParameters.ParameterCount, HandleId );
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[4] - Subject's process id
+ //
+
+ ProcessId = PsProcessAuditId( PsGetCurrentProcess() );
+
+ SepSetParmTypeUlong( AuditParameters, AuditParameters.ParameterCount, ProcessId );
+
+ AuditParameters.ParameterCount++;
+
+ SepAdtLogAuditRecord( &AuditParameters );
+
+ }
+}
+
+
+
+VOID
+SepAdtDeleteObjectAuditAlarm (
+ IN PUNICODE_STRING CapturedSubsystemName,
+ IN PVOID HandleId,
+ IN PVOID Object,
+ IN PSID UserSid,
+ IN LUID AuthenticationId
+ )
+
+/*++
+
+Routine Description:
+
+ This routine implements NtDeleteObjectAuditAlarm after parameters have
+ been captured.
+
+ This routine is used to generate audit and alarm messages when an object
+ in a protected subsystem object is deleted. This routine may result in
+ several messages being generated and sent to Port objects. This may
+ result in a significant latency before returning. Design of routines
+ that must call this routine must take this potential latency into
+ account. This may have an impact on the approach taken for data
+ structure mutex locking, for example.
+
+ This API requires the caller have SeTcbPrivilege privilege. The test
+ for this privilege is always against the primary token of the calling
+ process, allowing the caller to be impersonating a client during the
+ call with no ill effects. It is assumed that this privilege has been
+ tested at a higher level.
+
+ This routine will create an SE_ADT_PARAMETERS array organized as follows:
+
+ Parameter[0] - User Sid
+
+ Parameter[1] - Subsystem name (if available)
+
+ Parameter[2] - Handle ID
+
+ Parameter[3] - Subject's process id
+
+Arguments:
+
+ CapturedSubsystemName - Supplies a name string identifying the
+ subsystem calling the routine.
+
+ HandleId - A unique value representing the client's handle to the
+ object.
+
+ Object - The address of the object being closed
+
+ UserSid - The Sid identifying the current caller.
+
+
+
+Return value:
+
+ None.
+
+
+--*/
+
+{
+
+ SE_ADT_PARAMETER_ARRAY AuditParameters;
+ BOOLEAN AccessGranted = TRUE;
+ HANDLE ProcessId;
+
+ PAGED_CODE();
+
+ if ( SepAdtAuditThisEvent( AuditCategoryObjectAccess, &AccessGranted ) ) {
+
+ //
+ // A completely zero'd entry will be interpreted
+ // as a "null string" or not supplied parameter.
+ //
+ // Initializing the entire array up front will allow
+ // us to avoid filling in each not supplied entry.
+ //
+
+ RtlZeroMemory (
+ (PVOID) &AuditParameters,
+ sizeof( AuditParameters )
+ );
+
+ ASSERT( SeAdtParmTypeNone == 0 );
+
+ AuditParameters.CategoryId = SE_CATEGID_OBJECT_ACCESS;
+ AuditParameters.AuditId = SE_AUDITID_DELETE_OBJECT;
+ AuditParameters.ParameterCount = 0;
+ AuditParameters.Type = EVENTLOG_AUDIT_SUCCESS;
+
+
+ //
+ // Parameter[0] - User Sid
+ //
+
+ SepSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, UserSid );
+
+ AuditParameters.ParameterCount++;
+
+
+ //
+ // Parameter[1] - Subsystem name (if available)
+ //
+
+ if ( ARGUMENT_PRESENT( CapturedSubsystemName )) {
+
+ SepSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, CapturedSubsystemName );
+ }
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[2] - Subsystem name (if available)
+ //
+
+ if ( ARGUMENT_PRESENT( CapturedSubsystemName )) {
+
+ SepSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, CapturedSubsystemName );
+ }
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[3] - New handle ID
+ //
+
+ SepSetParmTypeUlong( AuditParameters, AuditParameters.ParameterCount, HandleId );
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[4] - Subject's process id
+ //
+
+ ProcessId = PsProcessAuditId( PsGetCurrentProcess() );
+
+ SepSetParmTypeUlong( AuditParameters, AuditParameters.ParameterCount, ProcessId );
+
+ AuditParameters.ParameterCount++;
+
+ SepAdtLogAuditRecord( &AuditParameters );
+
+ }
+}
+
+
+
+//
+//VOID
+//SepAdtTraverseAuditAlarm(
+// IN PLUID OperationId,
+// IN PVOID DirectoryObject,
+// IN PSID UserSid,
+// IN LUID AuthenticationId,
+// IN ACCESS_MASK DesiredAccess,
+// IN PPRIVILEGE_SET Privileges OPTIONAL,
+// IN BOOLEAN AccessGranted,
+// IN BOOLEAN GenerateAudit,
+// IN BOOLEAN GenerateAlarm
+// )
+///*++
+//
+//Routine Description:
+//
+// This routine constructs an audit record to record that a request
+// to traverse a directory has occurred.
+//
+//Arguments:
+//
+// OperationID - LUID identifying the operation in progress
+//
+// DirectoryObject - Pointer to the directory being traversed.
+//
+// UserSid - Provides the User Sid for the caller.
+//
+// DesiredAccess - Mask to indicate the traverse access for this object
+// type.
+//
+// Privileges - Optional parameter to indicate any privilges that the
+// subject may have used to gain access to the object.
+//
+// AccessGranted - Indicates if the access was granted or denied based on
+// the access check or privilege check.
+//
+// GenerateAudit - Indicates if we should generate an audit for this operation.
+//
+// GenerateAlarm - Indicates if we should generate an alarm for this operation.
+//
+//Return Value:
+//
+// None.
+//
+//--*/
+//{
+// POLICY_AUDIT_TRAVERSE AuditTraverse;
+//
+// UNREFERENCED_PARAMETER( GenerateAudit );
+// UNREFERENCED_PARAMETER( GenerateAlarm );
+// UNREFERENCED_PARAMETER( DirectoryObject );
+// UNREFERENCED_PARAMETER( DesiredAccess );
+//
+// //
+// // BUGWARNING need a way to get the directory name from
+// // the directory object
+// //
+//
+// AuditTraverse.AccessGranted = AccessGranted;
+// AuditTraverse.DirectoryName = NULL;
+// AuditTraverse.OperationId = *OperationId;
+// AuditTraverse.PrivilegeSet = Privileges;
+// AuditTraverse.UserSid = UserSid;
+// AuditTraverse.AuthenticationId = AuthenticationId;
+//
+//// SepAdtLogAuditRecord( AuditEventTraverse, &AuditTraverse );
+//}
+
+
+
+
+//
+//VOID
+//SepAdtCreateObjectAuditAlarm(
+// IN PLUID OperationID,
+// IN PUNICODE_STRING DirectoryName,
+// IN PUNICODE_STRING ComponentName,
+// IN PSID UserSid,
+// IN LUID AuthenticationId,
+// IN ACCESS_MASK DesiredAccess,
+// IN BOOLEAN AccessGranted,
+// IN BOOLEAN GenerateAudit,
+// IN BOOLEAN GenerateAlarm
+// )
+///*++
+//
+//Routine Description:
+//
+// description-of-function.
+//
+//Arguments:
+//
+//
+// GenerateAudit - Indicates if we should generate an audit for this operation.
+//
+// GenerateAlarm - Indicates if we should generate an alarm for this operation.
+//
+//Return Value:
+//
+// return-value - Description of conditions needed to return value. - or -
+// None.
+//
+//--*/
+//
+//{
+// POLICY_AUDIT_CREATE_OBJECT AuditCreateObject;
+//
+// UNREFERENCED_PARAMETER( GenerateAudit );
+// UNREFERENCED_PARAMETER( GenerateAlarm );
+//
+//
+// AuditCreateObject.AccessGranted = AccessGranted;
+// AuditCreateObject.DesiredAccess = DesiredAccess;
+// AuditCreateObject.DirectoryName = DirectoryName;
+// AuditCreateObject.ComponentName = ComponentName;
+// AuditCreateObject.OperationId = *OperationID;
+// AuditCreateObject.UserSid = UserSid;
+// AuditCreateObject.AuthenticationId = AuthenticationId;
+//
+//// SepAdtLogAuditRecord( AuditEventCreateObject, &AuditCreateObject );
+//}
+
+
+
+//
+//VOID
+//SepAdtImplicitObjectAuditAlarm(
+// IN PLUID OperationId OPTIONAL,
+// IN PVOID Object,
+// IN PSID UserSid,
+// IN ACCESS_MASK DesiredAccess,
+// IN PPRIVILEGE_SET Privileges OPTIONAL,
+// IN BOOLEAN AccessGranted,
+// IN BOOLEAN GenerateAudit,
+// IN BOOLEAN GenerateAlarm
+// )
+///*++
+//
+//Routine Description:
+//
+// description-of-function.
+//
+//Arguments:
+//
+// GenerateAudit - Indicates if we should generate an audit for this operation.
+//
+// GenerateAlarm - Indicates if we should generate an alarm for this operation.
+//
+//
+//Return Value:
+//
+// None
+//
+//--*/
+//
+//{
+// POLICY_AUDIT_IMPLICIT_ACCESS AuditImplicitAccess;
+//
+// UNREFERENCED_PARAMETER( GenerateAudit );
+// UNREFERENCED_PARAMETER( GenerateAlarm );
+//
+//
+// //
+// // BUGWARNING need a way to obtain the object type
+// //
+//
+// AuditImplicitAccess.AccessGranted = AccessGranted;
+// AuditImplicitAccess.DesiredAccess = DesiredAccess;
+// AuditImplicitAccess.ObjectTypeName = NULL;
+// AuditImplicitAccess.OperationId = *OperationId;
+// AuditImplicitAccess.PrivilegeSet = Privileges;
+// AuditImplicitAccess.UserSid = UserSid;
+//
+// SepAdtLogAuditRecord( AuditEventImplicitAccess, &AuditImplicitAccess );
+//}
+
+
+
+
+
+VOID
+SepAdtHandleAuditAlarm(
+ IN PUNICODE_STRING Source,
+ IN LUID OperationId,
+ IN HANDLE Handle,
+ IN PSID UserSid
+ )
+
+/*++
+
+Routine Description:
+
+ Creates an audit record for the creation of an object handle.
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ BOOLEAN AccessGranted = TRUE;
+ SE_ADT_PARAMETER_ARRAY AuditParameters;
+ HANDLE ProcessID;
+
+ PAGED_CODE();
+
+ if ( SepAdtAuditThisEvent( AuditCategoryObjectAccess, &AccessGranted )) {
+
+ //
+ // A completely zero'd entry will be interpreted
+ // as a "null string" or not supplied parameter.
+ //
+ // Initializing the entire array up front will allow
+ // us to avoid filling in each not supplied entry.
+ //
+
+ RtlZeroMemory (
+ (PVOID) &AuditParameters,
+ sizeof( AuditParameters )
+ );
+
+ ASSERT( SeAdtParmTypeNone == 0 );
+
+ AuditParameters.CategoryId = SE_CATEGID_OBJECT_ACCESS;
+ AuditParameters.AuditId = SE_AUDITID_CREATE_HANDLE;
+ AuditParameters.ParameterCount = 0;
+ AuditParameters.Type = EVENTLOG_AUDIT_SUCCESS;
+
+
+ //
+ // Parameter[0] - User Sid
+ //
+
+ SepSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, UserSid );
+
+ AuditParameters.ParameterCount++;
+
+
+ //
+ // Parameter[1] - Subsystem name (if available)
+ //
+
+ if ( ARGUMENT_PRESENT( Source )) {
+
+ SepSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, Source );
+ }
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameter[2] - New handle ID
+ //
+
+ SepSetParmTypeUlong( AuditParameters, AuditParameters.ParameterCount, Handle );
+
+ AuditParameters.ParameterCount++;
+
+ //
+ // Parameters 3,4 - Operation ID
+ //
+
+
+ SepSetParmTypeUlong( AuditParameters, AuditParameters.ParameterCount, OperationId.HighPart );
+
+ AuditParameters.ParameterCount++;
+
+ SepSetParmTypeUlong( AuditParameters, AuditParameters.ParameterCount, OperationId.LowPart );
+
+ AuditParameters.ParameterCount++;
+
+
+ //
+ // Parameter[5] - Subject's process id
+ //
+
+ ProcessID = PsProcessAuditId( PsGetCurrentProcess() );
+
+ SepSetParmTypeUlong( AuditParameters, AuditParameters.ParameterCount, ProcessID );
+
+ AuditParameters.ParameterCount++;
+
+ SepAdtLogAuditRecord( &AuditParameters );
+
+ }
+}
+
+
+
+
+VOID
+SepAdtObjectReferenceAuditAlarm(
+ IN PLUID OperationId OPTIONAL,
+ IN PVOID Object,
+ IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
+ IN ACCESS_MASK DesiredAccess,
+ IN PPRIVILEGE_SET Privileges OPTIONAL,
+ IN BOOLEAN AccessGranted,
+ IN BOOLEAN GenerateAudit,
+ IN BOOLEAN GenerateAlarm
+ )
+
+/*++
+
+Routine Description:
+
+ description-of-function.
+
+ This routine will create an SE_ADT_PARAMETERS array organized as follows:
+
+ Parameter[0] - User Sid
+
+ Parameter[1] - Subsystem name (if available)
+
+ Parameter[2] - Object Type Name
+
+ Parameter[3] - Object Name
+
+ Parameter[4] - Subject's process id
+
+ Parameter[5] - Subject's primary authentication ID
+
+ Parameter[6] - Subject's client authentication ID
+
+ Parameter[7] - DesiredAccess mask
+
+
+Arguments:
+
+ GenerateAudit - Indicates if we should generate an audit for this operation.
+
+ GenerateAlarm - Indicates if we should generate an alarm for this operation.
+
+Return Value:
+
+ return-value - Description of conditions needed to return value. - or -
+ None.
+
+--*/
+
+{
+ SE_ADT_PARAMETER_ARRAY AuditParameters;
+ ULONG ObjectTypeIndex;
+ POBJECT_NAME_INFORMATION ObjectNameInformation;
+ PUNICODE_STRING ObjectTypeInformation;
+ PSID UserSid;
+ LUID PrimaryAuthenticationId;
+ LUID ClientAuthenticationId;
+
+ PTOKEN ClientToken = (PTOKEN)SubjectSecurityContext->ClientToken;
+ PTOKEN PrimaryToken = (PTOKEN)SubjectSecurityContext->PrimaryToken;
+
+ PAGED_CODE();
+
+
+ if ( ARGUMENT_PRESENT( ClientToken )) {
+
+ UserSid = SepTokenUserSid( ClientToken );
+ ClientAuthenticationId = SepTokenAuthenticationId( ClientToken );
+
+ } else {
+
+ UserSid = SepTokenUserSid( PrimaryToken );
+ }
+
+ PrimaryAuthenticationId = SepTokenAuthenticationId( PrimaryToken );
+
+ //
+ // A completely zero'd entry will be interpreted
+ // as a "null string" or not supplied parameter.
+ //
+ // Initializing the entire array up front will allow
+ // us to avoid filling in each not supplied entry.
+ //
+
+ RtlZeroMemory (
+ (PVOID) &AuditParameters,
+ sizeof( AuditParameters )
+ );
+
+ ASSERT( SeAdtParmTypeNone == 0 );
+
+ AuditParameters.CategoryId = SE_CATEGID_DETAILED_TRACKING;
+ AuditParameters.AuditId = SE_AUDITID_INDIRECT_REFERENCE;
+ AuditParameters.ParameterCount = 8;
+
+ if ( AccessGranted ) {
+
+ AuditParameters.Type = EVENTLOG_AUDIT_SUCCESS;
+
+ } else {
+
+ AuditParameters.Type = EVENTLOG_AUDIT_FAILURE;
+ }
+
+ //
+ // Obtain the object name and object type name from the
+ // object.
+ //
+
+ ObjectNameInformation = SepQueryNameString( Object );
+
+
+ ObjectTypeInformation = SepQueryTypeString( Object );
+
+
+
+
+ //
+ // Parameter[0] - User Sid
+ //
+
+ SepSetParmTypeSid( AuditParameters, 0, UserSid );
+
+
+ //
+ // Parameter[1] - Subsystem name (if available)
+ //
+
+ SepSetParmTypeString( AuditParameters, 1, &SeSubsystemName );
+
+
+ //
+ // Parameter[2] - Object Type Name
+ //
+
+ if ( ObjectTypeInformation != NULL ) {
+
+ SepSetParmTypeString( AuditParameters, 2, ObjectTypeInformation );
+ ObjectTypeIndex = 2;
+ }
+
+
+
+ //
+ // Parameter[3] - Object Name
+ //
+
+ if ( ObjectNameInformation != NULL ) {
+
+ SepSetParmTypeString( AuditParameters, 3, &ObjectNameInformation->Name );
+ }
+
+
+
+
+ //
+ // Parameter[4] - Subject's process id
+ //
+
+ //
+ // BUGWARNING: The process Id is currently unavailable.
+ //
+
+ SepSetParmTypeUlong( AuditParameters, 4, SubjectSecurityContext->ProcessAuditId );
+
+
+
+
+ //
+ // Parameter[5] - Subject's primary authentication ID
+ //
+
+
+ SepSetParmTypeLogonId( AuditParameters, 5, PrimaryAuthenticationId );
+
+
+
+
+ //
+ // Parameter[6] - Subject's client authentication ID
+ //
+
+ if ( ARGUMENT_PRESENT( ClientToken )) {
+
+ SepSetParmTypeLogonId( AuditParameters, 6, ClientAuthenticationId );
+
+ } else {
+
+ SepSetParmTypeNoLogon( AuditParameters, 6 );
+
+ }
+
+ //
+ // Parameter[7] - DesiredAccess mask
+ //
+
+
+ SepSetParmTypeAccessMask( AuditParameters, 7, DesiredAccess, ObjectTypeIndex );
+
+
+ SepAdtLogAuditRecord( &AuditParameters );
+
+ if ( ObjectNameInformation != NULL ) {
+ ExFreePool( ObjectNameInformation );
+ }
+
+ if ( ObjectTypeInformation != NULL ) {
+ ExFreePool( ObjectTypeInformation );
+ }
+
+}
+
+
+//VOID
+//SepAdtCreateInstanceAuditAlarm(
+// IN PLUID OperationID,
+// IN PVOID Object,
+// IN PSID UserSid,
+// IN LUID AuthenticationId,
+// IN ACCESS_MASK DesiredAccess,
+// IN PPRIVILEGE_SET Privileges OPTIONAL,
+// IN BOOLEAN AccessGranted,
+// IN BOOLEAN GenerateAudit,
+// IN BOOLEAN GenerateAlarm
+// )
+//
+///*++
+//
+//Routine Description:
+//
+// description-of-function.
+//
+//Arguments:
+//
+// GenerateAudit - Indicates if we should generate an audit for this operation.
+//
+// GenerateAlarm - Indicates if we should generate an alarm for this operation.
+//
+//
+//Return Value:
+//
+// return-value - Description of conditions needed to return value. - or -
+// None.
+//
+//--*/
+//
+//{
+// POLICY_AUDIT_CREATE_INSTANCE AuditCreateInstance;
+//
+// UNREFERENCED_PARAMETER( GenerateAudit );
+// UNREFERENCED_PARAMETER( GenerateAlarm );
+//
+// //
+// // BUGWARNING Must obtain the object type object from the passed
+// // object
+// //
+//
+// AuditCreateInstance.AccessGranted = AccessGranted;
+// AuditCreateInstance.ObjectTypeName = NULL;
+// AuditCreateInstance.OperationId = *OperationID;
+// AuditCreateInstance.UserSid = UserSid;
+// AuditCreateInstance.AuthenticationId = AuthenticationId;
+//
+//// SepAdtLogAuditRecord( AuditEventCreateInstance, &AuditCreateInstance );
+//
+// return;
+//}
+
+
+
+//
+//VOID
+//SeShutdownAuditAlarm(
+// VOID
+// )
+//
+///*++
+//
+//Routine Description:
+//
+// This routine will can a shutdown audit record to be generated.
+//
+// There must be a forced delay after this routine is called to ensure
+// that the generated audit record actually makes it to disk.
+//
+//Arguments:
+//
+// None
+//
+//Return Value:
+//
+// None.
+//
+//--*/
+//
+//{
+// SECURITY_SUBJECT_CONTEXT SubjectSecurityContext;
+// SE_ADT_PARAMETER_ARRAY AuditParameters;
+// UNICODE_STRING SubsystemName;
+// PSID UserSid;
+//
+// //
+// // Make sure we're auditing shutdown events.
+// //
+//
+// if ( SepAdtAuditThisEvent( AuditEventShutdown, NULL )) {
+//
+// SeCaptureSubjectContext( &SubjectSecurityContext );
+//
+//
+// //
+// // A completely zero'd entry will be interpreted
+// // as a "null string" or not supplied parameter.
+// //
+// // Initializing the entire array up front will allow
+// // us to avoid filling in each not supplied entry.
+// //
+//
+// RtlZeroMemory (
+// (PVOID) &AuditParameters,
+// sizeof( AuditParameters )
+// );
+//
+// ASSERT( SeAdtParmTypeNone == 0 );
+//
+// AuditParameters.CategoryId = SE_CATEGID_SYSTEM;
+// AuditParameters.AuditId = SE_AUDITID_SYSTEM_SHUTDOWN;
+// AuditParameters.ParameterCount = 2;
+//
+// UserSid = SepTokenUserSid(EffectiveToken( &SubjectSecurityContext ));
+//
+//
+// RtlInitUnicodeString( &SubsystemName, L"Security" );
+//
+//
+// //
+// // Parameter[0] - User Sid
+// //
+//
+// SepSetParmTypeSid( AuditParameters, 0, UserSid );
+//
+//
+// //
+// // Parameter[1] - Subsystem name (if available)
+// //
+//
+// SepSetParmTypeString( AuditParameters, 1, &SubsystemName );
+//
+//
+// SepAdtLogAuditRecord( &AuditParameters );
+//
+// SeReleaseSubjectContext( &SubjectSecurityContext );
+//
+// }
+//
+//}
+
+
+
+
+
+//
+// BOOLEAN
+// SepAdtAuditThisEvent(
+// IN POLICY_AUDIT_EVENT_TYPE AuditType,
+// IN PBOOLEAN AccessGranted OPTIONAL
+// )
+//
+// /*++
+//
+// Routine Description:
+//
+// This routine will return whether or not to generate an audit log
+// record for the passed event type.
+//
+// Arguments:
+//
+// AuditType - The type of event to be audited.
+//
+// AccessGranted - An optional flag indicating whether or not
+// the operation was successful. This does not all apply to all
+// types of audit events.
+//
+// Note that a pointer to the flag is passed rather than the
+// flag itself, so that we may tell whether or not the argument
+// is present.
+//
+// Return Value:
+//
+// Flag indicating whether or not to proceed with the audit.
+//
+// --*/
+//
+// {
+// PAGED_CODE();
+//
+// if (SepAdtAuditingEnabled) {
+//
+// if ( ARGUMENT_PRESENT( AccessGranted )) {
+//
+// if ((SeAuditingState[AuditType].AuditOnSuccess && *AccessGranted) ||
+// SeAuditingState[AuditType].AuditOnFailure && !(*AccessGranted)) {
+//
+// return( TRUE );
+//
+// }
+// }
+// }
+//
+// return( FALSE );
+// }
+
+
+
+
+
+POBJECT_NAME_INFORMATION
+SepQueryNameString(
+ IN PVOID Object
+ )
+
+/*++
+
+Routine Description:
+
+ Takes a pointer to an object and returns the name of the object.
+
+Arguments:
+
+ Object - a pointer to an object.
+
+
+Return Value:
+
+ A pointer to a buffer containing a POBJECT_NAME_INFORMATION
+ structure containing the name of the object. The string is
+ allocated out of paged pool and should be freed by the caller.
+
+ NULL may also be returned.
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ ULONG ReturnLength = 0;
+ POBJECT_NAME_INFORMATION ObjectNameInfo = NULL;
+ PUNICODE_STRING ObjectName = NULL;
+
+ PAGED_CODE();
+
+ Status = ObQueryNameString(
+ Object,
+ ObjectNameInfo,
+ 0,
+ &ReturnLength
+ );
+
+ if ( Status == STATUS_INFO_LENGTH_MISMATCH ) {
+
+ ObjectNameInfo = ExAllocatePoolWithTag( PagedPool, ReturnLength, 'nOeS' );
+
+ if ( ObjectNameInfo != NULL ) {
+
+ Status = ObQueryNameString(
+ Object,
+ ObjectNameInfo,
+ ReturnLength,
+ &ReturnLength
+ );
+
+ if ( NT_SUCCESS( Status )) {
+
+ if (ObjectNameInfo->Name.Length != 0) {
+
+ return( ObjectNameInfo );
+
+ } else {
+
+ ExFreePool( ObjectNameInfo );
+ return( NULL );
+ }
+ }
+ }
+ }
+
+ return( NULL );
+}
+
+
+
+
+PUNICODE_STRING
+SepQueryTypeString(
+ IN PVOID Object
+ )
+/*++
+
+Routine Description:
+
+ Takes a pointer to an object and returns the type of the object.
+
+Arguments:
+
+ Object - a pointer to an object.
+
+
+Return Value:
+
+ A pointer to a UNICODE_STRING that contains the name of the object
+ type. The string is allocated out of paged pool and should be freed
+ by the caller.
+
+ NULL may also be returned.
+
+
+--*/
+
+{
+
+ NTSTATUS Status;
+ PUNICODE_STRING TypeName = NULL;
+ ULONG ReturnLength;
+
+ PAGED_CODE();
+
+ Status = ObQueryTypeName(
+ Object,
+ TypeName,
+ 0,
+ &ReturnLength
+ );
+
+ if ( Status == STATUS_INFO_LENGTH_MISMATCH ) {
+
+ TypeName = ExAllocatePoolWithTag( PagedPool, ReturnLength, 'nTeS' );
+
+ if ( TypeName != NULL ) {
+
+ Status = ObQueryTypeName(
+ Object,
+ TypeName,
+ ReturnLength,
+ &ReturnLength
+ );
+
+ if ( NT_SUCCESS( Status )) {
+
+ return( TypeName );
+ }
+ }
+ }
+
+ return( NULL );
+}
+
+
+
+VOID
+SeAuditProcessCreation(
+ PEPROCESS Process,
+ PEPROCESS Parent
+ )
+/*++
+
+Routine Description:
+
+ Audits the creation of a process. It is the caller's responsibility
+ to determine if process auditing is in progress.
+
+
+Arguments:
+
+ Process - Points to the new process object.
+
+ Parent - Points to the creator (parent) process object.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ ANSI_STRING Ansi;
+ LUID UserAuthenticationId;
+ NTSTATUS Status;
+ PSID UserSid;
+ SECURITY_SUBJECT_CONTEXT SubjectSecurityContext;
+ SE_ADT_PARAMETER_ARRAY AuditParameters;
+ UNICODE_STRING ImageFileName;
+
+ PAGED_CODE();
+
+ RtlInitAnsiString( &Ansi, Process->ImageFileName );
+
+ Status = RtlAnsiStringToUnicodeString(
+ &ImageFileName,
+ &Ansi,
+ TRUE
+ );
+
+ //
+ // Not enough memory to complete the audit, return.
+ //
+
+ if ( !NT_SUCCESS( Status )) {
+ return;
+ }
+
+ SeCaptureSubjectContext( &SubjectSecurityContext );
+
+ RtlZeroMemory (
+ (PVOID) &AuditParameters,
+ sizeof( AuditParameters )
+ );
+
+ ASSERT( SeAdtParmTypeNone == 0 );
+
+ AuditParameters.CategoryId = SE_CATEGID_DETAILED_TRACKING;
+ AuditParameters.AuditId = SE_AUDITID_PROCESS_CREATED;
+ AuditParameters.ParameterCount = 0;
+ AuditParameters.Type = EVENTLOG_AUDIT_SUCCESS;
+
+ //
+ // Use the primary token here, because that's what's going to show up
+ // when the created process exits.
+ //
+
+ UserSid = SepTokenUserSid( SubjectSecurityContext.PrimaryToken );
+
+ UserAuthenticationId = SepTokenAuthenticationId( SubjectSecurityContext.PrimaryToken );
+
+ //
+ // Fill in the AuditParameters structure.
+ //
+
+ SepSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, UserSid );
+ AuditParameters.ParameterCount++;
+
+ SepSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &SeSubsystemName );
+ AuditParameters.ParameterCount++;
+
+ SepSetParmTypeUlong( AuditParameters, AuditParameters.ParameterCount, Process );
+ AuditParameters.ParameterCount++;
+
+ SepSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &ImageFileName );
+ AuditParameters.ParameterCount++;
+
+ SepSetParmTypeUlong( AuditParameters, AuditParameters.ParameterCount, Parent );
+ AuditParameters.ParameterCount++;
+
+ SepSetParmTypeLogonId( AuditParameters, AuditParameters.ParameterCount, UserAuthenticationId );
+ AuditParameters.ParameterCount++;
+
+ SepAdtLogAuditRecord( &AuditParameters );
+
+ SeReleaseSubjectContext( &SubjectSecurityContext );
+
+ RtlFreeUnicodeString( &ImageFileName );
+
+ return;
+}
+
+
+VOID
+SeAuditHandleDuplication(
+ PVOID SourceHandle,
+ PVOID NewHandle,
+ PEPROCESS SourceProcess,
+ PEPROCESS TargetProcess
+ )
+
+/*++
+
+Routine Description:
+
+ This routine generates a handle duplication audit. It is up to the caller
+ to determine if this routine should be called or not.
+
+Arguments:
+
+ SourceHandle - Original handle
+
+ NewHandle - New handle
+
+ SourceProcess - Process containing SourceHandle
+
+ TargetProcess - Process containing NewHandle
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ SE_ADT_PARAMETER_ARRAY AuditParameters;
+ SECURITY_SUBJECT_CONTEXT SubjectSecurityContext;
+ PSID UserSid;
+
+ PAGED_CODE();
+
+ SeCaptureSubjectContext( &SubjectSecurityContext );
+
+ UserSid = SepTokenUserSid( EffectiveToken( &SubjectSecurityContext ));
+
+ RtlZeroMemory (
+ (PVOID) &AuditParameters,
+ sizeof( AuditParameters )
+ );
+
+
+ ASSERT( SeAdtParmTypeNone == 0 );
+
+ AuditParameters.CategoryId = SE_CATEGID_DETAILED_TRACKING;
+ AuditParameters.AuditId = SE_AUDITID_DUPLICATE_HANDLE;
+ AuditParameters.ParameterCount = 0;
+ AuditParameters.Type = EVENTLOG_AUDIT_SUCCESS;
+
+ SepSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, UserSid );
+ AuditParameters.ParameterCount++;
+
+ SepSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &SeSubsystemName );
+ AuditParameters.ParameterCount++;
+
+ SepSetParmTypeUlong( AuditParameters, AuditParameters.ParameterCount, SourceHandle );
+ AuditParameters.ParameterCount++;
+
+ SepSetParmTypeUlong( AuditParameters, AuditParameters.ParameterCount, PsProcessAuditId( SourceProcess ));
+ AuditParameters.ParameterCount++;
+
+ SepSetParmTypeUlong( AuditParameters, AuditParameters.ParameterCount, NewHandle );
+ AuditParameters.ParameterCount++;
+
+ SepSetParmTypeUlong( AuditParameters, AuditParameters.ParameterCount, PsProcessAuditId( TargetProcess ));
+ AuditParameters.ParameterCount++;
+
+
+ SepAdtLogAuditRecord( &AuditParameters );
+
+ SeReleaseSubjectContext( &SubjectSecurityContext );
+}
+
+
+VOID
+SeAuditProcessExit(
+ PEPROCESS Process
+ )
+/*++
+
+Routine Description:
+
+ Audits the exit of a process. The caller is responsible for
+ determining if this should be called.
+
+Arguments:
+
+ Process - Pointer to the process object that is exiting.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PTOKEN Token;
+ SE_ADT_PARAMETER_ARRAY AuditParameters;
+ PSID UserSid;
+ LUID LogonId;
+
+ PAGED_CODE();
+
+ Token = (PTOKEN)Process->Token;
+
+ UserSid = SepTokenUserSid( Token );
+ LogonId = SepTokenAuthenticationId( Token );
+
+ RtlZeroMemory (
+ (PVOID) &AuditParameters,
+ sizeof( AuditParameters )
+ );
+
+
+ ASSERT( SeAdtParmTypeNone == 0 );
+
+ AuditParameters.CategoryId = SE_CATEGID_DETAILED_TRACKING;
+ AuditParameters.AuditId = SE_AUDITID_PROCESS_EXIT;
+ AuditParameters.ParameterCount = 0;
+ AuditParameters.Type = EVENTLOG_AUDIT_SUCCESS;
+
+ SepSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, UserSid );
+ AuditParameters.ParameterCount++;
+
+ SepSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &SeSubsystemName );
+ AuditParameters.ParameterCount++;
+
+ SepSetParmTypeUlong( AuditParameters, AuditParameters.ParameterCount, PsProcessAuditId( Process ));
+ AuditParameters.ParameterCount++;
+
+ SepSetParmTypeLogonId( AuditParameters, AuditParameters.ParameterCount, LogonId );
+ AuditParameters.ParameterCount++;
+
+ SepAdtLogAuditRecord( &AuditParameters );
+}
+
+
+
+VOID
+SepAdtGenerateDiscardAudit(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Generates an 'audits discarded' audit.
+
+Arguments:
+
+ none
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ SE_ADT_PARAMETER_ARRAY AuditParameters;
+ PSID UserSid;
+
+ PAGED_CODE();
+
+ UserSid = SeLocalSystemSid;
+
+ RtlZeroMemory (
+ (PVOID) &AuditParameters,
+ sizeof( AuditParameters )
+ );
+
+
+ ASSERT( SeAdtParmTypeNone == 0 );
+
+ AuditParameters.CategoryId = SE_CATEGID_SYSTEM;
+ AuditParameters.AuditId = SE_AUDITID_AUDITS_DISCARDED;
+ AuditParameters.ParameterCount = 0;
+ AuditParameters.Type = EVENTLOG_AUDIT_SUCCESS;
+
+ SepSetParmTypeSid( AuditParameters, AuditParameters.ParameterCount, UserSid );
+ AuditParameters.ParameterCount++;
+
+ SepSetParmTypeString( AuditParameters, AuditParameters.ParameterCount, &SeSubsystemName );
+ AuditParameters.ParameterCount++;
+
+ SepSetParmTypeUlong( AuditParameters, AuditParameters.ParameterCount, SepAdtCountEventsDiscarded );
+ AuditParameters.ParameterCount++;
+
+ SepAdtLogAuditRecord( &AuditParameters );
+}
diff --git a/private/ntos/se/sources.inc b/private/ntos/se/sources.inc
new file mode 100644
index 000000000..fcab0ab68
--- /dev/null
+++ b/private/ntos/se/sources.inc
@@ -0,0 +1,74 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=ntos
+MINORCOMP=se
+
+TARGETNAME=se
+TARGETTYPE=LIBRARY
+
+INCLUDES=..;..\..\inc;..\..\..\inc
+MIPS_OPTIONS=-nodwalign
+GPSIZE=32
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+C_DEFINES=-D_NTSYSTEM_
+
+SOURCES= \
+ ..\accessck.c \
+ ..\capture.c \
+ ..\privileg.c \
+ ..\rmlogon.c \
+ ..\rmmain.c \
+ ..\rmvars.c \
+ ..\seassign.c \
+ ..\seaudit.c \
+ ..\sepaudit.c \
+ ..\seclient.c \
+ ..\seglobal.c \
+ ..\seinit.c \
+ ..\semethod.c \
+ ..\sep.c \
+ ..\subject.c \
+ ..\seastate.c \
+ ..\token.c \
+ ..\tokenadj.c \
+ ..\tokendup.c \
+ ..\tokenopn.c \
+ ..\tokenqry.c \
+ ..\tokenset.c \
+ ..\adtlog.c \
+ ..\adtinit.c \
+ ..\adtvars.c \
+ ..\rmaudit.c
+
+SOURCES_USED=..\sources.inc
+
+! IF 0
+
+UMTEST=uttoken*utrtl*utseacc*utseqos*utaccess
+UMTYPE=wincon
+
+!ENDIF
diff --git a/private/ntos/se/subject.c b/private/ntos/se/subject.c
new file mode 100644
index 000000000..b7344361e
--- /dev/null
+++ b/private/ntos/se/subject.c
@@ -0,0 +1,432 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ Subject.c
+
+Abstract:
+
+ This Module implements services related to subject security context.
+ These services are part of the services provided by the Reference Monitor
+ component.
+
+ FOR PERFORMANCE SAKE, THIS MODULE IS AWARE OF INTERNAL TOKEN OBJECT
+ FORMATS.
+
+Author:
+
+ Jim Kelly (JimK) 2-Aug-1990
+
+Environment:
+
+ Kernel Mode
+
+Revision History:
+
+--*/
+
+#include "sep.h"
+#include "seopaque.h"
+#include "tokenp.h"
+
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE,SeCaptureSubjectContext)
+#pragma alloc_text(PAGE,SeLockSubjectContext)
+#pragma alloc_text(PAGE,SeUnlockSubjectContext)
+#pragma alloc_text(PAGE,SeReleaseSubjectContext)
+#pragma alloc_text(PAGE,SepGetDefaultsSubjectContext)
+#pragma alloc_text(PAGE,SepValidOwnerSubjectContext)
+#endif
+
+
+VOID
+SeCaptureSubjectContext (
+ OUT PSECURITY_SUBJECT_CONTEXT SubjectContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes a snapshot of the calling thread's security
+ context (locking tokens as necessary to do so). This function
+ is intended to support the object manager and other components
+ that utilize the reference monitor's access validation,
+ privilege test, and audit generation services.
+
+ A subject's security context should be captured before initiating
+ access validation and should be released after audit messages
+ are generated. This is necessary to provide a consistent security
+ context to all those services.
+
+ After calling access validation, privilege test, and audit generation
+ services, the captured context should be released as soon as possible
+ using the SeReleaseSubjectContext() service.
+
+Arguments:
+
+ SubjectContext - Points to a SECURITY_SUBJECT_CONTEXT data structure
+ to be filled in with a snapshot of the calling thread's security
+ profile.
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+
+ PEPROCESS CurrentProcess;
+
+ //PVOID Objects[2];
+
+ BOOLEAN IgnoreCopyOnOpen;
+ BOOLEAN IgnoreEffectiveOnly;
+
+ PAGED_CODE();
+
+ CurrentProcess = PsGetCurrentProcess();
+ SubjectContext->ProcessAuditId = PsProcessAuditId( CurrentProcess );
+
+ //
+ // Get pointers to primary and impersonation tokens
+ //
+
+ SubjectContext->ClientToken = PsReferenceImpersonationToken(
+ PsGetCurrentThread(),
+ &IgnoreCopyOnOpen,
+ &IgnoreEffectiveOnly,
+ &(SubjectContext->ImpersonationLevel)
+ );
+
+ SubjectContext->PrimaryToken = PsReferencePrimaryToken(CurrentProcess);
+
+ return;
+
+}
+
+
+
+VOID
+SeLockSubjectContext(
+ IN PSECURITY_SUBJECT_CONTEXT SubjectContext
+ )
+
+/*++
+
+Routine Description:
+
+ Acquires READ LOCKS on the primary and impersonation tokens
+ in the passed SubjectContext.
+
+ This call must be undone by a call to SeUnlockSubjectContext().
+
+ No one outside of the SE component should need to acquire a
+ write lock to a token. Therefore there is no public interface
+ to do this.
+
+Arguments:
+
+ SubjectContext - Points to a SECURITY_SUBJECT_CONTEXT data structure
+ which points to a primary token and an optional impersonation token.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ PAGED_CODE();
+
+ SepAcquireTokenReadLock((PTOKEN)(SubjectContext->PrimaryToken));
+
+ if (ARGUMENT_PRESENT(SubjectContext->ClientToken)) {
+
+ SepAcquireTokenReadLock((PTOKEN)(SubjectContext->ClientToken));
+ }
+
+ return;
+}
+
+
+
+VOID
+SeUnlockSubjectContext(
+ IN PSECURITY_SUBJECT_CONTEXT SubjectContext
+ )
+
+/*++
+
+Routine Description:
+
+ Releases the read locks on the token(s) in the passed SubjectContext.
+
+Arguments:
+
+ SubjectContext - Points to a SECURITY_SUBJECT_CONTEXT data structure
+ which points to a primary token and an optional impersonation token.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ PAGED_CODE();
+
+ SepReleaseTokenReadLock((PTOKEN)(SubjectContext->PrimaryToken));
+
+ if (ARGUMENT_PRESENT(SubjectContext->ClientToken)) {
+
+ SepReleaseTokenReadLock((PTOKEN)(SubjectContext->ClientToken));
+ }
+
+
+}
+
+
+
+VOID
+SeReleaseSubjectContext (
+ IN PSECURITY_SUBJECT_CONTEXT SubjectContext
+ )
+
+/*++
+
+
+Routine Description:
+
+ This routine releases a subject security context previously captured by
+ SeCaptureSubjectContext().
+
+Arguments:
+
+ SubjectContext - Points to a SECURITY_SUBJECT_CONTEXT data structure
+ containing a subject's previously captured security context.
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ PsDereferencePrimaryToken( SubjectContext->PrimaryToken );
+
+ PsDereferenceImpersonationToken( SubjectContext->ClientToken );
+
+ return;
+
+}
+
+VOID
+SepGetDefaultsSubjectContext(
+ IN PSECURITY_SUBJECT_CONTEXT SubjectContext,
+ OUT PSID *Owner,
+ OUT PSID *Group,
+ OUT PSID *ServerOwner,
+ OUT PSID *ServerGroup,
+ OUT PACL *Dacl
+ )
+/*++
+
+Routine Description:
+
+ This routine retrieves pointers to the default owner, primary group,
+ and, if present, discretionary ACL of the provided subject security
+ context.
+
+Arguments:
+
+ SubjectContext - Points to the subject security context whose default
+ values are to be retrieved.
+
+ Owner - Receives a pointer to the subject's default owner SID. This
+ value will always be returned as a non-zero pointer. That is,
+ a subject's security context must contain a owner SID.
+
+ Group - Receives a pointer to the subject's default primary group SID.
+ This value will always be returned as a non-zero pointer. That is,
+ a subject's security context must contain a primary group.
+
+ Dacl - Receives a pointer to the subject's default discretionary ACL,
+ if one is define for the subject. Note that a subject security context
+ does not have to include a default discretionary ACL. In this case,
+ this value will be returned as NULL.
+
+
+
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ PTOKEN EffectiveToken;
+ PTOKEN PrimaryToken;
+
+ PAGED_CODE();
+
+ if (ARGUMENT_PRESENT(SubjectContext->ClientToken)) {
+ EffectiveToken = (PTOKEN)SubjectContext->ClientToken;
+ } else {
+ EffectiveToken = (PTOKEN)SubjectContext->PrimaryToken;
+ }
+
+ (*Owner) = EffectiveToken->UserAndGroups[EffectiveToken->DefaultOwnerIndex].Sid;
+
+ (*Group) = EffectiveToken->PrimaryGroup;
+
+ (*Dacl) = EffectiveToken->DefaultDacl;
+
+ PrimaryToken = (PTOKEN)SubjectContext->PrimaryToken;
+
+ *ServerOwner = PrimaryToken->UserAndGroups[PrimaryToken->DefaultOwnerIndex].Sid;
+
+ *ServerGroup = PrimaryToken->PrimaryGroup;
+
+ return;
+}
+
+
+BOOLEAN
+SepValidOwnerSubjectContext(
+ IN PSECURITY_SUBJECT_CONTEXT SubjectContext,
+ IN PSID Owner,
+ IN BOOLEAN ServerObject
+ )
+/*++
+
+Routine Description:
+
+ This routine checks to see whether the provided SID is one the subject
+ is authorized to assign as the owner of objects. It will also check to
+ see if the caller has SeRestorePrivilege, if so, the request is granted.
+
+Arguments:
+
+ SubjectContext - Points to the subject's security context.
+
+ Owner - Points to the SID to be checked.
+
+
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+
+ ULONG Index;
+ BOOLEAN Found;
+ PTOKEN EffectiveToken;
+ BOOLEAN Rc = FALSE;
+
+ PAGED_CODE();
+
+ //
+ // It is invalid to assign a NULL owner, regardless of
+ // whether you have SeRestorePrivilege or not.
+ //
+
+ if (Owner == NULL) {
+ return( FALSE );
+ }
+
+ //
+ // Allowable owners come from the primary if it's a server object.
+ //
+
+ if (!ServerObject && ARGUMENT_PRESENT(SubjectContext->ClientToken)) {
+ EffectiveToken = (PTOKEN)SubjectContext->ClientToken;
+ } else {
+ EffectiveToken = (PTOKEN)SubjectContext->PrimaryToken;
+ }
+
+ SepAcquireTokenReadLock( EffectiveToken );
+
+ //
+ // Walk through the list of user and group IDs looking
+ // for a match to the specified SID. If one is found,
+ // make sure it may be assigned as an owner.
+ //
+ // This code is similar to that performed to set the default
+ // owner of a token (NtSetInformationToken).
+ //
+
+
+ Index = 0;
+ while (Index < EffectiveToken->UserAndGroupCount) {
+
+
+ Found = RtlEqualSid(
+ Owner,
+ EffectiveToken->UserAndGroups[Index].Sid
+ );
+
+ if ( Found ) {
+
+ //
+ // We may return success if the Sid is one that may be assigned
+ // as an owner, or if the caller has SeRestorePrivilege
+ //
+
+ if ( SepIdAssignableAsOwner(EffectiveToken,Index) ) {
+
+ SepReleaseTokenReadLock( EffectiveToken );
+ Rc = TRUE;
+ goto exit;
+
+ } else {
+
+ //
+ // Rc is already set to FALSE, just exit.
+ //
+
+ SepReleaseTokenReadLock( EffectiveToken );
+ goto exit;
+
+ } //endif assignable
+
+
+ } //endif Found
+
+
+ Index += 1;
+
+ } //endwhile
+
+
+ SepReleaseTokenReadLock( EffectiveToken );
+
+exit:
+
+ //
+ // If we are going to fail this call, check for Restore privilege,
+ // and succeed if he has it.
+ //
+
+ //
+ // We should really have gotten PreviousMode from the caller, but we
+ // didn't, so hard wire it to be user-mode here.
+ //
+
+ if ( Rc == FALSE ) {
+ Rc = SeSinglePrivilegeCheck( SeRestorePrivilege, UserMode );
+ }
+
+ return Rc;
+}
+
diff --git a/private/ntos/se/token.c b/private/ntos/se/token.c
new file mode 100644
index 000000000..6d3930c4f
--- /dev/null
+++ b/private/ntos/se/token.c
@@ -0,0 +1,2385 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ token.c
+
+Abstract:
+
+ This module implements the initialization, open, duplicate and other
+ services of the executive token object.
+
+Author:
+
+ Jim Kelly (JimK) 5-April-1990
+
+Environment:
+
+ Kernel mode only.
+
+Revision History:
+
+ v15: robertre
+ updated ACL_REVISION
+
+--*/
+
+
+#include "sep.h"
+#include <zwapi.h>
+#include "tokenp.h"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(INIT,SepTokenInitialization)
+#pragma alloc_text(INIT,SeMakeSystemToken)
+#pragma alloc_text(PAGE,SeTokenType)
+#pragma alloc_text(PAGE,SepCreateToken)
+#pragma alloc_text(PAGE,SeTokenImpersonationLevel)
+#pragma alloc_text(PAGE,SeAssignPrimaryToken)
+#pragma alloc_text(PAGE,SeDeassignPrimaryToken)
+#pragma alloc_text(PAGE,SeExchangePrimaryToken)
+#pragma alloc_text(PAGE,SeGetTokenControlInformation)
+#pragma alloc_text(PAGE,SeSubProcessToken)
+#pragma alloc_text(PAGE,NtCreateToken)
+#pragma alloc_text(PAGE,SepTokenDeleteMethod)
+#pragma alloc_text(PAGE,SepIdAssignableAsOwner)
+#endif
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Global Variables //
+// //
+////////////////////////////////////////////////////////////////////////
+
+//
+// Generic mapping of access types
+//
+
+GENERIC_MAPPING SepTokenMapping = { TOKEN_READ,
+ TOKEN_WRITE,
+ TOKEN_EXECUTE,
+ TOKEN_ALL_ACCESS
+ };
+
+//
+// Address of token object type descriptor.
+//
+
+POBJECT_TYPE SepTokenObjectType;
+
+
+//
+// Used to track whether or not a system token has been created or not.
+//
+
+#if DBG
+BOOLEAN SystemTokenCreated = FALSE;
+#endif //DBG
+
+
+//
+// Token lock
+//
+
+ERESOURCE SepTokenLock;
+
+
+
+
+//
+// Used to control the active token diagnostic support provided
+//
+
+#ifdef TOKEN_DIAGNOSTICS_ENABLED
+ULONG TokenGlobalFlag = 0;
+#endif // TOKEN_DIAGNOSTICS_ENABLED
+
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Token Object Routines & Methods //
+// //
+////////////////////////////////////////////////////////////////////////
+
+
+
+
+TOKEN_TYPE
+SeTokenType(
+ IN PACCESS_TOKEN Token
+ )
+
+/*++
+
+Routine Description:
+
+ This function returns the type of an instance of a token (TokenPrimary,
+ or TokenImpersonation).
+
+
+Arguments:
+
+ Token - Points to the token whose type is to be returned.
+
+Return Value:
+
+ The token's type.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ return (((PTOKEN)Token)->TokenType);
+}
+
+
+
+SECURITY_IMPERSONATION_LEVEL
+SeTokenImpersonationLevel(
+ IN PACCESS_TOKEN Token
+ )
+
+/*++
+
+Routine Description:
+
+ This function returns the impersonation level of a token. The token
+ is assumed to be a TokenImpersonation type token.
+
+
+Arguments:
+
+ Token - Points to the token whose impersonation level is to be returned.
+
+Return Value:
+
+ The token's impersonation level.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ return ((PTOKEN)Token)->ImpersonationLevel;
+}
+
+
+VOID
+SeAssignPrimaryToken(
+ IN PEPROCESS Process,
+ IN PACCESS_TOKEN Token
+ )
+
+
+/*++
+
+Routine Description:
+
+ This function establishes a primary token for a process.
+
+Arguments:
+
+ Token - Points to the new primary token.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ NTSTATUS
+ Status;
+
+ PTOKEN
+ NewToken = (PTOKEN)Token;
+
+ PAGED_CODE();
+
+ ASSERT(NewToken->TokenType == TokenPrimary);
+ ASSERT( !NewToken->TokenInUse );
+
+
+ //
+ // Dereference the old token if there is one.
+ //
+ // Processes typically already have a token that must be
+ // dereferenced. There are two cases where this may not
+ // be the situation. First, during phase 0 system initialization,
+ // the initial system process starts out without a token. Second,
+ // if an error occurs during process creation, we may be cleaning
+ // up a process that hasn't yet had a primary token assigned.
+ //
+
+ if (Process->Token != NULL) {
+ SeDeassignPrimaryToken( Process );
+ }
+
+
+ Process->Token=Token;
+ NewToken->TokenInUse = TRUE;
+ ObReferenceObject(NewToken);
+ return;
+}
+
+
+
+VOID
+SeDeassignPrimaryToken(
+ IN PEPROCESS Process
+ )
+
+
+/*++
+
+Routine Description:
+
+ This function causes a process reference to a token to be
+ dropped.
+
+Arguments:
+
+ Process - Points to the process whose primary token is no longer needed.
+ This is probably only the case at process deletion or when
+ a primary token is being replaced.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PTOKEN
+ OldToken = (PTOKEN)(Process->Token);
+
+ PAGED_CODE();
+
+ ASSERT(OldToken->TokenType == TokenPrimary);
+ ASSERT(OldToken->TokenInUse);
+
+ OldToken->TokenInUse = FALSE;
+ ObDereferenceObject( OldToken );
+
+
+ return;
+}
+
+
+
+NTSTATUS
+SeExchangePrimaryToken(
+ IN PEPROCESS Process,
+ IN PACCESS_TOKEN NewAccessToken,
+ OUT PACCESS_TOKEN *OldAccessToken
+ )
+
+
+/*++
+
+Routine Description:
+
+ This function is used to perform the portions of changing a primary
+ token that reference the internals of token structures.
+
+ The new token is checked to make sure it is not already in use.
+
+ The
+
+
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !!!!!!!! WARNING WARNING WARNING !!!!!!!!
+ !!!!!!!! !!!!!!!!
+ !!!!!!!! THIS ROUTINE MUST BE CALLED WITH THE GOBAL !!!!!!!!
+ !!!!!!!! PROCESS SECURITY FIELDS LOCK HELD !!!!!!!!
+ !!!!!!!! !!!!!!!!
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+Arguments:
+
+ Process - Points to the process whose primary token is being exchanged.
+
+ NewAccessToken - Points to the process's new primary token.
+
+ OldAccessToken - Receives a pointer to the process's current token.
+ The caller is responsible for dereferencing this token when
+ it is no longer needed. This can't be done while the process
+ security locks are held.
+
+
+Return Value:
+
+ STATUS_SUCCESS - Everything has been updated.
+
+ STATUS_TOKEN_ALREADY_IN_USE - A primary token can only be used by a
+ single process. That is, each process must have its own primary
+ token. The token passed to be assigned as the primary token is
+ already in use as a primary token.
+
+ STATUS_BAD_TOKEN_TYPE - The new token is not a primary token.
+
+
+--*/
+
+{
+ NTSTATUS
+ Status;
+
+ PTOKEN
+ OldToken = (PTOKEN)(Process->Token);
+
+ PTOKEN
+ NewToken = (PTOKEN)NewAccessToken;
+
+ PAGED_CODE();
+
+
+ //
+ // We need to access the security fields of both tokens.
+ // Access to these fields is guarded by the global Process Security
+ // fields lock.
+ //
+
+ ASSERT(OldToken->TokenType == TokenPrimary);
+ ASSERT(OldToken->TokenInUse);
+
+
+ //
+ // Make sure the new token is a primary token...
+ //
+
+ if ( NewToken->TokenType != TokenPrimary ) {
+ return(STATUS_BAD_TOKEN_TYPE);
+ }
+
+ //
+ // and that it is not already in use...
+ //
+
+ if (NewToken->TokenInUse) {
+ return(STATUS_TOKEN_ALREADY_IN_USE);
+ }
+
+
+
+
+ //
+ // Switch the tokens
+ //
+
+ Process->Token=NewAccessToken;
+ NewToken->TokenInUse = TRUE;
+ ObReferenceObject(NewToken);
+
+ //
+ // Mark the token as "NOT USED"
+ //
+
+ OldToken->TokenInUse = FALSE;
+
+ //
+ // Return the pointer to the old token. The caller
+ // is responsible for dereferencing it if they don't need it.
+ //
+
+ (*OldAccessToken) = OldToken;
+
+ return (STATUS_SUCCESS);
+}
+
+
+
+
+
+VOID
+SeGetTokenControlInformation (
+ IN PACCESS_TOKEN Token,
+ OUT PTOKEN_CONTROL TokenControl
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is provided for communication session layers, or
+ any other executive component that needs to keep track of
+ whether a caller's security context has changed between calls.
+ Communication session layers will need to check this, for some
+ security quality of service modes, to determine whether or not
+ a server's security context needs to be updated to reflect
+ changes in the client's security context.
+
+ This routine will also be useful to communications subsystems
+ that need to retrieve client' authentication information from
+ the local security authority in order to perform a remote
+ authentication.
+
+
+Parameters:
+
+ Token - Points to the token whose information is to be retrieved.
+
+ TokenControl - Points to the buffer to receive the token control
+ information.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ //
+ // acquire exclusive access to the token
+ //
+
+ SepAcquireTokenReadLock( (PTOKEN)Token );
+
+ //
+ // Grab the data and run
+ //
+
+ TokenControl->TokenId = ((TOKEN *)Token)->TokenId;
+ TokenControl->AuthenticationId = ((TOKEN *)Token)->AuthenticationId;
+ TokenControl->ModifiedId = ((TOKEN *)Token)->ModifiedId;
+ TokenControl->TokenSource = ((TOKEN *)Token)->TokenSource;
+
+ SepReleaseTokenReadLock( (PTOKEN)Token );
+
+ return;
+
+}
+
+PACCESS_TOKEN
+SeMakeSystemToken ()
+
+/*++
+
+Routine Description:
+
+ This routine is provided for use by executive components
+ DURING SYSTEM INITIALIZATION ONLY. It creates a token for
+ use by system components.
+
+ A system token has the following characteristics:
+
+ - It has LOCAL_SYSTEM as its user ID
+
+ - It has the following groups with the corresponding
+ attributes:
+
+ ADMINS_ALIAS EnabledByDefault |
+ Enabled |
+ Owner
+
+ WORLD EnabledByDefault |
+ Enabled |
+ Mandatory
+
+ ADMINISTRATORS (alias) Owner (disabled)
+
+
+ - It has LOCAL_SYSTEM as its primary group.
+
+ - It has the privileges shown in comments below.
+
+
+ - It has protection that provides TOKEN_ALL_ACCESS to
+ the LOCAL_SYSTEM ID.
+
+
+ - It has a default ACL that grants GENERIC_ALL access
+ to LOCAL_SYSTEM and GENERIC_EXECUTE to WORLD.
+
+
+Parameters:
+
+ None.
+
+Return Value:
+
+ Pointer to a system token.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ PVOID Token;
+
+ SID_AND_ATTRIBUTES UserId;
+ TOKEN_PRIMARY_GROUP PrimaryGroup;
+ PSID_AND_ATTRIBUTES GroupIds;
+ LUID_AND_ATTRIBUTES Privileges[30];
+ PACL TokenAcl;
+ PSID Owner;
+ ULONG NormalGroupAttributes;
+ ULONG OwnerGroupAttributes;
+ ULONG Length;
+ OBJECT_ATTRIBUTES TokenObjectAttributes;
+ PSECURITY_DESCRIPTOR TokenSecurityDescriptor;
+ ULONG BufferLength;
+ PVOID Buffer;
+
+ ULONG GroupIdsBuffer[128];
+
+ TIME_FIELDS TimeFields;
+ LARGE_INTEGER NoExpiration;
+
+ PAGED_CODE();
+
+
+ //
+ // Make sure only one system token gets created.
+ //
+
+#if DBG
+ ASSERT( !SystemTokenCreated );
+ SystemTokenCreated = TRUE;
+#endif //DBG
+
+
+ //
+ // Set up expiration times
+ //
+
+ TimeFields.Year = 3000;
+ TimeFields.Month = 1;
+ TimeFields.Day = 1;
+ TimeFields.Hour = 1;
+ TimeFields.Minute = 1;
+ TimeFields.Second = 1;
+ TimeFields.Milliseconds = 1;
+ TimeFields.Weekday = 1;
+
+ RtlTimeFieldsToTime( &TimeFields, &NoExpiration );
+
+
+// //
+// // The amount of memory used in the following is gross overkill, but
+// // it is freed up immediately after creating the token.
+// //
+//
+// GroupIds = (PSID_AND_ATTRIBUTES)ExAllocatePool( NonPagedPool, 512 );
+
+ GroupIds = (PSID_AND_ATTRIBUTES)GroupIdsBuffer;
+
+
+ //
+ // Set up the attributes to be assigned to groups
+ //
+
+ NormalGroupAttributes = (SE_GROUP_MANDATORY |
+ SE_GROUP_ENABLED_BY_DEFAULT |
+ SE_GROUP_ENABLED
+ );
+
+ OwnerGroupAttributes = (SE_GROUP_ENABLED_BY_DEFAULT |
+ SE_GROUP_ENABLED |
+ SE_GROUP_OWNER
+ );
+
+ //
+ // Set up the user ID
+ //
+
+ UserId.Sid = SeLocalSystemSid;
+ UserId.Attributes = 0;
+
+ //
+ // Set up the groups
+ //
+
+
+ GroupIds->Sid = SeAliasAdminsSid;
+ (GroupIds+1)->Sid = SeWorldSid;
+
+ GroupIds->Attributes = OwnerGroupAttributes;
+ (GroupIds+1)->Attributes = NormalGroupAttributes;
+
+ ASSERT( (RtlLengthSid(GroupIds->Sid) +
+ RtlLengthSid((GroupIds+1)->Sid) +
+ 2*sizeof(ULONG)) <= 512
+ );
+
+
+ //
+ // Privileges
+ //
+
+ //
+ // The privileges in the system token are as follows:
+ //
+ // Privilege Name Attributes
+ // -------------- ----------
+ //
+ // SeTcbPrivilege enabled/enabled by default
+ // SeCreateTokenPrivilege DISabled/NOT enabled by default
+ // SeTakeOwnershipPrivilege DISabled/NOT enabled by default
+ // SeCreatePagefilePrivilege enabled/enabled by default
+ // SeLockMemoryPrivilege enabled/enabled by default
+ // SeAssignPrimaryTokenPrivilege DISabled/NOT enabled by default
+ // SeIncreaseQuotaPrivilege DISabled/NOT enabled by default
+ // SeIncreaseBasePriorityPrivilege enabled/enabled by default
+ // SeCreatePermanentPrivilege enabled/enabled by default
+ // SeDebugPrivilege enabled/enabled by default
+ // SeAuditPrivilege enabled/enabled by default
+ // SeSecurityPrivilege DISabled/NOT enabled by default
+ // SeSystemEnvironmentPrivilege DISabled/NOT enabled by default
+ // SeChangeNotifyPrivilege enabled/enabled by default
+ // SeBackupPrivilege DISabled/NOT enabled by default
+ // SeRestorePrivilege DISabled/NOT enabled by default
+ // SeShutdownPrivilege DISabled/NOT enabled by default
+ // SeLoadDriverPrivilege DISabled/NOT enabled by default
+ // SeProfileSingleProcessPrivilege enabled/enabled by default
+ // SeSystemtimePrivilege DISabled/NOT enabled by default
+ //
+
+ Privileges[0].Luid = SeTcbPrivilege;
+ Privileges[0].Attributes =
+ (SE_PRIVILEGE_ENABLED_BY_DEFAULT | // Enabled by default
+ SE_PRIVILEGE_ENABLED); // Enabled
+
+ Privileges[1].Luid = SeCreateTokenPrivilege;
+ Privileges[1].Attributes = 0; // Only the LSA should enable this.
+
+ Privileges[2].Luid = SeTakeOwnershipPrivilege;
+ Privileges[2].Attributes = 0;
+
+ Privileges[3].Luid = SeCreatePagefilePrivilege;
+ Privileges[3].Attributes =
+ (SE_PRIVILEGE_ENABLED_BY_DEFAULT | // Enabled by default
+ SE_PRIVILEGE_ENABLED); // Enabled
+
+ Privileges[4].Luid = SeLockMemoryPrivilege;
+ Privileges[4].Attributes =
+ (SE_PRIVILEGE_ENABLED_BY_DEFAULT | // Enabled by default
+ SE_PRIVILEGE_ENABLED); // Enabled
+
+ Privileges[5].Luid = SeAssignPrimaryTokenPrivilege;
+ Privileges[5].Attributes = 0; // disabled, not enabled by default
+
+ Privileges[6].Luid = SeIncreaseQuotaPrivilege;
+ Privileges[6].Attributes = 0; // disabled, not enabled by default
+
+ Privileges[7].Luid = SeIncreaseBasePriorityPrivilege;
+ Privileges[7].Attributes =
+ (SE_PRIVILEGE_ENABLED_BY_DEFAULT | // Enabled by default
+ SE_PRIVILEGE_ENABLED); // Enabled
+
+ Privileges[8].Luid = SeCreatePermanentPrivilege;
+ Privileges[8].Attributes =
+ (SE_PRIVILEGE_ENABLED_BY_DEFAULT | // Enabled by default
+ SE_PRIVILEGE_ENABLED); // Enabled
+
+ Privileges[9].Luid = SeDebugPrivilege;
+ Privileges[9].Attributes =
+ (SE_PRIVILEGE_ENABLED_BY_DEFAULT | // Enabled by default
+ SE_PRIVILEGE_ENABLED); // Enabled
+
+ Privileges[10].Luid = SeAuditPrivilege;
+ Privileges[10].Attributes =
+ (SE_PRIVILEGE_ENABLED_BY_DEFAULT | // Enabled by default
+ SE_PRIVILEGE_ENABLED); // Enabled
+
+ Privileges[11].Luid = SeSecurityPrivilege;
+ Privileges[11].Attributes = 0; // disabled, not enabled by default
+
+ Privileges[12].Luid = SeSystemEnvironmentPrivilege;
+ Privileges[12].Attributes = 0; // disabled, not enabled by default
+
+ Privileges[13].Luid = SeChangeNotifyPrivilege;
+ Privileges[13].Attributes =
+ (SE_PRIVILEGE_ENABLED_BY_DEFAULT | // Enabled by default
+ SE_PRIVILEGE_ENABLED); // Enabled
+
+
+ Privileges[14].Luid = SeBackupPrivilege;
+ Privileges[14].Attributes = 0; // disabled, not enabled by default
+
+ Privileges[15].Luid = SeRestorePrivilege;
+ Privileges[15].Attributes = 0; // disabled, not enabled by default
+
+ Privileges[16].Luid = SeShutdownPrivilege;
+ Privileges[16].Attributes = 0; // disabled, not enabled by default
+
+ Privileges[17].Luid = SeLoadDriverPrivilege;
+ Privileges[17].Attributes = 0; // disabled, not enabled by default
+
+ Privileges[18].Luid = SeProfileSingleProcessPrivilege;
+ Privileges[18].Attributes =
+ (SE_PRIVILEGE_ENABLED_BY_DEFAULT | // Enabled by default
+ SE_PRIVILEGE_ENABLED); // Enabled
+
+ Privileges[19].Luid = SeSystemtimePrivilege;
+ Privileges[19].Attributes = 0; // disabled, not enabled by default
+
+ //BEFORE ADDING ANOTHER PRIVILEGE ^^ HERE ^^ CHECK THE ARRAY BOUND
+ //ALSO INCREMENT THE PRIVILEGE COUNT IN THE SepCreateToken() call
+
+
+ //
+ // Establish the primary group and default owner
+ //
+
+ PrimaryGroup.PrimaryGroup = SeLocalSystemSid; // Primary group
+ Owner = SeAliasAdminsSid; // Default owner
+
+
+
+
+
+ //
+ // Set up an ACL to protect token as well ...
+ // give system full reign of terror. This includes user-mode components
+ // running as part of the system.
+ //
+
+ Length = (ULONG)sizeof(ACL) +
+ (ULONG)sizeof(ACCESS_ALLOWED_ACE) +
+ SeLengthSid( SeLocalSystemSid ) +
+ 8; // The 8 is just for good measure
+ ASSERT( Length < 200 );
+
+ TokenAcl = (PACL)ExAllocatePoolWithTag(PagedPool, 200, 'cAeS');
+
+ Status = RtlCreateAcl( TokenAcl, Length, ACL_REVISION2);
+ ASSERT( NT_SUCCESS(Status) );
+
+ Status = RtlAddAccessAllowedAce (
+ TokenAcl,
+ ACL_REVISION2,
+ TOKEN_ALL_ACCESS,
+ SeLocalSystemSid
+ );
+ ASSERT( NT_SUCCESS(Status) );
+
+ TokenSecurityDescriptor =
+ (PSECURITY_DESCRIPTOR)ExAllocatePoolWithTag(
+ PagedPool,
+ SECURITY_DESCRIPTOR_MIN_LENGTH,
+ 'dSeS'
+ );
+
+
+ Status = RtlCreateSecurityDescriptor(
+ TokenSecurityDescriptor,
+ SECURITY_DESCRIPTOR_REVISION
+ );
+ ASSERT( NT_SUCCESS(Status) );
+
+ Status = RtlSetDaclSecurityDescriptor (
+ TokenSecurityDescriptor,
+ TRUE,
+ TokenAcl,
+ FALSE
+ );
+ ASSERT( NT_SUCCESS(Status) );
+
+
+ Status = RtlSetOwnerSecurityDescriptor (
+ TokenSecurityDescriptor,
+ SeAliasAdminsSid,
+ FALSE // Owner defaulted
+ );
+ ASSERT( NT_SUCCESS(Status) );
+
+ Status = RtlSetGroupSecurityDescriptor (
+ TokenSecurityDescriptor,
+ SeAliasAdminsSid,
+ FALSE // Group defaulted
+ );
+ ASSERT( NT_SUCCESS(Status) );
+
+
+ //
+ // Create the system token
+ //
+
+#ifdef TOKEN_DEBUG
+////////////////////////////////////////////////////////////////////////////
+//
+// Debug
+ DbgPrint("\n Creating system token...\n");
+// Debug
+//
+////////////////////////////////////////////////////////////////////////////
+#endif //TOKEN_DEBUG
+
+ InitializeObjectAttributes(
+ &TokenObjectAttributes,
+ NULL,
+ 0,
+ NULL,
+ TokenSecurityDescriptor
+ );
+
+
+
+ ASSERT(SeSystemDefaultDacl != NULL);
+ Status = SepCreateToken(
+ (PHANDLE)&Token,
+ KernelMode,
+ 0, // No handle created for system token
+ &TokenObjectAttributes,
+ TokenPrimary,
+ (SECURITY_IMPERSONATION_LEVEL)NULL,
+ &SeSystemAuthenticationId,
+ &NoExpiration,
+ &UserId,
+ 2, // GroupCount
+ GroupIds,
+ 512, // see call to ExAllocatePool above
+ 20, // privileges
+ Privileges,
+ sizeof(Privileges),
+ Owner,
+ PrimaryGroup.PrimaryGroup,
+ SeSystemDefaultDacl,
+ &SeSystemTokenSource,
+ TRUE, // System token
+ NULL,
+ NULL
+ );
+
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // Assign the security descriptor here, since we don't do it
+ // in SepCreateToken for the System Token.
+ //
+
+ BufferLength = Length +
+ sizeof(SECURITY_DESCRIPTOR) +
+ 2 * SeLengthSid(SeAliasAdminsSid);
+
+ Buffer = (PSECURITY_DESCRIPTOR)ExAllocatePoolWithTag( PagedPool,
+ BufferLength,
+ 'dSeS'
+ );
+
+ Status = RtlAbsoluteToSelfRelativeSD( TokenSecurityDescriptor,
+ Buffer,
+ &BufferLength
+ );
+ ASSERT(NT_SUCCESS(Status));
+
+ Status = ObAssignObjectSecurityDescriptor( Token,
+ Buffer,
+ PagedPool
+ );
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // We can free the old one now.
+ //
+
+ ExFreePool( TokenAcl );
+ ExFreePool( TokenSecurityDescriptor );
+
+ return (PACCESS_TOKEN)Token;
+
+}
+
+NTSTATUS
+SeSubProcessToken (
+ IN PEPROCESS ParentProcess,
+ OUT PACCESS_TOKEN *ChildToken
+ )
+
+/*++
+
+Routine Description:
+
+ This routine makes a token for a sub-process that is a duplicate
+ of the parent process's token.
+
+
+
+Parameters:
+
+ ParentProcess - Pointer to the parent process object. This is used
+ to locate the parent process's primary token, and for logging
+ purposes.
+
+ ChildToken - Receives a pointer to the child process's token.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the sub-process's token has been created
+ successfully.
+
+ Other status values may be returned from memory allocation or object
+ creation services used and typically indicate insufficient resources
+ or quota on the requestor's part.
+
+
+
+--*/
+
+{
+
+ //
+ // NOTE: THIS ROUTINE CAN BE MADE MUCH MORE EFFICIENT.
+ // IT IS DONE IN A BRUTE FORCE FASHION FOR THE LARGE_INTEGER
+ // BEING TO GET THINGS UP AND RUNNING.
+ //
+ // THE PERFORMANCE OF THIS ROUTINE DIRECTLY IMPACTS
+ // THE PERFORMANCE OF SUB-PROCESS CREATION.
+ //
+
+
+ KPROCESSOR_MODE PreviousMode;
+ PTOKEN ParentToken;
+ PTOKEN NewToken;
+ HANDLE NewTokenHandle;
+ OBJECT_ATTRIBUTES PrimaryTokenAttributes;
+
+ NTSTATUS Status;
+ NTSTATUS IgnoreStatus;
+
+ PAGED_CODE();
+
+ PreviousMode = KeGetPreviousMode();
+
+
+
+ InitializeObjectAttributes(
+ &PrimaryTokenAttributes,
+ NULL,
+ 0,
+ NULL,
+ NULL
+ );
+
+
+#ifdef TOKEN_DEBUG
+ DbgPrint("\nCreating sub-process token...\n");
+ DbgPrint("Parent token address = 0x%lx\n", ParentProcess->Token);
+#endif //TOKEN_DEBUG
+
+
+ ParentToken = (PTOKEN)PsReferencePrimaryToken( ParentProcess );
+ Status = SepDuplicateToken(
+ ParentToken, // ExistingToken
+ &PrimaryTokenAttributes, // ObjectAttributes
+ FALSE, // EffectiveOnly
+ TokenPrimary, // TokenType
+ (SECURITY_IMPERSONATION_LEVEL)NULL, // ImpersonationLevel
+ KernelMode, // RequestorMode
+ &NewToken // NewToken
+ );
+ PsDereferencePrimaryToken( (PACCESS_TOKEN)ParentToken );
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Insert the new token object, up its ref count, and then
+ // delete the new handle.
+ //
+
+ Status = ObInsertObject(
+ NewToken,
+ NULL,
+ 0,
+ 0,
+ (PVOID *)NULL,
+ &NewTokenHandle
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ //Status = ObReferenceObject(
+ // NewToken // Object
+ // );
+ Status = ObReferenceObjectByHandle(
+ NewTokenHandle, // Handle
+ DELETE, // DesiredAccess
+ SepTokenObjectType, // ObjectType
+ KernelMode, // AccessMode
+ (PVOID *)ChildToken, // Object
+ NULL // GrantedAccess
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ ((PTOKEN)NewToken)->TokenInUse = TRUE;
+ IgnoreStatus = ZwClose( NewTokenHandle );
+ }
+
+ //
+ // At this point, either the token has a reference
+ // outstanding (and no handles), or the reference
+ // failed. If the reference failed, the status will
+ // be returned indicating why.
+ //
+
+ }
+
+ }
+
+
+
+ return Status;
+
+}
+
+BOOLEAN
+SepTokenInitialization ( VOID )
+
+/*++
+
+Routine Description:
+
+ This function creates the token object type descriptor at system
+ initialization and stores the address of the object type descriptor
+ in global storage. It also created token related global variables.
+
+ Furthermore, some number of pseudo tokens are created during system
+ initialization. These tokens are tracked down and replaced with
+ real tokens.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ A value of TRUE is returned if the object type descriptor is
+ successfully initialized. Otherwise a value of FALSE is returned.
+
+--*/
+
+{
+
+ OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
+ NTSTATUS Status;
+ UNICODE_STRING TypeName;
+
+ PAGED_CODE();
+
+ //
+ // Initialize string descriptor.
+ //
+
+ RtlInitUnicodeString(&TypeName, L"Token");
+
+
+ //
+ // Create the global token lock
+ //
+
+ ExInitializeResource(&SepTokenLock);
+
+
+#if 0
+BUG, BUG Need to get system default ACL to protect token object
+#endif
+
+ //
+ // Create object type descriptor.
+ //
+
+ RtlZeroMemory(&ObjectTypeInitializer,sizeof(ObjectTypeInitializer));
+ ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
+ ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
+ ObjectTypeInitializer.GenericMapping = SepTokenMapping;
+ ObjectTypeInitializer.SecurityRequired = TRUE;
+ ObjectTypeInitializer.UseDefaultObject = TRUE;
+ ObjectTypeInitializer.PoolType = PagedPool;
+ ObjectTypeInitializer.ValidAccessMask = TOKEN_ALL_ACCESS;
+ ObjectTypeInitializer.DeleteProcedure = SepTokenDeleteMethod;
+
+ Status = ObCreateObjectType(&TypeName,
+ &ObjectTypeInitializer,
+ (PSECURITY_DESCRIPTOR)NULL, // BUG, BUG assign real protection
+ &SepTokenObjectType
+ );
+
+
+#if 0
+BUG, BUG Now track down all pseudo tokens used during system initialization
+BUG, BUG and replace them with real ones.
+#endif
+
+ //
+ // If the object type descriptor was successfully created, then
+ // return a value of TRUE. Otherwise return a value of FALSE.
+ //
+
+ return (BOOLEAN)NT_SUCCESS(Status);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// //
+// Temporary, for Debug only //
+// //
+//////////////////////////////////////////////////////////////////////////
+#ifdef TOKEN_DEBUG
+VOID
+SepDumpToken(
+ IN PTOKEN T
+ )
+
+{
+ ULONG Index;
+
+ //
+ // Dump a token
+ //
+
+ DbgPrint("\n");
+
+ DbgPrint(" address: 0x%lx \n", ((ULONG)T) );
+
+ DbgPrint(" TokenId: (0x%lx, 0x%lx) \n",
+ T->TokenId.HighPart, T->TokenId.LowPart );
+
+ if ( (T->AuthenticationId.Data[0] == SeSystemAuthenticationId.Data[0]) &&
+ (T->AuthenticationId.Data[1] == SeSystemAuthenticationId.Data[1]) &&
+ (T->AuthenticationId.Data[2] == SeSystemAuthenticationId.Data[2]) &&
+ (T->AuthenticationId.Data[3] == SeSystemAuthenticationId.Data[3]) ) {
+
+ DbgPrint(" AuthenticationId: SeSystemAuthenticationId \n");
+
+ } else {
+
+ DbgPrint(" AuthenticationId: (0x%lx, 0x%lx, 0x%lx, 0x%lx) \n",
+ T->AuthenticationId.Data[0],
+ T->AuthenticationId.Data[1],
+ T->AuthenticationId.Data[2],
+ T->AuthenticationId.Data[3] );
+ }
+
+ DbgPrint(" ExpirationTime: 0x%lx, 0x%lx \n",
+ T->ExpirationTime.HighPart,
+ T->ExpirationTime.LowPart );
+
+ if (T->TokenType == TokenPrimary) {
+ DbgPrint(" TokenType: Primary \n");
+ } else {
+ if (T->TokenType == TokenImpersonation) {
+ DbgPrint(" TokenType: Impersonation \n");
+ } else {
+ DbgPrint(" TokenType: (Unknown type, value = 0x%lx) \n",
+ ((ULONG)T-TokenType) );
+ }
+ }
+
+ DbgPrint(" ImpersonationLevel: 0x%lx \n",
+ ((ULONG)T->ImpersonationLevel) );
+
+ DbgPrint(" TokenSource: (not yet provided) \n");
+ DbgPrint(" DynamicCharged: 0x%lx \n", T->DynamicCharged);
+ DbgPrint(" UserAndGroupCount: 0x%lx \n", T->UserAndGroupCount);
+ DbgPrint(" PrivilegeCount: 0x%lx \n", T->PrivilegeCount);
+ DbgPrint(" VariableLength: 0x%lx \n", T->VariableLength);
+
+
+ DbgPrint(" ModifiedId: (0x%lx, 0x%lx) \n",
+ T->ModifiedId.HighPart,
+ T->ModifiedId.LowPart );
+ DbgPrint(" DynamicAvailable: 0x%lx \n", T->DynamicAvailable);
+ DbgPrint(" DefaultOwnerIndex: 0x%lx \n", T->DefaultOwnerIndex);
+
+
+ DbgPrint(" Address of DynamicPart: 0x%lx \n",
+ (* (PULONG)((PVOID)(&(T->DynamicPart)))) );
+ DbgPrint(" Address of Default DACL: 0x%lx \n",
+ (* (PULONG)((PVOID)(&(T->DefaultDacl)))) );
+
+ DbgPrint(" Address Of Variable Part: 0x%lx \n",
+ &(T->VariablePart) );
+
+ DbgPrint("\n");
+ DbgPrint(" PrimaryGroup:\n");
+ DbgPrint(" Address: 0x%lx \n",
+ (* (PULONG)((PVOID)(&(T->PrimaryGroup)))) );
+ DbgPrint(" Length: 0x%lx \n",
+ SeLengthSid((T->PrimaryGroup)) );
+ DbgPrint("\n");
+ DbgPrint(" UserAndGroups: 0x%lx \n",
+ (* (PULONG)((PVOID)(&(T->UserAndGroups)))) );
+ DbgPrint(" User ID - \n");
+ DbgPrint(" Address: 0x%lx \n",
+ (* (PULONG)((PVOID)(&(T->UserAndGroups[0].Sid)))) );
+ DbgPrint(" Attributes: 0x%lx \n",
+ (T->UserAndGroups[0].Attributes) );
+ DbgPrint(" Length: 0x%lx \n",
+ SeLengthSid((T->UserAndGroups[0].Sid)) );
+ Index = 1;
+ while (Index < T->UserAndGroupCount) {
+ DbgPrint(" Group 0x%lx - \n", Index );
+ DbgPrint(" Address: 0x%lx \n",
+ (* (PULONG)((PVOID)(&(T->UserAndGroups[Index].Sid)))) );
+ DbgPrint(" Attributes: 0x%lx \n",
+ (T->UserAndGroups[Index].Attributes) );
+ DbgPrint(" Length: 0x%lx \n",
+ SeLengthSid((T->UserAndGroups[Index].Sid)) );
+ Index += 1;
+ }
+
+
+ DbgPrint("\n");
+ DbgPrint(" Privileges: 0x%lx\n",
+ (* (PULONG)((PVOID)(&(T->Privileges)))) );
+ Index = 0;
+ while (Index < T->PrivilegeCount) {
+ DbgPrint(" Privilege 0x%lx - \n", Index );
+ DbgPrint(" Address: 0x%lx \n",
+ (&(T->Privileges[Index])) );
+ DbgPrint(" LUID: (0x%lx, 0x%lx) \n",
+ T->Privileges[Index].Luid.HighPart,
+ T->Privileges[Index].Luid.LowPart );
+ DbgPrint(" Attributes: 0x%lx \n",
+ T->Privileges[Index].Attributes );
+
+ Index += 1;
+ }
+
+ return;
+
+}
+#endif //TOKEN_DEBUG
+
+NTSTATUS
+NtCreateToken(
+ OUT PHANDLE TokenHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
+ IN TOKEN_TYPE TokenType,
+ IN PLUID AuthenticationId,
+ IN PLARGE_INTEGER ExpirationTime,
+ IN PTOKEN_USER User,
+ IN PTOKEN_GROUPS Groups,
+ IN PTOKEN_PRIVILEGES Privileges,
+ IN PTOKEN_OWNER Owner OPTIONAL,
+ IN PTOKEN_PRIMARY_GROUP PrimaryGroup,
+ IN PTOKEN_DEFAULT_DACL DefaultDacl OPTIONAL,
+ IN PTOKEN_SOURCE TokenSource
+ )
+
+/*++
+
+Routine Description:
+
+ Create a token object and return a handle opened for access to
+ that token. This API requires SeCreateTokenPrivilege privilege.
+
+Arguments:
+
+ TokenHandle - Receives the handle of the newly created token.
+
+ DesiredAccess - Is an access mask indicating which access types
+ the handle is to provide to the new object.
+
+ ObjectAttributes - Points to the standard object attributes data
+ structure. Refer to the NT Object Management
+ Specification for a description of this data structure.
+
+ If the token type is TokenImpersonation, then this parameter
+ must specify the impersonation level of the token.
+
+ TokenType - Type of token to be created. Privilege is required
+ to create any type of token.
+
+ AuthenticationId - Points to a LUID (or LUID) providing a unique
+ identifier associated with the authentication. This is used
+ within security only, for audit purposes.
+
+ ExpirationTime - Time at which the token becomes invalid. If this
+ value is specified as zero, then the token has no expiration
+ time.
+
+ User - Is the user SID to place in the token.
+
+ Groups - Are the group SIDs to place in the token.
+
+ Privileges - Are the privileges to place in the token.
+
+ Owner - (Optionally) identifies an identifier that is to be used
+ as the default owner for the token. If not provided, the
+ user ID is made the default owner.
+
+ PrimaryGroup - Identifies which of the group IDs is to be the
+ primary group of the token.
+
+ DefaultDacl - (optionally) establishes an ACL to be used as the
+ default discretionary access protection for the token.
+
+ TokenSource - Identifies the token source name string and
+ identifier to be assigned to the token.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the operation was successful.
+
+ STATUS_INVALID_OWNER - Indicates the ID provided to be assigned
+ as the default owner of the token does not have an attribute
+ indicating it may be assigned as an owner.
+
+ STATUS_INVALID_PRIMARY_GROUP - Indicates the group ID provided
+ via the PrimaryGroup parameter was not among those assigned
+ to the token in the Groups parameter.
+
+ STATUS_BAD_IMPERSONATION_LEVEL - Indicates no impersonation level
+ was provided when attempting to create a token of type
+ TokenImpersonation.
+
+--*/
+
+{
+
+ KPROCESSOR_MODE PreviousMode;
+ NTSTATUS Status;
+ ULONG Ignore;
+
+
+ HANDLE LocalHandle;
+
+ BOOLEAN SecurityQosPresent = FALSE;
+ SECURITY_ADVANCED_QUALITY_OF_SERVICE CapturedSecurityQos;
+
+ LUID CapturedAuthenticationId;
+ LARGE_INTEGER CapturedExpirationTime;
+
+ PSID_AND_ATTRIBUTES CapturedUser = NULL;
+ ULONG CapturedUserLength;
+
+ ULONG CapturedGroupCount;
+ PSID_AND_ATTRIBUTES CapturedGroups = NULL;
+ ULONG CapturedGroupsLength;
+
+ ULONG CapturedPrivilegeCount;
+ PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL;
+ ULONG CapturedPrivilegesLength;
+
+ PSID CapturedOwner = NULL;
+
+ PSID CapturedPrimaryGroup = NULL;
+
+ PACL CapturedDefaultDacl = NULL;
+
+ TOKEN_SOURCE CapturedTokenSource;
+
+ ULONG CapturedAddress;
+
+ PAGED_CODE();
+
+ PreviousMode = KeGetPreviousMode();
+
+ if (PreviousMode != KernelMode) {
+
+ //
+ // Probe everything necessary for input to the capture subroutines.
+ //
+
+ try {
+
+ ProbeForWriteHandle(TokenHandle);
+
+
+ ProbeForRead( ExpirationTime, sizeof(LARGE_INTEGER), sizeof(ULONG) );
+ ProbeForRead( Groups, sizeof(TOKEN_GROUPS), sizeof(ULONG) );
+ ProbeForRead( Privileges, sizeof(TOKEN_PRIVILEGES), sizeof(ULONG) );
+ ProbeForRead( TokenSource, sizeof(TOKEN_SOURCE), sizeof(ULONG) );
+
+
+ if ( ARGUMENT_PRESENT(Owner) ) {
+ ProbeForRead( Owner, sizeof(TOKEN_OWNER), sizeof(ULONG) );
+ }
+
+
+ ProbeForRead(
+ PrimaryGroup,
+ sizeof(TOKEN_PRIMARY_GROUP),
+ sizeof(ULONG)
+ );
+
+
+ if ( ARGUMENT_PRESENT(DefaultDacl) ) {
+ ProbeForRead(
+ DefaultDacl,
+ sizeof(TOKEN_DEFAULT_DACL),
+ sizeof(ULONG)
+ );
+ }
+
+ ProbeForRead(
+ AuthenticationId,
+ sizeof(LUID),
+ sizeof(ULONG)
+ );
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ return GetExceptionCode();
+ } // end_try
+
+ } //end_if
+
+ //
+ // Capture the security quality of service.
+ // This capture routine necessarily does some probing of its own.
+ //
+
+ Status = SeCaptureSecurityQos(
+ ObjectAttributes,
+ PreviousMode,
+ &SecurityQosPresent,
+ &CapturedSecurityQos
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+ if (TokenType == TokenImpersonation) {
+
+ if (!SecurityQosPresent) {
+ return STATUS_BAD_IMPERSONATION_LEVEL;
+ } // endif
+
+ } // endif
+
+
+ //
+ // Capture the rest of the arguments.
+ // These arguments have already been probed.
+ //
+
+ try {
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Capture and validate AuthenticationID
+ //
+
+ RtlCopyLuid( &CapturedAuthenticationId, AuthenticationId );
+
+ //
+ // Capture ExpirationTime
+ //
+
+ CapturedExpirationTime = (*ExpirationTime);
+
+ //
+ // Capture User
+ //
+
+ if (NT_SUCCESS(Status)) {
+ Status = SeCaptureSidAndAttributesArray(
+ &(User->User),
+ 1,
+ PreviousMode,
+ NULL, 0,
+ PagedPool,
+ TRUE,
+ &CapturedUser,
+ &CapturedUserLength
+ );
+ }
+
+
+ //
+ // Capture Groups
+ //
+
+ if (NT_SUCCESS(Status)) {
+ CapturedGroupCount = Groups->GroupCount;
+ Status = SeCaptureSidAndAttributesArray(
+ (Groups->Groups),
+ CapturedGroupCount,
+ PreviousMode,
+ NULL, 0,
+ PagedPool,
+ TRUE,
+ &CapturedGroups,
+ &CapturedGroupsLength
+ );
+ }
+
+
+ //
+ // Capture Privileges
+ //
+
+ if (NT_SUCCESS(Status)) {
+ CapturedPrivilegeCount = Privileges->PrivilegeCount;
+ Status = SeCaptureLuidAndAttributesArray(
+ (Privileges->Privileges),
+ CapturedPrivilegeCount,
+ PreviousMode,
+ NULL, 0,
+ PagedPool,
+ TRUE,
+ &CapturedPrivileges,
+ &CapturedPrivilegesLength
+ );
+ }
+
+
+ //
+ // Capture Owner
+ //
+
+ if ( ARGUMENT_PRESENT(Owner) && NT_SUCCESS(Status)) {
+ CapturedAddress = (ULONG)(Owner->Owner);
+ Status = SeCaptureSid(
+ (PSID)CapturedAddress,
+ PreviousMode,
+ NULL, 0,
+ PagedPool,
+ TRUE,
+ &CapturedOwner
+ );
+ }
+
+
+ //
+ // Capture PrimaryGroup
+ //
+ if (NT_SUCCESS(Status)) {
+ CapturedAddress = (ULONG)(PrimaryGroup->PrimaryGroup);
+ Status = SeCaptureSid(
+ (PSID)CapturedAddress,
+ PreviousMode,
+ NULL, 0,
+ PagedPool,
+ TRUE,
+ &CapturedPrimaryGroup
+ );
+ }
+
+
+ //
+ // Capture DefaultDacl
+ //
+
+ if ( ARGUMENT_PRESENT(DefaultDacl) && NT_SUCCESS(Status) ) {
+ CapturedAddress = (ULONG)(DefaultDacl->DefaultDacl);
+ if ((PVOID)CapturedAddress != NULL) {
+ Status = SeCaptureAcl(
+ (PACL)CapturedAddress,
+ PreviousMode,
+ NULL, 0,
+ NonPagedPool,
+ TRUE,
+ &CapturedDefaultDacl,
+ &Ignore
+ );
+ }
+ }
+
+ //
+ // Capture TokenSource
+ //
+
+ CapturedTokenSource = (*TokenSource);
+
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ if (CapturedUser != NULL) {
+ SeReleaseSidAndAttributesArray(
+ CapturedUser,
+ PreviousMode,
+ TRUE
+ );
+ }
+
+ if (CapturedGroups != NULL) {
+ SeReleaseSidAndAttributesArray(
+ CapturedGroups,
+ PreviousMode,
+ TRUE
+ );
+ }
+
+ if (CapturedPrivileges != NULL) {
+ SeReleaseLuidAndAttributesArray(
+ CapturedPrivileges,
+ PreviousMode,
+ TRUE
+ );
+ }
+
+ if (CapturedOwner != NULL) {
+ SeReleaseSid( CapturedOwner, PreviousMode, TRUE);
+ }
+
+ if (CapturedPrimaryGroup != NULL) {
+ SeReleaseSid( CapturedPrimaryGroup, PreviousMode, TRUE);
+ }
+
+ if (CapturedDefaultDacl != NULL) {
+ SeReleaseAcl( CapturedDefaultDacl, PreviousMode, TRUE);
+ }
+
+ if (SecurityQosPresent == TRUE) {
+ SeFreeCapturedSecurityQos( &CapturedSecurityQos );
+ }
+
+ return GetExceptionCode();
+
+ } // end_try{}
+
+ //
+ // Create the token
+ //
+
+ if (NT_SUCCESS(Status)) {
+ Status = SepCreateToken(
+ &LocalHandle,
+ PreviousMode,
+ DesiredAccess,
+ ObjectAttributes,
+ TokenType,
+ CapturedSecurityQos.ImpersonationLevel,
+ &CapturedAuthenticationId,
+ &CapturedExpirationTime,
+ CapturedUser,
+ CapturedGroupCount,
+ CapturedGroups,
+ CapturedGroupsLength,
+ CapturedPrivilegeCount,
+ CapturedPrivileges,
+ CapturedPrivilegesLength,
+ CapturedOwner,
+ CapturedPrimaryGroup,
+ CapturedDefaultDacl,
+ &CapturedTokenSource,
+ FALSE, // Not a system token
+ SecurityQosPresent ? CapturedSecurityQos.ProxyData : NULL,
+ SecurityQosPresent ? CapturedSecurityQos.AuditData : NULL
+ );
+ }
+
+ //
+ // Clean up the temporary capture buffers
+ //
+
+ if (CapturedUser != NULL) {
+ SeReleaseSidAndAttributesArray( CapturedUser, PreviousMode, TRUE);
+ }
+ if (CapturedGroups != NULL) {
+ SeReleaseSidAndAttributesArray( CapturedGroups, PreviousMode, TRUE);
+ }
+
+ if (CapturedPrivileges != NULL) {
+ SeReleaseLuidAndAttributesArray( CapturedPrivileges, PreviousMode, TRUE);
+ }
+
+ if (CapturedOwner != NULL) {
+ SeReleaseSid( CapturedOwner, PreviousMode, TRUE);
+ }
+
+ if (CapturedPrimaryGroup != NULL) {
+ SeReleaseSid( CapturedPrimaryGroup, PreviousMode, TRUE);
+ }
+
+ if (CapturedDefaultDacl != NULL) {
+ SeReleaseAcl( CapturedDefaultDacl, PreviousMode, TRUE);
+ }
+
+ if (SecurityQosPresent == TRUE) {
+ SeFreeCapturedSecurityQos( &CapturedSecurityQos );
+ }
+
+ //
+ // Return the handle to this new token
+ //
+
+ if (NT_SUCCESS(Status)) {
+ try { *TokenHandle = LocalHandle; }
+ except(EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); }
+ }
+
+ return Status;
+
+}
+
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Token Private Routines //
+// //
+////////////////////////////////////////////////////////////////////////
+
+
+VOID
+SepTokenDeleteMethod (
+ IN PVOID Token
+ )
+
+/*++
+
+Routine Description:
+
+ This function is the token object type-specific delete method.
+ It is needed to ensure that all memory allocated for the token
+ gets deallocated.
+
+Arguments:
+
+ Token - Points to the token object being deleted.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ //
+ // De-reference the logon session referenced by this token object
+ //
+
+ SepDeReferenceLogonSession( &(((TOKEN *)Token)->AuthenticationId) );
+
+
+ //
+ // If the token has an associated Dynamic part, deallocate it.
+ //
+
+ if (ARGUMENT_PRESENT( ((TOKEN *)Token)->DynamicPart)) {
+ ExFreePool( ((TOKEN *)Token)->DynamicPart );
+ }
+
+ //
+ // Free the Proxy and Global audit structures if present.
+ //
+
+ if (ARGUMENT_PRESENT(((TOKEN *) Token)->ProxyData)) {
+ SepFreeProxyData( ((TOKEN *)Token)->ProxyData );
+ }
+
+ if (ARGUMENT_PRESENT(((TOKEN *)Token)->AuditData )) {
+ ExFreePool( (((TOKEN *)Token)->AuditData) );
+ }
+
+
+ return;
+}
+
+NTSTATUS
+SepCreateToken(
+ OUT PHANDLE TokenHandle,
+ IN KPROCESSOR_MODE RequestorMode,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
+ IN TOKEN_TYPE TokenType,
+ IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel OPTIONAL,
+ IN PLUID AuthenticationId,
+ IN PLARGE_INTEGER ExpirationTime,
+ IN PSID_AND_ATTRIBUTES User,
+ IN ULONG GroupCount,
+ IN PSID_AND_ATTRIBUTES Groups,
+ IN ULONG GroupsLength,
+ IN ULONG PrivilegeCount,
+ IN PLUID_AND_ATTRIBUTES Privileges,
+ IN ULONG PrivilegesLength,
+ IN PSID Owner OPTIONAL,
+ IN PSID PrimaryGroup,
+ IN PACL DefaultDacl OPTIONAL,
+ IN PTOKEN_SOURCE TokenSource,
+ IN BOOLEAN SystemToken,
+ IN PSECURITY_TOKEN_PROXY_DATA ProxyData OPTIONAL,
+ IN PSECURITY_TOKEN_AUDIT_DATA AuditData OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ Create a token object and return a handle opened for access to
+ that token. This API implements the bulk of the work needed
+ for NtCreateToken.
+
+ All parameters except DesiredAccess and ObjectAttributes are assumed
+ to have been probed and captured.
+
+ The output parameter (TokenHandle) is expected to be returned to a
+ safe address, rather than to a user mode address that may cause an
+ exception.
+
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ NOTE: This routine is also used to create the initial system token.
+ In that case, the SystemToken parameter is TRUE and no handle
+ is established to the token. Instead, a pointer to the token
+ is returned via the TokenHandle parameter.
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+
+Arguments:
+
+ TokenHandle - Receives the handle of the newly created token. If the
+ SystemToken parameter is specified is true, then this parameter
+ receives a pointer to the token instead of a handle to the token.
+
+ RequestorMode - The mode of the caller on whose behalf the token
+ is being created.
+
+ DesiredAccess - Is an access mask indicating which access types
+ the handle is to provide to the new object.
+
+ ObjectAttributes - Points to the standard object attributes data
+ structure. Refer to the NT Object Management
+ Specification for a description of this data structure.
+
+ TokenType - Type of token to be created. Privilege is required
+ to create any type of token.
+
+ ImpersonationLevel - If the token type is TokenImpersonation, then
+ this parameter is used to specify the impersonation level of
+ the token.
+
+ AuthenticationId - Points to a LUID (or LUID) providing a unique
+ identifier associated with the authentication. This is used
+ within security only, for audit purposes.
+
+ ExpirationTime - Time at which the token becomes invalid. If this
+ value is specified as zero, then the token has no expiration
+ time.
+
+ User - Is the user SID to place in the token.
+
+ GroupCount - Indicates the number of groups in the 'Groups' parameter.
+ This value may be zero, in which case the 'Groups' parameter is
+ ignored.
+
+ Groups - Are the group SIDs, and their corresponding attributes,
+ to place in the token.
+
+ GroupsLength - Indicates the length, in bytes, of the array of groups
+ to place in the token.
+
+ PrivilegeCount - Indicates the number of privileges in the 'Privileges'
+ parameter. This value may be zero, in which case the 'Privileges'
+ parameter is ignored.
+
+ Privileges - Are the privilege LUIDs, and their corresponding attributes,
+ to place in the token.
+
+ PrivilegesLength - Indicates the length, in bytes, of the array of
+ privileges to place in the token.
+
+ Owner - (Optionally) identifies an identifier that is to be used
+ as the default owner for the token. If not provided, the
+ user ID is made the default owner.
+
+ PrimaryGroup - Identifies which of the group IDs is to be the
+ primary group of the token.
+
+ DefaultDacl - (optionally) establishes an ACL to be used as the
+ default discretionary access protection for the token.
+
+ TokenSource - Identifies the token source name string and
+ identifier to be assigned to the token.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the operation was successful.
+
+ STATUS_INVALID_OWNER - Indicates the ID provided to be assigned
+ as the default owner of the token does not have an attribute
+ indicating it may be assigned as an owner.
+
+ STATUS_INVALID_PRIMARY_GROUP - Indicates the group ID provided
+ via the PrimaryGroup parameter was not among those assigned
+ to the token in the Groups parameter.
+
+ STATUS_INVALID_PARAMETER - Indicates that a required parameter,
+ such as User or PrimaryGroup, was not provided with a legitimate
+ value.
+
+--*/
+
+{
+
+ PTOKEN Token;
+ NTSTATUS Status;
+
+ ULONG PagedPoolSize;
+
+ ULONG PrimaryGroupLength;
+
+ ULONG TokenBodyLength;
+ ULONG VariableLength;
+
+ ULONG DefaultOwnerIndex;
+
+ ULONG NextFree;
+ PSID NextSidFree;
+
+ ULONG DynamicLength = TOKEN_DEFAULT_DYNAMIC_CHARGE;
+ ULONG DynamicLengthUsed;
+
+ ULONG SubAuthorityCount;
+ ULONG GroupIndex;
+ ULONG PrivilegeIndex;
+ BOOLEAN OwnerFound;
+
+ UCHAR TokenFlags = 0;
+
+ ACCESS_STATE AccessState;
+ AUX_ACCESS_DATA AuxData;
+ LUID NewModifiedId;
+
+ PAGED_CODE();
+
+ ASSERT( sizeof(SECURITY_IMPERSONATION_LEVEL) <= sizeof(ULONG) );
+
+ //
+ // Make sure the Enabled and Enabled-by-default bits are set on every
+ // mandatory group.
+ //
+
+ for (GroupIndex=0; GroupIndex < GroupCount; GroupIndex++) {
+ if (Groups[GroupIndex].Attributes & SE_GROUP_MANDATORY) {
+ Groups[GroupIndex].Attributes |= (SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT);
+ }
+ }
+
+ //
+ // Check to see if the token being created is going to be granted
+ // SeChangeNotifyPrivilege. If so, set a flag in the TokenFlags field
+ // so we can find this out quickly.
+ //
+
+ for (PrivilegeIndex = 0; PrivilegeIndex < PrivilegeCount; PrivilegeIndex++) {
+
+ if (((RtlEqualLuid(&Privileges[PrivilegeIndex].Luid,&SeChangeNotifyPrivilege))
+ &&
+ (Privileges[PrivilegeIndex].Attributes & SE_PRIVILEGE_ENABLED))) {
+
+ TokenFlags = TOKEN_HAS_TRAVERSE_PRIVILEGE;
+ break;
+ }
+ }
+
+
+ //
+ // Get a ModifiedId to use
+ //
+
+ ExAllocateLocallyUniqueId( &NewModifiedId );
+
+ //
+ // Validate the owner ID, if provided and establish the default
+ // owner index.
+ //
+
+ if (!ARGUMENT_PRESENT(Owner)) {
+
+ DefaultOwnerIndex = 0;
+
+ } else {
+
+
+ if ( RtlEqualSid( Owner, User->Sid ) ) {
+
+ DefaultOwnerIndex = 0;
+
+ } else {
+
+ GroupIndex = 0;
+ OwnerFound = FALSE;
+
+ while ((GroupIndex < GroupCount) && (!OwnerFound)) {
+
+ if ( RtlEqualSid( Owner, (Groups[GroupIndex].Sid) ) ) {
+
+ //
+ // Found a match - make sure it is assignable as owner.
+ //
+
+ if ( SepArrayGroupAttributes( Groups, GroupIndex ) &
+ SE_GROUP_OWNER ) {
+
+ DefaultOwnerIndex = GroupIndex + 1;
+ OwnerFound = TRUE;
+
+ } else {
+
+ return STATUS_INVALID_OWNER;
+
+ } // endif Owner attribute set
+
+ } // endif owner = group
+
+ GroupIndex += 1;
+
+ } // endwhile
+
+ if (!OwnerFound) {
+
+ return STATUS_INVALID_OWNER;
+
+ } // endif !OwnerFound
+ } // endif owner = user
+ } // endif owner specified
+
+
+
+ //
+ // Increment the reference count for this logon session
+ // (fail if there is no corresponding logon session.)
+ //
+
+ Status = SepReferenceLogonSession( AuthenticationId );
+ if ( !NT_SUCCESS(Status) ) {
+ return Status;
+ }
+
+
+
+
+ //
+ // Calculate the length needed for the variable portion of the token
+ // This includes the User ID, Group IDs, and Privileges
+ //
+
+ VariableLength = GroupsLength + PrivilegesLength;
+
+ SubAuthorityCount = ((SID *)(User->Sid))->SubAuthorityCount;
+ VariableLength += sizeof(SID_AND_ATTRIBUTES) +
+ (ULONG)LongAlign(RtlLengthRequiredSid( SubAuthorityCount ));
+
+
+
+ //
+ // Calculate the length needed for the dynamic portion of the token
+ // This includes the default Dacl and the primary group.
+ //
+
+ SubAuthorityCount = ((SID *)PrimaryGroup)->SubAuthorityCount;
+ DynamicLengthUsed = (ULONG)LongAlign(RtlLengthRequiredSid( SubAuthorityCount ));
+
+ if (ARGUMENT_PRESENT(DefaultDacl)) {
+ DynamicLengthUsed += (ULONG)LongAlign(DefaultDacl->AclSize);
+ }
+
+ if (DynamicLengthUsed > DynamicLength) {
+ DynamicLength = DynamicLengthUsed;
+ }
+
+ //
+ // Now create the token body
+ //
+
+ TokenBodyLength = sizeof(TOKEN) + VariableLength;
+ PagedPoolSize = TokenBodyLength + DynamicLength;
+
+
+ Status = ObCreateObject(
+ RequestorMode, // ProbeMode
+ SepTokenObjectType, // ObjectType
+ ObjectAttributes, // ObjectAttributes
+ RequestorMode, // OwnershipMode
+ NULL, // ParseContext
+ TokenBodyLength, // ObjectBodySize
+ PagedPoolSize, // PagedPoolCharge
+ 0, // NonPagedPoolCharge
+ (PVOID *)&Token // Return pointer to object
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ SepDeReferenceLogonSession( AuthenticationId );
+ return Status;
+ }
+
+ //
+ // After this point, we rely on token deletion to clean up the referenced
+ // logon session if the creation fails.
+ //
+
+
+ //
+ // Main Body initialization
+ //
+
+
+ ExAllocateLocallyUniqueId( &(Token->TokenId) );
+ Token->AuthenticationId = (*AuthenticationId);
+ Token->TokenInUse = FALSE;
+ Token->ModifiedId = NewModifiedId;
+ Token->ExpirationTime = (*ExpirationTime);
+ Token->TokenType = TokenType;
+ Token->ImpersonationLevel = ImpersonationLevel;
+ Token->TokenSource = (*TokenSource);
+
+ Token->TokenFlags = TokenFlags;
+
+ Token->DynamicCharged = DynamicLength;
+ Token->DynamicAvailable = DynamicLength - DynamicLengthUsed;
+
+ Token->DefaultOwnerIndex = DefaultOwnerIndex;
+ Token->DefaultDacl = NULL;
+
+ Token->VariableLength = VariableLength;
+
+ // Ensure SepTokenDeleteMethod knows the buffers aren't allocated yet.
+ Token->ProxyData = NULL;
+ Token->AuditData = NULL;
+ Token->DynamicPart = NULL;
+
+ if (ARGUMENT_PRESENT( ProxyData )) {
+
+ Status = SepCopyProxyData(
+ &Token->ProxyData,
+ ProxyData
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ ObDereferenceObject( Token );
+ return( STATUS_NO_MEMORY );
+ }
+
+ } else {
+
+ Token->ProxyData = NULL;
+ }
+
+ if (ARGUMENT_PRESENT( AuditData )) {
+
+ Token->AuditData = ExAllocatePool( PagedPool, sizeof( SECURITY_TOKEN_AUDIT_DATA ));
+
+ if (Token->AuditData == NULL) {
+ ObDereferenceObject( Token );
+ return( STATUS_NO_MEMORY );
+ }
+
+ *(Token->AuditData) = *AuditData;
+
+ } else {
+
+ Token->AuditData = NULL;
+ }
+
+
+ //
+ // Variable part initialization
+ // Data is in the following order:
+ //
+ // Privileges array
+ // User (SID_AND_ATTRIBUTES)
+ // Groups (SID_AND_ATTRIBUTES)
+ // SIDs
+ //
+
+ NextFree = (ULONG)(&Token->VariablePart);
+ Token->Privileges = (PLUID_AND_ATTRIBUTES)NextFree;
+ Token->PrivilegeCount = PrivilegeCount;
+ RtlCopyLuidAndAttributesArray( PrivilegeCount,
+ Privileges,
+ (PLUID_AND_ATTRIBUTES)NextFree
+ );
+
+ NextFree += (PrivilegeCount * (ULONG)sizeof(LUID_AND_ATTRIBUTES));
+ VariableLength -= ( (PrivilegeCount * (ULONG)sizeof(LUID_AND_ATTRIBUTES))
+ + (ULONG)sizeof(SID_AND_ATTRIBUTES) );
+
+ NextSidFree = (PSID)(NextFree +
+ ((1 + GroupCount) * (ULONG)sizeof(SID_AND_ATTRIBUTES))
+ );
+ Token->UserAndGroups = (PSID_AND_ATTRIBUTES)NextFree;
+ Token->UserAndGroupCount = 1 + GroupCount;
+ Status = RtlCopySidAndAttributesArray(
+ 1,
+ User,
+ VariableLength,
+ (PSID_AND_ATTRIBUTES)NextFree,
+ NextSidFree,
+ &NextSidFree,
+ &VariableLength
+ );
+
+ ASSERT(NT_SUCCESS(Status));
+ NextFree += (ULONG)sizeof(SID_AND_ATTRIBUTES);
+ VariableLength -= (GroupCount * (ULONG)sizeof(SID_AND_ATTRIBUTES));
+
+ Status = RtlCopySidAndAttributesArray(
+ GroupCount,
+ Groups,
+ VariableLength,
+ (PSID_AND_ATTRIBUTES)NextFree,
+ NextSidFree,
+ &NextSidFree,
+ &VariableLength
+ );
+
+
+ ASSERT(NT_SUCCESS(Status));
+ NextFree += (GroupCount * (ULONG)sizeof(SID_AND_ATTRIBUTES));
+
+ //
+ // Dynamic part initialization
+ // Data is in the following order:
+ //
+ // PrimaryGroup (SID)
+ // Default Discreationary Acl (ACL)
+ //
+
+ Token->DynamicPart = (PULONG)ExAllocatePoolWithTag( PagedPool, DynamicLength, 'dTeS' );
+
+ //
+ // The attempt to allocate the DynamicPart of the token may have
+ // failed. Dereference the created object and exit with an error.
+ //
+
+ if (Token->DynamicPart == NULL) {
+ ObDereferenceObject( Token );
+ return( STATUS_NO_MEMORY );
+ }
+
+
+ NextFree = (ULONG)(Token->DynamicPart);
+
+ Token->PrimaryGroup = (PSID)NextFree;
+ PrimaryGroupLength = RtlLengthRequiredSid( ((SID *)PrimaryGroup)->SubAuthorityCount );
+ RtlCopySid( PrimaryGroupLength, (PSID)NextFree, PrimaryGroup );
+ NextFree += ((ULONG)LongAlign(PrimaryGroupLength));
+
+ if (ARGUMENT_PRESENT(DefaultDacl)) {
+ Token->DefaultDacl = (PACL)NextFree;
+
+ RtlMoveMemory( (PVOID)NextFree,
+ (PVOID)DefaultDacl,
+ DefaultDacl->AclSize
+ );
+ }
+
+#ifdef TOKEN_DEBUG
+////////////////////////////////////////////////////////////////////////////
+//
+// Debug
+ SepDumpToken( Token );
+// Debug
+//
+////////////////////////////////////////////////////////////////////////////
+#endif //TOKEN_DEBUG
+
+
+ //
+ // Insert the token unless it is a system token.
+ //
+
+ if (!SystemToken) {
+
+ Status = SeCreateAccessState(
+ &AccessState,
+ &AuxData,
+ DesiredAccess,
+ &SepTokenObjectType->TypeInfo.GenericMapping
+ );
+
+ if ( NT_SUCCESS(Status) ) {
+ BOOLEAN PrivilegeHeld;
+
+ PrivilegeHeld = SeSinglePrivilegeCheck(
+ SeCreateTokenPrivilege,
+ KeGetPreviousMode()
+ );
+
+ if (PrivilegeHeld) {
+
+ Status = ObInsertObject( Token,
+ &AccessState,
+ 0,
+ 0,
+ (PVOID *)NULL,
+ TokenHandle
+ );
+
+ } else {
+
+ Status = STATUS_PRIVILEGE_NOT_HELD;
+ ObDereferenceObject( Token );
+ }
+
+ SeDeleteAccessState( &AccessState );
+
+ } else {
+
+ ObDereferenceObject( Token );
+ }
+ } else {
+
+ ASSERT( NT_SUCCESS( Status ) );
+ ObDeleteCapturedInsertInfo(Token);
+ //
+ // Return pointer instead of handle.
+ //
+
+ (*TokenHandle) = (HANDLE)Token;
+ }
+
+ return Status;
+
+}
+
+BOOLEAN
+SepIdAssignableAsOwner(
+ IN PTOKEN Token,
+ IN ULONG Index
+ )
+
+/*++
+
+
+Routine Description:
+
+ This routine returns a boolean value indicating whether the user
+ or group ID in the specified token with the specified index is
+ assignable as the owner of an object.
+
+ If the index is 0, which is always the USER ID, then the ID is
+ assignable as owner. Otherwise, the ID is that of a group, and
+ it must have the "Owner" attribute set to be assignable.
+
+
+
+Arguments:
+
+ Token - Pointer to a locked Token to use.
+
+ Index - Index into the Token's UserAndGroupsArray. This value
+ is assumed to be valid.
+
+Return Value:
+
+ TRUE - Indicates the index corresponds to an ID that may be assigned
+ as the owner of objects.
+
+ FALSE - Indicates the index does not correspond to an ID that may be
+ assigned as the owner of objects.
+
+--*/
+{
+ PAGED_CODE();
+
+ if (Index == 0) {
+
+ return TRUE;
+
+ } else {
+
+ return (BOOLEAN)
+ ( (SepTokenGroupAttributes(Token,Index) & SE_GROUP_OWNER)
+ != 0
+ );
+ }
+}
diff --git a/private/ntos/se/tokenadj.c b/private/ntos/se/tokenadj.c
new file mode 100644
index 000000000..fb89fa5e9
--- /dev/null
+++ b/private/ntos/se/tokenadj.c
@@ -0,0 +1,1447 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ tokenadj.c
+
+Abstract:
+
+ This module implements the services that perform individual adjustments
+ on token objects.
+
+Author:
+
+ Jim Kelly (JimK) 15-June-1990
+
+Environment:
+
+ Kernel mode only.
+
+Revision History:
+
+--*/
+
+#include "sep.h"
+#include "seopaque.h"
+#include "tokenp.h"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE,NtAdjustPrivilegesToken)
+#pragma alloc_text(PAGE,NtAdjustGroupsToken)
+#pragma alloc_text(PAGE,SepAdjustPrivileges)
+#pragma alloc_text(PAGE,SepAdjustGroups)
+#endif
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Token Object Routines & Methods //
+// //
+////////////////////////////////////////////////////////////////////////
+
+
+NTSTATUS
+NtAdjustPrivilegesToken (
+ IN HANDLE TokenHandle,
+ IN BOOLEAN DisableAllPrivileges,
+ IN PTOKEN_PRIVILEGES NewState OPTIONAL,
+ IN ULONG BufferLength OPTIONAL,
+ IN PTOKEN_PRIVILEGES PreviousState OPTIONAL,
+ OUT PULONG ReturnLength
+ )
+
+
+/*++
+
+
+Routine Description:
+
+ This routine is used to disable or enable privileges in the
+ specified token. The absence of some of the privileges listed to
+ be changed won't effect the successful modification of the
+ privileges that are in the token. The previous enabled/disabled
+ state of changed privileges may optionally be capture (for
+ resetting later).
+
+ TOKEN_ADJUST_PRIVILEGES access is required to enable or disable
+ privileges in a token.
+
+
+Arguments:
+
+ TokenHandle - Provides a handle to the token to operate on.
+
+ DisableAllPrivileges - This boolean parameter may be
+ used to disable all privileges assigned to the token. If
+ this parameter is specified as TRUE, then the NewState parameter is
+ ignored.
+
+ NewState - This (optional) parameter points to a TOKEN_PRIVILEGES
+ data structure containing the privileges whose states are to
+ be adjusted (disabled or enabled). Only the Enabled flag of
+ the attributes associated with each privilege is used. It
+ provides the new value that is to be assigned to the
+ privilege in the token.
+
+ BufferLength - This optional parameter indicates the length (in
+ bytes) of the PreviousState buffer. This value must be
+ provided if the PreviousState parameter is provided.
+
+ PreviousState - This (optional) parameter points to a buffer to
+ receive the state of any privileges actually changed by this
+ request. This information is formated as a TOKEN_PRIVILEGES
+ data structure which may be passed as the NewState parameter
+ in a subsequent call to this routine to restore the original
+ state of those privilges. TOKEN_QUERY access is needed to
+ use this parameter.
+
+ If this buffer does not contain enough space to receive the
+ complete list of modified privileges, then no privilege
+ states are changed and STATUS_BUFFER_TOO_SMALL is returned.
+ In this case, the ReturnLength OUT parameter will
+ contain the actual number of bytes needed to hold the
+ information.
+
+ ReturnLength - Indicates the actual number of bytes needed to
+ contain the previous privilege state information.
+ This parameter is ignored if the PreviousState argument is not
+ passed.
+
+Return Value:
+
+ STATUS_SUCCESS - The service successfully completed the requested
+ operation.
+
+ STATUS_NOT_ALL_ASSIGNED - This NT_SUCCESS severity return status
+ indicates that not all the specified privileges are currently
+ assigned to the caller. All specified privileges that are
+ currently assigned have been successfully adjusted.
+
+ STATUS_BUFFER_TOO_SMALL - Indicates the optional buffer provided
+ to receive the previous states of changed privileges wasn't
+ large enough to receive that information. No changes to
+ privilege states has been made. The number of bytes needed
+ to hold the state change information is returned via the
+ ReturnLength parameter.
+
+ STATUS_INVALID_PARAMETER - Indicates neither the DisableAllPrivileges
+ parameter was specified as true, nor was an explicit NewState
+ provided.
+
+--*/
+
+{
+ KPROCESSOR_MODE PreviousMode;
+ NTSTATUS Status;
+
+ PTOKEN Token;
+
+ ACCESS_MASK DesiredAccess;
+
+ ULONG CapturedPrivilegeCount;
+ PLUID_AND_ATTRIBUTES CapturedPrivileges = NULL;
+ ULONG CapturedPrivilegesLength;
+
+ ULONG LocalReturnLength;
+ ULONG ChangeCount;
+ BOOLEAN ChangesMade;
+
+ ULONG ParameterLength;
+
+ PAGED_CODE();
+
+ //
+ // The semantics of the PreviousState parameter leads to a two-pass
+ // approach to adjusting privileges. The first pass simply checks
+ // to see which privileges will change and counts them. This allows
+ // the amount of space needed to be calculated and returned. If
+ // the caller's PreviousState return buffer is not large enough, then
+ // an error is returned without making any modifications. Otherwise,
+ // a second pass is made to actually make the changes.
+ //
+ //
+
+ if (!DisableAllPrivileges && !ARGUMENT_PRESENT(NewState)) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Get previous processor mode and probe parameters if necessary.
+ //
+
+ PreviousMode = KeGetPreviousMode();
+ if (PreviousMode != KernelMode) {
+ try {
+
+ //
+ // Make sure we can see all of the new state
+ //
+
+ if (!DisableAllPrivileges) {
+
+ ProbeForRead(
+ NewState,
+ sizeof(TOKEN_PRIVILEGES),
+ sizeof(ULONG)
+ );
+
+ CapturedPrivilegeCount = NewState->PrivilegeCount;
+ ParameterLength = (ULONG)sizeof(TOKEN_PRIVILEGES) +
+ ( (CapturedPrivilegeCount - ANYSIZE_ARRAY) *
+ (ULONG)sizeof(LUID_AND_ATTRIBUTES) );
+
+ ProbeForRead(
+ NewState,
+ ParameterLength,
+ sizeof(ULONG)
+ );
+
+ }
+
+
+ //
+ // Check the PreviousState buffer for writeability
+ //
+
+ if (ARGUMENT_PRESENT(PreviousState)) {
+
+ ProbeForWrite(
+ PreviousState,
+ BufferLength,
+ sizeof(ULONG)
+ );
+
+ ProbeForWriteUlong(ReturnLength);
+ }
+
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ return GetExceptionCode();
+ }
+
+ } else {
+
+ if (!DisableAllPrivileges) {
+
+ CapturedPrivilegeCount = NewState->PrivilegeCount;
+ }
+ }
+
+
+
+ //
+ // Capture NewState if passed.
+ //
+
+ if (!DisableAllPrivileges) {
+
+ try {
+
+
+ Status = SeCaptureLuidAndAttributesArray(
+ (NewState->Privileges),
+ CapturedPrivilegeCount,
+ PreviousMode,
+ NULL, 0,
+ PagedPool,
+ TRUE,
+ &CapturedPrivileges,
+ &CapturedPrivilegesLength
+ );
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ return GetExceptionCode();
+
+ }
+
+ if (!NT_SUCCESS(Status)) {
+
+ return Status;
+
+ }
+
+ }
+
+
+ //
+ // Reference the token object and validate the caller's right
+ // to adjust the privileges.
+ //
+
+ if (ARGUMENT_PRESENT(PreviousState)) {
+ DesiredAccess = (TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY);
+ } else {
+ DesiredAccess = TOKEN_ADJUST_PRIVILEGES;
+ }
+
+ Status = ObReferenceObjectByHandle(
+ TokenHandle, // Handle
+ DesiredAccess, // DesiredAccess
+ SepTokenObjectType, // ObjectType
+ PreviousMode, // AccessMode
+ (PVOID *)&Token, // Object
+ NULL // GrantedAccess
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ if (CapturedPrivileges != NULL) {
+ SeReleaseLuidAndAttributesArray(
+ CapturedPrivileges,
+ PreviousMode,
+ TRUE
+ );
+ }
+
+ return Status;
+ }
+
+ //
+ // Gain exclusive access to the token.
+ //
+
+ SepAcquireTokenWriteLock( Token );
+
+ //
+ // First pass through the privileges list - just count the changes
+ //
+
+
+ Status = SepAdjustPrivileges(
+ Token,
+ FALSE, // Don't make changes this pass
+ DisableAllPrivileges,
+ CapturedPrivilegeCount,
+ CapturedPrivileges,
+ PreviousState,
+ &LocalReturnLength,
+ &ChangeCount,
+ &ChangesMade
+ );
+
+ if (ARGUMENT_PRESENT(PreviousState)) {
+
+ try {
+
+ (*ReturnLength) = LocalReturnLength;
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ SepReleaseTokenWriteLock( Token, FALSE );
+ ObDereferenceObject( Token );
+
+ if (CapturedPrivileges != NULL) {
+ SeReleaseLuidAndAttributesArray(
+ CapturedPrivileges,
+ PreviousMode,
+ TRUE
+ );
+ }
+
+ return GetExceptionCode();
+ }
+
+ }
+
+
+ //
+ // Make sure there is enough room to return any requested
+ // information.
+ //
+
+ if (ARGUMENT_PRESENT(PreviousState)) {
+ if (LocalReturnLength > BufferLength) {
+
+ SepReleaseTokenWriteLock( Token, FALSE );
+ ObDereferenceObject( Token );
+
+ if (CapturedPrivileges != NULL) {
+ SeReleaseLuidAndAttributesArray(
+ CapturedPrivileges,
+ PreviousMode,
+ TRUE
+ );
+ }
+
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+ }
+
+ //
+ // Second pass through the privileges list - Make the changes.
+ //
+ // Note that the internal routine attempts to write the previous
+ // state directly to the caller's buffer - and so may get an exception.
+ //
+
+ try {
+
+ Status = SepAdjustPrivileges(
+ Token,
+ TRUE, // Make the changes this pass
+ DisableAllPrivileges,
+ CapturedPrivilegeCount,
+ CapturedPrivileges,
+ PreviousState,
+ &LocalReturnLength,
+ &ChangeCount,
+ &ChangesMade
+ );
+
+
+ if (ARGUMENT_PRESENT(PreviousState)) {
+
+ PreviousState->PrivilegeCount = ChangeCount;
+ }
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ SepReleaseTokenWriteLock( Token, TRUE );
+ ObDereferenceObject( Token );
+ if (CapturedPrivileges != NULL) {
+ SeReleaseLuidAndAttributesArray(
+ CapturedPrivileges,
+ PreviousMode,
+ TRUE
+ );
+ }
+ return GetExceptionCode();
+
+ }
+
+
+ SepReleaseTokenWriteLock( Token, ChangesMade );
+ ObDereferenceObject( Token );
+ if (CapturedPrivileges != NULL) {
+ SeReleaseLuidAndAttributesArray(
+ CapturedPrivileges,
+ PreviousMode,
+ TRUE
+ );
+ }
+
+ return Status;
+
+}
+
+
+NTSTATUS
+NtAdjustGroupsToken (
+ IN HANDLE TokenHandle,
+ IN BOOLEAN ResetToDefault,
+ IN PTOKEN_GROUPS NewState OPTIONAL,
+ IN ULONG BufferLength OPTIONAL,
+ IN PTOKEN_GROUPS PreviousState OPTIONAL,
+ OUT PULONG ReturnLength
+ )
+
+/*++
+
+
+Routine Description:
+
+ This routine is used to disable or enable groups in the specified
+ token. The absence of some of the groups listed to be changed
+ won't effect the successful modification of the groups that are in
+ the token. The previous enabled/disabled state of changed groups
+ may optionally be capture (for resetting later).
+
+ TOKEN_ADJUST_GROUPS access is required to enable or disable groups
+ in a token
+
+ Note that mandatory groups can not be disabled. An attempt
+ disable any mandatory groups will cause the call to fail, leaving
+ the state of all groups unchanged.
+
+
+Arguments:
+
+ TokenHandle - Provides a handle to the token to operate on.
+
+ ResetToDefault - The parameter indicates whether all the groups
+ in the token are to be reset to their default enabled/disabled
+ state.
+
+ NewState - This parameter points to a TOKEN_GROUPS data structure
+ containing the groups whose states are to be adjusted
+ (disabled or enabled). Only the Enabled flag of the
+ attributes associated with each group is used. It provides
+ the new value that is to be assigned to the group in the
+ token. If the ResetToDefault argument is specified as TRUE,
+ then this argument is ignored. Otherwise, it must be passed.
+
+ BufferLength - This optional parameter indicates the length (in
+ bytes) of the PreviousState buffer. This value must be
+ provided if the PreviousState parameter is provided.
+
+ PreviousState - This (optional) parameter points to a buffer to
+ receive the state of any groups actually changed by this
+ request. This information is formated as a TOKEN_GROUPS data
+ structure which may be passed as the NewState parameter in a
+ subsequent call to NtAdjustGroups to restore the original state
+ of those groups. TOKEN_QUERY access is needed to use this
+ parameter.
+
+ If this buffer does not contain enough space to receive the
+ complete list of modified groups, then no group states are
+ changed and STATUS_BUFFER_TOO_SMALL is returned. In this
+ case, the ReturnLength return parameter will contain the
+ actual number of bytes needed to hold the information.
+
+ ReturnLength - Indicates the actual number of bytes needed to
+ contain the previous group state information.
+ This parameter is ignored if the PreviousState argument is not
+ passed.
+
+
+Return Value:
+
+ STATUS_SUCCESS - The service successfully completed the requested
+ operation.
+
+ STATUS_NOT_ALL_ASSIGNED - This NT_SUCCESS severity return status
+ indicates that not all the specified groups are currently
+ assigned to the caller. All specified groups that are
+ currently assigned have been successfully adjusted.
+
+ STATUS_CANT_DISABLE_MANDATORY - Indicates an attempt was made to
+ disable a mandatory group. The states of all groups remains
+ unchanged.
+
+ STATUS_BUFFER_TOO_SMALL - Indicates the optional buffer provided
+ to receive the previous states of changed group wasn't large
+ enough to receive that information. No changes to group
+ states has been made. The number of bytes needed to hold the
+ state change information is returned via the ReturnLength
+ parameter.
+
+ STATUS_INVALID_PARAMETER - Indicates neither the ResetToDefault
+ parameter was specified as true, nor was an explicit NewState
+ provided.
+
+--*/
+{
+
+ KPROCESSOR_MODE PreviousMode;
+ NTSTATUS Status;
+
+ PTOKEN Token;
+
+ ACCESS_MASK DesiredAccess;
+
+ ULONG CapturedGroupCount;
+ PSID_AND_ATTRIBUTES CapturedGroups = NULL;
+ ULONG CapturedGroupsLength;
+
+ ULONG LocalReturnLength;
+ ULONG ChangeCount;
+ BOOLEAN ChangesMade;
+ PSID SidBuffer;
+
+ PAGED_CODE();
+
+ //
+ // The semantics of the PreviousState parameter and the
+ // STATUS_CANT_DISABLE_MANDATORY completion status leads to a two-pass
+ // approach to adjusting groups. The first pass simply checks
+ // to see which groups will change and counts them. This allows
+ // the amount of space needed to be calculated and returned. If
+ // the caller's PreviousState return buffer is not large enough, or
+ // one of the specified groups is a mandatory group, then an error
+ // is returned without making any modifications. Otherwise, a second
+ // pass is made to actually make the changes.
+ //
+
+ if (!ResetToDefault && !ARGUMENT_PRESENT(NewState)) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Get previous processor mode and probe parameters if necessary.
+ //
+
+ PreviousMode = KeGetPreviousMode();
+ if (PreviousMode != KernelMode) {
+ try {
+
+ if (!ResetToDefault) {
+ ProbeForRead(
+ NewState,
+ sizeof(TOKEN_GROUPS),
+ sizeof(ULONG)
+ );
+ }
+
+ if (ARGUMENT_PRESENT(PreviousState)) {
+
+ ProbeForRead(
+ PreviousState,
+ BufferLength,
+ sizeof(ULONG)
+ );
+
+ //
+ // This parameter is only used if PreviousState
+ // is present
+ //
+
+ ProbeForWriteUlong(ReturnLength);
+
+ }
+
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ return GetExceptionCode();
+ }
+ }
+
+ //
+ // Capture NewState.
+ //
+
+ if (!ResetToDefault) {
+
+ try {
+
+ CapturedGroupCount = NewState->GroupCount;
+ Status = SeCaptureSidAndAttributesArray(
+ &(NewState->Groups[0]),
+ CapturedGroupCount,
+ PreviousMode,
+ NULL, 0,
+ PagedPool,
+ TRUE,
+ &CapturedGroups,
+ &CapturedGroupsLength
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ return Status;
+
+ }
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ return GetExceptionCode();
+
+ } // endtry
+ } // endif !ResetToDefault
+
+
+ //
+ // Reference the token object and validate the caller's right
+ // to adjust the groups.
+ //
+
+ if (ARGUMENT_PRESENT(PreviousState)) {
+ DesiredAccess = (TOKEN_ADJUST_GROUPS | TOKEN_QUERY);
+ } else {
+ DesiredAccess = TOKEN_ADJUST_GROUPS;
+ }
+
+ Status = ObReferenceObjectByHandle(
+ TokenHandle, // Handle
+ DesiredAccess, // DesiredAccess
+ SepTokenObjectType, // ObjectType
+ PreviousMode, // AccessMode
+ (PVOID *)&Token, // Object
+ NULL // GrantedAccess
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ if (ARGUMENT_PRESENT(CapturedGroups)) {
+ SeReleaseSidAndAttributesArray( CapturedGroups, PreviousMode, TRUE );
+ }
+
+ return Status;
+ }
+
+ //
+ // Gain exclusive access to the token.
+ //
+
+ SepAcquireTokenWriteLock( Token );
+
+ //
+ // First pass through the groups list.
+ //
+ // This pass is always necessary for groups to make sure the caller
+ // isn't trying to do anything illegal to mandatory groups.
+ //
+
+ Status = SepAdjustGroups(
+ Token,
+ FALSE, // Don't make changes this pass
+ ResetToDefault,
+ CapturedGroupCount,
+ CapturedGroups,
+ PreviousState,
+ NULL, // Not returning SIDs this call
+ &LocalReturnLength,
+ &ChangeCount,
+ &ChangesMade
+ );
+
+ if (ARGUMENT_PRESENT(PreviousState)) {
+
+ try {
+
+ (*ReturnLength) = LocalReturnLength;
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ SepReleaseTokenWriteLock( Token, FALSE );
+ ObDereferenceObject( Token );
+
+ if (ARGUMENT_PRESENT(CapturedGroups)) {
+ SeReleaseSidAndAttributesArray(
+ CapturedGroups,
+ PreviousMode,
+ TRUE
+ );
+ }
+
+ return GetExceptionCode();
+ }
+ }
+
+ //
+ // Make sure we didn't encounter an error
+ //
+
+ if (!NT_SUCCESS(Status)) {
+
+ SepReleaseTokenWriteLock( Token, FALSE );
+ ObDereferenceObject( Token );
+
+ if (ARGUMENT_PRESENT(CapturedGroups)) {
+ SeReleaseSidAndAttributesArray(
+ CapturedGroups,
+ PreviousMode,
+ TRUE
+ );
+ }
+
+ return Status;
+
+ }
+
+ //
+ // Make sure there is enough room to return requested information.
+ // Also go on to calculate where the SID values go.
+ //
+
+ if (ARGUMENT_PRESENT(PreviousState)) {
+ if (LocalReturnLength > BufferLength) {
+
+ SepReleaseTokenWriteLock( Token, FALSE );
+ ObDereferenceObject( Token );
+
+ if (ARGUMENT_PRESENT(CapturedGroups)) {
+ SeReleaseSidAndAttributesArray(
+ CapturedGroups,
+ PreviousMode,
+ TRUE
+ );
+ }
+
+
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // Calculate where the SIDs can be placed in the PreviousState
+ // buffer.
+ //
+
+ SidBuffer = (PSID)(LongAlign(
+ (ULONG)PreviousState + (ULONG)sizeof(TOKEN_GROUPS) +
+ (ChangeCount * (ULONG)sizeof(SID_AND_ATTRIBUTES)) -
+ (ANYSIZE_ARRAY * (ULONG)sizeof(SID_AND_ATTRIBUTES))
+ ) );
+
+ }
+
+ //
+ // Second pass through the groups list.
+ //
+
+ try {
+
+ Status = SepAdjustGroups(
+ Token,
+ TRUE, // Make changes in this pass
+ ResetToDefault,
+ CapturedGroupCount,
+ CapturedGroups,
+ PreviousState,
+ SidBuffer,
+ &LocalReturnLength,
+ &ChangeCount,
+ &ChangesMade
+ );
+
+ if (ARGUMENT_PRESENT(PreviousState)) {
+
+ PreviousState->GroupCount = ChangeCount;
+ }
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ //SepFreeToken( Token, TRUE );
+ SepReleaseTokenWriteLock( Token, TRUE );
+ ObDereferenceObject( Token );
+ SeReleaseSidAndAttributesArray( CapturedGroups, PreviousMode, TRUE );
+ return GetExceptionCode();
+
+ }
+
+ //SepFreeToken( Token, ChangesMade );
+ SepReleaseTokenWriteLock( Token, ChangesMade );
+ ObDereferenceObject( Token );
+
+ if (ARGUMENT_PRESENT(CapturedGroups)) {
+ SeReleaseSidAndAttributesArray( CapturedGroups, PreviousMode, TRUE );
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+SepAdjustPrivileges(
+ IN PTOKEN Token,
+ IN BOOLEAN MakeChanges,
+ IN BOOLEAN DisableAllPrivileges,
+ IN ULONG PrivilegeCount OPTIONAL,
+ IN PLUID_AND_ATTRIBUTES NewState OPTIONAL,
+ OUT PTOKEN_PRIVILEGES PreviousState OPTIONAL,
+ OUT PULONG ReturnLength,
+ OUT PULONG ChangeCount,
+ OUT PBOOLEAN ChangesMade
+ )
+
+/*++
+
+
+Routine Description:
+
+ This routine is used to walk the privileges array in a token as a
+ result of a request to adjust privileges.
+
+ If the MakeChanges parameter is FALSE, this routine simply determines
+ what changes are needed and how much space is necessary to save the
+ current state of changed privileges.
+
+ If the MakeChanges parameter is TRUE, this routine will not only
+ calculate the space necessary to save the current state, but will
+ actually make the changes.
+
+ This routine makes the following assumptions:
+
+ 1) The token is locked for exclusive access.
+
+ 2) The PrivilegeCount and NewState parameters (if passed) are captured
+ and accesses to them will not result in access violations.
+
+ 4) Any access violations encountered may leave the request
+ partially completed. It is the calling routine's responsibility
+ to catch exceptions.
+
+ 5) The calling routine is responsible for inrementing the token's
+ ModifiedId field.
+
+Arguments:
+
+ Token - Pointer to the token to act upon.
+
+ MakeChanges - A boolean value indicating whether the changes should
+ actually be made, or just evaluated. A value of TRUE indicates
+ the changes should be made.
+
+ DisableAllPrivilegs - A boolean value indicating whether all privileges
+ are to be disabled, or only select, specified privileges. A value
+ of TRUE indicates all privileges are to be disabled.
+
+ PrivilegeCount - This parameter is required only if the NewState parameter
+ is used. In that case, this parameter indicates how many entries are
+ in the NewState parameter.
+
+ NewState - This parameter is ignored if the DisableAllPrivileges
+ argument is TRUE. If the DisableAllPrivileges argument is FALSE,
+ then this parameter must be provided and specifies the new state
+ to set privileges to (enabled or disabled).
+
+ PreviousState - This (optional) parameter points to a buffer to
+ receive the state of any privileges actually changed by this
+ request. This information is formated as a TOKEN_PRIVILEGES data
+ structure which may be passed as the NewState parameter in a
+ subsequent call to NtAdjustPrivileges to restore the original state
+ of those privileges. It is the caller's responsibility to make
+ sure this buffer is large enough to receive all the state
+ information.
+
+ ReturnLength - Points to a buffer to receive the number of bytes needed
+ to retrieve the previous state information of changed privileges.
+ This parameter is ignored if the PreviousState argument is not
+ passed.
+
+ ChangeCount - Points to a ULONG to receive the number of privileges
+ which were adjusted (or would be adjusted, if changes are made).
+
+ ChangesMade - Points to a boolean flag which is to receive an indication
+ as to whether any changes were made as a result of this call. This
+ is expected to be used to decide whether or not to increment the
+ token's ModifiedId field.
+
+Return Value:
+
+ STATUS_SUCCESS - Call completed sccessfully.
+
+ STATUS_NOT_ALL_ASSIGNED - Indicates not all the specified adjustments
+ have been made (or could be made, if update wasn't requested).
+
+--*/
+{
+ NTSTATUS CompletionStatus = STATUS_SUCCESS;
+
+ ULONG OldIndex;
+ ULONG NewIndex;
+ BOOLEAN Found;
+ ULONG MatchCount = 0;
+
+ LUID_AND_ATTRIBUTES CurrentPrivilege;
+
+ PAGED_CODE();
+
+ //
+ // Walk through the privileges array to determine which need to be
+ // adjusted.
+ //
+
+ OldIndex = 0;
+ (*ChangeCount) = 0;
+
+ while (OldIndex < Token->PrivilegeCount) {
+
+ CurrentPrivilege = (Token->Privileges)[OldIndex];
+
+ if (DisableAllPrivileges) {
+
+ if (SepTokenPrivilegeAttributes(Token,OldIndex) &
+ SE_PRIVILEGE_ENABLED ) {
+
+ //
+ // Change, if necessary (saving previous state if
+ // appropriate).
+ //
+
+ if (MakeChanges) {
+
+ if (ARGUMENT_PRESENT(PreviousState)) {
+
+ PreviousState->Privileges[(*ChangeCount)] =
+ CurrentPrivilege;
+ }
+
+ SepTokenPrivilegeAttributes(Token,OldIndex) &=
+ ~SE_PRIVILEGE_ENABLED;
+
+
+
+ } //endif make changes
+
+ //
+ // increment the number of changes
+ //
+
+ (*ChangeCount) += 1;
+
+ } // endif privilege enabled
+
+ } else {
+
+ //
+ // Selective adjustments - this is a little trickier
+ // Compare the current privilege to each of those in
+ // the NewState array. If a match is found, then adjust
+ // the current privilege appropriately.
+ //
+
+ NewIndex = 0;
+ Found = FALSE;
+
+ while ( (NewIndex < PrivilegeCount) && !Found) {
+
+ //
+ // Look for a comparison
+ //
+
+ if (RtlEqualLuid(&CurrentPrivilege.Luid,&NewState[NewIndex].Luid)) {
+
+ Found = TRUE;
+ MatchCount += 1;
+
+ if ( (SepArrayPrivilegeAttributes( NewState, NewIndex ) &
+ SE_PRIVILEGE_ENABLED)
+ !=
+ (SepTokenPrivilegeAttributes(Token,OldIndex) &
+ SE_PRIVILEGE_ENABLED) ) {
+
+ //
+ // Change, if necessary (saving previous state if
+ // appropriate).
+ //
+
+ if (MakeChanges) {
+
+ if (ARGUMENT_PRESENT(PreviousState)) {
+
+ PreviousState->Privileges[(*ChangeCount)] =
+ CurrentPrivilege;
+ }
+
+ SepTokenPrivilegeAttributes(Token,OldIndex) &=
+ ~(SepTokenPrivilegeAttributes(Token,OldIndex)
+ & SE_PRIVILEGE_ENABLED);
+ SepTokenPrivilegeAttributes(Token,OldIndex) |=
+ (SepArrayPrivilegeAttributes(NewState,NewIndex)
+ & SE_PRIVILEGE_ENABLED);
+
+ //
+ // if this is SeChangeNotifyPrivilege, then
+ // change its corresponding bit in TokenFlags
+ //
+
+ if (RtlEqualLuid(&CurrentPrivilege.Luid,
+ &SeChangeNotifyPrivilege)) {
+ Token->TokenFlags ^= TOKEN_HAS_TRAVERSE_PRIVILEGE;
+ }
+
+
+
+ } //endif make changes
+
+ //
+ // increment the number of changes
+ //
+
+ (*ChangeCount) += 1;
+
+
+ } // endif change needed
+
+ } // endif found
+
+ NewIndex += 1;
+
+ } // endwhile searching NewState
+
+ } // endelse
+
+ OldIndex += 1;
+
+ } // endwhile privileges in token
+
+ //
+ // If we disabled all privileges, then clear the TokenFlags flag
+ // corresponding to the SeChangeNotifyPrivilege privilege.
+ //
+
+
+ if (DisableAllPrivileges) {
+ Token->TokenFlags &= ~TOKEN_HAS_TRAVERSE_PRIVILEGE;
+ }
+
+ //
+ // Set completion status appropriately if some not assigned
+ //
+
+ if (!DisableAllPrivileges) {
+
+ if (MatchCount < PrivilegeCount) {
+ CompletionStatus = STATUS_NOT_ALL_ASSIGNED;
+ }
+ }
+
+ //
+ // Indicate whether changes were made
+ //
+
+ if ((*ChangeCount) > 0 && MakeChanges) {
+ (*ChangesMade) = TRUE;
+ } else {
+ (*ChangesMade) = FALSE;
+ }
+
+ //
+ // Calculate the space needed to return previous state information
+ //
+
+ if (ARGUMENT_PRESENT(PreviousState)) {
+
+ (*ReturnLength) = (ULONG)sizeof(TOKEN_PRIVILEGES) +
+ ((*ChangeCount) * (ULONG)sizeof(LUID_AND_ATTRIBUTES)) -
+ (ANYSIZE_ARRAY * (ULONG)sizeof(LUID_AND_ATTRIBUTES));
+ }
+
+ return CompletionStatus;
+}
+
+NTSTATUS
+SepAdjustGroups(
+ IN PTOKEN Token,
+ IN BOOLEAN MakeChanges,
+ IN BOOLEAN ResetToDefault,
+ IN ULONG GroupCount,
+ IN PSID_AND_ATTRIBUTES NewState OPTIONAL,
+ OUT PTOKEN_GROUPS PreviousState OPTIONAL,
+ OUT PSID SidBuffer OPTIONAL,
+ OUT PULONG ReturnLength,
+ OUT PULONG ChangeCount,
+ OUT PBOOLEAN ChangesMade
+ )
+
+/*++
+
+
+Routine Description:
+
+ This routine is used to walk the groups array in a token as a
+ result of a request to adjust groups.
+
+ If the MakeChanges parameter is FALSE, this routine simply determines
+ what changes are needed and how much space is necessary to save the
+ current state of changed groups.
+
+ If the MakeChanges parameter is TRUE, this routine will not only
+ calculate the space necessary to save the current state, but will
+ actually make the changes.
+
+ This routine makes the following assumptions:
+
+ 1) The token is locked for exclusive access.
+
+ 2) The NewState parameter is captured and accesses
+ to it will not result in access violations.
+
+ 4) Any access violations encountered may leave the request
+ partially completed. It is the calling routine's responsibility
+ to catch exceptions.
+
+ 5) The calling routine is responsible for inrementing the token's
+ ModifiedId field.
+
+Arguments:
+
+ Token - Pointer to the token to act upon.
+
+ MakeChanges - A boolean value indicating whether the changes should
+ actually be made, or just evaluated. A value of TRUE indicates
+ the changes should be made.
+
+ ResetToDefault - Indicates that the groups are to be reset to their
+ default enabled/disabled state.
+
+ GroupCount - This parameter is required only if the NewState parameter
+ is used. In that case, this parameter indicates how many entries are
+ in the NewState parameter.
+
+ NewState - This parameter points to a SID_AND_ATTRIBUTES array
+ containing the groups whose states are to be adjusted
+ (disabled or enabled). Only the Enabled flag of the
+ attributes associated with each group is used. It provides
+ the new value that is to be assigned to the group in the
+ token. If the ResetToDefault argument is specified as TRUE,
+ then this argument is ignored. Otherwise, it must be passed.
+
+ PreviousState - This (optional) parameter points to a buffer to
+ receive the state of any groups actually changed by this
+ request. This information is formated as a TOKEN_GROUPS data
+ structure which may be passed as the NewState parameter in a
+ subsequent call to NtAdjustGroups to restore the original state
+ of those groups. It is the caller's responsibility to make
+ sure this buffer is large enough to receive all the state
+ information.
+
+ SidBuffer - Pointer to buffer to receive the SID values corresponding
+ to the groups returned in the PreviousState argument.
+
+ ReturnLength - Points to a buffer to receive the number of bytes needed
+ to retrieve the previous state information of changed privileges.
+ This parameter is ignored if the PreviousState argument is not
+ passed.
+
+ ChangeCount - Points to a ULONG to receive the number of groups
+ which were adjusted (or would be adjusted, if changes are made).
+
+ ChangesMade - Points to a boolean flag which is to receive an indication
+ as to whether any changes were made as a result of this call. This
+ is expected to be used to decide whether or not to increment the
+ token's ModifiedId field.
+
+Return Value:
+
+ STATUS_SUCCESS - Call completed sccessfully.
+
+ STATUS_NOT_ALL_ASSIGNED - Indicates not all the specified adjustments
+ have been made (or could be made, if update wasn't requested).
+
+ STATUS_CANT_DISABLE_MANDATORY - Not all adjustments were made (or could
+ be made, if update not requested) because an attempt was made to
+ disable a mandatory group. The state of the groups is left
+ in an underterministic state if update was requested.
+
+
+--*/
+{
+
+ NTSTATUS CompletionStatus = STATUS_SUCCESS;
+
+ ULONG OldIndex;
+ ULONG NewIndex;
+ ULONG SidLength;
+ ULONG LocalReturnLength = 0;
+ PSID NextSid;
+ BOOLEAN Found;
+ ULONG MatchCount = 0;
+ BOOLEAN EnableGroup;
+ BOOLEAN DisableGroup;
+ ULONG TokenGroupAttributes;
+
+ SID_AND_ATTRIBUTES CurrentGroup;
+
+ PAGED_CODE();
+
+ //
+ // NextSid is used to copy group SID values if asked for previous state.
+ //
+
+ NextSid = SidBuffer;
+
+
+ //
+ // Walk through the groups array to determine which need to be
+ // adjusted.
+ //
+
+ OldIndex = 1; // Don't evaluate the 0th entry (user ID)
+ (*ChangeCount) = 0;
+
+ while (OldIndex < Token->UserAndGroupCount) {
+
+ CurrentGroup = Token->UserAndGroups[OldIndex];
+
+ if (ResetToDefault) {
+
+ TokenGroupAttributes = SepTokenGroupAttributes(Token,OldIndex);
+
+ //
+ // If the group is enabled by default and currently disabled,
+ // then we must enable it.
+ //
+
+ EnableGroup = (BOOLEAN)( (TokenGroupAttributes & SE_GROUP_ENABLED_BY_DEFAULT)
+ && !(TokenGroupAttributes & SE_GROUP_ENABLED));
+
+ //
+ // If the group is disabled by default and currently enabled,
+ // then we must disable it.
+ //
+
+ DisableGroup = (BOOLEAN)( !(TokenGroupAttributes & SE_GROUP_ENABLED_BY_DEFAULT)
+ && (TokenGroupAttributes & SE_GROUP_ENABLED));
+
+ //
+ // Blow up if it's a mandatory group that is not both
+ // enabled by default and enabled (SepCreateToken should
+ // make sure that this never happens).
+ //
+
+ ASSERT(!(TokenGroupAttributes & SE_GROUP_MANDATORY)
+ || (TokenGroupAttributes & (SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED)
+ == (SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED)));
+
+ if ( EnableGroup || DisableGroup ) {
+
+ SidLength = SeLengthSid( CurrentGroup.Sid );
+ SidLength = (ULONG)LongAlign(SidLength);
+ LocalReturnLength += SidLength;
+
+ //
+ // Change, if necessary (saving previous state if
+ // appropriate).
+ //
+
+ if (MakeChanges) {
+
+ if (ARGUMENT_PRESENT(PreviousState)) {
+
+ (*(PreviousState)).Groups[(*ChangeCount)].Attributes =
+ CurrentGroup.Attributes;
+
+ (*(PreviousState)).Groups[(*ChangeCount)].Sid =
+ NextSid;
+
+ RtlCopySid( SidLength, NextSid, CurrentGroup.Sid );
+ NextSid = (PSID)((ULONG)NextSid + SidLength);
+ }
+
+ if (EnableGroup) {
+ SepTokenGroupAttributes(Token,OldIndex) |= SE_GROUP_ENABLED;
+ } else {
+ SepTokenGroupAttributes(Token,OldIndex) &= ~SE_GROUP_ENABLED;
+ }
+
+
+ } //endif make changes
+
+ //
+ // increment the number of changes
+ //
+
+ (*ChangeCount) += 1;
+
+ } // endif group enabled
+
+ } else {
+
+ //
+ // Selective adjustments - this is a little trickier
+ // Compare the current group to each of those in
+ // the NewState array. If a match is found, then adjust
+ // the current group appropriately.
+ //
+
+ NewIndex = 0;
+ Found = FALSE;
+
+ while ( (NewIndex < GroupCount) && !Found) {
+
+ //
+ // Look for a comparison
+ //
+
+ if (RtlEqualSid(
+ CurrentGroup.Sid,
+ NewState[NewIndex].Sid
+ ) ) {
+
+ Found = TRUE;
+ MatchCount += 1;
+
+
+ //
+ // See if it needs to be changed
+ //
+
+ if ( (SepArrayGroupAttributes( NewState, NewIndex ) &
+ SE_GROUP_ENABLED ) !=
+ (SepTokenGroupAttributes(Token,OldIndex) &
+ SE_GROUP_ENABLED ) ) {
+
+ //
+ // Make sure group is not mandatory
+ //
+
+ if (SepTokenGroupAttributes(Token,OldIndex) &
+ SE_GROUP_MANDATORY ) {
+ return STATUS_CANT_DISABLE_MANDATORY;
+ }
+
+ SidLength = SeLengthSid( CurrentGroup.Sid );
+ SidLength = (ULONG)LongAlign(SidLength);
+ LocalReturnLength += SidLength;
+
+ //
+ // Change, if necessary (saving previous state if
+ // appropriate).
+ //
+
+ if (MakeChanges) {
+
+ if (ARGUMENT_PRESENT(PreviousState)) {
+
+ PreviousState->Groups[(*ChangeCount)].Attributes =
+ CurrentGroup.Attributes;
+
+ PreviousState->Groups[(*ChangeCount)].Sid =
+ NextSid;
+
+ RtlCopySid( SidLength, NextSid, CurrentGroup.Sid );
+
+ NextSid = (PSID)((ULONG)NextSid + SidLength);
+ }
+
+ SepTokenGroupAttributes(Token,OldIndex) &=
+ ~(SepTokenGroupAttributes(Token,OldIndex)
+ & SE_GROUP_ENABLED);
+ SepTokenGroupAttributes(Token,OldIndex) |=
+ (SepArrayGroupAttributes(NewState,NewIndex)
+ & SE_GROUP_ENABLED);
+
+
+ } //endif make changes
+
+ //
+ // increment the number of changes
+ //
+
+ (*ChangeCount) += 1;
+
+
+ } // endif change needed
+
+ } // endif found
+
+ NewIndex += 1;
+
+ } // endwhile searching NewState
+
+ } // endelse
+
+ OldIndex += 1;
+
+ } // endwhile more groups in token
+
+ //
+ // Set completion status appropriately if some not assigned
+ //
+
+ if (!ResetToDefault) {
+
+ if (MatchCount < GroupCount) {
+ CompletionStatus = STATUS_NOT_ALL_ASSIGNED;
+ }
+ }
+
+ //
+ // Indicate whether changes were made
+ //
+
+ if ((*ChangeCount) > 0 && MakeChanges) {
+ (*ChangesMade) = TRUE;
+ } else {
+ (*ChangesMade) = FALSE;
+ }
+
+ //
+ // Calculate the space needed to return previous state information
+ // (The SID lengths have already been added up in LocalReturnLength).
+ //
+
+ if (ARGUMENT_PRESENT(PreviousState)) {
+
+ (*ReturnLength) = LocalReturnLength +
+ (ULONG)sizeof(TOKEN_GROUPS) +
+ ((*ChangeCount) * (ULONG)sizeof(SID_AND_ATTRIBUTES)) -
+ (ANYSIZE_ARRAY * (ULONG)sizeof(SID_AND_ATTRIBUTES));
+ }
+
+ return CompletionStatus;
+}
diff --git a/private/ntos/se/tokendup.c b/private/ntos/se/tokendup.c
new file mode 100644
index 000000000..d2ba49ae6
--- /dev/null
+++ b/private/ntos/se/tokendup.c
@@ -0,0 +1,945 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ tokendup.c
+
+Abstract:
+
+ This module implements the token duplication service.
+
+
+Author:
+
+ Jim Kelly (JimK) 5-April-1990
+
+Environment:
+
+ Kernel mode only.
+
+Revision History:
+
+--*/
+
+//#ifndef TOKEN_DEBUG
+//#define TOKEN_DEBUG
+//#endif
+
+#include "sep.h"
+#include "seopaque.h"
+#include "tokenp.h"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE,NtDuplicateToken)
+#pragma alloc_text(PAGE,SepDuplicateToken)
+#pragma alloc_text(PAGE,SepMakeTokenEffectiveOnly)
+#pragma alloc_text(PAGE,SeCopyClientToken)
+#endif
+
+
+NTSTATUS
+NtDuplicateToken(
+ IN HANDLE ExistingTokenHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes,
+ IN BOOLEAN EffectiveOnly,
+ IN TOKEN_TYPE TokenType,
+ OUT PHANDLE NewTokenHandle
+ )
+
+/*++
+
+
+Routine Description:
+
+ Create a new token that is a duplicate of an existing token.
+
+Arguments:
+
+ ExistingTokenHandle - Is a handle to a token already open for
+ TOKEN_DUPLICATE access.
+
+ DesiredAccess - Is an access mask indicating which access types
+ are desired to the newly created token. If specified as zero,
+ the granted access mask of the existing token handle
+ is used as the desired access mask for the new token.
+
+ ObjectAttributes - Points to the standard object attributes data
+ structure. Refer to the NT Object Management
+ Specification for a description of this data structure.
+
+ If the new token type is TokenImpersonation, then this
+ parameter may be used to specify the impersonation level
+ of the new token. If no value is provided, and the source
+ token is an impersonation token, then the impersonation level
+ of the source will become that of the target as well. If the
+ source token is a primary token, then an impersonation level
+ must be explicitly provided.
+
+ If the token being duplicated is an impersonation token, and
+ an impersonation level is explicitly provided for the target,
+ then the value provided must not be greater than that of the
+ source token. For example, an Identification level token can
+ not be duplicated to produce a Delegation level token.
+
+ EffectiveOnly - Is a boolean flag indicating whether the entire
+ source token should be duplicated into the target token or
+ just the effective (currently enabled) part of the token.
+ This provides a means for a caller of a protected subsystem
+ to limit which privileges and optional groups are made
+ available to the protected subsystem. A value of TRUE
+ indicates only the currently enabled parts of the source
+ token are to be duplicated. Otherwise, the entire source
+ token is duplicated.
+
+ TokenType - Indicates which type of object the new object is to
+ be created as (primary or impersonation). If you are duplicating
+ an Impersonation token to produce a Primary token, then
+ the Impersonation token must have an impersonation level of
+ either DELEGATE or IMPERSONATE.
+
+
+ NewTokenHandle - Receives the handle of the newly created token.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the operation was successful.
+
+ STATUS_INVALID_PARAMETER - Indicates one or more of the parameter values
+ was invalid. This value is returned if the target token is not
+ an impersonation token.
+
+ STATUS_BAD_IMPERSONATION_LEVEL - Indicates the impersonation level
+ requested for the duplicate token is not compatible with the
+ level of the source token. The duplicate token may not be assigned
+ a level greater than that of the source token.
+
+--*/
+{
+
+ PTOKEN Token;
+ PTOKEN NewToken;
+ KPROCESSOR_MODE PreviousMode;
+ NTSTATUS Status;
+
+ SECURITY_ADVANCED_QUALITY_OF_SERVICE SecurityQos;
+ BOOLEAN SecurityQosPresent = FALSE;
+ HANDLE LocalHandle;
+
+ OBJECT_HANDLE_INFORMATION HandleInformation;
+ ACCESS_MASK EffectiveDesiredAccess;
+
+ PAGED_CODE();
+
+ PreviousMode = KeGetPreviousMode();
+
+ //
+ // Probe parameters
+ //
+
+ if (PreviousMode != KernelMode) {
+
+ try {
+
+ //
+ // Make sure the TokenType is valid
+ //
+
+ if ( (TokenType < TokenPrimary) && (TokenType > TokenImpersonation) ) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // Make sure we can write the handle
+ //
+
+ ProbeForWriteHandle(NewTokenHandle);
+
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ return GetExceptionCode();
+ } // end_try
+
+ } //end_if
+
+
+
+ Status = SeCaptureSecurityQos(
+ ObjectAttributes,
+ PreviousMode,
+ &SecurityQosPresent,
+ &SecurityQos
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+
+ //
+ // Check the handle's access to the existing token and get
+ // a pointer to that token. Pick up the default desired
+ // access mask from the handle while we're at it.
+ //
+
+ Status = ObReferenceObjectByHandle(
+ ExistingTokenHandle, // Handle
+ TOKEN_DUPLICATE, // DesiredAccess
+ SepTokenObjectType, // ObjectType
+ PreviousMode, // AccessMode
+ (PVOID *)&Token, // Object
+ &HandleInformation // GrantedAccess
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ if (SecurityQosPresent) {
+ SeFreeCapturedSecurityQos( &SecurityQos );
+ }
+ return Status;
+ }
+
+
+#ifdef TOKEN_DEBUG
+////////////////////////////////////////////////////////////////////////////
+//
+// Debug
+ SepAcquireTokenReadLock( Token );
+ DbgPrint("\n");
+ DbgPrint("\n");
+ DbgPrint("Token being duplicated: \n");
+ SepDumpToken( Token );
+ SepReleaseTokenReadLock( Token );
+// Debug
+//
+////////////////////////////////////////////////////////////////////////////
+#endif //TOKEN_DEBUG
+
+
+ //
+ // Check to see if an alternate desired access mask was provided.
+ //
+
+ if (ARGUMENT_PRESENT(DesiredAccess)) {
+
+ EffectiveDesiredAccess = DesiredAccess;
+
+ } else {
+
+ EffectiveDesiredAccess = HandleInformation.GrantedAccess;
+ }
+
+
+ //
+ // If no impersonation level was specified, pick one up from
+ // the source token.
+ //
+
+ if ( !SecurityQosPresent ) {
+
+ SecurityQos.ImpersonationLevel = Token->ImpersonationLevel;
+
+ }
+
+
+
+ if (Token->TokenType == TokenImpersonation) {
+
+ //
+ // Make sure a legitimate transformation is being requested:
+ //
+ // (1) The impersonation level of a target duplicate must not
+ // exceed that of the source token.
+ //
+ //
+
+ ASSERT( SecurityDelegation > SecurityImpersonation );
+ ASSERT( SecurityImpersonation > SecurityIdentification );
+ ASSERT( SecurityIdentification > SecurityAnonymous );
+
+ if ( (SecurityQos.ImpersonationLevel > Token->ImpersonationLevel) ) {
+
+ ObDereferenceObject( (PVOID)Token );
+ if (SecurityQosPresent) {
+ SeFreeCapturedSecurityQos( &SecurityQos );
+ }
+ return STATUS_BAD_IMPERSONATION_LEVEL;
+ }
+
+ }
+
+ //
+ // If we are producing a Primary token from an impersonation
+ // token, then specify an impersonation level of at least
+ // Impersonate.
+ //
+
+ if ( (Token->TokenType == TokenImpersonation) &&
+ (TokenType == TokenPrimary) &&
+ (Token->ImpersonationLevel < SecurityImpersonation)
+ ) {
+ ObDereferenceObject( (PVOID)Token );
+ if (SecurityQosPresent) {
+ SeFreeCapturedSecurityQos( &SecurityQos );
+ }
+ return STATUS_BAD_IMPERSONATION_LEVEL;
+ }
+
+ //
+ // Duplicate the existing token
+ //
+
+ NewToken = NULL;
+ Status = SepDuplicateToken(
+ Token,
+ ObjectAttributes,
+ EffectiveOnly,
+ TokenType,
+ SecurityQos.ImpersonationLevel,
+ PreviousMode,
+ &NewToken
+ );
+
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // Insert the new token
+ //
+
+ Status = ObInsertObject( NewToken,
+ NULL,
+ EffectiveDesiredAccess,
+ 0,
+ (PVOID *)NULL,
+ &LocalHandle
+ );
+
+ if (!NT_SUCCESS( Status )) {
+ DbgPrint( "SE: ObInsertObject failed (%x) for token at %x\n", Status, NewToken );
+ }
+
+ } else
+ if (NewToken != NULL) {
+ DbgPrint( "SE: SepDuplicateToken failed (%x) but allocated token at %x\n", Status, NewToken );
+ }
+
+ //
+ // We no longer need our reference to the source token
+ //
+
+ ObDereferenceObject( (PVOID)Token );
+
+ if (SecurityQosPresent) {
+ SeFreeCapturedSecurityQos( &SecurityQos );
+ }
+
+ // BUGWARNING Probably need to audit here
+
+ //
+ // Return the new handle
+ //
+
+ if (NT_SUCCESS(Status)) {
+ try { *NewTokenHandle = LocalHandle; }
+ except(EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); }
+ }
+
+ return Status;
+}
+
+NTSTATUS
+SepDuplicateToken(
+ IN PTOKEN ExistingToken,
+ IN POBJECT_ATTRIBUTES ObjectAttributes,
+ IN BOOLEAN EffectiveOnly,
+ IN TOKEN_TYPE TokenType,
+ IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel OPTIONAL,
+ IN KPROCESSOR_MODE RequestorMode,
+ OUT PTOKEN *DuplicateToken
+ )
+
+
+/*++
+
+
+Routine Description:
+
+ This routine does the bulk of the work to actually duplicate
+ a token. This routine assumes all access validation and argument
+ probing (except the ObjectAttributes) has been performed.
+
+ THE CALLER IS RESPONSIBLE FOR CHECKING SUBJECT RIGHTS TO CREATE THE
+ TYPE OF TOKEN BEING CREATED.
+
+ This routine acquires a read lock on the token being duplicated.
+
+Arguments:
+
+ ExistingToken - Points to the token to be duplicated.
+
+ ObjectAttributes - Points to the standard object attributes data
+ structure. Refer to the NT Object Management
+ Specification for a description of this data structure.
+
+ The security Quality Of Service of the object attributes are ignored.
+ This information must be specified using parameters to this
+ routine.
+
+ EffectiveOnly - Is a boolean flag indicating whether the entire
+ source token should be duplicated into the target token or
+ just the effective (currently enabled) part of the token.
+ This provides a means for a caller of a protected subsystem
+ to limit which privileges and optional groups are made
+ available to the protected subsystem. A value of TRUE
+ indicates only the currently enabled parts of the source
+ token are to be duplicated. Otherwise, the entire source
+ token is duplicated.
+
+ TokenType - Indicates the type of token to make the duplicate token.
+
+ ImpersonationLevel - This value specifies the impersonation level
+ to assign to the duplicate token. If the TokenType of the
+ duplicate is not TokenImpersonation then this parameter is
+ ignored. Otherwise, it is must be provided.
+
+ RequestorMode - Mode of client requesting the token be duplicated.
+
+ DuplicateToken - Receives a pointer to the duplicate token.
+ The token has not yet been inserted into any object table.
+ No exceptions are expected when tring to set this OUT value.
+
+Return Value:
+
+ STATUS_SUCCESS - The service successfully completed the requested
+ operation.
+
+
+--*/
+{
+ NTSTATUS Status;
+
+ PTOKEN NewToken;
+ PULONG DynamicPart;
+ ULONG PagedPoolSize;
+ ULONG NonPagedPoolSize;
+ ULONG TokenBodyLength;
+ ULONG FieldOffset;
+
+ ULONG Index;
+
+ PSECURITY_TOKEN_PROXY_DATA NewProxyData;
+ PSECURITY_TOKEN_AUDIT_DATA NewAuditData;
+
+
+ PAGED_CODE();
+
+ ASSERT( sizeof(SECURITY_IMPERSONATION_LEVEL) <= sizeof(ULONG) );
+
+
+ if ( TokenType == TokenImpersonation ) {
+
+ ASSERT( SecurityDelegation > SecurityImpersonation );
+ ASSERT( SecurityImpersonation > SecurityIdentification );
+ ASSERT( SecurityIdentification > SecurityAnonymous );
+
+ if ( (ImpersonationLevel > SecurityDelegation) ||
+ (ImpersonationLevel < SecurityAnonymous) ) {
+
+ return STATUS_BAD_IMPERSONATION_LEVEL;
+ }
+ }
+
+
+ //
+ // Increment the reference count for this logon session
+ // This can not fail, since there is already a token in this logon
+ // session.
+ //
+
+ Status = SepReferenceLogonSession( &(ExistingToken->AuthenticationId) );
+ ASSERT( NT_SUCCESS(Status) );
+
+
+
+ //
+ // Note that the size of the dynamic portion of a token can not change
+ // once established.
+ //
+
+ //
+ // Allocate the dynamic portion
+ //
+
+ DynamicPart = (PULONG)ExAllocatePoolWithTag(
+ PagedPool,
+ ExistingToken->DynamicCharged,
+ 'dTeS'
+ );
+
+ if (DynamicPart == NULL) {
+ SepDeReferenceLogonSession( &(ExistingToken->AuthenticationId) );
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ if (ARGUMENT_PRESENT(ExistingToken->ProxyData)) {
+
+ Status = SepCopyProxyData(
+ &NewProxyData,
+ ExistingToken->ProxyData
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ SepDeReferenceLogonSession( &(ExistingToken->AuthenticationId) );
+ ExFreePool( DynamicPart );
+ return( Status );
+ }
+
+ } else {
+
+ NewProxyData = NULL;
+ }
+
+ if (ARGUMENT_PRESENT( ExistingToken->AuditData )) {
+
+ NewAuditData = ExAllocatePool( PagedPool, sizeof( SECURITY_TOKEN_AUDIT_DATA ));
+
+ if (NewAuditData == NULL) {
+
+ SepFreeProxyData( NewProxyData );
+ SepDeReferenceLogonSession( &(ExistingToken->AuthenticationId) );
+ ExFreePool( DynamicPart );
+
+ return( STATUS_INSUFFICIENT_RESOURCES );
+
+ } else {
+
+ *NewAuditData = *(ExistingToken->AuditData);
+ }
+
+ } else {
+
+ NewAuditData = NULL;
+
+ }
+
+ //
+ // Create a new object
+ //
+
+ TokenBodyLength = (ULONG)sizeof(TOKEN) +
+ ExistingToken->VariableLength;
+
+ NonPagedPoolSize = TokenBodyLength;
+ PagedPoolSize = ExistingToken->DynamicCharged;
+
+ Status = ObCreateObject(
+ RequestorMode, // ProbeMode
+ SepTokenObjectType, // ObjectType
+ ObjectAttributes, // ObjectAttributes
+ RequestorMode, // OwnershipMode
+ NULL, // ParseContext
+ TokenBodyLength, // ObjectBodySize
+ PagedPoolSize, // PagedPoolCharge
+ NonPagedPoolSize, // NonPagedPoolCharge
+ (PVOID *)&NewToken // Return pointer to object
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ SepDeReferenceLogonSession( &(ExistingToken->AuthenticationId) );
+ ExFreePool( DynamicPart );
+ SepFreeProxyData( NewProxyData );
+
+ if (NewAuditData != NULL) {
+ ExFreePool( NewAuditData );
+ }
+
+ return Status;
+ }
+
+
+ //
+ // acquire exclusive access to the source token
+ //
+
+ SepAcquireTokenReadLock( ExistingToken );
+
+
+ //
+ // Main Body initialization
+ //
+
+ //
+ // The following fields are unchanged from the source token.
+ // Although some may change if EffectiveOnly has been specified.
+ //
+
+ NewToken->AuthenticationId = ExistingToken->AuthenticationId;
+ NewToken->ModifiedId = ExistingToken->ModifiedId;
+ NewToken->ExpirationTime = ExistingToken->ExpirationTime;
+ NewToken->TokenSource = ExistingToken->TokenSource;
+ NewToken->DynamicCharged = ExistingToken->DynamicCharged;
+ NewToken->DynamicAvailable = ExistingToken->DynamicAvailable;
+ NewToken->DefaultOwnerIndex = ExistingToken->DefaultOwnerIndex;
+ NewToken->UserAndGroupCount = ExistingToken->UserAndGroupCount;
+ NewToken->PrivilegeCount = ExistingToken->PrivilegeCount;
+ NewToken->VariableLength = ExistingToken->VariableLength;
+ NewToken->TokenFlags = ExistingToken->TokenFlags;
+ NewToken->ProxyData = NewProxyData;
+ NewToken->AuditData = NewAuditData;
+
+
+ //
+ // The following fields differ in the new token.
+ //
+
+ ExAllocateLocallyUniqueId( &(NewToken->TokenId) );
+ NewToken->TokenInUse = FALSE;
+ NewToken->TokenType = TokenType;
+ NewToken->ImpersonationLevel = ImpersonationLevel;
+
+
+ //
+ // Copy and initialize the variable part.
+ // The variable part is assumed to be position independent.
+ //
+
+ RtlMoveMemory( (PVOID)&(NewToken->VariablePart),
+ (PVOID)&(ExistingToken->VariablePart),
+ ExistingToken->VariableLength
+ );
+
+ //
+ // Set the address of the UserAndGroups array.
+ //
+
+ ASSERT( ARGUMENT_PRESENT(ExistingToken->UserAndGroups ) );
+ ASSERT( (ULONG)(ExistingToken->UserAndGroups) >=
+ (ULONG)(&(ExistingToken->VariablePart)) );
+
+ FieldOffset = (ULONG)(ExistingToken->UserAndGroups) -
+ (ULONG)(&(ExistingToken->VariablePart));
+
+ NewToken->UserAndGroups =
+ (PSID_AND_ATTRIBUTES)(FieldOffset + (ULONG)(&(NewToken->VariablePart)) );
+
+ //
+ // Now go through and change the address of each SID pointer
+ // for the user and groups
+ //
+
+ Index = 0;
+
+ while (Index < ExistingToken->UserAndGroupCount) {
+
+ FieldOffset = (ULONG)(ExistingToken->UserAndGroups[Index].Sid) -
+ (ULONG)(&(ExistingToken->VariablePart));
+
+ NewToken->UserAndGroups[Index].Sid =
+ (PSID)( FieldOffset + (ULONG)(&(NewToken->VariablePart)) );
+
+ Index += 1;
+
+ }
+
+
+ //
+ // If present, set the address of the privileges
+ //
+
+ if (ExistingToken->PrivilegeCount > 0) {
+ ASSERT( ARGUMENT_PRESENT(ExistingToken->Privileges ) );
+ ASSERT( (ULONG)(ExistingToken->Privileges) >=
+ (ULONG)(&(ExistingToken->VariablePart)) );
+
+ FieldOffset = (ULONG)(ExistingToken->Privileges) -
+ (ULONG)(&(ExistingToken->VariablePart));
+ NewToken->Privileges = (PLUID_AND_ATTRIBUTES)(
+ FieldOffset +
+ (ULONG)(&(NewToken->VariablePart))
+ );
+ } else {
+
+ NewToken->Privileges = NULL;
+
+ }
+
+
+
+ //
+ // Copy and initialize the dynamic part.
+ // The dynamic part is assumed to be position independent.
+ //
+
+ RtlMoveMemory( (PVOID)DynamicPart,
+ (PVOID)(ExistingToken->DynamicPart),
+ ExistingToken->DynamicCharged
+ );
+
+ NewToken->DynamicPart = DynamicPart;
+
+ //
+ // If present, set the address of the default Dacl
+ //
+
+ if (ARGUMENT_PRESENT(ExistingToken->DefaultDacl)) {
+
+ ASSERT( (ULONG)(ExistingToken->DefaultDacl) >=
+ (ULONG)(ExistingToken->DynamicPart) );
+
+ FieldOffset = (ULONG)(ExistingToken->DefaultDacl) -
+ (ULONG)(ExistingToken->DynamicPart);
+
+ NewToken->DefaultDacl = (PACL)(FieldOffset + (ULONG)DynamicPart);
+
+ } else {
+
+ NewToken->DefaultDacl = NULL;
+ }
+
+
+ //
+ // Set the address of the primary group
+ //
+
+ ASSERT(ARGUMENT_PRESENT(ExistingToken->PrimaryGroup));
+
+ ASSERT( (ULONG)(ExistingToken->PrimaryGroup) >=
+ (ULONG)(ExistingToken->DynamicPart) );
+
+ FieldOffset = (ULONG)(ExistingToken->PrimaryGroup) -
+ (ULONG)(ExistingToken->DynamicPart);
+
+ NewToken->PrimaryGroup = (PACL)(FieldOffset + (ULONG)(DynamicPart));
+
+
+ //
+ // For the time being, take the easy way to generating an "EffectiveOnly"
+ // duplicate. That is, use the same space required of the original, just
+ // eliminate any IDs or privileges not active.
+ //
+ // Ultimately, if duplication becomes a common operation, then it will be
+ // worthwhile to recalculate the actual space needed and copy only the
+ // effective IDs/privileges into the new token.
+ //
+
+ if (EffectiveOnly) {
+ SepMakeTokenEffectiveOnly( NewToken );
+ }
+
+
+#ifdef TOKEN_DEBUG
+////////////////////////////////////////////////////////////////////////////
+//
+// Debug
+ DbgPrint("\n");
+ DbgPrint("\n");
+ DbgPrint("\n");
+ DbgPrint("Duplicate token:\n");
+ SepDumpToken( NewToken );
+// Debug
+//
+////////////////////////////////////////////////////////////////////////////
+#endif //TOKEN_DEBUG
+
+ //
+ // Release the source token.
+ //
+
+ SepReleaseTokenReadLock( ExistingToken );
+
+
+ (*DuplicateToken) = NewToken;
+ return Status;
+}
+
+
+VOID
+SepMakeTokenEffectiveOnly(
+ IN PTOKEN Token
+ )
+
+
+/*++
+
+
+Routine Description:
+
+ This routine eliminates all but the effective groups and privileges from
+ a token. It does this by moving elements of the SID and privileges arrays
+ to overwrite lapsed IDs/privileges, and then reducing the array element
+ counts. This results in wasted memory within the token object.
+
+ One side effect of this routine is that a token that initially had a
+ default owner ID corresponding to a lapsed group will be changed so
+ that the default owner ID is the user ID.
+
+ THIS ROUTINE MUST BE CALLED ONLY AS PART OF TOKEN CREATION (FOR TOKENS
+ WHICH HAVE NOT YET BEEN INSERTED INTO AN OBJECT TABLE.) THIS ROUTINE
+ MODIFIES READ ONLY TOKEN FIELDS.
+
+ Note that since we are operating on a token that is not yet visible
+ to the user, we do not bother acquiring a read lock on the token
+ being modified.
+
+Arguments:
+
+ Token - Points to the token to be made effective only.
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ ULONG Index;
+ ULONG ElementCount;
+
+ PAGED_CODE();
+
+ //
+ // Walk the privilege array, discarding any lapsed privileges
+ //
+
+ ElementCount = Token->PrivilegeCount;
+ Index = 0;
+
+ while (Index < ElementCount) {
+
+ //
+ // If this privilege is not enabled, replace it with the one at
+ // the end of the array and reduce the size of the array by one.
+ // Otherwise, move on to the next entry in the array.
+ //
+
+ if ( !(SepTokenPrivilegeAttributes(Token,Index) & SE_PRIVILEGE_ENABLED)
+ ) {
+
+ (Token->Privileges)[Index] =
+ (Token->Privileges)[ElementCount - 1];
+ ElementCount -= 1;
+
+ } else {
+
+ Index += 1;
+
+ }
+
+ } // endwhile
+
+ Token->PrivilegeCount = ElementCount;
+
+ //
+ // Walk the UserAndGroups array (except for the first entry, which is
+ // the user - and can't be disabled) discarding any lapsed groups.
+ //
+
+ ElementCount = Token->UserAndGroupCount;
+ ASSERT( ElementCount >= 1 ); // Must be at least a user ID
+ Index = 1; // Start at the first group, not the user ID.
+
+ while (Index < ElementCount) {
+
+ //
+ // If this group is not enabled, replace it with the one at
+ // the end of the array and reduce the size of the array by one.
+ //
+ if ( !(SepTokenGroupAttributes(Token, Index) & SE_GROUP_ENABLED) ){
+
+ (Token->UserAndGroups)[Index] =
+ (Token->UserAndGroups)[ElementCount - 1];
+ ElementCount -= 1;
+
+ } else {
+
+ Index += 1;
+
+ }
+
+ } // endwhile
+
+ Token->UserAndGroupCount = ElementCount;
+
+ return;
+}
+
+NTSTATUS
+SeCopyClientToken(
+ IN PACCESS_TOKEN ClientToken,
+ IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
+ IN KPROCESSOR_MODE RequestorMode,
+ OUT PACCESS_TOKEN *DuplicateToken
+ )
+
+/*++
+
+
+Routine Description:
+
+ This routine copies a client's token as part of establishing a client
+ context for impersonation.
+
+ The result will be an impersonation token.
+
+ No handles to the new token are established.
+
+ The token will be an exact duplicate of the source token. It is the
+ caller's responsibility to ensure an effective only copy of the token
+ is produced when the token is opened, if necessary.
+
+
+Arguments:
+
+ ClientToken - Points to the token to be duplicated. This may be either
+ a primary or impersonation token.
+
+ ImpersonationLevel - The impersonation level to be assigned to the new
+ token.
+
+ RequestorMode - Mode to be assigned as the owner mode of the new token.
+
+ DuplicateToken - Receives a pointer to the duplicate token.
+ The token has not yet been inserted into any object table.
+ No exceptions are expected when tring to set this OUT value.
+
+Return Value:
+
+ STATUS_SUCCESS - The service successfully completed the requested
+ operation.
+
+
+--*/
+{
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ PTOKEN NewToken;
+
+ PAGED_CODE();
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ NULL,
+ 0,
+ NULL,
+ NULL
+ );
+
+ Status = SepDuplicateToken(
+ (PTOKEN)ClientToken, // ExistingToken
+ &ObjectAttributes, // ObjectAttributes
+ FALSE, // EffectiveOnly
+ TokenImpersonation, // TokenType (target)
+ ImpersonationLevel, // ImpersonationLevel
+ RequestorMode, // RequestorMode
+ &NewToken // DuplicateToken
+ );
+
+ (*DuplicateToken) = (PACCESS_TOKEN)NewToken;
+
+ return Status;
+
+}
diff --git a/private/ntos/se/tokenopn.c b/private/ntos/se/tokenopn.c
new file mode 100644
index 000000000..a60f15a03
--- /dev/null
+++ b/private/ntos/se/tokenopn.c
@@ -0,0 +1,594 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ tokenopn.c
+
+Abstract:
+
+ This module implements the open thread and process token services.
+
+Author:
+
+ Jim Kelly (JimK) 2-Aug-1990
+
+Environment:
+
+ Kernel mode only.
+
+Revision History:
+
+--*/
+
+//#ifndef TOKEN_DEBUG
+//#define TOKEN_DEBUG
+//#endif
+
+#include "sep.h"
+#include "seopaque.h"
+#include "tokenp.h"
+
+NTSTATUS
+SepCreateImpersonationTokenDacl(
+ IN PTOKEN Token,
+ IN PACCESS_TOKEN PrimaryToken,
+ OUT PACL *Acl
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE,NtOpenProcessToken)
+#pragma alloc_text(PAGE,NtOpenThreadToken)
+#pragma alloc_text(PAGE,SepCreateImpersonationTokenDacl)
+#endif
+
+
+
+NTSTATUS
+SepCreateImpersonationTokenDacl(
+ IN PTOKEN Token,
+ IN PACCESS_TOKEN PrimaryToken,
+ OUT PACL *Acl
+ )
+/*++
+
+Routine Description:
+
+ This routine modifies the DACL protecting the passed token to allow
+ the current user (described by the PrimaryToken parameter) full access.
+ This permits callers of NtOpenThreadToken to call with OpenAsSelf==TRUE
+ and succeed.
+
+ The new DACL placed on the token is as follows:
+
+ ACE 0 - Server gets TOKEN_ALL_ACCESS
+
+ ACE 1 - Client gets TOKEN_ALL_ACCESS
+
+ ACE 2 - Admins gets TOKEN_ALL_ACCESS
+
+ ACE 3 - System gets TOKEN_ALL_ACCESS
+
+Arguments:
+
+ Token - The token whose protection is to be modified.
+
+ PrimaryToken - Token representing the subject to be granted access.
+
+ Acl - Returns the modified ACL, allocated out of PagedPool.
+
+
+Return Value:
+
+
+--*/
+
+{
+ PSID ServerUserSid;
+ PSID ClientUserSid;
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG AclLength;
+ PACL NewDacl;
+ PSECURITY_DESCRIPTOR OldDescriptor;
+ BOOLEAN MemoryAllocated;
+ PACL OldDacl;
+ BOOLEAN DaclPresent;
+ BOOLEAN DaclDefaulted;
+
+ PAGED_CODE();
+
+ ServerUserSid = ((PTOKEN)PrimaryToken)->UserAndGroups[0].Sid;
+
+ ClientUserSid = Token->UserAndGroups[0].Sid;
+
+ //
+ // Compute how much space we'll need for the new DACL.
+ //
+
+ AclLength = 4 * sizeof( ACCESS_ALLOWED_ACE ) - 4 * sizeof( ULONG ) +
+ SeLengthSid( ServerUserSid ) + SeLengthSid( SeLocalSystemSid ) +
+ SeLengthSid( ClientUserSid ) + SeLengthSid( SeAliasAdminsSid ) +
+ sizeof( ACL );
+
+ NewDacl = ExAllocatePool( PagedPool, AclLength );
+
+ if (NewDacl == NULL) {
+
+ *Acl = NULL;
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = RtlCreateAcl( NewDacl, AclLength, ACL_REVISION2 );
+ ASSERT(NT_SUCCESS( Status ));
+
+ Status = RtlAddAccessAllowedAce (
+ NewDacl,
+ ACL_REVISION2,
+ TOKEN_ALL_ACCESS,
+ ServerUserSid
+ );
+ ASSERT( NT_SUCCESS( Status ));
+
+ Status = RtlAddAccessAllowedAce (
+ NewDacl,
+ ACL_REVISION2,
+ TOKEN_ALL_ACCESS,
+ ClientUserSid
+ );
+ ASSERT( NT_SUCCESS( Status ));
+
+ Status = RtlAddAccessAllowedAce (
+ NewDacl,
+ ACL_REVISION2,
+ TOKEN_ALL_ACCESS,
+ SeAliasAdminsSid
+ );
+ ASSERT( NT_SUCCESS( Status ));
+
+ Status = RtlAddAccessAllowedAce (
+ NewDacl,
+ ACL_REVISION2,
+ TOKEN_ALL_ACCESS,
+ SeLocalSystemSid
+ );
+ ASSERT( NT_SUCCESS( Status ));
+
+ *Acl = NewDacl;
+ return STATUS_SUCCESS;
+}
+
+
+
+NTSTATUS
+NtOpenProcessToken(
+ IN HANDLE ProcessHandle,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PHANDLE TokenHandle
+ )
+
+/*++
+
+Routine Description:
+
+ Open a token object associated with a process and return a handle
+ that may be used to access that token.
+
+Arguments:
+
+ ProcessHandle - Specifies the process whose token is to be
+ opened.
+
+ DesiredAccess - Is an access mask indicating which access types
+ are desired to the token. These access types are reconciled
+ with the Discretionary Access Control list of the token to
+ determine whether the accesses will be granted or denied.
+
+ TokenHandle - Receives the handle of the newly opened token.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the operation was successful.
+
+--*/
+{
+
+ PVOID Token;
+ KPROCESSOR_MODE PreviousMode;
+ NTSTATUS Status;
+
+ HANDLE LocalHandle;
+
+ PAGED_CODE();
+
+ PreviousMode = KeGetPreviousMode();
+
+ //
+ // Probe parameters
+ //
+
+ if (PreviousMode != KernelMode) {
+
+ try {
+
+ ProbeForWriteHandle(TokenHandle);
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ return GetExceptionCode();
+ } // end_try
+
+ } //end_if
+
+
+ //
+ // Valdiate access to the process and obtain a pointer to the
+ // process's token. If successful, this will cause the token's
+ // reference count to be incremented.
+ //
+
+ Status = PsOpenTokenOfProcess( ProcessHandle, ((PACCESS_TOKEN *)&Token));
+
+ if (!NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+ //
+ // Now try to open the token for the specified desired access
+ //
+
+ Status = ObOpenObjectByPointer(
+ (PVOID)Token, // Object
+ 0, // HandleAttributes
+ NULL, // AccessState
+ DesiredAccess, // DesiredAccess
+ SepTokenObjectType, // ObjectType
+ PreviousMode, // AccessMode
+ &LocalHandle // Handle
+ );
+
+ //
+ // And decrement the reference count of the token to counter
+ // the action performed by PsOpenTokenOfProcess(). If the open
+ // was successful, the handle will have caused the token's
+ // reference count to have been incremented.
+ //
+
+ ObDereferenceObject( Token );
+
+ //
+ // Return the new handle
+ //
+
+ if (NT_SUCCESS(Status)) {
+
+ try {
+
+ *TokenHandle = LocalHandle;
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ return GetExceptionCode();
+
+ }
+ }
+
+ return Status;
+
+}
+
+
+NTSTATUS
+NtOpenThreadToken(
+ IN HANDLE ThreadHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN BOOLEAN OpenAsSelf,
+ OUT PHANDLE TokenHandle
+ )
+
+/*++
+
+
+Routine Description:
+
+Open a token object associated with a thread and return a handle that
+may be used to access that token.
+
+Arguments:
+
+ ThreadHandle - Specifies the thread whose token is to be opened.
+
+ DesiredAccess - Is an access mask indicating which access types
+ are desired to the token. These access types are reconciled
+ with the Discretionary Access Control list of the token to
+ determine whether the accesses will be granted or denied.
+
+ OpenAsSelf - Is a boolean value indicating whether the access should
+ be made using the calling thread's current security context, which
+ may be that of a client if impersonating, or using the caller's
+ process-level security context. A value of FALSE indicates the
+ caller's current context should be used un-modified. A value of
+ TRUE indicates the request should be fulfilled using the process
+ level security context.
+
+ This parameter is necessary to allow a server process to open
+ a client's token when the client specified IDENTIFICATION level
+ impersonation. In this case, the caller would not be able to
+ open the client's token using the client's context (because you
+ can't create executive level objects using IDENTIFICATION level
+ impersonation).
+
+ TokenHandle - Receives the handle of the newly opened token.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the operation was successful.
+
+ STATUS_NO_TOKEN - Indicates an attempt has been made to open a
+ token associated with a thread that is not currently
+ impersonating a client.
+
+ STATUS_CANT_OPEN_ANONYMOUS - Indicates the client requested anonymous
+ impersonation level. An anonymous token can not be openned.
+
+--*/
+{
+
+ KPROCESSOR_MODE PreviousMode;
+ NTSTATUS Status;
+
+ PVOID Token;
+ PTOKEN NewToken;
+ BOOLEAN CopyOnOpen;
+ BOOLEAN EffectiveOnly;
+ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
+ SE_IMPERSONATION_STATE DisabledImpersonationState;
+ BOOLEAN RestoreImpersonationState = FALSE;
+
+ HANDLE LocalHandle;
+ SECURITY_DESCRIPTOR SecurityDescriptor;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ PACL NewAcl = NULL;
+ PETHREAD Thread;
+ PACCESS_TOKEN PrimaryToken;
+ SECURITY_SUBJECT_CONTEXT SubjectSecurityContext;
+
+ PAGED_CODE();
+
+ PreviousMode = KeGetPreviousMode();
+
+ //
+ // Probe parameters
+ //
+
+ if (PreviousMode != KernelMode) {
+
+ try {
+
+ ProbeForWriteHandle(TokenHandle);
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ return GetExceptionCode();
+ } // end_try
+
+ } //end_if
+
+ //
+ // Valdiate access to the thread and obtain a pointer to the
+ // thread's token (if there is one). If successful, this will
+ // cause the token's reference count to be incremented.
+ //
+ // This routine disabled impersonation as necessary to properly
+ // honor the OpenAsSelf flag.
+ //
+
+ Status = PsOpenTokenOfThread( ThreadHandle,
+ OpenAsSelf,
+ ((PACCESS_TOKEN *)&Token),
+ &CopyOnOpen,
+ &EffectiveOnly,
+ &ImpersonationLevel
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+
+ //
+ // The token was successfully referenced.
+ //
+
+ //
+ // We need to create and/or open a token object, so disable impersonation
+ // if necessary.
+ //
+
+ if (OpenAsSelf) {
+ RestoreImpersonationState = PsDisableImpersonation(
+ PsGetCurrentThread(),
+ &DisabledImpersonationState
+ );
+ }
+
+ //
+ // If the CopyOnOpen flag is not set, then the token can be
+ // opened directly. Otherwise, the token must be duplicated,
+ // and a handle to the duplicate returned.
+ //
+
+ if (CopyOnOpen) {
+
+ //
+ // Create the new security descriptor for the token.
+ //
+ // We must obtain the correct SID to put into the Dacl. Do this
+ // by finding the process associated with the passed thread
+ // and grabbing the User SID out of that process's token.
+ // If we just use the current SubjectContext, we'll get the
+ // SID of whoever is calling us, which isn't what we want.
+ //
+
+ Status = ObReferenceObjectByHandle(
+ ThreadHandle,
+ THREAD_ALL_ACCESS,
+ PsThreadType,
+ KernelMode,
+ (PVOID)&Thread,
+ NULL
+ );
+
+ ASSERT( NT_SUCCESS( Status ));
+
+ PrimaryToken = PsReferencePrimaryToken(Thread->ThreadsProcess);
+
+ Status = SepCreateImpersonationTokenDacl(
+ (PTOKEN)Token,
+ PrimaryToken,
+ &NewAcl
+ );
+
+ PsDereferencePrimaryToken( PrimaryToken );
+
+ if (NT_SUCCESS( Status )) {
+
+ if (NewAcl != NULL) {
+
+ //
+ // There exist tokens that either do not have security descriptors at all,
+ // or have security descriptors, but do not have DACLs. In either case, do
+ // nothing.
+ //
+
+ Status = RtlCreateSecurityDescriptor ( &SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION );
+ ASSERT( NT_SUCCESS( Status ));
+
+ Status = RtlSetDaclSecurityDescriptor (
+ &SecurityDescriptor,
+ TRUE,
+ NewAcl,
+ FALSE
+ );
+
+ ASSERT( NT_SUCCESS( Status ));
+ }
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ NULL,
+ 0L,
+ NULL,
+ NewAcl == NULL ? NULL : &SecurityDescriptor
+ );
+
+ //
+ // Open a copy of the token
+ //
+
+ Status = SepDuplicateToken(
+ (PTOKEN)Token, // ExistingToken
+ &ObjectAttributes, // ObjectAttributes
+ EffectiveOnly, // EffectiveOnly
+ TokenImpersonation, // TokenType
+ ImpersonationLevel, // ImpersonationLevel
+ KernelMode, // RequestorMode must be kernel mode
+ &NewToken
+ );
+
+ if (NT_SUCCESS( Status )) {
+
+ //
+ // Insert the new token
+ //
+
+ Status = ObInsertObject( NewToken,
+ NULL,
+ DesiredAccess,
+ 0,
+ (PVOID *)NULL,
+ &LocalHandle
+ );
+ }
+ }
+
+ } else {
+
+ //
+ // We do not have to modify the security on the token in the static case,
+ // because in all the places in the system where impersonation takes place
+ // over a secure transport (e.g., LPC), CopyOnOpen is set. The only reason
+ // we'be be here is if the impersonation is taking place because someone did
+ // an NtSetInformationThread and passed in a token.
+ //
+ // In that case, we absolutely do not want to give the caller guaranteed
+ // access, because that would allow anyone who has access to a thread to
+ // impersonate any of that thread's clients for any access.
+ //
+
+ //
+ // Open the existing token
+ //
+
+ Status = ObOpenObjectByPointer(
+ (PVOID)Token, // Object
+ 0, // HandleAttributes
+ NULL, // AccessState
+ DesiredAccess, // DesiredAccess
+ SepTokenObjectType, // ObjectType
+ PreviousMode, // AccessMode
+ &LocalHandle // Handle
+ );
+ }
+
+ if (NewAcl != NULL) {
+ ExFreePool( NewAcl );
+ }
+
+ if (RestoreImpersonationState) {
+ PsRestoreImpersonation(
+ PsGetCurrentThread(),
+ &DisabledImpersonationState
+ );
+ }
+
+ //
+ // And decrement the reference count of the existing token to counter
+ // the action performed by PsOpenTokenOfThread. If the open
+ // was successful, the handle will have caused the token's
+ // reference count to have been incremented.
+ //
+
+ ObDereferenceObject( Token );
+
+ if (NT_SUCCESS( Status ) && CopyOnOpen) {
+
+ //
+ // Assign the newly duplicated token to the thread.
+ //
+
+ PsImpersonateClient( Thread,
+ NewToken,
+ FALSE, // turn off CopyOnOpen flag
+ EffectiveOnly,
+ ImpersonationLevel
+ );
+
+ }
+
+ if (CopyOnOpen) {
+
+ ObDereferenceObject( Thread );
+ }
+
+ //
+ // Return the new handle
+ //
+
+ if (NT_SUCCESS(Status)) {
+ try { *TokenHandle = LocalHandle; }
+ except(EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); }
+ }
+
+ return Status;
+
+}
+
diff --git a/private/ntos/se/tokenp.h b/private/ntos/se/tokenp.h
new file mode 100644
index 000000000..76beed85c
--- /dev/null
+++ b/private/ntos/se/tokenp.h
@@ -0,0 +1,595 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ tokenp.h
+
+Abstract:
+
+ This module contains the internal (private) declarations needed by the
+ TOKEN object routines.
+
+ It also contains global variables needed by the TOKEN object routines.
+
+Author:
+
+ Jim Kelly (JimK) 18-May-1990
+
+Revision History:
+
+ v10: robertre
+ Added SepAccessCheck and SepPrivilegeCheck prototypes
+ v11: robertre
+ Added parameter to SepAccessCheck
+
+--*/
+
+#ifndef _TOKENP_
+#define _TOKENP_
+
+//#define TOKEN_DEBUG
+
+#include "ntos.h"
+#include "sep.h"
+#include "seopaque.h"
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Token Diagnostics //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+
+
+#if DBG
+#define TOKEN_DIAGNOSTICS_ENABLED 1
+#endif // DBG
+
+
+//
+// These definitions are useful diagnostics aids
+//
+
+#if TOKEN_DIAGNOSTICS_ENABLED
+
+//
+// Test for enabled diagnostic
+//
+
+#define IF_TOKEN_GLOBAL( FlagName ) \
+ if (TokenGlobalFlag & (TOKEN_DIAG_##FlagName))
+
+//
+// Diagnostics print statement
+//
+
+#define TokenDiagPrint( FlagName, _Text_ ) \
+ IF_TOKEN_GLOBAL( FlagName ) \
+ DbgPrint _Text_
+
+
+
+#else // !TOKEN_DIAGNOSTICS_ENABLED
+
+//
+// No diagnostics included in build
+//
+
+
+//
+// Test for diagnostics enabled
+//
+
+#define IF_TOKEN_GLOBAL( FlagName ) if (FALSE)
+
+//
+// Diagnostics print statement (expands to no-op)
+//
+
+#define TokenDiagPrint( FlagName, _Text_ ) ;
+
+#endif // TOKEN_DIAGNOSTICS_ENABLED
+
+
+//
+// The following flags enable or disable various diagnostic
+// capabilities within token code. These flags are set in
+// TokenGlobalFlag (only available within a DBG system).
+//
+//
+// TOKEN_LOCKS - Display information about acquisition and freeing
+// of token locks.
+//
+
+#define TOKEN_DIAG_TOKEN_LOCKS ((ULONG) 0x00000001L)
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Token Related Constants //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+//
+// By default, a token is charged the following for its dynamic component.
+// The dynamic component houses the default ACL and primary group ID.
+// If the size of these parameters passed upon token creation are larger
+// than this default, then the larger value will be charged.
+//
+
+#define TOKEN_DEFAULT_DYNAMIC_CHARGE 500
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Token Object Body //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+//
+// Tokens have three parts:
+//
+// Fixed part of body,
+// Variable part of body,
+// Dynamic part (not in body).
+//
+// The fixed and variable parts are allocated in a single block of memory.
+// The dynamic part is a separately allocated block of memory.
+//
+// The fixed part of the body contains the fixed length fields. These
+// are defined in the TOKEN data type.
+//
+// The variable part of the body is variable in length and contains
+// privileges and user/group SIDs. This part is variable in length
+// between different token objects, but does not change once established
+// for an individual token.
+//
+// The dynamic part is used to house default discretionary ACL information
+// and the primary group ID.
+//
+// Pictorially, a token looks like:
+//
+// ============== +---------------+
+// ^ | |
+// | | |
+// | | |
+// | | DynamicPart o-----------+
+// | |- - - - - - - -| |
+// +-----o Privileges | |
+// Token | |- - - - - - - -| |
+// Body | +--o UserAndGroups | \|/
+// | | |- - - - - - - -| +---------------------+
+// | | | | PrimaryGroup o------->| [Primary Group SID] |
+// | | | |- - - - - - - -| | o |
+// | | | | DefaultAcl o---+ | o |
+// | | | |- - - - - - - -| | | o |
+// | | | | o | | |- - - - - - - - - - -|
+// | | | | o | +--->| [ Default Acl ] |
+// v | | | o | | o |
+// ==============| | |===============| | o |
+// ^ | +->| SIDs Array | | o |
+// | | | [User SID ] | +---------------------+
+// | | | [Group SID ] |
+// | | [Group SID ] |
+// Variable | | o |
+// Part | | o |
+// | |- - - - - - - -|
+// | +---->| Privileges |
+// | | Array |
+// v | |
+// ============== +---------------+
+//
+// WARNING: The positions of fields illustrated in this picture are not
+// intented to reflect their actual or even relative positions
+// within the real data structures. The exception to this is
+// that THE USER SID IS THE FIRST SID IN THE UserAndGroups
+// ARRAY.
+//
+
+
+//
+// ! ! ! ! IMPORTANT ! ! ! !
+//
+// The access validation routines assume the SIDs are arranged
+// in a particular order within the variable part of the token.
+// Any changes to the order of the SIDs must be coordinated with
+// corresponding changes to the access validation routines.
+//
+// ! ! ! ! ! ! ! ! ! ! !
+
+
+
+typedef struct _TOKEN {
+
+ //
+ // Fields arranged by size to preserve alignment.
+ // Large fields before small fields.
+ //
+
+
+ //
+ // The following fields are either ReadOnly or ReadWrite.
+ // ReadOnly fields may be referenced any time a pointer to the
+ // token is still valid. ReadWrite fields may only be referenced
+ // when the TokenLock is held.
+
+ // The dynamic part of the token (pointed to by the DynamicPart field)
+ // is also protected by the token lock.
+ //
+ // ReadOnly fields are marked Ro: in their comment.
+ // ReadWrite fields are marked Wr: in their comment.
+ //
+
+ TOKEN_SOURCE TokenSource; // Ro: 16-Bytes
+
+ LUID TokenId; // Ro: 8-Bytes
+ LUID AuthenticationId; // Ro: 8-Bytes
+ LARGE_INTEGER ExpirationTime; // Ro: 8-Bytes
+
+ //
+ // Each time the security information in a token is changed, the
+ // following ID is changed. Fields that cause this field to be
+ // updated are marked as (Mod) in their comment field.
+ //
+
+ LUID ModifiedId; // Wr: 8-Bytes
+
+ ULONG UserAndGroupCount; // Ro: 4-Bytes
+ ULONG PrivilegeCount; // Ro: 4-Bytes
+ ULONG VariableLength; // Ro: 4-Bytes
+ ULONG DynamicCharged; // Ro: 4-Bytes
+
+ ULONG DynamicAvailable; // Wr: 4-Bytes (Mod)
+ ULONG DefaultOwnerIndex; // Wr: 4-Bytes (Mod)
+ PSID_AND_ATTRIBUTES UserAndGroups; // Wr: 4-Bytes (Mod)
+ PSID PrimaryGroup; // Wr: 4-Bytes (Mod)
+ PLUID_AND_ATTRIBUTES Privileges; // Wr: 4-Bytes (Mod)
+ PULONG DynamicPart; // Wr: 4-Bytes (Mod)
+ PACL DefaultDacl; // Wr: 4-Bytes (Mod)
+
+
+
+ TOKEN_TYPE TokenType; // Ro: 1-Byte
+ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel; // Ro: 1-Byte
+
+ UCHAR TokenFlags; // Ro: 4-Bytes
+ BOOLEAN TokenInUse; // Wr: 1-Byte
+
+ PSECURITY_TOKEN_PROXY_DATA ProxyData; // Ro: 4-Bytes
+ PSECURITY_TOKEN_AUDIT_DATA AuditData; // Ro: 4-Bytes
+
+ //
+ // This marks the beginning of the variable part of the token.
+ // It must follow all other fields in the token.
+ //
+
+ ULONG VariablePart; // Wr: 4-Bytes (Mod)
+
+ } TOKEN, * PTOKEN;
+
+//
+// Where:
+//
+// TokenSource - Information provided by the executive component that
+// requested the logon that the token represents.
+//
+//
+// TokenId - Is an LUID value. Each token object has a uniquely
+// assigned LUID.
+//
+//
+// AuthenticationId - Is the LUID assigned by the domain controller for
+// the logon session.
+//
+//
+// ExpirationTime - Not yet supported in NT.
+//
+//
+// ModifiedId - Is an LUID which is changed each time a modification is
+// made to this token which changes the security semantics of the
+// token. This includes enabling/disabling privileges and groups
+// and changing default ACLs, et cetera. Any token which is a
+// duplicate of this token will have the same ModifiedId (until
+// one or the other is changed). This does not cover changes to
+// non-security semantics fields, like TokenInUse.
+//
+//
+// UserAndGroupCount - Indicates the number of user/group IDs in this token.
+// This value must be at least 1. A value of 1 indicates a user
+// ID with no supplemental group IDs. A value of 5 indicates a
+// user ID and 4 supplemental group IDs.
+//
+// PrivilegeCount - Indicates how many privileges are included in
+// this token. May be zero or larger.
+//
+// TokenType - Indicates which type of token this token object is.
+//
+// ImpersonationLevel - For TokenImpersonation type tokens, this field
+// indicates the impersonation level. For TokenPrimary type tokens,
+// this field is ignored.
+//
+// DynamicCharged - Indicates how much pool quota has been charged
+// for the dynamic portion of this token.
+//
+// DynamicAvailable - Indicates how much of the charged quota is still
+// available for use. This is modified when pool associated
+// with the dynamic portion of the token is allocated or freed,
+// such as when the default DACL or primary group is replaced.
+//
+//
+// DefaultOwnerIndex - If non-zero, identifies an ID that has explicitly
+// been established as the default owner for this token. If it is zero,
+// the standard default (user ID) is used as the default owner.
+//
+// UserAndGroups - Points to an array of SID_AND_ATTRIBUTES. The first
+// element in this array is the token's user ID. Any additional
+// elements are those of groups. The number of entries in this
+// array is one greater than
+//
+// PrimaryGroup - Points to an SID that is to be used as the primary
+// group of the token. There are no value restrictions
+// placed upon what can be used as a primary group. This
+// SID is not one of user or group IDs (although it may have the
+// same value as one of those IDs).
+//
+// Privileges - Points to an array of privileges represented as
+// LUID_AND_ATTRIBUTES. The number of elements in this array
+// is contained in the PrivilegesCount field.
+//
+// TokenInUse - Is a boolean that indicates whether a primary token
+// is already in use by a process. This field value is only
+// valid for primary tokens.
+//
+// ProxyData - Optionally points to a Proxy data structure, containing
+// the information to be passed to AVR routines by file systems.
+// This field being non-null identifies the token as a proxy token.
+//
+// AuditData - Optionally points to an Audit data structure, containing
+// global auditing data for this subject.
+//
+// NOTE: Access to this field is guarded by the global
+// PROCESS SECURITY FIELDS LOCK.
+// VariablePart - Is the beginning of the variable part of the token.
+//
+
+
+/////////////////////////////////////////////////////////////////////////
+// //
+// Token Specific Macros //
+// //
+/////////////////////////////////////////////////////////////////////////
+
+
+
+
+
+#ifndef TOKEN_DIAGNOSTICS_ENABLED
+
+#define SepAcquireTokenReadLock(T) KeEnterCriticalRegion(); \
+ ExAcquireResourceShared(&SepTokenLock, TRUE)
+
+#define SepAcquireTokenWriteLock(T) KeEnterCriticalRegion(); \
+ ExAcquireResourceExclusive(&SepTokenLock, TRUE)
+
+#define SepReleaseTokenReadLock(T) ExReleaseResource(&SepTokenLock); \
+ KeLeaveCriticalRegion()
+
+#else // TOKEN_DIAGNOSTICS_ENABLED
+
+#define SepAcquireTokenReadLock(T) if (TokenGlobalFlag & TOKEN_DIAG_TOKEN_LOCKS) { \
+ DbgPrint("SE (Token): Acquiring Token READ Lock for access to token 0x%lx\n", (T)); \
+ } \
+ KeEnterCriticalRegion(); \
+ ExAcquireResourceShared(&SepTokenLock, TRUE)
+
+#define SepAcquireTokenWriteLock(T) if (TokenGlobalFlag & TOKEN_DIAG_TOKEN_LOCKS) { \
+ DbgPrint("SE (Token): Acquiring Token WRITE Lock for access to token 0x%lx ********************* EXCLUSIVE *****\n", (T)); \
+ } \
+ KeEnterCriticalRegion(); \
+ ExAcquireResourceExclusive(&SepTokenLock, TRUE)
+
+#define SepReleaseTokenReadLock(T) if (TokenGlobalFlag & TOKEN_DIAG_TOKEN_LOCKS) { \
+ DbgPrint("SE (Token): Releasing Token Lock for access to token 0x%lx\n", (T)); \
+ } \
+ ExReleaseResource(&SepTokenLock); \
+ KeLeaveCriticalRegion()
+
+#endif // TOKEN_DIAGNOSTICS_ENABLED
+
+#define SepReleaseTokenWriteLock(T,M) \
+ { \
+ if ((M)) { \
+ ExAllocateLocallyUniqueId( &((PTOKEN)(T))->ModifiedId ); \
+ } \
+ SepReleaseTokenReadLock( T ); \
+ }
+
+//
+// Reference individual privilege attribute flags of any privilege array
+//
+// P - is a pointer to an array of privileges (PLUID_AND_ATTRIBUTES)
+// I - is the index of the privilege
+// A - is the name of the attribute desired (e.g., Enabled, EnabledByDefault, etc. )
+//
+
+#define SepArrayPrivilegeAttributes(P,I) ( (P)[I].Attributes )
+
+//
+// Reference individual privilege attribute flags of token privileges
+//
+// T - is a pointer to a token
+// I - is the index of the privilege
+// A - is the name of the attribute desired (e.g., Enabled, EnabledByDefault, etc. )
+//
+
+#define SepTokenPrivilegeAttributes(T,I) ( (T)->Privileges[I].Attributes )
+
+//
+// Reference individual group attribute flags of any group array
+//
+// G - is a pointer to the array of groups (SID_AND_ATTRIBUTES[])
+// I - is the index of the group
+//
+
+#define SepArrayGroupAttributes(G,I) ( (G)[I].Attributes )
+
+
+//
+// Reference individual group attribute flags of token groups
+//
+// T - is a pointer to a token
+// I - is the index of the group
+//
+
+#define SepTokenGroupAttributes(T,I) ( (T)->UserAndGroups[I].Attributes )
+
+
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Private Routine Declarations //
+// //
+////////////////////////////////////////////////////////////////////////
+
+NTSTATUS
+SepAdjustGroups(
+ IN PTOKEN Token,
+ IN BOOLEAN MakeChanges,
+ IN BOOLEAN ResetToDefault,
+ IN ULONG GroupCount OPTIONAL,
+ IN PSID_AND_ATTRIBUTES NewState OPTIONAL,
+ OUT PTOKEN_GROUPS PreviousState OPTIONAL,
+ OUT PSID SidBuffer OPTIONAL,
+ OUT PULONG ReturnLength,
+ OUT PULONG ChangeCount,
+ OUT PBOOLEAN ChangesMade
+ );
+
+NTSTATUS
+SepAdjustPrivileges(
+ IN PTOKEN Token,
+ IN BOOLEAN MakeChanges,
+ IN BOOLEAN DisableAllPrivileges,
+ IN ULONG PrivilegeCount OPTIONAL,
+ IN PLUID_AND_ATTRIBUTES NewState OPTIONAL,
+ OUT PTOKEN_PRIVILEGES PreviousState OPTIONAL,
+ OUT PULONG ReturnLength,
+ OUT PULONG ChangeCount,
+ OUT PBOOLEAN ChangesMade
+ );
+
+VOID
+SepAppendDefaultDacl(
+ IN PTOKEN Token,
+ IN PACL PAcl
+ );
+
+VOID
+SepAppendPrimaryGroup(
+ IN PTOKEN Token,
+ IN PSID PSid
+ );
+
+NTSTATUS
+SepDuplicateToken(
+ IN PTOKEN ExistingToken,
+ IN POBJECT_ATTRIBUTES ObjectAttributes,
+ IN BOOLEAN EffectiveOnly,
+ IN TOKEN_TYPE TokenType,
+ IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel OPTIONAL,
+ IN KPROCESSOR_MODE RequestorMode,
+ OUT PTOKEN *DuplicateToken
+ );
+
+VOID
+SepFreeDefaultDacl(
+ IN PTOKEN Token
+ );
+
+VOID
+SepFreePrimaryGroup(
+ IN PTOKEN Token
+ );
+
+
+BOOLEAN
+SepIdAssignableAsOwner(
+ IN PTOKEN Token,
+ IN ULONG Index
+ );
+
+VOID
+SepMakeTokenEffectiveOnly(
+ IN PTOKEN Token
+ );
+
+BOOLEAN
+SepTokenInitialization( VOID );
+
+
+VOID
+SepTokenDeleteMethod (
+ IN PVOID Token
+ );
+
+//
+// These are here because if they are placed in sep.h, we don't
+// have the PTOKEN datatype available.
+//
+
+BOOLEAN
+SepPrivilegeCheck(
+ IN PTOKEN Token,
+ IN OUT PLUID_AND_ATTRIBUTES RequiredPrivileges,
+ IN ULONG RequiredPrivilegeCount,
+ IN ULONG PrivilegeSetControl,
+ IN KPROCESSOR_MODE PreviousMode
+ );
+
+BOOLEAN
+SepAccessCheck (
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN PTOKEN PrimaryToken,
+ IN PTOKEN ClientToken,
+ IN ACCESS_MASK DesiredAccess,
+ IN PGENERIC_MAPPING GenericMapping,
+ IN ACCESS_MASK PreviouslyGrantedAccess,
+ IN KPROCESSOR_MODE PreviousMode,
+ OUT PACCESS_MASK GrantedAccess,
+ OUT PPRIVILEGE_SET *Privileges OPTIONAL,
+ OUT PNTSTATUS AccessStatus
+ );
+
+
+#ifdef TOKEN_DEBUG
+VOID
+SepDumpToken(
+ IN PTOKEN T
+ );
+#endif //TOKEN_DEBUG
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Global Variables //
+// //
+////////////////////////////////////////////////////////////////////////
+
+
+extern GENERIC_MAPPING SepTokenMapping;
+extern POBJECT_TYPE SepTokenObjectType;
+
+extern ERESOURCE SepTokenLock;
+
+
+#ifdef TOKEN_DIAGNOSTICS_ENABLED
+extern ULONG TokenGlobalFlag;
+#endif // TOKEN_DIAGNOSTICS_ENABLED
+
+
+#endif // _TOKENP_
diff --git a/private/ntos/se/tokenqry.c b/private/ntos/se/tokenqry.c
new file mode 100644
index 000000000..04c5e3f11
--- /dev/null
+++ b/private/ntos/se/tokenqry.c
@@ -0,0 +1,1612 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ Tokenqry.c
+
+Abstract:
+
+ This module implements the QUERY function for the executive
+ token object.
+
+Author:
+
+ Jim Kelly (JimK) 15-June-1990
+
+
+Revision History:
+
+--*/
+
+#include "sep.h"
+#include "seopaque.h"
+#include "tokenp.h"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE,NtQueryInformationToken)
+#pragma alloc_text(PAGE,SeQueryAuthenticationIdToken)
+#pragma alloc_text(PAGE,SeQueryInformationToken)
+#endif
+
+
+NTSTATUS
+NtQueryInformationToken (
+ IN HANDLE TokenHandle,
+ IN TOKEN_INFORMATION_CLASS TokenInformationClass,
+ OUT PVOID TokenInformation,
+ IN ULONG TokenInformationLength,
+ OUT PULONG ReturnLength
+ )
+
+/*++
+
+
+Routine Description:
+
+ Retrieve information about a specified token.
+
+Arguments:
+
+ TokenHandle - Provides a handle to the token to operate on.
+
+ TokenInformationClass - The token information class about which
+ to retrieve information.
+
+ TokenInformation - The buffer to receive the requested class of
+ information. The buffer must be aligned on at least a
+ longword boundary. The actual structures returned are
+ dependent upon the information class requested, as defined in
+ the TokenInformationClass parameter description.
+
+ TokenInformation Format By Information Class:
+
+ TokenUser => TOKEN_USER data structure. TOKEN_QUERY
+ access is needed to retrieve this information about a
+ token.
+
+ TokenGroups => TOKEN_GROUPS data structure. TOKEN_QUERY
+ access is needed to retrieve this information about a
+ token.
+
+ TokenPrivileges => TOKEN_PRIVILEGES data structure.
+ TOKEN_QUERY access is needed to retrieve this information
+ about a token.
+
+ TokenOwner => TOKEN_OWNER data structure. TOKEN_QUERY
+ access is needed to retrieve this information about a
+ token.
+
+ TokenPrimaryGroup => TOKEN_PRIMARY_GROUP data structure.
+ TOKEN_QUERY access is needed to retrieve this information
+ about a token.
+
+ TokenDefaultDacl => TOKEN_DEFAULT_DACL data structure.
+ TOKEN_QUERY access is needed to retrieve this information
+ about a token.
+
+ TokenSource => TOKEN_SOURCE data structure.
+ TOKEN_QUERY_SOURCE access is needed to retrieve this
+ information about a token.
+
+ TokenType => TOKEN_TYPE data structure.
+ TOKEN_QUERY access is needed to retrieve this information
+ about a token.
+
+ TokenStatistics => TOKEN_STATISTICS data structure.
+ TOKEN_QUERY access is needed to retrieve this
+ information about a token.
+
+ TokenInformationLength - Indicates the length, in bytes, of the
+ TokenInformation buffer.
+
+ ReturnLength - This OUT parameter receives the actual length of
+ the requested information. If this value is larger than that
+ provided by the TokenInformationLength parameter, then the
+ buffer provided to receive the requested information is not
+ large enough to hold that data and no data is returned.
+
+ If the queried class is TokenDefaultDacl and there is no
+ default Dacl established for the token, then the return
+ length will be returned as zero, and no data will be returned.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the operation was successful.
+
+ STATUS_BUFFER_TOO_SMALL - if the requested information did not
+ fit in the provided output buffer. In this case, the
+ ReturnLength OUT parameter contains the number of bytes
+ actually needed to store the requested information.
+
+--*/
+{
+
+ KPROCESSOR_MODE PreviousMode;
+ NTSTATUS Status;
+
+ PTOKEN Token;
+
+ ULONG RequiredLength;
+ ULONG Index;
+
+ PTOKEN_TYPE LocalType;
+ PTOKEN_USER LocalUser;
+ PTOKEN_GROUPS LocalGroups;
+ PTOKEN_PRIVILEGES LocalPrivileges;
+ PTOKEN_OWNER LocalOwner;
+ PTOKEN_PRIMARY_GROUP LocalPrimaryGroup;
+ PTOKEN_DEFAULT_DACL LocalDefaultDacl;
+ PTOKEN_SOURCE LocalSource;
+ PSECURITY_IMPERSONATION_LEVEL LocalImpersonationLevel;
+ PTOKEN_STATISTICS LocalStatistics;
+
+ PSID PSid;
+ PACL PAcl;
+
+ PVOID Ignore;
+
+ PAGED_CODE();
+
+ //
+ // Get previous processor mode and probe output argument if necessary.
+ //
+
+ PreviousMode = KeGetPreviousMode();
+ if (PreviousMode != KernelMode) {
+ try {
+
+ ProbeForWrite(
+ TokenInformation,
+ TokenInformationLength,
+ sizeof(ULONG)
+ );
+
+ ProbeForWriteUlong(ReturnLength);
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ return GetExceptionCode();
+ }
+ }
+
+ //
+ // Case on information class.
+ //
+
+ switch ( TokenInformationClass ) {
+
+ case TokenUser:
+
+ LocalUser = (PTOKEN_USER)TokenInformation;
+
+ Status = ObReferenceObjectByHandle(
+ TokenHandle, // Handle
+ TOKEN_QUERY, // DesiredAccess
+ SepTokenObjectType, // ObjectType
+ PreviousMode, // AccessMode
+ (PVOID *)&Token, // Object
+ NULL // GrantedAccess
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ return Status;
+ }
+
+ //
+ // Gain exclusive access to the token.
+ //
+
+ SepAcquireTokenReadLock( Token );
+
+
+
+ //
+ // Return the length required now in case not enough buffer
+ // was provided by the caller and we have to return an error.
+ //
+
+ RequiredLength = SeLengthSid( Token->UserAndGroups[0].Sid) +
+ (ULONG)sizeof( TOKEN_USER );
+
+ try {
+
+ *ReturnLength = RequiredLength;
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ SepReleaseTokenReadLock( Token );
+ ObDereferenceObject( Token );
+ return GetExceptionCode();
+ }
+
+ if ( TokenInformationLength < RequiredLength ) {
+
+ SepReleaseTokenReadLock( Token );
+ ObDereferenceObject( Token );
+ return STATUS_BUFFER_TOO_SMALL;
+
+ }
+
+ //
+ // Return the user SID
+ //
+
+ try {
+
+ //
+ // Put SID immediately following TOKEN_USER data structure
+ //
+ PSid = (PSID)( (ULONG)LocalUser + (ULONG)sizeof(TOKEN_USER) );
+
+ RtlCopySidAndAttributesArray(
+ 1,
+ Token->UserAndGroups,
+ RequiredLength,
+ &(LocalUser->User),
+ PSid,
+ ((PSID *)&Ignore),
+ ((PULONG)&Ignore)
+ );
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ SepReleaseTokenReadLock( Token );
+ ObDereferenceObject( Token );
+ return GetExceptionCode();
+ }
+
+
+ SepReleaseTokenReadLock( Token );
+ ObDereferenceObject( Token );
+ return STATUS_SUCCESS;
+
+ case TokenGroups:
+
+ LocalGroups = (PTOKEN_GROUPS)TokenInformation;
+
+ Status = ObReferenceObjectByHandle(
+ TokenHandle, // Handle
+ TOKEN_QUERY, // DesiredAccess
+ SepTokenObjectType, // ObjectType
+ PreviousMode, // AccessMode
+ (PVOID *)&Token, // Object
+ NULL // GrantedAccess
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ return Status;
+ }
+
+ //
+ // Gain exclusive access to the token.
+ //
+
+ SepAcquireTokenReadLock( Token );
+
+ //
+ // Figure out how much space is needed to return the group SIDs.
+ // That's the size of TOKEN_GROUPS (without any array entries)
+ // plus the size of an SID_AND_ATTRIBUTES times the number of groups.
+ // The number of groups is Token->UserAndGroups-1 (since the count
+ // includes the user ID). Then the lengths of each individual group
+ // must be added.
+ //
+
+ RequiredLength = (ULONG)sizeof(TOKEN_GROUPS) +
+ ((Token->UserAndGroupCount - ANYSIZE_ARRAY - 1) *
+ ((ULONG)sizeof(SID_AND_ATTRIBUTES)) );
+
+ Index = 1;
+ while (Index < Token->UserAndGroupCount) {
+
+ RequiredLength += SeLengthSid( Token->UserAndGroups[Index].Sid );
+
+ Index += 1;
+
+ } // endwhile
+
+ //
+ // Return the length required now in case not enough buffer
+ // was provided by the caller and we have to return an error.
+ //
+
+ try {
+
+ *ReturnLength = RequiredLength;
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ SepReleaseTokenReadLock( Token );
+ ObDereferenceObject( Token );
+ return GetExceptionCode();
+ }
+
+ if ( TokenInformationLength < RequiredLength ) {
+
+ SepReleaseTokenReadLock( Token );
+ ObDereferenceObject( Token );
+ return STATUS_BUFFER_TOO_SMALL;
+
+ }
+
+ //
+ // Now copy the groups.
+ //
+
+ try {
+
+ LocalGroups->GroupCount = Token->UserAndGroupCount - 1;
+
+ PSid = (PSID)( (ULONG)LocalGroups +
+ (ULONG)sizeof(TOKEN_GROUPS) +
+ ( (Token->UserAndGroupCount - ANYSIZE_ARRAY - 1) *
+ (ULONG)sizeof(SID_AND_ATTRIBUTES) )
+ );
+
+ RtlCopySidAndAttributesArray(
+ (ULONG)(Token->UserAndGroupCount - 1),
+ &(Token->UserAndGroups[1]),
+ RequiredLength,
+ LocalGroups->Groups,
+ PSid,
+ ((PSID *)&Ignore),
+ ((PULONG)&Ignore)
+ );
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ SepReleaseTokenReadLock( Token );
+ ObDereferenceObject( Token );
+ return GetExceptionCode();
+ }
+
+
+ SepReleaseTokenReadLock( Token );
+ ObDereferenceObject( Token );
+ return STATUS_SUCCESS;
+
+ case TokenPrivileges:
+
+ LocalPrivileges = (PTOKEN_PRIVILEGES)TokenInformation;
+
+ Status = ObReferenceObjectByHandle(
+ TokenHandle, // Handle
+ TOKEN_QUERY, // DesiredAccess
+ SepTokenObjectType, // ObjectType
+ PreviousMode, // AccessMode
+ (PVOID *)&Token, // Object
+ NULL // GrantedAccess
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ return Status;
+ }
+
+ //
+ // Gain exclusive access to the token to prevent changes
+ // from occuring to the privileges.
+ //
+
+ SepAcquireTokenReadLock( Token );
+
+
+ //
+ // Return the length required now in case not enough buffer
+ // was provided by the caller and we have to return an error.
+ //
+
+ RequiredLength = (ULONG)sizeof(TOKEN_PRIVILEGES) +
+ ((Token->PrivilegeCount - ANYSIZE_ARRAY) *
+ ((ULONG)sizeof(LUID_AND_ATTRIBUTES)) );
+
+
+ try {
+
+ *ReturnLength = RequiredLength;
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ SepReleaseTokenReadLock( Token );
+ ObDereferenceObject( Token );
+ return GetExceptionCode();
+ }
+
+ if ( TokenInformationLength < RequiredLength ) {
+
+ SepReleaseTokenReadLock( Token );
+ ObDereferenceObject( Token );
+ return STATUS_BUFFER_TOO_SMALL;
+
+ }
+
+ //
+ // Return the token privileges.
+ //
+
+ try {
+
+ LocalPrivileges->PrivilegeCount = Token->PrivilegeCount;
+
+ RtlCopyLuidAndAttributesArray(
+ Token->PrivilegeCount,
+ Token->Privileges,
+ LocalPrivileges->Privileges
+ );
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ SepReleaseTokenReadLock( Token );
+ ObDereferenceObject( Token );
+ return GetExceptionCode();
+ }
+
+
+ SepReleaseTokenReadLock( Token );
+ ObDereferenceObject( Token );
+ return STATUS_SUCCESS;
+
+ case TokenOwner:
+
+ LocalOwner = (PTOKEN_OWNER)TokenInformation;
+
+ Status = ObReferenceObjectByHandle(
+ TokenHandle, // Handle
+ TOKEN_QUERY, // DesiredAccess
+ SepTokenObjectType, // ObjectType
+ PreviousMode, // AccessMode
+ (PVOID *)&Token, // Object
+ NULL // GrantedAccess
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ return Status;
+ }
+
+ //
+ // Gain exclusive access to the token to prevent changes
+ // from occuring to the owner.
+ //
+
+ SepAcquireTokenReadLock( Token );
+
+ //
+ // Return the length required now in case not enough buffer
+ // was provided by the caller and we have to return an error.
+ //
+
+ PSid = Token->UserAndGroups[Token->DefaultOwnerIndex].Sid;
+ RequiredLength = (ULONG)sizeof(TOKEN_OWNER) +
+ SeLengthSid( PSid );
+
+ try {
+
+ *ReturnLength = RequiredLength;
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ SepReleaseTokenReadLock( Token );
+ ObDereferenceObject( Token );
+ return GetExceptionCode();
+ }
+
+ if ( TokenInformationLength < RequiredLength ) {
+
+ SepReleaseTokenReadLock( Token );
+ ObDereferenceObject( Token );
+ return STATUS_BUFFER_TOO_SMALL;
+
+ }
+
+ //
+ // Return the owner SID
+ //
+
+ PSid = (PSID)((ULONG)LocalOwner +
+ (ULONG)sizeof(TOKEN_OWNER));
+
+ try {
+
+ LocalOwner->Owner = PSid;
+
+ Status = RtlCopySid(
+ (RequiredLength - (ULONG)sizeof(TOKEN_OWNER)),
+ PSid,
+ Token->UserAndGroups[Token->DefaultOwnerIndex].Sid
+ );
+
+ ASSERT( NT_SUCCESS(Status) );
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ SepReleaseTokenReadLock( Token );
+ ObDereferenceObject( Token );
+ return GetExceptionCode();
+ }
+
+
+ SepReleaseTokenReadLock( Token );
+ ObDereferenceObject( Token );
+ return STATUS_SUCCESS;
+
+ case TokenPrimaryGroup:
+
+ LocalPrimaryGroup = (PTOKEN_PRIMARY_GROUP)TokenInformation;
+
+ Status = ObReferenceObjectByHandle(
+ TokenHandle, // Handle
+ TOKEN_QUERY, // DesiredAccess
+ SepTokenObjectType, // ObjectType
+ PreviousMode, // AccessMode
+ (PVOID *)&Token, // Object
+ NULL // GrantedAccess
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ return Status;
+ }
+
+ //
+ // Gain exclusive access to the token to prevent changes
+ // from occuring to the owner.
+ //
+
+ SepAcquireTokenReadLock( Token );
+
+ //
+ // Return the length required now in case not enough buffer
+ // was provided by the caller and we have to return an error.
+ //
+
+ RequiredLength = (ULONG)sizeof(TOKEN_PRIMARY_GROUP) +
+ SeLengthSid( Token->PrimaryGroup );
+
+ try {
+
+ *ReturnLength = RequiredLength;
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ SepReleaseTokenReadLock( Token );
+ ObDereferenceObject( Token );
+ return GetExceptionCode();
+ }
+
+ if ( TokenInformationLength < RequiredLength ) {
+
+ SepReleaseTokenReadLock( Token );
+ ObDereferenceObject( Token );
+ return STATUS_BUFFER_TOO_SMALL;
+
+ }
+
+ //
+ // Return the primary group SID
+ //
+
+ PSid = (PSID)((ULONG)LocalPrimaryGroup +
+ (ULONG)sizeof(TOKEN_PRIMARY_GROUP));
+
+ try {
+
+ LocalPrimaryGroup->PrimaryGroup = PSid;
+
+ Status = RtlCopySid( (RequiredLength - (ULONG)sizeof(TOKEN_PRIMARY_GROUP)),
+ PSid,
+ Token->PrimaryGroup
+ );
+
+ ASSERT( NT_SUCCESS(Status) );
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ SepReleaseTokenReadLock( Token );
+ ObDereferenceObject( Token );
+ return GetExceptionCode();
+ }
+
+
+ SepReleaseTokenReadLock( Token );
+ ObDereferenceObject( Token );
+ return STATUS_SUCCESS;
+
+ case TokenDefaultDacl:
+
+ LocalDefaultDacl = (PTOKEN_DEFAULT_DACL)TokenInformation;
+
+ Status = ObReferenceObjectByHandle(
+ TokenHandle, // Handle
+ TOKEN_QUERY, // DesiredAccess
+ SepTokenObjectType, // ObjectType
+ PreviousMode, // AccessMode
+ (PVOID *)&Token, // Object
+ NULL // GrantedAccess
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ return Status;
+ }
+
+ //
+ // Gain exclusive access to the token to prevent changes
+ // from occuring to the owner.
+ //
+
+ SepAcquireTokenReadLock( Token );
+
+
+ //
+ // Return the length required now in case not enough buffer
+ // was provided by the caller and we have to return an error.
+ //
+
+ RequiredLength = (ULONG)sizeof(TOKEN_DEFAULT_DACL);
+
+ if (ARGUMENT_PRESENT(Token->DefaultDacl)) {
+
+ RequiredLength += Token->DefaultDacl->AclSize;
+
+ }
+
+ try {
+
+ *ReturnLength = RequiredLength;
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ SepReleaseTokenReadLock( Token );
+ ObDereferenceObject( Token );
+ return GetExceptionCode();
+ }
+
+ if ( TokenInformationLength < RequiredLength ) {
+
+ SepReleaseTokenReadLock( Token );
+ ObDereferenceObject( Token );
+ return STATUS_BUFFER_TOO_SMALL;
+
+ }
+
+ //
+ // Return the default Dacl
+ //
+
+ PAcl = (PACL)((ULONG)LocalDefaultDacl +
+ (ULONG)sizeof(TOKEN_DEFAULT_DACL));
+
+ try {
+
+ if (ARGUMENT_PRESENT(Token->DefaultDacl)) {
+
+ LocalDefaultDacl->DefaultDacl = PAcl;
+
+ RtlMoveMemory( (PVOID)PAcl,
+ (PVOID)Token->DefaultDacl,
+ Token->DefaultDacl->AclSize
+ );
+ } else {
+
+ LocalDefaultDacl->DefaultDacl = NULL;
+
+ }
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ SepReleaseTokenReadLock( Token );
+ ObDereferenceObject( Token );
+ return GetExceptionCode();
+ }
+
+
+ SepReleaseTokenReadLock( Token );
+ ObDereferenceObject( Token );
+ return STATUS_SUCCESS;
+
+
+
+ case TokenSource:
+
+ LocalSource = (PTOKEN_SOURCE)TokenInformation;
+
+ Status = ObReferenceObjectByHandle(
+ TokenHandle, // Handle
+ TOKEN_QUERY_SOURCE, // DesiredAccess
+ SepTokenObjectType, // ObjectType
+ PreviousMode, // AccessMode
+ (PVOID *)&Token, // Object
+ NULL // GrantedAccess
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ return Status;
+ }
+
+ //
+ // The type of a token can not be changed, so
+ // exclusive access to the token is not necessary.
+ //
+
+ //
+ // Return the length required now in case not enough buffer
+ // was provided by the caller and we have to return an error.
+ //
+
+ RequiredLength = (ULONG) sizeof(TOKEN_SOURCE);
+
+ try {
+
+ *ReturnLength = RequiredLength;
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ ObDereferenceObject( Token );
+ return GetExceptionCode();
+ }
+
+ if ( TokenInformationLength < RequiredLength ) {
+
+ ObDereferenceObject( Token );
+ return STATUS_BUFFER_TOO_SMALL;
+
+ }
+
+
+ //
+ // Return the token source
+ //
+
+ try {
+
+ (*LocalSource) = Token->TokenSource;
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ ObDereferenceObject( Token );
+ return GetExceptionCode();
+ }
+
+
+ ObDereferenceObject( Token );
+ return STATUS_SUCCESS;
+
+ case TokenType:
+
+ LocalType = (PTOKEN_TYPE)TokenInformation;
+
+ Status = ObReferenceObjectByHandle(
+ TokenHandle, // Handle
+ TOKEN_QUERY, // DesiredAccess
+ SepTokenObjectType, // ObjectType
+ PreviousMode, // AccessMode
+ (PVOID *)&Token, // Object
+ NULL // GrantedAccess
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ return Status;
+ }
+
+ //
+ // The type of a token can not be changed, so
+ // exclusive access to the token is not necessary.
+ //
+
+ //
+ // Return the length required now in case not enough buffer
+ // was provided by the caller and we have to return an error.
+ //
+
+ RequiredLength = (ULONG) sizeof(TOKEN_TYPE);
+
+ try {
+
+ *ReturnLength = RequiredLength;
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ ObDereferenceObject( Token );
+ return GetExceptionCode();
+ }
+
+ if ( TokenInformationLength < RequiredLength ) {
+
+ ObDereferenceObject( Token );
+ return STATUS_BUFFER_TOO_SMALL;
+
+ }
+
+
+ //
+ // Return the token type
+ //
+
+ try {
+
+ (*LocalType) = Token->TokenType;
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ ObDereferenceObject( Token );
+ return GetExceptionCode();
+ }
+
+
+ ObDereferenceObject( Token );
+ return STATUS_SUCCESS;
+
+
+ case TokenImpersonationLevel:
+
+ LocalImpersonationLevel = (PSECURITY_IMPERSONATION_LEVEL)TokenInformation;
+
+ Status = ObReferenceObjectByHandle(
+ TokenHandle, // Handle
+ TOKEN_QUERY, // DesiredAccess
+ SepTokenObjectType, // ObjectType
+ PreviousMode, // AccessMode
+ (PVOID *)&Token, // Object
+ NULL // GrantedAccess
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ return Status;
+ }
+
+ //
+ // The impersonation level of a token can not be changed, so
+ // exclusive access to the token is not necessary.
+ //
+
+ //
+ // Make sure the token is an appropriate type to be retrieving
+ // the impersonation level from.
+ //
+
+ if (Token->TokenType != TokenImpersonation) {
+
+ ObDereferenceObject( Token );
+ return STATUS_INVALID_INFO_CLASS;
+
+ }
+
+ //
+ // Return the length required now in case not enough buffer
+ // was provided by the caller and we have to return an error.
+ //
+
+ RequiredLength = (ULONG) sizeof(SECURITY_IMPERSONATION_LEVEL);
+
+ try {
+
+ *ReturnLength = RequiredLength;
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ ObDereferenceObject( Token );
+ return GetExceptionCode();
+ }
+
+ if ( TokenInformationLength < RequiredLength ) {
+
+ ObDereferenceObject( Token );
+ return STATUS_BUFFER_TOO_SMALL;
+
+ }
+
+
+ //
+ // Return the impersonation level
+ //
+
+ try {
+
+ (*LocalImpersonationLevel) = Token->ImpersonationLevel;
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ ObDereferenceObject( Token );
+ return GetExceptionCode();
+ }
+
+
+ ObDereferenceObject( Token );
+ return STATUS_SUCCESS;
+
+
+ case TokenStatistics:
+
+ LocalStatistics = (PTOKEN_STATISTICS)TokenInformation;
+
+ Status = ObReferenceObjectByHandle(
+ TokenHandle, // Handle
+ TOKEN_QUERY, // DesiredAccess
+ SepTokenObjectType, // ObjectType
+ PreviousMode, // AccessMode
+ (PVOID *)&Token, // Object
+ NULL // GrantedAccess
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ return Status;
+ }
+
+ //
+ // Gain exclusive access to the token.
+ //
+
+ SepAcquireTokenReadLock( Token );
+
+
+
+ //
+ // Return the length required now in case not enough buffer
+ // was provided by the caller and we have to return an error.
+ //
+
+ RequiredLength = (ULONG)sizeof( TOKEN_STATISTICS );
+
+ try {
+
+ *ReturnLength = RequiredLength;
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ SepReleaseTokenReadLock( Token );
+ ObDereferenceObject( Token );
+ return GetExceptionCode();
+ }
+
+ if ( TokenInformationLength < RequiredLength ) {
+
+ SepReleaseTokenReadLock( Token );
+ ObDereferenceObject( Token );
+ return STATUS_BUFFER_TOO_SMALL;
+
+ }
+
+ //
+ // Return the statistics
+ //
+
+ try {
+
+ LocalStatistics->TokenId = Token->TokenId;
+ LocalStatistics->AuthenticationId = Token->AuthenticationId;
+ LocalStatistics->ExpirationTime = Token->ExpirationTime;
+ LocalStatistics->TokenType = Token->TokenType;
+ LocalStatistics->ImpersonationLevel = Token->ImpersonationLevel;
+ LocalStatistics->DynamicCharged = Token->DynamicCharged;
+ LocalStatistics->DynamicAvailable = Token->DynamicAvailable;
+ LocalStatistics->GroupCount = Token->UserAndGroupCount-1;
+ LocalStatistics->PrivilegeCount = Token->PrivilegeCount;
+ LocalStatistics->ModifiedId = Token->ModifiedId;
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ SepReleaseTokenReadLock( Token );
+ ObDereferenceObject( Token );
+ return GetExceptionCode();
+ }
+
+
+ SepReleaseTokenReadLock( Token );
+ ObDereferenceObject( Token );
+ return STATUS_SUCCESS;
+
+ default:
+
+ return STATUS_INVALID_INFO_CLASS;
+ }
+}
+
+
+NTSTATUS
+SeQueryAuthenticationIdToken(
+ IN PACCESS_TOKEN Token,
+ OUT PLUID AuthenticationId
+ )
+
+/*++
+
+
+Routine Description:
+
+ Retrieve authentication ID out of the token.
+
+Arguments:
+
+ Token - Referenced pointer to a token.
+
+ AutenticationId - Receives the token's authentication ID.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the operation was successful.
+
+ This is the only expected status.
+
+--*/
+{
+ PAGED_CODE();
+
+ SepAcquireTokenReadLock( ((PTOKEN)Token) );
+ (*AuthenticationId) = ((PTOKEN)Token)->AuthenticationId;
+ SepReleaseTokenReadLock( ((PTOKEN)Token) );
+ return(STATUS_SUCCESS);
+}
+
+
+
+NTSTATUS
+SeQueryInformationToken (
+ IN PACCESS_TOKEN AccessToken,
+ IN TOKEN_INFORMATION_CLASS TokenInformationClass,
+ OUT PVOID *TokenInformation
+ )
+
+/*++
+
+
+Routine Description:
+
+ Retrieve information about a specified token.
+
+Arguments:
+
+ TokenHandle - Provides a handle to the token to operate on.
+
+ TokenInformationClass - The token information class about which
+ to retrieve information.
+
+ TokenInformation - Receives a pointer to the requested information.
+ The actual structures returned are dependent upon the information
+ class requested, as defined in the TokenInformationClass parameter
+ description.
+
+ TokenInformation Format By Information Class:
+
+ TokenUser => TOKEN_USER data structure. TOKEN_QUERY
+ access is needed to retrieve this information about a
+ token.
+
+ TokenGroups => TOKEN_GROUPS data structure. TOKEN_QUERY
+ access is needed to retrieve this information about a
+ token.
+
+ TokenPrivileges => TOKEN_PRIVILEGES data structure.
+ TOKEN_QUERY access is needed to retrieve this information
+ about a token.
+
+ TokenOwner => TOKEN_OWNER data structure. TOKEN_QUERY
+ access is needed to retrieve this information about a
+ token.
+
+ TokenPrimaryGroup => TOKEN_PRIMARY_GROUP data structure.
+ TOKEN_QUERY access is needed to retrieve this information
+ about a token.
+
+ TokenDefaultDacl => TOKEN_DEFAULT_DACL data structure.
+ TOKEN_QUERY access is needed to retrieve this information
+ about a token.
+
+ TokenSource => TOKEN_SOURCE data structure.
+ TOKEN_QUERY_SOURCE access is needed to retrieve this
+ information about a token.
+
+ TokenType => TOKEN_TYPE data structure.
+ TOKEN_QUERY access is needed to retrieve this information
+ about a token.
+
+ TokenStatistics => TOKEN_STATISTICS data structure.
+ TOKEN_QUERY access is needed to retrieve this
+ information about a token.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the operation was successful.
+
+--*/
+{
+
+ NTSTATUS Status;
+
+ ULONG RequiredLength;
+ ULONG Index;
+
+ PSID PSid;
+ PACL PAcl;
+
+ PVOID Ignore;
+ PTOKEN Token = (PTOKEN)AccessToken;
+
+ PAGED_CODE();
+
+ //
+ // Case on information class.
+ //
+
+ switch ( TokenInformationClass ) {
+
+ case TokenUser:
+ {
+ PTOKEN_USER LocalUser;
+
+ LocalUser = (PTOKEN_USER)(*TokenInformation);
+
+ //
+ // Gain exclusive access to the token.
+ //
+
+ SepAcquireTokenReadLock( Token );
+
+ //
+ // Return the length required now in case not enough buffer
+ // was provided by the caller and we have to return an error.
+ //
+
+ RequiredLength = SeLengthSid( Token->UserAndGroups[0].Sid) +
+ (ULONG)sizeof( TOKEN_USER );
+
+ LocalUser = ExAllocatePool( PagedPool, RequiredLength );
+
+ if (LocalUser == NULL) {
+ SepReleaseTokenReadLock( Token );
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ //
+ // Return the user SID
+ //
+ // Put SID immediately following TOKEN_USER data structure
+ //
+
+ PSid = (PSID)( (ULONG)LocalUser + (ULONG)sizeof(TOKEN_USER) );
+
+ RtlCopySidAndAttributesArray(
+ 1,
+ Token->UserAndGroups,
+ RequiredLength,
+ &(LocalUser->User),
+ PSid,
+ ((PSID *)&Ignore),
+ ((PULONG)&Ignore)
+ );
+
+ SepReleaseTokenReadLock( Token );
+ return STATUS_SUCCESS;
+ }
+
+
+ case TokenGroups:
+ {
+ PTOKEN_GROUPS LocalGroups;
+
+ LocalGroups = (PTOKEN_GROUPS)(*TokenInformation);
+
+ //
+ // Gain exclusive access to the token.
+ //
+
+ SepAcquireTokenReadLock( Token );
+
+ //
+ // Figure out how much space is needed to return the group SIDs.
+ // That's the size of TOKEN_GROUPS (without any array entries)
+ // plus the size of an SID_AND_ATTRIBUTES times the number of groups.
+ // The number of groups is Token->UserAndGroups-1 (since the count
+ // includes the user ID). Then the lengths of each individual group
+ // must be added.
+ //
+
+ RequiredLength = (ULONG)sizeof(TOKEN_GROUPS) +
+ ((Token->UserAndGroupCount - ANYSIZE_ARRAY - 1) *
+ ((ULONG)sizeof(SID_AND_ATTRIBUTES)) );
+
+ Index = 1;
+ while (Index < Token->UserAndGroupCount) {
+
+ RequiredLength += SeLengthSid( Token->UserAndGroups[Index].Sid );
+
+ Index += 1;
+
+ } // endwhile
+
+ LocalGroups = ExAllocatePool( PagedPool, RequiredLength );
+
+ if (LocalGroups == NULL) {
+ SepReleaseTokenReadLock( Token );
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ //
+ // Now copy the groups.
+ //
+
+ LocalGroups->GroupCount = Token->UserAndGroupCount - 1;
+
+ PSid = (PSID)( (ULONG)LocalGroups +
+ (ULONG)sizeof(TOKEN_GROUPS) +
+ ( (Token->UserAndGroupCount - ANYSIZE_ARRAY - 1) *
+ (ULONG)sizeof(SID_AND_ATTRIBUTES) )
+ );
+
+ RtlCopySidAndAttributesArray(
+ (ULONG)(Token->UserAndGroupCount - 1),
+ &(Token->UserAndGroups[1]),
+ RequiredLength,
+ LocalGroups->Groups,
+ PSid,
+ ((PSID *)&Ignore),
+ ((PULONG)&Ignore)
+ );
+
+ SepReleaseTokenReadLock( Token );
+ return STATUS_SUCCESS;
+ }
+
+
+ case TokenPrivileges:
+ {
+ PTOKEN_PRIVILEGES LocalPrivileges;
+
+ LocalPrivileges = (PTOKEN_PRIVILEGES)(*TokenInformation);
+
+ //
+ // Gain exclusive access to the token to prevent changes
+ // from occuring to the privileges.
+ //
+
+ SepAcquireTokenReadLock( Token );
+
+ //
+ // Return the length required now in case not enough buffer
+ // was provided by the caller and we have to return an error.
+ //
+
+ RequiredLength = (ULONG)sizeof(TOKEN_PRIVILEGES) +
+ ((Token->PrivilegeCount - ANYSIZE_ARRAY) *
+ ((ULONG)sizeof(LUID_AND_ATTRIBUTES)) );
+
+ LocalPrivileges = ExAllocatePool( PagedPool, RequiredLength );
+
+ if (LocalPrivileges == NULL) {
+ SepReleaseTokenReadLock( Token );
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ //
+ // Return the token privileges.
+ //
+
+ LocalPrivileges->PrivilegeCount = Token->PrivilegeCount;
+
+ RtlCopyLuidAndAttributesArray(
+ Token->PrivilegeCount,
+ Token->Privileges,
+ LocalPrivileges->Privileges
+ );
+
+ SepReleaseTokenReadLock( Token );
+ return STATUS_SUCCESS;
+ }
+
+
+ case TokenOwner:
+ {
+ PTOKEN_OWNER LocalOwner;
+
+ LocalOwner = (PTOKEN_OWNER)(*TokenInformation);
+
+ //
+ // Gain exclusive access to the token to prevent changes
+ // from occuring to the owner.
+ //
+
+ SepAcquireTokenReadLock( Token );
+
+ //
+ // Return the length required now in case not enough buffer
+ // was provided by the caller and we have to return an error.
+ //
+
+ PSid = Token->UserAndGroups[Token->DefaultOwnerIndex].Sid;
+ RequiredLength = (ULONG)sizeof(TOKEN_OWNER) +
+ SeLengthSid( PSid );
+
+ LocalOwner = ExAllocatePool( PagedPool, RequiredLength );
+
+ if (LocalOwner == NULL) {
+ SepReleaseTokenReadLock( Token );
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ //
+ // Return the owner SID
+ //
+
+ PSid = (PSID)((ULONG)LocalOwner +
+ (ULONG)sizeof(TOKEN_OWNER));
+
+ LocalOwner->Owner = PSid;
+
+ Status = RtlCopySid(
+ (RequiredLength - (ULONG)sizeof(TOKEN_OWNER)),
+ PSid,
+ Token->UserAndGroups[Token->DefaultOwnerIndex].Sid
+ );
+
+ ASSERT( NT_SUCCESS(Status) );
+
+ SepReleaseTokenReadLock( Token );
+ return STATUS_SUCCESS;
+ }
+
+
+ case TokenPrimaryGroup:
+ {
+ PTOKEN_PRIMARY_GROUP LocalPrimaryGroup;
+
+ LocalPrimaryGroup = (PTOKEN_PRIMARY_GROUP)(*TokenInformation);
+
+ //
+ // Gain exclusive access to the token to prevent changes
+ // from occuring to the owner.
+ //
+
+ SepAcquireTokenReadLock( Token );
+
+ //
+ // Return the length required now in case not enough buffer
+ // was provided by the caller and we have to return an error.
+ //
+
+ RequiredLength = (ULONG)sizeof(TOKEN_PRIMARY_GROUP) +
+ SeLengthSid( Token->PrimaryGroup );
+
+ LocalPrimaryGroup = ExAllocatePool( PagedPool, RequiredLength );
+
+ if (LocalPrimaryGroup == NULL) {
+ SepReleaseTokenReadLock( Token );
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ //
+ // Return the primary group SID
+ //
+
+ PSid = (PSID)((ULONG)LocalPrimaryGroup +
+ (ULONG)sizeof(TOKEN_PRIMARY_GROUP));
+
+ LocalPrimaryGroup->PrimaryGroup = PSid;
+
+ Status = RtlCopySid( (RequiredLength - (ULONG)sizeof(TOKEN_PRIMARY_GROUP)),
+ PSid,
+ Token->PrimaryGroup
+ );
+
+ ASSERT( NT_SUCCESS(Status) );
+
+ SepReleaseTokenReadLock( Token );
+ return STATUS_SUCCESS;
+ }
+
+
+ case TokenDefaultDacl:
+ {
+ PTOKEN_DEFAULT_DACL LocalDefaultDacl;
+
+ LocalDefaultDacl = (PTOKEN_DEFAULT_DACL)(*TokenInformation);
+
+ //
+ // Gain exclusive access to the token to prevent changes
+ // from occuring to the owner.
+ //
+
+ SepAcquireTokenReadLock( Token );
+
+ //
+ // Return the length required now in case not enough buffer
+ // was provided by the caller and we have to return an error.
+ //
+
+ RequiredLength = (ULONG)sizeof(TOKEN_DEFAULT_DACL);
+
+ if (ARGUMENT_PRESENT(Token->DefaultDacl)) {
+
+ RequiredLength += Token->DefaultDacl->AclSize;
+ }
+
+ LocalDefaultDacl = ExAllocatePool( PagedPool, RequiredLength );
+
+ if (LocalDefaultDacl == NULL) {
+ SepReleaseTokenReadLock( Token );
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ //
+ // Return the default Dacl
+ //
+
+ PAcl = (PACL)((ULONG)LocalDefaultDacl +
+ (ULONG)sizeof(TOKEN_DEFAULT_DACL));
+
+ if (ARGUMENT_PRESENT(Token->DefaultDacl)) {
+
+ LocalDefaultDacl->DefaultDacl = PAcl;
+
+ RtlMoveMemory( (PVOID)PAcl,
+ (PVOID)Token->DefaultDacl,
+ Token->DefaultDacl->AclSize
+ );
+ } else {
+
+ LocalDefaultDacl->DefaultDacl = NULL;
+ }
+
+ SepReleaseTokenReadLock( Token );
+ return STATUS_SUCCESS;
+ }
+
+
+ case TokenSource:
+ {
+ PTOKEN_SOURCE LocalSource;
+
+ LocalSource = (PTOKEN_SOURCE)(*TokenInformation);
+
+ //
+ // The type of a token can not be changed, so
+ // exclusive access to the token is not necessary.
+ //
+
+ //
+ // Return the length required now in case not enough buffer
+ // was provided by the caller and we have to return an error.
+ //
+
+ RequiredLength = (ULONG) sizeof(TOKEN_SOURCE);
+
+ LocalSource = ExAllocatePool( PagedPool, RequiredLength );
+
+ if (LocalSource == NULL) {
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ //
+ // Return the token source
+ //
+
+ (*LocalSource) = Token->TokenSource;
+
+ return STATUS_SUCCESS;
+ }
+
+
+ case TokenType:
+ {
+ PTOKEN_TYPE LocalType;
+
+ LocalType = (PTOKEN_TYPE)(*TokenInformation);
+
+ //
+ // The type of a token can not be changed, so
+ // exclusive access to the token is not necessary.
+ //
+
+ //
+ // Return the length required now in case not enough buffer
+ // was provided by the caller and we have to return an error.
+ //
+
+ RequiredLength = (ULONG) sizeof(TOKEN_TYPE);
+
+ LocalType = ExAllocatePool( PagedPool, RequiredLength );
+
+ if (LocalType == NULL) {
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ //
+ // Return the token type
+ //
+
+ (*LocalType) = Token->TokenType;
+
+ return STATUS_SUCCESS;
+ }
+
+
+ case TokenImpersonationLevel:
+ {
+ PSECURITY_IMPERSONATION_LEVEL LocalImpersonationLevel;
+
+ LocalImpersonationLevel = (PSECURITY_IMPERSONATION_LEVEL)(*TokenInformation);
+
+ //
+ // The impersonation level of a token can not be changed, so
+ // exclusive access to the token is not necessary.
+ //
+
+ //
+ // Make sure the token is an appropriate type to be retrieving
+ // the impersonation level from.
+ //
+
+ if (Token->TokenType != TokenImpersonation) {
+
+ return STATUS_INVALID_INFO_CLASS;
+ }
+
+ //
+ // Return the length required now in case not enough buffer
+ // was provided by the caller and we have to return an error.
+ //
+
+ RequiredLength = (ULONG) sizeof(SECURITY_IMPERSONATION_LEVEL);
+
+ LocalImpersonationLevel = ExAllocatePool( PagedPool, RequiredLength );
+
+ if (LocalImpersonationLevel == NULL) {
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ //
+ // Return the impersonation level
+ //
+
+ (*LocalImpersonationLevel) = Token->ImpersonationLevel;
+
+ return STATUS_SUCCESS;
+ }
+
+
+ case TokenStatistics:
+ {
+ PTOKEN_STATISTICS LocalStatistics;
+
+ LocalStatistics = (PTOKEN_STATISTICS)(*TokenInformation);
+
+ //
+ // Gain exclusive access to the token.
+ //
+
+ SepAcquireTokenReadLock( Token );
+
+ //
+ // Return the length required now in case not enough buffer
+ // was provided by the caller and we have to return an error.
+ //
+
+ RequiredLength = (ULONG)sizeof( TOKEN_STATISTICS );
+
+ LocalStatistics = ExAllocatePool( PagedPool, RequiredLength );
+
+ if (LocalStatistics == NULL) {
+ SepReleaseTokenReadLock( Token );
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ //
+ // Return the statistics
+ //
+
+ LocalStatistics->TokenId = Token->TokenId;
+ LocalStatistics->AuthenticationId = Token->AuthenticationId;
+ LocalStatistics->ExpirationTime = Token->ExpirationTime;
+ LocalStatistics->TokenType = Token->TokenType;
+ LocalStatistics->ImpersonationLevel = Token->ImpersonationLevel;
+ LocalStatistics->DynamicCharged = Token->DynamicCharged;
+ LocalStatistics->DynamicAvailable = Token->DynamicAvailable;
+ LocalStatistics->GroupCount = Token->UserAndGroupCount-1;
+ LocalStatistics->PrivilegeCount = Token->PrivilegeCount;
+ LocalStatistics->ModifiedId = Token->ModifiedId;
+
+ SepReleaseTokenReadLock( Token );
+ return STATUS_SUCCESS;
+ }
+
+ default:
+
+ return STATUS_INVALID_INFO_CLASS;
+ }
+}
diff --git a/private/ntos/se/tokenset.c b/private/ntos/se/tokenset.c
new file mode 100644
index 000000000..0dab7dfb3
--- /dev/null
+++ b/private/ntos/se/tokenset.c
@@ -0,0 +1,798 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ Tokenset.c
+
+Abstract:
+
+ This module implements the SET function for the executive
+ token object.
+
+Author:
+
+ Jim Kelly (JimK) 15-June-1990
+
+
+Revision History:
+
+--*/
+
+#include "sep.h"
+#include "seopaque.h"
+#include "tokenp.h"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE,NtSetInformationToken)
+#pragma alloc_text(PAGE,SepFreePrimaryGroup)
+#pragma alloc_text(PAGE,SepFreeDefaultDacl)
+#pragma alloc_text(PAGE,SepAppendPrimaryGroup)
+#pragma alloc_text(PAGE,SepAppendDefaultDacl)
+#endif
+
+
+NTSTATUS
+NtSetInformationToken (
+ IN HANDLE TokenHandle,
+ IN TOKEN_INFORMATION_CLASS TokenInformationClass,
+ IN PVOID TokenInformation,
+ IN ULONG TokenInformationLength
+ )
+
+/*++
+
+
+Routine Description:
+
+ Modify information in a specified token.
+
+Arguments:
+
+ TokenHandle - Provides a handle to the token to operate on.
+
+ TokenInformationClass - The token information class being set.
+
+ TokenInformation - The buffer containing the new values for the
+ specified class of information. The buffer must be aligned
+ on at least a longword boundary. The actual structures
+ provided are dependent upon the information class specified,
+ as defined in the TokenInformationClass parameter
+ description.
+
+ TokenInformation Format By Information Class:
+
+ TokenUser => This value is not a valid value for this API.
+ The User ID may not be replaced.
+
+ TokenGroups => This value is not a valid value for this
+ API. The Group IDs may not be replaced. However, groups
+ may be enabled and disabled using NtAdjustGroupsToken().
+
+ TokenPrivileges => This value is not a valid value for
+ this API. Privilege information may not be replaced.
+ However, privileges may be explicitly enabled and disabled
+ using the NtAdjustPrivilegesToken API.
+
+ TokenOwner => TOKEN_OWNER data structure.
+ TOKEN_ADJUST_DEFAULT access is needed to replace this
+ information in a token. The owner values that may be
+ specified are restricted to the user and group IDs with an
+ attribute indicating they may be assigned as the owner of
+ objects.
+
+ TokenPrimaryGroup => TOKEN_PRIMARY_GROUP data structure.
+ TOKEN_ADJUST_DEFAULT access is needed to replace this
+ information in a token. The primary group values that may
+ be specified are restricted to be one of the group IDs
+ already in the token.
+
+ TokenDefaultDacl => TOKEN_DEFAULT_DACL data structure.
+ TOKEN_ADJUST_DEFAULT access is needed to replace this
+ information in a token. The ACL provided as a new default
+ discretionary ACL is not validated for structural
+ correctness or consistency.
+
+ TokenSource => This value is not a valid value for this
+ API. The source name and context handle may not be
+ replaced.
+
+ TokenStatistics => This value is not a valid value for this
+ API. The statistics of a token are read-only.
+
+ TokenInformationLength - Indicates the length, in bytes, of the
+ TokenInformation buffer. This is only the length of the primary
+ buffer. All extensions of the primary buffer are self describing.
+
+Return Value:
+
+ STATUS_SUCCESS - The operation was successful.
+
+ STATUS_INVALID_OWNER - The ID specified to be an owner (or
+ default owner) is not one the caller may assign as the owner
+ of an object.
+
+ STATUS_INVALID_INFO_CLASS - The specified information class is
+ not one that may be specified in this API.
+
+ STATUS_ALLOTTED_SPACE_EXCEEDED - The space allotted for storage
+ of the default discretionary access control and the primary
+ group ID is not large enough to accept the new value of one
+ of these fields.
+
+--*/
+{
+
+ KPROCESSOR_MODE PreviousMode;
+ NTSTATUS Status;
+
+ PTOKEN Token;
+
+ ULONG Index;
+ BOOLEAN Found;
+ BOOLEAN TokenModified = FALSE;
+
+ ULONG NewLength;
+ ULONG CurrentLength;
+
+ PSID CapturedOwner;
+ PSID CapturedPrimaryGroup;
+ PACL CapturedDefaultDacl;
+
+ PAGED_CODE();
+
+ //
+ // Get previous processor mode and probe input buffer if necessary.
+ //
+
+ PreviousMode = KeGetPreviousMode();
+ if (PreviousMode != KernelMode) {
+ try {
+
+ //
+ // This just probes the main part of the information buffer.
+ // Any information class-specific data hung off the primary
+ // buffer are self describing and must be probed separately
+ // below.
+ //
+
+ ProbeForRead(
+ TokenInformation,
+ TokenInformationLength,
+ sizeof(ULONG)
+ );
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ return GetExceptionCode();
+ }
+ }
+
+ //
+ // Return error if not legal class
+ //
+ if ( (TokenInformationClass != TokenOwner) &&
+ (TokenInformationClass != TokenPrimaryGroup) &&
+ (TokenInformationClass != TokenDefaultDacl) ) {
+
+ return STATUS_INVALID_INFO_CLASS;
+
+ }
+
+ //
+ // Check access rights and reference token
+ //
+
+ Status = ObReferenceObjectByHandle(
+ TokenHandle, // Handle
+ TOKEN_ADJUST_DEFAULT, // DesiredAccess
+ SepTokenObjectType, // ObjectType
+ PreviousMode, // AccessMode
+ (PVOID *)&Token, // Object
+ NULL // GrantedAccess
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ return Status;
+ }
+
+
+ //
+ // Case on information class.
+ //
+
+ switch ( TokenInformationClass ) {
+
+ case TokenOwner:
+
+ //
+ // Make sure the buffer is large enough to hold the
+ // necessary information class data structure.
+ //
+
+ if (TokenInformationLength < (ULONG)sizeof(TOKEN_OWNER)) {
+
+ ObDereferenceObject( Token );
+ return STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ //
+ // Capture and copy
+
+ try {
+
+ //
+ // Capture Owner SID
+ //
+
+ CapturedOwner = ((PTOKEN_OWNER)TokenInformation)->Owner;
+ Status = SeCaptureSid(
+ CapturedOwner,
+ PreviousMode,
+ NULL, 0,
+ PagedPool,
+ TRUE,
+ &CapturedOwner
+ );
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ ObDereferenceObject( Token );
+ return GetExceptionCode();
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ ObDereferenceObject( Token );
+ return Status;
+ }
+
+ //
+ // Gain write access to the token.
+ //
+
+ SepAcquireTokenWriteLock( Token );
+
+ //
+ // Walk through the list of user and group IDs looking
+ // for a match to the specified SID. If one is found,
+ // make sure it may be assigned as an owner. If it can,
+ // then set the index in the token's OwnerIndex field.
+ // Otherwise, return invalid owner error.
+ //
+
+ Index = 0;
+ while (Index < Token->UserAndGroupCount) {
+
+ try {
+
+ Found = RtlEqualSid(
+ CapturedOwner,
+ Token->UserAndGroups[Index].Sid
+ );
+
+ if ( Found ) {
+
+ if ( SepIdAssignableAsOwner(Token,Index) ){
+
+ Token->DefaultOwnerIndex = Index;
+ TokenModified = TRUE;
+ Status = STATUS_SUCCESS;
+
+ } else {
+
+ Status = STATUS_INVALID_OWNER;
+
+ } //endif assignable
+
+ SepReleaseTokenWriteLock( Token, TokenModified );
+ ObDereferenceObject( Token );
+ SeReleaseSid( CapturedOwner, PreviousMode, TRUE);
+ return Status;
+
+ } //endif Found
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ SepReleaseTokenWriteLock( Token, TokenModified );
+ ObDereferenceObject( Token );
+ SeReleaseSid( CapturedOwner, PreviousMode, TRUE);
+ return GetExceptionCode();
+
+ } //endtry
+
+ Index += 1;
+
+ } //endwhile
+
+ SepReleaseTokenWriteLock( Token, TokenModified );
+ ObDereferenceObject( Token );
+ SeReleaseSid( CapturedOwner, PreviousMode, TRUE);
+ return STATUS_INVALID_OWNER;
+
+ case TokenPrimaryGroup:
+
+ //
+ // Assuming everything works out, the strategy is to move everything
+ // in the Dynamic part of the token (exept the primary group) to
+ // the beginning of the dynamic part, freeing up the entire end of
+ // the dynamic part for the new primary group.
+ //
+
+ //
+ // Make sure the buffer is large enough to hold the
+ // necessary information class data structure.
+ //
+
+ if (TokenInformationLength < (ULONG)sizeof(TOKEN_PRIMARY_GROUP)) {
+
+ ObDereferenceObject( Token );
+ return STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ //
+ // Capture And Validate TOKEN_PRIMARY_GROUP and corresponding SID.
+ //
+
+ try {
+
+ CapturedPrimaryGroup =
+ ((PTOKEN_PRIMARY_GROUP)TokenInformation)->PrimaryGroup;
+
+ Status = SeCaptureSid(
+ CapturedPrimaryGroup,
+ PreviousMode,
+ NULL, 0,
+ PagedPool,
+ TRUE,
+ &CapturedPrimaryGroup
+ );
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ ObDereferenceObject( Token );
+ return GetExceptionCode();
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ ObDereferenceObject( Token );
+ return Status;
+ }
+
+ //
+ // Gain write access to the token.
+ //
+
+ SepAcquireTokenWriteLock( Token );
+
+ //
+ // See if there is enough room in the dynamic part of the token
+ // to replace the current Primary Group with the one specified.
+ //
+
+ NewLength = SeLengthSid( CapturedPrimaryGroup );
+ CurrentLength = SeLengthSid( Token->PrimaryGroup );
+
+ if (NewLength > (CurrentLength + Token->DynamicAvailable) ) {
+
+ SepReleaseTokenWriteLock( Token, TokenModified );
+ ObDereferenceObject( Token );
+ SeReleaseSid( CapturedPrimaryGroup, PreviousMode, TRUE);
+ return STATUS_ALLOTTED_SPACE_EXCEEDED;
+ }
+
+ //
+ // Free up the existing primary group
+ //
+
+ SepFreePrimaryGroup( Token );
+
+ //
+ // And put the new SID in its place
+ //
+
+ SepAppendPrimaryGroup( Token, CapturedPrimaryGroup );
+
+ TokenModified = TRUE;
+
+ //
+ // All done.
+ //
+
+ SepReleaseTokenWriteLock( Token, TokenModified );
+ ObDereferenceObject( Token );
+ SeReleaseSid( CapturedPrimaryGroup, PreviousMode, TRUE);
+ return STATUS_SUCCESS;
+
+
+ case TokenDefaultDacl:
+
+ //
+ // Assuming everything works out, the strategy is to move everything
+ // in the Dynamic part of the token (exept the default Dacl) to
+ // the beginning of the dynamic part, freeing up the entire end of
+ // the dynamic part for the new default Dacl.
+ //
+
+ //
+ // Make sure the buffer is large enough to hold the
+ // necessary information class data structure.
+ //
+
+ if (TokenInformationLength < (ULONG)sizeof(TOKEN_DEFAULT_DACL)) {
+
+ ObDereferenceObject( Token );
+ return STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ //
+ // Capture And Validate TOKEN_DEFAULT_DACL and corresponding ACL.
+ //
+
+ try {
+
+ CapturedDefaultDacl =
+ ((PTOKEN_DEFAULT_DACL)TokenInformation)->DefaultDacl;
+
+ if (ARGUMENT_PRESENT(CapturedDefaultDacl)) {
+ Status = SeCaptureAcl(
+ CapturedDefaultDacl,
+ PreviousMode,
+ NULL, 0,
+ PagedPool,
+ TRUE,
+ &CapturedDefaultDacl,
+ &NewLength
+ );
+
+ } else {
+ NewLength = 0;
+ Status = STATUS_SUCCESS;
+ }
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ ObDereferenceObject( Token );
+ return GetExceptionCode();
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ ObDereferenceObject( Token );
+ return Status;
+ }
+
+ //
+ // Gain write access to the token.
+ //
+
+ SepAcquireTokenWriteLock( Token );
+
+ //
+ // See if there is enough room in the dynamic part of the token
+ // to replace the current Default Dacl with the one specified.
+ //
+
+ if (ARGUMENT_PRESENT(Token->DefaultDacl)) {
+ CurrentLength = Token->DefaultDacl->AclSize;
+ } else {
+ CurrentLength = 0;
+ }
+
+ if (NewLength > (CurrentLength + Token->DynamicAvailable) ) {
+
+ SepReleaseTokenWriteLock( Token, TokenModified );
+ ObDereferenceObject( Token );
+ if (ARGUMENT_PRESENT(CapturedDefaultDacl)) {
+ SeReleaseAcl( CapturedDefaultDacl, PreviousMode, TRUE);
+ }
+ return STATUS_ALLOTTED_SPACE_EXCEEDED;
+ }
+
+ //
+ // Free up the existing Default Dacl
+ //
+
+ SepFreeDefaultDacl( Token );
+
+ //
+ // And put the new ACL in its place
+ //
+
+ if (ARGUMENT_PRESENT(CapturedDefaultDacl)) {
+ SepAppendDefaultDacl( Token, CapturedDefaultDacl );
+ }
+
+ TokenModified = TRUE;
+
+ //
+ // All done.
+ //
+
+ SepReleaseTokenWriteLock( Token, TokenModified );
+ ObDereferenceObject( Token );
+ if (ARGUMENT_PRESENT(CapturedDefaultDacl)) {
+ SeReleaseAcl( CapturedDefaultDacl, PreviousMode, TRUE);
+ }
+ return STATUS_SUCCESS;
+
+ } //endswitch
+
+ ASSERT( TRUE == FALSE ); // Should never reach here.
+
+}
+
+
+VOID
+SepFreePrimaryGroup(
+ IN PTOKEN Token
+ )
+
+/*++
+
+
+Routine Description:
+
+ Free up the space in the dynamic part of the token take up by the primary
+ group.
+
+ The token is assumed to be locked for write access before calling
+ this routine.
+
+Arguments:
+
+ Token - Pointer to the token.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PAGED_CODE();
+
+ //
+ // Add the size of the primary group to the DynamicAvailable field.
+ //
+
+ Token->DynamicAvailable += SeLengthSid( Token->PrimaryGroup );
+
+ //
+ // If there is a default discretionary ACL, and it is not already at the
+ // beginning of the dynamic part, move it there (remember to update the
+ // pointer to it).
+ //
+
+ if (ARGUMENT_PRESENT(Token->DefaultDacl)) {
+ if (Token->DynamicPart != (PULONG)(Token->DefaultDacl)) {
+
+ RtlMoveMemory(
+ (PVOID)(Token->DynamicPart),
+ (PVOID)(Token->DefaultDacl),
+ Token->DefaultDacl->AclSize
+ );
+
+ Token->DefaultDacl = (PACL)(Token->DynamicPart);
+
+ }
+ }
+
+ return;
+
+}
+
+
+VOID
+SepFreeDefaultDacl(
+ IN PTOKEN Token
+ )
+
+/*++
+
+
+Routine Description:
+
+ Free up the space in the dynamic part of the token take up by the default
+ discretionary access control list.
+
+ The token is assumed to be locked for write access before calling
+ this routine.
+
+Arguments:
+
+ Token - Pointer to the token.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ ULONG PrimaryGroupSize;
+
+ PAGED_CODE();
+
+ //
+ // Add the size of the Default Dacl (if there is one) to the
+ // DynamicAvailable field.
+ //
+ if (ARGUMENT_PRESENT(Token->DefaultDacl)) {
+
+ Token->DynamicAvailable += Token->DefaultDacl->AclSize;
+ Token->DefaultDacl = NULL;
+
+ }
+
+ //
+ // If it is not already at the beginning of the dynamic part, move
+ // the primary group there (remember to update the pointer to it).
+ //
+
+ if (Token->DynamicPart != (PULONG)(Token->PrimaryGroup)) {
+
+ PrimaryGroupSize = SeLengthSid( Token->PrimaryGroup );
+
+ RtlMoveMemory(
+ (PVOID)(Token->DynamicPart),
+ (PVOID)(Token->PrimaryGroup),
+ PrimaryGroupSize
+ );
+
+ Token->PrimaryGroup = (PSID)(Token->DynamicPart);
+ }
+
+ return;
+}
+
+VOID
+SepAppendPrimaryGroup(
+ IN PTOKEN Token,
+ IN PSID PSid
+ )
+
+/*++
+
+
+Routine Description:
+
+ Add a primary group SID to the available space at the end of the dynamic
+ part of the token. It is the caller's responsibility to ensure that the
+ primary group SID fits within the available space of the dynamic part of
+ the token.
+
+ The token is assumed to be locked for write access before calling
+ this routine.
+
+Arguments:
+
+ Token - Pointer to the token.
+
+ PSid - Pointer to the SID to add.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ ULONG NextFree;
+ ULONG SidSize;
+
+ PAGED_CODE();
+
+ //
+ // Add the size of the Default Dacl (if there is one) to the
+ // address of the Dynamic Part of the token to establish
+ // where the primary group should be placed.
+ //
+
+ if (ARGUMENT_PRESENT(Token->DefaultDacl)) {
+
+// ASSERT( (ULONG)(Token->DefaultDacl->AclSize) ==
+// LongAlign(Token->DefaultDacl->AclSize) );
+
+ NextFree = (ULONG)(Token->DynamicPart) +
+ Token->DefaultDacl->AclSize;
+
+ } else {
+
+ NextFree = (ULONG)(Token->DynamicPart);
+
+ }
+
+ //
+ // Now copy the primary group SID.
+ //
+
+
+ SidSize = SeLengthSid( PSid );
+
+ RtlMoveMemory(
+ (PVOID)NextFree,
+ (PVOID)PSid,
+ SidSize
+ );
+
+ Token->PrimaryGroup = (PSID)NextFree;
+
+ //
+ // And decrement the amount of the dynamic part that is available.
+ //
+
+ ASSERT( SidSize <= (Token->DynamicAvailable) );
+ Token->DynamicAvailable -= SidSize;
+
+ return;
+
+}
+
+VOID
+SepAppendDefaultDacl(
+ IN PTOKEN Token,
+ IN PACL PAcl
+ )
+
+/*++
+
+
+Routine Description:
+
+ Add a default discretionary ACL to the available space at the end of the
+ dynamic part of the token. It is the caller's responsibility to ensure
+ that the default Dacl fits within the available space of the dynamic
+ part of the token.
+
+ The token is assumed to be locked for write access before calling
+ this routine.
+
+Arguments:
+
+ Token - Pointer to the token.
+
+ PAcl - Pointer to the ACL to add.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ ULONG NextFree;
+ ULONG AclSize;
+
+ PAGED_CODE();
+
+ //
+ // Add the size of the primary group to the
+ // address of the Dynamic Part of the token to establish
+ // where the primary group should be placed.
+ //
+
+ ASSERT(ARGUMENT_PRESENT(Token->PrimaryGroup));
+
+ NextFree = (ULONG)(Token->DynamicPart) +
+ SeLengthSid(Token->PrimaryGroup);
+
+ //
+ // Now copy the default Dacl
+ //
+
+ AclSize = (ULONG)(PAcl->AclSize);
+// ASSERT(AclSize == LongAlign(AclSize));
+
+ RtlMoveMemory(
+ (PVOID)NextFree,
+ (PVOID)PAcl,
+ AclSize
+ );
+
+ Token->DefaultDacl = (PACL)NextFree;
+
+ //
+ // And decrement the amount of the dynamic part that is available.
+ //
+
+ ASSERT( AclSize <= (Token->DynamicAvailable) );
+ Token->DynamicAvailable -= AclSize;
+
+ return;
+
+}
diff --git a/private/ntos/se/tse.c b/private/ntos/se/tse.c
new file mode 100644
index 000000000..cd6a6a6e0
--- /dev/null
+++ b/private/ntos/se/tse.c
@@ -0,0 +1,579 @@
+//////////////////////////////////////////////////////////////////////////
+// WARNING - WARNING - WARNING - WARNING - WARNING - WARNING - WARNING //
+// //
+// This test file is not current with the security implementation. //
+// This file contains references to data types and APIs that do not //
+// exist. //
+// //
+//////////////////////////////////////////////////////////////////////////
+
+/*++
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ tse.c
+
+Abstract:
+
+ Test program for the SE subcomponent of the NTOS project
+
+Author:
+
+ Gary Kimura (garyki) 20-Nov-1989
+
+Revision History:
+
+--*/
+
+#include <stdio.h>
+
+#include "sep.h"
+#include <zwapi.h>
+
+BOOLEAN SeTest();
+
+#include "ttoken.c"
+
+int
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ VOID KiSystemStartup();
+
+ TestFunction = SeTest;
+ KiSystemStartup();
+ return( 0 );
+}
+
+
+BOOLEAN
+SeTest()
+{
+ BOOLEAN TestMakeSystemToken();
+ BOOLEAN TestTokenCopy();
+ BOOLEAN TestTokenSize();
+ BOOLEAN TestDefaultObjectMethod();
+ BOOLEAN TestCaptureSecurityDescriptor();
+ BOOLEAN TestAssignSecurity();
+ BOOLEAN TestAccessCheck();
+ BOOLEAN TestGenerateMessage();
+
+ DbgPrint("Start SeTest()\n");
+
+ if (!TestMakeSystemToken()) {
+ DbgPrint("TestMakeSystemToken() Error\n");
+ return FALSE;
+ }
+ if (!TestTokenCopy()) {
+ DbgPrint("TestTokenCopy() Error\n");
+ return FALSE;
+ }
+ if (!TestTokenSize()) {
+ DbgPrint("TestTokenSize() Error\n");
+ return FALSE;
+ }
+ if (!TestDefaultObjectMethod()) {
+ DbgPrint("TestDefaultObjectMethod() Error\n");
+ return FALSE;
+ }
+ if (!TestCaptureSecurityDescriptor()) {
+ DbgPrint("TestCaptureSecurityDescriptor() Error\n");
+ return FALSE;
+ }
+ if (!TestAssignSecurity()) {
+ DbgPrint("TestAssignSecurity() Error\n");
+ return FALSE;
+ }
+ if (!TestAccessCheck()) {
+ DbgPrint("TestAccessCheck() Error\n");
+ return FALSE;
+ }
+ if (!TestGenerateMessage()) {
+ DbgPrint("TestGenerateMessage() Error\n");
+ return FALSE;
+ }
+
+ DbgPrint("End SeTest()\n");
+ return TRUE;
+}
+
+
+BOOLEAN
+TestMakeSystemToken()
+{
+ PACCESS_TOKEN Token;
+
+ //
+ // Make the system token
+ //
+
+ Token = (PACCESS_TOKEN)SeMakeSystemToken( PagedPool );
+
+ //
+ // and check its contents
+ //
+
+ if (!SepIsSystemToken( Token, ((ACCESS_TOKEN *)Token)->Size )) {
+ DbgPrint("SepIsSystemToken Error\n");
+ return FALSE;
+ }
+
+ ExFreePool( Token );
+
+ return TRUE;
+}
+
+
+BOOLEAN
+TestTokenCopy()
+{
+ PACCESS_TOKEN InToken;
+ PACCESS_TOKEN OutToken;
+
+ NTSTATUS Status;
+
+ ULONG i;
+
+ //
+ // Allocate Buffers, and build a token
+ //
+
+ InToken = (PACCESS_TOKEN)ExAllocatePool(PagedPool, 512);
+ OutToken = (PACCESS_TOKEN)ExAllocatePool(PagedPool, 512);
+
+ BuildToken( Fred, InToken );
+
+ //
+ // Make a copy of the token
+ //
+
+ if (!NT_SUCCESS(Status = SeTokenCopy( InToken, OutToken, 512))) {
+ DbgPrint("SeTokenCopy Error: %8lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // check both tokens for equality
+ //
+
+ for (i = 0; i < ((ACCESS_TOKEN *)InToken)->Size; i += 1) {
+ if (*((PUCHAR)InToken + 1) != *((PUCHAR)OutToken + 1)) {
+ DbgPrint("Token copy error\n");
+ return FALSE;
+ }
+ }
+
+ ExFreePool( InToken );
+ ExFreePool( OutToken );
+
+ return TRUE;
+}
+
+
+BOOLEAN
+TestTokenSize()
+{
+ PACCESS_TOKEN Token;
+
+ //
+ // Allocate and build a token
+ //
+
+ Token = (PACCESS_TOKEN)ExAllocatePool(PagedPool, 512);
+
+ BuildToken( Wilma, Token );
+
+ //
+ // Get the token size
+ //
+
+ if (SeTokenSize(Token) != ((ACCESS_TOKEN *)Token)->Size) {
+ DbgPrint("SeTokenSize error\n");
+ return FALSE;
+ }
+
+ ExFreePool( Token );
+
+ return TRUE;
+}
+
+
+BOOLEAN
+TestDefaultObjectMethod()
+{
+ SECURITY_DESCRIPTOR SecurityDescriptor;
+ PSECURITY_DESCRIPTOR Buffer;
+ PACL Acl;
+ NTSTATUS Status;
+ PSECURITY_DESCRIPTOR ObjectsSecurityDescriptor;
+ ULONG Length;
+
+ Acl = (PACL)ExAllocatePool( PagedPool, 1024 );
+ Buffer = (PSECURITY_DESCRIPTOR)ExAllocatePool( PagedPool, 1024 );
+
+ BuildAcl( Fred, Acl, 1024 );
+ DiscretionarySecurityDescriptor( &SecurityDescriptor, Acl );
+
+ ObjectsSecurityDescriptor = NULL;
+
+ //
+ // Set the descriptor
+ //
+
+ if (!NT_SUCCESS(Status = SeDefaultObjectMethod( NULL,
+ SetSecurityDescriptor,
+ &SecurityDescriptor,
+ 0,
+ NULL,
+ &ObjectsSecurityDescriptor,
+ PagedPool ))) {
+
+ DbgPrint("SeDefaultObjectMethod setting error: %8lx\n", Status );
+ return FALSE;
+ }
+
+ //
+ // Query the descriptor
+ //
+
+ if (!NT_SUCCESS(Status = SeDefaultObjectMethod( NULL,
+ QuerySecurityDescriptor,
+ Buffer,
+ AllAclInformation,
+ &Length,
+ &ObjectsSecurityDescriptor,
+ PagedPool ))) {
+
+ DbgPrint("SeDefaultObjectMethod reading error: %8lx\n", Status );
+ return FALSE;
+ }
+
+ //
+ // Replace the descriptor
+ //
+
+ BuildAcl( Wilma, Acl, 1024 );
+
+ if (!NT_SUCCESS(Status = SeDefaultObjectMethod( NULL,
+ SetSecurityDescriptor,
+ &SecurityDescriptor,
+ AllAclInformation,
+ &Length,
+ &ObjectsSecurityDescriptor,
+ PagedPool ))) {
+
+ DbgPrint("SeDefaultObjectMethod reading error: %8lx\n", Status );
+ return FALSE;
+ }
+
+ //
+ // Delete the descriptor
+ //
+
+ if (!NT_SUCCESS(Status = SeDefaultObjectMethod( NULL,
+ DeleteSecurityDescriptor,
+ NULL,
+ 0,
+ NULL,
+ &ObjectsSecurityDescriptor,
+ PagedPool ))) {
+
+ DbgPrint("SeDefaultObjectMethod deleting error: %8lx\n", Status );
+ return FALSE;
+ }
+
+ ExFreePool(Acl);
+ ExFreePool(Buffer);
+
+ return TRUE;
+}
+
+
+BOOLEAN
+TestCaptureSecurityDescriptor()
+{
+ SECURITY_DESCRIPTOR SecurityDescriptor;
+ PACL Sacl;
+ PACL Dacl;
+ PSECURITY_DESCRIPTOR NewDescriptor;
+ NTSTATUS Status;
+
+ Sacl = (PACL)ExAllocatePool( PagedPool, 1024 );
+ Dacl = (PACL)ExAllocatePool( PagedPool, 1024 );
+ BuildAcl( Pebbles, Sacl, 1024 );
+ BuildAcl( Barney, Dacl, 1024 );
+
+ DiscretionarySecurityDescriptor( &SecurityDescriptor, Dacl );
+ SecurityDescriptor.SecurityInformationClass = AllAclInformation;
+ SecurityDescriptor.SystemAcl = Sacl;
+
+ //
+ // Capture kernel mode and don't force
+ //
+
+ if (!NT_SUCCESS(Status = SeCaptureSecurityDescriptor( &SecurityDescriptor,
+ KernelMode,
+ PagedPool,
+ FALSE,
+ &NewDescriptor ))) {
+ DbgPrint("SeCapture Error, KernelMode, FALSE : %8lx\n", Status );
+ return FALSE;
+ }
+
+ //
+ // Capture kernel mode and force
+ //
+
+ if (!NT_SUCCESS(Status = SeCaptureSecurityDescriptor( &SecurityDescriptor,
+ KernelMode,
+ PagedPool,
+ TRUE,
+ &NewDescriptor ))) {
+ DbgPrint("SeCapture Error, KernelMode, TRUE : %8lx\n", Status );
+ return FALSE;
+ } else {
+ ExFreePool( NewDescriptor );
+ }
+
+ //
+ // Capture user mode
+ //
+
+ if (!NT_SUCCESS(Status = SeCaptureSecurityDescriptor( &SecurityDescriptor,
+ UserMode,
+ PagedPool,
+ TRUE,
+ &NewDescriptor ))) {
+ DbgPrint("SeCapture Error, UserMode, FALSE : %8lx\n", Status );
+ return FALSE;
+ } else {
+ ExFreePool( NewDescriptor );
+ }
+
+ return TRUE;
+}
+
+BOOLEAN
+TestAssignSecurity()
+{
+ SECURITY_DESCRIPTOR SecurityDescriptor;
+ PACL Acl;
+
+ PACCESS_TOKEN Token;
+
+ GENERIC_MAPPING GenericMapping;
+
+ PSECURITY_DESCRIPTOR NewDescriptor;
+
+ NTSTATUS Status;
+
+ Acl = (PACL)ExAllocatePool( PagedPool, 1024 );
+ BuildAcl( Fred, Acl, 1024 );
+
+ Token = (PACCESS_TOKEN)ExAllocatePool( PagedPool, 512 );
+ BuildToken( Fred, Token );
+
+ DiscretionarySecurityDescriptor( &SecurityDescriptor, Acl );
+
+ //
+ // Kernel mode, non dir, and no new
+ //
+
+ NewDescriptor = NULL;
+ if (!NT_SUCCESS(Status = SeAssignSecurity( &SecurityDescriptor,
+ &NewDescriptor,
+ FALSE,
+ Token,
+ &GenericMapping,
+ KernelMode ))) {
+ DbgPrint("SeAssign Error NoNew, NoDir, Kernel : %8lx\n", Status );
+ return FALSE;
+ }
+
+ //
+ // Kernel mode, non dir, and new
+ //
+
+ if (!NT_SUCCESS(Status = SeAssignSecurity( &SecurityDescriptor,
+ &NewDescriptor,
+ FALSE,
+ Token,
+ &GenericMapping,
+ KernelMode ))) {
+ DbgPrint("SeAssign Error New, NoDir, Kernel : %8lx\n", Status );
+ return FALSE;
+ }
+
+ //
+ // Kernel mode, dir, and no new
+ //
+
+ NewDescriptor = NULL;
+ if (!NT_SUCCESS(Status = SeAssignSecurity( &SecurityDescriptor,
+ &NewDescriptor,
+ TRUE,
+ Token,
+ &GenericMapping,
+ KernelMode ))) {
+ DbgPrint("SeAssign Error NoNew, Dir, Kernel : %8lx\n", Status );
+ return FALSE;
+ }
+
+ //
+ // Kernel mode, dir, and new
+ //
+
+ if (!NT_SUCCESS(Status = SeAssignSecurity( &SecurityDescriptor,
+ &NewDescriptor,
+ TRUE,
+ Token,
+ &GenericMapping,
+ KernelMode ))) {
+ DbgPrint("SeAssign Error New, Dir, Kernel : %8lx\n", Status );
+ return FALSE;
+ }
+
+
+ //
+ // User mode, non dir, and no new
+ //
+
+ NewDescriptor = NULL;
+ if (!NT_SUCCESS(Status = SeAssignSecurity( &SecurityDescriptor,
+ &NewDescriptor,
+ FALSE,
+ Token,
+ &GenericMapping,
+ UserMode ))) {
+ DbgPrint("SeAssign Error NoNew, NoDir, User : %8lx\n", Status );
+ return FALSE;
+ }
+
+ //
+ // User mode, non dir, and new
+ //
+
+ if (!NT_SUCCESS(Status = SeAssignSecurity( &SecurityDescriptor,
+ &NewDescriptor,
+ FALSE,
+ Token,
+ &GenericMapping,
+ UserMode ))) {
+ DbgPrint("SeAssign Error New, NoDir, User : %8lx\n", Status );
+ return FALSE;
+ }
+
+ //
+ // User mode, dir, and no new
+ //
+
+ NewDescriptor = NULL;
+ if (!NT_SUCCESS(Status = SeAssignSecurity( &SecurityDescriptor,
+ &NewDescriptor,
+ TRUE,
+ Token,
+ &GenericMapping,
+ UserMode ))) {
+ DbgPrint("SeAssign Error NoNew, Dir, User : %8lx\n", Status );
+ return FALSE;
+ }
+
+ //
+ // User mode, dir, and new
+ //
+
+ if (!NT_SUCCESS(Status = SeAssignSecurity( &SecurityDescriptor,
+ &NewDescriptor,
+ TRUE,
+ Token,
+ &GenericMapping,
+ UserMode ))) {
+ DbgPrint("SeAssign Error New, Dir, User : %8lx\n", Status );
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+BOOLEAN
+TestAccessCheck()
+{
+ SECURITY_DESCRIPTOR SecurityDescriptor;
+ PACL Acl;
+
+ PACCESS_TOKEN Token;
+
+ Acl = (PACL)ExAllocatePool( PagedPool, 1024 );
+ BuildAcl( Fred, Acl, 1024 );
+
+ Token = (PACCESS_TOKEN)ExAllocatePool( PagedPool, 512 );
+ BuildToken( Fred, Token );
+
+ DiscretionarySecurityDescriptor( &SecurityDescriptor, Acl );
+
+ //
+ // Test should be successful based on aces
+ //
+
+ if (!SeAccessCheck( &SecurityDescriptor,
+ Token,
+ 0x00000001,
+ NULL,
+ UserMode )) {
+ DbgPrint("SeAccessCheck Error should allow access\n");
+ return FALSE;
+ }
+
+ //
+ // Test should be successful based on owner
+ //
+
+ if (!SeAccessCheck( &SecurityDescriptor,
+ Token,
+ READ_CONTROL,
+ &FredGuid,
+ UserMode )) {
+ DbgPrint("SeAccessCheck Error should allow owner\n");
+ return FALSE;
+ }
+
+ //
+ // Test should be unsuccessful based on aces
+ //
+
+ if (SeAccessCheck( &SecurityDescriptor,
+ Token,
+ 0x0000000f,
+ &FredGuid,
+ UserMode )) {
+ DbgPrint("SeAccessCheck Error shouldn't allow access\n");
+ return FALSE;
+ }
+
+ //
+ // Test should be unsuccessful based on non owner
+ //
+
+ if (SeAccessCheck( &SecurityDescriptor,
+ Token,
+ READ_CONTROL,
+ &BarneyGuid,
+ UserMode )) {
+ DbgPrint("SeAccessCheck Error shouldn't allow non owner access\n");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+BOOLEAN
+TestGenerateMessage()
+{
+ return TRUE;
+}
+
diff --git a/private/ntos/se/tsecomm.c b/private/ntos/se/tsecomm.c
new file mode 100644
index 000000000..06a15dd57
--- /dev/null
+++ b/private/ntos/se/tsecomm.c
@@ -0,0 +1,136 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ tsecomm.c
+
+Abstract:
+
+ Common security definitions and routines.
+
+ This module defines macros that provide a mode-independent
+ interface for security test procedures.
+
+ The mode must be specified by defining one, but not both,
+ of:
+ _TST_USER_ (for user mode tests)
+ _TST_KERNEL_ (for kernel mode tests)
+
+Author:
+
+ Jim Kelly (JimK) 23-Mar-1990
+
+Environment:
+
+ Test of security.
+
+Revision History:
+
+--*/
+
+#ifndef _TSECOMM_
+#define _TSECOMM_
+
+
+////////////////////////////////////////////////////////////////
+// //
+// Common Definitions //
+// //
+////////////////////////////////////////////////////////////////
+
+#define SEASSERT_SUCCESS(s) { \
+ if (!NT_SUCCESS((s))) { \
+ DbgPrint("** ! Failed ! **\n"); \
+ DbgPrint("Status is: 0x%lx \n", (s)); \
+ } \
+ ASSERT(NT_SUCCESS(s)); }
+
+
+
+////////////////////////////////////////////////////////////////
+// //
+// Kernel Mode Definitions //
+// //
+////////////////////////////////////////////////////////////////
+
+#ifdef _TST_KERNEL_
+
+#define TstAllocatePool(PoolType,NumberOfBytes) \
+ (ExAllocatePool( (PoolType), (NumberOfBytes) ))
+
+#define TstDeallocatePool(Pointer, NumberOfBytes) \
+ (ExFreePool( (Pointer) ))
+
+#endif // _TST_KERNEL_
+
+
+////////////////////////////////////////////////////////////////
+// //
+// User Mode Definitions //
+// //
+////////////////////////////////////////////////////////////////
+
+
+#ifdef _TST_USER_
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+
+#include "sep.h"
+
+#define TstAllocatePool(IgnoredPoolType,NumberOfBytes) \
+ (ITstAllocatePool( (NumberOfBytes) ))
+
+#define TstDeallocatePool(Pointer, NumberOfBytes) \
+ (ITstDeallocatePool((Pointer),(NumberOfBytes) ))
+
+PVOID
+ITstAllocatePool(
+ IN ULONG NumberOfBytes
+ )
+{
+ NTSTATUS Status;
+ PVOID PoolAddress = NULL;
+ ULONG RegionSize;
+
+ RegionSize = NumberOfBytes;
+
+ Status = NtAllocateVirtualMemory( NtCurrentProcess(),
+ &PoolAddress,
+ 0,
+ &RegionSize,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+
+ return PoolAddress;
+}
+
+VOID
+ITstDeallocatePool(
+ IN PVOID Pointer,
+ IN ULONG NumberOfBytes
+ )
+{
+ NTSTATUS Status;
+ PVOID PoolAddress;
+ ULONG RegionSize;
+
+ RegionSize = NumberOfBytes;
+ PoolAddress = Pointer;
+
+ Status = NtFreeVirtualMemory( NtCurrentProcess(),
+ &PoolAddress,
+ &RegionSize,
+ MEM_DECOMMIT
+ );
+
+ return;
+}
+#endif // _TST_USER_
+
+#endif //_TSECOMM_
diff --git a/private/ntos/se/tseum.c b/private/ntos/se/tseum.c
new file mode 100644
index 000000000..0eb17ea34
--- /dev/null
+++ b/private/ntos/se/tseum.c
@@ -0,0 +1,578 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ tseum.c
+
+Abstract:
+
+ Test program for the security system
+
+Author:
+
+ Gary Kimura [GaryKi] 20-Nov-1989
+
+Revision History:
+
+ v3: robertre
+ Updated ACL_REVISION
+
+--*/
+
+#include <stdio.h>
+#include <string.h>
+
+#include <nt.h>
+#include <ntrtl.h>
+
+#include "sep.h"
+#include "ttoken.c"
+
+VOID
+RtlDumpAcl(
+ IN PACL Acl
+ );
+
+
+main(
+ int argc,
+ char *argv[],
+ char *envp[]
+ )
+{
+ HANDLE CurrentProcessHandle;
+ NTSTATUS Status;
+ ULONG i;
+ VOID SeMain();
+
+ CurrentProcessHandle = NtCurrentProcess();
+ Status = STATUS_SUCCESS;
+
+ DbgPrint( "Entering User Mode Test Program\n" );
+
+ DbgPrint( "argc: %ld\n", argc );
+ if (argv != NULL) {
+ for (i=0; i<argc; i++) {
+ DbgPrint( "argv[ %ld ]: %s\n", i, argv[ i ] );
+ }
+ }
+
+ if (envp != NULL) {
+ i = 0;
+ while (*envp) {
+ DbgPrint( "envp[ %02ld ]: %s\n", i++, *envp++ );
+ }
+ }
+
+ SeMain();
+
+ DbgPrint( "Exiting User Mode Test Program with Status = %lx\n", Status );
+
+ NtTerminateProcess( CurrentProcessHandle, Status );
+}
+
+
+VOID
+SeMain(
+ )
+{
+ BOOLEAN TestCreateAcl();
+ BOOLEAN TestQueryInformationAcl();
+ BOOLEAN TestSetInformationAcl();
+ BOOLEAN TestAddAce();
+ BOOLEAN TestDeleteAce();
+ BOOLEAN TestGetAce();
+
+ BOOLEAN TestAccessCheck();
+ BOOLEAN TestGenerateMessage();
+
+ DbgPrint("Starting User Mode Security Test\n");
+
+ if (!TestCreateAcl()) {
+ DbgPrint("TestCreateAcl Error\n");
+ return;
+ }
+ if (!TestQueryInformationAcl()) {
+ DbgPrint("TestCreateAcl Error\n");
+ return;
+ }
+ if (!TestSetInformationAcl()) {
+ DbgPrint("TestCreateAcl Error\n");
+ return;
+ }
+ if (!TestAddAce()) {
+ DbgPrint("TestCreateAcl Error\n");
+ return;
+ }
+ if (!TestDeleteAce()) {
+ DbgPrint("TestCreateAcl Error\n");
+ return;
+ }
+ if (!TestGetAce()) {
+ DbgPrint("TestCreateAcl Error\n");
+ return;
+ }
+
+ if (!TestAccessCheck()) {
+ DbgPrint("TestCreateAcl Error\n");
+ return;
+ }
+ if (!TestGenerateMessage()) {
+ DbgPrint("TestCreateAcl Error\n");
+ return;
+ }
+
+ DbgPrint("Ending User Mode Security Test\n");
+
+ return;
+}
+
+
+BOOLEAN
+TestCreateAcl()
+{
+ UCHAR Buffer[512];
+ PACL Acl;
+
+ NTSTATUS Status;
+
+ Acl = (PACL)Buffer;
+
+ //
+ // Create a good large acl
+ //
+
+ if (!NT_SUCCESS(Status = RtlCreateAcl( Acl, 512, 1))) {
+ DbgPrint("RtlCreateAcl Error large Acl : %8lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // Create a good small acl
+ //
+
+ if (!NT_SUCCESS(Status = RtlCreateAcl( Acl, sizeof(ACL), 1))) {
+ DbgPrint("RtlCreateAcl Error small Acl : %8lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // Create a too small acl
+ //
+
+ if (NT_SUCCESS(Status = RtlCreateAcl( Acl, sizeof(ACL) - 1, 1))) {
+ DbgPrint("RtlCreateAcl Error too small Acl : %8lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // Create a bad version acl
+ //
+
+ if (NT_SUCCESS(Status = RtlCreateAcl( Acl, 512, 2))) {
+ DbgPrint("RtlCreateAcl Error bad version : %8lx\n", Status);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+BOOLEAN
+TestQueryInformationAcl()
+{
+ UCHAR Buffer[512];
+ PACL Acl;
+ ACL_REVISION_INFORMATION AclRevisionInfo;
+ ACL_SIZE_INFORMATION AclSizeInfo;
+
+ NTSTATUS Status;
+
+ Acl = (PACL)Buffer;
+
+ BuildAcl( Fred, Acl, 512 );
+
+ //
+ // Query the revision information
+ //
+
+ if (!NT_SUCCESS(Status = RtlQueryInformationAcl( Acl,
+ (PVOID)&AclRevisionInfo,
+ sizeof(ACL_REVISION_INFORMATION),
+ AclRevisionInformation))) {
+ DbgPrint("RtlQueryInformationAcl revision info error : %8lx\n", Status);
+ return FALSE;
+ }
+ if (AclRevisionInfo.AclRevision != ACL_REVISION) {
+ DbgPrint("RtlAclRevision Error\n");
+ return FALSE;
+ }
+
+ //
+ // Query size information
+ //
+
+ if (!NT_SUCCESS(Status = RtlQueryInformationAcl( Acl,
+ (PVOID)&AclSizeInfo,
+ sizeof(ACL_SIZE_INFORMATION),
+ AclSizeInformation))) {
+ DbgPrint("RtlQueryInformationAcl size info Error : %8lx\n", Status);
+ return FALSE;
+ }
+ if ((AclSizeInfo.AceCount != 6) ||
+ (AclSizeInfo.AclBytesInUse != (sizeof(ACL)+6*sizeof(STANDARD_ACE))) ||
+ (AclSizeInfo.AclBytesFree != 512 - AclSizeInfo.AclBytesInUse)) {
+ DbgPrint("RtlAclSize Error\n");
+ DbgPrint("AclSizeInfo.AceCount = %8lx\n", AclSizeInfo.AceCount);
+ DbgPrint("AclSizeInfo.AclBytesInUse = %8lx\n", AclSizeInfo.AclBytesInUse);
+ DbgPrint("AclSizeInfo.AclBytesFree = %8lx\n", AclSizeInfo.AclBytesFree);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+BOOLEAN
+TestSetInformationAcl()
+{
+ UCHAR Buffer[512];
+ PACL Acl;
+ ACL_REVISION_INFORMATION AclRevisionInfo;
+
+ NTSTATUS Status;
+
+ Acl = (PACL)Buffer;
+
+ BuildAcl( Fred, Acl, 512 );
+
+ //
+ // Set the revision information to the current revision level
+ //
+
+ AclRevisionInfo.AclRevision = ACL_REVISION;
+ if (!NT_SUCCESS(Status = RtlSetInformationAcl( Acl,
+ (PVOID)&AclRevisionInfo,
+ sizeof(ACL_REVISION_INFORMATION),
+ AclRevisionInformation))) {
+ DbgPrint("RtlSetInformationAcl revision info error : %8lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // Set the revision information to something wrong
+ //
+
+ AclRevisionInfo.AclRevision = ACL_REVISION+1;
+ if (NT_SUCCESS(Status = RtlSetInformationAcl( Acl,
+ (PVOID)&AclRevisionInfo,
+ sizeof(ACL_REVISION_INFORMATION),
+ AclRevisionInformation))) {
+ DbgPrint("RtlSetInformationAcl revision to wrong info error : %8lx\n", Status);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+BOOLEAN
+TestAddAce()
+{
+ UCHAR AclBuffer[512];
+ PACL Acl;
+
+ STANDARD_ACE AceList[2];
+
+ NTSTATUS Status;
+
+ Acl = (PACL)AclBuffer;
+
+ //
+ // Create a good large acl
+ //
+
+ if (!NT_SUCCESS(Status = RtlCreateAcl( Acl, 512, 1))) {
+ DbgPrint("RtlCreateAcl Error large Acl : %8lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // test add ace to add two aces to an empty acl
+ //
+
+ AceList[0].Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
+ AceList[0].Header.AceSize = sizeof(STANDARD_ACE);
+ AceList[0].Header.InheritFlags = 0;
+ AceList[0].Header.AceFlags = 0;
+ AceList[0].Mask = 0x22222222;
+ CopyGuid(&AceList[0].Guid, &FredGuid);
+
+ AceList[1].Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
+ AceList[1].Header.AceSize = sizeof(STANDARD_ACE);
+ AceList[1].Header.InheritFlags = 0;
+ AceList[1].Header.AceFlags = 0;
+ AceList[1].Mask = 0x44444444;
+ CopyGuid(&AceList[1].Guid, &WilmaGuid);
+
+ if (!NT_SUCCESS(Status = RtlAddAce( Acl,
+ 1,
+ 0,
+ AceList,
+ 2*sizeof(STANDARD_ACE)))) {
+ DbgPrint("RtlAddAce to empty acl Error : %8lx\n", Status);
+ return FALSE;
+ }
+
+// RtlDumpAcl(Acl);
+
+ //
+ // test add ace to add one to the beginning of an acl
+ //
+
+ AceList[0].Header.AceType = SYSTEM_AUDIT_ACE_TYPE;
+ AceList[0].Header.AceSize = sizeof(STANDARD_ACE);
+ AceList[0].Header.InheritFlags = 0;
+ AceList[0].Header.AceFlags = 0;
+ AceList[0].Mask = 0x11111111;
+ CopyGuid(&AceList[0].Guid, &PebblesGuid);
+
+ if (!NT_SUCCESS(Status = RtlAddAce( Acl,
+ 1,
+ 0,
+ AceList,
+ sizeof(STANDARD_ACE)))) {
+ DbgPrint("RtlAddAce to beginning of acl Error : %8lx\n", Status);
+ return FALSE;
+ }
+
+// RtlDumpAcl(Acl);
+
+ //
+ // test add ace to add one to the middle of an acl
+ //
+
+ AceList[0].Header.AceType = ACCESS_DENIED_ACE_TYPE;
+ AceList[0].Header.AceSize = sizeof(STANDARD_ACE);
+ AceList[0].Header.InheritFlags = 0;
+ AceList[0].Header.AceFlags = 0;
+ AceList[0].Mask = 0x33333333;
+ CopyGuid(&AceList[0].Guid, &DinoGuid);
+
+ if (!NT_SUCCESS(Status = RtlAddAce( Acl,
+ 1,
+ 2,
+ AceList,
+ sizeof(STANDARD_ACE)))) {
+ DbgPrint("RtlAddAce to middle of acl Error : %8lx\n", Status);
+ return FALSE;
+ }
+
+// RtlDumpAcl(Acl);
+
+ //
+ // test add ace to add one to the end of an acl
+ //
+
+ AceList[0].Header.AceType = ACCESS_DENIED_ACE_TYPE;
+ AceList[0].Header.AceSize = sizeof(STANDARD_ACE);
+ AceList[0].Header.InheritFlags = 0;
+ AceList[0].Header.AceFlags = 0;
+ AceList[0].Mask = 0x55555555;
+ CopyGuid(&AceList[0].Guid, &FlintstoneGuid);
+
+ if (!NT_SUCCESS(Status = RtlAddAce( Acl,
+ 1,
+ MAXULONG,
+ AceList,
+ sizeof(STANDARD_ACE)))) {
+ DbgPrint("RtlAddAce to end of an acl Error : %8lx\n", Status);
+ return FALSE;
+ }
+
+// RtlDumpAcl(Acl);
+
+ return TRUE;
+}
+
+
+BOOLEAN
+TestDeleteAce()
+{
+ UCHAR Buffer[512];
+ PACL Acl;
+
+ NTSTATUS Status;
+
+ Acl = (PACL)Buffer;
+
+ BuildAcl( Fred, Acl, 512 );
+
+ //
+ // test delete first ace
+ //
+
+ if (!NT_SUCCESS(Status = RtlDeleteAce(Acl, 0))) {
+ DbgPrint("RtlDeleteAce first ace Error : %8lx\n", Status);
+ return FALSE;
+ }
+
+// RtlDumpAcl(Acl);
+
+ //
+ // test delete middle ace
+ //
+
+ if (!NT_SUCCESS(Status = RtlDeleteAce(Acl, 2))) {
+ DbgPrint("RtlDeleteAce middle ace Error : %8lx\n", Status);
+ return FALSE;
+ }
+
+// RtlDumpAcl(Acl);
+
+ //
+ // test delete last ace
+ //
+
+ if (!NT_SUCCESS(Status = RtlDeleteAce(Acl, 3))) {
+ DbgPrint("RtlDeleteAce last ace Error : %8lx\n", Status);
+ return FALSE;
+ }
+
+// RtlDumpAcl(Acl);
+
+ return TRUE;
+}
+
+
+BOOLEAN
+TestGetAce()
+{
+ UCHAR Buffer[512];
+ PACL Acl;
+
+ STANDARD_ACE Ace;
+
+ NTSTATUS Status;
+
+ Acl = (PACL)Buffer;
+
+ BuildAcl( Fred, Acl, 512 );
+
+ //
+ // Get first ace
+ //
+
+ if (!NT_SUCCESS(Status = RtlGetAce(Acl, 0, (PVOID)&Ace))) {
+ DbgPrint("RtlGetAce first ace error : %8lx\n", Status);
+ return FALSE;
+ }
+
+// RtlDumpAcl(Acl);
+
+ //
+ // Get middle ace
+ //
+
+ if (!NT_SUCCESS(Status = RtlGetAce(Acl, 3, (PVOID)&Ace))) {
+ DbgPrint("RtlGetAce middle ace error : %8lx\n", Status);
+ return FALSE;
+ }
+
+// RtlDumpAcl(Acl);
+
+ //
+ // Get last ace
+ //
+
+ if (!NT_SUCCESS(Status = RtlGetAce(Acl, 5, (PVOID)&Ace))) {
+ DbgPrint("RtlGetAce last ace error : %8lx\n", Status);
+ return FALSE;
+ }
+
+// RtlDumpAcl(Acl);
+
+ return TRUE;
+}
+
+
+BOOLEAN
+TestAccessCheck()
+{
+ UCHAR AclBuffer[1024];
+ SECURITY_DESCRIPTOR SecurityDescriptor;
+ PACL Acl;
+
+ UCHAR TokenBuffer[512];
+ PACCESS_TOKEN Token;
+
+ NTSTATUS Status;
+
+ Acl = (PACL)AclBuffer;
+ BuildAcl( Fred, Acl, 1024 );
+
+ Token = (PACCESS_TOKEN)TokenBuffer;
+ BuildToken( Fred, Token );
+
+ DiscretionarySecurityDescriptor( &SecurityDescriptor, Acl );
+
+ //
+ // Test should be successful based on aces
+ //
+
+ if (!NT_SUCCESS(Status = NtAccessCheck( &SecurityDescriptor,
+ Token,
+ 0x00000001,
+ NULL ))) {
+ DbgPrint("NtAccessCheck Error should allow access : %8lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // Test should be successful based on owner
+ //
+
+ if (!NT_SUCCESS(Status = NtAccessCheck( &SecurityDescriptor,
+ Token,
+ READ_CONTROL,
+ &FredGuid ))) {
+ DbgPrint("NtAccessCheck Error should allow owner : %8lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // Test should be unsuccessful based on aces
+ //
+
+ if (NT_SUCCESS(Status = NtAccessCheck( &SecurityDescriptor,
+ Token,
+ 0x0000000f,
+ &FredGuid ))) {
+ DbgPrint("NtAccessCheck Error shouldn't allow access : %8lx\n", Status);
+ return FALSE;
+ }
+
+ //
+ // Test should be unsuccessful based on non owner
+ //
+
+ if (NT_SUCCESS(Status = NtAccessCheck( &SecurityDescriptor,
+ Token,
+ READ_CONTROL,
+ &BarneyGuid ))) {
+ DbgPrint("NtAccessCheck Error shouldn't allow non owner access : %8lx\n", Status);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+BOOLEAN
+TestGenerateMessage()
+{
+ return TRUE;
+}
+
diff --git a/private/ntos/se/tsevars.c b/private/ntos/se/tsevars.c
new file mode 100644
index 000000000..749bc5b92
--- /dev/null
+++ b/private/ntos/se/tsevars.c
@@ -0,0 +1,377 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ tsevars.c
+
+Abstract:
+
+ This Module contains variables used in security test routines.
+
+
+Author:
+
+ Jim Kelly (JimK) 23-Mar-1990
+
+Environment:
+
+ Test.
+
+Revision History:
+
+--*/
+
+#include "tsecomm.c" // Mode dependent macros and routines.
+
+
+#ifndef _TSEVARS_
+#define _TSEVARS_
+
+
+
+
+typedef enum _USERS {
+ Fred,
+ Wilma,
+ Pebbles,
+ Barney,
+ Betty,
+ Bambam,
+ Dino
+} USERS;
+
+
+
+//
+// Define the Bedrock domain and its inhabitants
+//
+// Bedrock Domain S-1-39824-21-3-17
+// Fred S-1-39824-21-3-17-2
+// Wilma S-1-39824-21-3-17-3
+// Pebbles S-1-39824-21-3-17-4
+// Dino S-1-39824-21-3-17-5
+// Barney S-1-39824-21-3-17-6
+// Betty S-1-39824-21-3-17-7
+// Bambam S-1-39824-21-3-17-8
+// Flintstone S-1-39824-21-3-17-9
+// Rubble S-1-39824-21-3-17-10
+// Adult S-1-39824-21-3-17-11
+// Child S-1-39824-21-3-17-12
+// Neanderthol S-1-39824-21-3-17-13
+//
+
+#define BEDROCK_AUTHORITY {0,0,0,0,155,144}
+#define BEDROCK_SUBAUTHORITY_0 0x00000015L
+#define BEDROCK_SUBAUTHORITY_1 0x00000003L
+#define BEDROCK_SUBAUTHORITY_2 0x00000011L
+
+#define FRED_RID 0x00000002L
+#define WILMA_RID 0x00000003L
+#define PEBBLES_RID 0x00000004L
+#define DINO_RID 0x00000005L
+
+#define BARNEY_RID 0x00000006L
+#define BETTY_RID 0x00000007L
+#define BAMBAM_RID 0x00000008L
+
+#define FLINTSTONE_RID 0x00000009L
+#define RUBBLE_RID 0x0000000AL
+
+#define ADULT_RID 0x0000000BL
+#define CHILD_RID 0x0000000CL
+
+#define NEANDERTHOL_RID 0x0000000DL
+
+
+PSID BedrockDomainSid;
+
+
+PSID FredSid;
+PSID WilmaSid;
+PSID PebblesSid;
+PSID DinoSid;
+
+PSID BarneySid;
+PSID BettySid;
+PSID BambamSid;
+
+PSID FlintstoneSid;
+PSID RubbleSid;
+
+PSID AdultSid;
+PSID ChildSid;
+
+PSID NeandertholSid;
+
+
+//
+// Universal well known SIDs
+//
+
+PSID NullSid;
+PSID WorldSid;
+PSID LocalSid;
+PSID CreatorSid;
+
+//
+// Sids defined by NT
+//
+
+PSID NtAuthoritySid;
+
+PSID DialupSid;
+PSID NetworkSid;
+PSID BatchSid;
+PSID InteractiveSid;
+PSID LocalSystemSid;
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////
+// //
+// Define the well known privileges //
+// //
+////////////////////////////////////////////////////////////////////////
+
+
+LUID CreateTokenPrivilege;
+LUID AssignPrimaryTokenPrivilege;
+LUID LockMemoryPrivilege;
+LUID IncreaseQuotaPrivilege;
+LUID UnsolicitedInputPrivilege;
+LUID TcbPrivilege;
+LUID SecurityPrivilege;
+
+LUID TakeOwnershipPrivilege;
+LUID LpcReplyBoostPrivilege;
+LUID CreatePagefilePrivilege;
+LUID IncreaseBasePriorityPrivilege;
+LUID SystemProfilePrivilege;
+LUID SystemtimePrivilege;
+LUID ProfileSingleProcessPrivilege;
+
+LUID RestorePrivilege;
+LUID BackupPrivilege;
+LUID CreatePermanentPrivilege;
+LUID ShutdownPrivilege;
+LUID DebugPrivilege;
+
+
+
+
+
+BOOLEAN
+TSeVariableInitialization()
+/*++
+
+Routine Description:
+
+ This function initializes the global variables used in security
+ tests.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ TRUE if variables successfully initialized.
+ FALSE if not successfully initialized.
+
+--*/
+{
+ ULONG SidWithZeroSubAuthorities;
+ ULONG SidWithOneSubAuthority;
+ ULONG SidWithThreeSubAuthorities;
+ ULONG SidWithFourSubAuthorities;
+
+ SID_IDENTIFIER_AUTHORITY NullSidAuthority = SECURITY_NULL_SID_AUTHORITY;
+ SID_IDENTIFIER_AUTHORITY WorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY;
+ SID_IDENTIFIER_AUTHORITY LocalSidAuthority = SECURITY_LOCAL_SID_AUTHORITY;
+ SID_IDENTIFIER_AUTHORITY CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;
+
+ SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
+
+
+ SID_IDENTIFIER_AUTHORITY BedrockAuthority = BEDROCK_AUTHORITY;
+
+
+ //
+ // The following SID sizes need to be allocated
+ //
+
+ SidWithZeroSubAuthorities = RtlLengthRequiredSid( 0 );
+ SidWithOneSubAuthority = RtlLengthRequiredSid( 1 );
+ SidWithThreeSubAuthorities = RtlLengthRequiredSid( 3 );
+ SidWithFourSubAuthorities = RtlLengthRequiredSid( 4 );
+
+ //
+ // Allocate and initialize the universal SIDs
+ //
+
+ NullSid = (PSID)TstAllocatePool(PagedPool,SidWithOneSubAuthority);
+ WorldSid = (PSID)TstAllocatePool(PagedPool,SidWithOneSubAuthority);
+ LocalSid = (PSID)TstAllocatePool(PagedPool,SidWithOneSubAuthority);
+ CreatorSid = (PSID)TstAllocatePool(PagedPool,SidWithOneSubAuthority);
+
+ RtlInitializeSid( NullSid, &NullSidAuthority, 1 );
+ RtlInitializeSid( WorldSid, &WorldSidAuthority, 1 );
+ RtlInitializeSid( LocalSid, &LocalSidAuthority, 1 );
+ RtlInitializeSid( CreatorSid, &CreatorSidAuthority, 1 );
+
+ *(RtlSubAuthoritySid( NullSid, 0 )) = SECURITY_NULL_RID;
+ *(RtlSubAuthoritySid( WorldSid, 0 )) = SECURITY_WORLD_RID;
+ *(RtlSubAuthoritySid( LocalSid, 0 )) = SECURITY_LOCAL_RID;
+ *(RtlSubAuthoritySid( CreatorSid, 0 )) = SECURITY_CREATOR_OWNER_RID;
+
+ //
+ // Allocate and initialize the NT defined SIDs
+ //
+
+ NtAuthoritySid = (PSID)TstAllocatePool(PagedPool,SidWithZeroSubAuthorities);
+ DialupSid = (PSID)TstAllocatePool(PagedPool,SidWithOneSubAuthority);
+ NetworkSid = (PSID)TstAllocatePool(PagedPool,SidWithOneSubAuthority);
+ BatchSid = (PSID)TstAllocatePool(PagedPool,SidWithOneSubAuthority);
+ InteractiveSid = (PSID)TstAllocatePool(PagedPool,SidWithOneSubAuthority);
+ LocalSystemSid = (PSID)TstAllocatePool(PagedPool,SidWithOneSubAuthority);
+
+ RtlInitializeSid( NtAuthoritySid, &NtAuthority, 0 );
+ RtlInitializeSid( DialupSid, &NtAuthority, 1 );
+ RtlInitializeSid( NetworkSid, &NtAuthority, 1 );
+ RtlInitializeSid( BatchSid, &NtAuthority, 1 );
+ RtlInitializeSid( InteractiveSid, &NtAuthority, 1 );
+ RtlInitializeSid( LocalSystemSid, &NtAuthority, 1 );
+
+ *(RtlSubAuthoritySid( DialupSid, 0 )) = SECURITY_DIALUP_RID;
+ *(RtlSubAuthoritySid( NetworkSid, 0 )) = SECURITY_NETWORK_RID;
+ *(RtlSubAuthoritySid( BatchSid, 0 )) = SECURITY_BATCH_RID;
+ *(RtlSubAuthoritySid( InteractiveSid, 0 )) = SECURITY_INTERACTIVE_RID;
+ *(RtlSubAuthoritySid( LocalSystemSid, 0 )) = SECURITY_LOCAL_SYSTEM_RID;
+
+
+
+ //
+ // Allocate and initialize the Bedrock SIDs
+ //
+
+ BedrockDomainSid = (PSID)TstAllocatePool(PagedPool,SidWithThreeSubAuthorities);
+
+ FredSid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+ WilmaSid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+ PebblesSid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+ DinoSid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+
+ BarneySid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+ BettySid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+ BambamSid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+
+ FlintstoneSid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+ RubbleSid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+
+ AdultSid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+ ChildSid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+
+ NeandertholSid = (PSID)TstAllocatePool(PagedPool,SidWithFourSubAuthorities);
+
+ RtlInitializeSid( BedrockDomainSid, &BedrockAuthority, 3 );
+ *(RtlSubAuthoritySid( BedrockDomainSid, 0)) = BEDROCK_SUBAUTHORITY_0;
+ *(RtlSubAuthoritySid( BedrockDomainSid, 1)) = BEDROCK_SUBAUTHORITY_1;
+ *(RtlSubAuthoritySid( BedrockDomainSid, 2)) = BEDROCK_SUBAUTHORITY_2;
+
+ RtlCopySid( SidWithFourSubAuthorities, FredSid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( FredSid )) += 1;
+ *(RtlSubAuthoritySid( FredSid, 3)) = FRED_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, WilmaSid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( WilmaSid )) += 1;
+ *(RtlSubAuthoritySid( WilmaSid, 3)) = WILMA_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, PebblesSid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( PebblesSid )) += 1;
+ *(RtlSubAuthoritySid( PebblesSid, 3)) = PEBBLES_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, DinoSid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( DinoSid )) += 1;
+ *(RtlSubAuthoritySid( DinoSid, 3)) = DINO_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, BarneySid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( BarneySid )) += 1;
+ *(RtlSubAuthoritySid( BarneySid, 3)) = BARNEY_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, BettySid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( BettySid )) += 1;
+ *(RtlSubAuthoritySid( BettySid, 3)) = BETTY_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, BambamSid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( BambamSid )) += 1;
+ *(RtlSubAuthoritySid( BambamSid, 3)) = BAMBAM_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, FlintstoneSid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( FlintstoneSid )) += 1;
+ *(RtlSubAuthoritySid( FlintstoneSid, 3)) = FLINTSTONE_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, RubbleSid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( RubbleSid )) += 1;
+ *(RtlSubAuthoritySid( RubbleSid, 3)) = RUBBLE_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, AdultSid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( AdultSid )) += 1;
+ *(RtlSubAuthoritySid( AdultSid, 3)) = ADULT_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, ChildSid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( ChildSid )) += 1;
+ *(RtlSubAuthoritySid( ChildSid, 3)) = CHILD_RID;
+
+ RtlCopySid( SidWithFourSubAuthorities, NeandertholSid, BedrockDomainSid);
+ *(RtlSubAuthorityCountSid( NeandertholSid )) += 1;
+ *(RtlSubAuthoritySid( NeandertholSid, 3)) = NEANDERTHOL_RID;
+
+
+ CreateTokenPrivilege =
+ RtlConvertLongToLargeInteger(SE_CREATE_TOKEN_PRIVILEGE);
+ AssignPrimaryTokenPrivilege =
+ RtlConvertLongToLargeInteger(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE);
+ LockMemoryPrivilege =
+ RtlConvertLongToLargeInteger(SE_LOCK_MEMORY_PRIVILEGE);
+ IncreaseQuotaPrivilege =
+ RtlConvertLongToLargeInteger(SE_INCREASE_QUOTA_PRIVILEGE);
+ UnsolicitedInputPrivilege =
+ RtlConvertLongToLargeInteger(SE_UNSOLICITED_INPUT_PRIVILEGE);
+ TcbPrivilege =
+ RtlConvertLongToLargeInteger(SE_TCB_PRIVILEGE);
+ SecurityPrivilege =
+ RtlConvertLongToLargeInteger(SE_SECURITY_PRIVILEGE);
+ TakeOwnershipPrivilege =
+ RtlConvertLongToLargeInteger(SE_TAKE_OWNERSHIP_PRIVILEGE);
+ LpcReplyBoostPrivilege =
+ RtlConvertLongToLargeInteger(SE_LPC_REPLY_BOOST_PRIVILEGE);
+ CreatePagefilePrivilege =
+ RtlConvertLongToLargeInteger(SE_CREATE_PAGEFILE_PRIVILEGE);
+ IncreaseBasePriorityPrivilege =
+ RtlConvertLongToLargeInteger(SE_INC_BASE_PRIORITY_PRIVILEGE);
+ SystemProfilePrivilege =
+ RtlConvertLongToLargeInteger(SE_SYSTEM_PROFILE_PRIVILEGE);
+ SystemtimePrivilege =
+ RtlConvertLongToLargeInteger(SE_SYSTEMTIME_PRIVILEGE);
+ ProfileSingleProcessPrivilege =
+ RtlConvertLongToLargeInteger(SE_PROF_SINGLE_PROCESS_PRIVILEGE);
+ CreatePermanentPrivilege =
+ RtlConvertLongToLargeInteger(SE_CREATE_PERMANENT_PRIVILEGE);
+ BackupPrivilege =
+ RtlConvertLongToLargeInteger(SE_BACKUP_PRIVILEGE);
+ RestorePrivilege =
+ RtlConvertLongToLargeInteger(SE_RESTORE_PRIVILEGE);
+ ShutdownPrivilege =
+ RtlConvertLongToLargeInteger(SE_SHUTDOWN_PRIVILEGE);
+ DebugPrivilege =
+ RtlConvertLongToLargeInteger(SE_DEBUG_PRIVILEGE);
+
+
+ return TRUE;
+
+}
+#endif // _TSEVARS_
diff --git a/private/ntos/se/ttokend.c b/private/ntos/se/ttokend.c
new file mode 100644
index 000000000..523b9ffe1
--- /dev/null
+++ b/private/ntos/se/ttokend.c
@@ -0,0 +1,12339 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ tmachine.c
+
+Abstract:
+
+ This module tests token duplication.
+
+Author:
+
+ Jim Kelly (JimK) 8-Feb-1994
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+
+--*/
+
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Includes //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+#include <stdio.h>
+#include <nt.h>
+#include <ntsam.h>
+#include <ntsamp.h>
+#include <ntlsa.h>
+#include <ntrpcp.h> // prototypes for MIDL user functions
+#include <seopaque.h>
+#include <string.h>
+
+
+
+#ifdef NOT_PART_OF_PROGRAM
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Definitions //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+
+
+
+
+#define TMPP_USER_NAME_ADMIN "Administrator"
+#define TMPP_USER_NAME_GUEST "Guest"
+#define TMPP_GROUP_NAME_ADMINS "Domain Admins"
+#define TMPP_GROUP_NAME_USERS "Domain Users"
+#define TMPP_GROUP_NAME_NONE "None"
+#define TMPP_ALIAS_NAME_ADMINS "Administrators"
+#define TMPP_ALIAS_NAME_SYSTEM_OPS "System Operators"
+#define TMPP_ALIAS_NAME_POWER_USERS "Power Users"
+#define TMPP_ALIAS_NAME_USERS "Users"
+#define TMPP_ALIAS_NAME_GUESTS "Guests"
+#define TMPP_ALIAS_NAME_ACCOUNT_OPS "Account Operators"
+#define TMPP_ALIAS_NAME_PRINT_OPS "Print Operators"
+#define TMPP_ALIAS_NAME_BACKUP_OPS "Backup Operators"
+
+
+
+#define GROUP_NAME1 "GROUP1"
+#define ALIAS_NAME1 "ALIAS1"
+#define ALIAS_NAME2 "ALIAS2"
+#define USER_NAME1 "USER1"
+#define USER_NAME2 "USER2"
+#define USER_NAME3 "USER3"
+
+// Keep these names not longer than 8 char's until long registry names supported
+#define DUMMY_NAME1 "DName1"
+#define DUMMY_NAME2 "2emaNuD"
+
+#define DUMMY_STRING1 "This is test string 1"
+#define DUMMY_STRING2 "Test String2 - test string 2 - tEST sTRING 2"
+
+#define ALL_NAMES_COUNT (3)
+#define SOME_NAMES_COUNT (7)
+#define NO_NAMES_COUNT (2)
+
+#define LOOKUP_KNOWN_NAME0 TMPP_USER_NAME_ADMIN
+#define LOOKUP_KNOWN_NAME1_A TMPP_GROUP_NAME_NONE
+#define LOOKUP_KNOWN_NAME2_A TMPP_GROUP_NAME_NONE
+#define LOOKUP_KNOWN_NAME1_P TMPP_GROUP_NAME_USERS
+#define LOOKUP_KNOWN_NAME2_P TMPP_GROUP_NAME_USERS
+
+#define LOOKUP_KNOWN_NAME0_RID DOMAIN_USER_RID_ADMIN
+#define LOOKUP_KNOWN_NAME1_RID DOMAIN_GROUP_RID_USERS
+#define LOOKUP_KNOWN_NAME2_RID DOMAIN_GROUP_RID_USERS
+
+#define LOOKUP_UNKNOWN_NAME0 "JoeJoe"
+#define LOOKUP_UNKNOWN_NAME1 "Tanya"
+#define LOOKUP_UNKNOWN_NAME2 "Fred"
+#define LOOKUP_UNKNOWN_NAME3 "Anyone"
+
+#define LOOKUP_KNOWN_NAME0_USE (SidTypeUser)
+#define LOOKUP_KNOWN_NAME1_USE (SidTypeGroup)
+#define LOOKUP_KNOWN_NAME2_USE (SidTypeGroup)
+
+
+//
+// This byte is expected to be different in the DummyLogonHours and
+// NoRestrictionLogonHours.
+//
+
+#define LOGON_HOURS_DIFFERENT_OFFSET (5)
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Global variables //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+LARGE_INTEGER LargeInteger1,
+ LargeInteger2;
+
+UNICODE_STRING DummyName1,
+ DummyName2,
+ DummyString1,
+ DummyString2;
+
+STRING DummyAnsiString1,
+ DummyAnsiString2;
+
+LOGON_HOURS NoLogonRestriction,
+ DummyLogonHours;
+
+CHAR NoLogonRestrictionBitMask[21],
+ DummyLogonHoursBitMask[21];
+
+
+UNICODE_STRING AllNames[ALL_NAMES_COUNT],
+ SomeNames[SOME_NAMES_COUNT],
+ NoNames[NO_NAMES_COUNT];
+
+
+SID_NAME_USE AllUses[ALL_NAMES_COUNT],
+ SomeUses[SOME_NAMES_COUNT],
+ NoUses[NO_NAMES_COUNT];
+
+ULONG AllRids[ALL_NAMES_COUNT],
+ SomeRids[SOME_NAMES_COUNT],
+ NoRids[NO_NAMES_COUNT];
+
+
+PSID BuiltinDomainSid,
+ AccountDomainSid,
+ PrimaryDomainSid,
+ WorldSid,
+ AdminsAliasSid,
+ AccountAliasSid;
+
+
+UNICODE_STRING BuiltinDomainName,
+ AccountDomainName,
+ PrimaryDomainName;
+
+BOOLEAN AccountDomainIsNotPrimaryDomain;
+
+
+//
+// These are NOT mutually exclusive
+//
+
+BOOLEAN BuiltinDomainTest, // Test the builting domain
+ SecurityOperatorTest, // Test auditing accessibility
+ AccountOpAliasTest, // Test account operator functions
+ AdminsAliasTest; // Test domain admin functions
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// private macros //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+
+//
+// VOID
+// TST_SUCCESS_ASSERT( IN NTSTATUS S );
+//
+
+#define TST_SUCCESS_ASSERT( S ) \
+{ \
+ if ( !NT_SUCCESS((S)) ) { \
+ printf("\n** SUCCESS STATUS ASSERTION FAILURE **\n"); \
+ printf(" Status is: 0x%lx\n", (S) ); \
+ ASSERT(NT_SUCCESS((S))); \
+ } \
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// private service prototypes //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+
+
+
+BOOLEAN
+TInitialize( VOID );
+
+BOOLEAN
+EnableSecurityPrivilege( VOID );
+
+VOID
+DetermineTestsToRun( VOID );
+
+VOID
+SeeIfSidIsSpecial(
+ IN PSID Sid
+ );
+
+BOOLEAN
+ServerTestSuite(
+ PHANDLE ServerHandle,
+ PHANDLE DomainHandle,
+ PHANDLE BuiltinDomainHandle,
+ PSID *DomainSid
+ );
+
+BOOLEAN
+SecurityTestSuite(
+ HANDLE ServerHandle,
+ HANDLE DomainHandle,
+ ULONG Pass
+ );
+
+BOOLEAN
+CheckReturnedSD(
+ IN SECURITY_INFORMATION SI,
+ IN PSECURITY_DESCRIPTOR SD,
+ IN BOOLEAN PrintTestSuccess
+ );
+
+
+BOOLEAN
+DomainTestSuite(
+ HANDLE DomainHandle
+ );
+
+BOOLEAN
+GroupTestSuite(
+ HANDLE DomainHandle,
+ ULONG Pass
+ );
+
+BOOLEAN
+AliasTestSuite(
+ HANDLE DomainHandle,
+ HANDLE BuiltinDomainHandle,
+ PSID DomainSid,
+ ULONG Pass
+ );
+
+BOOLEAN
+UserTestSuite(
+ HANDLE DomainHandle,
+ ULONG Pass
+ );
+
+
+NTSTATUS
+SampSetDomainPolicy( VOID );
+
+
+NTSTATUS
+SampGetLsaDomainInfo(
+ PPOLICY_ACCOUNT_DOMAIN_INFO *PolicyAccountDomainInfo,
+ PPOLICY_PRIMARY_DOMAIN_INFO *PolicyPrimaryDomainInfo
+ );
+
+
+//
+// The following are in WRAPPERS.C, but are prototyped here since this
+// test is the only thing that should ever call them.
+//
+
+NTSTATUS
+SamTestPrivateFunctionsDomain(
+ IN HANDLE DomainHandle
+ );
+
+NTSTATUS
+SamTestPrivateFunctionsUser(
+ IN HANDLE UserHandle
+ );
+
+
+#endif // NOT_PART_OF_PROGRAM
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Routines //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+
+
+VOID
+main (argc, argv)
+int argc;
+char **argv;
+
+/*++
+
+Routine Description:
+
+ This is the main entry routine for this test.
+
+Arguments:
+
+ NONE
+
+
+Return Value:
+
+
+
+
+--*/
+{
+ NTSTATUS
+ NtStatus;
+
+ HANDLE
+ h1, h2, h3;
+
+ OBJECT_ATTRIBUTES
+ ObjectAttributes;
+
+ SECURITY_QUALITY_OF_SERVICE
+ Qos;
+
+ //
+ // Duplicate our primary token to get an impersonation token.
+ // (no security QOS causes duplicate to have Anonymous level)
+ //
+
+ NtStatus = NtOpenProcessToken( NtCurrentProcess(),
+ TOKEN_DUPLICATE,
+ &h1);
+ printf("Test: Open Process Token: 0x%lx\n", NtStatus);
+
+ InitializeObjectAttributes( &ObjectAttributes, NULL, 0, 0, NULL );
+ NtStatus = NtDuplicateToken( h1,
+ TOKEN_DUPLICATE,
+ &ObjectAttributes,
+ FALSE, // EffectiveOnly
+ TokenImpersonation,
+ &h2);
+ printf("Test: Duplicate Primary to anonymous Impersonation: 0x%lx\n", NtStatus);
+
+
+ //
+ // Now duplicate that to get a primary
+ //
+ NtStatus = NtDuplicateToken( h2,
+ TOKEN_DUPLICATE,
+ &ObjectAttributes,
+ FALSE, // EffectiveOnly
+ TokenPrimary,
+ &h3);
+ printf("Test: Duplicate anonymous Impersonation to Primary: 0x%lx\n", NtStatus);
+
+ //
+ // Now try it again with Impersonate level.
+ //
+
+ Qos.Length = sizeof(Qos);
+ Qos.ImpersonationLevel = SecurityImpersonation;
+ Qos.ContextTrackingMode = SECURITY_STATIC_TRACKING;
+ Qos.EffectiveOnly = FALSE;
+
+ InitializeObjectAttributes( &ObjectAttributes, NULL, 0, 0, NULL );
+ ObjectAttributes.SecurityQualityOfService = &Qos;
+
+ NtStatus = NtDuplicateToken( h1,
+ TOKEN_DUPLICATE,
+ &ObjectAttributes,
+ FALSE, // EffectiveOnly
+ TokenImpersonation,
+ &h2);
+ printf("Test: Duplicate Primary to IMPERSONATE Impersonation: 0x%lx\n", NtStatus);
+
+
+ //
+ // Now duplicate that to get a primary
+ //
+ NtStatus = NtDuplicateToken( h2,
+ TOKEN_DUPLICATE,
+ &ObjectAttributes,
+ FALSE, // EffectiveOnly
+ TokenPrimary,
+ &h3);
+ printf("Test: Duplicate IMPERSONATE Impersonation to Primary: 0x%lx\n", NtStatus);
+
+ return;
+}
+
+#ifdef NOT_PART_OF_PROGRAM
+
+BOOLEAN
+TInitialize (
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Initialize test variables, et cetera.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+
+ Note:
+
+
+--*/
+{
+ NTSTATUS NtStatus;
+ STRING Name;
+ ULONG i;
+
+ SID_IDENTIFIER_AUTHORITY WorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY;
+ SID_IDENTIFIER_AUTHORITY DomainSidAuthority = {0,0,0,0,0,0};
+ SID_IDENTIFIER_AUTHORITY BuiltinAuthority = SECURITY_NT_AUTHORITY;
+
+
+ //
+ // Get the domain SIDs from the policy database...
+ //
+
+ NtStatus = SampSetDomainPolicy();
+ ASSERT(NT_SUCCESS(NtStatus));
+
+
+ //
+ // A random large integer value..
+ //
+
+ LargeInteger1.LowPart = 1234;
+ LargeInteger1.HighPart = 0;
+
+ LargeInteger2.LowPart = 4321;
+ LargeInteger2.HighPart = 0;
+
+
+ RtlInitString( &Name, DUMMY_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &DummyName1, &Name, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ RtlInitString( &Name, DUMMY_NAME2 );
+ NtStatus = RtlAnsiStringToUnicodeString( &DummyName2, &Name, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+
+ RtlInitString( &DummyAnsiString1, DUMMY_STRING1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &DummyString1, &DummyAnsiString1, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ RtlInitString( &DummyAnsiString2, DUMMY_STRING2 );
+ NtStatus = RtlAnsiStringToUnicodeString( &DummyString2, &DummyAnsiString2, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+
+ DummyLogonHours.UnitsPerWeek = SAM_HOURS_PER_WEEK;
+ DummyLogonHours.LogonHours = &DummyLogonHoursBitMask[0];
+ DummyLogonHoursBitMask[LOGON_HOURS_DIFFERENT_OFFSET] = 103; // Any non-zero value
+
+ NoLogonRestriction.UnitsPerWeek = SAM_HOURS_PER_WEEK;
+ NoLogonRestriction.LogonHours = &NoLogonRestrictionBitMask[0];
+ for ( i=0; i<(ULONG)((NoLogonRestriction.UnitsPerWeek+7)/8); i++) {
+ NoLogonRestrictionBitMask[0] = 0;
+ }
+
+
+
+ //
+ // Initialize some SIDs
+ //
+
+
+ WorldSid = RtlAllocateHeap( RtlProcessHeap(), 0, RtlLengthRequiredSid(1) );
+ ASSERT(WorldSid != NULL);
+ RtlInitializeSid( WorldSid, &WorldSidAuthority, 1 );
+ *(RtlSubAuthoritySid( WorldSid, 0 )) = SECURITY_WORLD_RID;
+
+ AdminsAliasSid = RtlAllocateHeap(RtlProcessHeap(), 0,RtlLengthRequiredSid( 2 ));
+ ASSERT(AdminsAliasSid != NULL);
+ RtlInitializeSid( AdminsAliasSid, &BuiltinAuthority, 2 );
+ *(RtlSubAuthoritySid( AdminsAliasSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID;
+ *(RtlSubAuthoritySid( AdminsAliasSid, 1 )) = DOMAIN_ALIAS_RID_ADMINS;
+
+ AccountAliasSid = RtlAllocateHeap(RtlProcessHeap(), 0,RtlLengthRequiredSid( 2 ));
+ ASSERT(AccountAliasSid != NULL);
+ RtlInitializeSid( AccountAliasSid, &BuiltinAuthority, 2 );
+ *(RtlSubAuthoritySid( AccountAliasSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID;
+ *(RtlSubAuthoritySid( AccountAliasSid, 1 )) = DOMAIN_ALIAS_RID_ACCOUNT_OPS;
+
+
+
+
+ //
+ // Initialize some stuff for SID and NAME lookup operations
+ //
+
+ RtlInitString( &Name, LOOKUP_KNOWN_NAME0 );
+
+ AllUses[0] = LOOKUP_KNOWN_NAME0_USE; AllRids[0] = LOOKUP_KNOWN_NAME0_RID;
+ NtStatus = RtlAnsiStringToUnicodeString( &AllNames[0], &Name, TRUE ); TST_SUCCESS_ASSERT(NtStatus);
+ SomeUses[0] = LOOKUP_KNOWN_NAME0_USE; SomeRids[0] = LOOKUP_KNOWN_NAME0_RID;
+ NtStatus = RtlAnsiStringToUnicodeString( &SomeNames[0], &Name, TRUE ); TST_SUCCESS_ASSERT(NtStatus);
+
+
+ if (AccountDomainIsNotPrimaryDomain == TRUE) {
+ RtlInitString( &Name, LOOKUP_KNOWN_NAME1_A );
+ } else {
+ RtlInitString( &Name, LOOKUP_KNOWN_NAME1_P );
+ }
+ AllUses[1] = LOOKUP_KNOWN_NAME1_USE; AllRids[1] = LOOKUP_KNOWN_NAME1_RID;
+ NtStatus = RtlAnsiStringToUnicodeString( &AllNames[1], &Name, TRUE ); TST_SUCCESS_ASSERT(NtStatus);
+ SomeUses[1] = LOOKUP_KNOWN_NAME1_USE; SomeRids[1] = LOOKUP_KNOWN_NAME1_RID;
+ NtStatus = RtlAnsiStringToUnicodeString( &SomeNames[1], &Name, TRUE ); TST_SUCCESS_ASSERT(NtStatus);
+
+ RtlInitString( &Name, LOOKUP_UNKNOWN_NAME0 );
+
+ SomeUses[2] = SidTypeUnknown;
+ NtStatus = RtlAnsiStringToUnicodeString( &SomeNames[2], &Name, TRUE ); TST_SUCCESS_ASSERT(NtStatus);
+ NoUses[0] = SidTypeUnknown;
+ NtStatus = RtlAnsiStringToUnicodeString( &NoNames[0], &Name, TRUE ); TST_SUCCESS_ASSERT(NtStatus);
+
+
+ RtlInitString( &Name, LOOKUP_UNKNOWN_NAME1 );
+
+ SomeUses[3] = SidTypeUnknown;
+ NtStatus = RtlAnsiStringToUnicodeString( &SomeNames[3], &Name, TRUE ); TST_SUCCESS_ASSERT(NtStatus);
+ NoUses[1] = SidTypeUnknown;
+ NtStatus = RtlAnsiStringToUnicodeString( &NoNames[1], &Name, TRUE ); TST_SUCCESS_ASSERT(NtStatus);
+
+
+
+ RtlInitString( &Name, LOOKUP_UNKNOWN_NAME2 );
+
+ SomeUses[4] = SidTypeUnknown;
+ NtStatus = RtlAnsiStringToUnicodeString( &SomeNames[4], &Name, TRUE ); TST_SUCCESS_ASSERT(NtStatus);
+
+
+ if (AccountDomainIsNotPrimaryDomain == TRUE) {
+ RtlInitString( &Name, LOOKUP_KNOWN_NAME2_A );
+ } else {
+ RtlInitString( &Name, LOOKUP_KNOWN_NAME2_P );
+ }
+ AllUses[2] = LOOKUP_KNOWN_NAME2_USE; AllRids[2] = LOOKUP_KNOWN_NAME2_RID;
+ NtStatus = RtlAnsiStringToUnicodeString( &AllNames[2], &Name, TRUE ); TST_SUCCESS_ASSERT(NtStatus);
+ SomeUses[5] = LOOKUP_KNOWN_NAME2_USE; SomeRids[5] = LOOKUP_KNOWN_NAME2_RID;
+ NtStatus = RtlAnsiStringToUnicodeString( &SomeNames[5], &Name, TRUE ); TST_SUCCESS_ASSERT(NtStatus);
+
+
+
+ RtlInitString( &Name, LOOKUP_UNKNOWN_NAME3 );
+
+ SomeUses[6] = SidTypeUnknown;
+ NtStatus = RtlAnsiStringToUnicodeString( &SomeNames[6], &Name, TRUE ); TST_SUCCESS_ASSERT(NtStatus);
+
+
+ DetermineTestsToRun();
+
+ return(TRUE);
+}
+
+
+NTSTATUS
+SampSetDomainPolicy(
+ )
+/*++
+
+
+Routine Description:
+
+ This routine sets the names and SIDs for the builtin and account domains.
+ The builtin account domain has a well known name and SID.
+ The account domain has these stored in the Policy database.
+
+
+ It places the information for these domains in:
+
+ BuiltinDomainSid
+ BuiltinDomainName
+ AccountDomainSid
+ AccountDomainName
+ PrimaryDomainSid
+ PrimaryDomainName
+
+ It also sets the boolean:
+
+ AccountDomainIsNotPrimaryDomain
+
+ to TRUE if the account domain is found to be different from the
+ Primary Domain.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+--*/
+
+{
+ NTSTATUS NtStatus;
+ PPOLICY_ACCOUNT_DOMAIN_INFO PolicyAccountDomainInfo;
+ PPOLICY_PRIMARY_DOMAIN_INFO PolicyPrimaryDomainInfo;
+ SID_IDENTIFIER_AUTHORITY BuiltinAuthority = SECURITY_NT_AUTHORITY;
+
+ //
+ // Builtin domain - well-known name and SID
+ //
+
+ RtlInitUnicodeString( &BuiltinDomainName, L"Builtin");
+
+ BuiltinDomainSid = RtlAllocateHeap(RtlProcessHeap(), 0,RtlLengthRequiredSid( 1 ));
+ ASSERT( BuiltinDomainSid != NULL );
+ RtlInitializeSid( BuiltinDomainSid, &BuiltinAuthority, 1 );
+ *(RtlSubAuthoritySid( BuiltinDomainSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID;
+
+ //
+ // Account domain
+ //
+
+ NtStatus = SampGetLsaDomainInfo(
+ &PolicyAccountDomainInfo,
+ &PolicyPrimaryDomainInfo
+ );
+
+ if (!NT_SUCCESS(NtStatus)) {
+
+ return(NtStatus);
+ }
+
+ AccountDomainSid = PolicyAccountDomainInfo->DomainSid;
+ AccountDomainName = PolicyAccountDomainInfo->DomainName;
+
+ PrimaryDomainSid = PolicyPrimaryDomainInfo->Sid;
+ PrimaryDomainName = PolicyPrimaryDomainInfo->Name;
+
+ //
+ // Determine whether the account domain is a primary domain.
+ //
+
+ AccountDomainIsNotPrimaryDomain =
+ !RtlEqualUnicodeString( &PrimaryDomainName, &AccountDomainName, TRUE);
+
+ return(NtStatus);;
+}
+
+
+
+NTSTATUS
+SampGetLsaDomainInfo(
+ PPOLICY_ACCOUNT_DOMAIN_INFO *PolicyAccountDomainInfo,
+ PPOLICY_PRIMARY_DOMAIN_INFO *PolicyPrimaryDomainInfo
+ )
+
+/*++
+
+Routine Description:
+
+ This routine retrieves ACCOUNT domain information from the LSA
+ policy database.
+
+
+Arguments:
+
+ PolicyAccountDomainInfo - Receives a pointer to a
+ POLICY_ACCOUNT_DOMAIN_INFO structure containing the account
+ domain info.
+
+ PolicyPrimaryDomainInfo - Receives a pointer to a
+ POLICY_PRIMARY_DOMAIN_INFO structure containing the Primary
+ domain info.
+
+
+Return Value:
+
+ STATUS_SUCCESS - Succeeded.
+
+ Other status values that may be returned from:
+
+ LsaOpenPolicy()
+ LsaQueryInformationPolicy()
+--*/
+
+{
+ NTSTATUS Status, IgnoreStatus;
+
+ LSA_HANDLE PolicyHandle;
+ OBJECT_ATTRIBUTES PolicyObjectAttributes;
+
+ //
+ // Open the policy database
+ //
+
+ InitializeObjectAttributes( &PolicyObjectAttributes,
+ NULL, // Name
+ 0, // Attributes
+ NULL, // Root
+ NULL ); // Security Descriptor
+
+ Status = LsaOpenPolicy( NULL,
+ &PolicyObjectAttributes,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ &PolicyHandle );
+ if ( NT_SUCCESS(Status) ) {
+
+ //
+ // Query the account domain information
+ //
+
+ Status = LsaQueryInformationPolicy( PolicyHandle,
+ PolicyAccountDomainInformation,
+ (PVOID *)PolicyAccountDomainInfo );
+#if DBG
+ if ( NT_SUCCESS(Status) ) {
+ ASSERT( (*PolicyAccountDomainInfo) != NULL );
+ ASSERT( (*PolicyAccountDomainInfo)->DomainSid != NULL );
+ ASSERT( (*PolicyAccountDomainInfo)->DomainName.Buffer != NULL );
+ }
+#endif \\DBG
+
+ //
+ // Query the Primary domain information
+ //
+
+ Status = LsaQueryInformationPolicy( PolicyHandle,
+ PolicyPrimaryDomainInformation,
+ (PVOID *)PolicyPrimaryDomainInfo );
+#if DBG
+ if ( NT_SUCCESS(Status) ) {
+ ASSERT( (*PolicyPrimaryDomainInfo) != NULL );
+ ASSERT( (*PolicyPrimaryDomainInfo)->Sid != NULL );
+ ASSERT( (*PolicyPrimaryDomainInfo)->Name.Buffer != NULL );
+ }
+#endif \\DBG
+
+ IgnoreStatus = LsaClose( PolicyHandle );
+ ASSERT(NT_SUCCESS(IgnoreStatus));
+ }
+
+ return(Status);
+}
+
+
+
+
+PSID
+CreateUserSid(
+ PSID DomainSid,
+ ULONG Rid
+ )
+
+/*++
+
+Routine Description:
+
+ This function creates a domain account sid given a domain sid and
+ the relative id of the account within the domain.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Pointer to Sid, or NULL on failure.
+ The returned Sid must be freed with DeleteUserSid
+
+--*/
+{
+
+ NTSTATUS IgnoreStatus;
+ PSID AccountSid;
+ UCHAR AccountSubAuthorityCount = *RtlSubAuthorityCountSid(DomainSid) + (UCHAR)1;
+ ULONG AccountSidLength = RtlLengthRequiredSid(AccountSubAuthorityCount);
+ PULONG RidLocation;
+
+ // Temp sanity check
+ ASSERT(AccountSidLength == RtlLengthSid(DomainSid) + sizeof(ULONG));
+
+ //
+ // Allocate space for the account sid
+ //
+
+ AccountSid = MIDL_user_allocate(AccountSidLength);
+
+ if (AccountSid != NULL) {
+
+ //
+ // Copy the domain sid into the first part of the account sid
+ //
+
+ IgnoreStatus = RtlCopySid(AccountSidLength, AccountSid, DomainSid);
+ ASSERT(NT_SUCCESS(IgnoreStatus));
+
+ //
+ // Increment the account sid sub-authority count
+ //
+
+ *RtlSubAuthorityCountSid(AccountSid) = AccountSubAuthorityCount;
+
+ //
+ // Add the rid as the final sub-authority
+ //
+
+ RidLocation = RtlSubAuthoritySid(AccountSid, AccountSubAuthorityCount - 1);
+ *RidLocation = Rid;
+ }
+
+ return(AccountSid);
+}
+
+
+
+VOID
+DeleteUserSid(
+ PSID UserSid
+ )
+
+/*++
+
+Routine Description:
+
+ Frees a sid returned by CreateUserSid.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ MIDL_user_free(UserSid);
+}
+
+
+
+BOOLEAN
+EnableSecurityPrivilege(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This function enabled the SeSecurityPrivilege privilege.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ TRUE if privilege successfully enabled.
+ FALSE if not successfully enabled.
+
+--*/
+{
+
+ NTSTATUS Status;
+ HANDLE Token;
+ LUID SecurityPrivilege;
+ PTOKEN_PRIVILEGES NewState;
+ ULONG ReturnLength;
+
+
+ //
+ // Open our own token
+ //
+
+ Status = NtOpenProcessToken(
+ NtCurrentProcess(),
+ TOKEN_ADJUST_PRIVILEGES,
+ &Token
+ );
+ if (!NT_SUCCESS(Status)) {
+ printf(" \n\n\n");
+ printf("Tsamobj: Can't open process token to enable Security Privilege.\n");
+ printf(" Completion status of NtOpenProcessToken() is: 0x%lx\n", Status);
+ printf("\n");
+ return(FALSE);
+ }
+
+
+ //
+ // Initialize the adjustment structure
+ //
+
+ SecurityPrivilege =
+ RtlConvertLongToLargeInteger(SE_SECURITY_PRIVILEGE);
+
+ ASSERT( (sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)) < 100);
+ NewState = RtlAllocateHeap( RtlProcessHeap(), 0, 100 );
+
+ NewState->PrivilegeCount = 1;
+ NewState->Privileges[0].Luid = SecurityPrivilege;
+ NewState->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+
+ //
+ // Set the state of the privilege to ENABLED.
+ //
+
+ Status = NtAdjustPrivilegesToken(
+ Token, // TokenHandle
+ FALSE, // DisableAllPrivileges
+ NewState, // NewState
+ 0, // BufferLength
+ NULL, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+ // don't use NT_SUCCESS here because STATUS_NOT_ALL_ASSIGNED is a success status
+ if (Status != STATUS_SUCCESS) {
+ return(FALSE);
+ }
+
+
+ //
+ // Clean up some stuff before returning
+ //
+
+ RtlFreeHeap( RtlProcessHeap(), 0, NewState );
+ Status = NtClose( Token );
+ ASSERT(NT_SUCCESS(Status));
+
+
+ return TRUE;
+
+}
+
+
+
+VOID
+printfSid(
+ PSID Sid
+ )
+
+/*++
+
+Routine Description:
+
+ Prints a sid
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ UCHAR Buffer[128];
+ UCHAR String[128];
+ UCHAR i;
+ ULONG Tmp;
+ PISID iSid = (PISID)Sid; // pointer to opaque structure
+ PSID NextSid = (PSID)Buffer;
+
+ ASSERT(sizeof(Buffer) >= RtlLengthRequiredSid(1));
+
+ {
+ SID_IDENTIFIER_AUTHORITY SidAuthority = SECURITY_WORLD_SID_AUTHORITY;
+ RtlInitializeSid(NextSid, &SidAuthority, 1 );
+ *(RtlSubAuthoritySid(NextSid, 0)) = SECURITY_WORLD_RID;
+ if (RtlEqualSid(Sid, NextSid)) {
+ printf("World");
+ return;
+ }
+ }
+
+ {
+ SID_IDENTIFIER_AUTHORITY SidAuthority = SECURITY_LOCAL_SID_AUTHORITY;
+ RtlInitializeSid(NextSid, &SidAuthority, 1 );
+ *(RtlSubAuthoritySid(NextSid, 0)) = SECURITY_LOCAL_RID;
+ if (RtlEqualSid(Sid, NextSid)) {
+ printf("Local");
+ return;
+ }
+ }
+
+ {
+ SID_IDENTIFIER_AUTHORITY SidAuthority = SECURITY_CREATOR_SID_AUTHORITY;
+ RtlInitializeSid(NextSid, &SidAuthority, 1 );
+ *(RtlSubAuthoritySid(NextSid, 0)) = SECURITY_CREATOR_OWNER_RID;
+ if (RtlEqualSid(Sid, NextSid)) {
+ printf("Creator");
+ return;
+ }
+ }
+
+ {
+ SID_IDENTIFIER_AUTHORITY SidAuthority = SECURITY_NT_AUTHORITY;
+ RtlInitializeSid(NextSid, &SidAuthority, 1 );
+ *(RtlSubAuthoritySid(NextSid, 0)) = SECURITY_DIALUP_RID;
+ if (RtlEqualSid(Sid, NextSid)) {
+ printf("Dialup");
+ return;
+ }
+ }
+
+ {
+ SID_IDENTIFIER_AUTHORITY SidAuthority = SECURITY_NT_AUTHORITY;
+ RtlInitializeSid(NextSid, &SidAuthority, 1 );
+ *(RtlSubAuthoritySid(NextSid, 0)) = SECURITY_NETWORK_RID;
+ if (RtlEqualSid(Sid, NextSid)) {
+ printf("Network");
+ return;
+ }
+ }
+
+ {
+ SID_IDENTIFIER_AUTHORITY SidAuthority = SECURITY_NT_AUTHORITY;
+ RtlInitializeSid(NextSid, &SidAuthority, 1 );
+ *(RtlSubAuthoritySid(NextSid, 0)) = SECURITY_BATCH_RID;
+ if (RtlEqualSid(Sid, NextSid)) {
+ printf("Batch");
+ return;
+ }
+ }
+
+ {
+ SID_IDENTIFIER_AUTHORITY SidAuthority = SECURITY_NT_AUTHORITY;
+ RtlInitializeSid(NextSid, &SidAuthority, 1 );
+ *(RtlSubAuthoritySid(NextSid, 0)) = SECURITY_INTERACTIVE_RID;
+ if (RtlEqualSid(Sid, NextSid)) {
+ printf("Interactive");
+ return;
+ }
+ }
+
+
+ {
+ SID_IDENTIFIER_AUTHORITY SidAuthority = SECURITY_NT_AUTHORITY;
+ RtlInitializeSid(NextSid, &SidAuthority, 1 );
+ *(RtlSubAuthoritySid(NextSid, 0)) = SECURITY_LOCAL_SYSTEM_RID;
+ if (RtlEqualSid(Sid, NextSid)) {
+ printf("Local System");
+ return;
+ }
+ }
+
+
+
+ sprintf(Buffer, "S-%u-", (USHORT)iSid->Revision );
+ strcpy(String, Buffer);
+
+ if ( (iSid->IdentifierAuthority.Value[0] != 0) ||
+ (iSid->IdentifierAuthority.Value[1] != 0) ){
+ sprintf(Buffer, "0x%02hx%02hx%02hx%02hx%02hx%02hx",
+ (USHORT)iSid->IdentifierAuthority.Value[0],
+ (USHORT)iSid->IdentifierAuthority.Value[1],
+ (USHORT)iSid->IdentifierAuthority.Value[2],
+ (USHORT)iSid->IdentifierAuthority.Value[3],
+ (USHORT)iSid->IdentifierAuthority.Value[4],
+ (USHORT)iSid->IdentifierAuthority.Value[5] );
+ strcat(String, Buffer);
+ } else {
+ Tmp = (ULONG)iSid->IdentifierAuthority.Value[5] +
+ (ULONG)(iSid->IdentifierAuthority.Value[4] << 8) +
+ (ULONG)(iSid->IdentifierAuthority.Value[3] << 16) +
+ (ULONG)(iSid->IdentifierAuthority.Value[2] << 24);
+ sprintf(Buffer, "%lu", Tmp);
+ strcat(String, Buffer);
+ }
+
+
+ for (i=0;i<iSid->SubAuthorityCount ;i++ ) {
+ sprintf(Buffer, "-%lu", iSid->SubAuthority[i]);
+ strcat(String, Buffer);
+ }
+
+ printf(Buffer);
+
+ return;
+}
+
+
+VOID
+DetermineTestsToRun(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This function determines which tests are to be run.
+
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+
+--*/
+{
+
+ NTSTATUS Status;
+ HANDLE Token;
+
+ PTOKEN_USER User;
+ PTOKEN_GROUPS Groups;
+
+ ULONG ReturnLength,
+ i;
+
+
+
+ //
+ // See if we can play with auditing information
+ //
+
+ SecurityOperatorTest = EnableSecurityPrivilege();
+
+
+ //
+ // Open our own token
+ //
+
+ Status = NtOpenProcessToken(
+ NtCurrentProcess(),
+ TOKEN_QUERY,
+ &Token
+ );
+ if (!NT_SUCCESS(Status)) {
+ printf(" \n\n\n");
+ printf("Tsamobj: Can't open process token to query owner.\n");
+ printf(" Completion status of NtOpenProcessToken() is: 0x%lx\n", Status);
+ printf("\n");
+ return;
+ }
+
+
+ //
+ // Query the user id
+ //
+
+ User = RtlAllocateHeap( RtlProcessHeap(), 0, 1000 ); // should be plenty big
+ Status = NtQueryInformationToken( Token, TokenUser, User, 1000, &ReturnLength );
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // See if the ID is one of the special IDs (e.g., local admin,
+ // domain account operator, or domain admin)
+ //
+
+ SeeIfSidIsSpecial( User->User.Sid );
+
+
+
+ //
+ // Query the group ids
+ //
+
+ Groups = RtlAllocateHeap( RtlProcessHeap(), 0, 1000 ); // should be plenty big
+ Status = NtQueryInformationToken( Token, TokenGroups, Groups, 1000, &ReturnLength );
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // See if any of these IDs are special IDs
+ //
+
+ for (i=0; i<Groups->GroupCount; i++) {
+ SeeIfSidIsSpecial( Groups->Groups[i].Sid );
+ }
+
+
+
+
+
+ //
+ // Clean up some stuff before returning
+ //
+
+ RtlFreeHeap( RtlProcessHeap(), 0, User );
+ RtlFreeHeap( RtlProcessHeap(), 0, Groups );
+ Status = NtClose( Token );
+ ASSERT(NT_SUCCESS(Status));
+
+
+
+ printf("\n\n\n\nPerforming:\n\n");
+
+ printf(" Administrator Alias Test. . . . . ");
+ if (AdminsAliasTest) {
+ printf("Yes\n\n");
+ } else {
+ printf("No\n\n");
+ }
+
+ printf(" Account Operator Alias Test . . ");
+ if (AccountOpAliasTest) {
+ printf("Yes\n\n");
+ } else {
+ printf("No\n\n");
+ }
+
+ printf(" Security Operator Test . . . . . ");
+ if (SecurityOperatorTest) {
+ printf("Yes\n\n");
+ } else {
+ printf("No\n\n");
+ }
+
+ printf("\n\n\n");
+
+
+
+ return;
+
+}
+
+
+VOID
+SeeIfSidIsSpecial(
+ IN PSID Sid
+ )
+
+/*++
+
+Routine Description:
+
+ This function determines whether the passed SID is one of the special
+ SIDs, such as ADMINISTRATORS alias, or DomainAccountOperator, and
+ sets test flags accordingly.
+
+
+Arguments:
+
+ Sid - Pointer to the SID to check.
+
+Return Value:
+
+ None.
+
+
+--*/
+{
+
+
+
+
+ if (RtlEqualSid( Sid, AdminsAliasSid )){
+ AdminsAliasTest = TRUE;
+ }
+
+ if (RtlEqualSid( Sid, AccountAliasSid )){
+ AccountOpAliasTest = TRUE;
+ }
+
+ return;
+
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Server Object Test Suite //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+
+BOOLEAN
+ServerTestSuite(
+ PHANDLE ServerHandle,
+ PHANDLE DomainHandle,
+ PHANDLE BuiltinDomainHandle,
+ PSID *DomainSid
+ )
+
+{
+ NTSTATUS NtStatus;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ BOOLEAN TestStatus = TRUE;
+ ULONG CountReturned;
+ SAM_ENUMERATE_HANDLE EnumerationContext;
+ PSAM_RID_ENUMERATION EnumerationBuffer;
+ PSID BuiltinDomainSid;
+ ACCESS_MASK ServerAccessMask, DomainAccessMask;
+
+
+
+
+
+ printf("\n");
+ printf("\n");
+ printf(" Server Object Test\n");
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // Connect To Server //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+ printf("\n");
+ printf(" Connect / Disconnect. . . . . . . . . . . . . . . . . Suite\n");
+
+ printf(" Connect . . . . . . . . . . . . . . . . . . . . . . ");
+
+
+ ServerAccessMask = SAM_SERVER_READ | SAM_SERVER_EXECUTE;
+ if (AdminsAliasTest) {
+ ServerAccessMask |= SAM_SERVER_ALL_ACCESS;
+ }
+ if (SecurityOperatorTest) {
+ ServerAccessMask |= ACCESS_SYSTEM_SECURITY;
+ }
+
+ InitializeObjectAttributes( &ObjectAttributes, NULL, 0, 0, NULL );
+
+
+ NtStatus = SamConnect(
+ NULL, // ServerName (Local machine)
+ ServerHandle,
+ ServerAccessMask,
+ &ObjectAttributes
+ );
+
+
+ if (!NT_SUCCESS(NtStatus)) {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ } else {
+ printf("Succeeded\n");
+ }
+
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ printf(" Disconnect . . . . . . . . . . . . . . . . . . . . ");
+
+ NtStatus = SamCloseHandle( (*ServerHandle) );
+
+ if (!NT_SUCCESS(NtStatus)) {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ } else {
+ printf("Succeeded\n");
+ }
+ }
+
+
+
+ printf(" Re-Connect . . . . . . . . . . . . . . . . . . . . ");
+
+
+ NtStatus = SamConnect(
+ NULL, // ServerName (Local machine)
+ ServerHandle,
+ ServerAccessMask,
+ &ObjectAttributes
+ );
+
+
+ if (!NT_SUCCESS(NtStatus)) {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ } else {
+ printf("Succeeded\n");
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // Lookup/Enumerate Domains Suite //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+
+ printf("\n");
+ printf(" Domain Lookup/Enumerate/Open . . . . . . . . . . . . Suite\n");
+
+
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ printf(" Lookup Account Domain . . . . . . . . . . . . . . . ");
+
+
+ NtStatus = SamLookupDomainInSamServer(
+ (*ServerHandle),
+ &AccountDomainName,
+ DomainSid
+ );
+
+ if (!NT_SUCCESS(NtStatus)) {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ } else {
+ if ( TRUE != RtlEqualSid((*DomainSid), AccountDomainSid)) {
+ printf("Failed\n");
+ printf(" The SID retrieved from the policy database did not\n");
+ printf(" match the SID retrieved from SAM for the account\n");
+ printf(" domain.\n");
+ printf(" Sid from Policy Database is: ");
+ printfSid( AccountDomainSid ); printf("\n");
+ printf(" Sid from SAM is: ");
+ printfSid( (*DomainSid) ); printf("\n");
+ TestStatus = FALSE;
+ } else {
+ printf("Succeeded\n");
+ }
+ }
+
+ }
+
+
+
+
+
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ printf(" Enumerate Domain . . . . . . . . . . . . . . . . . ");
+
+
+ EnumerationContext = 0;
+ EnumerationBuffer = NULL;
+ NtStatus = SamEnumerateDomainsInSamServer(
+ (*ServerHandle),
+ &EnumerationContext,
+ (PVOID *)&EnumerationBuffer,
+ 1024, // PreferedMaximumLength
+ &CountReturned
+ );
+
+ if (!NT_SUCCESS(NtStatus)) {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ } else {
+
+ if (CountReturned == 0) {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ printf(" CountReturned is: 0x%lx\n", CountReturned);
+ printf(" EnumerationContext is: 0x%lx\n", EnumerationContext);
+ printf(" EnumerationBuffer Address is: 0x%lx\n", (ULONG)EnumerationBuffer);
+ TestStatus = FALSE;
+
+ } else {
+ printf("Succeeded\n");
+ }
+
+ SamFreeMemory( EnumerationBuffer );
+ }
+
+ }
+
+
+
+
+
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ printf(" Open Account Domain . . . . . . . . . . . . . . . . ");
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ DomainAccessMask = DOMAIN_READ | DOMAIN_EXECUTE;
+ if (AccountOpAliasTest) {
+ DomainAccessMask |= DOMAIN_READ | DOMAIN_WRITE | DOMAIN_EXECUTE;
+ }
+ if (AdminsAliasTest) {
+ DomainAccessMask |= DOMAIN_ALL_ACCESS;
+ }
+ if (SecurityOperatorTest) {
+ DomainAccessMask |= ACCESS_SYSTEM_SECURITY;
+ }
+ NtStatus = SamOpenDomain(
+ (*ServerHandle),
+ DomainAccessMask,
+ *DomainSid,
+ DomainHandle
+ );
+
+ if (!NT_SUCCESS(NtStatus)) {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ } else {
+ printf("Succeeded\n");
+ }
+ }
+
+ }
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ printf(" Open Builtin Domain . . . . . . . . . . . . . . . . ");
+
+ NtStatus = SamLookupDomainInSamServer(
+ (*ServerHandle),
+ &BuiltinDomainName,
+ &BuiltinDomainSid
+ );
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ DomainAccessMask = DOMAIN_READ | DOMAIN_EXECUTE;
+ if (AccountOpAliasTest) {
+ DomainAccessMask |= DOMAIN_READ | DOMAIN_WRITE | DOMAIN_EXECUTE;
+ }
+ if (AdminsAliasTest) {
+ DomainAccessMask |= (DOMAIN_EXECUTE | DOMAIN_READ |
+ DOMAIN_READ_OTHER_PARAMETERS |
+ DOMAIN_ADMINISTER_SERVER |
+ DOMAIN_CREATE_ALIAS);
+ }
+// if (SecurityOperatorTest) {
+// DomainAccessMask |= ACCESS_SYSTEM_SECURITY;
+// }
+ NtStatus = SamOpenDomain(
+ (*ServerHandle),
+ DomainAccessMask,
+ BuiltinDomainSid,
+ BuiltinDomainHandle
+ );
+
+ if (!NT_SUCCESS(NtStatus)) {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ } else {
+ printf("Succeeded\n");
+ }
+ }
+
+ }
+
+ return(TestStatus);
+
+
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Security Manipulation Test Suite //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+
+BOOLEAN
+SecurityTestSuite(
+ HANDLE ServerHandle,
+ HANDLE DomainHandle,
+ ULONG Pass
+ )
+{
+
+ BOOLEAN TestStatus = TRUE;
+ NTSTATUS NtStatus;
+
+ PSECURITY_DESCRIPTOR OriginalServerSD,
+ OriginalDomainSD,
+ OriginalUserSD,
+ OriginalGroupSD,
+ SD1;
+
+ SECURITY_INFORMATION SI1;
+ PVOID TmpPointer1;
+
+ SECURITY_DESCRIPTOR SD1_Body;
+
+ HANDLE GroupHandle,
+ UserHandle;
+
+
+
+
+ printf("\n");
+ printf("\n");
+ printf("\n");
+
+ if (Pass == 1) {
+
+ printf(" Security Manipulation (Pass #1) Test\n");
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // Query Suite //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+ printf("\n");
+ printf(" Query Security . . . . . . . . . . . . . . . . . . . Suite\n");
+
+
+ //
+ // Get Server's original SD
+ //
+
+
+ SI1 = 0;
+ if (AdminsAliasTest) {
+ SI1 |= OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
+ DACL_SECURITY_INFORMATION;
+ }
+ if (SecurityOperatorTest) {
+ SI1 |= SACL_SECURITY_INFORMATION;
+ }
+ if (SI1 != 0) {
+ printf(" Query Server Security Descriptor . . . . . . . . . . ");
+ SD1 = NULL;
+ NtStatus = SamQuerySecurityObject(
+ ServerHandle,
+ SI1,
+ &SD1
+ );
+ if (NT_SUCCESS(NtStatus)) {
+
+ TestStatus = CheckReturnedSD( SI1, SD1, TRUE );
+
+ //
+ // Normally we would do a "SamFreeMemory( SD1 )" here.
+ // However, we want to save this SD for future reference
+ // and use.
+ //
+
+ OriginalServerSD = SD1;
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ }
+
+
+
+
+
+ //
+ // Get domain's original SD
+ //
+
+
+ SI1 = 0;
+ if (AdminsAliasTest) {
+ SI1 |= OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
+ DACL_SECURITY_INFORMATION;
+ }
+ if (SecurityOperatorTest) {
+ SI1 |= SACL_SECURITY_INFORMATION;
+ }
+ if (SI1 != 0) {
+ printf(" Query Domain Security Descriptor . . . . . . . . . . ");
+ SD1 = NULL;
+ NtStatus = SamQuerySecurityObject(
+ DomainHandle,
+ SI1,
+ &SD1
+ );
+ if (NT_SUCCESS(NtStatus)) {
+
+ TestStatus = CheckReturnedSD( SI1, SD1, TRUE );
+
+ //
+ // Normally we would do a "SamFreeMemory( SD1 )" here.
+ // However, we want to save this SD for future reference
+ // and use.
+ //
+
+ OriginalDomainSD = SD1;
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ }
+
+
+
+
+
+
+ //
+ // Make sure the wrapper doesn't choke on a non-null pointer being passed
+ // (assuming we have allocated memory).
+ //
+
+
+ SI1 = 0;
+ if (AdminsAliasTest) {
+ SI1 |= OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
+ DACL_SECURITY_INFORMATION;
+ }
+ if (SecurityOperatorTest) {
+ SI1 |= SACL_SECURITY_INFORMATION;
+ }
+ if (SI1 != 0) {
+ printf(" Query Passing Non-null return buffer . . . . . . . . ");
+ SD1 = RtlAllocateHeap( RtlProcessHeap(), 0, 1000 ); ASSERT(SD1 != NULL);
+ TmpPointer1 = SD1;
+ NtStatus = SamQuerySecurityObject(
+ DomainHandle,
+ SI1,
+ &SD1
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (SD1 != TmpPointer1) {
+
+ TestStatus = CheckReturnedSD( SI1, SD1, TRUE );
+ if (TestStatus) {
+ SamFreeMemory( SD1 );
+ }
+
+
+ } else {
+ printf("Failed\n");
+ printf(" Passed buffer address used on return.\n");
+ printf(" RPC should have allocated another buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+ RtlFreeHeap( RtlProcessHeap(), 0, TmpPointer1 );
+
+ }
+
+
+
+
+
+
+ //
+ // Make sure we can query nothing
+ //
+
+ printf(" Query Nothing . . . . . . . . . . . . . . . . . . . . ");
+
+ SI1 = 0;
+ SD1 = NULL;
+ NtStatus = SamQuerySecurityObject(
+ DomainHandle,
+ SI1,
+ &SD1
+ );
+ if (NT_SUCCESS(NtStatus)) {
+
+ TestStatus = CheckReturnedSD( SI1, SD1, TRUE );
+ if (TestStatus) {
+ SamFreeMemory( SD1 );
+ }
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+
+
+ //
+ // Query owner
+ //
+
+
+ if (AdminsAliasTest) {
+ printf(" Query Owner (Server Object) . . . . . . . . . . . . . ");
+ SI1 = OWNER_SECURITY_INFORMATION;
+ SD1 = NULL;
+ NtStatus = SamQuerySecurityObject(
+ ServerHandle,
+ SI1,
+ &SD1
+ );
+ if (NT_SUCCESS(NtStatus)) {
+
+ TestStatus = CheckReturnedSD( SI1, SD1, TRUE );
+ if (TestStatus) {
+ SamFreeMemory( SD1 );
+ }
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ }
+
+
+
+
+ if (AdminsAliasTest) {
+ printf(" Query Owner (Domain Object) . . . . . . . . . . . . . ");
+ SI1 = OWNER_SECURITY_INFORMATION;
+ SD1 = NULL;
+ NtStatus = SamQuerySecurityObject(
+ DomainHandle,
+ SI1,
+ &SD1
+ );
+ if (NT_SUCCESS(NtStatus)) {
+
+ TestStatus = CheckReturnedSD( SI1, SD1, TRUE );
+ if (TestStatus) {
+ SamFreeMemory( SD1 );
+ }
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ }
+
+
+
+
+
+ if (AdminsAliasTest) {
+
+ //
+ // Query Group
+ //
+
+ printf(" Query Group . . . . . . . . . . . . . . . . . . . . . ");
+
+ SI1 = GROUP_SECURITY_INFORMATION;
+ SD1 = NULL;
+ NtStatus = SamQuerySecurityObject(
+ DomainHandle,
+ SI1,
+ &SD1
+ );
+ if (NT_SUCCESS(NtStatus)) {
+
+ TestStatus = CheckReturnedSD( SI1, SD1, TRUE );
+ if (TestStatus) {
+ SamFreeMemory( SD1 );
+ }
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+
+
+ //
+ // Query Dacl
+ //
+
+ printf(" Query DACL . . . . . . . . . . . . . . . . . . . . . ");
+
+ SI1 = DACL_SECURITY_INFORMATION;
+ SD1 = NULL;
+ NtStatus = SamQuerySecurityObject(
+ DomainHandle,
+ SI1,
+ &SD1
+ );
+ if (NT_SUCCESS(NtStatus)) {
+
+ TestStatus = CheckReturnedSD( SI1, SD1, TRUE );
+ if (TestStatus) {
+ SamFreeMemory( SD1 );
+ }
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+
+
+ //
+ // Query Sacl
+ //
+
+ printf(" Query SACL . . . . . . . . . . . . . . . . . . . . . ");
+
+ SI1 = SACL_SECURITY_INFORMATION;
+ SD1 = NULL;
+ NtStatus = SamQuerySecurityObject(
+ DomainHandle,
+ SI1,
+ &SD1
+ );
+ if (NT_SUCCESS(NtStatus)) {
+
+ TestStatus = CheckReturnedSD( SI1, SD1, TRUE );
+ if (TestStatus) {
+ SamFreeMemory( SD1 );
+ }
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+ } // end_if (AdminsAliasTest)
+
+
+
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // Set Suite //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+ printf("\n");
+ printf(" Set Security . . . . . . . . . . . . . . . . . . . . Suite\n");
+
+
+ //
+ // Make sure we can set nothing
+ //
+
+ printf(" Set Nothing . . . . . . . . . . . . . . . . . . . . . ");
+
+ SI1 = 0;
+ SD1 = &SD1_Body;
+ NtStatus = RtlCreateSecurityDescriptor( SD1, SECURITY_DESCRIPTOR_REVISION1 );
+ ASSERT( NT_SUCCESS(NtStatus) );
+ NtStatus = SamSetSecurityObject(
+ DomainHandle,
+ SI1, // <------ This is invalid
+ SD1
+ );
+ if (NtStatus == STATUS_INVALID_PARAMETER) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+
+
+
+ //
+ // set something not passed
+ //
+
+ printf(" Set something not passed. . . . . . . . . . . . . . . ");
+
+ SI1 = GROUP_SECURITY_INFORMATION;
+ SD1 = &SD1_Body;
+ NtStatus = RtlCreateSecurityDescriptor( SD1, SECURITY_DESCRIPTOR_REVISION1 );
+ ASSERT( NT_SUCCESS(NtStatus) );
+ NtStatus = SamSetSecurityObject(
+ DomainHandle,
+ SI1,
+ SD1
+ );
+ if (NtStatus == STATUS_BAD_DESCRIPTOR_FORMAT) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+
+
+
+ //
+ // set a non-existant DACL
+ //
+
+ if (AdminsAliasTest) {
+ printf(" Set non-existant DACL (Server object) . . . . . . . . ");
+
+ SI1 = DACL_SECURITY_INFORMATION;
+ SD1 = &SD1_Body;
+ NtStatus = RtlCreateSecurityDescriptor( SD1, SECURITY_DESCRIPTOR_REVISION1 );
+ SD1_Body.Control = SE_DACL_PRESENT;
+ ASSERT( NT_SUCCESS(NtStatus) );
+ NtStatus = SamSetSecurityObject(
+ ServerHandle,
+ SI1,
+ SD1
+ );
+ if (NT_SUCCESS(NtStatus)) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ }
+
+
+
+ if (AdminsAliasTest) {
+ printf(" Set non-existant DACL (Domain Object) . . . . . . . . ");
+
+ SI1 = DACL_SECURITY_INFORMATION;
+ SD1 = &SD1_Body;
+ NtStatus = RtlCreateSecurityDescriptor( SD1, SECURITY_DESCRIPTOR_REVISION1 );
+ SD1_Body.Control = SE_DACL_PRESENT;
+ ASSERT( NT_SUCCESS(NtStatus) );
+ NtStatus = SamSetSecurityObject(
+ DomainHandle,
+ SI1,
+ SD1
+ );
+ if (NT_SUCCESS(NtStatus)) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ }
+
+
+
+
+
+ //
+ // set original DACL (From original SD)
+ //
+
+ if (AdminsAliasTest) {
+
+ printf(" Set original DACL (Server Object) . . . . . . . . . . ");
+
+ SI1 = DACL_SECURITY_INFORMATION;
+ SD1 = OriginalServerSD;
+ NtStatus = SamSetSecurityObject(
+ ServerHandle,
+ SI1,
+ SD1
+ );
+ if (NT_SUCCESS(NtStatus)) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ }
+
+
+
+ if (AdminsAliasTest) {
+
+ printf(" Set original DACL (Domain Object) . . . . . . . . . . ");
+
+ SI1 = DACL_SECURITY_INFORMATION;
+ SD1 = OriginalDomainSD;
+ NtStatus = SamSetSecurityObject(
+ DomainHandle,
+ SI1,
+ SD1
+ );
+ if (NT_SUCCESS(NtStatus)) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ }
+
+
+
+
+
+ if (AdminsAliasTest) {
+
+ //
+ // set a non-existant SACL
+ //
+
+ printf(" Set non-existant SACL . . . . . . . . . . . . . . . . ");
+
+ SI1 = SACL_SECURITY_INFORMATION;
+ SD1 = &SD1_Body;
+ NtStatus = RtlCreateSecurityDescriptor( SD1, SECURITY_DESCRIPTOR_REVISION1 );
+ SD1_Body.Control = SE_SACL_PRESENT;
+ ASSERT( NT_SUCCESS(NtStatus) );
+ NtStatus = SamSetSecurityObject(
+ DomainHandle,
+ SI1,
+ SD1
+ );
+ if (NT_SUCCESS(NtStatus)) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+
+
+
+ //
+ // set original SACL (From original SD)
+ //
+
+ printf(" Set original SACL . . . . . . . . . . . . . . . . . . ");
+
+ SI1 = SACL_SECURITY_INFORMATION;
+ SD1 = OriginalDomainSD;
+ NtStatus = SamSetSecurityObject(
+ DomainHandle,
+ SI1,
+ SD1
+ );
+ if (NT_SUCCESS(NtStatus)) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+
+
+ //
+ // set a owner to null
+ //
+
+ printf(" Set null Owner . . . . . . . . . . . . . . . . . . . ");
+
+ SI1 = OWNER_SECURITY_INFORMATION;
+ SD1 = &SD1_Body;
+ NtStatus = RtlCreateSecurityDescriptor( SD1, SECURITY_DESCRIPTOR_REVISION1 );
+ SD1_Body.Owner = NULL;
+ ASSERT( NT_SUCCESS(NtStatus) );
+ NtStatus = SamSetSecurityObject(
+ DomainHandle,
+ SI1,
+ SD1
+ );
+ if (NtStatus = STATUS_BAD_DESCRIPTOR_FORMAT) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+
+
+ //
+ // set owner to invalid value
+ //
+
+ printf(" Set owner to invalid value . . . . . . . . . . . . . ");
+
+ SI1 = OWNER_SECURITY_INFORMATION;
+ SD1 = &SD1_Body;
+ NtStatus = RtlCreateSecurityDescriptor( SD1, SECURITY_DESCRIPTOR_REVISION1 );
+ SD1_Body.Owner = WorldSid;
+ ASSERT( NT_SUCCESS(NtStatus) );
+ NtStatus = SamSetSecurityObject(
+ DomainHandle,
+ SI1,
+ SD1
+ );
+ if (NtStatus = STATUS_INVALID_OWNER) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+
+ //
+ // set a owner to valid value
+ //
+
+ printf(" Set owner to valid value . . . . . . . . . . . . . . ");
+
+ printf("Untested\n");
+
+
+
+
+
+ //
+ // set group to null
+ //
+
+ printf(" Set null Group . . . . . . . . . . . . . . . . . . . ");
+
+ SI1 = GROUP_SECURITY_INFORMATION;
+ SD1 = &SD1_Body;
+ NtStatus = RtlCreateSecurityDescriptor( SD1, SECURITY_DESCRIPTOR_REVISION1 );
+ SD1_Body.Group = NULL;
+ ASSERT( NT_SUCCESS(NtStatus) );
+ NtStatus = SamSetSecurityObject(
+ DomainHandle,
+ SI1,
+ SD1
+ );
+ if (NtStatus = STATUS_BAD_DESCRIPTOR_FORMAT) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+
+
+
+ //
+ // set Group to valid value
+ //
+
+ printf(" Set Group to valid value . . . . . . . . . . . . . . ");
+
+ SI1 = GROUP_SECURITY_INFORMATION;
+ SD1 = &SD1_Body;
+ NtStatus = RtlCreateSecurityDescriptor( SD1, SECURITY_DESCRIPTOR_REVISION1 );
+ SD1_Body.Group = WorldSid;
+ ASSERT( NT_SUCCESS(NtStatus) );
+ NtStatus = SamSetSecurityObject(
+ DomainHandle,
+ SI1,
+ SD1
+ );
+ if (NT_SUCCESS(NtStatus)) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+
+
+ //
+ // set Group back to original value
+ //
+
+ printf(" Set Group to original value . . . . . . . . . . . . . ");
+
+ SI1 = GROUP_SECURITY_INFORMATION;
+ SD1 = OriginalDomainSD;
+ NtStatus = SamSetSecurityObject(
+ DomainHandle,
+ SI1,
+ SD1
+ );
+ if (NT_SUCCESS(NtStatus)) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ }
+
+
+
+
+ } // end Pass1
+
+
+ if (Pass == 2) {
+
+ ACCESS_MASK AccessMask;
+ PSID_NAME_USE LookedUpUses;
+ PULONG LookedUpRids;
+ UNICODE_STRING AccountNames[10];
+ STRING AccountNameAnsi;
+
+
+ //
+ // This pass depends upon user and group accounts established in pass #1
+ //
+
+
+
+
+
+ if (AdminsAliasTest) {
+
+
+ printf(" Security Manipulation (Pass #2) Test\n");
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // Query Suite //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+ printf("\n");
+ printf(" Query Security (User Object). . . . . . . . . . . . . Suite\n");
+
+
+ AccessMask = READ_CONTROL;
+ if (SecurityOperatorTest) {
+ AccessMask |= ACCESS_SYSTEM_SECURITY;
+ }
+
+ //
+ // Open the user created in pass #1
+ //
+
+ RtlInitString( &AccountNameAnsi, USER_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountNames[0], &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ NtStatus = SamLookupNamesInDomain(
+ DomainHandle,
+ 1,
+ &AccountNames[0],
+ &LookedUpRids,
+ &LookedUpUses
+ );
+ RtlFreeUnicodeString( &AccountNames[0] );
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(LookedUpUses[0] == SidTypeUser);
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ AccessMask,
+ LookedUpRids[0],
+ &UserHandle);
+ SamFreeMemory( LookedUpUses ); SamFreeMemory( LookedUpRids );
+ if (!NT_SUCCESS(NtStatus)) {
+ printf("Failed to open user account created in pass #1\n");
+ }
+ TST_SUCCESS_ASSERT(NT_SUCCESS(NtStatus));
+
+
+
+ //
+ // Get user's original SD
+ //
+
+ SI1 |= OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
+ DACL_SECURITY_INFORMATION;
+ if (SecurityOperatorTest) {
+ SI1 |= SACL_SECURITY_INFORMATION;
+ }
+
+ printf(" Query User Security Descriptor . . . . . . . . . . . ");
+ SD1 = NULL;
+ NtStatus = SamQuerySecurityObject(
+ UserHandle,
+ SI1,
+ &SD1
+ );
+ if (NT_SUCCESS(NtStatus)) {
+
+ TestStatus = CheckReturnedSD( SI1, SD1, TRUE );
+
+ //
+ // Normally we would do a "SamFreeMemory( SD1 )" here.
+ // However, we want to save this SD for future reference
+ // and use.
+ //
+
+ OriginalUserSD = SD1;
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+
+
+ NtStatus = SamCloseHandle( UserHandle );
+ TST_SUCCESS_ASSERT( UserHandle );
+
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // Set Suite //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+ printf("\n");
+ printf(" Set Security (User Object) . . . . . . . . . . . . . Suite\n");
+
+ AccessMask = WRITE_DAC | WRITE_OWNER;
+ if (SecurityOperatorTest) {
+ AccessMask |= ACCESS_SYSTEM_SECURITY;
+ }
+
+ //
+ // Open the user created in pass #1
+ //
+
+ RtlInitString( &AccountNameAnsi, USER_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountNames[0], &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ NtStatus = SamLookupNamesInDomain(
+ DomainHandle,
+ 1,
+ &AccountNames[0],
+ &LookedUpRids,
+ &LookedUpUses
+ );
+ RtlFreeUnicodeString( &AccountNames[0] );
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(LookedUpUses[0] == SidTypeUser);
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ AccessMask,
+ LookedUpRids[0],
+ &UserHandle);
+ SamFreeMemory( LookedUpUses ); SamFreeMemory( LookedUpRids );
+ if (!NT_SUCCESS(NtStatus)) {
+ printf("Failed to open user account created in pass #1\n");
+ }
+ TST_SUCCESS_ASSERT(NT_SUCCESS(NtStatus));
+
+
+ //
+ // Make sure we can set nothing
+ //
+
+ printf(" Set Nothing . . . . . . . . . . . . . . . . . . . . . ");
+
+ SI1 = 0;
+ SD1 = &SD1_Body;
+ NtStatus = RtlCreateSecurityDescriptor( SD1, SECURITY_DESCRIPTOR_REVISION1 );
+ ASSERT( NT_SUCCESS(NtStatus) );
+ NtStatus = SamSetSecurityObject(
+ UserHandle,
+ SI1, // <------ This is invalid
+ SD1
+ );
+ if (NtStatus == STATUS_INVALID_PARAMETER) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+
+
+
+ //
+ // set something not passed
+ //
+
+ printf(" Set something not passed. . . . . . . . . . . . . . . ");
+
+ SI1 = GROUP_SECURITY_INFORMATION;
+ SD1 = &SD1_Body;
+ NtStatus = RtlCreateSecurityDescriptor( SD1, SECURITY_DESCRIPTOR_REVISION1 );
+ ASSERT( NT_SUCCESS(NtStatus) );
+ NtStatus = SamSetSecurityObject(
+ UserHandle,
+ SI1,
+ SD1
+ );
+ if (NtStatus == STATUS_BAD_DESCRIPTOR_FORMAT) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+
+
+
+
+
+ printf(" Set non-existant DACL . . . . . . . . . . . . . . . . ");
+
+ SI1 = DACL_SECURITY_INFORMATION;
+ SD1 = &SD1_Body;
+ NtStatus = RtlCreateSecurityDescriptor( SD1, SECURITY_DESCRIPTOR_REVISION1 );
+ SD1_Body.Control = SE_DACL_PRESENT;
+ ASSERT( NT_SUCCESS(NtStatus) );
+ NtStatus = SamSetSecurityObject(
+ UserHandle,
+ SI1,
+ SD1
+ );
+ if (NT_SUCCESS(NtStatus)) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+
+
+
+ //
+ // set original DACL (From original SD)
+ //
+
+
+ printf(" Set original DACL . . . . . . . . . . . . . . . . . . ");
+
+ SI1 = DACL_SECURITY_INFORMATION;
+ SD1 = OriginalUserSD;
+ NtStatus = SamSetSecurityObject(
+ UserHandle,
+ SI1,
+ SD1
+ );
+ if (NT_SUCCESS(NtStatus)) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+
+
+
+
+ NtStatus = SamCloseHandle( UserHandle );
+ TST_SUCCESS_ASSERT( UserHandle );
+
+
+
+ }
+
+ DBG_UNREFERENCED_LOCAL_VARIABLE( GroupHandle );
+ DBG_UNREFERENCED_LOCAL_VARIABLE( OriginalGroupSD );
+ }
+
+
+
+
+
+ return TestStatus;
+}
+
+
+BOOLEAN
+CheckReturnedSD(
+ IN SECURITY_INFORMATION SI,
+ IN PSECURITY_DESCRIPTOR SD,
+ IN BOOLEAN PrintTestSuccess
+ )
+
+
+{
+ NTSTATUS NtStatus;
+
+ BOOLEAN Failed = FALSE,
+ IgnoreBoolean,
+ AclPresent,
+ TestStatus = TRUE;
+
+ PSID SID;
+ PACL ACL;
+
+
+
+ //
+ // Check a returned security descriptor agains the information requested.
+ //
+
+ if (SD == NULL) {
+ TestStatus = FALSE;
+ if (PrintTestSuccess) {
+ printf("Failed\n");
+ Failed = TRUE;
+ printf(" The SecurityDescriptor return address was not properly\n");
+ printf(" set.\n");
+ }
+ }
+
+
+ if (TestStatus) {
+
+ //
+ // Check owner
+ //
+
+ NtStatus = RtlGetOwnerSecurityDescriptor ( SD, &SID, &IgnoreBoolean);
+ ASSERT(NT_SUCCESS(NtStatus));
+ if (SI & OWNER_SECURITY_INFORMATION) {
+ if (SID == NULL) {
+ if (PrintTestSuccess) {
+ if (!Failed) {
+ printf("Failed\n");
+ printf(" Security descriptor address is 0x%lx\n", SD );
+ Failed = TRUE;
+ }
+ printf(" An owner was requested but the owner field of the\n");
+ printf(" security descriptor is not set.\n");
+ TestStatus = FALSE;
+
+ }
+ }
+ } else { // Owner not specified
+ if (SID != NULL) {
+ if (PrintTestSuccess) {
+ if (!Failed) {
+ printf("Failed\n");
+ printf(" Security descriptor address is 0x%lx\n", SD );
+ Failed = TRUE;
+ }
+ printf(" An owner was not requested but the owner field of the\n");
+ printf(" security descriptor is set.\n");
+ TestStatus = FALSE;
+ }
+ }
+ }
+
+
+
+
+ //
+ // Check group
+ //
+
+ NtStatus = RtlGetGroupSecurityDescriptor ( SD, &SID, &IgnoreBoolean);
+ ASSERT(NT_SUCCESS(NtStatus));
+ if (SI & GROUP_SECURITY_INFORMATION) {
+ if (SID == NULL) {
+ if (PrintTestSuccess) {
+ if (!Failed) {
+ printf("Failed\n");
+ printf(" Security descriptor address is 0x%lx\n", SD );
+ Failed = TRUE;
+ }
+ printf(" A group was requested but the group field of the\n");
+ printf(" security descriptor is not set.\n");
+ TestStatus = FALSE;
+
+ }
+ }
+ } else { // Group not specified
+ if (SID != NULL) {
+ if (PrintTestSuccess) {
+ if (!Failed) {
+ printf("Failed\n");
+ printf(" Security descriptor address is 0x%lx\n", SD );
+ Failed = TRUE;
+ }
+ printf(" A group was not requested but the group field of the\n");
+ printf(" security descriptor is set.\n");
+ TestStatus = FALSE;
+ }
+ }
+ }
+
+
+
+
+ //
+ // Check sacl
+ //
+
+ NtStatus = RtlGetSaclSecurityDescriptor ( SD, &AclPresent, &ACL, &IgnoreBoolean);
+ ASSERT(NT_SUCCESS(NtStatus));
+ if (SI & SACL_SECURITY_INFORMATION) {
+ if (!AclPresent) {
+ if (PrintTestSuccess) {
+ if (!Failed) {
+ printf("Failed\n");
+ printf(" Security descriptor address is 0x%lx\n", SD );
+ Failed = TRUE;
+ }
+ printf(" An SACL was requested but the SaclPresent flag\n");
+ printf(" of the security descriptor is not set.\n");
+ TestStatus = FALSE;
+
+ }
+ }
+ } else { // sacl not specified
+ if (AclPresent) {
+ if (PrintTestSuccess) {
+ if (!Failed) {
+ printf("Failed\n");
+ printf(" Security descriptor address is 0x%lx\n", SD );
+ Failed = TRUE;
+ }
+ printf(" An SACL was not requested but the SaclPresent flag\n");
+ printf(" of the security descriptor is set.\n");
+ TestStatus = FALSE;
+ }
+ }
+ }
+
+
+
+
+
+ //
+ // Check Dacl
+ //
+
+ NtStatus = RtlGetDaclSecurityDescriptor ( SD, &AclPresent, &ACL, &IgnoreBoolean);
+ ASSERT(NT_SUCCESS(NtStatus));
+ if (SI & DACL_SECURITY_INFORMATION) {
+ if (!AclPresent) {
+ if (PrintTestSuccess) {
+ if (!Failed) {
+ printf("Failed\n");
+ printf(" Security descriptor address is 0x%lx\n", SD );
+ Failed = TRUE;
+ }
+ printf(" A DACL was requested but the DaclPresent flag\n");
+ printf(" of the security descriptor is not set.\n");
+ TestStatus = FALSE;
+
+ }
+ }
+ } else { // Dacl not specified
+ if (AclPresent) {
+ if (PrintTestSuccess) {
+ if (!Failed) {
+ printf("Failed\n");
+ printf(" Security descriptor address is 0x%lx\n", SD );
+ Failed = TRUE;
+ }
+ printf(" A DACL was not requested but the DaclPresent flag\n");
+ printf(" of the security descriptor is set.\n");
+ TestStatus = FALSE;
+ }
+ }
+ }
+
+
+
+
+
+ }
+
+
+
+
+ if (PrintTestSuccess) {
+ if (TestStatus) {
+ printf("Succeeded\n");
+ }
+ }
+
+
+
+ return(TestStatus);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Domain Object Test Suite //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+
+BOOLEAN
+DomainTestSuite(
+ HANDLE DomainHandle
+ )
+{
+
+ BOOLEAN TestStatus = TRUE;
+ NTSTATUS NtStatus, IgnoreStatus;
+ PVOID Buffer, Buffer1, Buffer2;
+ CHAR UnusedBuffer[20];
+ UNICODE_STRING AccountName;
+ STRING AccountNameAnsi;
+ HANDLE GroupHandle = NULL;
+ HANDLE AliasHandle = NULL;
+ HANDLE UserHandle = NULL;
+ HANDLE ValidUserHandle = NULL;
+ ULONG GroupRid, AliasRid, UserRid, SavedGroupRid, SavedAliasRid, AccountCount, i;
+ SAM_ENUMERATE_HANDLE EnumerationContext;
+ ULONG CountReturned;
+ USHORT NameLength;
+ PUNICODE_STRING LookedUpNames;
+ PSID_NAME_USE LookedUpUses;
+ PULONG LookedUpRids;
+
+
+ printf("\n");
+ printf("\n");
+ printf("\n");
+ printf(" Domain Test\n");
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // Query Suite //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+ printf("\n");
+ printf(" Query Information . . . . . . . . . . . . . . . . . . Suite\n");
+
+
+ //
+ // Make sure the wrapper doesn't choke on a non-null pointer being passed
+ // (assuming we have allocated memory).
+ //
+
+ printf(" Query Buffer Allocation Test . . . . . . . . . . . . ");
+
+ Buffer = &UnusedBuffer[0];
+ NtStatus = SamQueryInformationDomain(
+ DomainHandle,
+ DomainStateInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != &UnusedBuffer[0]) {
+ if (Buffer != NULL) {
+ printf("Succeeded\n");
+ SamFreeMemory( Buffer );
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Passed buffer address used on return.\n");
+ printf(" RPC should have allocated another buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+ //
+ // Query all the fixed length info levels
+ // Query - Password, Logoff, ServerRole, DomainState, ModifiedCount, LockoutInfo
+ //
+
+ printf(" Query DomainState . . . . . . . . . . . . . . . . . . ");
+
+ NtStatus = SamQueryInformationDomain(
+ DomainHandle,
+ DomainStateInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+ printf("Succeeded\n");
+ SamFreeMemory( Buffer );
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+
+ }
+
+
+ printf(" Query ServerRole . . . . . . . . . . . . . . . . . . ");
+ NtStatus = SamQueryInformationDomain(
+ DomainHandle,
+ DomainServerRoleInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+ printf("Succeeded\n");
+ SamFreeMemory( Buffer );
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+
+ }
+
+
+ printf(" Query Password Information . . . . . . . . . . . . . ");
+ NtStatus = SamQueryInformationDomain(
+ DomainHandle,
+ DomainPasswordInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+ printf("Succeeded\n");
+ SamFreeMemory( Buffer );
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+
+ }
+
+
+ printf(" Query Logoff Information . . . . . . . . . . . . . . ");
+ NtStatus = SamQueryInformationDomain(
+ DomainHandle,
+ DomainLogoffInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+ printf("Succeeded\n");
+ SamFreeMemory( Buffer );
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+
+ }
+
+
+ printf(" Query Modified . . . . . . . . . . . . . . . . . . . ");
+ NtStatus = SamQueryInformationDomain(
+ DomainHandle,
+ DomainModifiedInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+ printf("Succeeded\n");
+ SamFreeMemory( Buffer );
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+
+ }
+
+
+ printf(" Query Lockout . . . . . . . . . . . . . . . . . . . . ");
+ NtStatus = SamQueryInformationDomain(
+ DomainHandle,
+ DomainLockoutInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+ printf("Succeeded\n");
+ SamFreeMemory( Buffer );
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+
+ }
+
+
+
+
+
+ //
+ // Query the name of the domain ...
+ //
+
+ printf(" Query Domain Name . . . . . . . . . . . . . . . . . . ");
+
+ Buffer = NULL;
+ NtStatus = SamQueryInformationDomain(
+ DomainHandle,
+ DomainNameInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+ if ( (((DOMAIN_NAME_INFORMATION *)Buffer)->DomainName.MaximumLength > 0) &&
+ (((DOMAIN_NAME_INFORMATION *)Buffer)->DomainName.Buffer != NULL) ) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" String body returned and allocated,\n");
+ printf(" but character buffer pointer is NULL.\n");
+ TestStatus = FALSE;
+ }
+ SamFreeMemory( Buffer );
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+ //
+ // Query whatever is in the OEM Information field ...
+ //
+
+ printf(" Query OEM Information . . . . . . . . . . . . . . . . ");
+
+ Buffer = NULL;
+ NtStatus = SamQueryInformationDomain(
+ DomainHandle,
+ DomainOemInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+ if ( (((DOMAIN_OEM_INFORMATION *)Buffer)->OemInformation.MaximumLength >= 0) &&
+ (((DOMAIN_OEM_INFORMATION *)Buffer)->OemInformation.Buffer != NULL) ) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" String body returned and allocated,\n");
+ printf(" but character buffer pointer is NULL.\n");
+ TestStatus = FALSE;
+ }
+ SamFreeMemory( Buffer );
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+ //
+ // Query whatever is in the Replication Information field ...
+ //
+
+ printf(" Query Replication Information . . . . . . . . . . . . ");
+
+ Buffer = NULL;
+ NtStatus = SamQueryInformationDomain(
+ DomainHandle,
+ DomainReplicationInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+ if ( (((DOMAIN_REPLICATION_INFORMATION *)Buffer)->ReplicaSourceNodeName.MaximumLength >= 0) &&
+ (((DOMAIN_REPLICATION_INFORMATION *)Buffer)->ReplicaSourceNodeName.Buffer != NULL) ) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" String body returned and allocated,\n");
+ printf(" but character buffer pointer is NULL.\n");
+ TestStatus = FALSE;
+ }
+ SamFreeMemory( Buffer );
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+ //
+ // Query domain general Information...
+ //
+
+ printf(" Query General Information . . . . . . . . . . . . . . ");
+
+ Buffer = NULL;
+ NtStatus = SamQueryInformationDomain(
+ DomainHandle,
+ DomainGeneralInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+
+ printf("Succeeded\n");
+ printf(" Number of Users is: 0x%lx\n",
+ ((DOMAIN_GENERAL_INFORMATION *)Buffer)->UserCount );
+ printf(" Number of groups is: 0x%lx\n",
+ ((DOMAIN_GENERAL_INFORMATION *)Buffer)->GroupCount);
+ printf(" Number of aliases is: 0x%lx\n",
+ ((DOMAIN_GENERAL_INFORMATION *)Buffer)->AliasCount);
+
+
+ SamFreeMemory( Buffer );
+
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+ //
+ // Query domain general Information...
+ //
+
+ printf(" Query General Information 2 . . . . . . . . . . . . . ");
+
+ Buffer = NULL;
+ NtStatus = SamQueryInformationDomain(
+ DomainHandle,
+ DomainGeneralInformation2,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+
+ printf("Succeeded\n");
+ printf(" Number of Users is: 0x%lx\n",
+ ((DOMAIN_GENERAL_INFORMATION2 *)Buffer)->I1.UserCount );
+ printf(" Number of groups is: 0x%lx\n",
+ ((DOMAIN_GENERAL_INFORMATION2 *)Buffer)->I1.GroupCount);
+ printf(" Number of aliases is: 0x%lx\n",
+ ((DOMAIN_GENERAL_INFORMATION2 *)Buffer)->I1.AliasCount);
+
+
+ SamFreeMemory( Buffer );
+
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+
+
+
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // Set Suite //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+ printf(" Set Information . . . . . . . . . . . . . . . . . . . Suite\n");
+
+ //
+ // Set all the fixed length info levels
+ // - Password, Logoff, ServerRole, DomainState, ModifiedCount
+ //
+
+/*
+ * CANT TEST SERVER STATE SETTING WITHOUT BREAKING THE REST OF THE TEST.
+ * THE REASON IS, ONCE THE STATE IS CHANGED, NOTHING ELSE CAN BE DONE.
+ *
+ * printf(" Set DomainState . . . . . . . . . . . . . . . . . . . ");
+ *
+ * //
+ * // Get the current value...
+ * //
+ *
+ * NtStatus = SamQueryInformationDomain(
+ * DomainHandle,
+ * DomainStateInformation,
+ * &Buffer1
+ * );
+ * ASSERT( NT_SUCCESS(NtStatus) );
+ *
+ * //
+ * // Change the field to a new value and write it out.
+ * //
+ *
+ * if ( ((DOMAIN_STATE_INFORMATION *)Buffer1)->DomainServerState ==
+ * DomainServerEnabled ) {
+ * ((DOMAIN_STATE_INFORMATION *)Buffer1)->DomainServerState =
+ * DomainServerDisabled;
+ * } else {
+ * ((DOMAIN_STATE_INFORMATION *)Buffer1)->DomainServerState =
+ * DomainServerEnabled;
+ * }
+ *
+ * NtStatus = SamSetInformationDomain(
+ * DomainHandle,
+ * DomainStateInformation,
+ * Buffer1
+ * );
+ * if ( NT_SUCCESS(NtStatus) ) {
+ *
+ * //
+ * // Now check that the change was really made...
+ * //
+ *
+ * NtStatus = SamQueryInformationDomain(
+ * DomainHandle,
+ * DomainStateInformation,
+ * &Buffer2
+ * );
+ * ASSERT(NT_SUCCESS( NtStatus ) );
+ * if (((DOMAIN_STATE_INFORMATION *)Buffer1)->DomainServerState ==
+ * ((DOMAIN_STATE_INFORMATION *)Buffer2)->DomainServerState ) {
+ *
+ * printf("Succeeded\n");
+ *
+ * } else {
+ *
+ * printf("Failed\n");
+ * printf(" Value queried doesn't match value written\n");
+ * printf(" Value Written is 0x%lx\n",
+ * (ULONG)((DOMAIN_STATE_INFORMATION *)Buffer1)->DomainServerState);
+ * printf(" Value Retrieved is 0x%lx\n",
+ * (ULONG)((DOMAIN_STATE_INFORMATION *)Buffer2)->DomainServerState);
+ *
+ * TestStatus = FALSE;
+ *
+ * }
+ *
+ * SamFreeMemory( Buffer1 );
+ * SamFreeMemory( Buffer2 );
+ *
+ * } else {
+ * printf("Failed\n");
+ * printf(" Completion status is 0x%lx\n", NtStatus);
+ * TestStatus = FALSE;
+ * SamFreeMemory( Buffer1 );
+ *
+ * }
+ */
+
+
+
+/*
+ * CANT TEST SERVER ROLE SETTING WITHOUT BREAKING THE REST OF THE TEST.
+ * THE REASON IS, ONCE THE ROLE IS SET TO BACKUP, NOTHING ELSE CAN BE
+ * SET.
+ *
+ * printf(" Set ServerRole . . . . . . . . . . . . . . . . . . . ");
+ *
+ * //
+ * // Get the current value...
+ * //
+ *
+ * NtStatus = SamQueryInformationDomain(
+ * DomainHandle,
+ * DomainServerRoleInformation,
+ * &Buffer1
+ * );
+ * ASSERT( NT_SUCCESS(NtStatus) );
+ *
+ * //
+ * // Change the field to a new value and write it out.
+ * //
+ *
+ * if ( ((DOMAIN_SERVER_ROLE_INFORMATION *)Buffer1)->DomainServerRole ==
+ * DomainServerRolePrimary ) {
+ * ((DOMAIN_SERVER_ROLE_INFORMATION *)Buffer1)->DomainServerRole =
+ * DomainServerRoleBackup;
+ * } else {
+ * ((DOMAIN_SERVER_ROLE_INFORMATION *)Buffer1)->DomainServerRole =
+ * DomainServerRolePrimary;
+ * }
+ *
+ * NtStatus = SamSetInformationDomain(
+ * DomainHandle,
+ * DomainServerRoleInformation,
+ * Buffer1
+ * );
+ * if ( NT_SUCCESS(NtStatus) ) {
+ *
+ * //
+ * // Now check that the change was really made...
+ * //
+ *
+ * NtStatus = SamQueryInformationDomain(
+ * DomainHandle,
+ * DomainServerRoleInformation,
+ * &Buffer2
+ * );
+ * ASSERT(NT_SUCCESS( NtStatus ) );
+ * if (((DOMAIN_SERVER_ROLE_INFORMATION *)Buffer1)->DomainServerRole ==
+ * ((DOMAIN_SERVER_ROLE_INFORMATION *)Buffer2)->DomainServerRole ) {
+ *
+ * printf("Succeeded\n");
+ *
+ * } else {
+ *
+ * printf("Failed\n");
+ * printf(" Value queried doesn't match value written\n");
+ * printf(" Value Written is 0x%lx\n",
+ * (ULONG)((DOMAIN_SERVER_ROLE_INFORMATION *)Buffer1)->DomainServerRole);
+ * printf(" Value Retrieved is 0x%lx\n",
+ * (ULONG)((DOMAIN_SERVER_ROLE_INFORMATION *)Buffer2)->DomainServerRole);
+ *
+ * TestStatus = FALSE;
+ *
+ * }
+ *
+ * SamFreeMemory( Buffer1 );
+ * SamFreeMemory( Buffer2 );
+ *
+ * } else {
+ * printf("Failed\n");
+ * printf(" Completion status is 0x%lx\n", NtStatus);
+ * TestStatus = FALSE;
+ * SamFreeMemory( Buffer1 );
+ *
+ * }
+ */
+
+
+
+ printf(" Set Password Information . . . . . . . . . . . . . . ");
+
+
+ //
+ // Get the current value...
+ //
+
+ NtStatus = SamQueryInformationDomain(
+ DomainHandle,
+ DomainPasswordInformation,
+ &Buffer1
+ );
+ ASSERT( NT_SUCCESS(NtStatus) );
+
+ //
+ // Change a field to a new value and write it out.
+ //
+
+ if ( ((DOMAIN_PASSWORD_INFORMATION *)Buffer1)->MinPasswordLength == 0 ) {
+ ((DOMAIN_PASSWORD_INFORMATION *)Buffer1)->MinPasswordLength = 6;
+ } else {
+ ((DOMAIN_PASSWORD_INFORMATION *)Buffer1)->MinPasswordLength = 0;
+ }
+
+ //
+ // Set PasswordProperties to COMPLEX so that tests run after this one
+ // are a little more interesting.
+ //
+
+ ((DOMAIN_PASSWORD_INFORMATION *)Buffer1)->PasswordProperties |= DOMAIN_PASSWORD_COMPLEX;
+
+ NtStatus = SamSetInformationDomain(
+ DomainHandle,
+ DomainPasswordInformation,
+ Buffer1
+ );
+ if ( NT_SUCCESS(NtStatus) ) {
+
+ //
+ // Now check that the change was really made...
+ //
+
+ NtStatus = SamQueryInformationDomain(
+ DomainHandle,
+ DomainPasswordInformation,
+ &Buffer2
+ );
+ ASSERT(NT_SUCCESS( NtStatus ) );
+ if (((DOMAIN_PASSWORD_INFORMATION *)Buffer1)->MinPasswordLength ==
+ ((DOMAIN_PASSWORD_INFORMATION *)Buffer2)->MinPasswordLength ) {
+
+ printf("Succeeded\n");
+
+ } else {
+
+ printf("Failed\n");
+ printf(" Value queried doesn't match value written\n");
+ printf(" Value Written is 0x%lx\n",
+ (ULONG)((DOMAIN_PASSWORD_INFORMATION *)Buffer1)->MinPasswordLength);
+ printf(" Value Retrieved is 0x%lx\n",
+ (ULONG)((DOMAIN_PASSWORD_INFORMATION *)Buffer2)->MinPasswordLength);
+
+ TestStatus = FALSE;
+
+ }
+
+ SamFreeMemory( Buffer1 );
+ SamFreeMemory( Buffer2 );
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ SamFreeMemory( Buffer1 );
+
+ }
+
+
+
+ printf(" Set Logoff Information . . . . . . . . . . . . . . . ");
+
+ //
+ // Get the current value...
+ //
+
+ NtStatus = SamQueryInformationDomain(
+ DomainHandle,
+ DomainLogoffInformation,
+ &Buffer1
+ );
+ ASSERT( NT_SUCCESS(NtStatus) );
+
+ //
+ // Change the field to a new value and write it out.
+ //
+
+ if ( ((DOMAIN_LOGOFF_INFORMATION *)Buffer1)->ForceLogoff.LowPart == 0 ) {
+ ((DOMAIN_LOGOFF_INFORMATION *)Buffer1)->ForceLogoff.LowPart = 1000;
+ } else {
+ ((DOMAIN_LOGOFF_INFORMATION *)Buffer1)->ForceLogoff.LowPart = 0;
+ }
+
+ NtStatus = SamSetInformationDomain(
+ DomainHandle,
+ DomainLogoffInformation,
+ Buffer1
+ );
+ if ( NT_SUCCESS(NtStatus) ) {
+
+ //
+ // Now check that the change was really made...
+ //
+
+ NtStatus = SamQueryInformationDomain(
+ DomainHandle,
+ DomainLogoffInformation,
+ &Buffer2
+ );
+ ASSERT(NT_SUCCESS( NtStatus ) );
+ if (((DOMAIN_LOGOFF_INFORMATION *)Buffer1)->ForceLogoff.LowPart ==
+ ((DOMAIN_LOGOFF_INFORMATION *)Buffer2)->ForceLogoff.LowPart ) {
+
+ printf("Succeeded\n");
+
+ } else {
+
+ printf("Failed\n");
+ printf(" Value queried doesn't match value written\n");
+ printf(" Value Written is 0x%lx\n",
+ (ULONG)((DOMAIN_LOGOFF_INFORMATION *)Buffer1)->ForceLogoff.LowPart);
+ printf(" Value Retrieved is 0x%lx\n",
+ (ULONG)((DOMAIN_LOGOFF_INFORMATION *)Buffer2)->ForceLogoff.LowPart);
+
+ TestStatus = FALSE;
+
+ }
+
+ SamFreeMemory( Buffer1 );
+ SamFreeMemory( Buffer2 );
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ SamFreeMemory( Buffer1 );
+
+ }
+
+
+
+ printf(" Set Modified . . . . . . . . . . . . . . . . . . . . ");
+
+
+ NtStatus = SamSetInformationDomain(
+ DomainHandle,
+ DomainModifiedInformation,
+ &LargeInteger1
+ );
+
+ if (NtStatus != STATUS_INVALID_INFO_CLASS) {
+
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ } else {
+ printf("Succeeded\n");
+ }
+
+
+ printf(" Set Lockout Information . . . . . . . . . . . . . . . ");
+
+ //
+ // Get the current value...
+ //
+
+ NtStatus = SamQueryInformationDomain(
+ DomainHandle,
+ DomainLockoutInformation,
+ &Buffer1
+ );
+ ASSERT( NT_SUCCESS(NtStatus) );
+
+ //
+ // Change the field to a new value and write it out.
+ //
+
+ if ( ((DOMAIN_LOCKOUT_INFORMATION *)Buffer1)->LockoutDuration.LowPart == 0 ) {
+ ((DOMAIN_LOCKOUT_INFORMATION *)Buffer1)->LockoutDuration.LowPart = 9000000;
+ } else {
+ ((DOMAIN_LOCKOUT_INFORMATION *)Buffer1)->LockoutDuration.LowPart = 0;
+ }
+ if ( ((DOMAIN_LOCKOUT_INFORMATION *)Buffer1)->LockoutObservationWindow.LowPart == 0 ) {
+ ((DOMAIN_LOCKOUT_INFORMATION *)Buffer1)->LockoutObservationWindow.LowPart = 8000000;
+ } else {
+ ((DOMAIN_LOCKOUT_INFORMATION *)Buffer1)->LockoutObservationWindow.LowPart = 0;
+ }
+ if ( ((DOMAIN_LOCKOUT_INFORMATION *)Buffer1)->LockoutThreshold == 0 ) {
+ ((DOMAIN_LOCKOUT_INFORMATION *)Buffer1)->LockoutThreshold = 2;
+ } else {
+ ((DOMAIN_LOCKOUT_INFORMATION *)Buffer1)->LockoutThreshold = 0;
+ }
+
+ NtStatus = SamSetInformationDomain(
+ DomainHandle,
+ DomainLockoutInformation,
+ Buffer1
+ );
+ if ( NT_SUCCESS(NtStatus) ) {
+
+ //
+ // Now check that the change was really made...
+ //
+
+ NtStatus = SamQueryInformationDomain(
+ DomainHandle,
+ DomainLockoutInformation,
+ &Buffer2
+ );
+ ASSERT(NT_SUCCESS( NtStatus ) );
+ if ( (((DOMAIN_LOCKOUT_INFORMATION *)Buffer1)->LockoutDuration.LowPart ==
+ ((DOMAIN_LOCKOUT_INFORMATION *)Buffer2)->LockoutDuration.LowPart ) &&
+ (((DOMAIN_LOCKOUT_INFORMATION *)Buffer1)->LockoutObservationWindow.LowPart ==
+ ((DOMAIN_LOCKOUT_INFORMATION *)Buffer2)->LockoutObservationWindow.LowPart ) &&
+ (((DOMAIN_LOCKOUT_INFORMATION *)Buffer1)->LockoutThreshold ==
+ ((DOMAIN_LOCKOUT_INFORMATION *)Buffer2)->LockoutThreshold ) ) {
+
+ printf("Succeeded\n");
+
+ } else {
+
+ printf("Failed\n");
+ printf(" Value queried doesn't match value written\n");
+ printf(" Duration Written is 0x%lx\n",
+ (ULONG)((DOMAIN_LOCKOUT_INFORMATION *)Buffer1)->LockoutDuration.LowPart);
+ printf(" Duration Retrieved is 0x%lx\n",
+ (ULONG)((DOMAIN_LOCKOUT_INFORMATION *)Buffer2)->LockoutDuration.LowPart);
+ printf(" Window Written is 0x%lx\n",
+ (ULONG)((DOMAIN_LOCKOUT_INFORMATION *)Buffer1)->LockoutObservationWindow.LowPart);
+ printf(" Window Retrieved is 0x%lx\n",
+ (ULONG)((DOMAIN_LOCKOUT_INFORMATION *)Buffer2)->LockoutObservationWindow.LowPart);
+ printf(" Duration Written is 0x%lx\n",
+ (ULONG)((DOMAIN_LOCKOUT_INFORMATION *)Buffer1)->LockoutThreshold);
+ printf(" Duration Retrieved is 0x%lx\n",
+ (ULONG)((DOMAIN_LOCKOUT_INFORMATION *)Buffer2)->LockoutThreshold);
+
+ TestStatus = FALSE;
+
+ }
+
+ SamFreeMemory( Buffer1 );
+ SamFreeMemory( Buffer2 );
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ SamFreeMemory( Buffer1 );
+
+ }
+
+
+
+
+ printf(" Set Domain Name . . . . . . . . . . . . . . . . . . . ");
+
+
+ NtStatus = SamSetInformationDomain(
+ DomainHandle,
+ DomainNameInformation,
+ &DummyName1
+ );
+
+ if (NtStatus != STATUS_INVALID_INFO_CLASS) {
+
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ } else {
+ printf("Succeeded\n");
+ }
+
+
+ printf(" Set OEM Information . . . . . . . . . . . . . . . . . ");
+
+ //
+ // Get the current value...
+ //
+
+ NtStatus = SamQueryInformationDomain(
+ DomainHandle,
+ DomainOemInformation,
+ &Buffer1
+ );
+ ASSERT( NT_SUCCESS(NtStatus) );
+
+ //
+ // Change the field to a new value and write it out.
+ //
+
+ NameLength = ((DOMAIN_OEM_INFORMATION *)Buffer1)->OemInformation.Length;
+ if ( NameLength == DummyName1.Length ) {
+ ((DOMAIN_OEM_INFORMATION *)Buffer1)->OemInformation = DummyName2;
+ } else {
+ ((DOMAIN_OEM_INFORMATION *)Buffer1)->OemInformation = DummyName1;
+ }
+
+ NtStatus = SamSetInformationDomain(
+ DomainHandle,
+ DomainOemInformation,
+ Buffer1
+ );
+ if ( NT_SUCCESS(NtStatus) ) {
+
+ //
+ // Now check that the change was really made...
+ //
+
+ NtStatus = SamQueryInformationDomain(
+ DomainHandle,
+ DomainOemInformation,
+ &Buffer2
+ );
+ ASSERT(NT_SUCCESS( NtStatus ) );
+ if (((DOMAIN_OEM_INFORMATION *)Buffer1)->OemInformation.Length ==
+ ((DOMAIN_OEM_INFORMATION *)Buffer2)->OemInformation.Length ) {
+
+ printf("Succeeded\n");
+
+ } else {
+
+ printf("Failed\n");
+ printf(" Value queried doesn't match value written\n");
+ printf(" Value Written is 0x%lx\n",
+ (ULONG)((DOMAIN_OEM_INFORMATION *)Buffer1)->OemInformation.Length);
+ printf(" Value Retrieved is 0x%lx\n",
+ (ULONG)((DOMAIN_OEM_INFORMATION *)Buffer2)->OemInformation.Length);
+
+ TestStatus = FALSE;
+
+ }
+
+ SamFreeMemory( Buffer1 );
+ SamFreeMemory( Buffer2 );
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ SamFreeMemory( Buffer1 );
+
+ }
+
+
+
+
+ printf(" Set Replication Information . . . . . . . . . . . . . ");
+
+ //
+ // Get the current value...
+ //
+
+ NtStatus = SamQueryInformationDomain(
+ DomainHandle,
+ DomainReplicationInformation,
+ &Buffer1
+ );
+ ASSERT( NT_SUCCESS(NtStatus) );
+
+ //
+ // Change the field to a new value and write it out.
+ //
+
+ NameLength = ((DOMAIN_REPLICATION_INFORMATION *)Buffer1)->ReplicaSourceNodeName.Length;
+ if ( NameLength == DummyName1.Length ) {
+ ((DOMAIN_REPLICATION_INFORMATION *)Buffer1)->ReplicaSourceNodeName = DummyName2;
+ } else {
+ ((DOMAIN_REPLICATION_INFORMATION *)Buffer1)->ReplicaSourceNodeName = DummyName1;
+ }
+
+ NtStatus = SamSetInformationDomain(
+ DomainHandle,
+ DomainReplicationInformation,
+ Buffer1
+ );
+ if ( NT_SUCCESS(NtStatus) ) {
+
+ //
+ // Now check that the change was really made...
+ //
+
+ NtStatus = SamQueryInformationDomain(
+ DomainHandle,
+ DomainReplicationInformation,
+ &Buffer2
+ );
+ ASSERT(NT_SUCCESS( NtStatus ) );
+ if (((DOMAIN_REPLICATION_INFORMATION *)Buffer1)->ReplicaSourceNodeName.Length ==
+ ((DOMAIN_REPLICATION_INFORMATION *)Buffer2)->ReplicaSourceNodeName.Length ) {
+
+ printf("Succeeded\n");
+
+ } else {
+
+ printf("Failed\n");
+ printf(" Value queried doesn't match value written\n");
+ printf(" Value Written is 0x%lx\n",
+ (ULONG)((DOMAIN_REPLICATION_INFORMATION *)Buffer1)->ReplicaSourceNodeName.Length);
+ printf(" Value Retrieved is 0x%lx\n",
+ (ULONG)((DOMAIN_REPLICATION_INFORMATION *)Buffer2)->ReplicaSourceNodeName.Length);
+
+ TestStatus = FALSE;
+
+ }
+
+ SamFreeMemory( Buffer1 );
+ SamFreeMemory( Buffer2 );
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ SamFreeMemory( Buffer1 );
+
+ }
+
+
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // Create User/Group/Alias Suite //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+ printf(" Create User/Group/Alias . . . . . . . . . . . . . . . . Suite\n");
+
+
+ printf(" Create Group . . . . . . . . . . . . . . . . . . . . ");
+
+ RtlInitString( &AccountNameAnsi, GROUP_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+
+ //InitializeObjectAttributes( &ObjectAttributes, &AccountName, 0, 0, NULL );
+
+ GroupRid = 0;
+ GroupHandle = NULL;
+ NtStatus = SamCreateGroupInDomain(
+ DomainHandle,
+ &AccountName,
+ GROUP_ALL_ACCESS,
+ &GroupHandle,
+ &GroupRid
+ );
+ RtlFreeUnicodeString( &AccountName );
+
+ if (NT_SUCCESS(NtStatus)) {
+ if ( (GroupHandle == NULL) || (GroupRid == 0) ) {
+
+ printf("Failed\n");
+ printf(" Invalid GroupHandle or GroupRid returned.\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ printf(" GroupHandle value is: 0x%lx\n", (ULONG)GroupHandle);
+ printf(" GroupRid value is: 0x%lx\n", GroupRid);
+ TestStatus = FALSE;
+ } else {
+
+ printf("Succeeded\n");
+ SavedGroupRid = GroupRid;
+ NtStatus = SamCloseHandle( GroupHandle );
+ if (!NT_SUCCESS(NtStatus)) {
+ printf(" SamCloseHandle() completion status is: 0x%lx\n", NtStatus);
+ }
+ ASSERT( NT_SUCCESS(NtStatus) );
+
+ }
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+
+ printf(" Create Duplicate Group . . . . . . . . . . . . . . . ");
+ RtlInitString( &AccountNameAnsi, GROUP_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ //InitializeObjectAttributes( &ObjectAttributes, &AccountName, 0, 0, NULL );
+
+
+ GroupRid = 0;
+ GroupHandle = NULL;
+ NtStatus = SamCreateGroupInDomain(
+ DomainHandle,
+ &AccountName,
+ GROUP_ALL_ACCESS,
+ &GroupHandle,
+ &GroupRid
+ );
+ RtlFreeUnicodeString( &AccountName );
+
+ if (NtStatus != STATUS_GROUP_EXISTS) {
+
+ printf("Failed\n");
+ printf(" Completion status should be STATUS_GROUP_EXISTS\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+
+ } else {
+
+ printf("Succeeded\n");
+
+ }
+
+
+
+ printf(" Create Alias . . . . . . . . . . . . . . . . . . . . ");
+
+ RtlInitString( &AccountNameAnsi, ALIAS_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+
+ AliasRid = 0;
+ AliasHandle = NULL;
+ NtStatus = SamCreateAliasInDomain(
+ DomainHandle,
+ &AccountName,
+ ALIAS_ALL_ACCESS,
+ &AliasHandle,
+ &AliasRid
+ );
+ RtlFreeUnicodeString( &AccountName );
+
+ if (NT_SUCCESS(NtStatus)) {
+ if ( (AliasHandle == NULL) || (AliasRid == 0) ) {
+
+ printf("Failed\n");
+ printf(" Invalid AliasHandle or AliasRid returned.\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ printf(" AliasHandle value is: 0x%lx\n", (ULONG)AliasHandle);
+ printf(" AliasRid value is: 0x%lx\n", AliasRid);
+ TestStatus = FALSE;
+ } else {
+
+ printf("Succeeded\n");
+ SavedAliasRid = AliasRid;
+ NtStatus = SamCloseHandle( AliasHandle );
+ if (!NT_SUCCESS(NtStatus)) {
+ printf(" SamCloseHandle() completion status is: 0x%lx\n", NtStatus);
+ }
+ ASSERT( NT_SUCCESS(NtStatus) );
+
+
+ if (AliasRid == SavedGroupRid) {
+ printf(" Create Group/Alias Comparison. . . . . . . . . . . . . Failed\n");
+
+ printf(" Same RID assigned to new alias and group.\n");
+ TestStatus = FALSE;
+ }
+ }
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+
+ printf(" Create another Alias . . . . . . . . . . . . . . . . ");
+
+ RtlInitString( &AccountNameAnsi, ALIAS_NAME2 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+
+ AliasRid = 0;
+ AliasHandle = NULL;
+ NtStatus = SamCreateAliasInDomain(
+ DomainHandle,
+ &AccountName,
+ ALIAS_ALL_ACCESS,
+ &AliasHandle,
+ &AliasRid
+ );
+ RtlFreeUnicodeString( &AccountName );
+
+ if (NT_SUCCESS(NtStatus)) {
+ if ( (AliasHandle == NULL) || (AliasRid == 0) ) {
+
+ printf("Failed\n");
+ printf(" Invalid AliasHandle or AliasRid returned.\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ printf(" AliasHandle value is: 0x%lx\n", (ULONG)AliasHandle);
+ printf(" AliasRid value is: 0x%lx\n", AliasRid);
+ TestStatus = FALSE;
+ } else {
+
+ printf("Succeeded\n");
+ SavedAliasRid = AliasRid;
+ NtStatus = SamCloseHandle( AliasHandle );
+ if (!NT_SUCCESS(NtStatus)) {
+ printf(" SamCloseHandle() completion status is: 0x%lx\n", NtStatus);
+ }
+ ASSERT( NT_SUCCESS(NtStatus) );
+
+
+ if (AliasRid == SavedGroupRid) {
+ printf(" Create Group/Alias Comparison. . . . . . . . . . . . . Failed\n");
+
+ printf(" Same RID assigned to new alias and group.\n");
+ TestStatus = FALSE;
+ }
+ }
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+
+ printf(" Create Duplicate Alias . . . . . . . . . . . . . . . ");
+ RtlInitString( &AccountNameAnsi, ALIAS_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+
+ AliasRid = 0;
+ AliasHandle = NULL;
+ NtStatus = SamCreateAliasInDomain(
+ DomainHandle,
+ &AccountName,
+ ALIAS_ALL_ACCESS,
+ &AliasHandle,
+ &AliasRid
+ );
+ RtlFreeUnicodeString( &AccountName );
+
+ if (NtStatus != STATUS_ALIAS_EXISTS) {
+
+ printf("Failed\n");
+ printf(" Completion status should be STATUS_ALIAS_EXISTS\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+
+ } else {
+
+ printf("Succeeded\n");
+
+ }
+
+
+
+
+
+ printf(" Create User . . . . . . . . . . . . . . . . . . . . . ");
+
+ RtlInitString( &AccountNameAnsi, USER_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+
+ UserRid = 0;
+ UserHandle = NULL;
+ NtStatus = SamCreateUserInDomain(
+ DomainHandle,
+ &AccountName,
+ USER_ALL_ACCESS,
+ &UserHandle,
+ &UserRid
+ );
+ RtlFreeUnicodeString( &AccountName );
+
+ if (NT_SUCCESS(NtStatus)) {
+ if ( (UserHandle == NULL) || (UserRid == 0) ) {
+
+ printf("Failed\n");
+ printf(" Invalid UserHandle or UserRid returned.\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ printf(" UserHandle value is: 0x%lx\n", (ULONG)UserHandle);
+ printf(" UserRid value is: 0x%lx\n", UserRid);
+ TestStatus = FALSE;
+ } else {
+
+ printf("Succeeded\n");
+ ValidUserHandle = UserHandle;
+
+
+ if (UserRid == SavedGroupRid) {
+ printf(" Create Group/User Comparison. . . . . . . . . . . . . Failed\n");
+
+ printf(" Same RID assigned to new user and group.\n");
+ TestStatus = FALSE;
+ }
+
+ if (UserRid == SavedAliasRid) {
+ printf(" Create Alias/User Comparison. . . . . . . . . . . . . Failed\n");
+
+ printf(" Same RID assigned to new user and alias.\n");
+ TestStatus = FALSE;
+ }
+ }
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+
+
+
+
+ printf(" Create Duplicate User . . . . . . . . . . . . . . . . ");
+
+ RtlInitString( &AccountNameAnsi, USER_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+
+ UserRid = 0;
+ UserHandle = NULL;
+ NtStatus = SamCreateUserInDomain(
+ DomainHandle,
+ &AccountName,
+ USER_ALL_ACCESS,
+ &UserHandle,
+ &UserRid
+ );
+ RtlFreeUnicodeString( &AccountName );
+
+ if (NtStatus != STATUS_USER_EXISTS) {
+
+ printf("Failed\n");
+ printf(" Completion status should be STATUS_USER_EXISTS\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+
+ } else {
+
+ printf("Succeeded\n");
+
+ }
+
+
+
+
+ printf(" Create Group With Same Name As User . . . . . . . . . ");
+
+ RtlInitString( &AccountNameAnsi, USER_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+
+ GroupRid = 0;
+ GroupHandle = NULL;
+ NtStatus = SamCreateGroupInDomain(
+ DomainHandle,
+ &AccountName,
+ GROUP_ALL_ACCESS,
+ &GroupHandle,
+ &GroupRid
+ );
+ RtlFreeUnicodeString( &AccountName );
+
+ if (NtStatus != STATUS_USER_EXISTS) {
+
+ printf("Failed\n");
+ printf(" Completion status should be STATUS_USER_EXISTS\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+
+ } else {
+
+ printf("Succeeded\n");
+
+ }
+
+
+
+
+ printf(" Create Group With Same Name As Alias. . . . . . . . . ");
+
+ RtlInitString( &AccountNameAnsi, ALIAS_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+
+ GroupRid = 0;
+ GroupHandle = NULL;
+ NtStatus = SamCreateGroupInDomain(
+ DomainHandle,
+ &AccountName,
+ GROUP_ALL_ACCESS,
+ &GroupHandle,
+ &GroupRid
+ );
+ RtlFreeUnicodeString( &AccountName );
+
+ if (NtStatus != STATUS_ALIAS_EXISTS) {
+
+ printf("Failed\n");
+ printf(" Completion status should be STATUS_ALIAS_EXISTS\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+
+ } else {
+
+ printf("Succeeded\n");
+
+ }
+
+
+
+ printf(" Create Alias With Same Name As Group. . . . . . . . . ");
+
+ RtlInitString( &AccountNameAnsi, GROUP_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+
+ AliasRid = 0;
+ AliasHandle = NULL;
+ NtStatus = SamCreateAliasInDomain(
+ DomainHandle,
+ &AccountName,
+ GROUP_ALL_ACCESS,
+ &AliasHandle,
+ &AliasRid
+ );
+ RtlFreeUnicodeString( &AccountName );
+
+ if (NtStatus != STATUS_GROUP_EXISTS) {
+
+ printf("Failed\n");
+ printf(" Completion status should be STATUS_GROUP_EXISTS\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+
+ } else {
+
+ printf("Succeeded\n");
+
+ }
+
+
+
+ printf(" Create User With Same Name As Group . . . . . . . . . ");
+
+ RtlInitString( &AccountNameAnsi, GROUP_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ //InitializeObjectAttributes( &ObjectAttributes, &AccountName, 0, 0, NULL );
+
+
+ UserRid = 0;
+ UserHandle = NULL;
+ NtStatus = SamCreateUserInDomain(
+ DomainHandle,
+ &AccountName,
+ USER_ALL_ACCESS,
+ &UserHandle,
+ &UserRid
+ );
+ RtlFreeUnicodeString( &AccountName );
+
+ if (NtStatus != STATUS_GROUP_EXISTS) {
+
+ printf("Failed\n");
+ printf(" Completion status should be STATUS_GROUP_EXISTS\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+
+ } else {
+
+ printf("Succeeded\n");
+
+ }
+
+
+
+ printf(" Create User With Same Name As Alias . . . . . . . . . ");
+
+ RtlInitString( &AccountNameAnsi, ALIAS_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+
+ UserRid = 0;
+ UserHandle = NULL;
+ NtStatus = SamCreateUserInDomain(
+ DomainHandle,
+ &AccountName,
+ USER_ALL_ACCESS,
+ &UserHandle,
+ &UserRid
+ );
+ RtlFreeUnicodeString( &AccountName );
+
+ if (NtStatus != STATUS_ALIAS_EXISTS) {
+
+ printf("Failed\n");
+ printf(" Completion status should be STATUS_ALIAS_EXISTS\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+
+ } else {
+
+ printf("Succeeded\n");
+
+ }
+
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // Call server to test internal functions //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+ printf("\n");
+ printf(" Test internal functions . . . . . . . . . . . . . . . Suite\n");
+ printf(" Test internal domain functions . . . . . . . . . . ");
+
+ NtStatus = SamTestPrivateFunctionsDomain( DomainHandle );
+
+ if ( NT_SUCCESS( NtStatus ) ) {
+
+ printf("Succeeded.\n");
+
+ } else {
+
+ if ( NtStatus == STATUS_NOT_IMPLEMENTED ) {
+
+ printf("Not Implemented\n");
+
+ } else {
+
+ printf("Failed.\n");
+ printf(" Status = %lx\n", NtStatus );
+ TestStatus = FALSE;
+ }
+ }
+
+ printf(" Test internal user functions . . . . . . . . . . . ");
+
+ if (ValidUserHandle == NULL) {
+
+ printf("Test omitted - Valid User handle not available\n");
+ TestStatus = FALSE;
+
+ } else {
+
+ NtStatus = SamTestPrivateFunctionsUser( ValidUserHandle );
+ IgnoreStatus = SamCloseHandle( ValidUserHandle );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+ if ( NT_SUCCESS( NtStatus ) ) {
+
+ printf("Succeeded.\n");
+
+ } else {
+
+ if ( NtStatus == STATUS_NOT_IMPLEMENTED ) {
+
+ printf("Not Implemented\n");
+
+ } else {
+
+ printf("Failed.\n");
+ printf(" Status = %lx\n", NtStatus );
+ TestStatus = FALSE;
+ }
+ }
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // Enumerate Users/Groups Suite //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+
+ printf(" Enumerate Users/Groups/Aliases. . . . . . . . . . . . Suite\n");
+
+ printf(" Enumerate Groups - large prefered length . . . . . . ");
+
+
+ EnumerationContext = 0;
+ NtStatus = SamEnumerateGroupsInDomain(
+ DomainHandle,
+ &EnumerationContext,
+ &Buffer,
+ 12000, // PreferedMaximumLength
+ &CountReturned
+ );
+ AccountCount = CountReturned; // Save for future test
+
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+ if (NtStatus == STATUS_SUCCESS) {
+
+ if (CountReturned > 1) {
+ printf("Succeeded\n");
+ for (i=0; i<CountReturned; i++) {
+ printf(" Rid/Name(%ld): 0x%lx / %wZ\n",i,
+ ((PSAM_RID_ENUMERATION)(Buffer))[i].RelativeId,
+ &((PSAM_RID_ENUMERATION)(Buffer))[i].Name
+ );
+ }
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ printf(" Expected several entries to be returned.\n");
+ printf(" Received 0x%lx entries instead.\n", CountReturned);
+ TestStatus = FALSE;
+ }
+
+ } else {
+
+ printf("Failed\n");
+ printf(" Expected STATUS_MORE_ENTRIES to be returned.\n");
+ printf(" Received 0x%lx instead.\n", NtStatus);
+ printf(" Buffer = 0x%lx\n", (ULONG)Buffer);
+ printf(" CountReturned = 0x%lx\n", CountReturned);
+ TestStatus = FALSE;
+ }
+
+ SamFreeMemory( Buffer );
+
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+
+ }
+
+
+
+
+ printf(" Enumerate Groups - small prefered length . . . . . . ");
+
+
+ for ( i=0; i<AccountCount; i++) {
+ EnumerationContext = i;
+ NtStatus = SamEnumerateGroupsInDomain(
+ DomainHandle,
+ &EnumerationContext,
+ &Buffer,
+ 0, // PreferedMaximumLength
+ &CountReturned
+ );
+
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+ if ( ((i >= AccountCount -1) && (NtStatus == STATUS_SUCCESS)) ||
+ ((i <= AccountCount -1) && (NtStatus == STATUS_MORE_ENTRIES)) ) {
+
+ if (CountReturned != 1) {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ printf(" Expected one entry to be returned.\n");
+ printf(" Received 0x%lx entries instead.\n", CountReturned);
+ TestStatus = FALSE;
+ i = AccountCount + 100;
+ }
+
+ } else {
+
+ printf("Failed\n");
+ if (i < AccountCount -1 ) {
+ printf(" Expected STATUS_MORE_ENTRIES to be returned.\n");
+ } else {
+ printf(" Expected STATUS_SUCCESS to be returned.\n");
+ }
+ printf(" Received 0x%lx instead.\n", NtStatus);
+ printf(" Buffer = 0x%lx\n", (ULONG)Buffer);
+ printf(" CountReturned = 0x%lx\n", CountReturned);
+ TestStatus = FALSE;
+ i = AccountCount + 100;
+ }
+
+ SamFreeMemory( Buffer );
+
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ i = AccountCount + 100;
+ }
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ i = AccountCount + 100;
+
+ }
+ }
+
+ if ( i == AccountCount) {
+ printf("Succeeded\n");
+ }
+
+
+
+
+ printf(" Enumerate Aliases - large prefered length . . . . . . ");
+
+
+ EnumerationContext = 0;
+ NtStatus = SamEnumerateAliasesInDomain(
+ DomainHandle,
+ &EnumerationContext,
+ &Buffer,
+ 12000, // PreferedMaximumLength
+ &CountReturned
+ );
+ AccountCount = CountReturned; // Save for future test
+
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+ if (NtStatus == STATUS_SUCCESS) {
+
+ if (CountReturned > 1) {
+ printf("Succeeded\n");
+ for (i=0; i<CountReturned; i++) {
+ printf(" Rid/Name(%ld): 0x%lx / %wZ\n",i,
+ ((PSAM_RID_ENUMERATION)(Buffer))[i].RelativeId,
+ &((PSAM_RID_ENUMERATION)(Buffer))[i].Name
+ );
+ }
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ printf(" Expected several entries to be returned.\n");
+ printf(" Received 0x%lx entries instead.\n", CountReturned);
+ TestStatus = FALSE;
+ }
+
+ } else {
+
+ printf("Failed\n");
+ printf(" Expected STATUS_MORE_ENTRIES to be returned.\n");
+ printf(" Received 0x%lx instead.\n", NtStatus);
+ printf(" Buffer = 0x%lx\n", (ULONG)Buffer);
+ printf(" CountReturned = 0x%lx\n", CountReturned);
+ TestStatus = FALSE;
+ }
+
+ SamFreeMemory( Buffer );
+
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+
+ }
+
+
+
+
+ printf(" Enumerate Aliases - small prefered length . . . . . . ");
+
+
+ for ( i=0; i<AccountCount; i++) {
+ EnumerationContext = i;
+ NtStatus = SamEnumerateAliasesInDomain(
+ DomainHandle,
+ &EnumerationContext,
+ &Buffer,
+ 0, // PreferedMaximumLength
+ &CountReturned
+ );
+
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+ if ( ((i >= AccountCount -1) && (NtStatus == STATUS_SUCCESS)) ||
+ ((i <= AccountCount -1) && (NtStatus == STATUS_MORE_ENTRIES)) ) {
+
+ if (CountReturned != 1) {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ printf(" Expected one entry to be returned.\n");
+ printf(" Received 0x%lx entries instead.\n", CountReturned);
+ TestStatus = FALSE;
+ i = AccountCount + 100;
+ }
+
+ } else {
+
+ printf("Failed\n");
+ if (i < AccountCount -1 ) {
+ printf(" Expected STATUS_MORE_ENTRIES to be returned.\n");
+ } else {
+ printf(" Expected STATUS_SUCCESS to be returned.\n");
+ }
+ printf(" Received 0x%lx instead.\n", NtStatus);
+ printf(" Buffer = 0x%lx\n", (ULONG)Buffer);
+ printf(" CountReturned = 0x%lx\n", CountReturned);
+ TestStatus = FALSE;
+ i = AccountCount + 100;
+ }
+
+ SamFreeMemory( Buffer );
+
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ i = AccountCount + 100;
+ }
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ i = AccountCount + 100;
+
+ }
+ }
+
+ if ( i == AccountCount) {
+ printf("Succeeded\n");
+ }
+
+
+
+
+
+ printf(" Enumerate Users - large prefered length . . . . . . ");
+
+
+ EnumerationContext = 0;
+ NtStatus = SamEnumerateUsersInDomain(
+ DomainHandle,
+ &EnumerationContext,
+ 0,
+ &Buffer,
+ 12000, // PreferedMaximumLength
+ &CountReturned
+ );
+ AccountCount = CountReturned; // Save for future test
+
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+ if (NtStatus == STATUS_SUCCESS) {
+
+ if (CountReturned > 1) {
+ printf("Succeeded\n");
+ for (i=0; i<CountReturned; i++) {
+ printf(" Rid/Name(%ld): 0x%lx / %wZ\n",i,
+ ((PSAM_RID_ENUMERATION)(Buffer))[i].RelativeId,
+ &((PSAM_RID_ENUMERATION)(Buffer))[i].Name
+ );
+ }
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ printf(" Expected several entries to be returned.\n");
+ printf(" Received 0x%lx entries instead.\n", CountReturned);
+ TestStatus = FALSE;
+ }
+
+ } else {
+
+ printf("Failed\n");
+ printf(" Expected STATUS_MORE_ENTRIES to be returned.\n");
+ printf(" Received 0x%lx instead.\n", NtStatus);
+ printf(" Buffer = 0x%lx\n", (ULONG)Buffer);
+ printf(" CountReturned = 0x%lx\n", CountReturned);
+ TestStatus = FALSE;
+ }
+
+ SamFreeMemory( Buffer );
+
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+
+ }
+
+
+
+
+ printf(" Enumerate Users - small prefered length . . . . . . ");
+
+
+ for ( i=0; i<AccountCount; i++) {
+ EnumerationContext = i;
+ NtStatus = SamEnumerateUsersInDomain(
+ DomainHandle,
+ &EnumerationContext,
+ 0,
+ &Buffer,
+ 0, // PreferedMaximumLength
+ &CountReturned
+ );
+
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+ if ( ((i >= AccountCount -1) && (NtStatus == STATUS_SUCCESS)) ||
+ ((i <= AccountCount -1) && (NtStatus == STATUS_MORE_ENTRIES)) ) {
+
+ if (CountReturned != 1) {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ printf(" Expected one entry to be returned.\n");
+ printf(" Received 0x%lx entries instead.\n", CountReturned);
+ TestStatus = FALSE;
+ i = AccountCount + 100;
+ }
+
+ } else {
+
+ printf("Failed\n");
+ if (i < AccountCount -1 ) {
+ printf(" Expected STATUS_MORE_ENTRIES to be returned.\n");
+ } else {
+ printf(" Expected STATUS_SUCCESS to be returned.\n");
+ }
+ printf(" Received 0x%lx instead.\n", NtStatus);
+ printf(" Buffer = 0x%lx\n", (ULONG)Buffer);
+ printf(" CountReturned = 0x%lx\n", CountReturned);
+ TestStatus = FALSE;
+ i = AccountCount + 100;
+ }
+
+ SamFreeMemory( Buffer );
+
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ i = AccountCount + 100;
+ }
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ i = AccountCount + 100;
+
+ }
+ }
+
+ if ( i == AccountCount) {
+ printf("Succeeded\n");
+ }
+
+
+
+
+
+
+
+
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // Lookup Names/IDs Suite //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+
+ // LATER add alias search to lookup name suite.....
+
+
+ printf("\n");
+ printf(" Lookup Names/IDs . . . . . . . . . . . . . . . . . . Suite\n");
+
+
+ printf(" Lookup Names (all existing) . . . . . . . . . . . . . ");
+
+ NtStatus = SamLookupNamesInDomain(
+ DomainHandle,
+ ALL_NAMES_COUNT,
+ &AllNames[0],
+ &LookedUpRids,
+ &LookedUpUses
+ );
+
+
+ if (NT_SUCCESS(NtStatus)) {
+ ASSERT( LookedUpRids != NULL );
+ ASSERT( LookedUpUses != NULL );
+
+ if (
+ (LookedUpRids[0] == AllRids[0]) && (LookedUpUses[0] == AllUses[0])
+ &&
+ (LookedUpRids[1] == AllRids[1]) && (LookedUpUses[1] == AllUses[1])
+ &&
+ (LookedUpRids[2] == AllRids[2]) && (LookedUpUses[2] == AllUses[2])
+ ) {
+
+
+ printf("Succeeded\n");
+
+
+ } else {
+ printf("Failed\n");
+ printf(" Rids or Uses dont match expected values.\n");
+ printf(" Expected Rids: 0x%lx, 0x%lx, 0x%lx\n",
+ AllRids[0], AllRids[1], AllRids[2]);
+ printf(" Received Rids: 0x%lx, 0x%lx, 0x%lx\n",
+ LookedUpRids[0], LookedUpRids[1], LookedUpRids[2]);
+ printf(" Expected Uses: 0x%lx, 0x%lx, 0x%lx\n",
+ AllUses[0], AllUses[1], AllUses[2]);
+ printf(" Received Uses: 0x%lx, 0x%lx, 0x%lx\n",
+ LookedUpUses[0], LookedUpUses[1], LookedUpUses[2]);
+ TestStatus = FALSE;
+ }
+
+
+ SamFreeMemory( LookedUpRids );
+ SamFreeMemory( LookedUpUses );
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+
+ printf(" Lookup Names (Some existing) . . . . . . . . . . . . ");
+
+ NtStatus = SamLookupNamesInDomain(
+ DomainHandle,
+ SOME_NAMES_COUNT,
+ &SomeNames[0],
+ &LookedUpRids,
+ &LookedUpUses
+ );
+
+
+ if (NtStatus == STATUS_SOME_NOT_MAPPED) {
+ ASSERT( LookedUpRids != NULL );
+ ASSERT( LookedUpUses != NULL );
+
+ if (
+ (LookedUpRids[0] == SomeRids[0]) && (LookedUpUses[0] == SomeUses[0])
+ &&
+ (LookedUpRids[1] == SomeRids[1]) && (LookedUpUses[1] == SomeUses[1])
+ &&
+ (LookedUpRids[2] == SomeRids[2]) && (LookedUpUses[2] == SomeUses[2])
+ &&
+ (LookedUpRids[3] == SomeRids[3]) && (LookedUpUses[3] == SomeUses[3])
+ &&
+ (LookedUpRids[4] == SomeRids[4]) && (LookedUpUses[4] == SomeUses[4])
+ &&
+ (LookedUpRids[5] == SomeRids[5]) && (LookedUpUses[5] == SomeUses[5])
+ &&
+ (LookedUpRids[6] == SomeRids[6]) && (LookedUpUses[6] == SomeUses[6])
+ ) {
+
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Rids or Uses dont match expected values.\n");
+ printf(" Expected Rids: 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx\n",
+ SomeRids[0], SomeRids[1], SomeRids[2], SomeRids[3], SomeRids[4], SomeRids[5], SomeRids[6]);
+ printf(" Received Rids: 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx\n",
+ LookedUpRids[0], LookedUpRids[1], LookedUpRids[2], LookedUpRids[3], LookedUpRids[4], LookedUpRids[5], LookedUpRids[6]);
+ printf(" Expected Uses: 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx\n",
+ SomeUses[0], SomeUses[1], SomeUses[2], SomeUses[3], SomeUses[4], SomeUses[5], SomeUses[6]);
+ printf(" Received Uses: 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx\n",
+ LookedUpUses[0], LookedUpUses[1], LookedUpUses[2], LookedUpUses[3], LookedUpUses[4], LookedUpUses[5], LookedUpUses[2]);
+ TestStatus = FALSE;
+ }
+
+
+ SamFreeMemory( LookedUpRids );
+ SamFreeMemory( LookedUpUses );
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+ printf(" Lookup Names (None existing) . . . . . . . . . . . . ");
+
+ NtStatus = SamLookupNamesInDomain(
+ DomainHandle,
+ NO_NAMES_COUNT,
+ &NoNames[0],
+ &LookedUpRids,
+ &LookedUpUses
+ );
+
+
+ if (NtStatus == STATUS_NONE_MAPPED) {
+ ASSERT( LookedUpRids == NULL );
+ ASSERT( LookedUpUses == NULL );
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+ printf(" Lookup SIDs (all existing) . . . . . . . . . . . . . ");
+
+ NtStatus = SamLookupIdsInDomain(
+ DomainHandle,
+ ALL_NAMES_COUNT,
+ &AllRids[0],
+ &LookedUpNames,
+ &LookedUpUses
+ );
+
+
+ if (NT_SUCCESS(NtStatus)) {
+ ASSERT( LookedUpUses != NULL );
+ ASSERT( LookedUpNames != NULL );
+ ASSERT( LookedUpNames[0].Buffer != NULL );
+ ASSERT( LookedUpNames[1].Buffer != NULL );
+ ASSERT( LookedUpNames[2].Buffer != NULL );
+
+ if (
+ (LookedUpUses[0] == AllUses[0]) &&
+ (LookedUpUses[1] == AllUses[1]) &&
+ (LookedUpUses[2] == AllUses[2]) &&
+ !RtlCompareString( (PSTRING)&LookedUpNames[0], (PSTRING)&AllNames[0], TRUE ) &&
+ !RtlCompareString( (PSTRING)&LookedUpNames[1], (PSTRING)&AllNames[1], TRUE ) &&
+ !RtlCompareString( (PSTRING)&LookedUpNames[2], (PSTRING)&AllNames[2], TRUE )
+ ) {
+
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Names or Uses dont match expected values.\n");
+ printf(" Expected Name[0]: %wZ\n", &AllNames[0] );
+ printf(" Received Name[0]: %wZ\n", &LookedUpNames[0] );
+ printf(" Expected Name[1]: %wZ\n", &AllNames[1] );
+ printf(" Received Name[1]: %wZ\n", &LookedUpNames[1] );
+ printf(" Expected Name[2]: %wZ\n", &AllNames[2] );
+ printf(" Received Name[2]: %wZ\n", &LookedUpNames[2] );
+
+ printf(" Expected Uses: 0x%lx, 0x%lx, 0x%lx\n",
+ AllUses[0], AllUses[1], AllUses[2]);
+ printf(" Received Uses: 0x%lx, 0x%lx, 0x%lx\n",
+ LookedUpUses[0], LookedUpUses[1], LookedUpUses[2]);
+ TestStatus = FALSE;
+ }
+
+
+ SamFreeMemory( LookedUpUses );
+ SamFreeMemory( LookedUpNames );
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+
+ printf(" Lookup SIDs (Some existing) . . . . . . . . . . . . . ");
+
+ NtStatus = SamLookupIdsInDomain(
+ DomainHandle,
+ SOME_NAMES_COUNT,
+ &SomeRids[0],
+ &LookedUpNames,
+ &LookedUpUses
+ );
+
+
+ if (NtStatus == STATUS_SOME_NOT_MAPPED) {
+ ASSERT( LookedUpUses != NULL );
+ ASSERT( LookedUpNames != NULL );
+ ASSERT( LookedUpNames[0].Buffer != NULL );
+ ASSERT( LookedUpNames[1].Buffer != NULL );
+ ASSERT( LookedUpNames[2].Buffer == NULL ); // Unknown
+ ASSERT( LookedUpNames[3].Buffer == NULL ); // Unknown
+ ASSERT( LookedUpNames[4].Buffer == NULL ); // Unknown
+ ASSERT( LookedUpNames[5].Buffer != NULL );
+ ASSERT( LookedUpNames[6].Buffer == NULL ); // Unknown
+
+ if (
+ (LookedUpUses[0] == SomeUses[0]) &&
+ (LookedUpUses[1] == SomeUses[1]) &&
+ (LookedUpUses[2] == SomeUses[2]) &&
+ !RtlCompareString( (PSTRING)&LookedUpNames[0], (PSTRING)&SomeNames[0], TRUE ) &&
+ !RtlCompareString( (PSTRING)&LookedUpNames[1], (PSTRING)&SomeNames[1], TRUE ) &&
+ !RtlCompareString( (PSTRING)&LookedUpNames[5], (PSTRING)&SomeNames[5], TRUE )
+ ) {
+
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Names or Uses dont match expected values.\n");
+ printf(" Expected Name[0]: %wZ\n", &SomeNames[0] );
+ printf(" Received Name[0]: %wZ\n", &LookedUpNames[0] );
+ printf(" Expected Name[1]: %wZ\n", &SomeNames[1] );
+ printf(" Received Name[1]: %wZ\n", &LookedUpNames[1] );
+ printf(" Name[2]: (Unknown)\n");
+ printf(" Name[3]: (Unknown)\n");
+ printf(" Name[4]: (Unknown)\n");
+ printf(" Expected Name[5]: %wZ\n", &SomeNames[5] );
+ printf(" Received Name[5]: %wZ\n", &LookedUpNames[5] );
+ printf(" Name[6]: (Unknown)\n");
+
+ printf(" Expected Uses: 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx\n",
+ SomeUses[0], SomeUses[1], SomeUses[2], SomeUses[3], SomeUses[4], SomeUses[5], SomeUses[6]);
+ printf(" Received Uses: 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx\n",
+ LookedUpUses[0], LookedUpUses[1], LookedUpUses[2], LookedUpUses[3], LookedUpUses[4], LookedUpUses[5], LookedUpUses[2]);
+ TestStatus = FALSE;
+ }
+
+
+ SamFreeMemory( LookedUpUses );
+ SamFreeMemory( LookedUpNames );
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+
+ printf(" Lookup SIDs (None existing) . . . . . . . . . . . . . ");
+
+ NtStatus = SamLookupIdsInDomain(
+ DomainHandle,
+ NO_NAMES_COUNT,
+ &NoRids[0],
+ &LookedUpNames,
+ &LookedUpUses
+ );
+
+
+ if (NtStatus == STATUS_NONE_MAPPED) {
+ ASSERT( LookedUpUses == NULL );
+ ASSERT( LookedUpNames == NULL );
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+
+
+
+
+ return TestStatus;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Group Object Test Suite //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+
+BOOLEAN
+GroupTestSuite(
+ HANDLE DomainHandle,
+ ULONG Pass
+ )
+
+{
+ NTSTATUS NtStatus, IgnoreStatus;
+ HANDLE GroupHandle1, GroupHandle2, UserHandle1;
+ ULONG CountReturned, NameLength, i, MemberCount;
+ ULONG UserRid, GroupRid;
+ PVOID Buffer, Buffer1, Buffer2;
+ SAM_ENUMERATE_HANDLE EnumerationContext;
+ PULONG Members, Attributes;
+ PSID_NAME_USE LookedUpUses;
+ PULONG LookedUpRids;
+ UNICODE_STRING AccountNames[10], AccountName;
+ STRING AccountNameAnsi;
+
+ BOOLEAN IndividualTestSucceeded, DeleteUser;
+ BOOLEAN TestStatus = TRUE;
+
+
+ if (Pass == 1) {
+ //
+ // This test suite assumes that lookup and enumeration API funciton
+ // properly.
+ //
+
+ printf("\n");
+ printf("\n");
+ printf(" Group (Pass #1) . . . . . . . . . . . . . . . . . . . Test\n");
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // Open Group Suite //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+ printf(" Open Group . . . . . . . . . . . . . . . . . . . . . Suite\n");
+ printf(" Open Groups . . . . . . . . . . . . . . . . . . . . . ");
+ IndividualTestSucceeded = TRUE;
+ EnumerationContext = 0;
+ NtStatus = SamEnumerateGroupsInDomain(
+ DomainHandle,
+ &EnumerationContext,
+ &Buffer,
+ 12000, // PreferedMaximumLength
+ &CountReturned
+ );
+
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(Buffer != NULL);
+ ASSERT(CountReturned > 0);
+
+ for (i=0; i<CountReturned; i++) {
+
+ NtStatus = SamOpenGroup(
+ DomainHandle,
+ GROUP_ALL_ACCESS,
+ ((PSAM_RID_ENUMERATION)(Buffer))[i].RelativeId,
+ &GroupHandle1
+ );
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ NtStatus = SamOpenGroup(
+ DomainHandle,
+ GENERIC_READ,
+ ((PSAM_RID_ENUMERATION)(Buffer))[i].RelativeId,
+ &GroupHandle2
+ );
+
+ if (NT_SUCCESS(NtStatus)) {
+ IgnoreStatus = SamCloseHandle( GroupHandle2 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ printf(" Failed opening group second time.\n");
+ printf(" Rid of account is: 0x%lx\n",
+ ((PSAM_RID_ENUMERATION)(Buffer))[i].RelativeId);
+ printf(" Name of account is: %wZ\n",
+ &((PSAM_RID_ENUMERATION)(Buffer))[i].Name );
+ TestStatus = FALSE;
+ IndividualTestSucceeded = FALSE;
+ }
+
+ IgnoreStatus = SamCloseHandle( GroupHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+ } else {
+
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ printf(" Failed opening group for first time.\n");
+ printf(" Rid of account is: 0x%lx\n",
+ ((PSAM_RID_ENUMERATION)(Buffer))[i].RelativeId);
+ printf(" Name of account is: %wZ\n",
+ &((PSAM_RID_ENUMERATION)(Buffer))[i].Name );
+ TestStatus = FALSE;
+ IndividualTestSucceeded = FALSE;
+ }
+
+ if (!IndividualTestSucceeded) {
+ printf(" ");
+ }
+ }
+
+
+ SamFreeMemory( Buffer );
+ if (IndividualTestSucceeded) {
+ printf("Succeeded\n");
+ }
+
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // Query Group Suite //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+ printf("\n");
+ printf(" Query Group . . . . . . . . . . . . . . . . . . . . . Suite\n");
+
+ printf(" Query Group General Information . . . . . . . . . . . ");
+
+
+ NtStatus = SamOpenGroup(
+ DomainHandle,
+ GROUP_READ_INFORMATION,
+ DOMAIN_GROUP_RID_USERS,
+ &GroupHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ Buffer = NULL;
+ NtStatus = SamQueryInformationGroup(
+ GroupHandle1,
+ GroupGeneralInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+
+ if ( (((GROUP_GENERAL_INFORMATION *)Buffer)->Name.MaximumLength > 0) &&
+ (((GROUP_GENERAL_INFORMATION *)Buffer)->Name.Buffer != NULL) ) {
+
+ printf("Succeeded\n");
+
+ printf(" Member Count is: 0x%lx\n",
+ (((GROUP_GENERAL_INFORMATION *)Buffer)->MemberCount) );
+ printf(" Attributes are: 0x%lx\n",
+ (((GROUP_GENERAL_INFORMATION *)Buffer)->Attributes) );
+ printf(" Group Name is: %wZ\n",
+ &(((GROUP_GENERAL_INFORMATION *)Buffer)->Name) );
+
+
+
+ } else {
+ printf("Failed\n");
+ printf(" Group Name not returned.\n");
+ TestStatus = FALSE;
+ }
+ SamFreeMemory( Buffer );
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ IgnoreStatus = SamCloseHandle( GroupHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+
+
+ printf(" Query Group Name Information . . . . . . . . . . . . ");
+
+
+ NtStatus = SamOpenGroup(
+ DomainHandle,
+ GROUP_READ_INFORMATION,
+ DOMAIN_GROUP_RID_USERS,
+ &GroupHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ Buffer = NULL;
+ NtStatus = SamQueryInformationGroup(
+ GroupHandle1,
+ GroupNameInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+
+ if ( (((GROUP_NAME_INFORMATION *)Buffer)->Name.MaximumLength > 0) &&
+ (((GROUP_NAME_INFORMATION *)Buffer)->Name.Buffer != NULL) ) {
+
+ printf("Succeeded\n");
+
+ printf(" Group Name is: %wZ\n",
+ &(((GROUP_NAME_INFORMATION *)Buffer)->Name) );
+
+
+
+ } else {
+ printf("Failed\n");
+ printf(" Group Name not returned.\n");
+ TestStatus = FALSE;
+ }
+ SamFreeMemory( Buffer );
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ IgnoreStatus = SamCloseHandle( GroupHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+
+
+ printf(" Query Group Admin Comment Information . . . . . . . . ");
+
+
+ NtStatus = SamOpenGroup(
+ DomainHandle,
+ GROUP_READ_INFORMATION,
+ DOMAIN_GROUP_RID_USERS,
+ &GroupHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ Buffer = NULL;
+ NtStatus = SamQueryInformationGroup(
+ GroupHandle1,
+ GroupAdminCommentInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+
+ if ( (((GROUP_ADM_COMMENT_INFORMATION *)Buffer)->AdminComment.MaximumLength >= 0) ) {
+
+ printf("Succeeded\n");
+
+ printf(" Group Admin Comment is: %wZ\n",
+ &(((GROUP_ADM_COMMENT_INFORMATION *)Buffer)->AdminComment) );
+
+
+
+ } else {
+ printf("Failed\n");
+ printf(" Group Admin Comment not returned.\n");
+ TestStatus = FALSE;
+ }
+ SamFreeMemory( Buffer );
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ IgnoreStatus = SamCloseHandle( GroupHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+
+
+ printf(" Query Group Attribute Information . . . . . . . . . . ");
+
+
+ NtStatus = SamOpenGroup(
+ DomainHandle,
+ GROUP_READ_INFORMATION,
+ DOMAIN_GROUP_RID_USERS,
+ &GroupHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ Buffer = NULL;
+ NtStatus = SamQueryInformationGroup(
+ GroupHandle1,
+ GroupAttributeInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+
+
+ printf("Succeeded\n");
+
+ printf(" Attributes are: 0x%lx\n",
+ (((GROUP_ATTRIBUTE_INFORMATION *)Buffer)->Attributes) );
+
+
+ SamFreeMemory( Buffer );
+
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ IgnoreStatus = SamCloseHandle( GroupHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // Get Members Of Group Suite //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+ printf("\n");
+ printf(" Get Members . . . . . . . . . . . . . . . . . . . . . Suite\n");
+
+ printf(" Get Members of Well-Known Account . . . . . . . . . . ");
+
+ NtStatus = SamOpenGroup(
+ DomainHandle,
+ GROUP_LIST_MEMBERS,
+ DOMAIN_GROUP_RID_USERS,
+ &GroupHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ Buffer = NULL;
+ NtStatus = SamGetMembersInGroup(
+ GroupHandle1,
+ &Members,
+ &Attributes,
+ &MemberCount
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Members != NULL || Attributes != NULL) {
+
+ printf("Succeeded\n");
+
+
+ printf(" Member Count: %d Users\n", MemberCount);
+ for ( i=0; i<MemberCount; i++) {
+
+ printf(" User[%d] Rid/Attributes: 0x%lx/0x%lx\n",
+ i, Members[i], Attributes[i]);
+
+
+ }
+
+ SamFreeMemory( Members );
+ SamFreeMemory( Attributes );
+
+
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ IgnoreStatus = SamCloseHandle( GroupHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+
+ printf(" Get Members of Empty Group. . . . . . . . . . . . . . ");
+
+ //
+ // This group was created earlier in the test
+ //
+
+ RtlInitString( &AccountNameAnsi, GROUP_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountNames[0], &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ NtStatus = SamLookupNamesInDomain(
+ DomainHandle,
+ 1,
+ &AccountNames[0],
+ &LookedUpRids,
+ &LookedUpUses
+ );
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(LookedUpUses[0] == SidTypeGroup);
+ RtlFreeUnicodeString( &AccountNames[0] );
+
+
+
+ GroupHandle1 = NULL;
+
+ NtStatus = SamOpenGroup( DomainHandle, GROUP_LIST_MEMBERS, LookedUpRids[0], &GroupHandle1 );
+ TST_SUCCESS_ASSERT(NtStatus);
+ SamFreeMemory( LookedUpUses ); SamFreeMemory( LookedUpRids );
+
+ NtStatus = SamGetMembersInGroup(
+ GroupHandle1,
+ &Members,
+ &Attributes,
+ &MemberCount
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (MemberCount == 0) {
+
+ printf("Succeeded\n");
+
+
+
+
+ } else {
+ printf("Failed\n");
+ printf(" Buffer addresses set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ printf(" Member Count: %d\n", MemberCount);
+ for ( i=0; i<MemberCount; i++) {
+
+ printf(" User[%d] Rid/Attributes: 0x%lx/0x%lx\n",
+ i, Members[i], Attributes[i]);
+ }
+
+ SamFreeMemory( Members );
+ SamFreeMemory( Attributes );
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ IgnoreStatus = SamCloseHandle( GroupHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // Set Group Suite (pass 1) //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+ printf("\n");
+ printf(" Set Group . . . . . . . . . . . . . . . . . . . . . . Suite\n");
+
+
+ printf(" Set Attribute . . . . . . . . . . . . . . . . . . . . ");
+ NtStatus = SamOpenGroup(
+ DomainHandle,
+ GROUP_WRITE_ACCOUNT | GROUP_READ_INFORMATION,
+ DOMAIN_GROUP_RID_USERS,
+ &GroupHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ Buffer1 = NULL;
+ NtStatus = SamQueryInformationGroup(
+ GroupHandle1,
+ GroupAttributeInformation,
+ &Buffer1
+ );
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(Buffer1 != NULL);
+
+ //
+ // Change the value and write it back
+ //
+
+ ((GROUP_ATTRIBUTE_INFORMATION *)Buffer1)->Attributes ^=
+ SE_GROUP_ENABLED_BY_DEFAULT;
+
+
+ NtStatus = SamSetInformationGroup(
+ GroupHandle1,
+ GroupAttributeInformation,
+ Buffer1
+ );
+ if (NT_SUCCESS(NtStatus)) {
+
+ //
+ // Check the written value to make sure it stuck
+ //
+
+ Buffer2 = NULL;
+ NtStatus = SamQueryInformationGroup(
+ GroupHandle1,
+ GroupAttributeInformation,
+ &Buffer2
+ );
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(Buffer2 != NULL);
+
+ if (((GROUP_ATTRIBUTE_INFORMATION *)Buffer1)->Attributes ==
+ ((GROUP_ATTRIBUTE_INFORMATION *)Buffer2)->Attributes ) {
+
+ printf("Succeeded\n");
+
+ SamFreeMemory( Buffer2 );
+
+ } else {
+ printf("Failed\n");
+ printf(" Returned Value Doesn't Match Set Value.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ SamFreeMemory( Buffer1 );
+ IgnoreStatus = SamCloseHandle( GroupHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+
+
+
+ printf(" Set Admin Comment . . . . . . . . . . . . . . . . . . ");
+
+ NtStatus = SamOpenGroup(
+ DomainHandle,
+ GROUP_WRITE_ACCOUNT | GROUP_READ_INFORMATION,
+ DOMAIN_GROUP_RID_USERS,
+ &GroupHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ //
+ // Get the current value...
+ //
+
+ Buffer1 = NULL;
+ NtStatus = SamQueryInformationGroup(
+ GroupHandle1,
+ GroupAdminCommentInformation,
+ &Buffer1
+ );
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(Buffer1 != NULL);
+
+
+ //
+ // Change the field to a new value and write it out.
+ //
+
+ NameLength = ((GROUP_ADM_COMMENT_INFORMATION *)Buffer1)->AdminComment.Length;
+ if ( NameLength == DummyString1.Length ) {
+ ((GROUP_ADM_COMMENT_INFORMATION *)Buffer1)->AdminComment = DummyString2;
+ } else {
+ ((GROUP_ADM_COMMENT_INFORMATION *)Buffer1)->AdminComment = DummyString1;
+ }
+
+ NtStatus = SamSetInformationGroup(
+ GroupHandle1,
+ GroupAdminCommentInformation,
+ Buffer1
+ );
+ if ( NT_SUCCESS(NtStatus) ) {
+
+ //
+ // Now check that the change was really made...
+ //
+
+ Buffer2 = NULL;
+ NtStatus = SamQueryInformationGroup(
+ GroupHandle1,
+ GroupAdminCommentInformation,
+ &Buffer2
+ );
+ ASSERT(NT_SUCCESS( NtStatus ) );
+ if (
+ !RtlCompareString(
+ (PSTRING)&((GROUP_ADM_COMMENT_INFORMATION *)Buffer1)->AdminComment,
+ (PSTRING)&((GROUP_ADM_COMMENT_INFORMATION *)Buffer2)->AdminComment,
+ TRUE)
+ ) {
+
+ printf("Succeeded\n");
+
+ } else {
+
+ printf("Failed\n");
+ printf(" Value queried doesn't match value written\n");
+ printf(" Value Written is %wZ\n",
+ (PUNICODE_STRING)&((GROUP_ADM_COMMENT_INFORMATION *)Buffer1)->AdminComment);
+ printf(" Value Retrieved is %wZ\n",
+ (PUNICODE_STRING)&((GROUP_ADM_COMMENT_INFORMATION *)Buffer2)->AdminComment);
+
+ TestStatus = FALSE;
+
+ }
+
+ SamFreeMemory( Buffer1 );
+ SamFreeMemory( Buffer2 );
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ SamFreeMemory( Buffer1 );
+
+ }
+
+
+
+
+
+
+ } // END PASS #1
+ if (Pass == 2) {
+
+ printf("\n");
+ printf("\n");
+ printf(" Group (Pass #2) . . . . . . . . . . . . . . . . . . . Test\n");
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // Delete Group Suite //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+ printf("\n");
+ printf(" Delete Group . . . . . . . . . . . . . . . . . . . . Suite\n");
+
+ printf(" Delete Normal Group . . . . . . . . . . . . . . . . . ");
+
+ //
+ // This group was created in pass #1
+ //
+
+ RtlInitString( &AccountNameAnsi, GROUP_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountNames[0], &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ NtStatus = SamLookupNamesInDomain(
+ DomainHandle,
+ 1,
+ &AccountNames[0],
+ &LookedUpRids,
+ &LookedUpUses
+ );
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(LookedUpUses[0] == SidTypeGroup);
+ RtlFreeUnicodeString( &AccountNames[0] );
+
+
+
+ GroupHandle1 = NULL;
+
+ NtStatus = SamOpenGroup( DomainHandle, DELETE, LookedUpRids[0], &GroupHandle1 );
+ TST_SUCCESS_ASSERT(NtStatus);
+ SamFreeMemory( LookedUpUses ); SamFreeMemory( LookedUpRids );
+
+ NtStatus = SamDeleteGroup( GroupHandle1 );
+ if (NT_SUCCESS(NtStatus)) {
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+
+ printf(" Delete Well Known Group . . . . . . . . . . . . . . . ");
+
+ GroupHandle1 = NULL;
+
+ NtStatus = SamOpenGroup( DomainHandle, DELETE, DOMAIN_GROUP_RID_USERS, &GroupHandle1 );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ NtStatus = SamDeleteGroup( GroupHandle1 );
+ if (NtStatus == STATUS_SPECIAL_ACCOUNT) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+ NtStatus = SamCloseHandle( GroupHandle1 );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+
+
+
+
+
+
+
+ printf(" Delete Primary Group Of A User. . . . . . . . . . . . ");
+
+ //
+ // Make a user (might already exist)
+ // Make a group
+ // Make the group the user's primary group
+ // Attempt to delete the group
+ // Change the user so the group isn't the primary group
+ // delete the group
+ // If we created the user, delete it.
+
+ //
+ // The following user might already exist (from earlier in the test)
+ //
+
+ RtlInitString( &AccountNameAnsi, USER_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ //InitializeObjectAttributes( &ObjectAttributes, &AccountName, 0, 0, NULL );
+
+
+ UserRid = 0;
+ UserHandle1 = NULL;
+ NtStatus = SamCreateUserInDomain(
+ DomainHandle,
+ &AccountName,
+ USER_ALL_ACCESS,
+ &UserHandle1,
+ &UserRid
+ );
+ RtlFreeUnicodeString( &AccountName );
+ DeleteUser = TRUE;
+ if (NtStatus == STATUS_USER_EXISTS) {
+ DeleteUser = FALSE;
+ RtlInitString( &AccountNameAnsi, USER_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountNames[0], &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ NtStatus = SamLookupNamesInDomain(
+ DomainHandle,
+ 1,
+ &AccountNames[0],
+ &LookedUpRids,
+ &LookedUpUses
+ );
+ RtlFreeUnicodeString( &AccountNames[0] );
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(LookedUpUses[0] == SidTypeUser);
+ UserRid = LookedUpRids[0];
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_ALL_ACCESS,
+ UserRid,
+ &UserHandle1);
+ SamFreeMemory( LookedUpUses ); SamFreeMemory( LookedUpRids );
+ }
+
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ //
+ // create the group
+ //
+
+ RtlInitString( &AccountNameAnsi, GROUP_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ //InitializeObjectAttributes( &ObjectAttributes, &AccountName, 0, 0, NULL );
+
+ GroupRid = 0;
+ GroupHandle1 = NULL;
+ NtStatus = SamCreateGroupInDomain(
+ DomainHandle,
+ &AccountName,
+ GROUP_ALL_ACCESS,
+ &GroupHandle1,
+ &GroupRid
+ );
+ RtlFreeUnicodeString( &AccountName );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ //
+ // Make the user a member of this group
+ //
+
+ NtStatus = SamAddMemberToGroup(
+ GroupHandle1,
+ UserRid,
+ SE_GROUP_MANDATORY |
+ SE_GROUP_ENABLED_BY_DEFAULT |
+ SE_GROUP_ENABLED
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+
+
+
+ //
+ // Now try to delete the group
+ //
+
+ NtStatus = SamDeleteGroup( GroupHandle1 );
+ if (NtStatus == STATUS_MEMBER_IN_GROUP) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+ //
+ // Now get rid of the group and possibly the user account
+ //
+
+ NtStatus = SamRemoveMemberFromGroup(GroupHandle1, UserRid);
+ ASSERT(NT_SUCCESS(NtStatus));
+
+
+ NtStatus = SamDeleteGroup( GroupHandle1 );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ if (DeleteUser == TRUE) {
+ NtStatus = SamDeleteUser( UserHandle1 );
+ ASSERT(NT_SUCCESS(NtStatus));
+ } else {
+ NtStatus = SamCloseHandle( UserHandle1 );
+ ASSERT(NT_SUCCESS(NtStatus));
+ }
+
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // Add/Remove Member Suite //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+ printf("\n");
+ printf(" Add/Remove Member Suite . . . . . . . . . . . . . . . Suite\n");
+
+ printf(" Add Member . . . . . . . . . . . . . . . . . . . . . ");
+
+ //
+ // This test sets things up for the next test
+ //
+
+ //
+ // The following user might already exist (from earlier in the test)
+ //
+
+ RtlInitString( &AccountNameAnsi, USER_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ //InitializeObjectAttributes( &ObjectAttributes, &AccountName, 0, 0, NULL );
+
+ UserRid = 0;
+ UserHandle1 = NULL;
+ NtStatus = SamCreateUserInDomain(
+ DomainHandle,
+ &AccountName,
+ USER_ALL_ACCESS,
+ &UserHandle1,
+ &UserRid
+ );
+ RtlFreeUnicodeString( &AccountName );
+ DeleteUser = TRUE;
+ if (NtStatus == STATUS_USER_EXISTS) {
+ DeleteUser = FALSE;
+ RtlInitString( &AccountNameAnsi, USER_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountNames[0], &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ NtStatus = SamLookupNamesInDomain(
+ DomainHandle,
+ 1,
+ &AccountNames[0],
+ &LookedUpRids,
+ &LookedUpUses
+ );
+ RtlFreeUnicodeString( &AccountNames[0] );
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(LookedUpUses[0] == SidTypeUser);
+ UserRid = LookedUpRids[0];
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_ALL_ACCESS,
+ UserRid,
+ &UserHandle1);
+ SamFreeMemory( LookedUpUses ); SamFreeMemory( LookedUpRids );
+ }
+
+ ASSERT(NT_SUCCESS(NtStatus));
+
+
+ //
+ // create the group
+ //
+
+ RtlInitString( &AccountNameAnsi, GROUP_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ //InitializeObjectAttributes( &ObjectAttributes, &AccountName, 0, 0, NULL );
+
+ GroupRid = 0;
+ GroupHandle1 = NULL;
+ NtStatus = SamCreateGroupInDomain(
+ DomainHandle,
+ &AccountName,
+ GROUP_ALL_ACCESS,
+ &GroupHandle1,
+ &GroupRid
+ );
+ RtlFreeUnicodeString( &AccountName );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ //
+ // Make the user a member of this group
+ //
+
+ NtStatus = SamAddMemberToGroup(
+ GroupHandle1,
+ UserRid,
+ SE_GROUP_MANDATORY |
+ SE_GROUP_ENABLED_BY_DEFAULT |
+ SE_GROUP_ENABLED
+ );
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ NtStatus = SamGetMembersInGroup(
+ GroupHandle1,
+ &Members,
+ &Attributes,
+ &MemberCount
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ NtStatus = STATUS_MEMBER_NOT_IN_GROUP;
+ for ( i=0; i<MemberCount; i++) {
+ if (Members[i] == UserRid) {
+ NtStatus = STATUS_SUCCESS;
+ break;
+ }
+ }
+
+ if (NT_SUCCESS(NtStatus)) {
+ if (Attributes[i] == SE_GROUP_MANDATORY |
+ SE_GROUP_ENABLED_BY_DEFAULT |
+ SE_GROUP_ENABLED) {
+ printf("Succeeded\n");
+ } else {
+ printf("Failed\n");
+ printf("Member Added but attributes don't match expected value.\n");
+ printf("Expected value: 0x%lx\n",(SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED));
+ printf("Retrieved value: 0x%lx\n",Attributes[i]);
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but user not in member list for group.\n");
+ TestStatus = FALSE;
+ }
+
+
+ if (Members != NULL) {
+ SamFreeMemory( Members );
+ SamFreeMemory( Attributes );
+ }
+
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+
+ printf(" Remove Member . . . . . . . . . . . . . . . . . . . . ");
+
+ //
+ // The previous test sets this one up.
+ //
+
+ //
+ // Now try to remove the user from the group
+ //
+
+ NtStatus = SamRemoveMemberFromGroup(GroupHandle1, UserRid);
+ if (NT_SUCCESS(NtStatus)) {
+
+ NtStatus = SamGetMembersInGroup(
+ GroupHandle1,
+ &Members,
+ &Attributes,
+ &MemberCount
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ for ( i=0; i<MemberCount; i++) {
+ if (Members[i] == UserRid) {
+ NtStatus = STATUS_MEMBER_IN_GROUP;
+ break;
+ }
+ }
+
+ if (NT_SUCCESS(NtStatus)) {
+ printf("Succeeded\n");
+ } else {
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but user still in member list for group.\n");
+ TestStatus = FALSE;
+ }
+
+
+ SamFreeMemory( Members );
+ SamFreeMemory( Attributes );
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+ //
+ // and clean up the user and group accounts
+ //
+
+ NtStatus = SamDeleteGroup( GroupHandle1 );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ if (DeleteUser == TRUE) {
+ NtStatus = SamDeleteUser( UserHandle1 );
+ ASSERT(NT_SUCCESS(NtStatus));
+ } else {
+ NtStatus = SamCloseHandle( UserHandle1 );
+ ASSERT(NT_SUCCESS(NtStatus));
+ }
+
+
+
+
+
+
+ printf(" Add Non-Existant Member . . . . . . . . . . . . . . . ");
+
+ //
+ // create the group
+ //
+
+ RtlInitString( &AccountNameAnsi, GROUP_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ //InitializeObjectAttributes( &ObjectAttributes, &AccountName, 0, 0, NULL );
+
+ GroupRid = 0;
+ GroupHandle1 = NULL;
+ NtStatus = SamCreateGroupInDomain(
+ DomainHandle,
+ &AccountName,
+ GROUP_ALL_ACCESS,
+ &GroupHandle1,
+ &GroupRid
+ );
+ RtlFreeUnicodeString( &AccountName );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ //
+ // Specify a non-existant user be added to this group
+ //
+
+ UserRid = 30732579; // Pretty sure this user doesn't exist.
+ NtStatus = SamAddMemberToGroup(
+ GroupHandle1,
+ UserRid,
+ SE_GROUP_MANDATORY |
+ SE_GROUP_ENABLED_BY_DEFAULT |
+ SE_GROUP_ENABLED
+ );
+
+ if (NtStatus == STATUS_NO_SUCH_USER) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+ NtStatus = SamDeleteGroup( GroupHandle1 );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+
+
+
+ printf(" Remove Non-existant Member . . . . . . . . . . . . . ");
+
+ //
+ // create the group
+ //
+
+ RtlInitString( &AccountNameAnsi, GROUP_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ //InitializeObjectAttributes( &ObjectAttributes, &AccountName, 0, 0, NULL );
+
+ GroupRid = 0;
+ GroupHandle1 = NULL;
+ NtStatus = SamCreateGroupInDomain(
+ DomainHandle,
+ &AccountName,
+ GROUP_ALL_ACCESS,
+ &GroupHandle1,
+ &GroupRid
+ );
+ RtlFreeUnicodeString( &AccountName );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ //
+ // Specify a non-existant user be removed from this group
+ //
+
+ UserRid = 30732579; // Pretty sure this user doesn't exist.
+ NtStatus = SamRemoveMemberFromGroup( GroupHandle1, UserRid );
+
+ if (NtStatus == STATUS_NO_SUCH_USER) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+ NtStatus = SamDeleteGroup( GroupHandle1 );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+
+
+
+ printf(" Remove Primary Group Of Member . . . . . . . . . . . ");
+
+
+ //
+ // Make a user (might already exist)
+ // Make a group
+ // Make the group the user's primary group
+ // Attempt to remove the group (should fail)
+ // Change the user so the group isn't the primary group
+ // remove the group
+ // delete the group
+ // If we created the user, delete it.
+
+ //
+ // The following user might already exist (from earlier in the test)
+ //
+
+ RtlInitString( &AccountNameAnsi, USER_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ //InitializeObjectAttributes( &ObjectAttributes, &AccountName, 0, 0, NULL );
+
+ UserRid = 0;
+ UserHandle1 = NULL;
+ NtStatus = SamCreateUserInDomain(
+ DomainHandle,
+ &AccountName,
+ USER_ALL_ACCESS,
+ &UserHandle1,
+ &UserRid
+ );
+ RtlFreeUnicodeString( &AccountName );
+ DeleteUser = TRUE;
+ if (NtStatus == STATUS_USER_EXISTS) {
+ DeleteUser = FALSE;
+ RtlInitString( &AccountNameAnsi, USER_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountNames[0], &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ NtStatus = SamLookupNamesInDomain(
+ DomainHandle,
+ 1,
+ &AccountNames[0],
+ &LookedUpRids,
+ &LookedUpUses
+ );
+ RtlFreeUnicodeString( &AccountNames[0] );
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(LookedUpUses[0] == SidTypeUser);
+ UserRid = LookedUpRids[0];
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_ALL_ACCESS,
+ UserRid,
+ &UserHandle1);
+ SamFreeMemory( LookedUpUses ); SamFreeMemory( LookedUpRids );
+ }
+
+ ASSERT(NT_SUCCESS(NtStatus));
+
+
+ //
+ // create the group
+ //
+
+ RtlInitString( &AccountNameAnsi, GROUP_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ //InitializeObjectAttributes( &ObjectAttributes, &AccountName, 0, 0, NULL );
+
+ GroupRid = 0;
+ GroupHandle1 = NULL;
+ NtStatus = SamCreateGroupInDomain(
+ DomainHandle,
+ &AccountName,
+ GROUP_ALL_ACCESS,
+ &GroupHandle1,
+ &GroupRid
+ );
+ RtlFreeUnicodeString( &AccountName );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ //
+ // Make the user a member of this group
+ //
+
+ NtStatus = SamAddMemberToGroup(
+ GroupHandle1,
+ UserRid,
+ SE_GROUP_MANDATORY |
+ SE_GROUP_ENABLED_BY_DEFAULT |
+ SE_GROUP_ENABLED
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+
+ //
+ // Set the user's primary group Id to be this group
+ //
+
+ NtStatus = SamSetInformationUser(
+ UserHandle1,
+ UserPrimaryGroupInformation,
+ &GroupRid
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+
+
+ //
+ // Now try to remove the user from the group
+ //
+
+ NtStatus = SamRemoveMemberFromGroup(GroupHandle1, UserRid);
+ if (NtStatus == STATUS_MEMBERS_PRIMARY_GROUP) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+ //
+ // Set the user's primary group Id back and remove the user
+ // from the group
+ //
+
+ GroupRid = DOMAIN_GROUP_RID_USERS;
+ NtStatus = SamSetInformationUser(
+ UserHandle1,
+ UserPrimaryGroupInformation,
+ &GroupRid
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+ NtStatus = SamRemoveMemberFromGroup(GroupHandle1, UserRid);
+ ASSERT(NT_SUCCESS(NtStatus));
+
+
+
+ //
+ // Now get rid of the group and possibly the user account
+ //
+
+
+ NtStatus = SamDeleteGroup( GroupHandle1 );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ if (DeleteUser == TRUE) {
+ NtStatus = SamDeleteUser( UserHandle1 );
+ ASSERT(NT_SUCCESS(NtStatus));
+ } else {
+ NtStatus = SamCloseHandle( UserHandle1 );
+ ASSERT(NT_SUCCESS(NtStatus));
+ }
+
+
+
+
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // Set Group Suite (pass 2) //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+ printf("\n");
+ printf(" Set Group . . . . . . . . . . . . . . . . . . . . . . Suite\n");
+
+
+ printf(" Set Name . . . . . . . . . . . . . . . . . . . . . . ");
+ printf("(Untested)\n");
+
+
+ printf(" Set Name Of Well-Known Account . . . . . . . . . . . ");
+ printf("(Untested)\n");
+
+ }
+
+ return(TestStatus);
+
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Alias Object Test Suite //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+
+BOOLEAN
+AliasTestSuite(
+ HANDLE DomainHandle,
+ HANDLE BuiltinDomainHandle,
+ PSID DomainSid,
+ ULONG Pass
+ )
+
+{
+ NTSTATUS NtStatus, IgnoreStatus;
+ HANDLE AdminAliasHandle, AliasHandle1, AliasHandle2, UserHandle1, UserHandle2, UserHandle3;
+ ULONG CountReturned, i, MemberCount;
+ ULONG UserRid, UserRid2, UserRid3, AliasRid, AliasRid2;
+ PVOID Buffer, Buffer1, Buffer2;
+ ULONG NameLength;
+ SAM_ENUMERATE_HANDLE EnumerationContext;
+ PULONG Members;
+ PSID *AliasMembers;
+ PSID_NAME_USE LookedUpUses;
+ PULONG LookedUpRids;
+ UNICODE_STRING AccountNames[10], AccountName;
+ STRING AccountNameAnsi;
+ PSID UserSid1, UserSid2, GroupSid;
+
+ BOOLEAN IndividualTestSucceeded, DeleteUser;
+ BOOLEAN TestStatus = TRUE;
+
+
+ if (Pass == 1) {
+ //
+ // This test suite assumes that lookup and enumeration API funciton
+ // properly.
+ //
+
+ printf("\n");
+ printf("\n");
+ printf(" Alias (Pass #1) . . . . . . . . . . . . . . . . . . . Test\n");
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // Open Alias Suite //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+ printf(" Open Alias . . . . . . . . . . . . . . . . . . . . . Suite\n");
+ printf(" Open Aliases . . . . . . . . . . . . . . . . . . . . . ");
+ IndividualTestSucceeded = TRUE;
+ EnumerationContext = 0;
+ NtStatus = SamEnumerateAliasesInDomain(
+ DomainHandle,
+ &EnumerationContext,
+ &Buffer,
+ 12000, // PreferedMaximumLength
+ &CountReturned
+ );
+
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(Buffer != NULL);
+ ASSERT(CountReturned > 0);
+
+ for (i=0; i<CountReturned; i++) {
+
+ NtStatus = SamOpenAlias(
+ DomainHandle,
+ ALIAS_ALL_ACCESS,
+ ((PSAM_RID_ENUMERATION)(Buffer))[i].RelativeId,
+ &AliasHandle1
+ );
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ NtStatus = SamOpenAlias(
+ DomainHandle,
+ GENERIC_READ,
+ ((PSAM_RID_ENUMERATION)(Buffer))[i].RelativeId,
+ &AliasHandle2
+ );
+
+ if (NT_SUCCESS(NtStatus)) {
+ IgnoreStatus = SamCloseHandle( AliasHandle2 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ printf(" Failed opening alias second time.\n");
+ printf(" Rid of account is: 0x%lx\n",
+ ((PSAM_RID_ENUMERATION)(Buffer))[i].RelativeId);
+ printf(" Name of account is: %wZ\n",
+ &((PSAM_RID_ENUMERATION)(Buffer))[i].Name );
+ TestStatus = FALSE;
+ IndividualTestSucceeded = FALSE;
+ }
+
+ IgnoreStatus = SamCloseHandle( AliasHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+ } else {
+
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ printf(" Failed opening alias for first time.\n");
+ printf(" Rid of account is: 0x%lx\n",
+ ((PSAM_RID_ENUMERATION)(Buffer))[i].RelativeId);
+ printf(" Name of account is: %wZ\n",
+ &((PSAM_RID_ENUMERATION)(Buffer))[i].Name );
+ TestStatus = FALSE;
+ IndividualTestSucceeded = FALSE;
+ }
+
+ if (!IndividualTestSucceeded) {
+ printf(" ");
+ }
+ }
+
+
+ SamFreeMemory( Buffer );
+ if (IndividualTestSucceeded) {
+ printf("Succeeded\n");
+ }
+
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // Query Alias Suite //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+
+ //
+ // Get the rid of an alias created earlier in the test
+ //
+
+ RtlInitString( &AccountNameAnsi, ALIAS_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountNames[0], &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ NtStatus = SamLookupNamesInDomain(
+ DomainHandle,
+ 1,
+ &AccountNames[0],
+ &LookedUpRids,
+ &LookedUpUses
+ );
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(LookedUpUses[0] == SidTypeAlias);
+ RtlFreeUnicodeString( &AccountNames[0] );
+
+ AliasRid = LookedUpRids[0];
+
+ SamFreeMemory( LookedUpUses ); SamFreeMemory( LookedUpRids );
+
+
+ printf("\n");
+ printf(" Query Alias . . . . . . . . . . . . . . . . . . . . . Suite\n");
+
+ printf(" Query Alias General Information . . . . . . . . . . . ");
+
+
+ NtStatus = SamOpenAlias(
+ DomainHandle,
+ ALIAS_READ_INFORMATION,
+ AliasRid,
+ &AliasHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ Buffer = NULL;
+ NtStatus = SamQueryInformationAlias(
+ AliasHandle1,
+ AliasGeneralInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+
+ if ( (((ALIAS_GENERAL_INFORMATION *)Buffer)->Name.MaximumLength > 0) &&
+ (((ALIAS_GENERAL_INFORMATION *)Buffer)->Name.Buffer != NULL) ) {
+
+ printf("Succeeded\n");
+
+ printf(" Member Count is: 0x%lx\n",
+ (((ALIAS_GENERAL_INFORMATION *)Buffer)->MemberCount) );
+ printf(" Alias Name is: %wZ\n",
+ &(((ALIAS_GENERAL_INFORMATION *)Buffer)->Name) );
+
+ } else {
+ printf("Failed\n");
+ printf(" Alias Name not returned.\n");
+ TestStatus = FALSE;
+ }
+ SamFreeMemory( Buffer );
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ IgnoreStatus = SamCloseHandle( AliasHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+
+
+ printf(" Query Alias Name Information . . . . . . . . . . . . ");
+
+
+ NtStatus = SamOpenAlias(
+ DomainHandle,
+ ALIAS_READ_INFORMATION,
+ AliasRid,
+ &AliasHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ Buffer = NULL;
+ NtStatus = SamQueryInformationAlias(
+ AliasHandle1,
+ AliasNameInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+
+ if ( (((ALIAS_NAME_INFORMATION *)Buffer)->Name.MaximumLength > 0) &&
+ (((ALIAS_NAME_INFORMATION *)Buffer)->Name.Buffer != NULL) ) {
+
+ printf("Succeeded\n");
+
+ printf(" Alias Name is: %wZ\n",
+ &(((ALIAS_NAME_INFORMATION *)Buffer)->Name) );
+ } else {
+ printf("Failed\n");
+ printf(" Alias Name not returned.\n");
+ TestStatus = FALSE;
+ }
+ SamFreeMemory( Buffer );
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ IgnoreStatus = SamCloseHandle( AliasHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+
+
+ printf(" Query Alias Admin Comment Information . . . . . . . . ");
+
+
+ NtStatus = SamOpenAlias(
+ DomainHandle,
+ ALIAS_READ_INFORMATION,
+ AliasRid,
+ &AliasHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ Buffer = NULL;
+ NtStatus = SamQueryInformationAlias(
+ AliasHandle1,
+ AliasAdminCommentInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+
+ if ( (((ALIAS_ADM_COMMENT_INFORMATION *)Buffer)->AdminComment.MaximumLength >= 0) ) {
+
+ printf("Succeeded\n");
+
+ printf(" Alias Admin Comment is: %wZ\n",
+ &(((ALIAS_ADM_COMMENT_INFORMATION *)Buffer)->AdminComment) );
+ } else {
+ printf("Failed\n");
+ printf(" Alias Admin Comment not returned.\n");
+ TestStatus = FALSE;
+ }
+ SamFreeMemory( Buffer );
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ IgnoreStatus = SamCloseHandle( AliasHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // Get Members Of Alias Suite //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+ printf("\n");
+ printf(" Get Members . . . . . . . . . . . . . . . . . . . . . Suite\n");
+
+#ifdef LATER // ALIAS_LATER - well-know aliases ?
+
+
+ davidc/chads - this needs to access the builtin domain.
+
+ printf(" Get Members of Well-Known Account . . . . . . . . . . ");
+
+ NtStatus = SamOpenAlias(
+ DomainHandle,
+ ALIAS_LIST_MEMBERS,
+ DOMAIN_ALIAS_RID_ADMINS,
+ &AliasHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ Buffer = NULL;
+ NtStatus = SamGetMembersInAlias(
+ AliasHandle1,
+ &AliasMembers,
+ &Attributes,
+ &MemberCount
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Members != NULL || Attributes != NULL) {
+
+ printf("Succeeded\n");
+
+
+ printf(" Member Count: %d Users\n", MemberCount);
+ for ( i=0; i<MemberCount; i++) {
+
+ // printf(" User[%d] Sid: 0x%lx\n",
+ // i, Members[i]);
+
+
+ }
+
+ SamFreeMemory( AliasMembers );
+ SamFreeMemory( Attributes );
+
+
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ IgnoreStatus = SamCloseHandle( AliasHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+#endif
+
+
+ printf(" Get Members of Empty Alias. . . . . . . . . . . . . . ");
+
+ //
+ // This alias was created earlier in the test
+ //
+
+ RtlInitString( &AccountNameAnsi, ALIAS_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountNames[0], &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ NtStatus = SamLookupNamesInDomain(
+ DomainHandle,
+ 1,
+ &AccountNames[0],
+ &LookedUpRids,
+ &LookedUpUses
+ );
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(LookedUpUses[0] == SidTypeAlias);
+ RtlFreeUnicodeString( &AccountNames[0] );
+
+
+
+ AliasHandle1 = NULL;
+
+ NtStatus = SamOpenAlias( DomainHandle, ALIAS_LIST_MEMBERS, LookedUpRids[0], &AliasHandle1 );
+ TST_SUCCESS_ASSERT(NtStatus);
+ SamFreeMemory( LookedUpUses ); SamFreeMemory( LookedUpRids );
+
+ NtStatus = SamGetMembersInAlias(
+ AliasHandle1,
+ &AliasMembers,
+ &MemberCount
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (MemberCount == 0) {
+
+ printf("Succeeded\n");
+
+
+
+
+ } else {
+ printf("Failed\n");
+ printf(" Member Count > 0 : %d\n", MemberCount);
+ for ( i=0; i<MemberCount; i++) {
+
+ // printf(" User[%d] Rid/Attributes: 0x%lx/0x%lx\n",
+ // i, Members[i], Attributes[i]);
+ }
+
+ SamFreeMemory( AliasMembers );
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ IgnoreStatus = SamCloseHandle( AliasHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // Set Alias Suite (pass 1) //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+ printf("\n");
+ printf(" Set Alias . . . . . . . . . . . . . . . . . . . . . . Suite\n");
+ //
+
+
+ // Get the rid of an alias created earlier in the test
+ //
+
+ RtlInitString( &AccountNameAnsi, ALIAS_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountNames[0], &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ NtStatus = SamLookupNamesInDomain(
+ DomainHandle,
+ 1,
+ &AccountNames[0],
+ &LookedUpRids,
+ &LookedUpUses
+ );
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(LookedUpUses[0] == SidTypeAlias);
+ RtlFreeUnicodeString( &AccountNames[0] );
+
+ AliasRid = LookedUpRids[0];
+
+ SamFreeMemory( LookedUpUses ); SamFreeMemory( LookedUpRids );
+
+
+
+ printf(" Set Admin Comment . . . . . . . . . . . . . . . . . . ");
+
+ NtStatus = SamOpenAlias(
+ DomainHandle,
+ ALIAS_WRITE_ACCOUNT | ALIAS_READ_INFORMATION,
+ AliasRid,
+ &AliasHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ //
+ // Get the current value...
+ //
+
+ Buffer1 = NULL;
+ NtStatus = SamQueryInformationAlias(
+ AliasHandle1,
+ AliasAdminCommentInformation,
+ &Buffer1
+ );
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(Buffer1 != NULL);
+
+
+ //
+ // Change the field to a new value and write it out.
+ //
+
+ NameLength = ((ALIAS_ADM_COMMENT_INFORMATION *)Buffer1)->AdminComment.Length;
+ if ( NameLength == DummyString1.Length ) {
+ ((ALIAS_ADM_COMMENT_INFORMATION *)Buffer1)->AdminComment = DummyString2;
+ } else {
+ ((ALIAS_ADM_COMMENT_INFORMATION *)Buffer1)->AdminComment = DummyString1;
+ }
+
+ NtStatus = SamSetInformationAlias(
+ AliasHandle1,
+ AliasAdminCommentInformation,
+ Buffer1
+ );
+ if ( NT_SUCCESS(NtStatus) ) {
+
+ //
+ // Now check that the change was really made...
+ //
+
+ Buffer2 = NULL;
+ NtStatus = SamQueryInformationAlias(
+ AliasHandle1,
+ AliasAdminCommentInformation,
+ &Buffer2
+ );
+ ASSERT(NT_SUCCESS( NtStatus ) );
+ if (
+ !RtlCompareString(
+ (PSTRING)&((ALIAS_ADM_COMMENT_INFORMATION *)Buffer1)->AdminComment,
+ (PSTRING)&((ALIAS_ADM_COMMENT_INFORMATION *)Buffer2)->AdminComment,
+ TRUE)
+ ) {
+
+ printf("Succeeded\n");
+
+ } else {
+
+ printf("Failed\n");
+ printf(" Value queried doesn't match value written\n");
+ printf(" Value Written is %wZ\n",
+ (PUNICODE_STRING)&((ALIAS_ADM_COMMENT_INFORMATION *)Buffer1)->AdminComment);
+ printf(" Value Retrieved is %wZ\n",
+ (PUNICODE_STRING)&((ALIAS_ADM_COMMENT_INFORMATION *)Buffer2)->AdminComment);
+
+ TestStatus = FALSE;
+
+ }
+
+ SamFreeMemory( Buffer1 );
+ SamFreeMemory( Buffer2 );
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ SamFreeMemory( Buffer1 );
+
+ }
+
+
+
+
+
+ } // END PASS #1
+ if (Pass == 2) {
+
+ printf("\n");
+ printf("\n");
+ printf(" Alias (Pass #2) . . . . . . . . . . . . . . . . . . . Test\n");
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // Delete Alias Suite //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+ printf("\n");
+ printf(" Delete Alias . . . . . . . . . . . . . . . . . . . . Suite\n");
+
+ printf(" Delete Normal Alias . . . . . . . . . . . . . . . . . ");
+
+ //
+ // This alias was created in pass #1
+ //
+
+ RtlInitString( &AccountNameAnsi, ALIAS_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountNames[0], &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ NtStatus = SamLookupNamesInDomain(
+ DomainHandle,
+ 1,
+ &AccountNames[0],
+ &LookedUpRids,
+ &LookedUpUses
+ );
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(LookedUpUses[0] == SidTypeAlias);
+ RtlFreeUnicodeString( &AccountNames[0] );
+
+
+
+ AliasHandle1 = NULL;
+
+ NtStatus = SamOpenAlias( DomainHandle, DELETE, LookedUpRids[0], &AliasHandle1 );
+ TST_SUCCESS_ASSERT(NtStatus);
+ SamFreeMemory( LookedUpUses ); SamFreeMemory( LookedUpRids );
+
+ NtStatus = SamDeleteAlias( AliasHandle1 );
+ if (NT_SUCCESS(NtStatus)) {
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+#ifdef LATER // ALIAS_LATER - well know aliases ?
+
+
+ printf(" Delete Well Known Alias . . . . . . . . . . . . . . . ");
+
+ AliasHandle1 = NULL;
+
+ NtStatus = SamOpenAlias( DomainHandle, DELETE, DOMAIN_GROUP_RID_USERS, &AliasHandle1 );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ NtStatus = SamDeleteAlias( AliasHandle1 );
+ if (NtStatus == STATUS_SPECIAL_ACCOUNT) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+ NtStatus = SamCloseHandle( AliasHandle1 );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+
+
+
+ printf(" Delete Admin Alias. . . . . . . . . . . . . . . . . . ");
+ AliasHandle1 = NULL;
+
+ NtStatus = SamOpenAlias( DomainHandle, DELETE, DOMAIN_ALIAS_RID_ADMINS, &AliasHandle1 );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ NtStatus = SamDeleteAlias( AliasHandle1 );
+ if (NtStatus == STATUS_SPECIAL_ACCOUNT) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+ NtStatus = SamCloseHandle( AliasHandle1 );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+
+#endif
+
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // Add/Remove Member Suite //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+ printf("\n");
+ printf(" Add/Remove Member Suite . . . . . . . . . . . . . . . Suite\n");
+
+ printf(" Add Member . . . . . . . . . . . . . . . . . . . . . ");
+
+ //
+ // This test sets things up for the next test
+ //
+
+ //
+ // The following user might already exist (from earlier in the test)
+ //
+
+ RtlInitString( &AccountNameAnsi, USER_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ UserRid = 0;
+ UserHandle1 = NULL;
+ NtStatus = SamCreateUserInDomain(
+ DomainHandle,
+ &AccountName,
+ USER_ALL_ACCESS,
+ &UserHandle1,
+ &UserRid
+ );
+ RtlFreeUnicodeString( &AccountName );
+ DeleteUser = TRUE;
+ if (NtStatus == STATUS_USER_EXISTS) {
+ DeleteUser = FALSE;
+ RtlInitString( &AccountNameAnsi, USER_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountNames[0], &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ NtStatus = SamLookupNamesInDomain(
+ DomainHandle,
+ 1,
+ &AccountNames[0],
+ &LookedUpRids,
+ &LookedUpUses
+ );
+ RtlFreeUnicodeString( &AccountNames[0] );
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(LookedUpUses[0] == SidTypeUser);
+ UserRid = LookedUpRids[0];
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_ALL_ACCESS,
+ UserRid,
+ &UserHandle1);
+ SamFreeMemory( LookedUpUses ); SamFreeMemory( LookedUpRids );
+ }
+
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ //
+ // This account won't exist yet
+ //
+
+ RtlInitString( &AccountNameAnsi, USER_NAME2 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ UserRid2 = 0;
+ UserHandle2 = NULL;
+ NtStatus = SamCreateUserInDomain(
+ DomainHandle,
+ &AccountName,
+ USER_ALL_ACCESS,
+ &UserHandle2,
+ &UserRid2
+ );
+ RtlFreeUnicodeString( &AccountName );
+
+ ASSERT(NT_SUCCESS(NtStatus));
+
+
+ //
+ // create the alias
+ //
+
+ RtlInitString( &AccountNameAnsi, ALIAS_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ AliasRid = 0;
+ AliasHandle1 = NULL;
+ NtStatus = SamCreateAliasInDomain(
+ DomainHandle,
+ &AccountName,
+ ALIAS_ALL_ACCESS,
+ &AliasHandle1,
+ &AliasRid
+ );
+ RtlFreeUnicodeString( &AccountName );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ //
+ // Make user1 a member of this alias
+ //
+
+ UserSid1 = CreateUserSid(DomainSid, UserRid);
+ ASSERT(UserSid1 != NULL);
+
+ UserSid2 = CreateUserSid(DomainSid, UserRid2);
+ ASSERT(UserSid2 != NULL);
+
+
+
+ NtStatus = SamAddMemberToAlias(
+ AliasHandle1,
+ UserSid1
+ );
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ NtStatus = SamGetMembersInAlias(
+ AliasHandle1,
+ &AliasMembers,
+ &MemberCount
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ NtStatus = STATUS_MEMBER_NOT_IN_ALIAS;
+ for ( i=0; i<MemberCount; i++) {
+ if (RtlEqualSid(AliasMembers[i], UserSid1)) {
+ NtStatus = STATUS_SUCCESS;
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(NtStatus)) {
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but user not in member list for alias.\n");
+ printf("Member list :\n");
+ for (i=0; i<MemberCount; i++) {
+ printfSid(AliasMembers[i]);
+ printf("\n");
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+ }
+
+
+ if (AliasMembers != NULL) {
+ SamFreeMemory( AliasMembers );
+ }
+
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ NtStatus = SamGetAliasMembership(
+ DomainHandle,
+ 1,
+ &UserSid1,
+ &MemberCount,
+ &Members
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ NtStatus = STATUS_MEMBER_NOT_IN_ALIAS;
+ for ( i=0; i<MemberCount; i++) {
+ if (Members[i] == AliasRid) {
+ NtStatus = STATUS_SUCCESS;
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(NtStatus)) {
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but alias not in account alias membership list.\n");
+ printf("Alias Membership :\n");
+ for (i=0; i<MemberCount; i++) {
+ printf("0x%lx\n", Members[i]);
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+ }
+
+ if (Members != NULL) {
+ SamFreeMemory( Members );
+ }
+
+
+ //
+ // Check for correct alias membership for multiple accounts
+ // User1 should be in alias1
+ // User2 should be no aliases.
+ //
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ PSID SidArray[2];
+ SidArray[0] = UserSid1;
+ SidArray[1] = UserSid2;
+
+ NtStatus = SamGetAliasMembership(
+ DomainHandle,
+ 2,
+ SidArray,
+ &MemberCount,
+ &Members
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ if (MemberCount != 1) {
+
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but combined alias membership count for 2 accounts not correct.\n");
+ printf("Combined Alias Membership :\n");
+ for (i=0; i<MemberCount; i++) {
+ printf("0x%lx\n", Members[i]);
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+
+ } else {
+
+ if (Members[0] != AliasRid) {
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but combined alias membership for 2 accounts not correct.\n");
+ printf("Combined Alias Membership :\n");
+ for (i=0; i<MemberCount; i++) {
+ printf("0x%lx\n", Members[i]);
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+
+ } else {
+ printf("Succeeded\n");
+ }
+ }
+
+ if (Members != NULL) {
+ SamFreeMemory( Members );
+ }
+ }
+ }
+
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+
+
+
+
+ printf(" Add another member to another alias . . . . . . . . . ");
+
+
+
+
+
+
+ //
+ // Make user2 a member of alias2
+ //
+
+ //
+ // This alias was created in pass #1
+ //
+
+ RtlInitString( &AccountNameAnsi, ALIAS_NAME2 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountNames[0], &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ NtStatus = SamLookupNamesInDomain(
+ DomainHandle,
+ 1,
+ &AccountNames[0],
+ &LookedUpRids,
+ &LookedUpUses
+ );
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(LookedUpUses[0] == SidTypeAlias);
+ RtlFreeUnicodeString( &AccountNames[0] );
+
+ AliasHandle2 = NULL;
+ AliasRid2 = LookedUpRids[0];
+
+ NtStatus = SamOpenAlias( DomainHandle, ALIAS_ALL_ACCESS, LookedUpRids[0], &AliasHandle2 );
+ TST_SUCCESS_ASSERT(NtStatus);
+ SamFreeMemory( LookedUpUses ); SamFreeMemory( LookedUpRids );
+
+
+ NtStatus = SamAddMemberToAlias(
+ AliasHandle2,
+ UserSid2
+ );
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ NtStatus = SamGetMembersInAlias(
+ AliasHandle2,
+ &AliasMembers,
+ &MemberCount
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ NtStatus = STATUS_MEMBER_NOT_IN_ALIAS;
+ for ( i=0; i<MemberCount; i++) {
+ if (RtlEqualSid(AliasMembers[i], UserSid2)) {
+ NtStatus = STATUS_SUCCESS;
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(NtStatus)) {
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but user not in member list for alias.\n");
+ printf("Member list :\n");
+ for (i=0; i<MemberCount; i++) {
+ printfSid(AliasMembers[i]);
+ printf("\n");
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+ }
+
+
+ if (AliasMembers != NULL) {
+ SamFreeMemory( AliasMembers );
+ }
+
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ NtStatus = SamGetAliasMembership(
+ DomainHandle,
+ 1,
+ &UserSid2,
+ &MemberCount,
+ &Members
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ NtStatus = STATUS_MEMBER_NOT_IN_ALIAS;
+ for ( i=0; i<MemberCount; i++) {
+ if (Members[i] == AliasRid2) {
+ NtStatus = STATUS_SUCCESS;
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(NtStatus)) {
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but alias not in account alias membership list.\n");
+ printf("Alias Membership :\n");
+ for (i=0; i<MemberCount; i++) {
+ printf("0x%lx\n", Members[i]);
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+ }
+
+ if (Members != NULL) {
+ SamFreeMemory( Members );
+ }
+
+ //
+ // Check for correct alias membership for multiple accounts
+ // User1 should be in alias1
+ // User2 should be in alias2.
+ //
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ PSID SidArray[2];
+ SidArray[0] = UserSid1;
+ SidArray[1] = UserSid2;
+
+ NtStatus = SamGetAliasMembership(
+ DomainHandle,
+ 2,
+ SidArray,
+ &MemberCount,
+ &Members
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ if (MemberCount != 2) {
+
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but combined alias membership count for 2 accounts not correct.\n");
+ printf("Combined Alias Membership :\n");
+ for (i=0; i<MemberCount; i++) {
+ printf("0x%lx\n", Members[i]);
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+
+ } else {
+
+ if (((Members[0] == AliasRid) && (Members[1] == AliasRid2)) ||
+ ((Members[0] == AliasRid2) && (Members[1] == AliasRid)) ) {
+ printf("Succeeded\n");
+ } else {
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but combined alias membership for 2 accounts not correct.\n");
+ printf("Combined Alias Membership :\n");
+ for (i=0; i<MemberCount; i++) {
+ printf("0x%lx\n", Members[i]);
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+ }
+ }
+
+ if (Members != NULL) {
+ SamFreeMemory( Members );
+ }
+ }
+ }
+
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+ //
+ // Remove user2 from alias2 again
+ //
+
+ NtStatus = SamRemoveMemberFromAlias(
+ AliasHandle2,
+ UserSid2
+ );
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ NtStatus = SamGetMembersInAlias(
+ AliasHandle2,
+ &AliasMembers,
+ &MemberCount
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ NtStatus = STATUS_MEMBER_NOT_IN_ALIAS;
+ for ( i=0; i<MemberCount; i++) {
+ if (RtlEqualSid(AliasMembers[i], UserSid2)) {
+ NtStatus = STATUS_SUCCESS;
+ break;
+ }
+ }
+
+ if (NtStatus != STATUS_MEMBER_NOT_IN_ALIAS) {
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but user still in member list for alias.\n");
+ printf("Member list :\n");
+ for (i=0; i<MemberCount; i++) {
+ printfSid(AliasMembers[i]);
+ printf("\n");
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+ }
+
+
+ if (AliasMembers != NULL) {
+ SamFreeMemory( AliasMembers );
+ }
+
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ NtStatus = SamGetAliasMembership(
+ DomainHandle,
+ 1,
+ &UserSid2,
+ &MemberCount,
+ &Members
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ for ( i=0; i<MemberCount; i++) {
+ if (Members[i] == AliasRid2) {
+ NtStatus = STATUS_MEMBER_IN_ALIAS;
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(NtStatus)) {
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but alias still in account alias membership list.\n");
+ printf("Alias Membership :\n");
+ for (i=0; i<MemberCount; i++) {
+ printf("0x%lx\n", Members[i]);
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+ }
+
+ if (Members != NULL) {
+ SamFreeMemory( Members );
+ }
+
+ //
+ // Check for correct alias membership for multiple accounts
+ // User1 should be in alias1
+ // User2 should be in no aliases.
+ //
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ PSID SidArray[2];
+ SidArray[0] = UserSid1;
+ SidArray[1] = UserSid2;
+
+ NtStatus = SamGetAliasMembership(
+ DomainHandle,
+ 2,
+ SidArray,
+ &MemberCount,
+ &Members
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ if (MemberCount != 1) {
+
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but combined alias membership count for 2 accounts not correct.\n");
+ printf("Combined Alias Membership :\n");
+ for (i=0; i<MemberCount; i++) {
+ printf("0x%lx\n", Members[i]);
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+
+ } else {
+
+ if (Members[0] == AliasRid) {
+ printf("Succeeded\n");
+ } else {
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but combined alias membership for 2 accounts not correct.\n");
+ printf("Combined Alias Membership :\n");
+ for (i=0; i<MemberCount; i++) {
+ printf("0x%lx\n", Members[i]);
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+ }
+ }
+
+ if (Members != NULL) {
+ SamFreeMemory( Members );
+ }
+ }
+ }
+
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+ NtStatus = SamCloseHandle( AliasHandle2 );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+
+
+
+
+ printf(" Add Another Member . . . . . . . . . . . . . . . . . ");
+
+
+ //
+ // Make user2 a member of this alias
+ //
+
+ NtStatus = SamAddMemberToAlias(
+ AliasHandle1,
+ UserSid2
+ );
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ NtStatus = SamGetMembersInAlias(
+ AliasHandle1,
+ &AliasMembers,
+ &MemberCount
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ NtStatus = STATUS_MEMBER_NOT_IN_ALIAS;
+ for ( i=0; i<MemberCount; i++) {
+ if (RtlEqualSid(AliasMembers[i], UserSid2)) {
+ NtStatus = STATUS_SUCCESS;
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(NtStatus)) {
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but user not in member list for alias.\n");
+ printf("Member list :\n");
+ for (i=0; i<MemberCount; i++) {
+ printfSid(AliasMembers[i]);
+ printf("\n");
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+ }
+
+
+ if (AliasMembers != NULL) {
+ SamFreeMemory( AliasMembers );
+ }
+
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ NtStatus = SamGetAliasMembership(
+ DomainHandle,
+ 1,
+ &UserSid2,
+ &MemberCount,
+ &Members
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ NtStatus = STATUS_MEMBER_NOT_IN_ALIAS;
+ for ( i=0; i<MemberCount; i++) {
+ if (Members[i] == AliasRid) {
+ NtStatus = STATUS_SUCCESS;
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(NtStatus)) {
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but alias not in account alias membership list.\n");
+ printf("Alias Membership :\n");
+ for (i=0; i<MemberCount; i++) {
+ printf("0x%lx\n", Members[i]);
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+ }
+
+ if (Members != NULL) {
+ SamFreeMemory( Members );
+ }
+
+ //
+ // Check for correct alias membership for multiple accounts
+ // User1 should be in alias1
+ // User2 should be in alias1.
+ //
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ PSID SidArray[2];
+ SidArray[0] = UserSid1;
+ SidArray[1] = UserSid2;
+
+ NtStatus = SamGetAliasMembership(
+ DomainHandle,
+ 2,
+ SidArray,
+ &MemberCount,
+ &Members
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ if (MemberCount != 1) {
+
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but combined alias membership count for 2 accounts not correct.\n");
+ printf("Combined Alias Membership :\n");
+ for (i=0; i<MemberCount; i++) {
+ printf("0x%lx\n", Members[i]);
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+
+ } else {
+
+ if (Members[0] != AliasRid) {
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but combined alias membership for 2 accounts not correct.\n");
+ printf("Combined Alias Membership :\n");
+ for (i=0; i<MemberCount; i++) {
+ printf("0x%lx\n", Members[i]);
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+
+ } else {
+ printf("Succeeded\n");
+ }
+ }
+
+ if (Members != NULL) {
+ SamFreeMemory( Members );
+ }
+ }
+ }
+
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+ printf(" Remove Member . . . . . . . . . . . . . . . . . . . . ");
+
+ //
+ // The previous test sets this one up.
+ //
+
+ //
+ // Now try to remove the user from the alias
+ //
+
+ NtStatus = SamRemoveMemberFromAlias(AliasHandle1, UserSid1);
+ if (NT_SUCCESS(NtStatus)) {
+
+ NtStatus = SamGetMembersInAlias(
+ AliasHandle1,
+ &AliasMembers,
+ &MemberCount
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ for ( i=0; i<MemberCount; i++) {
+ if (RtlEqualSid(AliasMembers[i], UserSid1)) {
+ NtStatus = STATUS_MEMBER_IN_ALIAS;
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(NtStatus)) {
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but user still in member list for alias.\n");
+ printf("Member list :\n");
+ for (i=0; i<MemberCount; i++) {
+ printfSid(AliasMembers[i]);
+ printf("\n");
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+ }
+
+ SamFreeMemory( AliasMembers );
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ NtStatus = SamGetAliasMembership(
+ DomainHandle,
+ 1,
+ &UserSid1,
+ &MemberCount,
+ &Members
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ for ( i=0; i<MemberCount; i++) {
+ if (Members[i] == AliasRid) {
+ NtStatus = STATUS_MEMBER_IN_ALIAS;
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(NtStatus)) {
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but alias still in account alias membership list.\n");
+ printf("Alias Membership :\n");
+ for (i=0; i<MemberCount; i++) {
+ printf("0x%lx\n", Members[i]);
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+ }
+
+ if (Members != NULL) {
+ SamFreeMemory( Members );
+ }
+
+ //
+ // Check for correct alias membership for multiple accounts
+ // User1 should be in no aliases
+ // User2 should be in alias1.
+ //
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ PSID SidArray[2];
+ SidArray[0] = UserSid1;
+ SidArray[1] = UserSid2;
+
+ NtStatus = SamGetAliasMembership(
+ DomainHandle,
+ 2,
+ SidArray,
+ &MemberCount,
+ &Members
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ if (MemberCount != 1) {
+
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but combined alias membership count for 2 accounts not correct.\n");
+ printf("Combined Alias Membership :\n");
+ for (i=0; i<MemberCount; i++) {
+ printf("0x%lx\n", Members[i]);
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+
+ } else {
+
+ if (Members[0] != AliasRid) {
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but combined alias membership for 2 accounts not correct.\n");
+ printf("Combined Alias Membership :\n");
+ for (i=0; i<MemberCount; i++) {
+ printf("0x%lx\n", Members[i]);
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+
+ } else {
+ printf("Succeeded\n");
+ }
+ }
+
+ if (Members != NULL) {
+ SamFreeMemory( Members );
+ }
+ }
+ }
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+
+ printf(" Add A User to ADMIN Alias . . . . . . . . . . . . . . ");
+
+ //
+ // Make user2 a member of the ADMIN alias
+ //
+
+ NtStatus = SamOpenAlias(
+ BuiltinDomainHandle,
+ ALIAS_ALL_ACCESS,
+ DOMAIN_ALIAS_RID_ADMINS,
+ &AdminAliasHandle
+ );
+
+ ASSERT( NT_SUCCESS( NtStatus ) );
+
+ NtStatus = SamAddMemberToAlias(
+ AdminAliasHandle,
+ UserSid2
+ );
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ NtStatus = SamGetMembersInAlias(
+ AdminAliasHandle,
+ &AliasMembers,
+ &MemberCount
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ NtStatus = STATUS_MEMBER_NOT_IN_ALIAS;
+ for ( i=0; i<MemberCount; i++) {
+ if (RtlEqualSid(AliasMembers[i], UserSid2)) {
+ NtStatus = STATUS_SUCCESS;
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(NtStatus)) {
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but user not in member list for alias.\n");
+ printf("Member list :\n");
+ for (i=0; i<MemberCount; i++) {
+ printfSid(AliasMembers[i]);
+ printf("\n");
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+ }
+
+
+ if (AliasMembers != NULL) {
+ SamFreeMemory( AliasMembers );
+ }
+
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ NtStatus = SamGetAliasMembership(
+ BuiltinDomainHandle,
+ 1,
+ &UserSid2,
+ &MemberCount,
+ &Members
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ NtStatus = STATUS_MEMBER_NOT_IN_ALIAS;
+ for ( i=0; i<MemberCount; i++) {
+ if (Members[i] == DOMAIN_ALIAS_RID_ADMINS) {
+ NtStatus = STATUS_SUCCESS;
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(NtStatus)) {
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but alias not in account alias membership list.\n");
+ printf("Alias Membership :\n");
+ for (i=0; i<MemberCount; i++) {
+ printf("0x%lx\n", Members[i]);
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+
+ } else {
+
+ printf("Succeeded\n");
+ }
+
+ if (Members != NULL) {
+ SamFreeMemory( Members );
+ }
+ }
+
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+ printf(" Add A Group to ADMIN Alias . . . . . . . . . . . . . . ");
+
+ //
+ // Make a group a member of the ADMIN alias
+ //
+
+ GroupSid = CreateUserSid(DomainSid, DOMAIN_GROUP_RID_USERS );
+ ASSERT(GroupSid != NULL);
+
+ NtStatus = SamAddMemberToAlias(
+ AdminAliasHandle,
+ GroupSid
+ );
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ NtStatus = SamGetMembersInAlias(
+ AdminAliasHandle,
+ &AliasMembers,
+ &MemberCount
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ NtStatus = STATUS_MEMBER_NOT_IN_ALIAS;
+ for ( i=0; i<MemberCount; i++) {
+ if (RtlEqualSid(AliasMembers[i], GroupSid)) {
+ NtStatus = STATUS_SUCCESS;
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(NtStatus)) {
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but user not in member list for alias.\n");
+ printf("Member list :\n");
+ for (i=0; i<MemberCount; i++) {
+ printfSid(AliasMembers[i]);
+ printf("\n");
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+ }
+
+
+ if (AliasMembers != NULL) {
+ SamFreeMemory( AliasMembers );
+ }
+
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ NtStatus = SamGetAliasMembership(
+ BuiltinDomainHandle,
+ 1,
+ &GroupSid,
+ &MemberCount,
+ &Members
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ NtStatus = STATUS_MEMBER_NOT_IN_ALIAS;
+ for ( i=0; i<MemberCount; i++) {
+ if (Members[i] == DOMAIN_ALIAS_RID_ADMINS) {
+ NtStatus = STATUS_SUCCESS;
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(NtStatus)) {
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but alias not in account alias membership list.\n");
+ printf("Alias Membership :\n");
+ for (i=0; i<MemberCount; i++) {
+ printf("0x%lx\n", Members[i]);
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+
+ } else {
+
+ printf("Succeeded\n");
+ }
+
+ if (Members != NULL) {
+ SamFreeMemory( Members );
+ }
+ }
+
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+// NOTE: user is already created in the group below. Should keep this
+// test, AND add another with an all-new group that's been added to the ADMIN
+// alias (then ADD user to group, rather than create in it).
+ printf(" Create user in ADMIN ALIAS'd Group. . . . . . . . . . . ");
+
+ RtlInitString( &AccountNameAnsi, USER_NAME3 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ UserRid3 = 0;
+ UserHandle3 = NULL;
+ NtStatus = SamCreateUserInDomain(
+ DomainHandle,
+ &AccountName,
+ USER_ALL_ACCESS,
+ &UserHandle3,
+ &UserRid3
+ );
+ RtlFreeUnicodeString( &AccountName );
+
+ if ( NT_SUCCESS( NtStatus ) ) {
+
+ printf("Succeeded\n");
+
+ } else {
+
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+//NOTE: doesn't work because this is primary group.
+//put back in when all-new group is created, above
+// printf(" Remove user from ADMIN ALIAS'd Group. . . . . . . . . . . ");
+//
+// NtStatus = SamOpenGroup(
+// DomainHandle,
+// GROUP_ALL_ACCESS,
+// DOMAIN_GROUP_RID_USERS,
+// &GroupHandle
+// );
+//
+// ASSERT(NT_SUCCESS(NtStatus));
+//
+// NtStatus = SamRemoveMemberFromGroup(
+// GroupHandle,
+// UserRid3
+// );
+//
+// if ( NT_SUCCESS( NtStatus ) ) {
+//
+// printf("Succeeded\n");
+//
+// } else {
+//
+// printf("Failed\n");
+// printf(" Completion status is 0x%lx\n", NtStatus);
+// TestStatus = FALSE;
+// }
+//
+// IgnoreStatus = SamCloseHandle( GroupHandle );
+// ASSERT(NT_SUCCESS(IgnoreStatus));
+ IgnoreStatus = SamCloseHandle( UserHandle3 );
+ ASSERT(NT_SUCCESS(IgnoreStatus));
+
+
+
+ printf(" Remove User from ADMIN alias. . . . . . . . . . . ");
+
+ //
+ // The previous test sets this one up.
+ //
+ // Now try to remove the user from the alias
+ //
+
+ NtStatus = SamRemoveMemberFromAlias(AdminAliasHandle, UserSid2);
+ if (NT_SUCCESS(NtStatus)) {
+
+ NtStatus = SamGetMembersInAlias(
+ AdminAliasHandle,
+ &AliasMembers,
+ &MemberCount
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ for ( i=0; i<MemberCount; i++) {
+ if (RtlEqualSid(AliasMembers[i], UserSid2)) {
+ NtStatus = STATUS_MEMBER_IN_ALIAS;
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(NtStatus)) {
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but user still in member list for alias.\n");
+ printf("Member list :\n");
+ for (i=0; i<MemberCount; i++) {
+ printfSid(AliasMembers[i]);
+ printf("\n");
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+ }
+
+ SamFreeMemory( AliasMembers );
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ NtStatus = SamGetAliasMembership(
+ BuiltinDomainHandle,
+ 1,
+ &UserSid2,
+ &MemberCount,
+ &Members
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ for ( i=0; i<MemberCount; i++) {
+ if (Members[i] == DOMAIN_ALIAS_RID_ADMINS) {
+ NtStatus = STATUS_MEMBER_IN_ALIAS;
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(NtStatus)) {
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but alias still in account alias membership list.\n");
+ printf("Alias Membership :\n");
+ for (i=0; i<MemberCount; i++) {
+ printf("0x%lx\n", Members[i]);
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+
+ } else {
+
+ printf("Succeeded\n");
+ }
+
+ if (Members != NULL) {
+ SamFreeMemory( Members );
+ }
+ }
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+
+ //
+ // Make user2 a member of the ADMIN alias again, so we can test
+ // the new function SamRemoveMemberFromForeignDomain().
+ // NOTE: we should make this a real test item.
+ //
+
+ NtStatus = SamAddMemberToAlias(
+ AdminAliasHandle,
+ UserSid2
+ );
+
+ ASSERT (NT_SUCCESS(NtStatus));
+
+ NtStatus = SamRemoveMemberFromForeignDomain(
+ BuiltinDomainHandle,
+ UserSid2 );
+
+ ASSERT (NT_SUCCESS(NtStatus));
+
+
+
+ printf(" Remove Group from ADMIN alias. . . . . . . . . . . ");
+
+ //
+ // The previous test sets this one up.
+ //
+ // Now try to remove the group from the alias
+ //
+
+ NtStatus = SamRemoveMemberFromAlias(AdminAliasHandle, GroupSid);
+ if (NT_SUCCESS(NtStatus)) {
+
+ NtStatus = SamGetMembersInAlias(
+ AdminAliasHandle,
+ &AliasMembers,
+ &MemberCount
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ for ( i=0; i<MemberCount; i++) {
+ if (RtlEqualSid(AliasMembers[i], GroupSid)) {
+ NtStatus = STATUS_MEMBER_IN_ALIAS;
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(NtStatus)) {
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but user still in member list for alias.\n");
+ printf("Member list :\n");
+ for (i=0; i<MemberCount; i++) {
+ printfSid(AliasMembers[i]);
+ printf("\n");
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+ }
+
+ SamFreeMemory( AliasMembers );
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ NtStatus = SamGetAliasMembership(
+ BuiltinDomainHandle,
+ 1,
+ &GroupSid,
+ &MemberCount,
+ &Members
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ for ( i=0; i<MemberCount; i++) {
+ if (Members[i] == DOMAIN_ALIAS_RID_ADMINS) {
+ NtStatus = STATUS_MEMBER_IN_ALIAS;
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(NtStatus)) {
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but alias still in account alias membership list.\n");
+ printf("Alias Membership :\n");
+ for (i=0; i<MemberCount; i++) {
+ printf("0x%lx\n", Members[i]);
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+
+ } else {
+
+ printf("Succeeded\n");
+ }
+
+ if (Members != NULL) {
+ SamFreeMemory( Members );
+ }
+ }
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+ IgnoreStatus = SamCloseHandle( AdminAliasHandle );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+
+ printf(" Delete account while member of alias. . . . . . . . . ");
+
+
+ //
+ // Now delete user2 and check the alias member list is updated
+ //
+
+ NtStatus = SamDeleteUser( UserHandle2 );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ NtStatus = SamGetMembersInAlias(
+ AliasHandle1,
+ &AliasMembers,
+ &MemberCount
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ for ( i=0; i<MemberCount; i++) {
+ if (RtlEqualSid(AliasMembers[i], UserSid2)) {
+ NtStatus = STATUS_MEMBER_IN_ALIAS;
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(NtStatus)) {
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but user still in member list for alias.\n");
+ printf("Member list :\n");
+ for (i=0; i<MemberCount; i++) {
+ printfSid(AliasMembers[i]);
+ printf("\n");
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+ }
+
+ SamFreeMemory( AliasMembers );
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ NtStatus = SamGetAliasMembership(
+ DomainHandle,
+ 1,
+ &UserSid2,
+ &MemberCount,
+ &Members
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ if (MemberCount != 0) {
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but alias still in alias membership list for account.\n");
+ printf("Alias Membership :\n");
+ for (i=0; i<MemberCount; i++) {
+ printf("0x%lx\n", Members[i]);
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+ }
+
+ if (Members != NULL) {
+ SamFreeMemory( Members );
+ }
+
+ //
+ // Check for correct alias membership for multiple accounts
+ // User1 should be in no aliases
+ // User2 should be in no aliases.
+ //
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ PSID SidArray[2];
+ SidArray[0] = UserSid1;
+ SidArray[1] = UserSid2;
+
+ NtStatus = SamGetAliasMembership(
+ DomainHandle,
+ 2,
+ SidArray,
+ &MemberCount,
+ &Members
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ if (MemberCount != 0) {
+
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but combined alias membership count for 2 accounts not correct.\n");
+ printf("Combined Alias Membership :\n");
+ for (i=0; i<MemberCount; i++) {
+ printf("0x%lx\n", Members[i]);
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+
+ } else {
+ printf("Succeeded\n");
+ }
+
+ if (Members != NULL) {
+ SamFreeMemory( Members );
+ }
+ }
+ }
+
+
+
+
+ printf(" Delete alias with members . . . . . . . . . . . . . . ");
+
+ //
+ // Make the user a member of this alias (again)
+ //
+
+ NtStatus = SamAddMemberToAlias(
+ AliasHandle1,
+ UserSid1
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ //
+ // Now delete the alias and check the membership list for user is updated
+ //
+
+ NtStatus = SamDeleteAlias( AliasHandle1 );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ NtStatus = SamGetAliasMembership(
+ DomainHandle,
+ 1,
+ &UserSid1,
+ &MemberCount,
+ &Members
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ for ( i=0; i<MemberCount; i++) {
+ if (Members[i] == AliasRid) {
+ NtStatus = STATUS_MEMBER_IN_ALIAS;
+ break;
+ }
+ }
+
+ if (NT_SUCCESS(NtStatus)) {
+ printf("Succeeded\n");
+ } else {
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but alias still in account alias membership list.\n");
+ printf("Alias Membership :\n");
+ for (i=0; i<MemberCount; i++) {
+ printf("0x%lx\n", Members[i]);
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+ }
+
+ if (Members != NULL) {
+ SamFreeMemory( Members );
+ }
+
+
+
+ DeleteUserSid(UserSid1);
+ DeleteUserSid(UserSid2);
+
+ //
+ // and clean up
+ //
+
+ if (DeleteUser == TRUE) {
+ NtStatus = SamDeleteUser( UserHandle1 );
+ ASSERT(NT_SUCCESS(NtStatus));
+ } else {
+ NtStatus = SamCloseHandle( UserHandle1 );
+ ASSERT(NT_SUCCESS(NtStatus));
+ }
+
+
+
+
+
+
+ printf(" Add Foreign Domain Member . . . . . . . . . . . . . . ");
+
+ //
+ // create the alias
+ //
+
+ RtlInitString( &AccountNameAnsi, ALIAS_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ AliasRid = 0;
+ AliasHandle1 = NULL;
+ NtStatus = SamCreateAliasInDomain(
+ DomainHandle,
+ &AccountName,
+ ALIAS_ALL_ACCESS,
+ &AliasHandle1,
+ &AliasRid
+ );
+ RtlFreeUnicodeString( &AccountName );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ //
+ // Specify a non-existant user be added to this alias
+ //
+
+
+ {
+ PSID ForeignDomainSid;
+
+ ForeignDomainSid = CreateUserSid(DomainSid, 307333); // random domain sub-authority
+ ASSERT(ForeignDomainSid != NULL);
+
+ UserRid = 45728; // Random user rid
+
+ UserSid1 = CreateUserSid(ForeignDomainSid, UserRid);
+ ASSERT(UserSid1 != NULL);
+
+ DeleteUserSid(ForeignDomainSid);
+ }
+
+
+ NtStatus = SamAddMemberToAlias(
+ AliasHandle1,
+ UserSid1
+ );
+
+ if (NtStatus == STATUS_SUCCESS) {
+
+ NtStatus = SamGetMembersInAlias(
+ AliasHandle1,
+ &AliasMembers,
+ &MemberCount
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ NtStatus = STATUS_MEMBER_NOT_IN_ALIAS;
+ for ( i=0; i<MemberCount; i++) {
+ if (RtlEqualSid(AliasMembers[i], UserSid1)) {
+ NtStatus = STATUS_SUCCESS;
+ break;
+ }
+ }
+
+ if (!NT_SUCCESS(NtStatus)) {
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but user not in member list for alias.\n");
+ printf("Member list :\n");
+ for (i=0; i<MemberCount; i++) {
+ printfSid(AliasMembers[i]);
+ printf("\n");
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+ }
+
+
+ if (AliasMembers != NULL) {
+ SamFreeMemory( AliasMembers );
+ }
+
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ NtStatus = SamGetAliasMembership(
+ DomainHandle,
+ 1,
+ &UserSid1,
+ &MemberCount,
+ &Members
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ NtStatus = STATUS_MEMBER_NOT_IN_ALIAS;
+ for ( i=0; i<MemberCount; i++) {
+ if (Members[i] == AliasRid) {
+ NtStatus = STATUS_SUCCESS;
+ break;
+ }
+ }
+
+ if (NT_SUCCESS(NtStatus)) {
+ printf("Succeeded\n");
+ } else {
+ printf("Failed\n");
+ printf("Service returned SUCCESS, but alias not in account alias membership list.\n");
+ printf("Alias Membership :\n");
+ for (i=0; i<MemberCount; i++) {
+ printf("0x%lx\n", Members[i]);
+ }
+ DebugBreak();
+ TestStatus = FALSE;
+ }
+
+ if (Members != NULL) {
+ SamFreeMemory( Members );
+ }
+ }
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+ DeleteUserSid(UserSid1);
+
+
+
+
+ printf(" Add alias as member . . . . . . . . . . . . . . . . . ");
+
+ //
+ // Specify an alias in the current domain be added to this alias
+ //
+
+
+ UserSid1 = CreateUserSid(DomainSid, AliasRid2);
+ ASSERT(UserSid1 != NULL);
+
+
+ NtStatus = SamAddMemberToAlias(
+ AliasHandle1,
+ UserSid1
+ );
+
+ if (NtStatus != STATUS_INVALID_MEMBER) {
+
+ printf("Failed\n");
+ printf("Expected service to return STATUS_INVALID_MEMBER, actually returned 0x%lx.\n", NtStatus);
+ DebugBreak();
+ TestStatus = FALSE;
+ } else {
+ printf("Succeeded\n");
+ }
+
+ DeleteUserSid(UserSid1);
+
+
+
+ printf(" Add non-existant account in this domain as member . . ");
+
+ //
+ // Specify a non-existant account in the current domain be added to this alias
+ //
+
+
+ UserSid1 = CreateUserSid(DomainSid, 32567); // Random rid
+ ASSERT(UserSid1 != NULL);
+
+
+ NtStatus = SamAddMemberToAlias(
+ AliasHandle1,
+ UserSid1
+ );
+
+ if (NtStatus != STATUS_NO_SUCH_MEMBER) {
+
+ printf("Failed\n");
+ printf("Expected service to return STATUS_NO_SUCH_MEMBER, actually returned 0x%lx.\n", NtStatus);
+ DebugBreak();
+ TestStatus = FALSE;
+ } else {
+ printf("Succeeded\n");
+ }
+
+ DeleteUserSid(UserSid1);
+
+
+
+ printf(" Remove Non-member . . . . . . . . . . . . . . . . . . ");
+
+ //
+ // Specify a non-existant user be removed from this alias
+ //
+
+ {
+ PSID ForeignDomainSid;
+
+ ForeignDomainSid = CreateUserSid(DomainSid, 35775); // random domain sub-authority
+ ASSERT(ForeignDomainSid != NULL);
+
+ UserRid = 623545; // Random user rid
+
+ UserSid1 = CreateUserSid(ForeignDomainSid, UserRid);
+ ASSERT(UserSid1 != NULL);
+
+ DeleteUserSid(ForeignDomainSid);
+ }
+
+ NtStatus = SamRemoveMemberFromAlias( AliasHandle1, UserSid1 );
+
+ if (NtStatus == STATUS_MEMBER_NOT_IN_ALIAS) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+ DeleteUserSid(UserSid1);
+
+ NtStatus = SamDeleteAlias( AliasHandle1 );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+
+
+
+ }
+
+ return(TestStatus);
+
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// User Object Test Suite //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+
+BOOLEAN
+UserTestSuite(
+ HANDLE DomainHandle,
+ ULONG Pass
+ )
+
+
+{
+
+ PUSER_ALL_INFORMATION All, All2;
+ NTSTATUS NtStatus, IgnoreStatus, TmpStatus;
+ HANDLE UserHandle1, UserHandle2, GroupHandle1;
+ ULONG CountReturned, NameLength, MembershipCount, i;
+ ULONG UserRid, GroupRid;
+ PVOID Buffer, Buffer1, Buffer2;
+ SAM_ENUMERATE_HANDLE EnumerationContext;
+ USER_GENERAL_INFORMATION GeneralInformation;
+ USER_LOGON_INFORMATION LogonInformation;
+ USER_ACCOUNT_INFORMATION AccountInformation;
+ PSID_NAME_USE LookedUpUses;
+ PULONG LookedUpRids;
+ UNICODE_STRING AccountNames[10], AccountName;
+ STRING AccountNameAnsi, TmpAnsiString;
+
+
+
+
+ BOOLEAN IndividualTestSucceeded, DeleteUser;
+ BOOLEAN TestStatus = TRUE;
+
+
+
+
+ if (Pass == 1) {
+ // This test suite assumes that lookup and enumeration API funciton
+ // properly.
+ //
+
+ printf("\n");
+ printf("\n");
+ printf(" User (Pass #1) . . . . . . . . . . . . . . . . . . . Test\n");
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // Open User Suite //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+ printf(" Open User . . . . . . . . . . . . . . . . . . . . . Suite\n");
+ printf(" Open Users . . . . . . . . . . . . . . . . . . . . . ");
+ IndividualTestSucceeded = TRUE;
+ EnumerationContext = 0;
+ NtStatus = SamEnumerateUsersInDomain(
+ DomainHandle,
+ &EnumerationContext,
+ 0,
+ &Buffer,
+ 12000, // PreferedMaximumLength
+ &CountReturned
+ );
+
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(Buffer != NULL);
+ ASSERT(CountReturned > 0);
+
+ for (i=0; i<CountReturned; i++) {
+
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_ALL_ACCESS,
+ ((PSAM_RID_ENUMERATION)(Buffer))[i].RelativeId,
+ &UserHandle1
+ );
+
+ if (NT_SUCCESS(NtStatus)) {
+
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ GENERIC_READ,
+ ((PSAM_RID_ENUMERATION)(Buffer))[i].RelativeId,
+ &UserHandle2
+ );
+
+ if (NT_SUCCESS(NtStatus)) {
+ IgnoreStatus = SamCloseHandle( UserHandle2 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ printf(" Failed opening User second time.\n");
+ printf(" Rid of account is: 0x%lx\n",
+ ((PSAM_RID_ENUMERATION)(Buffer))[i].RelativeId);
+ printf(" Name of account is: %wZ\n",
+ &((PSAM_RID_ENUMERATION)(Buffer))[i].Name );
+ TestStatus = FALSE;
+ IndividualTestSucceeded = FALSE;
+ }
+
+ IgnoreStatus = SamCloseHandle( UserHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+ } else {
+
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ printf(" Failed opening User for first time.\n");
+ printf(" Rid of account is: 0x%lx\n",
+ ((PSAM_RID_ENUMERATION)(Buffer))[i].RelativeId);
+ printf(" Name of account is: %wZ\n",
+ &((PSAM_RID_ENUMERATION)(Buffer))[i].Name );
+ TestStatus = FALSE;
+ IndividualTestSucceeded = FALSE;
+ }
+
+ }
+
+
+ SamFreeMemory( Buffer );
+ if (IndividualTestSucceeded) {
+ printf("Succeeded\n");
+ }
+
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // Query User Suite //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+ printf("\n");
+ printf(" Query User . . . . . . . . . . . . . . . . . . . . . Suite\n");
+
+ printf(" Query User General Information . . . . . . . . . . . ");
+
+
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_READ_GENERAL,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ Buffer = NULL;
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserGeneralInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+
+ if ( (((USER_GENERAL_INFORMATION *)Buffer)->UserName.MaximumLength
+ >= 0) &&
+ (((USER_GENERAL_INFORMATION *)Buffer)->UserName.Buffer != NULL)
+ ) {
+
+ printf("Succeeded\n");
+
+ printf(" Primary Group is: 0x%lx\n",
+ (((USER_GENERAL_INFORMATION *)Buffer)->PrimaryGroupId) );
+ printf(" User Name is: *%wZ*\n",
+ &(((USER_GENERAL_INFORMATION *)Buffer)->UserName) );
+ printf(" Full Name is: *%wZ*\n",
+ &(((USER_GENERAL_INFORMATION *)Buffer)->FullName) );
+ printf(" Admin Comment is: *%wZ*\n",
+ &(((USER_GENERAL_INFORMATION *)Buffer)->AdminComment) );
+ printf(" User Comment is: *%wZ*\n",
+ &(((USER_GENERAL_INFORMATION *)Buffer)->UserComment) );
+
+
+ } else {
+ printf("Failed\n");
+ printf(" One of the UNICODE_STRINGs not returned.\n");
+ TestStatus = FALSE;
+ }
+ SamFreeMemory( Buffer );
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ IgnoreStatus = SamCloseHandle( UserHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+
+
+ printf(" Query User Name Information . . . . . . . . . . . . . ");
+
+
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_READ_GENERAL,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ Buffer = NULL;
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserNameInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+
+ if ( (((USER_NAME_INFORMATION *)Buffer)->UserName.MaximumLength > 0) &&
+ (((USER_NAME_INFORMATION *)Buffer)->UserName.Buffer != NULL)
+ ) {
+
+ printf("Succeeded\n");
+
+ printf(" User Name is: *%wZ*\n",
+ &(((USER_NAME_INFORMATION *)Buffer)->UserName) );
+ printf(" Full Name is: *%wZ*\n",
+ &(((USER_NAME_INFORMATION *)Buffer)->FullName) );
+
+
+
+ } else {
+ printf("Failed\n");
+ printf(" One of the UNICODE_STRINGs not returned.\n");
+ TestStatus = FALSE;
+ }
+ SamFreeMemory( Buffer );
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ IgnoreStatus = SamCloseHandle( UserHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+
+
+ printf(" Query User Account Name Information . . . . . . . . . ");
+
+
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_READ_GENERAL,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ Buffer = NULL;
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserAccountNameInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+
+ if ( (((USER_ACCOUNT_NAME_INFORMATION *)Buffer)->UserName.MaximumLength > 0) &&
+ (((USER_ACCOUNT_NAME_INFORMATION *)Buffer)->UserName.Buffer != NULL)
+ ) {
+
+ printf("Succeeded\n");
+
+ printf(" User Name is: *%wZ*\n",
+ &(((USER_ACCOUNT_NAME_INFORMATION *)Buffer)->UserName) );
+
+
+
+ } else {
+ printf("Failed\n");
+ printf(" UNICODE_STRING not returned.\n");
+ TestStatus = FALSE;
+ }
+ SamFreeMemory( Buffer );
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ IgnoreStatus = SamCloseHandle( UserHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+
+
+ printf(" Query User Full Name Information . . . . . . . . . . ");
+
+
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_READ_GENERAL,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ Buffer = NULL;
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserFullNameInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+
+ if ( (((USER_FULL_NAME_INFORMATION *)Buffer)->FullName.MaximumLength
+ >= 0)
+ ) {
+
+ printf("Succeeded\n");
+
+ printf(" Full Name is: *%wZ*\n",
+ &(((USER_FULL_NAME_INFORMATION *)Buffer)->FullName) );
+
+
+
+ } else {
+ printf("Failed\n");
+ printf(" UNICODE_STRING not returned.\n");
+ TestStatus = FALSE;
+ }
+ SamFreeMemory( Buffer );
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ IgnoreStatus = SamCloseHandle( UserHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+
+
+ printf(" Query User Admin Comment Information . . . . . . . . ");
+
+
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_READ_GENERAL,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ Buffer = NULL;
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserAdminCommentInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+
+ if ( (((USER_ADMIN_COMMENT_INFORMATION *)Buffer)->AdminComment.MaximumLength
+ >= 0)
+ ) {
+
+ printf("Succeeded\n");
+
+ printf(" Admin Comment is: *%wZ*\n",
+ &(((USER_ADMIN_COMMENT_INFORMATION *)Buffer)->AdminComment) );
+
+ } else {
+ printf("Failed\n");
+ printf(" User Admin Comment not returned.\n");
+ TestStatus = FALSE;
+ }
+ SamFreeMemory( Buffer );
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ IgnoreStatus = SamCloseHandle( UserHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+
+
+ printf(" Query User Primary Group Information . . . . . . . . ");
+
+
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_READ_GENERAL,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ Buffer = NULL;
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserPrimaryGroupInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+
+
+ printf("Succeeded\n");
+
+ printf(" Primary Group is: 0x%lx\n",
+ (((USER_PRIMARY_GROUP_INFORMATION *)Buffer)->PrimaryGroupId) );
+
+ SamFreeMemory( Buffer );
+
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ IgnoreStatus = SamCloseHandle( UserHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+
+
+ printf(" Query User Control Information . . . . . . . . . . . ");
+
+
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_READ_ACCOUNT,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ Buffer = NULL;
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserControlInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+
+
+ printf("Succeeded\n");
+
+ printf(" Account Control is: 0x%lx\n",
+ (((USER_CONTROL_INFORMATION *)Buffer)->UserAccountControl) );
+
+ SamFreeMemory( Buffer );
+
+
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ IgnoreStatus = SamCloseHandle( UserHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+
+
+ printf(" Query User Expiration Information . . . . . . . . . . ");
+
+
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_READ_ACCOUNT,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ Buffer = NULL;
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserExpiresInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+
+
+ printf("Succeeded\n");
+
+ printf(" Account Expires on: (0x%lx, 0x%lx)\n",
+ (((USER_EXPIRES_INFORMATION *)Buffer)->AccountExpires.HighPart),
+ (((USER_EXPIRES_INFORMATION *)Buffer)->AccountExpires.LowPart) );
+
+ SamFreeMemory( Buffer );
+
+
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ IgnoreStatus = SamCloseHandle( UserHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+ printf(" Query User Preferences Information . . . . . . . . . ");
+
+
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_READ_PREFERENCES | USER_READ_GENERAL,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ Buffer = NULL;
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserPreferencesInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+
+ if ( (((USER_PREFERENCES_INFORMATION *)Buffer)->UserComment.MaximumLength
+ >= 0)
+ ) {
+
+ printf("Succeeded\n");
+
+ printf(" User Comment is: *%wZ*\n",
+ &(((USER_PREFERENCES_INFORMATION *)Buffer)->UserComment) );
+
+ } else {
+ printf("Failed\n");
+ printf(" One of the UNICODE_STRINGs not returned.\n");
+ TestStatus = FALSE;
+ }
+ SamFreeMemory( Buffer );
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ IgnoreStatus = SamCloseHandle( UserHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+
+
+
+ printf(" Query User Home Directory Information . . . . . . . . ");
+
+
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_READ_LOGON,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ Buffer = NULL;
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserHomeInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+
+ if ( (((USER_HOME_INFORMATION *)Buffer)->HomeDirectory.MaximumLength
+ >= 0) &&
+ (((USER_HOME_INFORMATION *)Buffer)->HomeDirectoryDrive.MaximumLength
+ >= 0)
+ ) {
+
+ printf("Succeeded\n");
+
+ printf(" Home Directory is: *%wZ*\n",
+ &(((USER_HOME_INFORMATION *)Buffer)->HomeDirectory) );
+ printf(" Home Directory Drive is: *%wZ*\n",
+ &(((USER_HOME_INFORMATION *)Buffer)->HomeDirectoryDrive) );
+
+
+ } else {
+ printf("Failed\n");
+ printf(" String not returned.\n");
+ TestStatus = FALSE;
+ }
+ SamFreeMemory( Buffer );
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ IgnoreStatus = SamCloseHandle( UserHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+
+
+ printf(" Query User Script Path Information . . . . . . . . . ");
+
+
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_READ_LOGON,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ Buffer = NULL;
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserScriptInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+
+ if ( (((USER_SCRIPT_INFORMATION *)Buffer)->ScriptPath.MaximumLength
+ >= 0)
+ ) {
+
+ printf("Succeeded\n");
+
+ printf(" Script Path is: *%wZ*\n",
+ &(((USER_SCRIPT_INFORMATION *)Buffer)->ScriptPath) );
+
+
+ } else {
+ printf("Failed\n");
+ printf(" String not returned.\n");
+ TestStatus = FALSE;
+ }
+ SamFreeMemory( Buffer );
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ IgnoreStatus = SamCloseHandle( UserHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+
+ printf(" Query User ProfilePath Information . . . . . . . . . ");
+
+
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_READ_LOGON,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ Buffer = NULL;
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserProfileInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+
+ if ( (((USER_PROFILE_INFORMATION *)Buffer)->ProfilePath.MaximumLength
+ >= 0)
+ ) {
+
+ printf("Succeeded\n");
+
+ printf(" Profile Path is: *%wZ*\n",
+ &(((USER_PROFILE_INFORMATION *)Buffer)->ProfilePath) );
+
+
+ } else {
+ printf("Failed\n");
+ printf(" String not returned.\n");
+ TestStatus = FALSE;
+ }
+ SamFreeMemory( Buffer );
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ IgnoreStatus = SamCloseHandle( UserHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+
+ printf(" Query User Logon Information . . . . . . . . . . . . ");
+
+
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_READ_ACCOUNT | USER_READ_GENERAL | USER_READ_PREFERENCES | USER_READ_LOGON,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ Buffer = NULL;
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserLogonInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+
+ if ( (((USER_LOGON_INFORMATION *)Buffer)->UserName.MaximumLength > 0) &&
+ (((USER_LOGON_INFORMATION *)Buffer)->UserName.Buffer != NULL)
+ ) {
+
+ printf("Succeeded\n");
+
+ printf(" User RID is: 0x%lx\n",
+ (((USER_LOGON_INFORMATION *)Buffer)->UserId) );
+ printf(" Primary Group is: 0x%lx\n",
+ (((USER_LOGON_INFORMATION *)Buffer)->PrimaryGroupId) );
+ printf(" Logon Units are: 0x%lx\n",
+ (((USER_LOGON_INFORMATION *)Buffer)->LogonHours.UnitsPerWeek) );
+ printf(" Bad PWD count is: 0x%lx\n",
+ (((USER_LOGON_INFORMATION *)Buffer)->BadPasswordCount) );
+ printf(" Logon count is: 0x%lx\n",
+ (((USER_LOGON_INFORMATION *)Buffer)->LogonCount) );
+
+ printf(" last Logon is: (0x%lx, 0x%lx)\n",
+ (((USER_LOGON_INFORMATION *)Buffer)->LastLogon.HighPart),
+ (((USER_LOGON_INFORMATION *)Buffer)->LastLogon.LowPart) );
+ printf(" last Logoff is: (0x%lx, 0x%lx)\n",
+ (((USER_LOGON_INFORMATION *)Buffer)->LastLogoff.HighPart),
+ (((USER_LOGON_INFORMATION *)Buffer)->LastLogoff.LowPart) );
+
+
+ printf(" User Name is: *%wZ*\n",
+ &(((USER_LOGON_INFORMATION *)Buffer)->UserName) );
+ printf(" Full Name is: *%wZ*\n",
+ &(((USER_LOGON_INFORMATION *)Buffer)->FullName) );
+ printf(" Home Dir is: *%wZ*\n",
+ &(((USER_LOGON_INFORMATION *)Buffer)->HomeDirectory) );
+ printf(" Home Dir Drive is: *%wZ*\n",
+ &(((USER_LOGON_INFORMATION *)Buffer)->HomeDirectoryDrive) );
+ printf(" Script Path is: *%wZ*\n",
+ &(((USER_LOGON_INFORMATION *)Buffer)->ScriptPath) );
+ printf(" Profile Path is: *%wZ*\n",
+ &(((USER_LOGON_INFORMATION *)Buffer)->ProfilePath) );
+ printf(" WorkStations are: *%wZ*\n",
+ &(((USER_LOGON_INFORMATION *)Buffer)->WorkStations) );
+
+
+
+
+ } else {
+ printf("Failed\n");
+ printf(" One of the UNICODE_STRINGs not returned.\n");
+ TestStatus = FALSE;
+ }
+ SamFreeMemory( Buffer );
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ IgnoreStatus = SamCloseHandle( UserHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+
+
+ printf(" Query User Logon Hours . . . . . . . . . . . . . . . ");
+
+
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_READ_LOGON,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ Buffer = NULL;
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserLogonHoursInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+
+ printf("Succeeded\n");
+
+ printf(" Logon Units are: 0x%lx\n",
+ (((USER_LOGON_HOURS_INFORMATION *)Buffer)->LogonHours.UnitsPerWeek) );
+
+
+ SamFreeMemory( Buffer );
+
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ IgnoreStatus = SamCloseHandle( UserHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+
+
+ printf(" Query Account Information . . . . . . . . . . . . . . ");
+
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_READ_GENERAL | USER_READ_PREFERENCES |
+ USER_READ_LOGON | USER_READ_ACCOUNT,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ Buffer = NULL;
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserAccountInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+
+ if ( (((USER_ACCOUNT_INFORMATION *)Buffer)->UserName.MaximumLength > 0) &&
+ (((USER_ACCOUNT_INFORMATION *)Buffer)->UserName.Buffer != NULL)
+ ) {
+
+ printf("Succeeded\n");
+
+ printf(" User RID is: 0x%lx\n",
+ (((USER_ACCOUNT_INFORMATION *)Buffer)->UserId) );
+ printf(" Primary Group is: 0x%lx\n",
+ (((USER_ACCOUNT_INFORMATION *)Buffer)->PrimaryGroupId) );
+ printf(" Logon Units are: 0x%lx\n",
+ (((USER_ACCOUNT_INFORMATION *)Buffer)->LogonHours.UnitsPerWeek) );
+ printf(" Bad PWD count is: 0x%lx\n",
+ (((USER_ACCOUNT_INFORMATION *)Buffer)->BadPasswordCount) );
+ printf(" Logon count is: 0x%lx\n",
+ (((USER_ACCOUNT_INFORMATION *)Buffer)->LogonCount) );
+ printf(" Account Ctrl is: 0x%lx\n",
+ (((USER_ACCOUNT_INFORMATION *)Buffer)->UserAccountControl) );
+
+ printf(" last Logon is: (0x%lx, 0x%lx)\n",
+ (((USER_ACCOUNT_INFORMATION *)Buffer)->LastLogon.HighPart),
+ (((USER_ACCOUNT_INFORMATION *)Buffer)->LastLogon.LowPart) );
+ printf(" last Logoff is: (0x%lx, 0x%lx)\n",
+ (((USER_ACCOUNT_INFORMATION *)Buffer)->LastLogoff.HighPart),
+ (((USER_ACCOUNT_INFORMATION *)Buffer)->LastLogoff.LowPart) );
+ printf(" Pwd Last Set is: (0x%lx, 0x%lx)\n",
+ (((USER_ACCOUNT_INFORMATION *)Buffer)->PasswordLastSet.HighPart),
+ (((USER_ACCOUNT_INFORMATION *)Buffer)->PasswordLastSet.LowPart) );
+ printf(" Account Expires is: (0x%lx, 0x%lx)\n",
+ (((USER_ACCOUNT_INFORMATION *)Buffer)->AccountExpires.HighPart),
+ (((USER_ACCOUNT_INFORMATION *)Buffer)->AccountExpires.LowPart) );
+
+
+ printf(" User Name is: *%wZ*\n",
+ &(((USER_ACCOUNT_INFORMATION *)Buffer)->UserName) );
+ printf(" Full Name is: *%wZ*\n",
+ &(((USER_ACCOUNT_INFORMATION *)Buffer)->FullName) );
+ printf(" Home Dir is: *%wZ*\n",
+ &(((USER_ACCOUNT_INFORMATION *)Buffer)->HomeDirectory) );
+ printf(" Home Dir Drive is: *%wZ*\n",
+ &(((USER_ACCOUNT_INFORMATION *)Buffer)->HomeDirectoryDrive) );
+ printf(" Script Path is: *%wZ*\n",
+ &(((USER_ACCOUNT_INFORMATION *)Buffer)->ScriptPath) );
+ printf(" Profile Path is: *%wZ*\n",
+ &(((USER_ACCOUNT_INFORMATION *)Buffer)->ProfilePath) );
+ printf(" Admin Comment is: *%wZ*\n",
+ &(((USER_ACCOUNT_INFORMATION *)Buffer)->AdminComment) );
+ printf(" WorkStations are: *%wZ*\n",
+ &(((USER_ACCOUNT_INFORMATION *)Buffer)->WorkStations) );
+
+
+
+ } else {
+ printf("Failed\n");
+ printf(" One of the UNICODE_STRINGs not returned.\n");
+ TestStatus = FALSE;
+ }
+ SamFreeMemory( Buffer );
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ IgnoreStatus = SamCloseHandle( UserHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+
+
+
+
+
+
+ printf(" Query Workstations Information . . . . . . . . . . . ");
+
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_READ_LOGON,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ Buffer = NULL;
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserWorkStationsInformation,
+ &Buffer
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+
+ if ( (((USER_WORKSTATIONS_INFORMATION *)Buffer)->WorkStations.MaximumLength
+ >= 0)
+ ) {
+
+ printf("Succeeded\n");
+
+ printf(" Workstations is: *%wZ*\n",
+ &(((USER_WORKSTATIONS_INFORMATION *)Buffer)->WorkStations) );
+
+
+ } else {
+ printf("Failed\n");
+ printf(" String not returned.\n");
+ TestStatus = FALSE;
+ }
+ SamFreeMemory( Buffer );
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ IgnoreStatus = SamCloseHandle( UserHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+
+
+
+ printf(" Query Internal1 Information . . . . . . . . . . . ");
+
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_READ_LOGON,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ Buffer = NULL;
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserInternal1Information,
+ &Buffer
+ );
+
+ if ( NtStatus == STATUS_INVALID_INFO_CLASS ) {
+
+ //
+ // We're not a trusted client, so we expected this to fail.
+ //
+
+ printf("Succeeded\n");
+
+ } else {
+
+ printf("Failed\n");
+ printf(" Status was %lx.\n", NtStatus );
+ TestStatus = FALSE;
+ if ( NT_SUCCESS( NtStatus ) ) {
+
+ SamFreeMemory( Buffer );
+ }
+ }
+
+// This is the code that USED to test this function, when it was allowed
+// for non-trusted clients.
+//
+// if (NT_SUCCESS(NtStatus)) {
+// if (Buffer != NULL) {
+//
+// if ( (((USER_INTERNAL1_INFORMATION *)Buffer)->CaseInsensitiveDbcs.MaximumLength > 0) &&
+// (((USER_INTERNAL1_INFORMATION *)Buffer)->CaseInsensitiveDbcs.Buffer != NULL) &&
+// (((USER_INTERNAL1_INFORMATION *)Buffer)->CaseSensitiveUnicode.MaximumLength > 0) &&
+// (((USER_INTERNAL1_INFORMATION *)Buffer)->CaseSensitiveUnicode.Buffer != NULL)
+// ) {
+//
+// printf("Succeeded\n");
+//
+// //
+// // Print them out as strings, even though they've been
+// // through a OWF.
+// //
+//
+// printf(" CaseInsensitiveDbcs is: *%s*\n",
+// &(((USER_INTERNAL1_INFORMATION *)Buffer)->CaseInsensitiveDbcs) );
+//
+// printf(" CaseSensitiveUnicode is: *%s*\n",
+// &(((USER_INTERNAL1_INFORMATION *)Buffer)->CaseSensitiveUnicode) );
+//
+//
+// } else {
+// printf("Failed\n");
+// printf(" One of the strings not returned.\n");
+// TestStatus = FALSE;
+// }
+// SamFreeMemory( Buffer );
+// } else {
+// printf("Failed\n");
+// printf(" Buffer address not set on return.\n");
+// printf(" RPC should have allocated a buffer.\n");
+// TestStatus = FALSE;
+// }
+// } else {
+// printf("Failed\n");
+// printf(" Completion status is 0x%lx\n", NtStatus);
+// TestStatus = FALSE;
+// }
+
+ IgnoreStatus = SamCloseHandle( UserHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+
+
+
+ printf(" Query Internal2 Information . . . . . . . . . . . ");
+
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_READ_LOGON,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ Buffer = NULL;
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserInternal2Information,
+ &Buffer
+ );
+
+ if ( NtStatus == STATUS_INVALID_INFO_CLASS ) {
+
+ //
+ // We're not a trusted client, so we don't expect to be able
+ // to do this.
+ //
+
+ printf("Succeeded.\n");
+
+ } else {
+
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ SamFreeMemory( Buffer );
+ }
+
+// This is the code that USED to test this function, when non-trusted
+// clients were allowed to do this...
+//
+// if (NT_SUCCESS(NtStatus)) {
+// if (Buffer != NULL) {
+//
+// printf("Succeeded\n");
+//
+// printf(" last Logon is: (0x%lx, 0x%lx)\n",
+// (((USER_INTERNAL2_INFORMATION *)Buffer)->LastLogon.HighPart),
+// (((USER_INTERNAL2_INFORMATION *)Buffer)->LastLogon.LowPart) );
+// printf(" last Logoff is: (0x%lx, 0x%lx)\n",
+// (((USER_INTERNAL2_INFORMATION *)Buffer)->LastLogoff.HighPart),
+// (((USER_INTERNAL2_INFORMATION *)Buffer)->LastLogoff.LowPart) );
+// printf(" BadPwdCount is: (0x%x)\n",
+// ((USER_INTERNAL2_INFORMATION *)Buffer)->BadPasswordCount );
+// printf(" LogonCount is: (0x%x)\n",
+// ((USER_INTERNAL2_INFORMATION *)Buffer)->LogonCount );
+//
+// SamFreeMemory( Buffer );
+// } else {
+// printf("Failed\n");
+// printf(" Buffer address not set on return.\n");
+// printf(" RPC should have allocated a buffer.\n");
+// TestStatus = FALSE;
+// }
+// } else {
+// printf("Failed\n");
+// printf(" Completion status is 0x%lx\n", NtStatus);
+// TestStatus = FALSE;
+// }
+
+ IgnoreStatus = SamCloseHandle( UserHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+
+
+
+ printf(" Query Set Password Information . . . . . . . . . . . ");
+
+
+
+
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_READ_LOGON,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ Buffer = NULL;
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserSetPasswordInformation,
+ &Buffer
+ );
+ if (NtStatus == STATUS_INVALID_INFO_CLASS ) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ printf(" Expected 0x%lx (INVALID_INFO_CLASS)\n", STATUS_INVALID_INFO_CLASS);
+ TestStatus = FALSE;
+ }
+ IgnoreStatus = SamCloseHandle( UserHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // Get Groups For User Suite //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+ printf("\n");
+ printf(" Get Groups For User . . . . . . . . . . . . . . . . . Suite\n");
+
+ printf(" Get Groups For Well-Known Account . . . . . . . . . . ");
+
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_LIST_GROUPS,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ Buffer = NULL;
+ NtStatus = SamGetGroupsForUser(
+ UserHandle1,
+ (PGROUP_MEMBERSHIP *)&Buffer,
+ &MembershipCount
+ );
+ if (NT_SUCCESS(NtStatus)) {
+ if (Buffer != NULL) {
+
+ printf("Succeeded\n");
+
+
+ printf(" Member of: %d groups\n", MembershipCount);
+ for ( i=0; i<MembershipCount; i++) {
+
+ printf(" Group[%d] Rid/Attributes: 0x%lx/0x%lx\n",
+ i,
+ (((PGROUP_MEMBERSHIP)Buffer)[i].RelativeId),
+ (((PGROUP_MEMBERSHIP)Buffer)[i].Attributes)
+ );
+
+ }
+
+ SamFreeMemory( Buffer );
+
+
+ } else {
+ printf("Failed\n");
+ printf(" Buffer address not set on return.\n");
+ printf(" RPC should have allocated a buffer.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ IgnoreStatus = SamCloseHandle( UserHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // Set User Suite //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+ printf("\n");
+ printf(" Set User . . . . . . . . . . . . . . . . . . . . . . Suite\n");
+
+ printf(" Set General Information . . . . . . . . . . . . . . . ");
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_ALL_ACCESS,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ //
+ // Make the parameter marshallable, but don't worry about values.
+ //
+
+ GeneralInformation.UserName = DummyName1;
+ GeneralInformation.FullName = DummyName1;
+ GeneralInformation.AdminComment = DummyName1;
+ GeneralInformation.UserComment = DummyName1;
+
+ Buffer = &GeneralInformation;
+ NtStatus = SamSetInformationUser(
+ UserHandle1,
+ UserGeneralInformation,
+ Buffer
+ );
+ if (NtStatus == STATUS_INVALID_INFO_CLASS ) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ printf(" Expected 0x%lx (INVALID_INFO_CLASS)\n", STATUS_INVALID_INFO_CLASS);
+ TestStatus = FALSE;
+ }
+ IgnoreStatus = SamCloseHandle( UserHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+
+ printf(" Set Preferences Information . . . . . . . . . . . . . ");
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_READ_GENERAL | USER_WRITE_PREFERENCES | USER_READ_PREFERENCES,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ //
+ // Get the current value...
+ //
+
+ Buffer1 = NULL;
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserPreferencesInformation,
+ &Buffer1
+ );
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(Buffer1 != NULL);
+
+
+ //
+ // Change the fields to new values and write them out.
+ //
+
+ NameLength = ((USER_PREFERENCES_INFORMATION *)Buffer1)->UserComment.Length;
+ if ( NameLength == DummyString1.Length ) {
+ ((USER_PREFERENCES_INFORMATION *)Buffer1)->UserComment = DummyString2;
+ } else {
+ ((USER_PREFERENCES_INFORMATION *)Buffer1)->UserComment = DummyString1;
+ }
+
+ ((USER_PREFERENCES_INFORMATION *)Buffer1)->CountryCode += 1;
+ ((USER_PREFERENCES_INFORMATION *)Buffer1)->CodePage += 1;
+
+ NtStatus = SamSetInformationUser(
+ UserHandle1,
+ UserPreferencesInformation,
+ Buffer1
+ );
+ if ( NT_SUCCESS(NtStatus) ) {
+
+ //
+ // Now check that the change was really made...
+ //
+
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserPreferencesInformation,
+ &Buffer2
+ );
+ ASSERT(NT_SUCCESS( NtStatus ) );
+ if (
+ !RtlCompareString(
+ (PSTRING)&((USER_PREFERENCES_INFORMATION *)Buffer1)->UserComment,
+ (PSTRING)&((USER_PREFERENCES_INFORMATION *)Buffer2)->UserComment,
+ TRUE)
+ &&
+ (((USER_PREFERENCES_INFORMATION *)Buffer1)->CountryCode ==
+ ((USER_PREFERENCES_INFORMATION *)Buffer2)->CountryCode)
+ &&
+ (((USER_PREFERENCES_INFORMATION *)Buffer1)->CodePage ==
+ ((USER_PREFERENCES_INFORMATION *)Buffer2)->CodePage)
+ ) {
+
+ printf("Succeeded\n");
+
+ //
+ // Change back some fields to keep from screwing up our database
+ //
+
+ ((USER_PREFERENCES_INFORMATION *)Buffer1)->CountryCode -= 1;
+ ((USER_PREFERENCES_INFORMATION *)Buffer1)->CodePage -= 1;
+
+ IgnoreStatus = SamSetInformationUser(
+ UserHandle1,
+ UserPreferencesInformation,
+ Buffer1
+ );
+ ASSERT(NT_SUCCESS(IgnoreStatus));
+
+ } else {
+
+ printf("Failed\n");
+ printf(" Values queried don't match values written\n");
+ printf(" UserComment Written is %wZ\n",
+ (PUNICODE_STRING)&((USER_PREFERENCES_INFORMATION *)Buffer1)->UserComment);
+ printf(" UserComment Retrieved is %wZ\n",
+ (PUNICODE_STRING)&((USER_PREFERENCES_INFORMATION *)Buffer2)->UserComment);
+ printf(" CountryCode Written is 0x%lx\n",
+ (ULONG)((USER_PREFERENCES_INFORMATION *)Buffer1)->CountryCode);
+ printf(" CountryCode Retrieved is 0x%lx\n",
+ (ULONG)((USER_PREFERENCES_INFORMATION *)Buffer2)->CountryCode);
+ printf(" CodePage Written is 0x%lx\n",
+ (ULONG)((USER_PREFERENCES_INFORMATION *)Buffer1)->CodePage);
+ printf(" CodePage Retrieved is 0x%lx\n",
+ (ULONG)((USER_PREFERENCES_INFORMATION *)Buffer2)->CodePage);
+
+ TestStatus = FALSE;
+
+ }
+
+ SamFreeMemory( Buffer1 );
+ SamFreeMemory( Buffer2 );
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ SamFreeMemory( Buffer1 );
+
+ }
+
+
+
+
+ printf(" Set Logon Information . . . . . . . . . . . . . . . . ");
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_ALL_ACCESS,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ //
+ // Make the parameter marshallable, but don't worry about values.
+ //
+
+ LogonInformation.UserName = DummyName1;
+ LogonInformation.FullName = DummyName1;
+ LogonInformation.HomeDirectory = DummyName1;
+ LogonInformation.HomeDirectoryDrive = DummyName1;
+ LogonInformation.ScriptPath = DummyName1;
+ LogonInformation.ProfilePath = DummyName1;
+ LogonInformation.WorkStations = DummyName1;
+
+ LogonInformation.LogonHours = DummyLogonHours;
+
+ Buffer = &LogonInformation;
+ NtStatus = SamSetInformationUser(
+ UserHandle1,
+ UserLogonInformation,
+ Buffer
+ );
+ if (NtStatus == STATUS_INVALID_INFO_CLASS ) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ printf(" Expected 0x%lx (INVALID_INFO_CLASS)\n", STATUS_INVALID_INFO_CLASS);
+ TestStatus = FALSE;
+ }
+ IgnoreStatus = SamCloseHandle( UserHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+
+ printf(" Set Logon Hours Information . . . . . . . . . . . . . ");
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_WRITE_ACCOUNT | USER_READ_LOGON,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ //
+ // Get the current value...
+ //
+
+ Buffer1 = NULL;
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserLogonHoursInformation,
+ &Buffer1
+ );
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(Buffer1 != NULL);
+ ASSERT( ((USER_LOGON_HOURS_INFORMATION *)Buffer1)->LogonHours.LogonHours
+ != NULL); //Don't support zero length bit masks in this test yet.
+
+
+ //
+ // Change the field to a new value and write it out.
+ // We have two choices for out test:
+ // NoLogonRestriction
+ // DummyLogonHours
+ //
+ // They are guaranteed to have different values in the
+ // LOGON_HOURS_DIFFERENT_OFFSET byte of their respective bit masks.
+ //
+
+ if ( 0 == ((USER_LOGON_HOURS_INFORMATION *)Buffer1)->LogonHours.LogonHours[LOGON_HOURS_DIFFERENT_OFFSET]) {
+ ((USER_LOGON_HOURS_INFORMATION *)Buffer1)->LogonHours = DummyLogonHours;
+ } else {
+ ((USER_LOGON_HOURS_INFORMATION *)Buffer1)->LogonHours = NoLogonRestriction;
+ }
+
+ NtStatus = SamSetInformationUser(
+ UserHandle1,
+ UserLogonHoursInformation,
+ Buffer1
+ );
+ if ( NT_SUCCESS(NtStatus) ) {
+
+ //
+ // Now check that the change was really made...
+ //
+
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserLogonHoursInformation,
+ &Buffer2
+ );
+ ASSERT(NT_SUCCESS( NtStatus ) );
+ if (
+ ((USER_LOGON_HOURS_INFORMATION *)Buffer1)->LogonHours.LogonHours[LOGON_HOURS_DIFFERENT_OFFSET]
+ ==
+ ((USER_LOGON_HOURS_INFORMATION *)Buffer2)->LogonHours.LogonHours[LOGON_HOURS_DIFFERENT_OFFSET]
+ ) {
+
+ printf("Succeeded\n");
+
+ } else {
+
+ printf("Failed\n");
+ printf(" Value queried doesn't match value written\n");
+ printf(" Units Written are 0x%lx\n",
+ ((USER_LOGON_HOURS_INFORMATION *)Buffer1)->LogonHours.UnitsPerWeek);
+ printf(" Units Retrieved are 0x%lx\n",
+ ((USER_LOGON_HOURS_INFORMATION *)Buffer2)->LogonHours.UnitsPerWeek);
+
+ printf(" Byte 0x%lx of the written bit mask is 0x%lx\n",
+ LOGON_HOURS_DIFFERENT_OFFSET,
+ (ULONG)((USER_LOGON_HOURS_INFORMATION *)Buffer1)->LogonHours.LogonHours[LOGON_HOURS_DIFFERENT_OFFSET]
+ );
+ printf(" Byte 0x%lx of the retrieved bit mask is 0x%lx\n",
+ LOGON_HOURS_DIFFERENT_OFFSET,
+ (ULONG)((USER_LOGON_HOURS_INFORMATION *)Buffer2)->LogonHours.LogonHours[LOGON_HOURS_DIFFERENT_OFFSET]
+ );
+
+ TestStatus = FALSE;
+
+ }
+
+ SamFreeMemory( Buffer1 );
+ SamFreeMemory( Buffer2 );
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ SamFreeMemory( Buffer1 );
+
+ }
+
+
+
+
+
+ printf(" Set Account Information . . . . . . . . . . . . . . . ");
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_WRITE_ACCOUNT |
+ USER_READ_GENERAL |
+ USER_READ_PREFERENCES |
+ USER_READ_LOGON,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ //
+ // Make the parameter marshallable, but don't worry about values.
+ //
+
+ AccountInformation.UserName = DummyName1;
+ AccountInformation.FullName = DummyName1;
+ AccountInformation.HomeDirectory = DummyName1;
+ AccountInformation.HomeDirectoryDrive = DummyName1;
+ AccountInformation.ScriptPath = DummyName1;
+ AccountInformation.ProfilePath = DummyName1;
+ AccountInformation.AdminComment = DummyName1;
+ AccountInformation.WorkStations = DummyName1;
+
+ AccountInformation.LogonHours = DummyLogonHours;
+
+ Buffer = &AccountInformation;
+ NtStatus = SamSetInformationUser(
+ UserHandle1,
+ UserAccountInformation,
+ Buffer
+ );
+ if (NtStatus == STATUS_INVALID_INFO_CLASS ) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ printf(" Expected 0x%lx (INVALID_INFO_CLASS)\n", STATUS_INVALID_INFO_CLASS);
+ TestStatus = FALSE;
+ }
+ IgnoreStatus = SamCloseHandle( UserHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+ printf(" Set Home . . . . . . . . . . . . . . . . . . . . . . ");
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_WRITE_ACCOUNT | USER_READ_LOGON,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ //
+ // Get the current value...
+ //
+
+ Buffer1 = NULL;
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserHomeInformation,
+ &Buffer1
+ );
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(Buffer1 != NULL);
+
+
+ //
+ // Change the field to a new value and write it out.
+ //
+
+ NameLength = ((USER_HOME_INFORMATION *)Buffer1)->HomeDirectory.Length;
+ if ( NameLength == DummyString1.Length ) {
+ ((USER_HOME_INFORMATION *)Buffer1)->HomeDirectory = DummyString2;
+ } else {
+ ((USER_HOME_INFORMATION *)Buffer1)->HomeDirectory = DummyString1;
+ }
+
+ NameLength = ((USER_HOME_INFORMATION *)Buffer1)->HomeDirectoryDrive.Length;
+ if ( NameLength == DummyString1.Length ) {
+ ((USER_HOME_INFORMATION *)Buffer1)->HomeDirectoryDrive = DummyString2;
+ } else {
+ ((USER_HOME_INFORMATION *)Buffer1)->HomeDirectoryDrive = DummyString1;
+ }
+
+ NtStatus = SamSetInformationUser(
+ UserHandle1,
+ UserHomeInformation,
+ Buffer1
+ );
+ if ( NT_SUCCESS(NtStatus) ) {
+
+ //
+ // Now check that the change was really made...
+ //
+
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserHomeInformation,
+ &Buffer2
+ );
+ ASSERT(NT_SUCCESS( NtStatus ) );
+
+ if (!RtlCompareString(
+ (PSTRING)&((USER_HOME_INFORMATION *)Buffer1)->HomeDirectory,
+ (PSTRING)&((USER_HOME_INFORMATION *)Buffer2)->HomeDirectory,
+ TRUE) ) {
+
+ if (!RtlCompareString(
+ (PSTRING)&((USER_HOME_INFORMATION *)Buffer1)->HomeDirectoryDrive,
+ (PSTRING)&((USER_HOME_INFORMATION *)Buffer2)->HomeDirectoryDrive,
+ TRUE)
+ ) {
+ printf("Succeeded\n");
+ } else {
+
+ printf("Failed\n");
+ printf(" Drive Value queried doesn't match value written\n");
+ printf(" Value Written is %wZ\n",
+ (PUNICODE_STRING)&((USER_HOME_INFORMATION *)Buffer1)->HomeDirectoryDrive);
+ printf(" Value Retrieved is %wZ\n",
+ (PUNICODE_STRING)&((USER_HOME_INFORMATION *)Buffer2)->HomeDirectoryDrive);
+
+ TestStatus = FALSE;
+ }
+
+ } else {
+
+ printf("Failed\n");
+ printf(" Directory Value queried doesn't match value written\n");
+ printf(" Value Written is %wZ\n",
+ (PUNICODE_STRING)&((USER_HOME_INFORMATION *)Buffer1)->HomeDirectory);
+ printf(" Value Retrieved is %wZ\n",
+ (PUNICODE_STRING)&((USER_HOME_INFORMATION *)Buffer2)->HomeDirectory);
+
+ TestStatus = FALSE;
+
+ }
+
+ SamFreeMemory( Buffer1 );
+ SamFreeMemory( Buffer2 );
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ SamFreeMemory( Buffer1 );
+
+ }
+
+
+
+
+ printf(" Set Script . . . . . . . . . . . . . . . . . . . . . ");
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_WRITE_ACCOUNT | USER_READ_LOGON,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ //
+ // Get the current value...
+ //
+
+ Buffer1 = NULL;
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserScriptInformation,
+ &Buffer1
+ );
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(Buffer1 != NULL);
+
+
+ //
+ // Change the field to a new value and write it out.
+ //
+
+ NameLength = ((USER_SCRIPT_INFORMATION *)Buffer1)->ScriptPath.Length;
+ if ( NameLength == DummyString1.Length ) {
+ ((USER_SCRIPT_INFORMATION *)Buffer1)->ScriptPath = DummyString2;
+ } else {
+ ((USER_SCRIPT_INFORMATION *)Buffer1)->ScriptPath = DummyString1;
+ }
+
+ NtStatus = SamSetInformationUser(
+ UserHandle1,
+ UserScriptInformation,
+ Buffer1
+ );
+ if ( NT_SUCCESS(NtStatus) ) {
+
+ //
+ // Now check that the change was really made...
+ //
+
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserScriptInformation,
+ &Buffer2
+ );
+ ASSERT(NT_SUCCESS( NtStatus ) );
+ if (
+ !RtlCompareString(
+ (PSTRING)&((USER_SCRIPT_INFORMATION *)Buffer1)->ScriptPath,
+ (PSTRING)&((USER_SCRIPT_INFORMATION *)Buffer2)->ScriptPath,
+ TRUE)
+ ) {
+
+ printf("Succeeded\n");
+
+ } else {
+
+ printf("Failed\n");
+ printf(" Value queried doesn't match value written\n");
+ printf(" Value Written is %wZ\n",
+ (PUNICODE_STRING)&((USER_SCRIPT_INFORMATION *)Buffer1)->ScriptPath);
+ printf(" Value Retrieved is %wZ\n",
+ (PUNICODE_STRING)&((USER_SCRIPT_INFORMATION *)Buffer2)->ScriptPath);
+
+ TestStatus = FALSE;
+
+ }
+
+ SamFreeMemory( Buffer1 );
+ SamFreeMemory( Buffer2 );
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ SamFreeMemory( Buffer1 );
+
+ }
+
+
+
+
+ printf(" Set Profile . . . . . . . . . . . . . . . . . . . . . ");
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_WRITE_ACCOUNT | USER_READ_LOGON,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ //
+ // Get the current value...
+ //
+
+ Buffer1 = NULL;
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserProfileInformation,
+ &Buffer1
+ );
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(Buffer1 != NULL);
+
+
+ //
+ // Change the field to a new value and write it out.
+ //
+
+ NameLength = ((USER_PROFILE_INFORMATION *)Buffer1)->ProfilePath.Length;
+ if ( NameLength == DummyString1.Length ) {
+ ((USER_PROFILE_INFORMATION *)Buffer1)->ProfilePath = DummyString2;
+ } else {
+ ((USER_PROFILE_INFORMATION *)Buffer1)->ProfilePath = DummyString1;
+ }
+
+ NtStatus = SamSetInformationUser(
+ UserHandle1,
+ UserProfileInformation,
+ Buffer1
+ );
+ if ( NT_SUCCESS(NtStatus) ) {
+
+ //
+ // Now check that the change was really made...
+ //
+
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserProfileInformation,
+ &Buffer2
+ );
+ ASSERT(NT_SUCCESS( NtStatus ) );
+ if (
+ !RtlCompareString(
+ (PSTRING)&((USER_PROFILE_INFORMATION *)Buffer1)->ProfilePath,
+ (PSTRING)&((USER_PROFILE_INFORMATION *)Buffer2)->ProfilePath,
+ TRUE)
+ ) {
+
+ printf("Succeeded\n");
+
+ } else {
+
+ printf("Failed\n");
+ printf(" Value queried doesn't match value written\n");
+ printf(" Value Written is %wZ\n",
+ (PUNICODE_STRING)&((USER_PROFILE_INFORMATION *)Buffer1)->ProfilePath);
+ printf(" Value Retrieved is %wZ\n",
+ (PUNICODE_STRING)&((USER_PROFILE_INFORMATION *)Buffer2)->ProfilePath);
+
+ TestStatus = FALSE;
+
+ }
+
+ SamFreeMemory( Buffer1 );
+ SamFreeMemory( Buffer2 );
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ SamFreeMemory( Buffer1 );
+
+ }
+
+
+
+
+ printf(" Set Admin Comment . . . . . . . . . . . . . . . . . . ");
+
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_WRITE_ACCOUNT | USER_READ_GENERAL,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ //
+ // Get the current value...
+ //
+
+ Buffer1 = NULL;
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserAdminCommentInformation,
+ &Buffer1
+ );
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(Buffer1 != NULL);
+
+
+ //
+ // Change the field to a new value and write it out.
+ //
+
+ NameLength = ((USER_ADMIN_COMMENT_INFORMATION *)Buffer1)->AdminComment.Length;
+ if ( NameLength == DummyString1.Length ) {
+ ((USER_ADMIN_COMMENT_INFORMATION *)Buffer1)->AdminComment = DummyString2;
+ } else {
+ ((USER_ADMIN_COMMENT_INFORMATION *)Buffer1)->AdminComment = DummyString1;
+ }
+
+ NtStatus = SamSetInformationUser(
+ UserHandle1,
+ UserAdminCommentInformation,
+ Buffer1
+ );
+ if ( NT_SUCCESS(NtStatus) ) {
+
+ //
+ // Now check that the change was really made...
+ //
+
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserAdminCommentInformation,
+ &Buffer2
+ );
+ ASSERT(NT_SUCCESS( NtStatus ) );
+ if (
+ !RtlCompareString(
+ (PSTRING)&((USER_ADMIN_COMMENT_INFORMATION *)Buffer1)->AdminComment,
+ (PSTRING)&((USER_ADMIN_COMMENT_INFORMATION *)Buffer2)->AdminComment,
+ TRUE)
+ ) {
+
+ printf("Succeeded\n");
+
+ } else {
+
+ printf("Failed\n");
+ printf(" Value queried doesn't match value written\n");
+ printf(" Value Written is %wZ\n",
+ (PUNICODE_STRING)&((USER_ADMIN_COMMENT_INFORMATION *)Buffer1)->AdminComment);
+ printf(" Value Retrieved is %wZ\n",
+ (PUNICODE_STRING)&((USER_ADMIN_COMMENT_INFORMATION *)Buffer2)->AdminComment);
+
+ TestStatus = FALSE;
+
+ }
+
+ SamFreeMemory( Buffer1 );
+ SamFreeMemory( Buffer2 );
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ SamFreeMemory( Buffer1 );
+
+ }
+
+
+ printf(" Set Workstations . . . . . . . . . . . . . . . . . . ");
+ printf("BROKEN TEST - NOT TESTED\n");
+#ifdef BROKEN
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_WRITE_ACCOUNT | USER_READ_LOGON,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ //
+ // Get the current value...
+ //
+
+ Buffer1 = NULL;
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserWorkStationsInformation,
+ &Buffer1
+ );
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(Buffer1 != NULL);
+
+
+ //
+ // Change the field to a new value and write it out.
+ //
+
+ NameLength = ((USER_WORKSTATIONS_INFORMATION *)Buffer1)->WorkStations.Length;
+ if ( NameLength == DummyString1.Length ) {
+ ((USER_WORKSTATIONS_INFORMATION *)Buffer1)->WorkStations = DummyString2;
+ } else {
+ ((USER_WORKSTATIONS_INFORMATION *)Buffer1)->WorkStations = DummyString1;
+ }
+
+ NtStatus = SamSetInformationUser(
+ UserHandle1,
+ UserWorkStationsInformation,
+ Buffer1
+ );
+ if ( NT_SUCCESS(NtStatus) ) {
+
+ //
+ // Now check that the change was really made...
+ //
+
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserWorkStationsInformation,
+ &Buffer2
+ );
+ ASSERT(NT_SUCCESS( NtStatus ) );
+ if (
+ !RtlCompareString(
+ (PSTRING)&((USER_WORKSTATIONS_INFORMATION *)Buffer1)->WorkStations,
+ (PSTRING)&((USER_WORKSTATIONS_INFORMATION *)Buffer2)->WorkStations,
+ TRUE)
+ ) {
+
+ printf("Succeeded\n");
+
+ } else {
+
+ printf("Failed\n");
+ printf(" Value queried doesn't match value written\n");
+ printf(" Value Written is %wZ\n",
+ (PUNICODE_STRING)&((USER_WORKSTATIONS_INFORMATION *)Buffer1)->WorkStations);
+ printf(" Value Retrieved is %wZ\n",
+ (PUNICODE_STRING)&((USER_WORKSTATIONS_INFORMATION *)Buffer2)->WorkStations);
+
+ TestStatus = FALSE;
+
+ }
+
+ SamFreeMemory( Buffer1 );
+ SamFreeMemory( Buffer2 );
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ SamFreeMemory( Buffer1 );
+
+ }
+#endif //BROKEN
+
+
+ printf(" Set Internal1 . . . . . . . . . . . . . . . . . . . ");
+
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_WRITE_ACCOUNT | USER_READ_LOGON | USER_FORCE_PASSWORD_CHANGE,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ //
+ // We can't get the current values, since this level is only
+ // queryable by trusted clients. So just try setting a couple
+ // of values and make sure that we don't get an error.
+ //
+
+ Buffer1 = RtlAllocateHeap( RtlProcessHeap(), 0, sizeof(USER_INTERNAL1_INFORMATION) );
+ ASSERT( Buffer1 != NULL );
+
+ ((PUSER_INTERNAL1_INFORMATION)Buffer1)->NtPasswordPresent = FALSE;
+ ((PUSER_INTERNAL1_INFORMATION)Buffer1)->LmPasswordPresent = FALSE;
+
+ NtStatus = SamSetInformationUser(
+ UserHandle1,
+ UserInternal1Information,
+ Buffer1
+ );
+
+ if (NtStatus != STATUS_PASSWORD_RESTRICTION) {
+
+ printf("Failed\n");
+ printf(" Expected Status = 0x%lx\n", STATUS_PASSWORD_RESTRICTION);
+ printf(" Received Status = 0x%lx\n", NtStatus );
+ TestStatus = FALSE;
+
+ } else {
+
+ //
+ // The NULL password worked, so let's try a real password.
+ //
+
+ NtStatus = RtlCalculateNtOwfPassword(
+ &DummyName1,
+ &((PUSER_INTERNAL1_INFORMATION)Buffer1)->NtOwfPassword
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ ((PUSER_INTERNAL1_INFORMATION)Buffer1)->NtPasswordPresent = TRUE;
+
+ NtStatus = RtlCalculateLmOwfPassword(
+ DUMMY_STRING1,
+ &((PUSER_INTERNAL1_INFORMATION)Buffer1)->LmOwfPassword
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ ((PUSER_INTERNAL1_INFORMATION)Buffer1)->LmPasswordPresent = TRUE;
+
+ NtStatus = SamSetInformationUser(
+ UserHandle1,
+ UserInternal1Information,
+ Buffer1
+ );
+
+ if ( NT_SUCCESS(NtStatus) ) {
+
+ printf("Succeeded\n");
+
+ } else {
+
+ printf("Failed\n");
+ printf(" Return status was %lx\n", NtStatus );
+ TestStatus = FALSE;
+ }
+ }
+
+ RtlFreeHeap( RtlProcessHeap(), 0, Buffer1 );
+
+
+// This is the code that used to be here, when UserInternal1Information was
+// queryable by non-trusted clients...
+//
+// Buffer1 = NULL;
+// NtStatus = SamQueryInformationUser(
+// UserHandle1,
+// UserInternal1Information,
+// &Buffer1
+// );
+// TST_SUCCESS_ASSERT(NtStatus);
+// ASSERT(Buffer1 != NULL);
+//
+// //
+// // The passwords were initially empty. Put in some random
+// // OWF passwords, and have them written out.
+// //
+//
+// NtStatus = RtlCalculateNtOwfPassword(
+// (PNT_PASSWORD)&DummyName1,
+// &EncryptedPasswordBuffer
+// );
+//
+// ((USER_INTERNAL1_INFORMATION *)Buffer1)->CaseSensitiveUnicode.Buffer = (PCHAR)&EncryptedPasswordBuffer;
+// ((USER_INTERNAL1_INFORMATION *)Buffer1)->CaseSensitiveUnicode.Length = 16;
+// ((USER_INTERNAL1_INFORMATION *)Buffer1)->CaseSensitiveUnicode.MaximumLength = 16;
+//
+// NtStatus = RtlCalculateNtOwfPassword(
+// (PNT_PASSWORD)&DummyName2,
+// &EncryptedPasswordBuffer2
+// );
+//
+// ((USER_INTERNAL1_INFORMATION *)Buffer1)->CaseInsensitiveDbcs.Buffer = (PCHAR)&EncryptedPasswordBuffer2;
+// ((USER_INTERNAL1_INFORMATION *)Buffer1)->CaseInsensitiveDbcs.Length = 16;
+// ((USER_INTERNAL1_INFORMATION *)Buffer1)->CaseInsensitiveDbcs.MaximumLength = 16;
+//
+// NtStatus = SamSetInformationUser(
+// UserHandle1,
+// UserInternal1Information,
+// Buffer1
+// );
+// if ( NT_SUCCESS(NtStatus) ) {
+//
+// //
+// // Now check that the change was really made...
+// //
+//
+// NtStatus = SamQueryInformationUser(
+// UserHandle1,
+// UserInternal1Information,
+// &Buffer2
+// );
+// ASSERT(NT_SUCCESS( NtStatus ) );
+//
+// if ( (
+// !RtlCompareString(
+// (PSTRING)&((USER_INTERNAL1_INFORMATION *)Buffer1)->CaseSensitiveUnicode,
+// (PSTRING)&((USER_INTERNAL1_INFORMATION *)Buffer2)->CaseSensitiveUnicode,
+// TRUE)
+// ) || (
+// !RtlCompareString(
+// (PSTRING)&((USER_INTERNAL1_INFORMATION *)Buffer1)->CaseInsensitiveDbcs,
+// (PSTRING)&((USER_INTERNAL1_INFORMATION *)Buffer2)->CaseInsensitiveDbcs,
+// TRUE)
+// ) ) {
+//
+// printf("Succeeded\n");
+//
+// } else {
+//
+// printf("Failed\n");
+// printf(" Value queried doesn't match value written\n");
+// printf(" CaseInsensitiveDbcs Written is %wZ\n",
+// (PUNICODE_STRING)&((USER_INTERNAL1_INFORMATION *)Buffer1)->CaseInsensitiveDbcs);
+// printf(" CaseInsensitiveDbcs Retrieved is %wZ\n",
+// (PUNICODE_STRING)&((USER_INTERNAL1_INFORMATION *)Buffer2)->CaseInsensitiveDbcs);
+// printf(" CaseSensitiveUnicode Written is %wZ\n",
+// (PUNICODE_STRING)&((USER_INTERNAL1_INFORMATION *)Buffer1)->CaseSensitiveUnicode);
+// printf(" CaseSensitiveUnicode Retrieved is %wZ\n",
+// (PUNICODE_STRING)&((USER_INTERNAL1_INFORMATION *)Buffer2)->CaseSensitiveUnicode);
+//
+// TestStatus = FALSE;
+//
+// }
+//
+// SamFreeMemory( Buffer1 );
+// SamFreeMemory( Buffer2 );
+//
+// } else {
+// printf("Failed\n");
+// printf(" Completion status is 0x%lx\n", NtStatus);
+// TestStatus = FALSE;
+// SamFreeMemory( Buffer1 );
+//
+// }
+
+
+
+ printf(" Set Internal2 . . . . . . . . . . . . . . . . . . . ");
+
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_WRITE_ACCOUNT | USER_READ_LOGON,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ //
+ // We can't get the current values, since this level is only
+ // queryable by trusted clients. We can't set either, but
+ // try it and make sure we get the correct error.
+ //
+
+ Buffer1 = RtlAllocateHeap( RtlProcessHeap(), 0, sizeof(USER_INTERNAL2_INFORMATION) );
+ ASSERT( Buffer1 != NULL );
+
+ ((USER_INTERNAL2_INFORMATION *)Buffer1)->LastLogon.HighPart = 1;
+ ((USER_INTERNAL2_INFORMATION *)Buffer1)->LastLogoff.HighPart = 2;
+ ((USER_INTERNAL2_INFORMATION *)Buffer1)->LastLogon.LowPart = 3;
+ ((USER_INTERNAL2_INFORMATION *)Buffer1)->LastLogoff.LowPart = 4;
+ ((USER_INTERNAL2_INFORMATION *)Buffer1)->BadPasswordCount = 5;
+ ((USER_INTERNAL2_INFORMATION *)Buffer1)->LogonCount = 6;
+
+ NtStatus = SamSetInformationUser(
+ UserHandle1,
+ UserInternal2Information,
+ Buffer1
+ );
+
+ RtlFreeHeap( RtlProcessHeap(), 0, Buffer1 );
+
+ if ( NtStatus == STATUS_INVALID_INFO_CLASS ) {
+
+ printf("Succeeded\n");
+
+ } else {
+
+ printf("Failed\n");
+ printf(" Expected Status = 0x%lx\n", STATUS_INVALID_INFO_CLASS);
+ printf(" Received Status = 0x%lx\n", NtStatus );
+ TestStatus = FALSE;
+ }
+
+// This is the code that was here when UserInternal2Information could be
+// queried and set by non-trusted clients...
+//
+// //
+// // Get the current values...
+// //
+//
+// Buffer1 = NULL;
+// NtStatus = SamQueryInformationUser(
+// UserHandle1,
+// UserInternal2Information,
+// &Buffer1
+// );
+// TST_SUCCESS_ASSERT(NtStatus);
+// ASSERT(Buffer1 != NULL);
+//
+// //
+// // Now change the fields and write them out.
+// //
+//
+// ((USER_INTERNAL2_INFORMATION *)Buffer1)->LastLogon.HighPart += 1;
+// ((USER_INTERNAL2_INFORMATION *)Buffer1)->LastLogoff.HighPart += 1;
+// ((USER_INTERNAL2_INFORMATION *)Buffer1)->LastLogon.LowPart += 2;
+// ((USER_INTERNAL2_INFORMATION *)Buffer1)->LastLogoff.LowPart += 2;
+// ((USER_INTERNAL2_INFORMATION *)Buffer1)->BadPasswordCount += 1;
+// ((USER_INTERNAL2_INFORMATION *)Buffer1)->LogonCount += 1;
+//
+// NtStatus = SamSetInformationUser(
+// UserHandle1,
+// UserInternal2Information,
+// Buffer1
+// );
+// if ( NT_SUCCESS(NtStatus) ) {
+//
+// //
+// // Now check that the change was really made...
+// //
+//
+// NtStatus = SamQueryInformationUser(
+// UserHandle1,
+// UserInternal2Information,
+// &Buffer2
+// );
+// ASSERT(NT_SUCCESS( NtStatus ) );
+// if (
+// (((USER_INTERNAL2_INFORMATION *)Buffer1)->LastLogon.HighPart ==
+// ((USER_INTERNAL2_INFORMATION *)Buffer2)->LastLogon.HighPart) &&
+// (((USER_INTERNAL2_INFORMATION *)Buffer1)->LastLogon.LowPart ==
+// ((USER_INTERNAL2_INFORMATION *)Buffer2)->LastLogon.LowPart) &&
+// (((USER_INTERNAL2_INFORMATION *)Buffer1)->LastLogoff.HighPart ==
+// ((USER_INTERNAL2_INFORMATION *)Buffer2)->LastLogoff.HighPart) &&
+// (((USER_INTERNAL2_INFORMATION *)Buffer1)->LastLogoff.LowPart ==
+// ((USER_INTERNAL2_INFORMATION *)Buffer2)->LastLogoff.LowPart) &&
+// (((USER_INTERNAL2_INFORMATION *)Buffer1)->BadPasswordCount ==
+// ((USER_INTERNAL2_INFORMATION *)Buffer2)->BadPasswordCount) &&
+// (((USER_INTERNAL2_INFORMATION *)Buffer1)->LogonCount ==
+// ((USER_INTERNAL2_INFORMATION *)Buffer2)->LogonCount)
+// ) {
+//
+// printf("Succeeded\n");
+//
+// } else {
+//
+// printf("Failed\n");
+// printf(" Value queried doesn't match value written\n");
+//
+// TestStatus = FALSE;
+//
+// }
+//
+// SamFreeMemory( Buffer1 );
+// SamFreeMemory( Buffer2 );
+//
+// } else {
+// printf("Failed\n");
+// printf(" Completion status is 0x%lx\n", NtStatus);
+// TestStatus = FALSE;
+// SamFreeMemory( Buffer1 );
+//
+// }
+
+
+
+ printf(" Set Password . . . . . . . . . . . . . . . . . . . . ");
+
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_FORCE_PASSWORD_CHANGE,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ //
+ // Create a fake cleartext UNICODE password and write it out.
+ //
+
+ NtStatus = SamSetInformationUser(
+ UserHandle1,
+ UserSetPasswordInformation,
+ &DummyName2
+ );
+ if ( NT_SUCCESS(NtStatus) ) {
+
+ //
+ // We can't verify that it really worked, so we just have
+ // to trust the return code.
+ //
+
+ printf("Succeeded\n");
+
+ } else {
+
+ printf("Failed\n");
+ printf(" Return code was %lx\n", NtStatus );
+ TestStatus = FALSE;
+ }
+
+
+
+ printf(" Set Control . . . . . . . . . . . . . . . . . . . . . ");
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_WRITE_ACCOUNT | USER_READ_ACCOUNT,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ Buffer1 = NULL;
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserControlInformation,
+ &Buffer1
+ );
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(Buffer1 != NULL);
+
+ //
+ // Change the value and write it back
+ //
+
+ ((USER_CONTROL_INFORMATION *)Buffer1)->UserAccountControl ^= USER_HOME_DIRECTORY_REQUIRED;
+
+
+ NtStatus = SamSetInformationUser(
+ UserHandle1,
+ UserControlInformation,
+ Buffer1
+ );
+ if (NT_SUCCESS(NtStatus)) {
+
+ //
+ // Check the written value to make sure it stuck
+ //
+
+ Buffer2 = NULL;
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserControlInformation,
+ &Buffer2
+ );
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(Buffer2 != NULL);
+
+ if ( ((USER_CONTROL_INFORMATION *)Buffer1)->UserAccountControl ==
+ ((USER_CONTROL_INFORMATION *)Buffer2)->UserAccountControl ) {
+
+ printf("Succeeded\n");
+
+ SamFreeMemory( Buffer2 );
+
+ //
+ // Make sure the account is left enabled to prevent problems.
+ //
+
+ ((USER_CONTROL_INFORMATION *)Buffer1)->UserAccountControl &= ~USER_ACCOUNT_DISABLED;
+
+ IgnoreStatus = SamSetInformationUser(
+ UserHandle1,
+ UserControlInformation,
+ Buffer1
+ );
+ ASSERT(NT_SUCCESS(IgnoreStatus));
+
+ } else {
+ printf("Failed\n");
+ printf(" Returned Value Doesn't Match Set Value.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ SamFreeMemory( Buffer1 );
+ IgnoreStatus = SamCloseHandle( UserHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+
+ printf(" Set Expires . . . . . . . . . . . . . . . . . . . . . ");
+ printf("BROKEN TEST - NOT TESTED\n");
+#ifdef BROKEN
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_WRITE_ACCOUNT | USER_READ_ACCOUNT,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ Buffer1 = NULL;
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserExpiresInformation,
+ &Buffer1
+ );
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(Buffer1 != NULL);
+
+ //
+ // Change the value and write it back
+ //
+
+ ((USER_EXPIRES_INFORMATION *)Buffer1)->AccountExpires.LowPart += 1234;
+ ((USER_EXPIRES_INFORMATION *)Buffer1)->AccountExpires.HighPart += 1234;
+
+
+ NtStatus = SamSetInformationUser(
+ UserHandle1,
+ UserExpiresInformation,
+ Buffer1
+ );
+ if (NT_SUCCESS(NtStatus)) {
+
+ //
+ // Check the written value to make sure it stuck
+ //
+
+ Buffer2 = NULL;
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserExpiresInformation,
+ &Buffer2
+ );
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(Buffer2 != NULL);
+
+ if ( ( ((USER_EXPIRES_INFORMATION *)Buffer1)->AccountExpires.LowPart ==
+ ((USER_EXPIRES_INFORMATION *)Buffer2)->AccountExpires.LowPart ) &&
+ ( ((USER_EXPIRES_INFORMATION *)Buffer1)->AccountExpires.HighPart ==
+ ((USER_EXPIRES_INFORMATION *)Buffer2)->AccountExpires.HighPart ) ) {
+
+ printf("Succeeded\n");
+
+ SamFreeMemory( Buffer2 );
+
+ //
+ // Change the values back
+ //
+
+ ((USER_EXPIRES_INFORMATION *)Buffer1)->AccountExpires.LowPart += 1234;
+ ((USER_EXPIRES_INFORMATION *)Buffer1)->AccountExpires.HighPart += 1234;
+
+ IgnoreStatus = SamSetInformationUser(
+ UserHandle1,
+ UserExpiresInformation,
+ Buffer1
+ );
+ ASSERT(NT_SUCCESS(IgnoreStatus));
+
+ } else {
+ printf("Failed\n");
+ printf(" Returned Value Doesn't Match Set Value.\n");
+ TestStatus = FALSE;
+ }
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+ SamFreeMemory( Buffer1 );
+ IgnoreStatus = SamCloseHandle( UserHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+#endif //BROKEN
+
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // Change Password For User Suite //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+ printf("\n");
+ printf(" Change Password For User . . . . . . . . . . . . . . Suite\n");
+
+ printf(" Change Password For Well-Known User . . . . . . . . . ");
+
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_CHANGE_PASSWORD,
+ DOMAIN_USER_RID_ADMIN,
+ &UserHandle1
+ );
+ ASSERT(NT_SUCCESS(NtStatus) );
+
+ Buffer = NULL;
+
+ //
+ // The current password is DummyName2. Using DummyName2 as the
+ // old password, change it to DummyName1 and make sure we get
+ // STATUS_SUCCESS.
+ //
+
+ NtStatus = SamChangePasswordUser(
+ UserHandle1,
+ &DummyName2,
+ &DummyName1
+ );
+
+ //
+ // The current password is DummyName1. Using something WRONG for
+ // the old password, try to change it to DummyName2 and make sure
+ // it doesn't succeed.
+ //
+
+ if ( NtStatus == STATUS_SUCCESS ) {
+
+ NtStatus = SamChangePasswordUser(
+ UserHandle1,
+ &DummyName2,
+ &DummyName2
+ );
+
+ if ( NtStatus == STATUS_SUCCESS ) {
+
+ NtStatus = STATUS_UNSUCCESSFUL;
+
+ } else {
+
+ NtStatus = STATUS_SUCCESS;
+ }
+ }
+
+ //
+ // The current password is DummyName1. Using DummyName1 as the
+ // old password, change it to DummyName2 and make sure it works
+ // since by default there is no password history.
+ //
+
+ if ( NtStatus == STATUS_SUCCESS ) {
+
+ NtStatus = SamChangePasswordUser(
+ UserHandle1,
+ &DummyName1,
+ &DummyName2
+ );
+ }
+
+ if ( NT_SUCCESS( NtStatus ) ) {
+
+ printf("Succeeded\n");
+
+ } else {
+
+ printf("Failed\n");
+ printf(" Status is %lx\n", NtStatus);
+
+ TestStatus = FALSE;
+ }
+
+ IgnoreStatus = SamCloseHandle( UserHandle1 );
+ ASSERT( NT_SUCCESS(IgnoreStatus) );
+
+ }
+
+// END PASS #1
+
+ if (Pass == 2) {
+
+ printf("\n");
+ printf("\n");
+ printf(" User (Pass #2) . . . . . . . . . . . . . . . . . . . Test\n");
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // Delete User Suite //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+ printf("\n");
+ printf(" Delete User . . . . . . . . . . . . . . . . . . . . Suite\n");
+
+
+
+ printf(" Delete Normal User . . . . . . . . . . . . . . . . . ");
+
+ //
+ // This User was created in pass #1
+ //
+
+ RtlInitString( &AccountNameAnsi, USER_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountNames[0], &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ NtStatus = SamLookupNamesInDomain(
+ DomainHandle,
+ 1,
+ &AccountNames[0],
+ &LookedUpRids,
+ &LookedUpUses
+ );
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(LookedUpUses[0] == SidTypeUser);
+ RtlFreeUnicodeString( &AccountNames[0] );
+
+
+
+ UserHandle1 = NULL;
+
+ NtStatus = SamOpenUser( DomainHandle, DELETE, LookedUpRids[0], &UserHandle1 );
+ TST_SUCCESS_ASSERT(NtStatus);
+ SamFreeMemory( LookedUpUses ); SamFreeMemory( LookedUpRids );
+
+ NtStatus = SamDeleteUser( UserHandle1 );
+ if (NT_SUCCESS(NtStatus)) {
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+
+
+ printf(" Delete Admin Group Member . . . . . . . . . . . . . . ");
+ printf("(Unimplemented)\n");
+
+ printf(" Delete Last Admin Group Member . . . . . . . . . . . ");
+ printf("(Unimplemented)\n");
+
+
+
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // Set User Suite //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+ printf("\n");
+ printf(" Set User (Pass 2) . . . . . . . . . . . . . . . . . . Suite\n");
+
+ printf(" Set ALL information. . . . . . . . . . . . ");
+ printf("BROKEN TEST - NOT TESTED\n");
+#ifdef BROKEN
+
+ RtlInitString( &AccountNameAnsi, "AllUser" );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ UserRid = 0;
+ UserHandle1 = NULL;
+ NtStatus = SamCreateUserInDomain(
+ DomainHandle,
+ &AccountName,
+ USER_ALL_ACCESS,
+ &UserHandle1,
+ &UserRid
+ );
+ RtlFreeUnicodeString( &AccountName );
+
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ All = NULL;
+
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserAllInformation,
+ &All
+ );
+
+ if ( NT_SUCCESS( NtStatus ) ) {
+
+ //
+ // Now change some of the data, and set it
+ //
+
+ RtlInitString( &TmpAnsiString, "FullName" );
+ TmpStatus = RtlAnsiStringToUnicodeString(
+ (PUNICODE_STRING)(&All->FullName),
+ &TmpAnsiString,
+ TRUE );
+ ASSERT( NT_SUCCESS( TmpStatus ) );
+
+ RtlInitString( &TmpAnsiString, "HomeDirectory" );
+ TmpStatus = RtlAnsiStringToUnicodeString(
+ (PUNICODE_STRING)(&All->HomeDirectory),
+ &TmpAnsiString,
+ TRUE );
+ ASSERT( NT_SUCCESS( TmpStatus ) );
+
+ RtlInitString( &TmpAnsiString, "HomeDirectoryDrive" );
+ TmpStatus = RtlAnsiStringToUnicodeString(
+ (PUNICODE_STRING)(&All->HomeDirectoryDrive),
+ &TmpAnsiString,
+ TRUE );
+ ASSERT( NT_SUCCESS( TmpStatus ) );
+
+ RtlInitString( &TmpAnsiString, "ScriptPath" );
+ TmpStatus = RtlAnsiStringToUnicodeString(
+ (PUNICODE_STRING)(&All->ScriptPath),
+ &TmpAnsiString,
+ TRUE );
+ ASSERT( NT_SUCCESS( TmpStatus ) );
+
+ RtlInitString( &TmpAnsiString, "ProfilePath" );
+ TmpStatus = RtlAnsiStringToUnicodeString(
+ (PUNICODE_STRING)(&All->ProfilePath),
+ &TmpAnsiString,
+ TRUE );
+ ASSERT( NT_SUCCESS( TmpStatus ) );
+
+ RtlInitString( &TmpAnsiString, "AdminComment" );
+ TmpStatus = RtlAnsiStringToUnicodeString(
+ (PUNICODE_STRING)(&All->AdminComment),
+ &TmpAnsiString,
+ TRUE );
+ ASSERT( NT_SUCCESS( TmpStatus ) );
+
+ RtlInitString( &TmpAnsiString, "WorkStations" );
+ TmpStatus = RtlAnsiStringToUnicodeString(
+ (PUNICODE_STRING)(&All->WorkStations),
+ &TmpAnsiString,
+ TRUE );
+ ASSERT( NT_SUCCESS( TmpStatus ) );
+
+ RtlInitString( &TmpAnsiString, "UserComment" );
+ TmpStatus = RtlAnsiStringToUnicodeString(
+ (PUNICODE_STRING)(&All->UserComment),
+ &TmpAnsiString,
+ TRUE );
+ ASSERT( NT_SUCCESS( TmpStatus ) );
+
+ RtlInitString( &TmpAnsiString, "Parameters" );
+ TmpStatus = RtlAnsiStringToUnicodeString(
+ (PUNICODE_STRING)(&All->Parameters),
+ &TmpAnsiString,
+ TRUE );
+ ASSERT( NT_SUCCESS( TmpStatus ) );
+
+ All->CountryCode = 7;
+ All->CodePage = 8;
+
+ All->PasswordExpired = TRUE;
+ All->NtPasswordPresent = TRUE;
+ All->LmPasswordPresent = FALSE;
+
+ RtlInitString( &TmpAnsiString, "NtPassword" );
+ TmpStatus = RtlAnsiStringToUnicodeString(
+ (PUNICODE_STRING)(&All->NtPassword),
+ &TmpAnsiString,
+ TRUE );
+ ASSERT( NT_SUCCESS( TmpStatus ) );
+
+ All->LogonHours.UnitsPerWeek = 7;
+
+ All->WhichFields = ( USER_ALL_FULLNAME |
+ USER_ALL_HOMEDIRECTORY |
+ USER_ALL_HOMEDIRECTORYDRIVE |
+ USER_ALL_SCRIPTPATH |
+ USER_ALL_PROFILEPATH |
+ USER_ALL_ADMINCOMMENT |
+ USER_ALL_WORKSTATIONS |
+ USER_ALL_USERCOMMENT |
+ USER_ALL_PARAMETERS |
+ USER_ALL_COUNTRYCODE |
+ USER_ALL_CODEPAGE |
+ USER_ALL_PASSWORDEXPIRED |
+ USER_ALL_NTPASSWORDPRESENT |
+ USER_ALL_LOGONHOURS );
+
+ NtStatus = SamSetInformationUser(
+ UserHandle1,
+ UserAllInformation,
+ All
+ );
+
+ if ( NT_SUCCESS( NtStatus ) ) {
+
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserAllInformation,
+ &All2
+ );
+
+ if ( NT_SUCCESS( NtStatus ) ) {
+
+ //
+ // Verify that queried info is as we set it
+ //
+
+ if (
+
+ //
+ // Fields that we didn't touch. Note that
+ // PasswordMustChange changed anyway, since we
+ // changed from a null to a non-null password.
+ //
+
+ ( All2->WhichFields != (USER_ALL_READ_GENERAL_MASK |
+ USER_ALL_READ_PREFERENCES_MASK |
+ USER_ALL_READ_ACCOUNT_MASK |
+ USER_ALL_READ_LOGON_MASK) ) ||
+ ( !(All->LastLogon.QuadPart ==
+ All2->LastLogon.QuadPart ) ) ||
+ ( !(All->LastLogoff.QuadPart ==
+ All2->LastLogoff.QuadPart ) ) ||
+ ( !(All->PasswordLastSet.QuadPart ==
+ All2->PasswordLastSet.QuadPart ) ) ||
+ ( !(All->AccountExpires.QuadPart ==
+ All2->AccountExpires.QuadPart ) ) ||
+ ( !(All->PasswordCanChange.QuadPart ==
+ All2->PasswordCanChange.QuadPart ) ) ||
+ ( (All->PasswordMustChange.QuadPart ==
+ All2->PasswordMustChange.QuadPart ) ) ||
+ (RtlCompareUnicodeString(
+ &(All->UserName),
+ &(All2->UserName),
+ FALSE) != 0) ||
+ ( All->UserId != All2->UserId ) ||
+ ( All->PrimaryGroupId != All2->PrimaryGroupId ) ||
+ ( All->UserAccountControl != All2->UserAccountControl ) ||
+ ( All->PrivateDataSensitive !=
+ All2->PrivateDataSensitive ) ||
+
+ // Fields that we changed. Note that we set
+ // NtPasswordSet, but it shouldn't be set on return.
+
+ (RtlCompareUnicodeString(
+ &(All->FullName),
+ &(All2->FullName),
+ FALSE) != 0) ||
+ (RtlCompareUnicodeString(
+ &(All->HomeDirectory),
+ &(All2->HomeDirectory),
+ FALSE) != 0) ||
+ (RtlCompareUnicodeString(
+ &(All->HomeDirectoryDrive),
+ &(All2->HomeDirectoryDrive),
+ FALSE) != 0) ||
+ (RtlCompareUnicodeString(
+ &(All->ScriptPath),
+ &(All2->ScriptPath),
+ FALSE) != 0) ||
+ (RtlCompareUnicodeString(
+ &(All->ProfilePath),
+ &(All2->ProfilePath),
+ FALSE) != 0) ||
+ (RtlCompareUnicodeString(
+ &(All->AdminComment),
+ &(All2->AdminComment),
+ FALSE) != 0) ||
+ (RtlCompareUnicodeString(
+ &(All->WorkStations),
+ &(All2->WorkStations),
+ FALSE) != 0) ||
+ (RtlCompareUnicodeString(
+ &(All->UserComment),
+ &(All2->UserComment),
+ FALSE) != 0) ||
+ (RtlCompareUnicodeString(
+ &(All->Parameters),
+ &(All2->Parameters),
+ FALSE) != 0) ||
+ ( All->CountryCode != All2->CountryCode ) ||
+ ( All->CodePage != All2->CodePage ) ||
+ ( All->LmPasswordPresent != All2->LmPasswordPresent ) ||
+ ( All->NtPasswordPresent == All2->NtPasswordPresent ) ||
+ ( All->LogonHours.UnitsPerWeek !=
+ All2->LogonHours.UnitsPerWeek )
+ ) {
+
+ NtStatus = STATUS_DATA_ERROR;
+ }
+
+ SamFreeMemory( All2 );
+ }
+ }
+
+ SamFreeMemory( All );
+ }
+
+ if (NtStatus == STATUS_SUCCESS) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+ //
+ // Now get rid of the user account if necessary
+ //
+
+ NtStatus = SamDeleteUser( UserHandle1 );
+ ASSERT(NT_SUCCESS(NtStatus));
+#endif //BROKEN
+
+
+ printf(" Set Primary Group (non member). . . . . . . . . . . . ");
+ //
+ // The following user might already exist (from earlier in the test)
+ //
+
+ RtlInitString( &AccountNameAnsi, USER_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ //InitializeObjectAttributes( &ObjectAttributes, &AccountName, 0, 0, NULL );
+
+
+ UserRid = 0;
+ UserHandle1 = NULL;
+ NtStatus = SamCreateUserInDomain(
+ DomainHandle,
+ &AccountName,
+ USER_ALL_ACCESS,
+ &UserHandle1,
+ &UserRid
+ );
+ RtlFreeUnicodeString( &AccountName );
+ DeleteUser = TRUE;
+ if (NtStatus == STATUS_USER_EXISTS) {
+ DeleteUser = FALSE;
+ RtlInitString( &AccountNameAnsi, USER_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountNames[0], &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ NtStatus = SamLookupNamesInDomain(
+ DomainHandle,
+ 1,
+ &AccountNames[0],
+ &LookedUpRids,
+ &LookedUpUses
+ );
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(LookedUpUses[0] == SidTypeUser);
+ RtlFreeUnicodeString( &AccountNames[0] );
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_ALL_ACCESS,
+ LookedUpRids[0],
+ &UserHandle1);
+ SamFreeMemory( LookedUpUses ); SamFreeMemory( LookedUpRids );
+ }
+
+ ASSERT(NT_SUCCESS(NtStatus));
+
+
+ //
+ // The user is not a member of DOMAIN_GROUP_RID_ADMINS.
+ // See if we can make this group the user's primary group
+ //
+
+ ASSERT(sizeof(GroupRid) == sizeof(USER_PRIMARY_GROUP_INFORMATION));
+ GroupRid = DOMAIN_GROUP_RID_ADMINS;
+ NtStatus = SamSetInformationUser(
+ UserHandle1,
+ UserPrimaryGroupInformation,
+ &GroupRid
+ );
+
+ if (NtStatus == STATUS_MEMBER_NOT_IN_GROUP) {
+
+ printf("Succeeded\n");
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+ //
+ // Now get rid of the user account if necessary
+ //
+
+ if (DeleteUser == TRUE) {
+ NtStatus = SamDeleteUser( UserHandle1 );
+ ASSERT(NT_SUCCESS(NtStatus));
+ } else {
+ NtStatus = SamCloseHandle( UserHandle1 );
+ ASSERT(NT_SUCCESS(NtStatus));
+ }
+
+
+
+ printf(" Set Primary Group (member). . . . . . . . . . . . . . ");
+
+ //
+ // Make a user (might already exist)
+ // Make a group
+ // Make the group the user's primary group
+ // Change the user so the group isn't the primary group
+ // remove the group
+ // delete the group
+ // If we created the user, delete it.
+
+ //
+ // The following user might already exist (from earlier in the test)
+ //
+
+ RtlInitString( &AccountNameAnsi, USER_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ //InitializeObjectAttributes( &ObjectAttributes, &AccountName, 0, 0, NULL );
+
+ UserRid = 0;
+ UserHandle1 = NULL;
+ NtStatus = SamCreateUserInDomain(
+ DomainHandle,
+ &AccountName,
+ USER_ALL_ACCESS,
+ &UserHandle1,
+ &UserRid
+ );
+ RtlFreeUnicodeString( &AccountName );
+ DeleteUser = TRUE;
+ if (NtStatus == STATUS_USER_EXISTS) {
+ DeleteUser = FALSE;
+ RtlInitString( &AccountNameAnsi, USER_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountNames[0], &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ NtStatus = SamLookupNamesInDomain(
+ DomainHandle,
+ 1,
+ &AccountNames[0],
+ &LookedUpRids,
+ &LookedUpUses
+ );
+ RtlFreeUnicodeString( &AccountNames[0] );
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(LookedUpUses[0] == SidTypeUser);
+ UserRid = LookedUpRids[0];
+ NtStatus = SamOpenUser(
+ DomainHandle,
+ USER_ALL_ACCESS,
+ UserRid,
+ &UserHandle1);
+ SamFreeMemory( LookedUpUses ); SamFreeMemory( LookedUpRids );
+ }
+
+ ASSERT(NT_SUCCESS(NtStatus));
+
+
+ //
+ // create the group
+ //
+
+ RtlInitString( &AccountNameAnsi, GROUP_NAME1 );
+ NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE );
+ TST_SUCCESS_ASSERT(NtStatus);
+
+ //InitializeObjectAttributes( &ObjectAttributes, &AccountName, 0, 0, NULL );
+
+ GroupRid = 0;
+ GroupHandle1 = NULL;
+ NtStatus = SamCreateGroupInDomain(
+ DomainHandle,
+ &AccountName,
+ GROUP_ALL_ACCESS,
+ &GroupHandle1,
+ &GroupRid
+ );
+ RtlFreeUnicodeString( &AccountName );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ //
+ // Make the user a member of this group
+ //
+
+ NtStatus = SamAddMemberToGroup(
+ GroupHandle1,
+ UserRid,
+ SE_GROUP_MANDATORY |
+ SE_GROUP_ENABLED_BY_DEFAULT |
+ SE_GROUP_ENABLED
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+
+ //
+ // Set the user's primary group Id to be this group
+ //
+
+ NtStatus = SamSetInformationUser(
+ UserHandle1,
+ UserPrimaryGroupInformation,
+ &GroupRid
+ );
+ if (NT_SUCCESS(NtStatus)) {
+
+ Buffer1 = NULL;
+ NtStatus = SamQueryInformationUser(
+ UserHandle1,
+ UserPrimaryGroupInformation,
+ &Buffer1
+ );
+ TST_SUCCESS_ASSERT(NtStatus);
+ ASSERT(Buffer1 != NULL);
+
+ if ( ((USER_PRIMARY_GROUP_INFORMATION *)Buffer1)->PrimaryGroupId ==
+ GroupRid ) {
+
+ printf("Succeeded\n");
+
+ SamFreeMemory( Buffer1 );
+ } else {
+
+ printf("Failed\n");
+ printf(" Returned Value Doesn't Match Set Value.\n");
+ printf(" Value written is: 0x%lx\n", GroupRid);
+ printf(" Value retrieved is: 0x%lx\n",
+ ((USER_PRIMARY_GROUP_INFORMATION *)Buffer1)->PrimaryGroupId);
+ TestStatus = FALSE;
+
+ }
+
+ } else {
+ printf("Failed\n");
+ printf(" Completion status is 0x%lx\n", NtStatus);
+ TestStatus = FALSE;
+ }
+
+
+ //
+ // Set the user's primary group Id back and remove the user
+ // from the group
+ //
+
+ GroupRid = DOMAIN_GROUP_RID_USERS;
+ NtStatus = SamSetInformationUser(
+ UserHandle1,
+ UserPrimaryGroupInformation,
+ &GroupRid
+ );
+ ASSERT(NT_SUCCESS(NtStatus));
+ NtStatus = SamRemoveMemberFromGroup(GroupHandle1, UserRid);
+ ASSERT(NT_SUCCESS(NtStatus));
+
+
+
+ //
+ // Now get rid of the group and possibly the user account
+ //
+
+
+ NtStatus = SamDeleteGroup( GroupHandle1 );
+ ASSERT(NT_SUCCESS(NtStatus));
+
+ if (DeleteUser == TRUE) {
+ NtStatus = SamDeleteUser( UserHandle1 );
+ ASSERT(NT_SUCCESS(NtStatus));
+ } else {
+ NtStatus = SamCloseHandle( UserHandle1 );
+ ASSERT(NT_SUCCESS(NtStatus));
+ }
+
+
+
+
+
+
+
+
+
+ printf(" Set Name Information . . . . . . . . . . . . . . . . ");
+ printf("(Untested)\n");
+
+
+ }
+
+ return(TestStatus);
+
+}
+#endif // NOT_PART_OF_PROGRAM
diff --git a/private/ntos/se/ttseacc.c b/private/ntos/se/ttseacc.c
new file mode 100644
index 000000000..dc36514dd
--- /dev/null
+++ b/private/ntos/se/ttseacc.c
@@ -0,0 +1,64 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ ttseacc.c
+
+Abstract:
+
+ Security component kernel-mode test.
+
+ Test Object Security manipulation and accessibility from kernel mode.
+
+Author:
+
+ Jim Kelly (JimK) 13-Apr-1990
+
+Revision History:
+
+--*/
+
+
+
+#define _TST_KERNEL_ //Kernel mode test
+
+#include <stdio.h>
+
+#include "sep.h"
+
+#include <zwapi.h>
+
+#include "tsevars.c" // Common test variables
+
+#include "ctseacc.c" // Common accessibility test routines
+
+
+
+BOOLEAN
+Test()
+{
+ BOOLEAN Result = TRUE;
+
+ DbgPrint("Se: Start Kernel Mode Security Test...\n");
+
+ Result = TSeAcc();
+
+ DbgPrint("Se: End Kernel Mode Security Test.\n");
+
+ return Result;
+}
+
+int
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ VOID KiSystemStartup();
+
+ TestFunction = Test;
+ KiSystemStartup();
+ return( 0 );
+}
diff --git a/private/ntos/se/ttsertl.c b/private/ntos/se/ttsertl.c
new file mode 100644
index 000000000..5f32fa995
--- /dev/null
+++ b/private/ntos/se/ttsertl.c
@@ -0,0 +1,75 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ ttsertl.c
+
+Abstract:
+
+ Kernel mode test of security rtl routines.
+
+
+
+Author:
+
+ Jim Kelly (JimK) 23-Mar-1990
+
+Environment:
+
+ Test of security.
+
+Revision History:
+
+--*/
+
+#ifndef _SERTL_DEBUG_
+#define _SERTL_DEBUG_
+#endif
+
+#define _TST_KERNEL_ //Kernel mode test
+
+#include <stdio.h>
+
+#include "sep.h"
+
+#include <zwapi.h>
+
+#include "tsevars.c" // Common test variables
+
+#include "ctsertl.c" // Common RTL test routines
+
+
+BOOLEAN SeRtlTest();
+
+int
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ VOID KiSystemStartup();
+
+ TestFunction = SeRtlTest;
+ KiSystemStartup();
+ return( 0 );
+}
+
+
+BOOLEAN
+SeRtlTest()
+{
+
+ BOOLEAN Result;
+
+ DbgPrint("Se: Start Kernel Mode RTL Test...\n");
+
+ Result = TestSeRtl();
+
+ if (!Result) {
+ DbgPrint("Se: ** Kernel Mode RTL Test Failed **\n");
+ }
+ DbgPrint("Se: End Kernel Mode RTL Test.\n");
+ return Result;
+}
diff --git a/private/ntos/se/uipers.c b/private/ntos/se/uipers.c
new file mode 100644
index 000000000..18942123c
--- /dev/null
+++ b/private/ntos/se/uipers.c
@@ -0,0 +1,580 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ uipers.c
+
+Abstract:
+
+ Temporary security context display command.
+
+
+Author:
+
+ Jim Kelly (JimK) 23-May-1991
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <stdio.h>
+#include <string.h>
+
+#define _TST_USER_ // User mode test
+
+
+#include "tsevars.c" // Common test variables
+#include "tsecomm.c" // Mode dependent macros and routines.
+
+
+ GUID SystemAuthenticationId = SYSTEM_GUID;
+
+
+VOID
+DisplaySecurityContext(
+ IN HANDLE TokenHandle
+ );
+
+
+VOID
+DisplayAccountSid(
+ PISID Sid
+ );
+
+
+BOOLEAN
+SidTranslation(
+ PSID Sid,
+ PSTRING AccountName
+ );
+
+
+
+
+////////////////////////////////////////////////////////////////
+// //
+// Private Macros //
+// //
+////////////////////////////////////////////////////////////////
+
+
+#define PrintGuid(G) \
+ printf( "(0x%lx-%hx-%hx-%hx-%hx-%hx-%hx-%hx-%hx-%hx-%hx)\n", \
+ (G)->Data1, (G)->Data2, (G)->Data3, \
+ (G)->Data4[0], (G)->Data4[1], (G)->Data4[2], \
+ (G)->Data4[3], (G)->Data4[4], (G)->Data4[5], \
+ (G)->Data4[6], (G)->Data4[7]); \
+
+
+BOOLEAN
+SidTranslation(
+ PSID Sid,
+ PSTRING AccountName
+ )
+// AccountName is expected to have a large maximum length
+
+{
+ if (RtlEqualSid(Sid, WorldSid)) {
+ RtlInitString( AccountName, "WORLD");
+ return(TRUE);
+ }
+
+ if (RtlEqualSid(Sid, LocalSid)) {
+ RtlInitString( AccountName, "LOCAL");
+
+ return(TRUE);
+ }
+
+ if (RtlEqualSid(Sid, NetworkSid)) {
+ RtlInitString( AccountName, "NETWORK");
+
+ return(TRUE);
+ }
+
+ if (RtlEqualSid(Sid, BatchSid)) {
+ RtlInitString( AccountName, "BATCH");
+
+ return(TRUE);
+ }
+
+ if (RtlEqualSid(Sid, InteractiveSid)) {
+ RtlInitString( AccountName, "INTERACTIVE");
+ return(TRUE);
+ }
+
+ if (RtlEqualSid(Sid, LocalSystemSid)) {
+ RtlInitString( AccountName, "SYSTEM");
+ return(TRUE);
+ }
+
+ if (RtlEqualSid(Sid, LocalManagerSid)) {
+ RtlInitString( AccountName, "LOCAL MANAGER");
+ return(TRUE);
+ }
+
+ if (RtlEqualSid(Sid, LocalAdminSid)) {
+ RtlInitString( AccountName, "LOCAL ADMIN");
+ return(TRUE);
+ }
+
+ return(FALSE);
+
+}
+
+
+VOID
+DisplayAccountSid(
+ PISID Sid
+ )
+{
+ UCHAR Buffer[128];
+ STRING AccountName;
+ UCHAR i;
+ ULONG Tmp;
+
+ Buffer[0] = 0;
+
+ AccountName.MaximumLength = 127;
+ AccountName.Length = 0;
+ AccountName.Buffer = (PVOID)&Buffer[0];
+
+
+
+ if (SidTranslation( (PSID)Sid, &AccountName) ) {
+
+ printf("%s\n", AccountName.Buffer );
+
+ } else {
+ printf("S-%lu-", (USHORT)Sid->Revision );
+ if ( (Sid->IdentifierAuthority.Value[0] != 0) ||
+ (Sid->IdentifierAuthority.Value[1] != 0) ){
+ printf("0x%02hx%02hx%02hx%02hx%02hx%02hx",
+ (USHORT)Sid->IdentifierAuthority.Value[0],
+ (USHORT)Sid->IdentifierAuthority.Value[1],
+ (USHORT)Sid->IdentifierAuthority.Value[2],
+ (USHORT)Sid->IdentifierAuthority.Value[3],
+ (USHORT)Sid->IdentifierAuthority.Value[4],
+ (USHORT)Sid->IdentifierAuthority.Value[5] );
+ } else {
+ Tmp = (ULONG)Sid->IdentifierAuthority.Value[5] +
+ (ULONG)(Sid->IdentifierAuthority.Value[4] << 8) +
+ (ULONG)(Sid->IdentifierAuthority.Value[3] << 16) +
+ (ULONG)(Sid->IdentifierAuthority.Value[2] << 24);
+ printf("%lu", Tmp);
+ }
+
+
+ for (i=0;i<Sid->SubAuthorityCount ;i++ ) {
+ printf("-%lu", Sid->SubAuthority[i]);
+ }
+ printf("\n");
+
+ }
+
+}
+
+
+
+BOOLEAN
+DisplayPrivilegeName(
+ PLUID Privilege
+ )
+{
+
+ //
+ // This should be rewritten to use RtlLookupPrivilegeName.
+ //
+ // First we should probably spec and write RtlLookupPrivilegeName.
+ //
+
+ if ( ((*Privilege)QuadPart == CreateTokenPrivilege.QuadPart)) {
+ printf("SeCreateTokenPrivilege ");
+ return(TRUE);
+ }
+
+ if ( ((*Privilege).QuadPart == AssignPrimaryTokenPrivilege.QuadPart)) {
+ printf("SeAssignPrimaryTokenPrivilege ");
+ return(TRUE);
+ }
+
+ if ( ((*Privilege).QuadPart == LockMemoryPrivilege.QuadPart)) {
+ printf("SeLockMemoryPrivilege ");
+ return(TRUE);
+ }
+
+ if ( ((*Privilege).QuadPart == IncreaseQuotaPrivilege.QuadPart)) {
+ printf("SeIncreaseQuotaPrivilege ");
+ return(TRUE);
+ }
+
+ if ( ((*Privilege).QuadPart == UnsolicitedInputPrivilege.QuadPart)) {
+ printf("SeUnsolicitedInputPrivilege ");
+ return(TRUE);
+ }
+
+ if ( ((*Privilege).QuadPart == TcbPrivilege.QuadPart)) {
+ printf("SeTcbPrivilege ");
+ return(TRUE);
+ }
+
+ if ( ((*Privilege).QuadPart == SecurityPrivilege.QuadPart)) {
+ printf("SeSecurityPrivilege (Security Operator) ");
+ return(TRUE);
+ }
+
+
+ if ( ((*Privilege).QuadPart == TakeOwnershipPrivilege.QuadPart)) {
+ printf("SeTakeOwnershipPrivilege ");
+ return(TRUE);
+ }
+
+ if ( ((*Privilege).QuadPart == LpcReplyBoostPrivilege.QuadPart)) {
+ printf("SeLpcReplyBoostPrivilege ");
+ return(TRUE);
+ }
+
+ if ( ((*Privilege).QuadPart == CreatePagefilePrivilege.QuadPart)) {
+ printf("SeCreatePagefilePrivilege ");
+ return(TRUE);
+ }
+
+ if ( ((*Privilege).QuadPart == IncreaseBasePriorityPrivilege.QuadPart)) {
+ printf("SeIncreaseBasePriorityPrivilege ");
+ return(TRUE);
+ }
+
+ if ( ((*Privilege).QuadPart == SystemProfilePrivilege.QuadPart)) {
+ printf("SeSystemProfilePrivilege ");
+ return(TRUE);
+ }
+
+ if ( ((*Privilege).QuadPart == SystemtimePrivilege.QuadPart)) {
+ printf("SeSystemtimePrivilege ");
+ return(TRUE);
+ }
+
+ if ( ((*Privilege).QuadPart == ProfileSingleProcessPrivilege.QuadPart)) {
+ printf("SeProfileSingleProcessPrivilege ");
+ return(TRUE);
+ }
+
+ if ( ((*Privilege).QuadPart == CreatePermanentPrivilege.QuadPart)) {
+ printf("SeCreatePermanentPrivilege ");
+ return(TRUE);
+ }
+
+ if ( ((*Privilege).QuadPart == BackupPrivilege.QuadPart)) {
+ printf("SeBackupPrivilege ");
+ return(TRUE);
+ }
+
+ if ( ((*Privilege).QuadPart == RestorePrivilege.QuadPart)) {
+ printf("SeRestorePrivilege ");
+ return(TRUE);
+ }
+
+ if ( ((*Privilege).QuadPart == ShutdownPrivilege.QuadPart)) {
+ printf("SeShutdownPrivilege ");
+ return(TRUE);
+ }
+
+ if ( ((*Privilege).QuadPart == DebugPrivilege.QuadPart)) {
+ printf("SeDebugPrivilege ");
+ return(TRUE);
+ }
+
+ if ( ((*Privilege).QuadPart == SystemEnvironmentPrivilege.QuadPart)) {
+ printf("SeSystemEnvironmentPrivilege ");
+ return(TRUE);
+ }
+
+ return(FALSE);
+
+}
+
+
+
+VOID
+DisplayPrivilege(
+ PLUID_AND_ATTRIBUTES Privilege
+ )
+{
+
+
+ if (!DisplayPrivilegeName(&Privilege->Luid)) {
+ printf("(Unknown Privilege. Value is: (0x%lx,0x%lx))",
+ Privilege->Luid.HighPart,
+ Privilege->Luid.LowPart
+ );
+ }
+
+
+
+ //
+ // Display the attributes assigned to the privilege.
+ //
+
+ printf("\n [");
+ if (!(Privilege->Attributes & SE_PRIVILEGE_ENABLED)) {
+ printf("Not ");
+ }
+ printf("Enabled");
+
+ //printf(" / ");
+ //if (!(Privilege->Attributes & SE_PRIVILEGE_ENABLED_BY_DEFAULT)) {
+ // printf("Not ");
+ //}
+ //printf("Enabled By Default");
+
+
+ printf("]\n");
+ printf(" ");
+
+
+ return;
+
+}
+
+
+VOID
+DisplaySecurityContext(
+ IN HANDLE TokenHandle
+ )
+{
+
+#define BUFFER_SIZE (2048)
+
+ NTSTATUS Status;
+ ULONG i;
+ ULONG ReturnLength;
+ TOKEN_STATISTICS ProcessTokenStatistics;
+ GUID AuthenticationId;
+ UCHAR Buffer[BUFFER_SIZE];
+
+
+ PTOKEN_USER UserId;
+ PTOKEN_OWNER DefaultOwner;
+ PTOKEN_PRIMARY_GROUP PrimaryGroup;
+ PTOKEN_GROUPS GroupIds;
+ PTOKEN_PRIVILEGES Privileges;
+
+
+
+
+ /////////////////////////////////////////////////////////////////////////
+ // //
+ // Logon ID //
+ // //
+ /////////////////////////////////////////////////////////////////////////
+
+ Status = NtQueryInformationToken(
+ TokenHandle, // Handle
+ TokenStatistics, // TokenInformationClass
+ &ProcessTokenStatistics, // TokenInformation
+ sizeof(TOKEN_STATISTICS), // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+ ASSERT(NT_SUCCESS(Status));
+ AuthenticationId = ProcessTokenStatistics.AuthenticationId;
+
+ printf(" Logon Session: ");
+ if (RtlEqualGuid(&AuthenticationId, &SystemAuthenticationId )) {
+ printf("(System Logon Session)\n");
+ } else {
+ PrintGuid( &AuthenticationId );
+ }
+
+
+
+
+ /////////////////////////////////////////////////////////////////////////
+ // //
+ // User Id //
+ // //
+ /////////////////////////////////////////////////////////////////////////
+
+ UserId = (PTOKEN_USER)&Buffer[0];
+ Status = NtQueryInformationToken(
+ TokenHandle, // Handle
+ TokenUser, // TokenInformationClass
+ UserId, // TokenInformation
+ BUFFER_SIZE, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+
+
+ ASSERT(NT_SUCCESS(Status));
+
+ printf(" User id: ");
+ DisplayAccountSid( (PISID)UserId->User.Sid );
+
+
+
+
+
+ /////////////////////////////////////////////////////////////////////////
+ // //
+ // Default Owner //
+ // //
+ /////////////////////////////////////////////////////////////////////////
+
+ DefaultOwner = (PTOKEN_OWNER)&Buffer[0];
+
+ Status = NtQueryInformationToken(
+ TokenHandle, // Handle
+ TokenOwner, // TokenInformationClass
+ DefaultOwner, // TokenInformation
+ BUFFER_SIZE, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+
+
+ ASSERT(NT_SUCCESS(Status));
+
+ printf(" Default Owner: ");
+ DisplayAccountSid( (PISID)DefaultOwner->Owner );
+
+
+
+
+
+
+ /////////////////////////////////////////////////////////////////////////
+ // //
+ // Primary Group //
+ // //
+ /////////////////////////////////////////////////////////////////////////
+
+ PrimaryGroup = (PTOKEN_PRIMARY_GROUP)&Buffer[0];
+
+ Status = NtQueryInformationToken(
+ TokenHandle, // Handle
+ TokenPrimaryGroup, // TokenInformationClass
+ PrimaryGroup, // TokenInformation
+ BUFFER_SIZE, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+
+
+ ASSERT(NT_SUCCESS(Status));
+
+ printf(" Primary Group: ");
+ DisplayAccountSid( (PISID)PrimaryGroup->PrimaryGroup );
+
+
+
+
+
+
+ /////////////////////////////////////////////////////////////////////////
+ // //
+ // Group Ids //
+ // //
+ /////////////////////////////////////////////////////////////////////////
+
+ printf("\n");
+ GroupIds = (PTOKEN_GROUPS)&Buffer[0];
+ Status = NtQueryInformationToken(
+ TokenHandle, // Handle
+ TokenGroups, // TokenInformationClass
+ GroupIds, // TokenInformation
+ BUFFER_SIZE, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+
+
+ ASSERT(NT_SUCCESS(Status));
+
+ //printf(" Number of groups: %ld\n", GroupIds->GroupCount);
+ printf(" Groups: ");
+
+ for (i=0; i < GroupIds->GroupCount; i++ ) {
+ //printf(" Group %ld: ", i);
+ DisplayAccountSid( (PISID)GroupIds->Groups[i].Sid );
+ printf(" ");
+ }
+
+
+
+
+
+ /////////////////////////////////////////////////////////////////////////
+ // //
+ // Privileges //
+ // //
+ /////////////////////////////////////////////////////////////////////////
+
+ printf("\n");
+ Privileges = (PTOKEN_PRIVILEGES)&Buffer[0];
+ Status = NtQueryInformationToken(
+ TokenHandle, // Handle
+ TokenPrivileges, // TokenInformationClass
+ Privileges, // TokenInformation
+ BUFFER_SIZE, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+
+
+ ASSERT(NT_SUCCESS(Status));
+
+ printf(" Privileges: ");
+ if (Privileges->PrivilegeCount > 0) {
+
+ for (i=0; i < Privileges->PrivilegeCount; i++ ) {
+ DisplayPrivilege( &(Privileges->Privileges[i]) );
+ }
+ } else {
+ printf("(none assigned)\n");
+ }
+
+
+
+ return;
+
+}
+
+
+BOOLEAN
+main()
+{
+
+ NTSTATUS Status;
+ HANDLE ProcessToken;
+
+
+ TSeVariableInitialization(); // Initialize global variables
+
+ printf("\n");
+
+
+ //
+ // Open our process token
+ //
+
+ Status = NtOpenProcessToken(
+ NtCurrentProcess(),
+ TOKEN_QUERY,
+ &ProcessToken
+ );
+ if (!NT_SUCCESS(Status)) {
+ printf("I'm terribly sorry, but you don't seem to have access to\n");
+ printf("open your own process's token.\n");
+ printf("\n");
+ return(FALSE);
+ }
+
+ printf("Your process level security context is:\n");
+ printf("\n");
+ DisplaySecurityContext( ProcessToken );
+
+
+ Status = NtClose( ProcessToken );
+
+ return(TRUE);
+}
+
diff --git a/private/ntos/se/uipriv.c b/private/ntos/se/uipriv.c
new file mode 100644
index 000000000..c62caff0b
--- /dev/null
+++ b/private/ntos/se/uipriv.c
@@ -0,0 +1,611 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ priv.c
+
+Abstract:
+
+ This module provides a command capability to enable and disable
+ privileges. This command is expected to be an internal cmd.exe
+ command, but is expected to be passed parameters as if it were
+ an external command.
+
+
+ THIS IS A TEMPORARY COMMAND. IF IT IS DESIRED TO MAKE THIS A
+ PERMANENT COMMAND, THEN THIS FILE NEEDS TO BE GONE THROUGH WITH
+ A FINE-TOOTH COMB TO ROBUSTLY HANDLE ALL ERROR SITUATIONS AND TO
+ PROVIDE APPROPRIATE ERROR MESSAGES.
+
+Author:
+
+ Jim Kelly 1-Apr-1991.
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+
+//#include <sys\types.h>
+//#include <sys\stat.h>
+//#include <malloc.h>
+//#include <stdlib.h>
+//#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+
+//#include <tools.h>
+
+//
+// command qualifier flag values
+//
+
+BOOLEAN SwitchEnable = FALSE;
+BOOLEAN SwitchDisable = FALSE;
+BOOLEAN SwitchReset = FALSE;
+BOOLEAN SwitchAll = FALSE;
+
+#ifndef SHIFT
+#define SHIFT(c,v) {c--; v++;}
+#endif //SHIFT
+
+
+
+
+//
+// Function definitions...
+//
+
+
+VOID
+Usage ( VOID );
+
+BOOLEAN
+OpenAppropriateToken(
+ OUT PHANDLE Token
+ );
+
+VOID
+EnableAllPrivileges( VOID );
+
+VOID
+ResetAllPrivileges( VOID );
+
+VOID
+DisableAllPrivileges( VOID );
+
+int
+PrivMain (
+ IN int c,
+ IN PCHAR v[]
+ );
+
+
+
+
+VOID
+Usage (
+ VOID
+ )
+/*++
+
+
+Routine Description:
+
+ This routine prints the "Usage:" message.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ printf( "\n");
+ printf( "\n");
+
+ printf( "Usage: priv [/EDRA] {PrivilegeName}\n");
+ printf( " /E - Enable Privilege(s)\n");
+ printf( " /D - Disable Privilege(s)\n");
+ printf( " /R - Reset to default setting(s)\n");
+ printf( " /A - Apply To All Privileges\n");
+ printf( "\n");
+
+ printf( " The qualifiers /E and /D are mutually exclusive and can not\n");
+ printf( " be used in the same command.\n");
+ printf( " If /A is specified, then the PrivilegeName is ignored.\n");
+ printf( "\n");
+ printf( "\n");
+ printf( "Examples:\n");
+ printf( "\n");
+ printf( " priv /ae\n");
+ printf( " (enables all held privileges.\n");
+ printf( "\n");
+ printf( " priv /ad\n");
+ printf( " disables all held privileges.\n");
+ printf( "\n");
+ printf( " priv /ar\n");
+ printf( " (returns all privileges to their default setting.\n");
+ printf( "\n");
+ printf( " priv /e SeSetTimePrivilege\n");
+ printf( " (enables the privileges called: SeSetTimePrivilege\n");
+ printf( "\n");
+ printf( "\n");
+
+ return;
+}
+
+
+BOOLEAN
+OpenAppropriateToken(
+ OUT PHANDLE Token
+ )
+/*++
+
+
+Routine Description:
+
+ This routine opens the appropriate TOKEN object. For an internal
+ command, this is the current process's token. If this command is
+ ever made external, then it will be the parent process's token.
+
+ If the token can't be openned, then a messages is printed indicating
+ a problem has been encountered.
+
+ The caller is expected to close this token when no longer needed.
+
+
+Arguments:
+
+ Token - Receives the handle value of the openned token.
+
+
+Return Value:
+
+ TRUE - Indicates the token was successfully openned.
+
+ FALSE - Indicates the token was NOT successfully openned.
+
+--*/
+
+{
+ NTSTATUS Status, IgnoreStatus;
+ OBJECT_ATTRIBUTES ProcessAttributes;
+ HANDLE Process;
+ PTEB CurrentTeb;
+
+ CurrentTeb = NtCurrentTeb();
+ InitializeObjectAttributes(&ProcessAttributes, NULL, 0, NULL, NULL);
+ Status = NtOpenProcess(
+ &Process, // TargetHandle
+ PROCESS_QUERY_INFORMATION, // DesiredAccess
+ &ProcessAttributes, // ObjectAttributes
+ &CurrentTeb->ClientId // ClientId
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ Status = NtOpenProcessToken(
+ Process,
+ TOKEN_ADJUST_PRIVILEGES |
+ TOKEN_QUERY,
+ Token
+ );
+
+ IgnoreStatus = NtClose( Process );
+
+ if ( NT_SUCCESS(Status) ) {
+
+ return TRUE;
+
+ }
+
+ }
+
+ printf( "\n");
+ printf( "\n");
+ printf( "You are not allowed to change your own privilege settings.\n");
+ printf( "Operation failed.\n");
+
+ return FALSE;
+
+}
+
+
+
+VOID
+EnableAllPrivileges(
+ VOID
+ )
+/*++
+
+
+Routine Description:
+
+ This routine enables all privileges in the token.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NTSTATUS Status;
+ HANDLE Token;
+ ULONG ReturnLength, Index;
+ PTOKEN_PRIVILEGES NewState;
+
+
+ if ( !OpenAppropriateToken(&Token) ) {
+ return;
+ }
+
+ //
+ // Get the size needed to query current privilege settings...
+ //
+
+ Status = NtQueryInformationToken(
+ Token, // TokenHandle
+ TokenPrivileges, // TokenInformationClass
+ NewState, // TokenInformation
+ 0, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+ ASSERT( Status == STATUS_BUFFER_TOO_SMALL );
+
+ NewState = RtlAllocateHeap( RtlProcessHeap(), 0, ReturnLength );
+ ASSERT( NewState != NULL );
+
+
+ Status = NtQueryInformationToken(
+ Token, // TokenHandle
+ TokenPrivileges, // TokenInformationClass
+ NewState, // TokenInformation
+ ReturnLength, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+ ASSERT( NT_SUCCESS(Status) || NT_INFORMATION(Status) );
+
+
+ //
+ // Set the state settings so that all privileges are enabled...
+ //
+
+ if (NewState->PrivilegeCount > 0) {
+ Index = NewState->PrivilegeCount;
+
+ while (Index < NewState->PrivilegeCount) {
+ NewState->Privileges[Index].Attributes = SE_PRIVILEGE_ENABLED;
+ Index += 1;
+ }
+ }
+
+
+ //
+ // Change the settings in the token...
+ //
+
+ Status = NtAdjustPrivilegesToken(
+ Token, // TokenHandle
+ FALSE, // DisableAllPrivileges
+ NewState, // NewState (OPTIONAL)
+ ReturnLength, // BufferLength
+ NULL, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+ ASSERT( NT_SUCCESS(Status) || NT_INFORMATION(Status) );
+
+
+
+ RtlFreeHeap( RtlProcessHeap(), 0, NewState );
+ Status = NtClose( Token );
+
+ return;
+
+}
+
+
+
+VOID
+ResetAllPrivileges(
+ VOID
+ )
+/*++
+
+
+Routine Description:
+
+ This routine resets all privileges in the token to their default state.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NTSTATUS Status;
+ HANDLE Token;
+ ULONG ReturnLength, Index;
+ PTOKEN_PRIVILEGES NewState;
+
+
+ if ( !OpenAppropriateToken(&Token) ) {
+ printf( "\n");
+ printf( "\n");
+ printf( "You are not allowed to change your own privilege settings.\n");
+ printf( "Operation failed.\n");
+ return;
+ }
+
+ //
+ // Get the size needed to query current privilege settings...
+ //
+
+ Status = NtQueryInformationToken(
+ Token, // TokenHandle
+ TokenPrivileges, // TokenInformationClass
+ NewState, // TokenInformation
+ 0, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+ ASSERT( STATUS_BUFFER_TOO_SMALL );
+
+ NewState = RtlAllocateHeap( RtlProcessHeap(), 0, ReturnLength );
+ ASSERT( NewState != NULL );
+
+
+ Status = NtQueryInformationToken(
+ Token, // TokenHandle
+ TokenPrivileges, // TokenInformationClass
+ NewState, // TokenInformation
+ ReturnLength, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+ ASSERT( NT_SUCCESS(Status) || NT_INFORMATION(Status) );
+
+
+ //
+ // Set the state settings so that all privileges are reset to
+ // their default settings...
+ //
+
+ if (NewState->PrivilegeCount > 0) {
+ Index = NewState->PrivilegeCount;
+
+ while (Index < NewState->PrivilegeCount) {
+ if (NewState->Privileges[Index].Attributes ==
+ SE_PRIVILEGE_ENABLED_BY_DEFAULT) {
+ NewState->Privileges[Index].Attributes = SE_PRIVILEGE_ENABLED;
+ }
+ else {
+ NewState->Privileges[Index].Attributes = 0;
+ }
+
+ Index += 1;
+ }
+ }
+
+
+ //
+ // Change the settings in the token...
+ //
+
+ Status = NtAdjustPrivilegesToken(
+ Token, // TokenHandle
+ FALSE, // DisableAllPrivileges
+ NewState, // NewState (OPTIONAL)
+ ReturnLength, // BufferLength
+ NULL, // PreviousState (OPTIONAL)
+ &ReturnLength // ReturnLength
+ );
+ ASSERT( NT_SUCCESS(Status) || NT_INFORMATION(Status) );
+
+
+
+ RtlFreeHeap( RtlProcessHeap(), 0, NewState );
+ Status = NtClose( Token );
+
+ return;
+
+}
+
+
+
+
+VOID
+DisableAllPrivileges(
+ VOID
+ )
+/*++
+
+
+Routine Description:
+
+ This routine disables all privileges in the token.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ ULONG IgnoredReturnLength;
+ HANDLE Token;
+ NTSTATUS Status;
+
+ if ( !OpenAppropriateToken(&Token) ) {
+ printf( "\n");
+ printf( "\n");
+ printf( "You are not allowed to change your own privilege settings.\n");
+ printf( "Operation failed.\n");
+ return;
+ }
+
+ //
+ // Disable all the privileges.
+ //
+
+
+ Status = NtAdjustPrivilegesToken(
+ Token, // TokenHandle
+ TRUE, // DisableAllPrivileges
+ NULL, // NewState (OPTIONAL)
+ 0, // BufferLength
+ NULL, // PreviousState (OPTIONAL)
+ &IgnoredReturnLength // ReturnLength
+ );
+ ASSERT( NT_SUCCESS(Status) || NT_INFORMATION(Status) );
+
+ Status = NtClose( Token );
+ return;
+
+}
+
+
+int
+PrivMain (
+ IN int c,
+ IN PCHAR v[]
+ )
+/*++
+
+
+Routine Description:
+
+ This routine is the main entry routine for the "priv" command.
+
+Arguments:
+
+ TBS
+
+Return Value:
+
+ TBS
+
+--*/
+{
+ PCHAR p;
+ CHAR ch;
+ ULONG DispositionDirectives;
+
+
+ try {
+ DispositionDirectives = 0;
+ SHIFT (c,v);
+ while ((c > 0) && ((ch = *v[0]))) {
+ p = *v;
+ if (ch == '/') {
+ while (*++p != '\0') {
+ if (*p == 'E') {
+ SwitchEnable = TRUE;
+ DispositionDirectives += 1;
+ }
+ if (*p == 'D') {
+ SwitchDisable = TRUE;
+ DispositionDirectives += 1;
+ }
+ if (*p == 'R') {
+ SwitchReset = TRUE;
+ DispositionDirectives += 1;
+ }
+ else if (*p == 'A') {
+ SwitchAll = TRUE;
+ }
+ else {
+ Usage();
+ }
+ }
+ SHIFT(c,v);
+ }
+ }
+
+ //
+ // Make sure we don't have conflicting parameters
+ //
+ // Rules:
+ //
+ // If /A isn't specified, then a privilege name must be.
+ // Exactly one of /E, /D, and /R must be specified.
+ //
+ //
+
+
+ if (!SwitchAll && (c == 0)) {
+ printf( "\n");
+ printf( "\n");
+ printf( "You must provide privilege name or use the /A switch.\n");
+ Usage();
+ return ( 0 );
+ }
+
+ if (DispositionDirectives != 1) {
+ printf( "\n");
+ printf( "\n");
+ printf( "You must provide one and only one of the following");
+ printf( "switches: /E, /D, /R\n");
+ Usage();
+ return ( 0 );
+
+ }
+
+
+ //
+ // Everything appears legitimate
+ //
+
+ if (SwitchAll) {
+
+ //
+ // /A switch specified
+ //
+
+ if (SwitchEnable) {
+ EnableAllPrivileges();
+ }
+ else if (SwitchDisable) {
+ DisableAllPrivileges();
+ }
+ else {
+ ResetAllPrivileges();
+ }
+ }
+
+ //
+ // privilege name specified...
+ //
+
+ else {
+ printf( "\n");
+ printf( "I'm sorry, but due to the lack of time and interest,\n");
+ printf( "individual privilege selection is not yet supported.\n");
+ printf( "Please use the /A qualifier for the time being.\n");
+ printf( "\n");
+ }
+
+ } finally {
+ return ( 0 );
+ }
+
+ return( 0 );
+
+}
diff --git a/private/ntos/se/up/makefile b/private/ntos/se/up/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/ntos/se/up/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/ntos/se/up/sources b/private/ntos/se/up/sources
new file mode 100644
index 000000000..6dca9c583
--- /dev/null
+++ b/private/ntos/se/up/sources
@@ -0,0 +1,27 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+TARGETPATH=..\..\obj
+
+!include ..\sources.inc
diff --git a/private/ntos/se/utaccess.c b/private/ntos/se/utaccess.c
new file mode 100644
index 000000000..444f7f7e4
--- /dev/null
+++ b/private/ntos/se/utaccess.c
@@ -0,0 +1,52 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ utaccess.c
+
+Abstract:
+
+ Security component user-mode test.
+
+Author:
+
+ Robert Reichel (RobertRe) 14-Dec-90
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#define _TST_USER_ // User mode test
+
+
+#include "tsevars.c" // Common test variables
+
+#include "ctaccess.c" // Common accessibility test routines
+
+
+
+BOOLEAN
+Test()
+{
+ BOOLEAN Result = TRUE;
+
+ DbgPrint("Se: Start User Mode Access Test...\n");
+
+ Result = CTAccess();
+
+ DbgPrint("Se: End User Mode Access Test.\n");
+
+ return Result;
+}
+
+BOOLEAN
+main()
+{
+ return Test();
+}
diff --git a/private/ntos/se/utlnpqos.c b/private/ntos/se/utlnpqos.c
new file mode 100644
index 000000000..de139f335
--- /dev/null
+++ b/private/ntos/se/utlnpqos.c
@@ -0,0 +1,76 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ utlnpqos.c
+
+Abstract:
+
+ Security component user-mode test.
+
+ Security quality of service test for Local Named Pipes from user mode.
+
+ This test must be run from the SM> prompt in the debugger.
+
+Author:
+
+ Jim Kelly (JimK) 27-June-1990
+
+Revision History:
+
+--*/
+
+#include <stdio.h>
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <string.h>
+
+#define _TST_USER_ // User mode test
+
+typedef ULONG NAMED_PIPE_TYPE;
+typedef NAMED_PIPE_TYPE *PNAMED_PIPE_TYPE;
+
+
+typedef ULONG READ_MODE;
+typedef READ_MODE *PREAD_MODE;
+
+typedef ULONG COMPLETION_MODE;
+typedef COMPLETION_MODE *PCOMPLETION_MODE;
+
+typedef ULONG NAMED_PIPE_CONFIGURATION;
+typedef NAMED_PIPE_CONFIGURATION *PNAMED_PIPE_CONFIGURATION;
+
+typedef ULONG NAMED_PIPE_STATE;
+typedef NAMED_PIPE_STATE *PNAMED_PIPE_STATE;
+
+typedef ULONG NAMED_PIPE_END;
+typedef NAMED_PIPE_END *PNAMED_PIPE_END;
+
+
+#include "tsecomm.c" // Common routines
+#include "ctlnpqos.c" // quality of service tests
+
+
+
+
+
+BOOLEAN
+Test()
+{
+ BOOLEAN Result = TRUE;
+
+
+ Result = CtLnpQos();
+
+
+ return Result;
+}
+
+BOOLEAN
+main()
+{
+ return Test();
+}
diff --git a/private/ntos/se/utlpcqos.c b/private/ntos/se/utlpcqos.c
new file mode 100644
index 000000000..cc388abeb
--- /dev/null
+++ b/private/ntos/se/utlpcqos.c
@@ -0,0 +1,52 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ utlpcqos.c
+
+Abstract:
+
+ Security component user-mode test.
+
+ Security quality of service test for LPC from user mode.
+
+ This test must be run from the SM> prompt in the debugger.
+
+Author:
+
+ Jim Kelly (JimK) 27-June-1990
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#define _TST_USER_ // User mode test
+
+#include "tsecomm.c" // Common routines
+#include "ctlpcqos.c" // quality of service tests
+
+
+
+BOOLEAN
+Test()
+{
+ BOOLEAN Result = TRUE;
+
+
+ Result = CtLpcQos();
+
+
+ return Result;
+}
+
+BOOLEAN
+main()
+{
+ return Test();
+}
diff --git a/private/ntos/se/utseacc.c b/private/ntos/se/utseacc.c
new file mode 100644
index 000000000..b38604044
--- /dev/null
+++ b/private/ntos/se/utseacc.c
@@ -0,0 +1,56 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ utseacc.c
+
+Abstract:
+
+ Security component user-mode test.
+
+ Test Object Security manipulation and accessibility from user mode.
+
+Author:
+
+ Jim Kelly (JimK) 13-Apr-1990
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#define _TST_USER_ // User mode test
+
+
+#include "tsevars.c" // Common test variables
+
+#include "ctseacc.c" // Common accessibility test routines
+
+
+
+BOOLEAN
+Test()
+{
+ BOOLEAN Result = TRUE;
+
+ DbgPrint("Se: Start User Mode Security Test...\n");
+
+ Result = TSeAcc();
+
+ DbgPrint("Se: End User Mode Security Test.\n");
+
+ return Result;
+}
+
+NTSTATUS
+main()
+{
+ Test();
+
+ return STATUS_SUCCESS;
+}
diff --git a/private/ntos/se/utseqos.c b/private/ntos/se/utseqos.c
new file mode 100644
index 000000000..aa9ba8853
--- /dev/null
+++ b/private/ntos/se/utseqos.c
@@ -0,0 +1,50 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ utseqos.c
+
+Abstract:
+
+ Security component user-mode test.
+
+ Security quality of service test from user mode.
+
+Author:
+
+ Jim Kelly (JimK) 27-June-1990
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#define _TST_USER_ // User mode test
+
+#include "tsecomm.c" // Common routines
+#include "ctseqos.c" // quality of service tests
+
+
+
+BOOLEAN
+Test()
+{
+ BOOLEAN Result = TRUE;
+
+
+ Result = CtSeQos();
+
+
+ return Result;
+}
+
+BOOLEAN
+main()
+{
+ return Test();
+}
diff --git a/private/ntos/se/utsertl.c b/private/ntos/se/utsertl.c
new file mode 100644
index 000000000..e0e86c338
--- /dev/null
+++ b/private/ntos/se/utsertl.c
@@ -0,0 +1,56 @@
+
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ utsertl.c
+
+Abstract:
+
+ Security component user-mode test.
+ Test security RTL routines from user mode.
+
+Author:
+
+ Jim Kelly (JimK) 13-Apr-1990
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#define _TST_USER_ // User mode test
+
+#include "tsevars.c" // Common test variables
+
+#include "ctsertl.c" // Common RTL test routines
+
+
+BOOLEAN
+turtl()
+{
+ BOOLEAN Result;
+
+ DbgPrint("Se: Start User Mode RTL Test...\n");
+
+ Result = TestSeRtl();
+
+ if (!Result) {
+ DbgPrint("Se: ** User Mode RTL Test Failed **\n");
+ }
+ DbgPrint("Se: End User Mode RTL Test.\n");
+ return Result;
+}
+
+NTSTATUS
+main()
+{
+ turtl();
+
+ return STATUS_SUCCESS;
+}
diff --git a/private/ntos/se/uttoken.c b/private/ntos/se/uttoken.c
new file mode 100644
index 000000000..636bf2fb9
--- /dev/null
+++ b/private/ntos/se/uttoken.c
@@ -0,0 +1,54 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ uttoken.c
+
+Abstract:
+
+ Security component user-mode test.
+
+ Token Object test from user mode.
+
+Author:
+
+ Jim Kelly (JimK) 27-June-1990
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#define _TST_USER_ // User mode test
+
+
+#include "tsevars.c" // Common test variables
+
+#include "cttoken.c" // Common accessibility test routines
+
+
+
+BOOLEAN
+Test()
+{
+ BOOLEAN Result = TRUE;
+
+ DbgPrint("Se: Start User Mode Token Object Test...\n");
+
+ Result = CTToken();
+
+ DbgPrint("Se: End User Mode Token Object Test.\n");
+
+ return Result;
+}
+
+BOOLEAN
+main()
+{
+ return Test();
+}
diff --git a/private/ntos/seaudit/dirs b/private/ntos/seaudit/dirs
new file mode 100644
index 000000000..622df6821
--- /dev/null
+++ b/private/ntos/seaudit/dirs
@@ -0,0 +1,29 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+ Steve Wood (stevewo) 17-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+
+DIRS=msaudite \
+ msobjs
+
+
+
+OPTIONAL_DIRS=
diff --git a/private/ntos/seaudit/msaudite/audit.rc b/private/ntos/seaudit/msaudite/audit.rc
new file mode 100644
index 000000000..2d9993b96
--- /dev/null
+++ b/private/ntos/seaudit/msaudite/audit.rc
@@ -0,0 +1,14 @@
+#include <windows.h>
+
+1 11 MSG00001.bin
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Security Audit Events DLL"
+#define VER_INTERNALNAME_STR "msaudite.dll"
+#define VER_ORIGINALFILENAME_STR "msaudite.dll"
+
+#include "common.ver"
+
diff --git a/private/ntos/seaudit/msaudite/makefile b/private/ntos/seaudit/msaudite/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/ntos/seaudit/msaudite/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/ntos/seaudit/msaudite/makefile.inc b/private/ntos/seaudit/msaudite/makefile.inc
new file mode 100644
index 000000000..2fd6c3210
--- /dev/null
+++ b/private/ntos/seaudit/msaudite/makefile.inc
@@ -0,0 +1,4 @@
+$(NTTARGETFILE0): msaudite.rc msg00001.bin
+
+msaudite.rc msg00001.bin: msaudite.mc
+ mc -v -r . -h $(_NTROOT)\public\sdk\inc\ msaudite.mc
diff --git a/private/ntos/seaudit/msaudite/msaudite.def b/private/ntos/seaudit/msaudite/msaudite.def
new file mode 100644
index 000000000..1daa14c4d
--- /dev/null
+++ b/private/ntos/seaudit/msaudite/msaudite.def
@@ -0,0 +1,3 @@
+LIBRARY msaudit
+
+DESCRIPTION 'Message File for Auditing'
diff --git a/private/ntos/seaudit/msaudite/msaudite.mc b/private/ntos/seaudit/msaudite/msaudite.mc
new file mode 100644
index 000000000..70691841c
--- /dev/null
+++ b/private/ntos/seaudit/msaudite/msaudite.mc
@@ -0,0 +1,2580 @@
+;/*++ BUILD Version: 0001 // Increment this if a change has global effects
+;
+;Copyright (c) 1991 Microsoft Corporation
+;
+;Module Name:
+;
+; msaudite.mc
+;
+;Abstract:
+;
+; Constant definitions for the NT Audit Event Messages.
+;
+;Author:
+;
+; Jim Kelly (JimK) 30-Mar-1992
+;
+;Revision History:
+;
+;Notes:
+;
+; The .h and .res forms of this file are generated from the .mc
+; form of the file (private\ntos\seaudit\msaudite\msaudite.mc).
+; Please make all changes to the .mc form of the file.
+;
+;
+;
+;--*/
+;
+;#ifndef _MSAUDITE_
+;#define _MSAUDITE_
+;
+;/*lint -e767 */ // Don't complain about different definitions // winnt
+
+
+MessageIdTypedef=ULONG
+
+SeverityNames=(None=0x0)
+
+FacilityNames=(None=0x0)
+
+
+
+MessageId=0x0000
+ Language=English
+Unused message ID
+.
+;// Message ID 0 is unused - just used to flush out the diagram
+
+
+
+
+
+
+;///////////////////////////////////////////////////////////////////////////
+;///////////////////////////////////////////////////////////////////////////
+;///////////////////////////////////////////////////////////////////////////
+;///////////////////////////////////////////////////////////////////////////
+;///////////////////////////////////////////////////////////////////////////
+;///////////////////////////////////////////////////////////////////////////
+;///////////////////////////////////////////////////////////////////////////
+;///////////////////////////////////////////////////////////////////////////
+;///////////////////////////////////////////////////////////////////////////
+;///////////////////////////////////////////////////////////////////////////
+;///////////////////////////////////////////////////////////////////////////
+;///////////////////////////////////////////////////////////////////////////
+;///////////////////////////////////////////////////////////////////////////
+;///////////////////////////////////////////////////////////////////////////
+;///////////////////////////////////////////////////////////////////////////
+;///////////////////////////////////////////////////////////////////////////
+;///////////////////////////////////////////////////////////////////////////
+;///////////////////////////////////////////////////////////////////////////
+;///////////////////////////////////////////////////////////////////////////
+;///////////////////////////////////////////////////////////////////////////
+;///////////////////////////////////////////////////////////////////////////
+;///////////////////////////////////////////////////////////////////////////
+;///////////////////////////////////////////////////////////////////////////
+;///////////////////////////////////////////////////////////////////////////
+;///////////////////////////////////////////////////////////////////////////
+;///////////////////////////////////////////////////////////////////////////
+;///////////////////////////////////////////////////////////////////////////
+;///////////////////////////////////////////////////////////////////////////
+
+
+
+
+;
+;///////////////////////////////////////////////////////////////////////////
+;///////////////////////////////////////////////////////////////////////////
+;// //
+;// WARNING - WARNING - WARNING - WARNING - WARNING //
+;// //
+;// //
+;// Everything above this is currently in use in the running system. //
+;// //
+;// Everything below this is currently under development and is //
+;// slated to replace everything above. //
+;// //
+;///////////////////////////////////////////////////////////////////////////
+;///////////////////////////////////////////////////////////////////////////
+
+
+
+;///////////////////////////////////////////////////////////////////////////
+;///////////////////////////////////////////////////////////////////////////
+;// //
+;// //
+;// Audit Message ID Space: //
+;// //
+;// 0x0000 - 0x00FF : Reserved for future use. //
+;// //
+;// 0x0100 - 0x01FF : Categories //
+;// //
+;// 0x0200 - 0x05FF : Events //
+;// //
+;// 0x0600 - 0x063F : Standard access types and names for //
+;// specific accesses when no specific names //
+;// can be found. //
+;// //
+;// 0x0640 - 0x06FF : Well known privilege names (as we would //
+;// like them displayed in the event viewer). //
+;// //
+;// 0x0700 - 0x0FFE : Reserved for future use. //
+;// //
+;// 0X0FFF : SE_ADT_LAST_SYSTEM_MESSAGE (the highest //
+;// value audit message used by the system) //
+;// //
+;// //
+;// 0x1000 and above: For use by Parameter Message Files //
+;// //
+;///////////////////////////////////////////////////////////////////////////
+;///////////////////////////////////////////////////////////////////////////
+
+
+
+
+
+MessageId=0x0FFF
+ SymbolicName=SE_ADT_LAST_SYSTEM_MESSAGE
+ Language=English
+Highest System-Defined Audit Message Value.
+.
+
+
+
+
+
+
+
+
+;
+;/////////////////////////////////////////////////////////////////////////////
+;// //
+;// //
+;// CATEGORIES //
+;// //
+;// Categories take up the range 0x1 - 0x400 //
+;// //
+;// Category IDs: //
+;// //
+;// SE_CATEGID_SYSTEM //
+;// SE_CATEGID_LOGON //
+;// SE_CATEGID_OBJECT_ACCESS //
+;// SE_CATEGID_PRIVILEGE_USE //
+;// SE_CATEGID_DETAILED_TRACKING //
+;// SE_CATEGID_POLICY_CHANGE //
+;// SE_CATEGID_ACCOUNT_MANAGEMENT //
+;// //
+;// //
+;/////////////////////////////////////////////////////////////////////////////
+
+MessageId=0x0001
+ SymbolicName=SE_CATEGID_SYSTEM
+ Language=English
+System Event
+.
+
+MessageId=0x0002
+ SymbolicName=SE_CATEGID_LOGON
+ Language=English
+Logon/Logoff
+.
+
+MessageId=0x0003
+ SymbolicName=SE_CATEGID_OBJECT_ACCESS
+ Language=English
+Object Access
+.
+
+MessageId=0x0004
+ SymbolicName=SE_CATEGID_PRIVILEGE_USE
+ Language=English
+Privilege Use
+.
+
+MessageId=0x0005
+ SymbolicName=SE_CATEGID_DETAILED_TRACKING
+ Language=English
+Detailed Tracking
+.
+
+MessageId=0x0006
+ SymbolicName=SE_CATEGID_POLICY_CHANGE
+ Language=English
+Policy Change
+.
+
+MessageId=0x0007
+ SymbolicName=SE_CATEGID_ACCOUNT_MANAGEMENT
+ Language=English
+Account Management
+.
+
+
+
+
+
+
+
+
+
+
+
+
+;
+;/////////////////////////////////////////////////////////////////////////////
+;// //
+;// //
+;// Messages for Category: SE_CATEGID_SYSTEM //
+;// //
+;// Event IDs: //
+;// SE_AUDITID_SYSTEM_RESTART //
+;// SE_AUDITID_SYSTEM_SHUTDOWN //
+;// SE_AUDITID_AUTH_PACKAGE_LOAD //
+;// SE_AUDITID_LOGON_PROC_REGISTER //
+;// SE_AUDITID_AUDITS_DISCARDED //
+;// SE_AUDITID_NOTIFY_PACKAGE_LOAD //
+;// //
+;/////////////////////////////////////////////////////////////////////////////
+
+
+
+;//
+;//
+;// SE_AUDITID_SYSTEM_RESTART
+;//
+;// Category: SE_CATEGID_SYSTEM
+;//
+;// Parameter Strings - None
+;//
+;//
+;//
+
+MessageId=0x0200
+ SymbolicName=SE_AUDITID_SYSTEM_RESTART
+ Language=English
+Windows NT is starting up.
+.
+
+
+;//
+;//
+;// SE_AUDITID_SYSTEM_SHUTDOWN
+;//
+;// Category: SE_CATEGID_SYSTEM
+;//
+;// Parameter Strings - None
+;//
+;//
+;//
+
+MessageId=0x0201
+ SymbolicName=SE_AUDITID_SYSTEM_SHUTDOWN
+ Language=English
+Windows NT is shutting down.
+All logon sessions will be terminated by this shutdown.
+.
+
+
+;//
+;//
+;// SE_AUDITID_SYSTEM_AUTH_PACKAGE_LOAD
+;//
+;// Category: SE_CATEGID_SYSTEM
+;//
+;// Parameter Strings -
+;//
+;// 1 - Authentication Package Name
+;//
+;//
+;//
+
+MessageId=0x0202
+ SymbolicName=SE_AUDITID_AUTH_PACKAGE_LOAD
+ Language=English
+An authentication package has been loaded by the Local Security Authority.
+This authentication package will be used to authenticate logon attempts.
+%n
+Authentication Package Name:%t%1
+.
+
+
+;//
+;//
+;// SE_AUDITID_SYSTEM_LOGON_PROC_REGISTER
+;//
+;// Category: SE_CATEGID_SYSTEM
+;//
+;// Parameter Strings -
+;//
+;// 1 - Logon Process Name
+;//
+;//
+;//
+
+MessageId=0x0203
+ SymbolicName=SE_AUDITID_SYSTEM_LOGON_PROC_REGISTER
+ Language=English
+A trusted logon process has registered with the Local Security Authority.
+This logon process will be trusted to submit logon requests.
+%n
+%n
+Logon Process Name:%t%1
+.
+
+
+;//
+;//
+;// SE_AUDITID_AUDITS_DISCARDED
+;//
+;// Category: SE_CATEGID_SYSTEM
+;//
+;// Parameter Strings -
+;//
+;// 1 - Number of audits discarded
+;//
+;//
+;//
+
+MessageId=0x0204
+ SymbolicName=SE_AUDITID_AUDITS_DISCARDED
+ Language=English
+Internal resources allocated for the queuing of audit messages have been exhausted,
+leading to the loss of some audits.
+%n
+%tNumber of audit messages discarded:%t%1
+.
+
+
+;//
+;//
+;// SE_AUDITID_AUDIT_LOG_CLEARED
+;//
+;// Category: SE_CATEGID_SYSTEM
+;//
+;// Parameter Strings -
+;//
+;// 1 - Primary user account name
+;//
+;// 2 - Primary authenticating domain name
+;//
+;// 3 - Primary logon ID string
+;//
+;// 4 - Client user account name ("-" if no client)
+;//
+;// 5 - Client authenticating domain name ("-" if no client)
+;//
+;// 6 - Client logon ID string ("-" if no client)
+;//
+;//
+;//
+
+MessageId=0x0205
+ SymbolicName=SE_AUDITID_AUDIT_LOG_CLEARED
+ Language=English
+The audit log was cleared
+%n
+%tPrimary User Name:%t%1%n
+%tPrimary Domain:%t%2%n
+%tPrimary Logon ID:%t%3%n
+%tClient User Name:%t%4%n
+%tClient Domain:%t%5%n
+%tClient Logon ID:%t%6%n
+.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+;//
+;//
+;// SE_AUDITID_SYSTEM_NOTIFY_PACKAGE_LOAD
+;//
+;// Category: SE_CATEGID_SYSTEM
+;//
+;// Parameter Strings -
+;//
+;// 1 - Notification Package Name
+;//
+;//
+;//
+
+MessageId=0x0206
+ SymbolicName=SE_AUDITID_NOTIFY_PACKAGE_LOAD
+ Language=English
+An notification package has been loaded by the Security Account Manager.
+This package will be notified of any account or password changes.
+%n
+Notification Package Name:%t%1
+.
+
+
+;
+;/////////////////////////////////////////////////////////////////////////////
+;// //
+;// //
+;// Messages for Category: SE_CATEGID_LOGON //
+;// //
+;// Event IDs: //
+;// SE_AUDITID_SUCCESSFUL_LOGON //
+;// SE_AUDITID_UNKNOWN_USER_OR_PWD //
+;// SE_AUDITID_ACCOUNT_TIME_RESTR //
+;// SE_AUDITID_ACCOUNT_DISABLED //
+;// SE_AUDITID_ACCOUNT_EXPIRED //
+;// SE_AUDITID_WORKSTATION_RESTR //
+;// SE_AUDITID_LOGON_TYPE_RESTR //
+;// SE_AUDITID_PASSWORD_EXPIRED //
+;// SE_AUDITID_NO_AUTHOR_RESPONSE //
+;// SE_AUDITID_NETLOGON_NOT_STARTED //
+;// SE_AUDITID_UNSUCCESSFUL_LOGON //
+;// SE_AUDITID_LOGOFF //
+;// SE_AUDITID_ACCOUNT_LOCKED //
+;// //
+;// //
+;// //
+;/////////////////////////////////////////////////////////////////////////////
+
+;//
+;//
+;// SE_AUDITID_SUCCESSFUL_LOGON
+;//
+;// Category: SE_CATEGID_LOGON
+;//
+;// Parameter Strings -
+;//
+;// 1 - User account name
+;//
+;// 2 - Authenticating domain name
+;//
+;// 3 - Logon ID string
+;//
+;// 4 - Logon Type string
+;//
+;// 5 - Logon process name
+;//
+;// 6 - Authentication package name
+;//
+;//
+;//
+
+MessageId=0x0210
+ SymbolicName=SE_AUDITID_SUCCESSFUL_LOGON
+ Language=English
+Successful Logon:%n
+%tUser Name:%t%1%n
+%tDomain:%t%t%2%n
+%tLogon ID:%t%t%3%n
+%tLogon Type:%t%4%n
+%tLogon Process:%t%5%n
+%tAuthentication Package:%t%6%n
+%tWorkstation Name:%t%7
+.
+
+;//
+;//
+;// SE_AUDITID_UNKNOWN_USER_OR_PWD
+;//
+;// Category: SE_CATEGID_LOGON
+;//
+;// Parameter Strings -
+;//
+;// 1 - User account name
+;//
+;// 2 - Authenticating domain name
+;//
+;// 3 - Logon Type string
+;//
+;// 4 - Logon process name
+;//
+;// 5 - Authentication package name
+;//
+;//
+
+MessageId=0x0211
+ SymbolicName=SE_AUDITID_UNKNOWN_USER_OR_PWD
+ Language=English
+Logon Failure:%n
+%tReason:%t%tUnknown user name or bad password%n
+%tUser Name:%t%1%n
+%tDomain:%t%t%2%n
+%tLogon Type:%t%3%n
+%tLogon Process:%t%4%n
+%tAuthentication Package:%t%5%n
+%tWorkstation Name:%t%6
+.
+
+;//
+;//
+;// SE_AUDITID_ACCOUNT_TIME_RESTR
+;//
+;// Category: SE_CATEGID_LOGON
+;//
+;// Parameter Strings -
+;//
+;// 1 - User account name
+;//
+;// 2 - Authenticating domain name
+;//
+;// 3 - Logon Type string
+;//
+;// 4 - Logon process name
+;//
+;// 5 - Authentication package name
+;//
+;//
+
+MessageId=0x0212
+ SymbolicName=SE_AUDITID_ACCOUNT_TIME_RESTR
+ Language=English
+Logon Failure:%n
+%tReason:%t%tAccount logon time restriction violation%n
+%tUser Name:%t%1%n
+%tDomain:%t%2%n
+%tLogon Type:%t%3%n
+%tLogon Process:%t%4%n
+%tAuthentication Package:%t%5%n
+%tWorkstation Name:%t%6
+.
+
+
+;//
+;//
+;// SE_AUDITID_ACCOUNT_DISABLED
+;//
+;// Category: SE_CATEGID_LOGON
+;//
+;// Parameter Strings -
+;//
+;// 1 - User account name
+;//
+;// 2 - Authenticating domain name
+;//
+;// 3 - Logon Type string
+;//
+;// 4 - Logon process name
+;//
+;// 5 - Authentication package name
+;//
+;//
+
+MessageId=0x0213
+ SymbolicName=SE_AUDITID_ACCOUNT_DISABLED
+ Language=English
+Logon Failure:%n
+%tReason:%t%tAccount currently disabled%n
+%tUser Name:%t%1%n
+%tDomain:%t%t%2%n
+%tLogon Type:%t%3%n
+%tLogon Process:%t%4%n
+%tAuthentication Package:%t%5%n
+%tWorkstation Name:%t%6
+.
+
+
+;//
+;//
+;// SE_AUDITID_ACCOUNT_EXPIRED
+;//
+;// Category: SE_CATEGID_LOGON
+;//
+;// Parameter Strings -
+;//
+;// 1 - User account name
+;//
+;// 2 - Authenticating domain name
+;//
+;// 3 - Logon Type string
+;//
+;// 4 - Logon process name
+;//
+;// 5 - Authentication package name
+;//
+;//
+
+MessageId=0x0214
+ SymbolicName=SE_AUDITID_ACCOUNT_EXPIRED
+ Language=English
+Logon Failure:%n
+%tReason:%t%tThe specified user account has expired%n
+%tUser Name:%t%1%n
+%tDomain:%t%t%2%n
+%tLogon Type:%t%3%n
+%tLogon Process:%t%4%n
+%tAuthentication Package:%t%5%n
+%tWorkstation Name:%t%6
+.
+
+
+;//
+;//
+;// SE_AUDITID_WORKSTATION_RESTR
+;//
+;// Category: SE_CATEGID_LOGON
+;//
+;// Parameter Strings -
+;//
+;// 1 - User account name
+;//
+;// 2 - Authenticating domain name
+;//
+;// 3 - Logon Type string
+;//
+;// 4 - Logon process name
+;//
+;// 5 - Authentication package name
+;//
+;//
+
+MessageId=0x0215
+ SymbolicName=SE_AUDITID_WORKSTATION_RESTR
+ Language=English
+Logon Failure:%n
+%tReason:%t%tUser not allowed to logon at this computer%n
+%tUser Name:%t%1%n
+%tDomain:%t%2%n
+%tLogon Type:%t%3%n
+%tLogon Process:%t%4%n
+%tAuthentication Package:%t%5%n
+%tWorkstation Name:%t%6
+.
+
+
+;//
+;//
+;// SE_AUDITID_LOGON_TYPE_RESTR
+;//
+;// Category: SE_CATEGID_LOGON
+;//
+;// Parameter Strings -
+;//
+;// 1 - User account name
+;//
+;// 2 - Authenticating domain name
+;//
+;// 3 - Logon Type string
+;//
+;// 4 - Logon process name
+;//
+;// 5 - Authentication package name
+;//
+;//
+
+MessageId=0x0216
+ SymbolicName=SE_AUDITID_LOGON_TYPE_RESTR
+ Language=English
+Logon Failure:%n
+%tReason:%tThe user has not be granted the requested%n
+%t%tlogon type at this machine%n
+%tUser Name:%t%1%n
+%tDomain:%t%t%2%n
+%tLogon Type:%t%3%n
+%tLogon Process:%t%4%n
+%tAuthentication Package:%t%5%n
+%tWorkstation Name:%t%6
+.
+
+
+;//
+;//
+;// SE_AUDITID_PASSWORD_EXPIRED
+;//
+;// Category: SE_CATEGID_LOGON
+;//
+;// Parameter Strings -
+;//
+;// 1 - User account name
+;//
+;// 2 - Authenticating domain name
+;//
+;// 3 - Logon Type string
+;//
+;// 4 - Logon process name
+;//
+;// 5 - Authentication package name
+;//
+;//
+
+MessageId=0x0217
+ SymbolicName=SE_AUDITID_PASSWORD_EXPIRED
+ Language=English
+Logon Failure:%n
+%tReason:%t%tThe specified account's password has expired%n
+%tUser Name:%t%1%n
+%tDomain:%t%t%2%n
+%tLogon Type:%t%3%n
+%tLogon Process:%t%4%n
+%tAuthentication Package:%t%5%n
+%tWorkstation Name:%t%6
+.
+
+
+;//
+;//
+;// SE_AUDITID_NETLOGON_NOT_STARTED
+;//
+;// Category: SE_CATEGID_LOGON
+;//
+;// Parameter Strings -
+;//
+;// 1 - User account name
+;//
+;// 2 - Authenticating domain name
+;//
+;// 3 - Logon Type string
+;//
+;// 4 - Logon process name
+;//
+;// 5 - Authentication package name
+;//
+;//
+
+MessageId=0x0218
+ SymbolicName=SE_AUDITID_NETLOGON_NOT_STARTED
+ Language=English
+Logon Failure:%n
+%tReason:%t%tThe NetLogon component is not active%n
+%tUser Name:%t%1%n
+%tDomain:%t%t%2%n
+%tLogon Type:%t%3%n
+%tLogon Process:%t%4%n
+%tAuthentication Package:%t%5%n
+%tWorkstation Name:%t%6
+.
+
+
+;//
+;//
+;// SE_AUDITID_UNSUCCESSFUL_LOGON
+;//
+;// Category: SE_CATEGID_LOGON
+;//
+;// Parameter Strings -
+;//
+;// 1 - User account name
+;//
+;// 2 - Authenticating domain name
+;//
+;// 3 - Logon Type string
+;//
+;// 4 - Logon process name
+;//
+;// 5 - Authentication package name
+;//
+;//
+
+MessageId=0x0219
+ SymbolicName=SE_AUDITID_UNSUCCESSFUL_LOGON
+ Language=English
+Logon Failure:%n
+%tReason:%t%tAn unexpected error occured during logon%n
+%tUser Name:%t%1%n
+%tDomain:%t%t%2%n
+%tLogon Type:%t%3%n
+%tLogon Process:%t%4%n
+%tAuthentication Package:%t%5%n
+%tWorkstation Name:%t%6
+.
+
+
+;//
+;//
+;// SE_AUDITID_LOGOFF
+;//
+;// Category: SE_CATEGID_LOGON
+;//
+;// Parameter Strings -
+;//
+;// 1 - User account name
+;//
+;// 2 - Authenticating domain name
+;//
+;// 3 - Logon ID string
+;//
+;// 3 - Logon Type string
+;//
+;//
+;//
+
+MessageId=0x021A
+ SymbolicName=SE_AUDITID_LOGOFF
+ Language=English
+User Logoff:%n
+%tUser Name:%t%1%n
+%tDomain:%t%t%2%n
+%tLogon ID:%t%t%3%n
+%tLogon Type:%t%4%n
+.
+
+;//
+;//
+;// SE_AUDITID_ACCOUNT_LOCKED
+;//
+;// Category: SE_CATEGID_LOGON
+;//
+;// Parameter Strings -
+;//
+;// 1 - User account name
+;//
+;// 2 - Authenticating domain name
+;//
+;// 3 - Logon Type string
+;//
+;// 4 - Logon process name
+;//
+;// 5 - Authentication package name
+;//
+;//
+
+MessageId=0x021B
+ SymbolicName=SE_AUDITID_ACCOUNT_LOCKED
+ Language=English
+Logon Failure:%n
+%tReason:%t%tAccount locked out%n
+%tUser Name:%t%1%n
+%tDomain:%t%2%n
+%tLogon Type:%t%3%n
+%tLogon Process:%t%4%n
+%tAuthentication Package:%t%5%n
+%tWorkstation Name:%t%6
+.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+;
+;/////////////////////////////////////////////////////////////////////////////
+;// //
+;// //
+;// Messages for Category: SE_CATEGID_OBJECT_ACCESS //
+;// //
+;// Event IDs: //
+;// SE_AUDITID_OPEN_HANDLE //
+;// SE_AUDITID_CLOSE_HANDLE //
+;// SE_AUDITID_OPEN_OBJECT_FOR_DELETE //
+;// SE_AUDITID_DELETE_OBJECT //
+;// //
+;// //
+;// //
+;/////////////////////////////////////////////////////////////////////////////
+
+
+;//
+;//
+;// SE_AUDITID_OPEN_HANDLE
+;//
+;// Category: SE_CATEGID_OBJECT_ACCESS
+;//
+;// Parameter Strings -
+;//
+;// 1 - Object Type string
+;//
+;// 2 - Object name
+;//
+;// 3 - New handle ID string
+;//
+;// 4 - Object server name
+;//
+;// 5 - Process ID string
+;//
+;// 6 - Primary user account name
+;//
+;// 7 - Primary authenticating domain name
+;//
+;// 8 - Primary logon ID string
+;//
+;// 9 - Client user account name ("-" if no client)
+;//
+;// 10 - Client authenticating domain name ("-" if no client)
+;//
+;// 11 - Client logon ID string ("-" if no client)
+;//
+;// 12 - Access names
+;//
+;//
+;//
+;//
+
+MessageId=0x0230
+ SymbolicName=SE_AUDITID_OPEN_HANDLE
+ Language=English
+Object Open:%n
+%tObject Server:%t%1%n
+%tObject Type:%t%2%n
+%tObject Name:%t%3%n
+%tNew Handle ID:%t%4%n
+%tOperation ID:%t{%5,%6}%n
+%tProcess ID:%t%7%n
+%tPrimary User Name:%t%8%n
+%tPrimary Domain:%t%9%n
+%tPrimary Logon ID:%t%10%n
+%tClient User Name:%t%11%n
+%tClient Domain:%t%12%n
+%tClient Logon ID:%t%13%n
+%tAccesses%t%t%14%n
+%tPrivileges%t%t%15%n
+.
+
+
+;//
+;//
+;// SE_AUDITID_CREATE_HANDLE
+;//
+;// Category: SE_CATEGID_OBJECT_ACCESS
+;//
+;// Parameter Strings -
+;//
+;// 1 - Handle ID string
+;//
+;// 2,3 - Operation ID
+;//
+;// 4 - Process ID string
+;//
+;//
+;//
+;//
+
+MessageId=0x0231
+ SymbolicName=SE_AUDITID_CREATE_HANDLE
+ Language=English
+Handle Allocated:%n
+%tHandle ID:%t%1%n
+%tOperation ID:%t{%2,%3}%n
+%tProcess ID:%t%4%n
+.
+
+
+;//
+;//
+;// SE_AUDITID_CLOSE_HANDLE
+;//
+;// Category: SE_CATEGID_OBJECT_ACCESS
+;//
+;// Parameter Strings -
+;//
+;// 1 - Object server name
+;//
+;// 2 - Handle ID string
+;//
+;// 3 - Process ID string
+;//
+;//
+;//
+;//
+
+MessageId=0x0232
+ SymbolicName=SE_AUDITID_CLOSE_HANDLE
+ Language=English
+Handle Closed:%n
+%tObject Server:%t%1%n
+%tHandle ID:%t%2%n
+%tProcess ID:%t%3%n
+.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+;//
+;//
+;// SE_AUDITID_OPEN_OBJECT_FOR_DELETE
+;//
+;// Category: SE_CATEGID_OBJECT_ACCESS
+;//
+;// Parameter Strings -
+;//
+;// 1 - Object Type string
+;//
+;// 2 - Object name
+;//
+;// 3 - New handle ID string
+;//
+;// 4 - Object server name
+;//
+;// 5 - Process ID string
+;//
+;// 6 - Primary user account name
+;//
+;// 7 - Primary authenticating domain name
+;//
+;// 8 - Primary logon ID string
+;//
+;// 9 - Client user account name ("-" if no client)
+;//
+;// 10 - Client authenticating domain name ("-" if no client)
+;//
+;// 11 - Client logon ID string ("-" if no client)
+;//
+;// 12 - Access names
+;//
+;//
+;//
+;//
+
+MessageId=0x0233
+ SymbolicName=SE_AUDITID_OPEN_OBJECT_FOR_DELETE
+ Language=English
+Object Open for Delete:%n
+%tObject Server:%t%1%n
+%tObject Type:%t%2%n
+%tObject Name:%t%3%n
+%tNew Handle ID:%t%4%n
+%tOperation ID:%t{%5,%6}%n
+%tProcess ID:%t%7%n
+%tPrimary User Name:%t%8%n
+%tPrimary Domain:%t%9%n
+%tPrimary Logon ID:%t%10%n
+%tClient User Name:%t%11%n
+%tClient Domain:%t%12%n
+%tClient Logon ID:%t%13%n
+%tAccesses%t%t%14%n
+%tPrivileges%t%t%15%n
+.
+
+
+;//
+;//
+;// SE_AUDITID_DELETE_OBJECT
+;//
+;// Category: SE_CATEGID_OBJECT_ACCESS
+;//
+;// Parameter Strings -
+;//
+;// 1 - Object server name
+;//
+;// 2 - Handle ID string
+;//
+;// 3 - Process ID string
+;//
+;//
+;//
+;//
+
+MessageId=0x0234
+ SymbolicName=SE_AUDITID_DELETE_OBJECT
+ Language=English
+Object Deleted:%n
+%tObject Server:%t%1%n
+%tHandle ID:%t%2%n
+%tProcess ID:%t%3%n
+.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+;
+;/////////////////////////////////////////////////////////////////////////////
+;// //
+;// //
+;// Messages for Category: SE_CATEGID_PRIVILEGE_USE //
+;// //
+;// Event IDs: //
+;// SE_AUDITID_ASSIGN_SPECIAL_PRIV //
+;// SE_AUDITID_PRIVILEGED_SERVICE //
+;// SE_AUDITID_PRIVILEGED_OBJECT //
+;// //
+;// //
+;// //
+;/////////////////////////////////////////////////////////////////////////////
+
+
+
+;//
+;//
+;// SE_AUDITID_ASSIGN_SPECIAL_PRIV
+;//
+;// Category: SE_CATEGID_PRIVILEGE_USE
+;//
+;// Parameter Strings -
+;//
+;// 1 - User name
+;//
+;// 2 - domain name
+;//
+;// 3 - Logon ID string
+;//
+;// 4 - Privilege names (as 1 string, with formatting)
+;//
+;//
+;//
+;//
+
+MessageId=0x0240
+ SymbolicName=SE_AUDITID_ASSIGN_SPECIAL_PRIV
+ Language=English
+Special privileges assigned to new logon:%n
+%tUser Name:%t%1%n
+%tDomain:%t%t%2%n
+%tLogon ID:%t%t%3%n
+%tAssigned:%t%t%4
+.
+
+
+
+
+
+;//
+;//
+;// SE_AUDITID_PRIVILEGED_SERVICE
+;//
+;// Category: SE_CATEGID_PRIVILEGE_USE
+;//
+;// Parameter Strings -
+;//
+;// 1 - server name
+;//
+;// 2 - service name
+;//
+;// 3 - Primary User name
+;//
+;// 4 - Primary domain name
+;//
+;// 5 - Primary Logon ID string
+;//
+;// 6 - Client User name (or "-" if not impersonating)
+;//
+;// 7 - Client domain name (or "-" if not impersonating)
+;//
+;// 8 - Client Logon ID string (or "-" if not impersonating)
+;//
+;// 9 - Privilege names (as 1 string, with formatting)
+;//
+;//
+;//
+;//
+
+MessageId=0x0241
+ SymbolicName=SE_AUDITID_PRIVILEGED_SERVICE
+ Language=English
+Privileged Service Called:%n
+%tServer:%t%t%1%n
+%tService:%t%t%2%n
+%tPrimary User Name:%t%3%n
+%tPrimary Domain:%t%4%n
+%tPrimary Logon ID:%t%5%n
+%tClient User Name:%t%6%n
+%tClient Domain:%t%7%n
+%tClient Logon ID:%t%8%n
+%tPrivileges:%t%9
+.
+
+
+
+
+
+
+;//
+;//
+;// SE_AUDITID_PRIVILEGED_OBJECT
+;//
+;// Category: SE_CATEGID_PRIVILEGE_USE
+;//
+;// Parameter Strings -
+;//
+;// 1 - Object type
+;//
+;// 2 - object name (if available)
+;//
+;// 3 - server name
+;//
+;// 4 - process ID string
+;//
+;// 5 - Primary User name
+;//
+;// 6 - Primary domain name
+;//
+;// 7 - Primary Logon ID string
+;//
+;// 8 - Client User name (or "-" if not impersonating)
+;//
+;// 9 - Client domain name (or "-" if not impersonating)
+;//
+;// 10 - Client Logon ID string (or "-" if not impersonating)
+;//
+;// 11 - Privilege names (as 1 string, with formatting)
+;//
+;//
+;//
+;//
+
+;//
+;// Jimk Original
+;//
+;//MessageId=0x0242
+;// SymbolicName=SE_AUDITID_PRIVILEGED_OBJECT
+;// Language=English
+;//%tPrivileged object operation:%n
+;//%t%tObject Type:%t%1%n
+;//%t%tObject Name:%t%2%n
+;//%t%tObject Server:%t%3%n
+;//%t%tProcess ID:%t%4%n
+;//%t%tPrimary User Name:%t%5%n
+;//%t%tPrimary Domain:%t%6%n
+;//%t%tPrimary Logon ID:%t%7%n
+;//%t%tClient User Name:%t%8%n
+;//%t%tClient Domain:%t%9%n
+;//%t%tClient Logon ID:%t%10%n
+;//%t%tPrivileges:%t%11
+;//.
+
+
+MessageId=0x0242
+ SymbolicName=SE_AUDITID_PRIVILEGED_OBJECT
+ Language=English
+Privileged object operation:%n
+%tObject Server:%t%1%n
+%tObject Handle:%t%2%n
+%tProcess ID:%t%3%n
+%tPrimary User Name:%t%4%n
+%tPrimary Domain:%t%5%n
+%tPrimary Logon ID:%t%6%n
+%tClient User Name:%t%7%n
+%tClient Domain:%t%8%n
+%tClient Logon ID:%t%9%n
+%tPrivileges:%t%10
+.
+
+
+
+
+
+
+
+
+
+;
+;/////////////////////////////////////////////////////////////////////////////
+;// //
+;// //
+;// Messages for Category: SE_CATEGID_DETAILED_TRACKING //
+;// //
+;// Event IDs: //
+;// SE_AUDITID_PROCESS_CREATED //
+;// SE_AUDITID_PROCESS_EXIT //
+;// SE_AUDITID_DUPLICATE_HANDLE //
+;// SE_AUDITID_INDIRECT_REFERENCE //
+;// //
+;// //
+;// //
+;/////////////////////////////////////////////////////////////////////////////
+
+
+;//
+;//
+;// SE_AUDITID_PROCESS_CREATED
+;//
+;// Category: SE_CATEGID_DETAILED_TRACKING
+;//
+;// Parameter Strings -
+;//
+;// 1 - process ID string
+;//
+;// 2 - Image file name (if available - otherwise "-")
+;//
+;// 3 - Creating process's ID
+;//
+;// 4 - User name (of new process)
+;//
+;// 5 - domain name (of new process)
+;//
+;// 6 - Logon ID string (of new process)
+;//
+
+MessageId=0x0250
+ SymbolicName=SE_AUDITID_PROCESS_CREATED
+ Language=English
+A new process has been created:%n
+%tNew Process ID:%t%1%n
+%tImage File Name:%t%2%n
+%tCreator Process ID:%t%3%n
+%tUser Name:%t%4%n
+%tDomain:%t%t%5%n
+%tLogon ID:%t%t%6%n
+.
+
+
+
+
+
+;//
+;//
+;// SE_AUDITID_PROCESS_EXIT
+;//
+;// Category: SE_CATEGID_DETAILED_TRACKING
+;//
+;// Parameter Strings -
+;//
+;// 1 - process ID string
+;//
+;// 2 - User name
+;//
+;// 3 - domain name
+;//
+;// 4 - Logon ID string
+;//
+;//
+;//
+;//
+
+MessageId=0x0251
+ SymbolicName=SE_AUDITID_PROCESS_EXIT
+ Language=English
+A process has exited:%n
+%tProcess ID:%t%1%n
+%tUser Name:%t%2%n
+%tDomain:%t%t%3%n
+%tLogon ID:%t%t%4%n
+.
+
+
+
+
+
+;//
+;//
+;// SE_AUDITID_DUPLICATE_HANDLE
+;//
+;// Category: SE_CATEGID_DETAILED_TRACKING
+;//
+;// Parameter Strings -
+;//
+;// 1 - Origin (source) handle ID string
+;//
+;// 2 - Origin (source) process ID string
+;//
+;// 3 - New (Target) handle ID string
+;//
+;// 4 - Target process ID string
+;//
+;//
+;//
+
+MessageId=0x0252
+ SymbolicName=SE_AUDITID_DUPLICATE_HANDLE
+ Language=English
+A handle to an object has been duplicated:%n
+%tSource Handle ID:%t%1%n
+%tSource Process ID:%t%2%n
+%tTarget Handle ID:%t%3%n
+%tTarget Process ID:%t%4%n
+.
+
+
+
+
+
+
+
+
+;//
+;//
+;// SE_AUDITID_INDIRECT_REFERENCE
+;//
+;// Category: SE_CATEGID_DETAILED_TRACKING
+;//
+;// Parameter Strings -
+;//
+;// 1 - Object type
+;//
+;// 2 - object name (if available - otherwise "-")
+;//
+;// 3 - ID string of handle used to gain access
+;//
+;// 3 - server name
+;//
+;// 4 - process ID string
+;//
+;// 5 - primary User name
+;//
+;// 6 - primary domain name
+;//
+;// 7 - primary logon ID
+;//
+;// 8 - client User name
+;//
+;// 9 - client domain name
+;//
+;// 10 - client logon ID
+;//
+;// 11 - granted access names (with formatting)
+;//
+;//
+
+MessageId=0x0253
+ SymbolicName=SE_AUDITID_INDIRECT_REFERENCE
+ Language=English
+Indirect access to an object has been obtained:%n
+%tObject Type:%t%1%n
+%tObject Name:%t%2%n
+%tProcess ID:%t%3%n
+%tPrimary User Name:%t%4%n
+%tPrimary Domain:%t%5%n
+%tPrimary Logon ID:%t%6%n
+%tClient User Name:%t%7%n
+%tClient Domain:%t%8%n
+%tClient Logon ID:%t%9%n
+%tAccesses:%t%10%n
+.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+;
+;/////////////////////////////////////////////////////////////////////////////
+;// //
+;// //
+;// Messages for Category: SE_CATEGID_POLICY_CHANGE //
+;// //
+;// Event IDs: //
+;// SE_AUDITID_USER_RIGHT_ASSIGNED //
+;// SE_AUDITID_USER_RIGHT_REMOVED //
+;// SE_AUDITID_TRUSTED_DOMAIN_ADD //
+;// SE_AUDITID_TRUSTED_DOMAIN_REM //
+;// SE_AUDITID_POLICY_CHANGE //
+;// //
+;// //
+;// //
+;/////////////////////////////////////////////////////////////////////////////
+
+
+
+;//
+;//
+;// SE_AUDITID_USER_RIGHT_ASSIGNED
+;//
+;// Category: SE_CATEGID_POLICY_CHANGE
+;//
+;// Parameter Strings -
+;//
+;// 1 - User right name
+;//
+;// 2 - SID string of account assigned the user right
+;//
+;// 3 - User name of subject assigning the right
+;//
+;// 4 - Domain name of subject assigning the right
+;//
+;// 5 - Logon ID string of subject assigning the right
+;//
+;//
+;//
+
+MessageId=0x0260
+ SymbolicName=SE_AUDITID_USER_RIGHT_ASSIGNED
+ Language=English
+User Right Assigned:%n
+%tUser Right:%t%1%n
+%tAssigned To:%t%2%n
+%tAssigned By:%n
+%tUser Name:%t%3%n
+%tDomain:%t%t%4%n
+%tLogon ID:%t%t%5%n
+.
+
+
+
+
+;//
+;//
+;// SE_AUDITID_USER_RIGHT_REMOVED
+;//
+;// Category: SE_CATEGID_POLICY_CHANGE
+;//
+;// Parameter Strings -
+;//
+;// 1 - User right name
+;//
+;// 2 - SID string of account from which the user
+;// right was removed
+;//
+;// 3 - User name of subject removing the right
+;//
+;// 4 - Domain name of subject removing the right
+;//
+;// 5 - Logon ID string of subject removing the right
+;//
+;//
+
+MessageId=0x0261
+ SymbolicName=SE_AUDITID_USER_RIGHT_REMOVED
+ Language=English
+User Right Removed:%n
+%tUser Right:%t%1%n
+%tRemoved From:%t%2%n
+%tRemoved By:%n
+%tUser Name:%t%3%n
+%tDomain:%t%t%4%n
+%tLogon ID:%t%t%5%n
+.
+
+
+
+
+
+
+;//
+;//
+;// SE_AUDITID_TRUSTED_DOMAIN_ADD
+;//
+;// Category: SE_CATEGID_POLICY_CHANGE
+;//
+;// Parameter Strings -
+;//
+;// 1 - Name of new trusted domain
+;//
+;// 2 - SID string of new trusted domain
+;//
+;// 3 - User name of subject adding the trusted domain
+;//
+;// 4 - Domain name of subject adding the trusted domain
+;//
+;// 5 - Logon ID string of subject adding the trusted domain
+;//
+
+MessageId=0x0262
+ SymbolicName=SE_AUDITID_TRUSTED_DOMAIN_ADD
+ Language=English
+New Trusted Domain:%n
+%tDomain Name:%t%1%n
+%tDomain ID:%t%2%n
+%tEstablished By:%n
+%tUser Name:%t%3%n
+%tDomain:%t%t%4%n
+%tLogon ID:%t%t%5%n
+.
+
+
+
+
+
+
+;//
+;//
+;// SE_AUDITID_TRUSTED_DOMAIN_REM
+;//
+;// Category: SE_CATEGID_POLICY_CHANGE
+;//
+;// Parameter Strings -
+;//
+;// 1 - Name of domain no longer trusted
+;//
+;// 2 - SID string of domain no longer trusted
+;//
+;// 3 - User name of subject removing the trusted domain
+;//
+;// 4 - Domain name of subject removing the trusted domain
+;//
+;// 5 - Logon ID string of subject removing the trusted domain
+;//
+;//
+;//
+
+MessageId=0x0263
+ SymbolicName=SE_AUDITID_TRUSTED_DOMAIN_REM
+ Language=English
+Removing Trusted Domain:%n
+%tDomain Name:%t%1%n
+%tDomain ID:%t%2%n
+%tRemoved By:%n
+%tUser Name:%t%3%n
+%tDomain:%t%t%4%n
+%tLogon ID:%t%t%5%n
+.
+
+
+
+
+
+
+;//
+;//
+;// SE_AUDITID_POLICY_CHANGE
+;//
+;// Category: SE_CATEGID_POLICY_CHANGE
+;//
+;// Parameter Strings -
+;//
+;// 1 - System success audit status ("+" or "-")
+;// 2 - System failure audit status ("+" or "-")
+;//
+;// 3 - Logon/Logoff success audit status ("+" or "-")
+;// 4 - Logon/Logoff failure audit status ("+" or "-")
+;//
+;// 5 - Object Access success audit status ("+" or "-")
+;// 6 - Object Access failure audit status ("+" or "-")
+;//
+;// 7 - Detailed Tracking success audit status ("+" or "-")
+;// 8 - Detailed Tracking failure audit status ("+" or "-")
+;//
+;// 9 - Privilege Use success audit status ("+" or "-")
+;// 10 - Privilege Use failure audit status ("+" or "-")
+;//
+;// 11 - Policy Change success audit status ("+" or "-")
+;// 12 - Policy Change failure audit status ("+" or "-")
+;//
+;// 13 - Account Management success audit status ("+" or "-")
+;// 14 - Account Management failure audit status ("+" or "-")
+;//
+;// 15 - Account Name of user that changed the policy
+;//
+;// 16 - Domain of user that changed the policy
+;//
+;// 17 - Logon ID of user that changed the policy
+;//
+;//
+
+MessageId=0x0264
+ SymbolicName=SE_AUDITID_POLICY_CHANGE
+ Language=English
+Audit Policy Change:%n
+New Policy:%n
+%tSuccess%tFailure%n
+%t %1%t %2%tSystem%n
+%t %3%t %4%tLogon/Logoff%n
+%t %5%t %6%tObject Access%n
+%t %7%t %8%tPrivilege Use%n
+%t %9%t %10%tDetailed Tracking%n
+%t %11%t %12%tPolicy Change%n
+%t %13%t %14%tAccount Management%n%n
+Changed By:%n
+%tUser Name:%t%15%n
+%tDomain Name:%t%16%n
+%tLogon ID:%t%t%17
+.
+
+
+
+
+
+
+
+
+
+
+
+
+;
+;/////////////////////////////////////////////////////////////////////////////
+;// //
+;// //
+;// Messages for Category: SE_CATEGID_ACCOUNT_MANAGEMENT //
+;// //
+;// Event IDs: //
+;// SE_AUDITID_USER_CREATED //
+;// SE_AUDITID_USER_CHANGE //
+;// SE_AUDITID_ACCOUNT_TYPE_CHANGE //
+;// SE_AUDITID_USER_ENABLED //
+;// SE_AUDITID_USER_PWD_CHANGED //
+;// SE_AUDITID_USER_PWD_SET //
+;// SE_AUDITID_USER_DISABLED //
+;// SE_AUDITID_USER_DELETED //
+;// SE_AUDITID_GLOBAL_GROUP_CREATED //
+;// SE_AUDITID_GLOBAL_GROUP_ADD //
+;// SE_AUDITID_GLOBAL_GROUP_REM //
+;// SE_AUDITID_GLOBAL_GROUP_DELETED //
+;// SE_AUDITID_LOCAL_GROUP_CREATED //
+;// SE_AUDITID_LOCAL_GROUP_ADD //
+;// SE_AUDITID_LOCAL_GROUP_REM //
+;// SE_AUDITID_LOCAL_GROUP_DELETED //
+;// SE_AUDITID_OTHER_ACCT_CHANGE //
+;// SE_AUDITID_DOMAIN_POLICY_CHANGE //
+;// //
+;// //
+;/////////////////////////////////////////////////////////////////////////////
+
+
+;//
+;//
+;// SE_AUDITID_USER_CREATED
+;//
+;// Category: SE_CATEGID_ACCOUNT_MANAGEMENT
+;//
+;// Parameter Strings -
+;//
+;// 1 - name of new user account
+;//
+;// 2 - domain of new user account
+;//
+;// 3 - SID string of new user account
+;//
+;// 4 - User name of subject creating the user account
+;//
+;// 5 - Domain name of subject creating the user account
+;//
+;// 6 - Logon ID string of subject creating the user account
+;//
+;// 7 - Privileges used to create the user account
+;//
+;//
+
+MessageId=0x0270
+ SymbolicName=SE_AUDITID_USER_CREATED
+ Language=English
+User Account Created:%n
+%tNew Account Name:%t%1%n
+%tNew Domain:%t%2%n
+%tNew Account ID:%t%3%n
+%tCaller User Name:%t%4%n
+%tCaller Domain:%t%5%n
+%tCaller Logon ID:%t%6%n
+%tPrivileges%t%t%7%n
+.
+
+
+
+
+
+
+;//
+;//
+;// SE_AUDITID_ACCOUNT_TYPE_CHANGE
+;//
+;// Category: SE_CATEGID_ACCOUNT_MANAGEMENT
+;//
+;// Parameter Strings -
+;//
+;// 1 - name of target user account
+;//
+;// 2 - domain of target user account
+;//
+;// 3 - SID string of target user account
+;//
+;// 4 - new account type string
+;// (sigh, this isn't going to be locallizable)
+;//
+;// 5 - User name of subject changing the user account
+;//
+;// 6 - Domain name of subject changing the user account
+;//
+;// 7 - Logon ID string of subject changing the user account
+;//
+;//
+
+MessageId=0x0271
+ SymbolicName=SE_AUDITID_ACCOUNT_TYPE_CHANGE
+ Language=English
+User Account Type Change:%n
+%tTarget Account Name:%t%1%n
+%tTarget Domain:%t%2%n
+%tTarget Account ID:%t%3%n
+%tNew Type:%t%4%n
+%tCaller User Name:%t%5%n
+%tCaller Domain:%t%6%n
+%tCaller Logon ID:%t%7%n
+.
+
+
+
+
+
+
+;//
+;//
+;// SE_AUDITID_USER_ENABLED
+;//
+;// Category: SE_CATEGID_ACCOUNT_MANAGEMENT
+;//
+;// Parameter Strings -
+;//
+;// 1 - name of target user account
+;//
+;// 2 - domain of target user account
+;//
+;// 3 - SID string of target user account
+;//
+;// 4 - User name of subject changing the user account
+;//
+;// 5 - Domain name of subject changing the user account
+;//
+;// 6 - Logon ID string of subject changing the user account
+;//
+;//
+
+MessageId=0x0272
+ SymbolicName=SE_AUDITID_USER_ENABLED
+ Language=English
+User Account Enabled:%n
+%tTarget Account Name:%t%1%n
+%tTarget Domain:%t%2%n
+%tTarget Account ID:%t%3%n
+%tCaller User Name:%t%4%n
+%tCaller Domain:%t%5%n
+%tCaller Logon ID:%t%6%n
+.
+
+
+
+
+
+
+;//
+;//
+;// SE_AUDITID_USER_PWD_CHANGED
+;//
+;// Category: SE_CATEGID_ACCOUNT_MANAGEMENT
+;//
+;// Parameter Strings -
+;//
+;// 1 - name of target user account
+;//
+;// 2 - domain of target user account
+;//
+;// 3 - SID string of target user account
+;//
+;// 4 - User name of subject changing the user account
+;//
+;// 5 - Domain name of subject changing the user account
+;//
+;// 6 - Logon ID string of subject changing the user account
+;//
+;//
+
+MessageId=0x0273
+ SymbolicName=SE_AUDITID_USER_PWD_CHANGED
+ Language=English
+Change Password Attempt:%n
+%tTarget Account Name:%t%1%n
+%tTarget Domain:%t%2%n
+%tTarget Account ID:%t%3%n
+%tCaller User Name:%t%4%n
+%tCaller Domain:%t%5%n
+%tCaller Logon ID:%t%6%n
+%tPrivileges:%t%7%n
+.
+
+
+
+
+
+
+;//
+;//
+;// SE_AUDITID_USER_PWD_SET
+;//
+;// Category: SE_CATEGID_ACCOUNT_MANAGEMENT
+;//
+;// Parameter Strings -
+;//
+;// 1 - name of target user account
+;//
+;// 2 - domain of target user account
+;//
+;// 3 - SID string of target user account
+;//
+;// 4 - User name of subject changing the user account
+;//
+;// 5 - Domain name of subject changing the user account
+;//
+;// 6 - Logon ID string of subject changing the user account
+;//
+;//
+
+MessageId=0x0274
+ SymbolicName=SE_AUDITID_USER_PWD_SET
+ Language=English
+User Account password set:%n
+%tTarget Account Name:%t%1%n
+%tTarget Domain:%t%2%n
+%tTarget Account ID:%t%3%n
+%tCaller User Name:%t%4%n
+%tCaller Domain:%t%5%n
+%tCaller Logon ID:%t%6%n
+.
+
+
+
+
+
+
+;//
+;//
+;// SE_AUDITID_USER_DISABLED
+;//
+;// Category: SE_CATEGID_ACCOUNT_MANAGEMENT
+;//
+;// Parameter Strings -
+;//
+;// 1 - name of target user account
+;//
+;// 2 - domain of target user account
+;//
+;// 3 - SID string of target user account
+;//
+;// 4 - User name of subject changing the user account
+;//
+;// 5 - Domain name of subject changing the user account
+;//
+;// 6 - Logon ID string of subject changing the user account
+;//
+;//
+
+MessageId=0x0275
+ SymbolicName=SE_AUDITID_USER_DISABLED
+ Language=English
+User Account Disabled:%n
+%tTarget Account Name:%t%1%n
+%tTarget Domain:%t%2%n
+%tTarget Account ID:%t%3%n
+%tCaller User Name:%t%4%n
+%tCaller Domain:%t%5%n
+%tCaller Logon ID:%t%6%n
+.
+
+
+
+
+
+
+;//
+;//
+;// SE_AUDITID_USER_DELETED
+;//
+;// Category: SE_CATEGID_ACCOUNT_MANAGEMENT
+;//
+;// Parameter Strings -
+;//
+;// 1 - name of target account
+;//
+;// 2 - domain of target account
+;//
+;// 3 - SID string of target account
+;//
+;// 4 - User name of subject changing the account
+;//
+;// 5 - Domain name of subject changing the account
+;//
+;// 6 - Logon ID string of subject changing the account
+;//
+;//
+
+MessageId=0x0276
+ SymbolicName=SE_AUDITID_USER_DELETED
+ Language=English
+User Account Deleted:%n
+%tTarget Account Name:%t%1%n
+%tTarget Domain:%t%2%n
+%tTarget Account ID:%t%3%n
+%tCaller User Name:%t%4%n
+%tCaller Domain:%t%5%n
+%tCaller Logon ID:%t%6%n
+%tPrivileges:%t%7%n
+.
+
+
+
+
+
+
+;//
+;//
+;// SE_AUDITID_GLOBAL_GROUP_CREATED
+;//
+;// Category: SE_CATEGID_ACCOUNT_MANAGEMENT
+;//
+;// Parameter Strings -
+;//
+;// 1 - name of new group account
+;//
+;// 2 - domain of new group account
+;//
+;// 3 - SID string of new group account
+;//
+;// 4 - User name of subject creating the account
+;//
+;// 5 - Domain name of subject creating the account
+;//
+;// 6 - Logon ID string of subject creating the account
+;//
+;//
+
+MessageId=0x0277
+ SymbolicName=SE_AUDITID_GLOBAL_GROUP_CREATED
+ Language=English
+Global Group Created:%n
+%tNew Account Name:%t%1%n
+%tNew Domain:%t%2%n
+%tNew Account ID:%t%3%n
+%tCaller User Name:%t%4%n
+%tCaller Domain:%t%5%n
+%tCaller Logon ID:%t%6%n
+%tPrivileges:%t%7%n
+.
+
+
+
+
+
+
+;//
+;//
+;// SE_AUDITID_GLOBAL_GROUP_ADD
+;//
+;// Category: SE_CATEGID_ACCOUNT_MANAGEMENT
+;//
+;// Parameter Strings -
+;//
+;// 1 - SID string of new member
+;//
+;// 2 - name of target account
+;//
+;// 3 - domain of target account
+;//
+;// 4 - SID string of target account
+;//
+;// 5 - User name of subject changing the account
+;//
+;// 6 - Domain name of subject changing the account
+;//
+;// 7 - Logon ID string of subject changing the account
+;//
+;//
+
+MessageId=0x0278
+ SymbolicName=SE_AUDITID_GLOBAL_GROUP_ADD
+ Language=English
+Global Group Member Added:%n
+%tMember:%t%1%n
+%tTarget Account Name:%t%2%n
+%tTarget Domain:%t%3%n
+%tTarget Account ID:%t%4%n
+%tCaller User Name:%t%5%n
+%tCaller Domain:%t%6%n
+%tCaller Logon ID:%t%7%n
+%tPrivileges:%t%8%n
+.
+
+
+
+
+
+
+;//
+;//
+;// SE_AUDITID_GLOBAL_GROUP_REM
+;//
+;// Category: SE_CATEGID_ACCOUNT_MANAGEMENT
+;//
+;// Parameter Strings -
+;//
+;// 1 - SID string of member being removed
+;//
+;// 2 - name of target account
+;//
+;// 3 - domain of target account
+;//
+;// 4 - SID string of target account
+;//
+;// 5 - User name of subject changing the account
+;//
+;// 6 - Domain name of subject changing the account
+;//
+;// 7 - Logon ID string of subject changing the account
+;//
+;//
+
+MessageId=0x0279
+ SymbolicName=SE_AUDITID_GLOBAL_GROUP_REM
+ Language=English
+Global Group Member Removed:%n
+%tMember:%t%1%n
+%tTarget Account Name:%t%2%n
+%tTarget Domain:%t%3%n
+%tTarget Account ID:%t%4%n
+%tCaller User Name:%t%5%n
+%tCaller Domain:%t%6%n
+%tCaller Logon ID:%t%7%n
+%tPrivileges:%t%8%n
+.
+
+
+
+
+
+
+;//
+;//
+;// SE_AUDITID_GLOBAL_GROUP_DELETED
+;//
+;// Category: SE_CATEGID_ACCOUNT_MANAGEMENT
+;//
+;// Parameter Strings -
+;//
+;// 1 - name of target account
+;//
+;// 2 - domain of target account
+;//
+;// 3 - SID string of target account
+;//
+;// 4 - User name of subject changing the account
+;//
+;// 5 - Domain name of subject changing the account
+;//
+;// 6 - Logon ID string of subject changing the account
+;//
+;//
+
+MessageId=0x027A
+ SymbolicName=SE_AUDITID_GLOBAL_GROUP_DELETED
+ Language=English
+Global Group Deleted:%n
+%tTarget Account Name:%t%1%n
+%tTarget Domain:%t%2%n
+%tTarget Account ID:%t%3%n
+%tCaller User Name:%t%4%n
+%tCaller Domain:%t%5%n
+%tCaller Logon ID:%t%6%n
+%tPrivileges:%t%7%n
+.
+
+
+
+
+
+;//
+;//
+;// SE_AUDITID_LOCAL_GROUP_CREATED
+;//
+;// Category: SE_CATEGID_ACCOUNT_MANAGEMENT
+;//
+;// Parameter Strings -
+;//
+;// 1 - name of new group account
+;//
+;// 2 - domain of new group account
+;//
+;// 3 - SID string of new group account
+;//
+;// 4 - User name of subject creating the account
+;//
+;// 5 - Domain name of subject creating the account
+;//
+;// 6 - Logon ID string of subject creating the account
+;//
+;//
+
+MessageId=0x027B
+ SymbolicName=SE_AUDITID_LOCAL_GROUP_CREATED
+ Language=English
+Local Group Created:%n
+%tNew Account Name:%t%1%n
+%tNew Domain:%t%2%n
+%tNew Account ID:%t%3%n
+%tCaller User Name:%t%4%n
+%tCaller Domain:%t%5%n
+%tCaller Logon ID:%t%6%n
+%tPrivileges:%t%7%n
+.
+
+
+
+
+
+
+;//
+;//
+;// SE_AUDITID_LOCAL_GROUP_ADD
+;//
+;// Category: SE_CATEGID_ACCOUNT_MANAGEMENT
+;//
+;// Parameter Strings -
+;//
+;// 1 - SID string of new member
+;//
+;// 2 - name of target account
+;//
+;// 3 - domain of target account
+;//
+;// 4 - SID string of target account
+;//
+;// 5 - User name of subject changing the account
+;//
+;// 6 - Domain name of subject changing the account
+;//
+;// 7 - Logon ID string of subject changing the account
+;//
+;//
+
+MessageId=0x027C
+ SymbolicName=SE_AUDITID_LOCAL_GROUP_ADD
+ Language=English
+Local Group Member Added:%n
+%tMember:%t%1%n
+%tTarget Account Name:%t%2%n
+%tTarget Domain:%t%t%3%n
+%tTarget Account ID:%t%t%4%n
+%tCaller User Name:%t%t%5%n
+%tCaller Domain:%t%t%6%n
+%tCaller Logon ID:%t%t%7%n
+%tPrivileges:%t%8%n
+.
+
+
+
+
+
+
+;//
+;//
+;// SE_AUDITID_LOCAL_GROUP_REM
+;//
+;// Category: SE_CATEGID_ACCOUNT_MANAGEMENT
+;//
+;// Parameter Strings -
+;//
+;// 1 - SID string of member being removed
+;//
+;// 2 - name of target account
+;//
+;// 3 - domain of target account
+;//
+;// 4 - SID string of target account
+;//
+;// 5 - User name of subject changing the account
+;//
+;// 6 - Domain name of subject changing the account
+;//
+;// 7 - Logon ID string of subject changing the account
+;//
+;//
+
+MessageId=0x027D
+ SymbolicName=SE_AUDITID_LOCAL_GROUP_REM
+ Language=English
+Local Group Member Removed:%n
+%tMember:%t%1%n
+%tTarget Account Name:%t%2%n
+%tTarget Domain:%t%t%3%n
+%tTarget Account ID:%t%t%4%n
+%tCaller User Name:%t%t%5%n
+%tCaller Domain:%t%t%6%n
+%tCaller Logon ID:%t%t%7%n
+%tPrivileges:%t%t%8%n
+.
+
+
+
+
+
+
+;//
+;//
+;// SE_AUDITID_LOCAL_GROUP_DELETED
+;//
+;// Category: SE_CATEGID_ACCOUNT_MANAGEMENT
+;//
+;// Parameter Strings -
+;//
+;// 1 - name of target account
+;//
+;// 2 - domain of target account
+;//
+;// 3 - SID string of target account
+;//
+;// 4 - User name of subject changing the account
+;//
+;// 5 - Domain name of subject changing the account
+;//
+;// 6 - Logon ID string of subject changing the account
+;//
+;//
+
+MessageId=0x027E
+ SymbolicName=SE_AUDITID_LOCAL_GROUP_DELETED
+ Language=English
+Local Group Deleted:%n
+%tTarget Account Name:%t%1%n
+%tTarget Domain:%t%2%n
+%tTarget Account ID:%t%3%n
+%tCaller User Name:%t%4%n
+%tCaller Domain:%t%5%n
+%tCaller Logon ID:%t%6%n
+%tPrivileges:%t%7%n
+.
+
+
+;//
+;//
+;// SE_AUDITID_LOCAL_GROUP_CHANGE
+;//
+;// Category: SE_CATEGID_ACCOUNT_MANAGEMENT
+;//
+;// Parameter Strings -
+;//
+;// 1 - name of target account
+;//
+;// 2 - domain of target account
+;//
+;// 3 - SID string of target account
+;//
+;// 4 - User name of subject changing the account
+;//
+;// 5 - Domain name of subject changing the account
+;//
+;// 6 - Logon ID string of subject changing the account
+;//
+;//
+
+MessageId=0x027F
+ SymbolicName=SE_AUDITID_LOCAL_GROUP_CHANGE
+ Language=English
+Local Group Changed:%n
+%tTarget Account Name:%t%1%n
+%tTarget Domain:%t%2%n
+%tTarget Account ID:%t%3%n
+%tCaller User Name:%t%4%n
+%tCaller Domain:%t%5%n
+%tCaller Logon ID:%t%6%n
+%tPrivileges:%t%7%n
+.
+
+
+
+
+
+
+
+;//
+;//
+;// SE_AUDITID_OTHER_ACCOUNT_CHANGE
+;//
+;// Category: SE_CATEGID_ACCOUNT_MANAGEMENT
+;//
+;// Parameter Strings -
+;//
+;// 1 - Type of change (sigh, this isn't localizable)
+;//
+;// 2 - Type of changed object
+;//
+;// 3 - SID string (of changed object)
+;//
+;// 4 - User name of subject changing the account
+;//
+;// 5 - Domain name of subject changing the account
+;//
+;// 6 - Logon ID string of subject changing the account
+;//
+;//
+
+MessageId=0x0280
+ SymbolicName=SE_AUDITID_OTHER_ACCOUNT_CHANGE
+ Language=English
+General Account Database Change:%n
+%tType of change:%t%1%n
+%tObject Type:%t%2%n
+%tObject Name:%t%3%n
+%tObject ID:%t%4%n
+%tCaller User Name:%t%5%n
+%tCaller Domain:%t%6%n
+%tCaller Logon ID:%t%7%n
+.
+
+
+
+
+
+
+
+;//
+;//
+;// SE_AUDITID_GLOBAL_GROUP_CHANGE
+;//
+;// Category: SE_CATEGID_ACCOUNT_MANAGEMENT
+;//
+;// Parameter Strings -
+;//
+;// 1 - name of target account
+;//
+;// 2 - domain of target account
+;//
+;// 3 - SID string of target account
+;//
+;// 4 - User name of subject changing the account
+;//
+;// 5 - Domain name of subject changing the account
+;//
+;// 6 - Logon ID string of subject changing the account
+;//
+;//
+
+MessageId=0x0281
+ SymbolicName=SE_AUDITID_GLOBAL_GROUP_CHANGE
+ Language=English
+Global Group Changed:%n
+%tTarget Account Name:%t%1%n
+%tTarget Domain:%t%2%n
+%tTarget Account ID:%t%3%n
+%tCaller User Name:%t%4%n
+%tCaller Domain:%t%5%n
+%tCaller Logon ID:%t%6%n
+%tPrivileges:%t%7%n
+.
+
+
+
+
+;//
+;//
+;// SE_AUDITID_USER_CHANGE
+;//
+;// Category: SE_CATEGID_ACCOUNT_MANAGEMENT
+;//
+;// Parameter Strings -
+;//
+;// 1 - name of target user account
+;//
+;// 2 - domain of target user account
+;//
+;// 3 - SID string of target user account
+;//
+;// 4 - User name of subject changing the user account
+;//
+;// 5 - Domain name of subject changing the user account
+;//
+;// 6 - Logon ID string of subject changing the user account
+;//
+;//
+
+MessageId=0x0282
+ SymbolicName=SE_AUDITID_USER_CHANGE
+ Language=English
+User Account Changed:%n
+%tTarget Account Name:%t%1%n
+%tTarget Domain:%t%2%n
+%tTarget Account ID:%t%3%n
+%tCaller User Name:%t%4%n
+%tCaller Domain:%t%5%n
+%tCaller Logon ID:%t%6%n
+%tPrivileges:%t%7%n
+.
+
+
+
+;//
+;//
+;// SE_AUDITID_DOMAIN_POLICY_CHANGE
+;//
+;// Category: SE_CATEGID_ACCOUNT_MANAGEMENT
+;//
+;// Parameter Strings -
+;//
+;// 1 - (unused)
+;//
+;// 2 - domain of target user account
+;//
+;// 3 - SID string of target user account
+;//
+;// 4 - User name of subject changing the user account
+;//
+;// 5 - Domain name of subject changing the user account
+;//
+;// 6 - Logon ID string of subject changing the user account
+;//
+;//
+
+MessageId=0x0283
+ SymbolicName=SE_AUDITID_DOMAIN_POLICY_CHANGE
+ Language=English
+Domain Policy Changed:%n
+%tDomain:%t%t%1%n
+%tDomain ID:%t%2%n
+%tCaller User Name:%t%3%n
+%tCaller Domain:%t%4%n
+%tCaller Logon ID:%t%5%n
+%tPrivileges:%t%6%n
+.
+
+
+
+
+
+
+
+
+
+
+;/*lint +e767 */ // Resume checking for different macro definitions // winnt
+;
+;
+;#endif // _MSAUDITE_
diff --git a/private/ntos/seaudit/msaudite/sources b/private/ntos/seaudit/msaudite/sources
new file mode 100644
index 000000000..462e1dc34
--- /dev/null
+++ b/private/ntos/seaudit/msaudite/sources
@@ -0,0 +1,41 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=ntos
+MINORCOMP=msaudite
+
+TARGETNAME=msaudite
+TARGETPATH=\nt\public\sdk\lib
+
+TARGETLIBS=
+
+TARGETTYPE=DYNLINK
+
+INCLUDES=.
+
+SOURCES= audit.rc
+
+UMLIBS=
+
+NTTARGETFILE0=audit.rc
diff --git a/private/ntos/seaudit/msauditt/audit.rc b/private/ntos/seaudit/msauditt/audit.rc
new file mode 100644
index 000000000..f78b05af5
--- /dev/null
+++ b/private/ntos/seaudit/msauditt/audit.rc
@@ -0,0 +1,13 @@
+#include <windows.h>
+
+1 11 MSG00001.bin
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Security Audit Types DLL"
+#define VER_INTERNALNAME_STR "msauditt.dll"
+
+#include "common.ver"
+
diff --git a/private/ntos/seaudit/msauditt/makefile b/private/ntos/seaudit/msauditt/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/ntos/seaudit/msauditt/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/ntos/seaudit/msauditt/msauditt.def b/private/ntos/seaudit/msauditt/msauditt.def
new file mode 100644
index 000000000..99f7f8806
--- /dev/null
+++ b/private/ntos/seaudit/msauditt/msauditt.def
@@ -0,0 +1,7 @@
+LIBRARY msaudit
+
+DESCRIPTION 'Message File for Auditing'
+
+EXPORTS
+ MsAuditTDummyEntry
+
diff --git a/private/ntos/seaudit/msauditt/msauditt.mc b/private/ntos/seaudit/msauditt/msauditt.mc
new file mode 100644
index 000000000..185446c17
--- /dev/null
+++ b/private/ntos/seaudit/msauditt/msauditt.mc
@@ -0,0 +1,365 @@
+;/*++ BUILD Version: 0001 // Increment this if a change has global effects
+;
+;Copyright (c) 1991 Microsoft Corporation
+;
+;Module Name:
+;
+; msauditt.mc
+;
+;Abstract:
+;
+; Constant definitions for the NT Audit Event Messages.
+;
+;Author:
+;
+; Jim Kelly (JimK) 30-Mar-1992
+;
+;Revision History:
+;
+;Notes:
+;
+; The .h and .res forms of this file are generated from the .mc
+; form of the file (private\ntos\seaudit\msauditt\msauditt.mc). Please make
+; all changes to the .mc form of the file.
+;
+;
+;
+;--*/
+;
+;#ifndef _MSAUDITT_
+;#define _MSAUDITT_
+;
+;/*lint -e767 */ // Don't complain about different definitions // winnt
+
+
+MessageIdTypedef=ULONG
+
+SeverityNames=(None=0x0)
+
+FacilityNames=(None=0x0)
+
+
+
+MessageId=0x0000
+ Language=English
+Unused message ID
+.
+;// Message ID 0 is unused - just used to flush out the diagram
+
+
+;
+;/////////////////////////////////////////////////////////////////////////
+;// //
+;// Logon Messages Follow //
+;// //
+;// //
+;/////////////////////////////////////////////////////////////////////////
+;
+
+;////////////////////////////////////////////////
+;//
+;// Type: SE_ADT_SUCCESSFUL_LOGON
+;//
+;// Parameter Strings:
+;//
+;// String1 - Description
+;//
+
+MessageId=0x0001
+ SymbolicName=SE_ADT_SUCCESSFUL_LOGON
+ Language=English
+Successful Logon -
+ Successful Logon
+.
+
+
+
+;////////////////////////////////////////////////
+;//
+;// Type: SE_ADT_UNSUCCESSFUL_LOGON
+;//
+;//
+;// Parameter Strings:
+;//
+;// String1 - Description
+;//
+;//
+;//
+
+MessageId=0x0002
+ SymbolicName=SE_ADT_UNSUCCESSFUL_LOGON
+ Language=English
+Failed Logon -
+ Unsuccessful Logon
+.
+
+
+;////////////////////////////////////////////////
+;//
+;// Type: SE_ADT_SUCCESSFUL_OBJECT_OPEN
+;//
+;//
+;// Parameter Strings:
+;//
+;// String1 - Description
+;//
+;//
+;//
+
+MessageId=0x0003
+ SymbolicName=SE_ADT_SUCCESSFUL_OBJECT_OPEN
+ Language=English
+Successful Object Open -
+ Successful Object Open
+.
+
+
+
+;////////////////////////////////////////////////
+;//
+;// Type: SE_ADT_UNSUCCESSFUL_OBJECT_OPEN
+;//
+;//
+;// Parameter Strings:
+;//
+;// String1 - Description
+;//
+;//
+;//
+
+MessageId=0x0004
+ SymbolicName=SE_ADT_UNSUCCESSFUL_OBJECT_OPEN
+ Language=English
+Unsuccessful Object Open -
+ Unsuccessful Object Open
+.
+
+
+;////////////////////////////////////////////////
+;//
+;// Type: SE_ADT_SYSTEM_RESTART
+;//
+;//
+;// Parameter Strings:
+;//
+;// String1 - Description
+;//
+;//
+
+MessageId=0x0005
+ SymbolicName=SE_ADT_SYSTEM_RESTART
+ Language=English
+System has been rebooted -
+ System has been rebooted
+.
+
+
+
+;////////////////////////////////////////////////
+;//
+;// Type: SE_ADT_HANDLE_ALLOCATION
+;//
+;//
+;// Parameter Strings:
+;//
+;// String1 - Description
+;//
+;//
+
+MessageId=0x0006
+ SymbolicName=SE_ADT_HANDLE_ALLOCATION
+ Language=English
+A handle has been allocated
+ A handle has been allocated
+.
+
+
+;////////////////////////////////////////////////
+;//
+;// Type: SE_ADT_SUCC_PRIV_SERVICE
+;//
+;//
+;// Parameter Strings:
+;//
+;// String1 - Description
+;//
+
+MessageId=0x0007
+ SymbolicName=SE_ADT_SUCC_PRIV_SERVICE
+ Language=English
+A privilege was successfully used in a system service
+ A privilege was successfully used in a system service
+
+.
+
+
+;////////////////////////////////////////////////
+;//
+;// Type: SE_ADT_FAILED_PRIV_SERVICE
+;//
+;//
+;// Parameter Strings:
+;//
+;//
+;//
+
+MessageId=0x0008
+ SymbolicName=SE_ADT_FAILED_PRIV_SERVICE
+ Language=English
+A privilege check in a system service failed
+ A privilege check in a system service failed
+
+.
+
+
+
+;////////////////////////////////////////////////
+;//
+;// Type: SE_ADT_SUCC_PRIV_OBJECT
+;//
+;//
+;// Parameter Strings:
+;//
+;// String1 - Description
+;//
+;//
+
+MessageId=0x0009
+ SymbolicName=SE_ADT_SUCC_PRIV_OBJECT
+ Language=English
+An attempt to access a privileged object succeeded
+ An attempt to access a privileged object succeeded
+
+.
+
+
+;////////////////////////////////////////////////
+;//
+;// Type: SE_ADT_FAILED_PRIV_OBJECT
+;//
+;//
+;// Parameter Strings:
+;//
+;// String1 - Description
+;//
+;//
+
+MessageId=0x000A
+ SymbolicName=SE_ADT_FAILED_PRIV_OBJECT
+ Language=English
+An attempt to access a privileged object failed
+ An attempt to access a privileged object failed
+
+.
+
+
+
+;////////////////////////////////////////////////
+;//
+;// Type: SE_ADT_SUCC_PRIV_SERVICE
+;//
+;//
+;// Parameter Strings:
+;//
+;// String1 - Description
+;//
+;//
+
+MessageId=0x000B
+ SymbolicName=SE_ADT_SUCC_PRIV_SERVICE
+ Language=English
+An attempt to execute a privileged service succeeded.
+ An attempt to execute a privileged service succeeded.
+
+.
+
+
+
+;////////////////////////////////////////////////
+;//
+;// Type: SE_ADT_OBJECT_CLOSE
+;//
+;//
+;// Parameter Strings:
+;//
+;// String1 - Description
+;//
+;//
+
+MessageId=0x000C
+ SymbolicName=SE_ADT_OBJECT_CLOSE
+ Language=English
+An object was closed
+ An object was closed
+
+.
+
+
+
+
+;////////////////////////////////////////////////
+;//
+;// Type: SE_ADT_SUCC_OBJECT_REFERENCE
+;//
+;//
+;// Parameter Strings:
+;//
+;// String1 - Description
+;//
+;//
+
+MessageId=0x000D
+ SymbolicName=SE_ADT_SUCC_OBJECT_REFERENCE
+ Language=English
+A named object was accessed but no handle was created
+ A named object was accessed but no handle was created
+
+.
+
+
+
+;////////////////////////////////////////////////
+;//
+;// Type: SE_ADT_FAILED_OBJECT_REFERENCE
+;//
+;//
+;// Parameter Strings:
+;//
+;// String1 - Description
+;//
+;//
+
+MessageId=0x000E
+ SymbolicName=SE_ADT_FAILED_OBJECT_REFERENCE
+ Language=English
+An attempt to reference a named object was denied
+ An attempt to reference a named object was denied
+
+.
+
+
+
+;////////////////////////////////////////////////
+;//
+;// Type: SE_ADT_SHUTDOWN
+;//
+;//
+;// Parameter Strings:
+;//
+;// String1 - Description
+;//
+;//
+
+MessageId=0x000E
+ SymbolicName=SE_ADT_SHUTDOWN
+ Language=English
+The system was shut down.
+ The system was shut down.
+
+.
+
+
+;/*lint +e767 */ // Resume checking for different macro definitions // winnt
+;
+;
+;#endif // _MSAUDITT_
diff --git a/private/ntos/seaudit/msauditt/mstmp.c b/private/ntos/seaudit/msauditt/mstmp.c
new file mode 100644
index 000000000..e7771438e
--- /dev/null
+++ b/private/ntos/seaudit/msauditt/mstmp.c
@@ -0,0 +1,55 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ tmp.c
+
+Abstract:
+
+ Temporary (unnecessary) DLL entry point routine.
+
+
+ The entry in this file is a bit of a hack. The code isn't
+ needed, but our linker doesn't know how to build a dll with data only.
+ When MikeOl gets this fixed, we can remove this obligatory source
+ file.
+
+
+Author:
+
+ Jim Kelly 24-Mar-1992
+
+Revision History:
+
+--*/
+
+
+#include <nt.h>
+
+BOOLEAN
+MsAuditTDummyEntry( VOID )
+
+/*++
+
+Routine Description:
+
+ This routine gets called when this DLL is loaded by the loader.
+ It does nothing and wouldn't be needed if the linker worked
+ correctly.
+
+Arguments:
+
+ None.
+
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ return TRUE;
+}
diff --git a/private/ntos/seaudit/msauditt/sources b/private/ntos/seaudit/msauditt/sources
new file mode 100644
index 000000000..e51a4c07c
--- /dev/null
+++ b/private/ntos/seaudit/msauditt/sources
@@ -0,0 +1,53 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=ntos
+MINORCOMP=msauditt
+
+TARGETNAME=msauditt
+TARGETPATH=\nt\public\sdk\lib
+
+TARGETLIBS=
+
+TARGETTYPE=DYNLINK
+
+#
+# The following entry information is a bit of a hack. The code isn't
+# needed, but our linker doesn't know how to build a dll with data only.
+# When MikeOl gets this fixed, we can remove the code and this obligatory
+# entry and base information. By the way, the base choice is just one I
+# know isn't used elsewhere in the system.
+#
+
+DLLBASE=@\NT\PUBLIC\SDK\LIB\coffbase.txt,msauditt
+
+DLLENTRY=MsAuditTDummyEntry
+
+
+INCLUDES=.
+
+SOURCES= audit.rc \
+ mstmp.c
+
+UMLIBS=
diff --git a/private/ntos/seaudit/msobjs/audit.rc b/private/ntos/seaudit/msobjs/audit.rc
new file mode 100644
index 000000000..54937ffee
--- /dev/null
+++ b/private/ntos/seaudit/msobjs/audit.rc
@@ -0,0 +1,14 @@
+#include <windows.h>
+
+1 11 MSG00001.bin
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "System object audit names"
+#define VER_INTERNALNAME_STR "msobjs.dll"
+#define VER_ORIGINALFILENAME_STR "msobjs.dll"
+
+#include "common.ver"
+
diff --git a/private/ntos/seaudit/msobjs/makefile b/private/ntos/seaudit/msobjs/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/ntos/seaudit/msobjs/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/ntos/seaudit/msobjs/makefile.inc b/private/ntos/seaudit/msobjs/makefile.inc
new file mode 100644
index 000000000..aec6a9639
--- /dev/null
+++ b/private/ntos/seaudit/msobjs/makefile.inc
@@ -0,0 +1,4 @@
+$(NTTARGETFILE0): msobjs.rc msg00001.bin
+
+msobjs.rc msg00001.bin: msobjs.mc
+ mc -v -r . -h $(_NTROOT)\public\sdk\inc\ msobjs.mc
diff --git a/private/ntos/seaudit/msobjs/msobjs.def b/private/ntos/seaudit/msobjs/msobjs.def
new file mode 100644
index 000000000..2bdaca27c
--- /dev/null
+++ b/private/ntos/seaudit/msobjs/msobjs.def
@@ -0,0 +1,3 @@
+LIBRARY msaudit
+
+DESCRIPTION 'Object access names for auditing'
diff --git a/private/ntos/seaudit/msobjs/msobjs.mc b/private/ntos/seaudit/msobjs/msobjs.mc
new file mode 100644
index 000000000..5ca7116e5
--- /dev/null
+++ b/private/ntos/seaudit/msobjs/msobjs.mc
@@ -0,0 +1,1908 @@
+;/*++ BUILD Version: 0001 // Increment this if a change has global effects
+;
+;Copyright (c) 1991 Microsoft Corporation
+;
+;Module Name:
+;
+; msobjs.mc
+;
+;Abstract:
+;
+; Constant definitions for the NT system-defined object access
+; types as we want them displayed in the event viewer for Auditing.
+;
+;
+;
+; ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
+; ! !
+; ! Note that this is a PARAMETER MESSAGE FILE from the event viewer's !
+; ! perspective, and so no messages with an ID lower than 0x1000 should !
+; ! be defined here. !
+; ! !
+; ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
+;
+;
+; Please add new object-specific types at the end of this file...
+;
+;
+;Author:
+;
+; Jim Kelly (JimK) 14-Oct-1992
+;
+;Revision History:
+;
+;Notes:
+;
+; The .h and .res forms of this file are generated from the .mc
+; form of the file (private\ntos\seaudit\msobjs\msobjs.mc). Please make
+; all changes to the .mc form of the file.
+;
+;
+;
+;--*/
+;
+;#ifndef _MSOBJS_
+;#define _MSOBJS_
+;
+;/*lint -e767 */ // Don't complain about different definitions // winnt
+
+
+MessageIdTypedef=ULONG
+
+SeverityNames=(None=0x0)
+
+FacilityNames=(None=0x0)
+
+
+
+MessageId=0x600
+ Language=English
+Unused message ID
+.
+;// Message ID 600 is unused - just used to flush out the diagram
+
+
+;
+;//////////////////////////////////////////////////////////////////////////////
+;//////////////////////////////////////////////////////////////////////////////
+;//////////////////////////////////////////////////////////////////////////////
+;//////////////////////////////////////////////////////////////////////////////
+;//////////////////////////////////////////////////////////////////////////////
+;//////////////////////////////////////////////////////////////////////////////
+;//////////////////////////////////////////////////////////////////////////////
+;//////////////////////////////////////////////////////////////////////////////
+;//////////////////////////////////////////////////////////////////////////////
+;//////////////////////////////////////////////////////////////////////////////
+;// //
+;// //
+;// WELL KNOWN ACCESS TYPE NAMES //
+;// //
+;// Must be below 0x1000 //
+;// //
+;// //
+;//////////////////////////////////////////////////////////////////////////////
+;//////////////////////////////////////////////////////////////////////////////
+;//////////////////////////////////////////////////////////////////////////////
+;//////////////////////////////////////////////////////////////////////////////
+;//////////////////////////////////////////////////////////////////////////////
+;//////////////////////////////////////////////////////////////////////////////
+;//////////////////////////////////////////////////////////////////////////////
+;//////////////////////////////////////////////////////////////////////////////
+;//////////////////////////////////////////////////////////////////////////////
+;//////////////////////////////////////////////////////////////////////////////
+
+;////////////////////////////////////////////////
+;//
+;// Access Type = DELETE
+;//
+
+MessageId=0x0601
+ SymbolicName=SE_ACCESS_NAME_DELETE
+ Language=English
+DELETE
+.
+
+
+;////////////////////////////////////////////////
+;//
+;// Access Type = READ_CONTROL
+;//
+
+MessageId=0x0602
+ SymbolicName=SE_ACCESS_NAME_READ_CONTROL
+ Language=English
+READ_CONTROL
+.
+
+
+;////////////////////////////////////////////////
+;//
+;// Access Type = WRITE_DAC
+;//
+
+MessageId=0x0603
+ SymbolicName=SE_ACCESS_NAME_WRITE_DAC
+ Language=English
+WRITE_DAC
+.
+
+
+;////////////////////////////////////////////////
+;//
+;// Access Type = WRITE_OWNER
+;//
+
+MessageId=0x0604
+ SymbolicName=SE_ACCESS_NAME_WRITE_OWNER
+ Language=English
+WRITE_OWNER
+.
+
+
+;////////////////////////////////////////////////
+;//
+;// Access Type = SYNCHRONIZE
+;//
+
+MessageId=0x0605
+ SymbolicName=SE_ACCESS_NAME_SYNCHRONIZE
+ Language=English
+SYNCHRONIZE
+.
+
+
+;////////////////////////////////////////////////
+;//
+;// Access Type = ACCESS_SYSTEM_SECURITY
+;//
+
+MessageId=0x0606
+ SymbolicName=SE_ACCESS_NAME_ACCESS_SYS_SEC
+ Language=English
+ACCESS_SYS_SEC
+.
+
+;////////////////////////////////////////////////
+;//
+;// Access Type = MAXIMUM_ALLOWED
+;//
+
+MessageId=0x0607
+ SymbolicName=SE_ACCESS_NAME_MAXIMUM_ALLOWED
+ Language=English
+MAX_ALLOWED
+.
+
+
+
+;
+;//////////////////////////////////////////////////////////////////////////////
+;// //
+;// //
+;// Names to use when specific access //
+;// names can not be located //
+;// //
+;// Must be below 0x1000 //
+;// //
+;// //
+;//////////////////////////////////////////////////////////////////////////////
+
+;////////////////////////////////////////////////
+;//
+;// Access Type = Specific access, bits 0 - 15
+;//
+
+MessageId=0x0610
+ SymbolicName=SE_ACCESS_NAME_SPECIFIC_0
+ Language=English
+Unknown specific access (bit 0)
+.
+
+
+MessageId=0x0611
+ SymbolicName=SE_ACCESS_NAME_SPECIFIC_1
+ Language=English
+Unknown specific access (bit 1)
+.
+
+
+MessageId=0x0612
+ SymbolicName=SE_ACCESS_NAME_SPECIFIC_2
+ Language=English
+Unknown specific access (bit 2)
+.
+
+
+MessageId=0x0613
+ SymbolicName=SE_ACCESS_NAME_SPECIFIC_3
+ Language=English
+Unknown specific access (bit 3)
+.
+
+
+MessageId=0x0614
+ SymbolicName=SE_ACCESS_NAME_SPECIFIC_4
+ Language=English
+Unknown specific access (bit 4)
+.
+
+
+MessageId=0x0615
+ SymbolicName=SE_ACCESS_NAME_SPECIFIC_5
+ Language=English
+Unknown specific access (bit 5)
+.
+
+
+MessageId=0x0616
+ SymbolicName=SE_ACCESS_NAME_SPECIFIC_6
+ Language=English
+Unknown specific access (bit 6)
+.
+
+
+MessageId=0x0617
+ SymbolicName=SE_ACCESS_NAME_SPECIFIC_7
+ Language=English
+Unknown specific access (bit 7)
+.
+
+
+MessageId=0x0618
+ SymbolicName=SE_ACCESS_NAME_SPECIFIC_8
+ Language=English
+Unknown specific access (bit 8)
+.
+
+
+MessageId=0x0619
+ SymbolicName=SE_ACCESS_NAME_SPECIFIC_9
+ Language=English
+Unknown specific access (bit 9)
+.
+
+
+MessageId=0x061A
+ SymbolicName=SE_ACCESS_NAME_SPECIFIC_10
+ Language=English
+Unknown specific access (bit 10)
+.
+
+
+MessageId=0x061B
+ SymbolicName=SE_ACCESS_NAME_SPECIFIC_11
+ Language=English
+Unknown specific access (bit 11)
+.
+
+
+MessageId=0x061C
+ SymbolicName=SE_ACCESS_NAME_SPECIFIC_12
+ Language=English
+Unknown specific access (bit 12)
+.
+
+
+MessageId=0x061D
+ SymbolicName=SE_ACCESS_NAME_SPECIFIC_13
+ Language=English
+Unknown specific access (bit 13)
+.
+
+
+MessageId=0x061E
+ SymbolicName=SE_ACCESS_NAME_SPECIFIC_14
+ Language=English
+Unknown specific access (bit 14)
+.
+
+
+MessageId=0x061F
+ SymbolicName=SE_ACCESS_NAME_SPECIFIC_15
+ Language=English
+Unknown specific access (bit 15)
+.
+
+
+
+
+
+
+
+;
+;//////////////////////////////////////////////////////////////////////////////
+;// //
+;// //
+;// Privilege names as we would like //
+;// them displayed for auditing //
+;// //
+;// //
+;// //
+;// NOTE: Eventually we will need a way to extend this mechanism to allow //
+;// for ISV and end-user defined privileges. One way would be to //
+;// stick a mapping from source/privilege name to parameter message //
+;// file offset in the registry. This is ugly and I don't like it, //
+;// but it works. Something else would be prefereable. //
+;// //
+;// THIS IS A BIT OF A HACK RIGHT NOW. IT IS BASED UPON THE //
+;// ASSUMPTION THAT ALL THE PRIVILEGES ARE WELL-KNOWN AND THAT //
+;// THEIR VALUE ARE ALL CONTIGUOUS. //
+;// //
+;// //
+;// //
+;// //
+;// //
+;//////////////////////////////////////////////////////////////////////////////
+
+
+MessageId=0x0641
+ SymbolicName=SE_ADT_PRIV_BASE
+ Language=English
+Not used
+.
+
+MessageId=0x0643
+ SymbolicName=SE_ADT_PRIV_3
+ Language=English
+Assign Primary Token Privilege
+.
+MessageId=0x0644
+ SymbolicName=SE_ADT_PRIV_4
+ Language=English
+Lock Memory Privilege
+.
+MessageId=0x0645
+ SymbolicName=SE_ADT_PRIV_5
+ Language=English
+Increase Memory Quota Privilege
+.
+MessageId=0x0646
+ SymbolicName=SE_ADT_PRIV_6
+ Language=English
+Unsolicited Input Privilege
+.
+MessageId=0x0647
+ SymbolicName=SE_ADT_PRIV_7
+ Language=English
+Trusted Computer Base Privilege
+.
+MessageId=0x0648
+ SymbolicName=SE_ADT_PRIV_8
+ Language=English
+Security Privilege
+.
+MessageId=0x0649
+ SymbolicName=SE_ADT_PRIV_9
+ Language=English
+Take Ownership Privilege
+.
+MessageId=0x064A
+ SymbolicName=SE_ADT_PRIV_10
+ Language=English
+Load/Unload Driver Privilege
+.
+MessageId=0x064B
+ SymbolicName=SE_ADT_PRIV_11
+ Language=English
+Profile System Privilege
+.
+MessageId=0x064C
+ SymbolicName=SE_ADT_PRIV_12
+ Language=English
+Set System Time Privilege
+.
+MessageId=0x064D
+ SymbolicName=SE_ADT_PRIV_13
+ Language=English
+Profile Single Process Privilege
+.
+MessageId=0x064E
+ SymbolicName=SE_ADT_PRIV_14
+ Language=English
+Increment Base Priority Privilege
+.
+MessageId=0x064F
+ SymbolicName=SE_ADT_PRIV_15
+ Language=English
+Create Pagefile Privilege
+.
+MessageId=0x0650
+ SymbolicName=SE_ADT_PRIV_16
+ Language=English
+Create Permanent Object Privilege
+.
+MessageId=0x0651
+ SymbolicName=SE_ADT_PRIV_17
+ Language=English
+Backup Privilege
+.
+MessageId=0x0652
+ SymbolicName=SE_ADT_PRIV_18
+ Language=English
+Restore From Backup Privilege
+.
+MessageId=0x0653
+ SymbolicName=SE_ADT_PRIV_19
+ Language=English
+Shutdown System Privilege
+.
+MessageId=0x0654
+ SymbolicName=SE_ADT_PRIV_20
+ Language=English
+Debug Privilege
+.
+MessageId=0x0655
+ SymbolicName=SE_ADT_PRIV_21
+ Language=English
+View or Change Audit Log Privilege
+.
+MessageId=0x0656
+ SymbolicName=SE_ADT_PRIV_22
+ Language=English
+Change Hardware Environment Privilege
+.
+MessageId=0x0657
+ SymbolicName=SE_ADT_PRIV_23
+ Language=English
+Change Notify (and Traverse) Privilege
+.
+MessageId=0x0658
+ SymbolicName=SE_ADT_PRIV_24
+ Language=English
+Remotely Shut System Down Privilege
+.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+;
+;//////////////////////////////////////////////////////////////////////////////
+;// //
+;// //
+;// Executive object access types as //
+;// we would like them displayed //
+;// for auditing //
+;// //
+;// Executive objects are: //
+;// //
+;// Channel //
+;// Device //
+;// Directory //
+;// Event //
+;// EventPair //
+;// File //
+;// IoCompletion //
+;// Key //
+;// Mutant //
+;// Port //
+;// Process //
+;// Profile //
+;// Section //
+;// Semaphore //
+;// SymbolicLink //
+;// Thread //
+;// Timer //
+;// Token //
+;// Type //
+;// //
+;// //
+;// Note that there are other kernel objects, but they //
+;// are not visible outside of the executive and are so //
+;// not subject to auditing. These objects include //
+;// //
+;// Adaptor //
+;// Controller //
+;// Driver //
+;// //
+;// //
+;// //
+;//////////////////////////////////////////////////////////////////////////////
+
+
+
+
+
+;//
+;// DEVICE object-specific access types
+;//
+
+MessageId=0x1100
+ SymbolicName=MS_DEVICE_ACCESS_BIT_0
+ Language=English
+Device Access Bit0
+.
+MessageId=0x1101
+ SymbolicName=MS_DEVICE_ACCESS_BIT_1
+ Language=English
+Device Access Bit 1
+.
+MessageId=0x1102
+ SymbolicName=MS_DEVICE_ACCESS_BIT_2
+ Language=English
+Device Access Bit 2
+.
+MessageId=0x1103
+ SymbolicName=MS_DEVICE_ACCESS_BIT_3
+ Language=English
+Device Access Bit 3
+.
+MessageId=0x1104
+ SymbolicName=MS_DEVICE_ACCESS_BIT_4
+ Language=English
+Device Access Bit 4
+.
+MessageId=0x1105
+ SymbolicName=MS_DEVICE_ACCESS_BIT_5
+ Language=English
+Device Access Bit 5
+.
+MessageId=0x1106
+ SymbolicName=MS_DEVICE_ACCESS_BIT_6
+ Language=English
+Device Access Bit 6
+.
+MessageId=0x1107
+ SymbolicName=MS_DEVICE_ACCESS_BIT_7
+ Language=English
+Device Access Bit 7
+.
+MessageId=0x1108
+ SymbolicName=MS_DEVICE_ACCESS_BIT_8
+ Language=English
+Device Access Bit 8
+.
+
+
+
+;//
+;// object DIRECTORY object-specific access types
+;//
+
+MessageId=0x1110
+ SymbolicName=MS_OBJECT_DIR_ACCESS_BIT_0
+ Language=English
+Query directory
+.
+MessageId=0x1111
+ SymbolicName=MS_OBJECT_DIR_ACCESS_BIT_1
+ Language=English
+Traverse
+.
+MessageId=0x1112
+ SymbolicName=MS_OBJECT_DIR_ACCESS_BIT_2
+ Language=English
+Create object in directory
+.
+MessageId=0x1113
+ SymbolicName=MS_OBJECT_DIR_ACCESS_BIT_3
+ Language=English
+Create sub-directory
+.
+
+
+;//
+;// EVENT object-specific access types
+;//
+
+MessageId=0x1120
+ SymbolicName=MS_EVENT_ACCESS_BIT_0
+ Language=English
+Query event state
+.
+MessageId=0x1121
+ SymbolicName=MS_EVENT_ACCESS_BIT_1
+ Language=English
+Modify event state
+.
+
+
+
+;//
+;// EVENT-PAIR object-specific access types
+;//
+
+;//
+;// Event pairs have no object-type-specific access bits.
+;// they use synchronize.
+;//
+;// reserve 0x1130 for future use and continuity
+;//
+
+
+;//
+;// File-specific access types
+;// (these are funny because they sorta hafta take directories
+;// and named pipes into account as well).
+;//
+
+MessageId=0x1140
+ SymbolicName=MS_FILE_ACCESS_BIT_0
+ Language=English
+ReadData (or ListDirectory)
+.
+MessageId=0x1141
+ SymbolicName=MS_FILE_ACCESS_BIT_1
+ Language=English
+WriteData (or AddFile)
+.
+MessageId=0x1142
+ SymbolicName=MS_FILE_ACCESS_BIT_2
+ Language=English
+AppendData (or AddSubdirectory or CreatePipeInstance)
+.
+MessageId=0x1143
+ SymbolicName=MS_FILE_ACCESS_BIT_3
+ Language=English
+ReadEA
+.
+MessageId=0x1144
+ SymbolicName=MS_FILE_ACCESS_BIT_4
+ Language=English
+WriteEA
+.
+MessageId=0x1145
+ SymbolicName=MS_FILE_ACCESS_BIT_5
+ Language=English
+Execute/Traverse
+.
+MessageId=0x1146
+ SymbolicName=MS_FILE_ACCESS_BIT_6
+ Language=English
+DeleteChild
+.
+MessageId=0x1147
+ SymbolicName=MS_FILE_ACCESS_BIT_7
+ Language=English
+ReadAttributes
+.
+MessageId=0x1148
+ SymbolicName=MS_FILE_ACCESS_BIT_8
+ Language=English
+WriteAttributes
+.
+
+
+
+;//
+;// KEY object-specific access types
+;//
+
+MessageId=0x1150
+ SymbolicName=MS_KEY_ACCESS_BIT_0
+ Language=English
+Query key value
+.
+
+MessageId=0x1151
+ SymbolicName=MS_KEY_ACCESS_BIT_1
+ Language=English
+Set key value
+.
+
+MessageId=0x1152
+ SymbolicName=MS_KEY_ACCESS_BIT_2
+ Language=English
+Create sub-key
+.
+
+MessageId=0x1153
+ SymbolicName=MS_KEY_ACCESS_BIT_3
+ Language=English
+Enumerate sub-keys
+.
+
+MessageId=0x1154
+ SymbolicName=MS_KEY_ACCESS_BIT_4
+ Language=English
+Notify about changes to keys
+.
+
+MessageId=0x1155
+ SymbolicName=MS_KEY_ACCESS_BIT_5
+ Language=English
+Create Link
+.
+
+
+;//
+;// MUTANT object-specific access types
+;//
+
+MessageId=0x1160
+ SymbolicName=MS_MUTANT_ACCESS_BIT_0
+ Language=English
+Query mutant state
+.
+
+
+
+;//
+;// lpc PORT object-specific access types
+;//
+
+MessageId=0x1170
+ SymbolicName=MS_LPC_PORT_ACCESS_BIT_0
+ Language=English
+Communicate using port
+.
+
+
+
+;//
+;// Process object-specific access types
+;//
+
+MessageId=0x1180
+ SymbolicName=MS_PROCESS_ACCESS_BIT_0
+ Language=English
+Force process termination
+.
+MessageId=0x1181
+ SymbolicName=MS_PROCESS_ACCESS_BIT_1
+ Language=English
+Create new thread in process
+.
+MessageId=0x1182
+ SymbolicName=MS_PROCESS_ACCESS_BIT_2
+ Language=English
+Unused access bit
+.
+MessageId=0x1183
+ SymbolicName=MS_PROCESS_ACCESS_BIT_3
+ Language=English
+Perform virtual memory operation
+.
+MessageId=0x1184
+ SymbolicName=MS_PROCESS_ACCESS_BIT_4
+ Language=English
+Read from process memory
+.
+MessageId=0x1185
+ SymbolicName=MS_PROCESS_ACCESS_BIT_5
+ Language=English
+Write to process memory
+.
+MessageId=0x1186
+ SymbolicName=MS_PROCESS_ACCESS_BIT_6
+ Language=English
+Duplicate handle into or out of process
+.
+MessageId=0x1187
+ SymbolicName=MS_PROCESS_ACCESS_BIT_7
+ Language=English
+Create a subprocess of process
+.
+MessageId=0x1188
+ SymbolicName=MS_PROCESS_ACCESS_BIT_8
+ Language=English
+Set process quotas
+.
+MessageId=0x1189
+ SymbolicName=MS_PROCESS_ACCESS_BIT_9
+ Language=English
+Set process information
+.
+MessageId=0x118A
+ SymbolicName=MS_PROCESS_ACCESS_BIT_A
+ Language=English
+Query process information
+.
+MessageId=0x118B
+ SymbolicName=MS_PROCESS_ACCESS_BIT_B
+ Language=English
+Set process termination port
+.
+
+
+
+;//
+;// PROFILE object-specific access types
+;//
+
+MessageId=0x1190
+ SymbolicName=MS_PROFILE_ACCESS_BIT_0
+ Language=English
+Control profile
+.
+
+
+;//
+;// SECTION object-specific access types
+;//
+
+MessageId=0x11A0
+ SymbolicName=MS_SECTION_ACCESS_BIT_0
+ Language=English
+Query section state
+.
+MessageId=0x11A1
+ SymbolicName=MS_SECTION_ACCESS_BIT_1
+ Language=English
+Map section for write
+.
+MessageId=0x11A2
+ SymbolicName=MS_SECTION_ACCESS_BIT_2
+ Language=English
+Map section for read
+.
+MessageId=0x11A3
+ SymbolicName=MS_SECTION_ACCESS_BIT_3
+ Language=English
+Map section for execute
+.
+MessageId=0x11A4
+ SymbolicName=MS_SECTION_ACCESS_BIT_4
+ Language=English
+Extend size
+.
+
+
+
+;//
+;// SEMAPHORE object-specific access types
+;//
+
+MessageId=0x11B0
+ SymbolicName=MS_SEMAPHORE_ACCESS_BIT_0
+ Language=English
+Query semaphore state
+.
+
+MessageId=0x11B1
+ SymbolicName=MS_SEMAPHORE_ACCESS_BIT_1
+ Language=English
+Modify semaphore state
+.
+
+
+;//
+;// SymbolicLink object-specific access types
+;//
+
+MessageId=0x11C0
+ SymbolicName=MS_SYMB_LINK_ACCESS_BIT_0
+ Language=English
+Use symbolic link
+.
+
+
+
+
+
+;//
+;// Thread object-specific access types
+;//
+
+MessageId=0x11D0
+ SymbolicName=MS_THREAD_ACCESS_BIT_0
+ Language=English
+Force thread termination
+.
+MessageId=0x11D1
+ SymbolicName=MS_THREAD_ACCESS_BIT_1
+ Language=English
+Suspend or resume thread
+.
+MessageId=0x11D2
+ SymbolicName=MS_THREAD_ACCESS_BIT_2
+ Language=English
+Send an alert to thread
+.
+MessageId=0x11D3
+ SymbolicName=MS_THREAD_ACCESS_BIT_3
+ Language=English
+Get thread context
+.
+MessageId=0x11D4
+ SymbolicName=MS_THREAD_ACCESS_BIT_4
+ Language=English
+Set thread context
+.
+MessageId=0x11D5
+ SymbolicName=MS_THREAD_ACCESS_BIT_5
+ Language=English
+Set thread information
+.
+MessageId=0x11D6
+ SymbolicName=MS_THREAD_ACCESS_BIT_6
+ Language=English
+Query thread information
+.
+MessageId=0x11D7
+ SymbolicName=MS_THREAD_ACCESS_BIT_7
+ Language=English
+Assign a token to the thread
+.
+MessageId=0x11D8
+ SymbolicName=MS_THREAD_ACCESS_BIT_8
+ Language=English
+Cause thread to directly impersonate another thread
+.
+MessageId=0x11D9
+ SymbolicName=MS_THREAD_ACCESS_BIT_9
+ Language=English
+Directly impersonate this thread
+.
+
+
+
+
+;//
+;// TIMER object-specific access types
+;//
+
+MessageId=0x11E0
+ SymbolicName=MS_TIMER_ACCESS_BIT_0
+ Language=English
+Query timer state
+.
+MessageId=0x11E1
+ SymbolicName=MS_TIMER_ACCESS_BIT_1
+ Language=English
+Modify timer state
+.
+
+
+;//
+;// Token-specific access types
+;//
+
+MessageId=0x11F0
+ SymbolicName=MS_TOKEN_ACCESS_BIT_0
+ Language=English
+AssignAsPrimary
+.
+MessageId=0x11F1
+ SymbolicName=MS_TOKEN_ACCESS_BIT_1
+ Language=English
+Duplicate
+.
+MessageId=0x11F2
+ SymbolicName=MS_TOKEN_ACCESS_BIT_2
+ Language=English
+Impersonate
+.
+MessageId=0x11F3
+ SymbolicName=MS_TOKEN_ACCESS_BIT_3
+ Language=English
+Query
+.
+MessageId=0x11F4
+ SymbolicName=MS_TOKEN_ACCESS_BIT_4
+ Language=English
+QuerySource
+.
+MessageId=0x11F5
+ SymbolicName=MS_TOKEN_ACCESS_BIT_5
+ Language=English
+AdjustPrivileges
+.
+MessageId=0x11F6
+ SymbolicName=MS_TOKEN_ACCESS_BIT_6
+ Language=English
+AdjustGroups
+.
+MessageId=0x11F7
+ SymbolicName=MS_TOKEN_ACCESS_BIT_7
+ Language=English
+AdjustDefaultDacl
+.
+
+
+
+;//
+;// OBJECT_TYPE object-specific access types
+;//
+
+MessageId=0x1200
+ SymbolicName=MS_OBJECT_TYPE_ACCESS_BIT_0
+ Language=English
+Create instance of object type
+.
+
+
+
+;//
+;// IoCompletion object-specific access types
+;//
+
+MessageId=0x1300
+ SymbolicName=MS_IO_COMPLETION_ACCESS_BIT_0
+ Language=English
+Query State
+.
+
+MessageId=0x1301
+ SymbolicName=MS_IO_COMPLETION_ACCESS_BIT_1
+ Language=English
+Modify State
+.
+
+
+
+;//
+;// CHANNEL object-specific access types
+;//
+
+MessageId=0x1400
+ SymbolicName=MS_CHANNEL_ACCESS_BIT_0
+ Language=English
+Channel read message
+.
+MessageId=0x1401
+ SymbolicName=MS_CHANNEL_ACCESS_BIT_1
+ Language=English
+Channel write message
+.
+MessageId=0x1402
+ SymbolicName=MS_CHANNEL_ACCESS_BIT_2
+ Language=English
+Channel query information
+.
+MessageId=0x1403
+ SymbolicName=MS_CHANNEL_ACCESS_BIT_3
+ Language=English
+Channel set information
+.
+
+
+
+;
+;//////////////////////////////////////////////////////////////////////////////
+;// //
+;// //
+;// Security Acount Manager Object Access //
+;// names as we would like them //
+;// displayed for auditing //
+;// //
+;// SAM objects are: //
+;// //
+;// SAM_SERVER //
+;// SAM_DOMAIN //
+;// SAM_GROUP //
+;// SAM_ALIAS //
+;// SAM_USER //
+;// //
+;// //
+;// //
+;//////////////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+;//
+;// SAM_SERVER object-specific access types
+;//
+
+MessageId=0x1500
+ SymbolicName=MS_SAM_SERVER_ACCESS_BIT_0
+ Language=English
+ConnectToServer
+.
+MessageId=0x1501
+ SymbolicName=MS_SAM_SERVER_ACCESS_BIT_1
+ Language=English
+ShutdownServer
+.
+MessageId=0x1502
+ SymbolicName=MS_SAM_SERVER_ACCESS_BIT_2
+ Language=English
+InitializeServer
+.
+MessageId=0x1503
+ SymbolicName=MS_SAM_SERVER_ACCESS_BIT_3
+ Language=English
+CreateDomain
+.
+MessageId=0x1504
+ SymbolicName=MS_SAM_SERVER_ACCESS_BIT_4
+ Language=English
+EnumerateDomains
+.
+MessageId=0x1505
+ SymbolicName=MS_SAM_SERVER_ACCESS_BIT_5
+ Language=English
+LookupDomain
+.
+
+
+
+
+;//
+;// SAM_DOMAIN object-specific access types
+;//
+
+MessageId=0x1510
+ SymbolicName=MS_SAM_DOMAIN_ACCESS_BIT_0
+ Language=English
+ReadPasswordParameters
+.
+MessageId=0x1511
+ SymbolicName=MS_SAM_DOMAIN_ACCESS_BIT_1
+ Language=English
+WritePasswordParameters
+.
+MessageId=0x1512
+ SymbolicName=MS_SAM_DOMAIN_ACCESS_BIT_2
+ Language=English
+ReadOtherParameters
+.
+MessageId=0x1513
+ SymbolicName=MS_SAM_DOMAIN_ACCESS_BIT_3
+ Language=English
+WriteOtherParameters
+.
+MessageId=0x1514
+ SymbolicName=MS_SAM_DOMAIN_ACCESS_BIT_4
+ Language=English
+CreateUser
+.
+MessageId=0x1515
+ SymbolicName=MS_SAM_DOMAIN_ACCESS_BIT_5
+ Language=English
+CreateGlobalGroup
+.
+MessageId=0x1516
+ SymbolicName=MS_SAM_DOMAIN_ACCESS_BIT_6
+ Language=English
+CreateLocalGroup
+.
+MessageId=0x1517
+ SymbolicName=MS_SAM_DOMAIN_ACCESS_BIT_7
+ Language=English
+GetLocalGroupMembership
+.
+MessageId=0x1518
+ SymbolicName=MS_SAM_DOMAIN_ACCESS_BIT_8
+ Language=English
+ListAccounts
+.
+MessageId=0x1519
+ SymbolicName=MS_SAM_DOMAIN_ACCESS_BIT_9
+ Language=English
+LookupIDs
+.
+MessageId=0x151A
+ SymbolicName=MS_SAM_DOMAIN_ACCESS_BIT_A
+ Language=English
+AdministerServer
+.
+
+
+
+
+;//
+;// SAM_GROUP (global) object-specific access types
+;//
+
+MessageId=0x1520
+ SymbolicName=MS_SAM_GLOBAL_GRP_ACCESS_BIT_0
+ Language=English
+ReadInformation
+.
+MessageId=0x1521
+ SymbolicName=MS_SAM_GLOBAL_GRP_ACCESS_BIT_1
+ Language=English
+WriteAccount
+.
+MessageId=0x1522
+ SymbolicName=MS_SAM_GLOBAL_GRP_ACCESS_BIT_2
+ Language=English
+AddMember
+.
+MessageId=0x1523
+ SymbolicName=MS_SAM_GLOBAL_GRP_ACCESS_BIT_3
+ Language=English
+RemoveMember
+.
+MessageId=0x1524
+ SymbolicName=MS_SAM_GLOBAL_GRP_ACCESS_BIT_4
+ Language=English
+ListMembers
+.
+
+
+
+
+;//
+;// SAM_ALIAS (local group) object-specific access types
+;//
+
+MessageId=0x1530
+ SymbolicName=MS_SAM_LOCAL_GRP_ACCESS_BIT_0
+ Language=English
+AddMember
+.
+MessageId=0x1531
+ SymbolicName=MS_SAM_LOCAL_GRP_ACCESS_BIT_1
+ Language=English
+RemoveMember
+.
+MessageId=0x1532
+ SymbolicName=MS_SAM_LOCAL_GRP_ACCESS_BIT_2
+ Language=English
+ListMembers
+.
+MessageId=0x1533
+ SymbolicName=MS_SAM_LOCAL_GRP_ACCESS_BIT_3
+ Language=English
+ReadInformation
+.
+MessageId=0x1534
+ SymbolicName=MS_SAM_LOCAL_GRP_ACCESS_BIT_4
+ Language=English
+WriteAccount
+.
+
+
+
+
+;//
+;// SAM_USER object-specific access types
+;//
+
+MessageId=0x1540
+ SymbolicName=MS_SAM_USER_ACCESS_BIT_0
+ Language=English
+ReadGeneralInformation
+.
+MessageId=0x1541
+ SymbolicName=MS_SAM_USER_ACCESS_BIT_1
+ Language=English
+ReadPreferences
+.
+MessageId=0x1542
+ SymbolicName=MS_SAM_USER_ACCESS_BIT_2
+ Language=English
+WritePreferences
+.
+MessageId=0x1543
+ SymbolicName=MS_SAM_USER_ACCESS_BIT_3
+ Language=English
+ReadLogon
+.
+MessageId=0x1544
+ SymbolicName=MS_SAM_USER_ACCESS_BIT_4
+ Language=English
+ReadAccount
+.
+MessageId=0x1545
+ SymbolicName=MS_SAM_USER_ACCESS_BIT_5
+ Language=English
+WriteAccount
+.
+MessageId=0x1546
+ SymbolicName=MS_SAM_USER_ACCESS_BIT_6
+ Language=English
+ChangePassword (with knowledge of old password)
+.
+MessageId=0x1547
+ SymbolicName=MS_SAM_USER_ACCESS_BIT_7
+ Language=English
+SetPassword (without knowledge of old password)
+.
+MessageId=0x1548
+ SymbolicName=MS_SAM_USER_ACCESS_BIT_8
+ Language=English
+ListGroups
+.
+MessageId=0x1549
+ SymbolicName=MS_SAM_USER_ACCESS_BIT_9
+ Language=English
+ReadGroupMembership
+.
+MessageId=0x154A
+ SymbolicName=MS_SAM_USER_ACCESS_BIT_A
+ Language=English
+ChangeGroupMembership
+.
+
+
+
+
+
+;
+;//////////////////////////////////////////////////////////////////////////////
+;// //
+;// //
+;// Local Security Authority Object Access //
+;// names as we would like them //
+;// displayed for auditing //
+;// //
+;// LSA objects are: //
+;// //
+;// PolicyObject //
+;// SecretObject //
+;// TrustedDomainObject //
+;// UserAccountObject //
+;// //
+;// //
+;// //
+;//////////////////////////////////////////////////////////////////////////////
+
+
+
+;//
+;// lsa POLICY object-specific access types
+;//
+
+MessageId=0x1600
+ SymbolicName=MS_LSA_POLICY_ACCESS_BIT_0
+ Language=English
+View non-sensitive policy information
+.
+MessageId=0x1601
+ SymbolicName=MS_LSA_POLICY_ACCESS_BIT_1
+ Language=English
+View system audit requirements
+.
+MessageId=0x1602
+ SymbolicName=MS_LSA_POLICY_ACCESS_BIT_2
+ Language=English
+Get sensitive policy information
+.
+MessageId=0x1603
+ SymbolicName=MS_LSA_POLICY_ACCESS_BIT_3
+ Language=English
+Modify domain trust relationships
+.
+MessageId=0x1604
+ SymbolicName=MS_LSA_POLICY_ACCESS_BIT_4
+ Language=English
+Create special accounts (for assignment of user rights)
+.
+MessageId=0x1605
+ SymbolicName=MS_LSA_POLICY_ACCESS_BIT_5
+ Language=English
+Create a secret object
+.
+MessageId=0x1606
+ SymbolicName=MS_LSA_POLICY_ACCESS_BIT_6
+ Language=English
+Create a privilege
+.
+MessageId=0x1607
+ SymbolicName=MS_LSA_POLICY_ACCESS_BIT_7
+ Language=English
+Set default quota limits
+.
+MessageId=0x1608
+ SymbolicName=MS_LSA_POLICY_ACCESS_BIT_8
+ Language=English
+Change system audit requirements
+.
+MessageId=0x1609
+ SymbolicName=MS_LSA_POLICY_ACCESS_BIT_9
+ Language=English
+Administer audit log attributes
+.
+MessageId=0x160A
+ SymbolicName=MS_LSA_POLICY_ACCESS_BIT_A
+ Language=English
+Enable/Disable LSA
+.
+MessageId=0x160B
+ SymbolicName=MS_LSA_POLICY_ACCESS_BIT_B
+ Language=English
+Lookup Names/SIDs
+.
+
+
+;//
+;// lsa SecretObject object-specific access types
+;//
+
+MessageId=0x1610
+ SymbolicName=MS_LSA_SECRET_ACCESS_BIT_0
+ Language=English
+Change secret value
+.
+MessageId=0x1611
+ SymbolicName=MS_LSA_SECRET_ACCESS_BIT_1
+ Language=English
+Query secret value
+.
+
+
+
+
+;//
+;// lsa TrustedDomainObject object-specific access types
+;//
+
+MessageId=0x1620
+ SymbolicName=MS_LSA_TRUST_ACCESS_BIT_0
+ Language=English
+Query trusted domain name/SID
+.
+MessageId=0x1621
+ SymbolicName=MS_LSA_TRUST_ACCESS_BIT_1
+ Language=English
+Retrieve the controllers in the trusted domain
+.
+MessageId=0x1622
+ SymbolicName=MS_LSA_TRUST_ACCESS_BIT_2
+ Language=English
+Change the controllers in the trusted domain
+.
+MessageId=0x1623
+ SymbolicName=MS_LSA_TRUST_ACCESS_BIT_3
+ Language=English
+Query the Posix ID offset assigned to the trusted domain
+.
+MessageId=0x1624
+ SymbolicName=MS_LSA_TRUST_ACCESS_BIT_4
+ Language=English
+Change the Posix ID offset assigned to the trusted domain
+.
+
+
+
+
+;//
+;// lsa UserAccount (privileged account) object-specific access types
+;//
+
+MessageId=0x1630
+ SymbolicName=MS_LSA_ACCOUNT_ACCESS_BIT_0
+ Language=English
+Query account information
+.
+MessageId=0x1631
+ SymbolicName=MS_LSA_ACCOUNT_ACCESS_BIT_1
+ Language=English
+Change privileges assigned to account
+.
+MessageId=0x1632
+ SymbolicName=MS_LSA_ACCOUNT_ACCESS_BIT_2
+ Language=English
+Change quotas assigned to account
+.
+MessageId=0x1633
+ SymbolicName=MS_LSA_ACCOUNT_ACCESS_BIT_3
+ Language=English
+Change logon capabilities assigned to account
+.
+
+
+
+
+;
+;//////////////////////////////////////////////////////////////////////////////
+;// //
+;// //
+;// Window Station Object Access //
+;// names as we would like them //
+;// displayed for auditing //
+;// //
+;// Window Station objects are: //
+;// //
+;// WindowStation //
+;// Desktop //
+;// //
+;// //
+;// //
+;//////////////////////////////////////////////////////////////////////////////
+
+
+
+;//
+;// WINDOW_STATION object-specific access types
+;//
+
+MessageId=0x1A00
+ SymbolicName=MS_WIN_STA_ACCESS_BIT_0
+ Language=English
+Enumerate desktops
+.
+
+MessageId=0x1A01
+ SymbolicName=MS_WIN_STA_ACCESS_BIT_1
+ Language=English
+Read attributes
+.
+
+MessageId=0x1A02
+ SymbolicName=MS_WIN_STA_ACCESS_BIT_2
+ Language=English
+Access Clipboard
+.
+
+MessageId=0x1A03
+ SymbolicName=MS_WIN_STA_ACCESS_BIT_3
+ Language=English
+Create desktop
+.
+
+MessageId=0x1A04
+ SymbolicName=MS_WIN_STA_ACCESS_BIT_4
+ Language=English
+Write attributes
+.
+
+MessageId=0x1A05
+ SymbolicName=MS_WIN_STA_ACCESS_BIT_5
+ Language=English
+Access global atoms
+.
+
+MessageId=0x1A06
+ SymbolicName=MS_WIN_STA_ACCESS_BIT_6
+ Language=English
+Exit windows
+.
+
+MessageId=0x1A07
+ SymbolicName=MS_WIN_STA_ACCESS_BIT_7
+ Language=English
+Unused Access Flag
+.
+
+MessageId=0x1A08
+ SymbolicName=MS_WIN_STA_ACCESS_BIT_8
+ Language=English
+Include this windowstation in enumerations
+.
+
+MessageId=0x1A09
+ SymbolicName=MS_WIN_STA_ACCESS_BIT_9
+ Language=English
+Read screen
+.
+
+
+
+;//
+;// DESKTOP object-specific access types
+;//
+
+MessageId=0x1A10
+ SymbolicName=MS_DESKTOP_ACCESS_BIT_0
+ Language=English
+Read Objects
+.
+
+MessageId=0x1A11
+ SymbolicName=MS_DESKTOP_ACCESS_BIT_1
+ Language=English
+Create window
+.
+
+MessageId=0x1A12
+ SymbolicName=MS_DESKTOP_ACCESS_BIT_2
+ Language=English
+Create menu
+.
+
+MessageId=0x1A13
+ SymbolicName=MS_DESKTOP_ACCESS_BIT_3
+ Language=English
+Hook control
+.
+
+MessageId=0x1A14
+ SymbolicName=MS_DESKTOP_ACCESS_BIT_4
+ Language=English
+Journal (record)
+.
+
+MessageId=0x1A15
+ SymbolicName=MS_DESKTOP_ACCESS_BIT_5
+ Language=English
+Journal (playback)
+.
+
+MessageId=0x1A16
+ SymbolicName=MS_DESKTOP_ACCESS_BIT_6
+ Language=English
+Include this desktop in enumerations
+.
+
+MessageId=0x1A17
+ SymbolicName=MS_DESKTOP_ACCESS_BIT_7
+ Language=English
+Write objects
+.
+
+MessageId=0x1A18
+ SymbolicName=MS_DESKTOP_ACCESS_BIT_8
+ Language=English
+Switch to this desktop
+.
+
+
+
+;
+;//////////////////////////////////////////////////////////////////////////////
+;// //
+;// //
+;// Print Server Object Access //
+;// names as we would like them //
+;// displayed for auditing //
+;// //
+;// Print Server objects are: //
+;// //
+;// Server //
+;// Printer //
+;// Document //
+;// //
+;// //
+;//////////////////////////////////////////////////////////////////////////////
+
+
+
+;//
+;// print-server SERVER object-specific access types
+;//
+
+MessageId=0x1B00
+ SymbolicName=MS_PRINT_SERVER_ACCESS_BIT_0
+ Language=English
+Administer print server
+.
+
+MessageId=0x1B01
+ SymbolicName=MS_PRINT_SERVER_ACCESS_BIT_1
+ Language=English
+Enumerate printers
+.
+
+;//
+;// print-server PRINTER object-specific access types
+;//
+;// Note that these are based at 0x1B10, but the first
+;// two bits aren't defined.
+;//
+
+MessageId=0x1B12
+ SymbolicName=MS_PRINTER_ACCESS_BIT_0
+ Language=English
+Full Control
+.
+
+MessageId=0x1B13
+ SymbolicName=MS_PRINTER_ACCESS_BIT_1
+ Language=English
+Print
+.
+
+;//
+;// print-server DOCUMENT object-specific access types
+;//
+;// Note that these are based at 0x1B20, but the first
+;// four bits aren't defined.
+
+MessageId=0x1B14
+ SymbolicName=MS_PRINTER_DOC_ACCESS_BIT_0
+ Language=English
+Administer Document
+.
+
+
+
+;
+;//////////////////////////////////////////////////////////////////////////////
+;// //
+;// //
+;// Service Controller Object Access //
+;// names as we would like them //
+;// displayed for auditing //
+;// //
+;// Service Controller objects are: //
+;// //
+;// SC_MANAGER Object //
+;// SERVICE Object //
+;// //
+;// //
+;//////////////////////////////////////////////////////////////////////////////
+
+
+
+
+;//
+;// SERVICE CONTROLLER "SC_MANAGER Object" object-specific access types
+;//
+
+MessageId=0x1C00
+ SymbolicName=MS_SC_MANAGER_ACCESS_BIT_0
+ Language=English
+Connect to service controller
+.
+
+MessageId=0x1C01
+ SymbolicName=MS_SC_MANAGER_ACCESS_BIT_1
+ Language=English
+Create a new service
+.
+
+MessageId=0x1C02
+ SymbolicName=MS_SC_MANAGER_ACCESS_BIT_2
+ Language=English
+Enumerate services
+.
+
+MessageId=0x1C03
+ SymbolicName=MS_SC_MANAGER_ACCESS_BIT_3
+ Language=English
+Lock service database for exclusive access
+.
+
+MessageId=0x1C04
+ SymbolicName=MS_SC_MANAGER_ACCESS_BIT_4
+ Language=English
+Query service database lock state
+.
+
+MessageId=0x1C05
+ SymbolicName=MS_SC_MANAGER_ACCESS_BIT_5
+ Language=English
+Set last-known-good state of service database
+.
+
+
+;//
+;// SERVICE CONTROLLER "SERVICE Object" object-specific access types
+;//
+
+MessageId=0x1C10
+ SymbolicName=MS_SC_SERVICE_ACCESS_BIT_0
+ Language=English
+Query service configuration information
+.
+
+MessageId=0x1C11
+ SymbolicName=MS_SC_SERVICE_ACCESS_BIT_1
+ Language=English
+Set service configuration information
+.
+
+MessageId=0x1C12
+ SymbolicName=MS_SC_SERVICE_ACCESS_BIT_2
+ Language=English
+Query status of service
+.
+
+MessageId=0x1C13
+ SymbolicName=MS_SC_SERVICE_ACCESS_BIT_3
+ Language=English
+Enumerate dependencies of service
+.
+
+MessageId=0x1C14
+ SymbolicName=MS_SC_SERVICE_ACCESS_BIT_4
+ Language=English
+Start the service
+.
+
+MessageId=0x1C15
+ SymbolicName=MS_SC_SERVICE_ACCESS_BIT_5
+ Language=English
+Stop the service
+.
+
+MessageId=0x1C16
+ SymbolicName=MS_SC_SERVICE_ACCESS_BIT_6
+ Language=English
+Pause or continue the service
+.
+
+MessageId=0x1C17
+ SymbolicName=MS_SC_SERVICE_ACCESS_BIT_7
+ Language=English
+Query information from service
+.
+
+MessageId=0x1C18
+ SymbolicName=MS_SC_SERVICE_ACCESS_BIT_8
+ Language=English
+Issue service-specific control commands
+.
+
+
+
+
+;
+;//////////////////////////////////////////////////////////////////////////////
+;// //
+;// //
+;// NetDDE Object Access //
+;// names as we would like them //
+;// displayed for auditing //
+;// //
+;// NetDDE objects are: //
+;// //
+;// DDE Share //
+;// //
+;// //
+;//////////////////////////////////////////////////////////////////////////////
+
+
+;//
+;// Net DDE object-specific access types
+;//
+
+
+;//
+;// DDE Share object-specific access types
+;//
+
+MessageId=0x1D00
+ SymbolicName=MS_DDE_SHARE_ACCESS_BIT_0
+ Language=English
+DDE Share Read
+.
+
+MessageId=0x1D01
+ SymbolicName=MS_DDE_SHARE_ACCESS_BIT_1
+ Language=English
+DDE Share Write
+.
+
+MessageId=0x1D02
+ SymbolicName=MS_DDE_SHARE_ACCESS_BIT_2
+ Language=English
+DDE Share Initiate Static
+.
+
+MessageId=0x1D03
+ SymbolicName=MS_DDE_SHARE_ACCESS_BIT_3
+ Language=English
+DDE Share Initiate Link
+.
+
+MessageId=0x1D04
+ SymbolicName=MS_DDE_SHARE_ACCESS_BIT_4
+ Language=English
+DDE Share Request
+.
+
+MessageId=0x1D05
+ SymbolicName=MS_DDE_SHARE_ACCESS_BIT_5
+ Language=English
+DDE Share Advise
+.
+
+MessageId=0x1D06
+ SymbolicName=MS_DDE_SHARE_ACCESS_BIT_6
+ Language=English
+DDE Share Poke
+.
+
+MessageId=0x1D07
+ SymbolicName=MS_DDE_SHARE_ACCESS_BIT_7
+ Language=English
+DDE Share Execute
+.
+
+MessageId=0x1D08
+ SymbolicName=MS_DDE_SHARE_ACCESS_BIT_8
+ Language=English
+DDE Share Add Items
+.
+
+MessageId=0x1D09
+ SymbolicName=MS_DDE_SHARE_ACCESS_BIT_9
+ Language=English
+DDE Share List Items
+.
+
+
+
+
+;/*lint +e767 */ // Resume checking for different macro definitions // winnt
+;
+;
+;#endif // _MSOBJS_
diff --git a/private/ntos/seaudit/msobjs/sources b/private/ntos/seaudit/msobjs/sources
new file mode 100644
index 000000000..5e38a08d5
--- /dev/null
+++ b/private/ntos/seaudit/msobjs/sources
@@ -0,0 +1,41 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=ntos
+MINORCOMP=msobjs
+
+TARGETNAME=msobjs
+TARGETPATH=\nt\public\sdk\lib
+
+TARGETLIBS=
+
+TARGETTYPE=DYNLINK
+
+INCLUDES=.
+
+SOURCES= audit.rc
+
+UMLIBS=
+
+NTTARGETFILE0=audit.rc