3070 lines
88 KiB
C
3070 lines
88 KiB
C
/*++
|
||
|
||
Copyright (c) 1991 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
SecDescr.c
|
||
|
||
Abstract:
|
||
|
||
This file contains services related to the establishment of and modification
|
||
of security descriptors for SAM objects.
|
||
|
||
Note that there are a couple of special security descriptors that this routine
|
||
does not build. These are the security descriptors for the DOMAIN_ADMIN group,
|
||
the ADMIN user account, and the SAM object. For the first release, in which
|
||
creation of domains is not supported, the DOMAIN object's security descriptor
|
||
is also not created here.
|
||
|
||
These security descriptors are built by the program that initializes a SAM
|
||
database.
|
||
|
||
|
||
Author:
|
||
|
||
Jim Kelly (JimK) 14-Oct-1991
|
||
|
||
Environment:
|
||
|
||
User Mode - Win32
|
||
|
||
Revision History:
|
||
|
||
|
||
--*/
|
||
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
// //
|
||
// Includes //
|
||
// //
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
|
||
#include <samsrvp.h>
|
||
|
||
|
||
|
||
|
||
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
// //
|
||
// private service prototypes //
|
||
// //
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SampValidatePassedSD(
|
||
IN ULONG Length,
|
||
IN PISECURITY_DESCRIPTOR PassedSD
|
||
);
|
||
|
||
NTSTATUS
|
||
SampCheckForDescriptorRestrictions(
|
||
IN PSAMP_OBJECT Context,
|
||
IN SAMP_OBJECT_TYPE ObjectType,
|
||
IN ULONG ObjectRid,
|
||
IN PISECURITY_DESCRIPTOR PassedSD
|
||
);
|
||
|
||
NTSTATUS
|
||
SampBuildSamProtection(
|
||
IN PSID WorldSid,
|
||
IN PSID AdminsAliasSid,
|
||
IN ULONG AceCount,
|
||
IN PSID AceSid[],
|
||
IN ACCESS_MASK AceMask[],
|
||
IN PGENERIC_MAPPING GenericMap,
|
||
IN BOOLEAN UserObject,
|
||
OUT PULONG DescriptorLength,
|
||
OUT PSECURITY_DESCRIPTOR *Descriptor,
|
||
OUT PULONG *RidToReplace OPTIONAL
|
||
);
|
||
|
||
|
||
|
||
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
// //
|
||
// Services available for use throughout SAM //
|
||
// //
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SampInitializeDomainDescriptors(
|
||
ULONG Index
|
||
)
|
||
/*++
|
||
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes security descriptors needed to protect
|
||
user, group, and alias objects.
|
||
|
||
These security descriptors are placed in the SampDefinedDomains[] array.
|
||
|
||
This routine expects all SIDs to be previously initialized.
|
||
|
||
The following security descriptors are prepared:
|
||
|
||
AdminUserSD - Contains a SD appropriate for applying to
|
||
a user object that is a member of the ADMINISTRATORS
|
||
alias.
|
||
|
||
AdminGroupSD - Contains a SD appropriate for applying to
|
||
a group object that is a member of the ADMINISTRATORS
|
||
alias.
|
||
|
||
NormalUserSD - Contains a SD appropriate for applying to
|
||
a user object that is NOT a member of the ADMINISTRATORS
|
||
alias.
|
||
|
||
NormalGroupSD - Contains a SD appropriate for applying to
|
||
a Group object that is NOT a member of the ADMINISTRATORS
|
||
alias.
|
||
|
||
NormalAliasSD - Contains a SD appropriate for applying to
|
||
newly created alias objects.
|
||
|
||
|
||
|
||
Additionally, the following related information is provided:
|
||
|
||
AdminUserRidPointer
|
||
NormalUserRidPointer
|
||
|
||
Points to the last RID of the ACE in the corresponding
|
||
SD's DACL which grants access to the user. This rid
|
||
must be replaced with the user's rid being the SD is
|
||
applied to the user object.
|
||
|
||
|
||
|
||
AdminUserSDLength
|
||
AdminGroupSDLength
|
||
NormalUserSDLength
|
||
NormalGroupSDLength
|
||
NormalAliasSDLength
|
||
|
||
The length, in bytes, of the corresponding security
|
||
descriptor.
|
||
|
||
|
||
|
||
|
||
Arguments:
|
||
|
||
Index - The index of the domain whose security descriptors are being
|
||
created. The Sid field of this domain's data structure is already
|
||
expected to be set.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - The security descriptors have been successfully initialized.
|
||
|
||
STATUS_INSUFFICIENT_RESOURCES - Heap could not be allocated to produce the needed
|
||
security descriptors.
|
||
|
||
--*/
|
||
{
|
||
|
||
NTSTATUS Status;
|
||
ULONG Size;
|
||
|
||
PSID AceSid[10]; // Don't expect more than 10 ACEs in any of these.
|
||
ACCESS_MASK AceMask[10]; // Access masks corresponding to Sids
|
||
|
||
GENERIC_MAPPING AliasMap = {ALIAS_READ,
|
||
ALIAS_WRITE,
|
||
ALIAS_EXECUTE,
|
||
ALIAS_ALL_ACCESS
|
||
};
|
||
|
||
GENERIC_MAPPING GroupMap = {GROUP_READ,
|
||
GROUP_WRITE,
|
||
GROUP_EXECUTE,
|
||
GROUP_ALL_ACCESS
|
||
};
|
||
|
||
GENERIC_MAPPING UserMap = {USER_READ,
|
||
USER_WRITE,
|
||
USER_EXECUTE,
|
||
USER_ALL_ACCESS
|
||
};
|
||
|
||
|
||
SID_IDENTIFIER_AUTHORITY
|
||
BuiltinAuthority = SECURITY_NT_AUTHORITY;
|
||
|
||
|
||
ULONG AdminsSidBuffer[8],
|
||
AccountSidBuffer[8];
|
||
|
||
PSID AdminsAliasSid = &AdminsSidBuffer[0],
|
||
AccountAliasSid = &AccountSidBuffer[0],
|
||
AnySidInAccountDomain = NULL;
|
||
|
||
|
||
//
|
||
// Make sure the buffer we've alloted for the simple sids above
|
||
// are large enough.
|
||
//
|
||
//
|
||
// ADMINISTRATORS and ACCOUNT_OPERATORS aliases
|
||
// are is S-1-5-20-x (2 sub-authorities)
|
||
//
|
||
ASSERT( RtlLengthRequiredSid(2) <= ( 8 * sizeof(ULONG) ) );
|
||
|
||
|
||
////////////////////////////////////////////////////////////////////////////////////
|
||
// //
|
||
// Initialize the SIDs we'll need.
|
||
// //
|
||
////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
|
||
RtlInitializeSid( AdminsAliasSid, &BuiltinAuthority, 2 );
|
||
*(RtlSubAuthoritySid( AdminsAliasSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID;
|
||
*(RtlSubAuthoritySid( AdminsAliasSid, 1 )) = DOMAIN_ALIAS_RID_ADMINS;
|
||
|
||
RtlInitializeSid( AccountAliasSid, &BuiltinAuthority, 2 );
|
||
*(RtlSubAuthoritySid( AccountAliasSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID;
|
||
*(RtlSubAuthoritySid( AccountAliasSid, 1 )) = DOMAIN_ALIAS_RID_ACCOUNT_OPS;
|
||
|
||
//
|
||
// Initialize a SID that can be used to represent accounts
|
||
// in this domain.
|
||
//
|
||
// This is the same as the domain sid found in the DefinedDomains[]
|
||
// array except it has one more sub-authority.
|
||
// It doesn't matter what the value of the last RID is because it
|
||
// is always replaced before use.
|
||
//
|
||
|
||
Size = RtlLengthSid( SampDefinedDomains[Index].Sid ) + sizeof(ULONG);
|
||
AnySidInAccountDomain = RtlAllocateHeap( RtlProcessHeap(), 0, Size);
|
||
ASSERT( AnySidInAccountDomain != NULL );
|
||
Status = RtlCopySid(
|
||
Size,
|
||
AnySidInAccountDomain,
|
||
SampDefinedDomains[Index].Sid );
|
||
ASSERT(NT_SUCCESS(Status));
|
||
(*RtlSubAuthorityCountSid( AnySidInAccountDomain )) += 1;
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
///////////////////////////////////////////////////////////////////////
|
||
///////////////////////////////////////////////////////////////////////
|
||
//
|
||
//
|
||
//
|
||
//
|
||
// The following security is assigned to groups that are made
|
||
// members of the ADMINISTRATORS alias.
|
||
//
|
||
//
|
||
// Owner: Administrators Alias
|
||
// Group: Administrators Alias
|
||
//
|
||
// Dacl: Grant Grant
|
||
// WORLD Administrators
|
||
// (Execute | Read) GenericAll
|
||
//
|
||
// Sacl: Audit
|
||
// Success | Fail
|
||
// WORLD
|
||
// (Write | Delete | WriteDacl | AccessSystemSecurity)
|
||
//
|
||
//
|
||
//
|
||
// All other aliases and groups must be assigned the following
|
||
// security:
|
||
//
|
||
// Owner: Administrators Alias
|
||
// Group: Administrators Alias
|
||
//
|
||
// Dacl: Grant Grant Grant
|
||
// WORLD Administrators AccountOperators Alias
|
||
// (Execute | Read) GenericAll GenericAll
|
||
//
|
||
// Sacl: Audit
|
||
// Success | Fail
|
||
// WORLD
|
||
// (Write | Delete | WriteDacl | AccessSystemSecurity)
|
||
//
|
||
//
|
||
//
|
||
//
|
||
//
|
||
// The following security is assigned to users that are made a
|
||
// member of the ADMINISTRATORS alias. This includes direct
|
||
// inclusion or indirect inclusion through group membership.
|
||
//
|
||
//
|
||
// Owner: Administrators Alias
|
||
// Group: Administrators Alias
|
||
//
|
||
// Dacl: Grant Grant Grant
|
||
// WORLD Administrators User's SID
|
||
// (Execute | Read) GenericAll GenericWrite
|
||
//
|
||
// Sacl: Audit
|
||
// Success | Fail
|
||
// WORLD
|
||
// (Write | Delete | WriteDacl | AccessSystemSecurity)
|
||
//
|
||
//
|
||
//
|
||
//
|
||
// All other users must be assigned the following
|
||
// security:
|
||
//
|
||
// Owner: AccountOperators Alias
|
||
// Group: AccountOperators Alias
|
||
//
|
||
// Dacl: Grant Grant Grant Grant
|
||
// WORLD Administrators Account Operators Alias User's SID
|
||
// (Execute | Read) GenericAll GenericAll GenericWrite
|
||
//
|
||
// Sacl: Audit
|
||
// Success | Fail
|
||
// WORLD
|
||
// (Write | Delete | WriteDacl | AccessSystemSecurity)
|
||
//
|
||
//
|
||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||
//
|
||
// Note that because we are going to cram these ACLs directly
|
||
// into the backing store, we must map the generic accesses
|
||
// beforehand.
|
||
//
|
||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||
//
|
||
///////////////////////////////////////////////////////////////////////
|
||
///////////////////////////////////////////////////////////////////////
|
||
|
||
|
||
|
||
|
||
|
||
//
|
||
// We're not particularly good about freeing memory on error
|
||
// conditions below. Generally speaking, if this doens't
|
||
// initialize correctly, the system is hosed.
|
||
//
|
||
|
||
|
||
//
|
||
// Normal Alias SD
|
||
//
|
||
|
||
AceSid[0] = SampWorldSid;
|
||
AceMask[0] = (ALIAS_EXECUTE | ALIAS_READ);
|
||
|
||
AceSid[1] = AdminsAliasSid;
|
||
AceMask[1] = (ALIAS_ALL_ACCESS);
|
||
|
||
AceSid[2] = AccountAliasSid;
|
||
AceMask[2] = (ALIAS_ALL_ACCESS);
|
||
|
||
|
||
Status = SampBuildSamProtection(
|
||
SampWorldSid, // WorldSid
|
||
AdminsAliasSid, // AdminsAliasSid
|
||
3, // AceCount
|
||
&AceSid[0], // AceSid array
|
||
&AceMask[0], // Ace Mask array
|
||
&AliasMap, // GenericMap
|
||
FALSE, // Not user object
|
||
&SampDefinedDomains[Index].NormalAliasSDLength, // Descriptor
|
||
&SampDefinedDomains[Index].NormalAliasSD, // Descriptor
|
||
NULL // RidToReplace
|
||
);
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto done;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
//
|
||
// Admin Group SD
|
||
//
|
||
|
||
AceSid[0] = SampWorldSid;
|
||
AceMask[0] = (GROUP_EXECUTE | GROUP_READ);
|
||
|
||
AceSid[1] = AdminsAliasSid;
|
||
AceMask[1] = (GROUP_ALL_ACCESS);
|
||
|
||
|
||
Status = SampBuildSamProtection(
|
||
SampWorldSid, // WorldSid
|
||
AdminsAliasSid, // AdminsAliasSid
|
||
2, // AceCount
|
||
&AceSid[0], // AceSid array
|
||
&AceMask[0], // Ace Mask array
|
||
&GroupMap, // GenericMap
|
||
FALSE, // Not user object
|
||
&SampDefinedDomains[Index].AdminGroupSDLength, // Descriptor
|
||
&SampDefinedDomains[Index].AdminGroupSD, // Descriptor
|
||
NULL // RidToReplace
|
||
);
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto done;
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// Normal GROUP SD
|
||
//
|
||
|
||
AceSid[0] = SampWorldSid;
|
||
AceMask[0] = (GROUP_EXECUTE | GROUP_READ);
|
||
|
||
AceSid[1] = AdminsAliasSid;
|
||
AceMask[1] = (GROUP_ALL_ACCESS);
|
||
|
||
AceSid[2] = AccountAliasSid;
|
||
AceMask[2] = (GROUP_ALL_ACCESS);
|
||
|
||
|
||
Status = SampBuildSamProtection(
|
||
SampWorldSid, // WorldSid
|
||
AdminsAliasSid, // AdminsAliasSid
|
||
3, // AceCount
|
||
&AceSid[0], // AceSid array
|
||
&AceMask[0], // Ace Mask array
|
||
&GroupMap, // GenericMap
|
||
FALSE, // Not user object
|
||
&SampDefinedDomains[Index].NormalGroupSDLength, // Descriptor
|
||
&SampDefinedDomains[Index].NormalGroupSD, // Descriptor
|
||
NULL // RidToReplace
|
||
);
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto done;
|
||
}
|
||
|
||
|
||
|
||
|
||
//
|
||
// Admin User SD
|
||
//
|
||
|
||
AceSid[0] = SampWorldSid;
|
||
AceMask[0] = (USER_EXECUTE | USER_READ);
|
||
|
||
AceSid[1] = AdminsAliasSid;
|
||
AceMask[1] = (USER_ALL_ACCESS);
|
||
|
||
AceSid[2] = AnySidInAccountDomain;
|
||
AceMask[2] = (USER_WRITE);
|
||
|
||
|
||
Status = SampBuildSamProtection(
|
||
SampWorldSid, // WorldSid
|
||
AdminsAliasSid, // AdminsAliasSid
|
||
3, // AceCount
|
||
&AceSid[0], // AceSid array
|
||
&AceMask[0], // Ace Mask array
|
||
&UserMap, // GenericMap
|
||
TRUE, // Not user object
|
||
&SampDefinedDomains[Index].AdminUserSDLength, // Descriptor
|
||
&SampDefinedDomains[Index].AdminUserSD, // Descriptor
|
||
&SampDefinedDomains[Index].AdminUserRidPointer // RidToReplace
|
||
);
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto done;
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// Normal User SD
|
||
//
|
||
|
||
AceSid[0] = SampWorldSid;
|
||
AceMask[0] = (USER_EXECUTE | USER_READ);
|
||
|
||
AceSid[1] = AdminsAliasSid;
|
||
AceMask[1] = (USER_ALL_ACCESS);
|
||
|
||
AceSid[2] = AccountAliasSid;
|
||
AceMask[2] = (USER_ALL_ACCESS);
|
||
|
||
AceSid[3] = AnySidInAccountDomain;
|
||
AceMask[3] = (USER_WRITE);
|
||
|
||
|
||
Status = SampBuildSamProtection(
|
||
SampWorldSid, // WorldSid
|
||
AdminsAliasSid, // AdminsAliasSid
|
||
4, // AceCount
|
||
&AceSid[0], // AceSid array
|
||
&AceMask[0], // Ace Mask array
|
||
&UserMap, // GenericMap
|
||
TRUE, // Not user object
|
||
&SampDefinedDomains[Index].NormalUserSDLength, // Descriptor
|
||
&SampDefinedDomains[Index].NormalUserSD, // Descriptor
|
||
&SampDefinedDomains[Index].NormalUserRidPointer // RidToReplace
|
||
);
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto done;
|
||
}
|
||
|
||
done:
|
||
|
||
|
||
RtlFreeHeap( RtlProcessHeap(), 0, AnySidInAccountDomain );
|
||
|
||
|
||
return(Status);
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SampBuildSamProtection(
|
||
IN PSID WorldSid,
|
||
IN PSID AdminsAliasSid,
|
||
IN ULONG AceCount,
|
||
IN PSID AceSid[],
|
||
IN ACCESS_MASK AceMask[],
|
||
IN PGENERIC_MAPPING GenericMap,
|
||
IN BOOLEAN UserObject,
|
||
OUT PULONG DescriptorLength,
|
||
OUT PSECURITY_DESCRIPTOR *Descriptor,
|
||
OUT PULONG *RidToReplace OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
|
||
Routine Description:
|
||
|
||
This routine builds a self-relative security descriptor ready
|
||
to be applied to one of the SAM objects.
|
||
|
||
If so indicated, a pointer to the last RID of the SID in the last
|
||
ACE of the DACL is returned and a flag set indicating that the RID
|
||
must be replaced before the security descriptor is applied to an object.
|
||
This is to support USER object protection, which must grant some
|
||
access to the user represented by the object.
|
||
|
||
The owner and group of each security descriptor will be set
|
||
to:
|
||
|
||
Owner: Administrators Alias
|
||
Group: Administrators Alias
|
||
|
||
|
||
The SACL of each of these objects will be set to:
|
||
|
||
|
||
Audit
|
||
Success | Fail
|
||
WORLD
|
||
(Write | Delete | WriteDacl | AccessSystemSecurity)
|
||
|
||
|
||
|
||
Arguments:
|
||
|
||
AceCount - The number of ACEs to be included in the DACL.
|
||
|
||
AceSid - Points to an array of SIDs to be granted access by the DACL.
|
||
If the target SAM object is a User object, then the last entry
|
||
in this array is expected to be the SID of an account within the
|
||
domain with the last RID not yet set. The RID will be set during
|
||
actual account creation.
|
||
|
||
AceMask - Points to an array of accesses to be granted by the DACL.
|
||
The n'th entry of this array corresponds to the n'th entry of
|
||
the AceSid array. These masks should not include any generic
|
||
access types.
|
||
|
||
GenericMap - Points to a generic mapping for the target object type.
|
||
|
||
|
||
UserObject - Indicates whether the target SAM object is a User object
|
||
or not. If TRUE (it is a User object), then the resultant
|
||
protection will be set up indicating Rid replacement is necessary.
|
||
|
||
|
||
DescriptorLength - Receives the length of the resultant SD.
|
||
|
||
Descriptor - Receives a pointer to the resultant SD.
|
||
|
||
RidToReplace - Is required aif userObject is TRUE and will be set
|
||
to point to the user's RID.
|
||
|
||
|
||
Return Value:
|
||
|
||
TBS.
|
||
|
||
--*/
|
||
{
|
||
|
||
NTSTATUS Status;
|
||
|
||
SECURITY_DESCRIPTOR Absolute;
|
||
PSECURITY_DESCRIPTOR Relative;
|
||
PACL TmpAcl;
|
||
PACCESS_ALLOWED_ACE TmpAce;
|
||
PSID TmpSid;
|
||
ULONG Length, i;
|
||
PULONG RidLocation;
|
||
BOOLEAN IgnoreBoolean;
|
||
ACCESS_MASK MappedMask;
|
||
|
||
//
|
||
// The approach is to set up an absolute security descriptor that
|
||
// looks like what we want and then copy it to make a self-relative
|
||
// security descriptor.
|
||
//
|
||
|
||
|
||
Status = RtlCreateSecurityDescriptor(
|
||
&Absolute,
|
||
SECURITY_DESCRIPTOR_REVISION1
|
||
);
|
||
ASSERT( NT_SUCCESS(Status) );
|
||
|
||
|
||
|
||
//
|
||
// Owner
|
||
//
|
||
|
||
Status = RtlSetOwnerSecurityDescriptor (&Absolute, AdminsAliasSid, FALSE );
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
|
||
|
||
//
|
||
// Group
|
||
//
|
||
|
||
Status = RtlSetGroupSecurityDescriptor (&Absolute, AdminsAliasSid, FALSE );
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
|
||
|
||
|
||
//
|
||
// Discretionary ACL
|
||
//
|
||
// Calculate its length,
|
||
// Allocate it,
|
||
// Initialize it,
|
||
// Add each ACE
|
||
// Add it to the security descriptor
|
||
//
|
||
|
||
Length = (ULONG)sizeof(ACL);
|
||
for (i=0; i<AceCount; i++) {
|
||
|
||
Length += RtlLengthSid( AceSid[i] ) +
|
||
(ULONG)sizeof(ACCESS_ALLOWED_ACE) -
|
||
(ULONG)sizeof(ULONG); //Subtract out SidStart field length
|
||
}
|
||
|
||
TmpAcl = RtlAllocateHeap( RtlProcessHeap(), 0, Length );
|
||
ASSERT(TmpAcl != NULL);
|
||
|
||
|
||
Status = RtlCreateAcl( TmpAcl, Length, ACL_REVISION2);
|
||
ASSERT( NT_SUCCESS(Status) );
|
||
|
||
for (i=0; i<AceCount; i++) {
|
||
MappedMask = AceMask[i];
|
||
RtlMapGenericMask( &MappedMask, GenericMap );
|
||
Status = RtlAddAccessAllowedAce (
|
||
TmpAcl,
|
||
ACL_REVISION2,
|
||
MappedMask,
|
||
AceSid[i]
|
||
);
|
||
ASSERT( NT_SUCCESS(Status) );
|
||
}
|
||
|
||
Status = RtlSetDaclSecurityDescriptor (&Absolute, TRUE, TmpAcl, FALSE );
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
|
||
|
||
|
||
//
|
||
// Sacl
|
||
//
|
||
|
||
|
||
Length = (ULONG)sizeof(ACL) +
|
||
RtlLengthSid( WorldSid ) +
|
||
(ULONG)sizeof(SYSTEM_AUDIT_ACE) -
|
||
(ULONG)sizeof(ULONG); //Subtract out SidStart field length
|
||
TmpAcl = RtlAllocateHeap( RtlProcessHeap(), 0, Length );
|
||
ASSERT(TmpAcl != NULL);
|
||
|
||
Status = RtlCreateAcl( TmpAcl, Length, ACL_REVISION2);
|
||
ASSERT( NT_SUCCESS(Status) );
|
||
|
||
Status = RtlAddAuditAccessAce (
|
||
TmpAcl,
|
||
ACL_REVISION2,
|
||
GenericMap->GenericWrite | DELETE | WRITE_DAC | ACCESS_SYSTEM_SECURITY,
|
||
WorldSid,
|
||
TRUE, //AuditSuccess,
|
||
TRUE //AuditFailure
|
||
);
|
||
ASSERT( NT_SUCCESS(Status) );
|
||
|
||
Status = RtlSetSaclSecurityDescriptor (&Absolute, TRUE, TmpAcl, FALSE );
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
|
||
|
||
|
||
|
||
|
||
//
|
||
// Convert the Security Descriptor to Self-Relative
|
||
//
|
||
// Get the length needed
|
||
// Allocate that much memory
|
||
// Copy it
|
||
// Free the generated absolute ACLs
|
||
//
|
||
|
||
Length = 0;
|
||
Status = RtlAbsoluteToSelfRelativeSD( &Absolute, NULL, &Length );
|
||
ASSERT(Status == STATUS_BUFFER_TOO_SMALL);
|
||
|
||
Relative = RtlAllocateHeap( RtlProcessHeap(), 0, Length );
|
||
ASSERT(Relative != NULL);
|
||
Status = RtlAbsoluteToSelfRelativeSD(&Absolute, Relative, &Length );
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
|
||
RtlFreeHeap( RtlProcessHeap(), 0, Absolute.Dacl );
|
||
RtlFreeHeap( RtlProcessHeap(), 0, Absolute.Sacl );
|
||
|
||
|
||
|
||
|
||
//
|
||
// If the object is a user object, then get the address of the
|
||
// last RID of the SID in the last ACE in the DACL.
|
||
//
|
||
|
||
if (UserObject == TRUE) {
|
||
|
||
Status = RtlGetDaclSecurityDescriptor(
|
||
Relative,
|
||
&IgnoreBoolean,
|
||
&TmpAcl,
|
||
&IgnoreBoolean
|
||
);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
Status = RtlGetAce ( TmpAcl, AceCount-1, (PVOID *)&TmpAce );
|
||
ASSERT(NT_SUCCESS(Status));
|
||
TmpSid = (PSID)(&TmpAce->SidStart),
|
||
|
||
RidLocation = RtlSubAuthoritySid(
|
||
TmpSid,
|
||
(ULONG)(*RtlSubAuthorityCountSid( TmpSid ) - 1)
|
||
);
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
//
|
||
// Set the result information
|
||
//
|
||
|
||
(*DescriptorLength) = Length;
|
||
(*Descriptor) = Relative;
|
||
if (ARGUMENT_PRESENT(RidToReplace)) {
|
||
(*RidToReplace) = RidLocation;
|
||
}
|
||
|
||
|
||
|
||
return(Status);
|
||
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SampGetNewAccountSecurity(
|
||
IN SAMP_OBJECT_TYPE ObjectType,
|
||
IN BOOLEAN Admin,
|
||
IN BOOLEAN TrustedClient,
|
||
IN BOOLEAN RestrictCreatorAccess,
|
||
IN ULONG NewAccountRid,
|
||
OUT PSECURITY_DESCRIPTOR *NewDescriptor,
|
||
OUT PULONG DescriptorLength
|
||
)
|
||
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This service creates a standard self-relative security descriptor
|
||
for a new USER, GROUP or ALIAS account.
|
||
|
||
|
||
Note: THIS ROUTINE REFERENCES THE CURRENT TRANSACTION DOMAIN
|
||
(ESTABLISHED USING SampSetTransactioDomain()). THIS
|
||
SERVICE MAY ONLY BE CALLED AFTER SampSetTransactionDomain()
|
||
AND BEFORE SampReleaseWriteLock().
|
||
|
||
|
||
Arguments:
|
||
|
||
ObjectType - Indicates the type of account for which a new security
|
||
descriptor is required. This must be either SampGroupObjectType
|
||
or SampUserObjectType.
|
||
|
||
Admin - if TRUE, indicates the security descriptor will be protecting
|
||
an object that is an admin object (e.g., is a member, directly
|
||
or indirectly, of the ADMINISTRATORS alias).
|
||
|
||
TrustedClient - Indicates whether the client is a trusted client
|
||
or not. TRUE indicates the client is trusted, FALSE indicates
|
||
the client is not trusted.
|
||
|
||
RestrictCreatorAccess - Indicates whether or not the creator's
|
||
access to the object is to be restricted according to
|
||
specific rules. Also indicates whether or not the account
|
||
is to be given any access to itself. An account will only
|
||
be given access to itself if there are no creator access
|
||
restrictions.
|
||
|
||
The following ObjectTypes have restriction rules that may
|
||
be requested:
|
||
|
||
User:
|
||
- Admin is assigned as owner of the object.
|
||
- Creator is given (DELETE | USER_WRITE) access.
|
||
|
||
|
||
NewAccountRid - The relative ID of the new account.
|
||
|
||
NewDescriptor - Receives a pointer to the new account's self-relative
|
||
security descriptor. Be sure to free this descriptor with
|
||
MIDL_user_free() when done.
|
||
|
||
DescriptorLength - Receives the length (in bytes) of the returned
|
||
security descriptor
|
||
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - A new security descriptor has been produced.
|
||
|
||
STATUS_INSUFFICIENT_RESOURCES - Memory could not be allocated to
|
||
produce the security descriptor.
|
||
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
SID_IDENTIFIER_AUTHORITY BuiltinAuthority = SECURITY_NT_AUTHORITY;
|
||
ULONG AccountSidBuffer[8];
|
||
PSID AccountAliasSid = &AccountSidBuffer[0];
|
||
|
||
SECURITY_DESCRIPTOR DaclDescriptor;
|
||
NTSTATUS NtStatus = STATUS_SUCCESS;
|
||
NTSTATUS IgnoreStatus;
|
||
HANDLE ClientToken = INVALID_HANDLE_VALUE;
|
||
ULONG DataLength = 0;
|
||
ACCESS_ALLOWED_ACE *NewAce = NULL;
|
||
PACL NewDacl = NULL;
|
||
PACL OldDacl = NULL;
|
||
PSECURITY_DESCRIPTOR StaticDescriptor = NULL;
|
||
PSECURITY_DESCRIPTOR LocalDescriptor = NULL;
|
||
PTOKEN_GROUPS ClientGroups = NULL;
|
||
PTOKEN_OWNER SubjectOwner = NULL;
|
||
PSID SubjectSid = NULL;
|
||
ULONG AceLength = 0;
|
||
ULONG i;
|
||
BOOLEAN AdminAliasFound = FALSE;
|
||
BOOLEAN AccountAliasFound = FALSE;
|
||
BOOLEAN DaclPresent, DaclDefaulted;
|
||
|
||
GENERIC_MAPPING GenericMapping;
|
||
GENERIC_MAPPING AliasMap = {ALIAS_READ,
|
||
ALIAS_WRITE,
|
||
ALIAS_EXECUTE,
|
||
ALIAS_ALL_ACCESS
|
||
};
|
||
|
||
GENERIC_MAPPING GroupMap = {GROUP_READ,
|
||
GROUP_WRITE,
|
||
GROUP_EXECUTE,
|
||
GROUP_ALL_ACCESS
|
||
};
|
||
|
||
GENERIC_MAPPING UserMap = {USER_READ,
|
||
USER_WRITE,
|
||
USER_EXECUTE,
|
||
USER_ALL_ACCESS
|
||
};
|
||
|
||
//
|
||
// Security account objects don't pick up security in the normal
|
||
// fashion in the release 1 timeframe. They are assigned a well-known
|
||
// security descriptor based upon their object type.
|
||
//
|
||
// Notice that all the accounts with tricky security are created when
|
||
// the domain is created (e.g., admin groups and admin user account).
|
||
//
|
||
|
||
switch (ObjectType) {
|
||
|
||
case SampGroupObjectType:
|
||
|
||
ASSERT(RestrictCreatorAccess == FALSE);
|
||
|
||
//
|
||
// NewAccountRid parameter is ignored for groups.
|
||
//
|
||
|
||
if (Admin == TRUE) {
|
||
|
||
StaticDescriptor =
|
||
SampDefinedDomains[SampTransactionDomainIndex].AdminGroupSD;
|
||
(*DescriptorLength) =
|
||
SampDefinedDomains[SampTransactionDomainIndex].AdminGroupSDLength;
|
||
} else {
|
||
|
||
StaticDescriptor =
|
||
SampDefinedDomains[SampTransactionDomainIndex].NormalGroupSD;
|
||
(*DescriptorLength) =
|
||
SampDefinedDomains[SampTransactionDomainIndex].NormalGroupSDLength;
|
||
}
|
||
|
||
GenericMapping = GroupMap;
|
||
|
||
break;
|
||
|
||
|
||
case SampAliasObjectType:
|
||
|
||
ASSERT(RestrictCreatorAccess == FALSE);
|
||
|
||
//
|
||
// Admin and NewAccountRid parameters are ignored for aliases.
|
||
//
|
||
|
||
StaticDescriptor =
|
||
SampDefinedDomains[SampTransactionDomainIndex].NormalAliasSD;
|
||
(*DescriptorLength) =
|
||
SampDefinedDomains[SampTransactionDomainIndex].NormalAliasSDLength;
|
||
|
||
GenericMapping = AliasMap;
|
||
|
||
break;
|
||
|
||
|
||
case SampUserObjectType:
|
||
|
||
if (Admin == TRUE) {
|
||
|
||
StaticDescriptor =
|
||
SampDefinedDomains[SampTransactionDomainIndex].AdminUserSD;
|
||
(*DescriptorLength) =
|
||
SampDefinedDomains[SampTransactionDomainIndex].AdminUserSDLength;
|
||
(*SampDefinedDomains[SampTransactionDomainIndex].AdminUserRidPointer)
|
||
= NewAccountRid;
|
||
|
||
} else {
|
||
|
||
StaticDescriptor =
|
||
SampDefinedDomains[SampTransactionDomainIndex].NormalUserSD;
|
||
(*DescriptorLength) =
|
||
SampDefinedDomains[SampTransactionDomainIndex].NormalUserSDLength;
|
||
(*SampDefinedDomains[SampTransactionDomainIndex].NormalUserRidPointer)
|
||
= NewAccountRid;
|
||
}
|
||
|
||
GenericMapping = UserMap;
|
||
|
||
break;
|
||
|
||
}
|
||
|
||
//
|
||
// We have a pointer to SAM's static security descriptor. Copy it
|
||
// into a heap buffer that RtlSetSecurityObject() will like.
|
||
//
|
||
|
||
LocalDescriptor = RtlAllocateHeap( RtlProcessHeap(), 0, (*DescriptorLength) );
|
||
|
||
if ( LocalDescriptor == NULL ) {
|
||
|
||
(*NewDescriptor) = NULL;
|
||
(*DescriptorLength) = 0;
|
||
|
||
return(STATUS_INSUFFICIENT_RESOURCES);
|
||
}
|
||
|
||
RtlCopyMemory(
|
||
LocalDescriptor,
|
||
StaticDescriptor,
|
||
(*DescriptorLength)
|
||
);
|
||
|
||
//
|
||
// If the caller is to have restricted access to this account,
|
||
// then remove the last ACE from the ACL (the one intended for
|
||
// the account itself).
|
||
//
|
||
|
||
if (RestrictCreatorAccess) {
|
||
NtStatus = RtlGetDaclSecurityDescriptor(
|
||
LocalDescriptor,
|
||
&DaclPresent,
|
||
&OldDacl,
|
||
&DaclDefaulted
|
||
);
|
||
ASSERT(NT_SUCCESS(NtStatus));
|
||
ASSERT(DaclPresent);
|
||
ASSERT(OldDacl->AceCount >= 1);
|
||
|
||
OldDacl->AceCount -= 1; // Remove the last ACE from the ACL.
|
||
}
|
||
|
||
|
||
//
|
||
// If the caller is not a trusted client, see if the caller is an
|
||
// administrator or an account operator. If not, add an ACCESS_ALLOWED
|
||
// ACE to the DACL that gives full access to the creator (or restricted
|
||
// access, if so specified).
|
||
//
|
||
|
||
if ( !TrustedClient ) {
|
||
|
||
NtStatus = I_RpcMapWin32Status(RpcImpersonateClient( NULL ));
|
||
|
||
if (NT_SUCCESS(NtStatus)) { // if (ImpersonatingClient)
|
||
|
||
NtStatus = NtOpenThreadToken(
|
||
NtCurrentThread(),
|
||
TOKEN_QUERY,
|
||
TRUE, //OpenAsSelf
|
||
&ClientToken
|
||
);
|
||
|
||
//
|
||
// Stop impersonating the client
|
||
//
|
||
|
||
IgnoreStatus = I_RpcMapWin32Status(RpcRevertToSelf());
|
||
ASSERT( NT_SUCCESS(IgnoreStatus) );
|
||
|
||
if (NT_SUCCESS(NtStatus)) { // if (TokenOpened)
|
||
|
||
|
||
|
||
|
||
//
|
||
// See if the caller is an administrator or an account
|
||
// operator. First, see how big
|
||
// a buffer we need to hold the caller's groups.
|
||
//
|
||
|
||
NtStatus = NtQueryInformationToken(
|
||
ClientToken,
|
||
TokenGroups,
|
||
NULL,
|
||
0,
|
||
&DataLength
|
||
);
|
||
|
||
if ( ( NtStatus == STATUS_BUFFER_TOO_SMALL ) &&
|
||
( DataLength > 0 ) ) {
|
||
|
||
ClientGroups = MIDL_user_allocate( DataLength );
|
||
|
||
if ( ClientGroups == NULL ) {
|
||
|
||
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Now get a list of the caller's groups.
|
||
//
|
||
|
||
NtStatus = NtQueryInformationToken(
|
||
ClientToken,
|
||
TokenGroups,
|
||
ClientGroups,
|
||
DataLength,
|
||
&DataLength
|
||
);
|
||
|
||
if ( NT_SUCCESS( NtStatus ) ) {
|
||
|
||
|
||
//
|
||
// Build the SID of the ACCOUNT_OPS alias, so we
|
||
// can see if the user is included in it.
|
||
//
|
||
|
||
RtlInitializeSid(
|
||
AccountAliasSid,
|
||
&BuiltinAuthority,
|
||
2 );
|
||
|
||
*(RtlSubAuthoritySid( AccountAliasSid, 0 )) =
|
||
SECURITY_BUILTIN_DOMAIN_RID;
|
||
|
||
*(RtlSubAuthoritySid( AccountAliasSid, 1 )) =
|
||
DOMAIN_ALIAS_RID_ACCOUNT_OPS;
|
||
|
||
//
|
||
// See if the ADMIN or ACCOUNT_OPS alias is in
|
||
// the caller's groups.
|
||
//
|
||
|
||
for ( i = 0; i < ClientGroups->GroupCount; i++ ) {
|
||
|
||
SubjectSid = ClientGroups->Groups[i].Sid;
|
||
ASSERT( SubjectSid != NULL );
|
||
|
||
if ( RtlEqualSid( SubjectSid, SampAdministratorsAliasSid ) ) {
|
||
|
||
AdminAliasFound = TRUE;
|
||
break;
|
||
}
|
||
if ( RtlEqualSid( SubjectSid, AccountAliasSid ) ) {
|
||
|
||
AccountAliasFound = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If the callers groups did not include the admins
|
||
// alias, add an ACCESS_ALLOWED ACE for the owner.
|
||
//
|
||
|
||
if ( !AdminAliasFound && !AccountAliasFound ) {
|
||
|
||
//
|
||
// First, find out what size buffer we need
|
||
// to get the owner.
|
||
//
|
||
|
||
NtStatus = NtQueryInformationToken(
|
||
ClientToken,
|
||
TokenOwner,
|
||
NULL,
|
||
0,
|
||
&DataLength
|
||
);
|
||
|
||
if ( ( NtStatus == STATUS_BUFFER_TOO_SMALL ) &&
|
||
( DataLength > 0 ) ) {
|
||
|
||
SubjectOwner = MIDL_user_allocate( DataLength );
|
||
|
||
if ( SubjectOwner == NULL ) {
|
||
|
||
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Now, query the owner that will be
|
||
// given access to the object
|
||
// created.
|
||
//
|
||
|
||
NtStatus = NtQueryInformationToken(
|
||
ClientToken,
|
||
TokenOwner,
|
||
SubjectOwner,
|
||
DataLength,
|
||
&DataLength
|
||
);
|
||
|
||
if ( NT_SUCCESS( NtStatus ) ) {
|
||
|
||
//
|
||
// Create an ACE that gives the
|
||
// owner full access.
|
||
//
|
||
|
||
AceLength = sizeof( ACE_HEADER ) +
|
||
sizeof( ACCESS_MASK ) +
|
||
RtlLengthSid(
|
||
SubjectOwner->Owner );
|
||
|
||
NewAce = (ACCESS_ALLOWED_ACE *)
|
||
MIDL_user_allocate( AceLength );
|
||
|
||
if ( NewAce == NULL ) {
|
||
|
||
NtStatus =
|
||
STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
} else {
|
||
|
||
NewAce->Header.AceType =
|
||
ACCESS_ALLOWED_ACE_TYPE;
|
||
|
||
NewAce->Header.AceSize = (USHORT) AceLength;
|
||
NewAce->Header.AceFlags = 0;
|
||
NewAce->Mask = USER_ALL_ACCESS;
|
||
|
||
//
|
||
// If the creator's access is
|
||
// to be restricted, change the
|
||
// AccessMask.
|
||
//
|
||
|
||
if (RestrictCreatorAccess) {
|
||
NewAce->Mask = DELETE |
|
||
USER_WRITE |
|
||
USER_FORCE_PASSWORD_CHANGE;
|
||
}
|
||
|
||
RtlCopySid(
|
||
RtlLengthSid(
|
||
SubjectOwner->Owner ),
|
||
(PSID)( &NewAce->SidStart ),
|
||
SubjectOwner->Owner );
|
||
|
||
//
|
||
// Allocate a new, larger ACL and
|
||
// copy the old one into it.
|
||
//
|
||
|
||
NtStatus =
|
||
RtlGetDaclSecurityDescriptor(
|
||
LocalDescriptor,
|
||
&DaclPresent,
|
||
&OldDacl,
|
||
&DaclDefaulted
|
||
);
|
||
|
||
if ( NT_SUCCESS( NtStatus ) ) {
|
||
|
||
NewDacl = MIDL_user_allocate(
|
||
OldDacl->AclSize +
|
||
AceLength );
|
||
|
||
if ( NewDacl == NULL ) {
|
||
|
||
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
} else {
|
||
|
||
RtlCopyMemory(
|
||
NewDacl,
|
||
OldDacl,
|
||
OldDacl->AclSize
|
||
);
|
||
|
||
NewDacl->AclSize =
|
||
OldDacl->AclSize +
|
||
(USHORT) AceLength;
|
||
|
||
//
|
||
// Add the new ACE
|
||
// to the new ACL.
|
||
//
|
||
|
||
NtStatus = RtlAddAce(
|
||
NewDacl,
|
||
ACL_REVISION2,
|
||
1, // add after first ACE (world)
|
||
(PVOID)NewAce,
|
||
AceLength
|
||
);
|
||
} // end_if (allocated NewDacl)
|
||
} // end_if (get DACL from SD)
|
||
} // end_if (allocated NewAce)
|
||
} // end_if (Query TokenOwner Succeeded)
|
||
} // end_if (Allocated TokenOwner buffer)
|
||
} // end_if (Query TokenOwner size Succeeded)
|
||
} // end_if (not admin)
|
||
} // end_if (Query TokenGroups Succeeded)
|
||
} // end_if (Allocated TokenGroups buffer)
|
||
} // end_if (Query TokenGroups size Succeeded)
|
||
|
||
IgnoreStatus = NtClose( ClientToken );
|
||
ASSERT(NT_SUCCESS(IgnoreStatus));
|
||
|
||
} // end_if (TokenOpened)
|
||
} // end_if (ImpersonatingClient)
|
||
} // end_if (TrustedClient)
|
||
|
||
if ( NT_SUCCESS( NtStatus ) ) {
|
||
|
||
//
|
||
// If we created a new DACL above, stick it on the security
|
||
// descriptor.
|
||
//
|
||
|
||
if ( NewDacl != NULL ) {
|
||
|
||
NtStatus = RtlCreateSecurityDescriptor(
|
||
&DaclDescriptor,
|
||
SECURITY_DESCRIPTOR_REVISION1
|
||
);
|
||
|
||
if ( NT_SUCCESS( NtStatus ) ) {
|
||
|
||
//
|
||
// Set the DACL on the LocalDescriptor. Note that this
|
||
// call will RtlFreeHeap() the old descriptor, and allocate
|
||
// a new one.
|
||
//
|
||
|
||
DaclDescriptor.Control = SE_DACL_PRESENT;
|
||
DaclDescriptor.Dacl = NewDacl;
|
||
|
||
NtStatus = RtlSetSecurityObject(
|
||
DACL_SECURITY_INFORMATION,
|
||
&DaclDescriptor,
|
||
&LocalDescriptor,
|
||
&GenericMapping,
|
||
NULL
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
if ( NT_SUCCESS( NtStatus ) ) {
|
||
|
||
//
|
||
// Copy the security descriptor and length into buffers for the
|
||
// caller. AceLength is 0 if we didn't add an ACE to the DACL
|
||
// above.
|
||
//
|
||
|
||
(*DescriptorLength) = (*DescriptorLength) + AceLength;
|
||
|
||
(*NewDescriptor) = MIDL_user_allocate( (*DescriptorLength) );
|
||
|
||
if ( (*NewDescriptor) == NULL ) {
|
||
|
||
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
} else {
|
||
|
||
RtlCopyMemory(
|
||
(*NewDescriptor),
|
||
LocalDescriptor,
|
||
(*DescriptorLength)
|
||
);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Free up local items that may have been allocated.
|
||
//
|
||
|
||
if ( LocalDescriptor != NULL ) {
|
||
RtlFreeHeap( RtlProcessHeap(), 0, LocalDescriptor );
|
||
}
|
||
|
||
if ( ClientGroups != NULL ) {
|
||
MIDL_user_free( ClientGroups );
|
||
}
|
||
|
||
if ( SubjectOwner != NULL ) {
|
||
MIDL_user_free( SubjectOwner );
|
||
}
|
||
|
||
if ( NewAce != NULL ) {
|
||
MIDL_user_free( NewAce );
|
||
}
|
||
|
||
if ( NewDacl != NULL ) {
|
||
MIDL_user_free( NewDacl );
|
||
}
|
||
|
||
|
||
if ( !NT_SUCCESS( NtStatus ) ) {
|
||
|
||
(*NewDescriptor) = NULL;
|
||
(*DescriptorLength) = 0;
|
||
}
|
||
|
||
return( NtStatus );
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SampModifyAccountSecurity(
|
||
IN SAMP_OBJECT_TYPE ObjectType,
|
||
IN BOOLEAN Admin,
|
||
IN PSECURITY_DESCRIPTOR OldDescriptor,
|
||
OUT PSECURITY_DESCRIPTOR *NewDescriptor,
|
||
OUT PULONG DescriptorLength
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This service modifies a self-relative security descriptor
|
||
for a USER or GROUP to add or remove account operator access.
|
||
|
||
|
||
Arguments:
|
||
|
||
ObjectType - Indicates the type of account for which a new security
|
||
descriptor is required. This must be either SampGroupObjectType
|
||
or SampUserObjectType.
|
||
|
||
Admin - if TRUE, indicates the security descriptor will be protecting
|
||
an object that is an admin object (e.g., is a member, directly
|
||
or indirectly, of the ADMINISTRATORS or an operator alias).
|
||
|
||
NewDescriptor - Receives a pointer to the new account's self-relative
|
||
security descriptor. Be sure to free this descriptor with
|
||
MIDL_user_free() when done.
|
||
|
||
DescriptorLength - Receives the length (in bytes) of the returned
|
||
security descriptor
|
||
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - A new security descriptor has been produced.
|
||
|
||
STATUS_INSUFFICIENT_RESOURCES - Memory could not be allocated to
|
||
produce the security descriptor.
|
||
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
SID_IDENTIFIER_AUTHORITY BuiltinAuthority = SECURITY_NT_AUTHORITY;
|
||
ULONG AccountSidBuffer[8];
|
||
PSID AccountAliasSid = &AccountSidBuffer[0];
|
||
NTSTATUS NtStatus = STATUS_SUCCESS;
|
||
NTSTATUS IgnoreStatus;
|
||
ULONG Length;
|
||
ULONG i,j;
|
||
ULONG AccountOpAceIndex;
|
||
ULONG AceCount;
|
||
PACL OldDacl;
|
||
PACL NewDacl = NULL;
|
||
BOOLEAN DaclDefaulted;
|
||
BOOLEAN DaclPresent;
|
||
ACL_SIZE_INFORMATION AclSizeInfo;
|
||
PACCESS_ALLOWED_ACE Ace;
|
||
PGENERIC_MAPPING GenericMapping;
|
||
ACCESS_MASK AccountOpAccess;
|
||
SECURITY_DESCRIPTOR AbsoluteDescriptor;
|
||
PSECURITY_DESCRIPTOR LocalDescriptor = NULL;
|
||
|
||
GENERIC_MAPPING GroupMap = {GROUP_READ,
|
||
GROUP_WRITE,
|
||
GROUP_EXECUTE,
|
||
GROUP_ALL_ACCESS
|
||
};
|
||
|
||
GENERIC_MAPPING UserMap = {USER_READ,
|
||
USER_WRITE,
|
||
USER_EXECUTE,
|
||
USER_ALL_ACCESS
|
||
};
|
||
|
||
|
||
NtStatus = RtlCopySecurityDescriptor(
|
||
OldDescriptor,
|
||
&LocalDescriptor
|
||
);
|
||
|
||
if (!NT_SUCCESS(NtStatus)) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Build the SID of the ACCOUNT_OPS alias, so we
|
||
// can see if is in the DACL or we can add it to the DACL.
|
||
//
|
||
|
||
RtlInitializeSid(
|
||
AccountAliasSid,
|
||
&BuiltinAuthority,
|
||
2
|
||
);
|
||
|
||
*(RtlSubAuthoritySid( AccountAliasSid, 0 )) =
|
||
SECURITY_BUILTIN_DOMAIN_RID;
|
||
|
||
*(RtlSubAuthoritySid( AccountAliasSid, 1 )) =
|
||
DOMAIN_ALIAS_RID_ACCOUNT_OPS;
|
||
|
||
//
|
||
// The approach is to set up an absolute security descriptor that
|
||
// contains the new DACL, and then merge that into the existing
|
||
// security descriptor.
|
||
//
|
||
|
||
|
||
IgnoreStatus = RtlCreateSecurityDescriptor(
|
||
&AbsoluteDescriptor,
|
||
SECURITY_DESCRIPTOR_REVISION1
|
||
);
|
||
ASSERT( NT_SUCCESS(IgnoreStatus) );
|
||
|
||
//
|
||
// Figure out the access granted to account operators and the
|
||
// generic mask to use.
|
||
//
|
||
|
||
if (ObjectType == SampUserObjectType) {
|
||
AccountOpAccess = USER_ALL_ACCESS;
|
||
GenericMapping = &UserMap;
|
||
} else if (ObjectType == SampGroupObjectType) {
|
||
AccountOpAccess = GROUP_ALL_ACCESS;
|
||
GenericMapping = &GroupMap;
|
||
} else {
|
||
//
|
||
// This doesn't apply to aliases, domains, or servers.
|
||
//
|
||
return(STATUS_INVALID_PARAMETER);
|
||
}
|
||
|
||
//
|
||
// Get the old DACL off the passed in security descriptor.
|
||
//
|
||
|
||
IgnoreStatus = RtlGetDaclSecurityDescriptor(
|
||
OldDescriptor,
|
||
&DaclPresent,
|
||
&OldDacl,
|
||
&DaclDefaulted
|
||
);
|
||
|
||
ASSERT(NT_SUCCESS(IgnoreStatus));
|
||
|
||
//
|
||
// We will only modify the DACL if it is present
|
||
//
|
||
|
||
if (!DaclPresent) {
|
||
*NewDescriptor = LocalDescriptor;
|
||
*DescriptorLength = RtlLengthSecurityDescriptor(LocalDescriptor);
|
||
return(STATUS_SUCCESS);
|
||
}
|
||
|
||
//
|
||
// Get the count of ACEs
|
||
//
|
||
|
||
IgnoreStatus = RtlQueryInformationAcl(
|
||
OldDacl,
|
||
&AclSizeInfo,
|
||
sizeof(AclSizeInfo),
|
||
AclSizeInformation
|
||
);
|
||
|
||
|
||
ASSERT(NT_SUCCESS(IgnoreStatus));
|
||
|
||
//
|
||
// Calculate the lenght of the new ACL.
|
||
//
|
||
|
||
Length = (ULONG)sizeof(ACL);
|
||
AccountOpAceIndex = 0xffffffff;
|
||
|
||
|
||
for (i = 0; i < AclSizeInfo.AceCount; i++) {
|
||
IgnoreStatus = RtlGetAce(
|
||
OldDacl,
|
||
i,
|
||
(PVOID *) &Ace
|
||
);
|
||
ASSERT(NT_SUCCESS(IgnoreStatus));
|
||
|
||
//
|
||
// Check if this is an access allowed ACE, and the ACE is for
|
||
// the Account Operators alias.
|
||
//
|
||
|
||
if ( (Ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE) &&
|
||
RtlEqualSid( AccountAliasSid,
|
||
&Ace->SidStart ) ) {
|
||
|
||
AccountOpAceIndex = i;
|
||
continue;
|
||
}
|
||
Length += Ace->Header.AceSize;
|
||
}
|
||
|
||
|
||
if (!Admin) {
|
||
|
||
//
|
||
// If we are making this account not be an admin account and it already
|
||
// has an account operator ace, we are done.
|
||
//
|
||
|
||
if ( AccountOpAceIndex != 0xffffffff ) {
|
||
|
||
*NewDescriptor = LocalDescriptor;
|
||
*DescriptorLength = RtlLengthSecurityDescriptor(LocalDescriptor);
|
||
return(STATUS_SUCCESS);
|
||
} else {
|
||
|
||
//
|
||
// Add the size of an account operator ace to the required length
|
||
//
|
||
|
||
Length += sizeof(ACCESS_ALLOWED_ACE) +
|
||
RtlLengthSid(AccountAliasSid) -
|
||
sizeof(ULONG);
|
||
}
|
||
|
||
}
|
||
|
||
NewDacl = RtlAllocateHeap( RtlProcessHeap(), 0, Length );
|
||
|
||
if (NewDacl == NULL) {
|
||
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto Cleanup;
|
||
}
|
||
|
||
IgnoreStatus = RtlCreateAcl( NewDacl, Length, ACL_REVISION2);
|
||
ASSERT( NT_SUCCESS(IgnoreStatus) );
|
||
|
||
//
|
||
// Add the old ACEs back into this ACL.
|
||
//
|
||
|
||
for (i = 0, j = 0; i < AclSizeInfo.AceCount; i++) {
|
||
if (i == AccountOpAceIndex) {
|
||
ASSERT(Admin);
|
||
continue;
|
||
}
|
||
//
|
||
// Add back in the old ACEs
|
||
//
|
||
|
||
IgnoreStatus = RtlGetAce(
|
||
OldDacl,
|
||
i,
|
||
(PVOID *) &Ace
|
||
);
|
||
ASSERT(NT_SUCCESS(IgnoreStatus));
|
||
|
||
IgnoreStatus = RtlAddAce (
|
||
NewDacl,
|
||
ACL_REVISION2,
|
||
j,
|
||
Ace,
|
||
Ace->Header.AceSize
|
||
);
|
||
ASSERT( NT_SUCCESS(IgnoreStatus) );
|
||
}
|
||
|
||
//
|
||
// If we are making this account not be an administrator, add the
|
||
// access allowed ACE for the account operator. This ACE is always
|
||
// the second to last one.
|
||
//
|
||
|
||
if (!Admin) {
|
||
IgnoreStatus = RtlAddAccessAllowedAce(
|
||
NewDacl,
|
||
ACL_REVISION2,
|
||
AccountOpAccess,
|
||
AccountAliasSid
|
||
);
|
||
ASSERT(NT_SUCCESS(IgnoreStatus));
|
||
}
|
||
|
||
//
|
||
// Insert this DACL into the security descriptor.
|
||
//
|
||
|
||
IgnoreStatus = RtlSetDaclSecurityDescriptor (
|
||
&AbsoluteDescriptor,
|
||
TRUE, // DACL present
|
||
NewDacl,
|
||
FALSE // DACL not defaulted
|
||
);
|
||
ASSERT(NT_SUCCESS(IgnoreStatus));
|
||
|
||
//
|
||
// Now call RtlSetSecurityObject to merge the existing security descriptor
|
||
// with the new DACL we just created.
|
||
//
|
||
|
||
|
||
NtStatus = RtlSetSecurityObject(
|
||
DACL_SECURITY_INFORMATION,
|
||
&AbsoluteDescriptor,
|
||
&LocalDescriptor,
|
||
GenericMapping,
|
||
NULL
|
||
);
|
||
if (!NT_SUCCESS(NtStatus)) {
|
||
goto Cleanup;
|
||
}
|
||
*NewDescriptor = LocalDescriptor;
|
||
*DescriptorLength = RtlLengthSecurityDescriptor(LocalDescriptor);
|
||
LocalDescriptor = NULL;
|
||
Cleanup:
|
||
|
||
if ( NewDacl != NULL ) {
|
||
RtlFreeHeap(RtlProcessHeap(),0, NewDacl );
|
||
}
|
||
if (LocalDescriptor != NULL) {
|
||
RtlDeleteSecurityObject(&LocalDescriptor);
|
||
}
|
||
|
||
return( NtStatus );
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SampGetObjectSD(
|
||
IN PSAMP_OBJECT Context,
|
||
OUT PULONG SecurityDescriptorLength,
|
||
OUT PSECURITY_DESCRIPTOR *SecurityDescriptor
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This retrieves a security descriptor from a SAM object's backing store.
|
||
|
||
|
||
|
||
|
||
Arguments:
|
||
|
||
Context - The object to which access is being requested.
|
||
|
||
SecurityDescriptorLength - Receives the length of the security descriptor.
|
||
|
||
SecurityDescriptor - Receives a pointer to the security descriptor.
|
||
|
||
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - The security descriptor has been retrieved.
|
||
|
||
STATUS_INTERNAL_DB_CORRUPTION - The object does not have a security descriptor.
|
||
This is bad.
|
||
|
||
|
||
STATUS_INSUFFICIENT_RESOURCES - Memory could not be allocated to retrieve the
|
||
security descriptor.
|
||
|
||
STATUS_UNKNOWN_REVISION - The security descriptor retrieved is no one known by
|
||
this revision of SAM.
|
||
|
||
|
||
|
||
--*/
|
||
{
|
||
|
||
NTSTATUS NtStatus;
|
||
ULONG Revision;
|
||
|
||
|
||
(*SecurityDescriptorLength) = 0;
|
||
|
||
NtStatus = SampGetAccessAttribute(
|
||
Context,
|
||
SAMP_OBJECT_SECURITY_DESCRIPTOR,
|
||
TRUE, // Make copy
|
||
&Revision,
|
||
SecurityDescriptor
|
||
);
|
||
|
||
if (NT_SUCCESS(NtStatus)) {
|
||
|
||
if ( ((Revision && 0xFFFF0000) > SAMP_MAJOR_REVISION) ||
|
||
(Revision > SAMP_REVISION) ) {
|
||
|
||
NtStatus = STATUS_UNKNOWN_REVISION;
|
||
}
|
||
|
||
|
||
if (!NT_SUCCESS(NtStatus)) {
|
||
MIDL_user_free( (*SecurityDescriptor) );
|
||
}
|
||
}
|
||
|
||
|
||
if (NT_SUCCESS(NtStatus)) {
|
||
*SecurityDescriptorLength = RtlLengthSecurityDescriptor(
|
||
(*SecurityDescriptor) );
|
||
}
|
||
|
||
return(NtStatus);
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SamrSetSecurityObject(
|
||
IN SAMPR_HANDLE ObjectHandle,
|
||
IN SECURITY_INFORMATION SecurityInformation,
|
||
IN PSAMPR_SR_SECURITY_DESCRIPTOR SecurityDescriptor
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function (SamrSetSecurityObject) takes a well formed Security
|
||
Descriptor provided by the caller and assigns specified portions of
|
||
it to an object. Based on the flags set in the SecurityInformation
|
||
parameter and the caller's access rights, this procedure will
|
||
replace any or all of the security information associated with an
|
||
object.
|
||
|
||
This is the only function available to users and applications for
|
||
changing security information, including the owner ID, group ID, and
|
||
the discretionary and system ACLs of an object. The caller must
|
||
have WRITE_OWNER access to the object to change the owner or primary
|
||
group of the object. The caller must have WRITE_DAC access to the
|
||
object to change the discretionary ACL. The caller must have
|
||
ACCESS_SYSTEM_SECURITY access to an object to assign a system ACL
|
||
to the object.
|
||
|
||
This API is modelled after the NtSetSecurityObject() system service.
|
||
|
||
|
||
Parameters:
|
||
|
||
ObjectHandle - A handle to an existing object.
|
||
|
||
SecurityInformation - Indicates which security information is to
|
||
be applied to the object. The value(s) to be assigned are
|
||
passed in the SecurityDescriptor parameter.
|
||
|
||
|
||
SecurityDescriptor - A pointer to a well formed self-relative Security
|
||
Descriptor and corresponding length.
|
||
|
||
|
||
Return Values:
|
||
|
||
STATUS_SUCCESS - normal, successful completion.
|
||
|
||
STATUS_ACCESS_DENIED - The specified handle was not opened for
|
||
either WRITE_OWNER, WRITE_DAC, or ACCESS_SYSTEM_SECURITY
|
||
access.
|
||
|
||
STATUS_INVALID_HANDLE - The specified handle is not that of an
|
||
opened SAM object.
|
||
|
||
STATUS_BAD_DESCRIPTOR_FORMAT - Indicates something about security descriptor
|
||
is not valid. This may indicate that the structure of the descriptor is
|
||
not valid or that a component of the descriptor specified via the
|
||
SecurityInformation parameter is not present in the security descriptor.
|
||
|
||
STATUS_INVALID_PARAMETER - Indicates no security information was specified.
|
||
|
||
STATUS_LAST_ADMIN - Indicates the new SD could potentially lead
|
||
to the administrator account being unusable and therefore
|
||
the new protection is being rejected.
|
||
|
||
--*/
|
||
{
|
||
|
||
NTSTATUS NtStatus, IgnoreStatus, TmpStatus;
|
||
PSAMP_OBJECT Context;
|
||
SAMP_OBJECT_TYPE FoundType;
|
||
SECURITY_DB_OBJECT_TYPE SecurityDbObjectType;
|
||
ACCESS_MASK DesiredAccess;
|
||
PSECURITY_DESCRIPTOR RetrieveSD, SetSD;
|
||
PISECURITY_DESCRIPTOR PassedSD;
|
||
ULONG RetrieveSDLength;
|
||
ULONG ObjectRid;
|
||
ULONG SecurityDescriptorIndex;
|
||
HANDLE ClientToken;
|
||
BOOLEAN NotificationType = TRUE;
|
||
|
||
|
||
|
||
|
||
|
||
//
|
||
// Make sure we understand what RPC is doing for (to) us.
|
||
//
|
||
|
||
if (SecurityDescriptor == NULL) {
|
||
return(STATUS_BAD_DESCRIPTOR_FORMAT);
|
||
}
|
||
if (SecurityDescriptor->SecurityDescriptor == NULL) {
|
||
return(STATUS_BAD_DESCRIPTOR_FORMAT);
|
||
}
|
||
|
||
PassedSD = (PISECURITY_DESCRIPTOR)(SecurityDescriptor->SecurityDescriptor);
|
||
|
||
|
||
//
|
||
// Validate the passed security descriptor
|
||
//
|
||
|
||
NtStatus = SampValidatePassedSD( SecurityDescriptor->Length, PassedSD );
|
||
if (!NT_SUCCESS(NtStatus)) {
|
||
return(NtStatus);
|
||
}
|
||
|
||
|
||
|
||
|
||
//
|
||
// Set the desired access based upon the specified SecurityInformation
|
||
//
|
||
|
||
DesiredAccess = 0;
|
||
if ( SecurityInformation & SACL_SECURITY_INFORMATION) {
|
||
DesiredAccess |= ACCESS_SYSTEM_SECURITY;
|
||
}
|
||
if ( SecurityInformation & (OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION)) {
|
||
DesiredAccess |= WRITE_OWNER;
|
||
}
|
||
if ( SecurityInformation & DACL_SECURITY_INFORMATION ) {
|
||
DesiredAccess |= WRITE_DAC;
|
||
}
|
||
|
||
|
||
//
|
||
// If no information was specified, then return invalid parameter.
|
||
//
|
||
if (DesiredAccess == 0) {
|
||
|
||
return(STATUS_INVALID_PARAMETER);
|
||
}
|
||
|
||
|
||
//
|
||
// Make sure the specified fields are present in the provided security descriptor.
|
||
// You can't screw up an SACL or DACL, but you can screw up an owner or group.
|
||
// Security descriptors must have owner and group fields.
|
||
//
|
||
|
||
|
||
if ( (SecurityInformation & OWNER_SECURITY_INFORMATION) ) {
|
||
if (PassedSD->Owner == NULL) {
|
||
return(STATUS_BAD_DESCRIPTOR_FORMAT);
|
||
}
|
||
}
|
||
|
||
|
||
if ( (SecurityInformation & GROUP_SECURITY_INFORMATION) ) {
|
||
if (PassedSD->Group == NULL) {
|
||
return(STATUS_BAD_DESCRIPTOR_FORMAT);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
//
|
||
// See if the handle is valid and opened for the requested access
|
||
//
|
||
|
||
|
||
NtStatus = SampAcquireWriteLock();
|
||
if (!NT_SUCCESS(NtStatus)) {
|
||
return(NtStatus);
|
||
}
|
||
|
||
|
||
Context = (PSAMP_OBJECT)ObjectHandle;
|
||
NtStatus = SampLookupContext(
|
||
Context,
|
||
DesiredAccess,
|
||
SampUnknownObjectType, // ExpectedType
|
||
&FoundType
|
||
);
|
||
|
||
switch ( FoundType ) {
|
||
|
||
case SampServerObjectType: {
|
||
|
||
SecurityDescriptorIndex = SAMP_SERVER_SECURITY_DESCRIPTOR;
|
||
ObjectRid = 0L;
|
||
NotificationType = FALSE;
|
||
break;
|
||
}
|
||
|
||
case SampDomainObjectType: {
|
||
|
||
//
|
||
// BUGBUG Bug #4388 - allow account operators to access pre-340
|
||
// databases
|
||
//
|
||
// This fix should NEVER be checked in. It is for one-time
|
||
// internal use only.
|
||
//
|
||
// Pre-340 databases had security descriptors on the domain
|
||
// objects that didn't allow account operators to operate on
|
||
// accounts. Replicating one of these old databases to a
|
||
// newer release copies the bad security descriptors. To avoid
|
||
// this, do the following ONE TIME only:
|
||
//
|
||
// Uncomment the code immediately below. Build a new
|
||
// SAMSRV.DLL. Put it on a BDC running the target version
|
||
// of the system. Replicate the pre-340 database from the
|
||
// PDC (which may be running a more recent version of the
|
||
// system, but still has the bad pre-340 descriptors from
|
||
// earlier replications).
|
||
//
|
||
// That's it; you've got a fixed database that can be safely
|
||
// replicated in the future. Get rid of this temporary code.
|
||
// (And put the real SAMSRV.DLL back on the BDC).
|
||
//
|
||
// IgnoreStatus = SampDeReferenceContext( Context, FALSE );
|
||
// IgnoreStatus = SampReleaseWriteLock( FALSE );
|
||
// return( STATUS_SUCCESS );
|
||
//
|
||
|
||
SecurityDbObjectType = SecurityDbObjectSamDomain;
|
||
SecurityDescriptorIndex = SAMP_DOMAIN_SECURITY_DESCRIPTOR;
|
||
ObjectRid = 0L;
|
||
break;
|
||
}
|
||
|
||
case SampUserObjectType: {
|
||
|
||
SecurityDbObjectType = SecurityDbObjectSamUser;
|
||
SecurityDescriptorIndex = SAMP_USER_SECURITY_DESCRIPTOR;
|
||
ObjectRid = Context->TypeBody.User.Rid;
|
||
break;
|
||
}
|
||
|
||
case SampGroupObjectType: {
|
||
|
||
SecurityDbObjectType = SecurityDbObjectSamGroup;
|
||
SecurityDescriptorIndex = SAMP_GROUP_SECURITY_DESCRIPTOR;
|
||
ObjectRid = Context->TypeBody.Group.Rid;
|
||
break;
|
||
}
|
||
|
||
case SampAliasObjectType: {
|
||
|
||
SecurityDbObjectType = SecurityDbObjectSamAlias;
|
||
SecurityDescriptorIndex = SAMP_ALIAS_SECURITY_DESCRIPTOR;
|
||
ObjectRid = Context->TypeBody.Alias.Rid;
|
||
break;
|
||
}
|
||
|
||
default: {
|
||
|
||
NotificationType = FALSE;
|
||
}
|
||
}
|
||
|
||
if (NT_SUCCESS(NtStatus)) {
|
||
|
||
|
||
//
|
||
// Get the security descriptor
|
||
//
|
||
|
||
|
||
RetrieveSD = NULL;
|
||
RetrieveSDLength = 0;
|
||
NtStatus = SampGetObjectSD( Context, &RetrieveSDLength, &RetrieveSD);
|
||
|
||
//
|
||
// Make sure the descriptor does not break any Administrator
|
||
// restrictions.
|
||
//
|
||
|
||
NtStatus = SampCheckForDescriptorRestrictions( Context,
|
||
FoundType,
|
||
ObjectRid,
|
||
PassedSD );
|
||
|
||
if (NT_SUCCESS(NtStatus)) {
|
||
|
||
//
|
||
// copy the retrieved descriptor into process heap so we can use RTL routines.
|
||
//
|
||
|
||
SetSD = NULL;
|
||
if (NT_SUCCESS(NtStatus)) {
|
||
|
||
SetSD = RtlAllocateHeap( RtlProcessHeap(), 0, RetrieveSDLength );
|
||
if ( SetSD == NULL) {
|
||
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
||
} else {
|
||
RtlCopyMemory( SetSD, RetrieveSD, RetrieveSDLength );
|
||
}
|
||
}
|
||
|
||
if (NT_SUCCESS(NtStatus)) {
|
||
|
||
//
|
||
// if the caller is replacing the owner and he is not
|
||
// trusted, then a handle to the impersonation token is
|
||
// necessary. If the caller is trusted then take process
|
||
// token.
|
||
//
|
||
|
||
ClientToken = 0;
|
||
if ( (SecurityInformation & OWNER_SECURITY_INFORMATION) ) {
|
||
|
||
if(!Context->TrustedClient) {
|
||
|
||
NtStatus = I_RpcMapWin32Status(RpcImpersonateClient( NULL ));
|
||
|
||
if (NT_SUCCESS(NtStatus)) {
|
||
|
||
NtStatus = NtOpenThreadToken(
|
||
NtCurrentThread(),
|
||
TOKEN_QUERY,
|
||
TRUE, //OpenAsSelf
|
||
&ClientToken
|
||
);
|
||
ASSERT( (ClientToken == 0) || NT_SUCCESS(NtStatus) );
|
||
|
||
|
||
|
||
//
|
||
// Stop impersonating the client
|
||
//
|
||
|
||
IgnoreStatus = I_RpcMapWin32Status(RpcRevertToSelf());
|
||
ASSERT( NT_SUCCESS(IgnoreStatus) );
|
||
}
|
||
}
|
||
else {
|
||
|
||
//
|
||
// trusted client
|
||
//
|
||
|
||
NtStatus = NtOpenProcessToken(
|
||
NtCurrentProcess(),
|
||
TOKEN_QUERY,
|
||
&ClientToken );
|
||
|
||
ASSERT( (ClientToken == 0) || NT_SUCCESS(NtStatus) );
|
||
|
||
}
|
||
|
||
}
|
||
|
||
if (NT_SUCCESS(NtStatus)) {
|
||
|
||
|
||
//
|
||
// Build the replacement security descriptor.
|
||
// This must be done in process heap to satisfy the needs of the RTL
|
||
// routine.
|
||
//
|
||
|
||
|
||
NtStatus = RtlSetSecurityObject(
|
||
SecurityInformation,
|
||
PassedSD,
|
||
&SetSD,
|
||
&SampObjectInformation[FoundType].GenericMapping,
|
||
ClientToken
|
||
);
|
||
if (ClientToken != 0) {
|
||
IgnoreStatus = NtClose( ClientToken );
|
||
ASSERT(NT_SUCCESS(IgnoreStatus));
|
||
}
|
||
|
||
|
||
|
||
if (NT_SUCCESS(NtStatus)) {
|
||
|
||
//
|
||
// Apply the security descriptor back onto the object.
|
||
//
|
||
|
||
NtStatus = SampSetAccessAttribute(
|
||
Context,
|
||
SecurityDescriptorIndex,
|
||
SetSD,
|
||
RtlLengthSecurityDescriptor(SetSD)
|
||
);
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
//
|
||
// Free up allocated memory
|
||
//
|
||
|
||
if (RetrieveSD != NULL) {
|
||
MIDL_user_free( RetrieveSD );
|
||
}
|
||
if (SetSD != NULL) {
|
||
RtlFreeHeap( RtlProcessHeap(), 0, SetSD );
|
||
}
|
||
|
||
|
||
|
||
|
||
//
|
||
// De-reference the object
|
||
//
|
||
|
||
if ( NT_SUCCESS( NtStatus ) ) {
|
||
|
||
NtStatus = SampDeReferenceContext( Context, TRUE );
|
||
|
||
} else {
|
||
|
||
IgnoreStatus = SampDeReferenceContext( Context, FALSE );
|
||
}
|
||
}
|
||
|
||
} //end_if
|
||
|
||
|
||
|
||
//
|
||
// Commit the changes to disk.
|
||
//
|
||
|
||
if ( NT_SUCCESS( NtStatus ) ) {
|
||
|
||
NtStatus = SampCommitAndRetainWriteLock();
|
||
|
||
if ( NotificationType && NT_SUCCESS( NtStatus ) ) {
|
||
|
||
SampNotifyNetlogonOfDelta(
|
||
SecurityDbChange,
|
||
SecurityDbObjectType,
|
||
ObjectRid,
|
||
(PUNICODE_STRING) NULL,
|
||
(DWORD) FALSE, // Replicate immediately
|
||
NULL // Delta data
|
||
);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// Release lock and propagate errors
|
||
//
|
||
|
||
TmpStatus = SampReleaseWriteLock( FALSE );
|
||
|
||
if (NT_SUCCESS(NtStatus)) {
|
||
NtStatus = TmpStatus;
|
||
}
|
||
|
||
|
||
return(NtStatus);
|
||
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
SampValidatePassedSD(
|
||
IN ULONG Length,
|
||
IN PISECURITY_DESCRIPTOR PassedSD
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine validates that a passed security descriptor is valid and does
|
||
not extend beyond its expressed length.
|
||
|
||
|
||
Parameters:
|
||
|
||
Length - The length of the security descriptor. This should be what RPC
|
||
used to allocate memory to receive the security descriptor.
|
||
|
||
PassedSD - Points to the security descriptor to inspect.
|
||
|
||
|
||
Return Values:
|
||
|
||
STATUS_SUCCESS - The security descriptor is valid.
|
||
|
||
STATUS_BAD_DESCRIPTOR_FORMAT - Something was wrong with the security
|
||
descriptor. It might have extended beyond its limits or had an
|
||
invalid component.
|
||
|
||
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS NtStatus;
|
||
|
||
PACL Acl;
|
||
PSID Sid;
|
||
PUCHAR SDEnd;
|
||
BOOLEAN Present, IgnoreBoolean;
|
||
|
||
|
||
if (Length < SECURITY_DESCRIPTOR_MIN_LENGTH) {
|
||
return(STATUS_BAD_DESCRIPTOR_FORMAT);
|
||
}
|
||
|
||
SDEnd = (PUCHAR)PassedSD + Length;
|
||
|
||
|
||
try {
|
||
|
||
|
||
//
|
||
// Make sure the DACL is within the SD
|
||
//
|
||
|
||
NtStatus = RtlGetDaclSecurityDescriptor(
|
||
(PSECURITY_DESCRIPTOR)PassedSD,
|
||
&Present,
|
||
&Acl,
|
||
&IgnoreBoolean
|
||
);
|
||
if (!NT_SUCCESS(NtStatus)) {
|
||
return( STATUS_BAD_DESCRIPTOR_FORMAT );
|
||
}
|
||
|
||
if (Present) {
|
||
if (Acl != NULL) {
|
||
|
||
//
|
||
// Make sure the ACl header is in the buffer.
|
||
//
|
||
|
||
if ( (((PUCHAR)Acl)+sizeof(ACL) > SDEnd) ||
|
||
(((PUCHAR)Acl) < (PUCHAR)PassedSD) ) {
|
||
return( STATUS_BAD_DESCRIPTOR_FORMAT );
|
||
}
|
||
|
||
//
|
||
// Make sure the rest of the ACL is within the buffer
|
||
//
|
||
|
||
if ( ((PUCHAR)Acl)+Acl->AclSize > SDEnd) {
|
||
return( STATUS_BAD_DESCRIPTOR_FORMAT );
|
||
}
|
||
|
||
//
|
||
// Make sure the rest of the ACL is valid
|
||
//
|
||
|
||
if (!RtlValidAcl( Acl )) {
|
||
return( STATUS_BAD_DESCRIPTOR_FORMAT );
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// Make sure the SACL is within the SD
|
||
//
|
||
|
||
NtStatus = RtlGetSaclSecurityDescriptor(
|
||
(PSECURITY_DESCRIPTOR)PassedSD,
|
||
&Present,
|
||
&Acl,
|
||
&IgnoreBoolean
|
||
);
|
||
if (!NT_SUCCESS(NtStatus)) {
|
||
return( STATUS_BAD_DESCRIPTOR_FORMAT );
|
||
}
|
||
|
||
if (Present) {
|
||
if (Acl != NULL) {
|
||
|
||
//
|
||
// Make sure the ACl header is in the buffer.
|
||
//
|
||
|
||
if ( (((PUCHAR)Acl)+sizeof(ACL) > SDEnd) ||
|
||
(((PUCHAR)Acl) < (PUCHAR)PassedSD) ) {
|
||
return( STATUS_BAD_DESCRIPTOR_FORMAT );
|
||
}
|
||
|
||
//
|
||
// Make sure the rest of the ACL is within the buffer
|
||
//
|
||
|
||
if ( ((PUCHAR)Acl)+Acl->AclSize > SDEnd) {
|
||
return( STATUS_BAD_DESCRIPTOR_FORMAT );
|
||
}
|
||
|
||
//
|
||
// Make sure the rest of the ACL is valid
|
||
//
|
||
|
||
if (!RtlValidAcl( Acl )) {
|
||
return( STATUS_BAD_DESCRIPTOR_FORMAT );
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Make sure the Owner SID is within the SD
|
||
//
|
||
|
||
NtStatus = RtlGetOwnerSecurityDescriptor(
|
||
(PSECURITY_DESCRIPTOR)PassedSD,
|
||
&Sid,
|
||
&IgnoreBoolean
|
||
);
|
||
if (!NT_SUCCESS(NtStatus)) {
|
||
return( STATUS_BAD_DESCRIPTOR_FORMAT );
|
||
}
|
||
|
||
if (Sid != NULL) {
|
||
|
||
//
|
||
// Make sure the SID header is in the SD
|
||
//
|
||
|
||
if ( (((PUCHAR)Sid)+sizeof(SID)-(ANYSIZE_ARRAY*sizeof(ULONG)) > SDEnd) ||
|
||
(((PUCHAR)Sid) < (PUCHAR)PassedSD) ) {
|
||
return( STATUS_BAD_DESCRIPTOR_FORMAT );
|
||
}
|
||
|
||
|
||
//
|
||
// Make sure there aren't too many sub-authorities
|
||
//
|
||
|
||
if (((PISID)Sid)->SubAuthorityCount > SID_MAX_SUB_AUTHORITIES) {
|
||
return( STATUS_BAD_DESCRIPTOR_FORMAT );
|
||
}
|
||
|
||
|
||
//
|
||
// Make sure the rest of the SID is within the SD
|
||
//
|
||
|
||
if ( ((PUCHAR)Sid)+RtlLengthSid(Sid) > SDEnd) {
|
||
return( STATUS_BAD_DESCRIPTOR_FORMAT );
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// Make sure the Group SID is within the SD
|
||
//
|
||
|
||
NtStatus = RtlGetGroupSecurityDescriptor(
|
||
(PSECURITY_DESCRIPTOR)PassedSD,
|
||
&Sid,
|
||
&IgnoreBoolean
|
||
);
|
||
if (!NT_SUCCESS(NtStatus)) {
|
||
return( STATUS_BAD_DESCRIPTOR_FORMAT );
|
||
}
|
||
|
||
if (Sid != NULL) {
|
||
|
||
//
|
||
// Make sure the SID header is in the SD
|
||
//
|
||
|
||
if ( (((PUCHAR)Sid)+sizeof(SID)-(ANYSIZE_ARRAY*sizeof(ULONG)) > SDEnd) ||
|
||
(((PUCHAR)Sid) < (PUCHAR)PassedSD) ) {
|
||
return( STATUS_BAD_DESCRIPTOR_FORMAT );
|
||
}
|
||
|
||
|
||
//
|
||
// Make sure there aren't too many sub-authorities
|
||
//
|
||
|
||
if (((PISID)Sid)->SubAuthorityCount > SID_MAX_SUB_AUTHORITIES) {
|
||
return( STATUS_BAD_DESCRIPTOR_FORMAT );
|
||
}
|
||
|
||
|
||
//
|
||
// Make sure the rest of the SID is within the SD
|
||
//
|
||
|
||
if ( ((PUCHAR)Sid)+RtlLengthSid(Sid) > SDEnd) {
|
||
return( STATUS_BAD_DESCRIPTOR_FORMAT );
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
return( STATUS_BAD_DESCRIPTOR_FORMAT );
|
||
} // end_try
|
||
|
||
|
||
|
||
|
||
return(STATUS_SUCCESS);
|
||
}
|
||
|
||
NTSTATUS
|
||
SampCheckForDescriptorRestrictions(
|
||
IN PSAMP_OBJECT Context,
|
||
IN SAMP_OBJECT_TYPE ObjectType,
|
||
IN ULONG ObjectRid,
|
||
IN PISECURITY_DESCRIPTOR PassedSD
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function ensures that the passed security descriptor,
|
||
which is being applied to an object of type 'FoundType' with
|
||
a Rid of value 'ObjectRid', does not violate any policies.
|
||
For example, you can not set protection on the Administrator
|
||
user account such that the administrator is unable to change
|
||
her password.
|
||
|
||
|
||
|
||
Parameters:
|
||
|
||
Context - The caller's context. This is used to determine
|
||
whether the caller is trusted or not. If the caller is
|
||
trusted, then there are no restrictions.
|
||
|
||
ObjectType - The type of object the new security descriptor
|
||
is being applied to.
|
||
|
||
ObjectRid - The RID of the object the new security descriptor
|
||
is being applied to.
|
||
|
||
PassedSD - The security descriptor passed by the client.
|
||
|
||
|
||
Return Values:
|
||
|
||
STATUS_SUCCESS - normal, successful completion.
|
||
|
||
STATUS_LAST_ADMIN - Indicates the new SD could potentially lead
|
||
to the administrator account being unusable and therefore
|
||
the new protection is being rejected.
|
||
|
||
|
||
|
||
--*/
|
||
{
|
||
|
||
NTSTATUS
|
||
NtStatus;
|
||
|
||
BOOLEAN
|
||
DaclPresent,
|
||
AdminSid,
|
||
Done,
|
||
IgnoreBoolean;
|
||
|
||
PACL
|
||
Dacl;
|
||
|
||
ACL_SIZE_INFORMATION
|
||
DaclInfo;
|
||
|
||
PACCESS_ALLOWED_ACE
|
||
Ace;
|
||
|
||
ACCESS_MASK
|
||
Accesses,
|
||
Remaining;
|
||
|
||
ULONG
|
||
AceIndex;
|
||
|
||
GENERIC_MAPPING
|
||
UserMap = {USER_READ,
|
||
USER_WRITE,
|
||
USER_EXECUTE,
|
||
USER_ALL_ACCESS};
|
||
|
||
//
|
||
// No checking for trusted client operations
|
||
//
|
||
|
||
if (Context->TrustedClient) {
|
||
return(STATUS_SUCCESS);
|
||
}
|
||
|
||
|
||
|
||
NtStatus = RtlGetDaclSecurityDescriptor ( (PSECURITY_DESCRIPTOR)PassedSD,
|
||
&DaclPresent,
|
||
&Dacl,
|
||
&IgnoreBoolean //DaclDefaulted
|
||
);
|
||
ASSERT(NT_SUCCESS(NtStatus));
|
||
|
||
|
||
if (!DaclPresent) {
|
||
|
||
//
|
||
// Not replacing the DACL
|
||
//
|
||
|
||
return(STATUS_SUCCESS);
|
||
}
|
||
|
||
if (Dacl == NULL) {
|
||
|
||
//
|
||
// Assigning "World all access"
|
||
//
|
||
|
||
return(STATUS_SUCCESS);
|
||
}
|
||
|
||
if (!RtlValidAcl(Dacl)) {
|
||
return(STATUS_INVALID_ACL);
|
||
}
|
||
|
||
NtStatus = RtlQueryInformationAcl ( Dacl,
|
||
&DaclInfo,
|
||
sizeof(ACL_SIZE_INFORMATION),
|
||
AclSizeInformation
|
||
);
|
||
ASSERT(NT_SUCCESS(NtStatus));
|
||
|
||
|
||
|
||
|
||
//
|
||
// Enforce Administrator user policies
|
||
//
|
||
|
||
NtStatus = STATUS_SUCCESS;
|
||
if (ObjectRid == DOMAIN_USER_RID_ADMIN) {
|
||
|
||
ASSERT(ObjectType == SampUserObjectType);
|
||
|
||
//
|
||
// For the administrator account, the ACL must grant
|
||
// these accesses:
|
||
//
|
||
|
||
Remaining = USER_READ_GENERAL |
|
||
USER_READ_PREFERENCES |
|
||
USER_WRITE_PREFERENCES |
|
||
USER_READ_LOGON |
|
||
USER_READ_ACCOUNT |
|
||
USER_WRITE_ACCOUNT |
|
||
USER_CHANGE_PASSWORD |
|
||
USER_FORCE_PASSWORD_CHANGE |
|
||
USER_LIST_GROUPS |
|
||
USER_READ_GROUP_INFORMATION |
|
||
USER_WRITE_GROUP_INFORMATION;
|
||
|
||
//
|
||
// to these SIDs:
|
||
//
|
||
// <domain>\Administrator
|
||
// <builtin>\Administrators
|
||
//
|
||
// It doesn't matter which accesses are granted to which SIDs,
|
||
// as long as collectively all the accesses are granted.
|
||
//
|
||
|
||
//
|
||
// Walk the ACEs collecting accesses that are granted to these
|
||
// SIDs. Make sure there are no DENYs that prevent them from
|
||
// being granted.
|
||
//
|
||
|
||
Done = FALSE;
|
||
for ( AceIndex=0;
|
||
(AceIndex < DaclInfo.AceCount) && !Done;
|
||
AceIndex++) {
|
||
|
||
NtStatus = RtlGetAce ( Dacl, AceIndex, &((PVOID)Ace) );
|
||
|
||
//
|
||
// Don't do anything with inherit-only ACEs
|
||
//
|
||
|
||
if ((Ace->Header.AceFlags & INHERIT_ONLY_ACE) == 0) {
|
||
|
||
//
|
||
// Note that we expect ACCESS_ALLOWED_ACE and ACCESS_DENIED_ACE
|
||
// to be identical structures in the following switch statement.
|
||
//
|
||
|
||
switch (Ace->Header.AceType) {
|
||
case ACCESS_ALLOWED_ACE_TYPE:
|
||
case ACCESS_DENIED_ACE_TYPE:
|
||
{
|
||
//
|
||
// Is this an interesting SID
|
||
//
|
||
|
||
AdminSid =
|
||
RtlEqualSid( ((PSID)(&Ace->SidStart)),
|
||
SampAdministratorUserSid)
|
||
||
|
||
RtlEqualSid( ((PSID)(&Ace->SidStart)),
|
||
SampAdministratorsAliasSid);
|
||
if (AdminSid) {
|
||
|
||
//
|
||
// Map the accesses granted or denied
|
||
//
|
||
|
||
Accesses = Ace->Mask;
|
||
RtlMapGenericMask( &Accesses, &UserMap );
|
||
|
||
if (Ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE) {
|
||
|
||
Remaining &= ~Accesses;
|
||
if (Remaining == 0) {
|
||
|
||
//
|
||
// All necessary accesses granted
|
||
//
|
||
|
||
Done = TRUE;
|
||
}
|
||
|
||
} else {
|
||
ASSERT(Ace->Header.AceType == ACCESS_DENIED_ACE_TYPE);
|
||
|
||
if (Remaining & Accesses) {
|
||
|
||
//
|
||
// We've just been denied some necessary
|
||
// accesses that haven't yet been granted.
|
||
//
|
||
|
||
Done = TRUE;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
default:
|
||
break;
|
||
} // end_switch
|
||
|
||
if (Done) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
} // end_for
|
||
|
||
if (Remaining != 0) {
|
||
NtStatus = STATUS_LAST_ADMIN;
|
||
}
|
||
|
||
|
||
} // end_if (Administrator Account)
|
||
|
||
|
||
|
||
return(NtStatus);
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SamrQuerySecurityObject(
|
||
IN SAMPR_HANDLE ObjectHandle,
|
||
IN SECURITY_INFORMATION SecurityInformation,
|
||
OUT PSAMPR_SR_SECURITY_DESCRIPTOR *SecurityDescriptor
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function (SamrQuerySecurityObject) returns to the caller requested
|
||
security information currently assigned to an object.
|
||
|
||
Based on the caller's access rights this procedure
|
||
will return a security descriptor containing any or all of the
|
||
object's owner ID, group ID, discretionary ACL or system ACL. To
|
||
read the owner ID, group ID, or the discretionary ACL the caller
|
||
must be granted READ_CONTROL access to the object. To read the
|
||
system ACL the caller must be granted ACCESS_SYSTEM_SECURITY
|
||
access.
|
||
|
||
This API is modelled after the NtQuerySecurityObject() system
|
||
service.
|
||
|
||
|
||
Parameters:
|
||
|
||
ObjectHandle - A handle to an existing object.
|
||
|
||
SecurityInformation - Supplies a value describing which pieces of
|
||
security information are being queried.
|
||
|
||
SecurityDescriptor - Provides a pointer to a structure to be filled
|
||
in with a security descriptor containing the requested security
|
||
information. This information is returned in the form of a
|
||
self-relative security descriptor.
|
||
|
||
Return Values:
|
||
|
||
STATUS_SUCCESS - normal, successful completion.
|
||
|
||
STATUS_ACCESS_DENIED - The specified handle was not opened for
|
||
either READ_CONTROL or ACCESS_SYSTEM_SECURITY
|
||
access.
|
||
|
||
STATUS_INVALID_HANDLE - The specified handle is not that of an
|
||
opened SAM object.
|
||
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS NtStatus, IgnoreStatus;
|
||
PSAMP_OBJECT Context;
|
||
SAMP_OBJECT_TYPE FoundType;
|
||
ACCESS_MASK DesiredAccess;
|
||
PSAMPR_SR_SECURITY_DESCRIPTOR RpcSD;
|
||
PSECURITY_DESCRIPTOR RetrieveSD, ReturnSD;
|
||
ULONG RetrieveSDLength, ReturnSDLength;
|
||
|
||
|
||
|
||
ReturnSD = NULL;
|
||
|
||
|
||
|
||
//
|
||
// Make sure we understand what RPC is doing for (to) us.
|
||
//
|
||
|
||
ASSERT (*SecurityDescriptor == NULL);
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
//
|
||
// Set the desired access based upon the requested SecurityInformation
|
||
//
|
||
|
||
DesiredAccess = 0;
|
||
if ( SecurityInformation & SACL_SECURITY_INFORMATION) {
|
||
DesiredAccess |= ACCESS_SYSTEM_SECURITY;
|
||
}
|
||
if ( SecurityInformation & (DACL_SECURITY_INFORMATION |
|
||
OWNER_SECURITY_INFORMATION |
|
||
GROUP_SECURITY_INFORMATION)
|
||
) {
|
||
DesiredAccess |= READ_CONTROL;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
//
|
||
// Allocate the first block of returned memory
|
||
//
|
||
|
||
RpcSD = MIDL_user_allocate( sizeof(SAMPR_SR_SECURITY_DESCRIPTOR) );
|
||
if (RpcSD == NULL) {
|
||
return(STATUS_INSUFFICIENT_RESOURCES);
|
||
}
|
||
RpcSD->Length = 0;
|
||
RpcSD->SecurityDescriptor = NULL;
|
||
|
||
|
||
|
||
//
|
||
// See if the handle is valid and opened for the requested access
|
||
//
|
||
|
||
|
||
SampAcquireReadLock();
|
||
Context = (PSAMP_OBJECT)ObjectHandle;
|
||
NtStatus = SampLookupContext(
|
||
Context,
|
||
DesiredAccess,
|
||
SampUnknownObjectType, // ExpectedType
|
||
&FoundType
|
||
);
|
||
|
||
|
||
if (NT_SUCCESS(NtStatus)) {
|
||
|
||
|
||
//
|
||
// Get the security descriptor
|
||
//
|
||
|
||
|
||
RetrieveSDLength = 0;
|
||
NtStatus = SampGetObjectSD( Context, &RetrieveSDLength, &RetrieveSD);
|
||
|
||
if (NT_SUCCESS(NtStatus)) {
|
||
|
||
//
|
||
// blank out the parts that aren't to be returned
|
||
//
|
||
|
||
if ( !(SecurityInformation & SACL_SECURITY_INFORMATION) ) {
|
||
((PISECURITY_DESCRIPTOR)RetrieveSD)->Control &= ~SE_SACL_PRESENT;
|
||
}
|
||
|
||
|
||
if ( !(SecurityInformation & DACL_SECURITY_INFORMATION) ) {
|
||
((PISECURITY_DESCRIPTOR)RetrieveSD)->Control &= ~SE_DACL_PRESENT;
|
||
}
|
||
|
||
|
||
if ( !(SecurityInformation & OWNER_SECURITY_INFORMATION) ) {
|
||
((PISECURITY_DESCRIPTOR)RetrieveSD)->Owner = NULL;
|
||
}
|
||
|
||
|
||
if ( !(SecurityInformation & GROUP_SECURITY_INFORMATION) ) {
|
||
((PISECURITY_DESCRIPTOR)RetrieveSD)->Group = NULL;
|
||
}
|
||
|
||
|
||
//
|
||
// Determine how much memory is needed for a self-relative
|
||
// security descriptor containing just this information.
|
||
//
|
||
|
||
|
||
ReturnSDLength = 0;
|
||
NtStatus = RtlMakeSelfRelativeSD(
|
||
RetrieveSD,
|
||
NULL,
|
||
&ReturnSDLength
|
||
);
|
||
ASSERT(!NT_SUCCESS(NtStatus));
|
||
|
||
if (NtStatus == STATUS_BUFFER_TOO_SMALL) {
|
||
|
||
|
||
ReturnSD = MIDL_user_allocate( ReturnSDLength );
|
||
if (ReturnSD == NULL) {
|
||
|
||
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
} else {
|
||
|
||
|
||
//
|
||
// make an appropriate self-relative security descriptor
|
||
//
|
||
|
||
NtStatus = RtlMakeSelfRelativeSD(
|
||
RetrieveSD,
|
||
ReturnSD,
|
||
&ReturnSDLength
|
||
);
|
||
}
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// Free up the retrieved SD
|
||
//
|
||
|
||
MIDL_user_free( RetrieveSD );
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// De-reference the object
|
||
//
|
||
|
||
IgnoreStatus = SampDeReferenceContext( Context, FALSE );
|
||
}
|
||
|
||
//
|
||
// Free the read lock
|
||
//
|
||
|
||
SampReleaseReadLock();
|
||
|
||
|
||
|
||
//
|
||
// If we succeeded, set up the return buffer.
|
||
// Otherwise, free any allocated memory.
|
||
//
|
||
|
||
if (NT_SUCCESS(NtStatus)) {
|
||
|
||
RpcSD->Length = ReturnSDLength;
|
||
RpcSD->SecurityDescriptor = (PUCHAR)ReturnSD;
|
||
(*SecurityDescriptor) = RpcSD;
|
||
|
||
} else {
|
||
|
||
MIDL_user_free( RpcSD );
|
||
if (ReturnSD != NULL) {
|
||
MIDL_user_free(ReturnSD);
|
||
}
|
||
(*SecurityDescriptor) = NULL;
|
||
}
|
||
|
||
|
||
|
||
return(NtStatus);
|
||
|
||
|
||
}
|