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);
|
|||
|
|
|||
|
|
|||
|
}
|