diff options
Diffstat (limited to 'private/ntos/se')
63 files changed, 62282 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(); +} |