|
|
/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
eztrust.c
Abstract:
This file contains routines to support the Easy Trust creation DCR.
Author:
Colin Brace (ColinBr) May 19, 2001
Environment:
User Mode
Revision History:
--*/ #include <lsapch2.h>
#include "dbp.h"
#include <permit.h>
//
// Forwards
//
NTSTATUS LsapGetDelegatedTDOQuotas( OUT ULONG *PerUserQuota OPTIONAL, OUT ULONG *GlobalQuota OPTIONAL, OUT ULONG *PerUserDeletedQuota OPTIONAL );
//
// This flags indicates to only search for deleted TDO's with
// the mdds-CreatorSid attribute equal to CreatorSid
//
#define LSAP_GET_DELEGATED_TDO_DELETED_ONLY 0x00000001
NTSTATUS LsapGetDelegatedTDOCount( IN ULONG Flags, IN PSID CreatorSid OPTIONAL, OUT ULONG *Count );
//
// Definitions
//
NTSTATUS LsapGetCurrentOwnerAndPrimaryGroup( OUT PTOKEN_OWNER * Owner, OUT PTOKEN_PRIMARY_GROUP * PrimaryGroup OPTIONAL ) /*++
Routine Description
This routine Impersonates the Client and obtains the owner and its primary group from the Token
Parameters:
Owner -- The Owner sid is returned in here PrimaryGroup The User's Primary Group is returned in here
Return Values:
STATUS_SUCCESS STATUS_INSUFFICIENT_RESOURCES --*/ {
HANDLE ClientToken = INVALID_HANDLE_VALUE; BOOLEAN fImpersonating = FALSE; ULONG RequiredLength=0; NTSTATUS NtStatus = STATUS_SUCCESS; BOOLEAN ImpersonatingNullSession = FALSE;
//
// Initialize Return Values
//
*Owner = NULL; if (PrimaryGroup) { *PrimaryGroup = NULL; }
//
// Impersonate the Client
//
NtStatus = I_RpcMapWin32Status(RpcImpersonateClient(0)); if (!NT_SUCCESS(NtStatus)) goto Error;
fImpersonating = TRUE;
//
// Grab the User's Sid
//
NtStatus = NtOpenThreadToken( NtCurrentThread(), TOKEN_QUERY, TRUE, //OpenAsSelf
&ClientToken );
if (!NT_SUCCESS(NtStatus)) goto Error;
//
// Query the Client Token For User's SID
//
NtStatus = NtQueryInformationToken( ClientToken, TokenOwner, NULL, 0, &RequiredLength );
if ((STATUS_BUFFER_TOO_SMALL == NtStatus) && ( RequiredLength > 0)) { //
// Alloc Memory
//
*Owner = LsapAllocateLsaHeap(RequiredLength); if (NULL==*Owner) { NtStatus = STATUS_INSUFFICIENT_RESOURCES; goto Error; }
//
// Query the Token
//
NtStatus = NtQueryInformationToken( ClientToken, TokenOwner, *Owner, RequiredLength, &RequiredLength );
} if (!NT_SUCCESS(NtStatus)) { goto Error; }
//
// Query the Client Token For User's PrimaryGroup
//
if (PrimaryGroup) {
RequiredLength = 0; NtStatus = NtQueryInformationToken( ClientToken, TokenPrimaryGroup, NULL, 0, &RequiredLength ); if ((STATUS_BUFFER_TOO_SMALL == NtStatus) && ( RequiredLength > 0)) { //
// Alloc Memory
//
*PrimaryGroup = LsapAllocateLsaHeap(RequiredLength); if (NULL==*PrimaryGroup) { NtStatus = STATUS_INSUFFICIENT_RESOURCES; goto Error; } //
// Query the Token
//
NtStatus = NtQueryInformationToken( ClientToken, TokenPrimaryGroup, *PrimaryGroup, RequiredLength, &RequiredLength ); } if (!NT_SUCCESS(NtStatus)) { goto Error; } }
Error:
//
// Clean up on Error
//
if (!NT_SUCCESS(NtStatus)) { if (*Owner) { LsapFreeLsaHeap(*Owner); *Owner = NULL; }
if (PrimaryGroup && *PrimaryGroup) { LsapFreeLsaHeap(*PrimaryGroup); *PrimaryGroup = NULL; } }
if (fImpersonating) I_RpcMapWin32Status(RpcRevertToSelf());
if (INVALID_HANDLE_VALUE!=ClientToken) NtClose(ClientToken);
return NtStatus;
}
NTSTATUS LsapIsAccessControlGranted( IN DSNAME* DsObject, IN ULONG ClassId, IN GUID* ControlAccessRight, IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId ) /*++
Routine Description:
This routine checks if the network client has ControlAccessRight on the DsObject for the class ClassId. Arguments:
DsObject -- an object in DS whose security descriptor will be used for the access check. ClassId -- which class of DsObject the ControlAccessRight should be checked agaist. ControlAccessRight -- a GUID ObjectTypeId -- the LSA object for which this access check applies (not necessarily the same type of object as DsObject).
Return Values:
STATUS_SUCCESS, STATUS_ACCESS_DENIED, STATUS_NO_SECURITY_ON_OBJECT (if DsObject doesn't have a security descriptor) resource error otherwise.
--*/ { NTSTATUS NtStatus = STATUS_SUCCESS; NTSTATUS SecondaryStatus = STATUS_SUCCESS; OBJECT_TYPE_LIST ObjList[2]; DWORD Results[2]; DWORD GrantedAccess[2]; PSECURITY_DESCRIPTOR pSD = NULL; GENERIC_MAPPING GenericMapping = DS_GENERIC_MAPPING; ACCESS_MASK DesiredAccess; GUID ClassGuid; ULONG ClassGuidLength = sizeof(GUID); BOOLEAN bTemp = FALSE; UNICODE_STRING ObjectName;
ASSERT(DsObject && DsObject->NameLen > 0);
//
// Setup the object name
//
ObjectName.Length = ObjectName.MaximumLength = (USHORT)(DsObject->NameLen * sizeof(WCHAR)); ObjectName.Buffer = DsObject->StringName;
//
// Obtain the security descriptor
//
NtStatus = LsapDsReadObjectSDByDsName(DsObject, &pSD);
if (!NT_SUCCESS(NtStatus)) { goto IsAccessControlGrantedError; }
//
// Get the Class GUID of the object
//
NtStatus = SampGetClassAttribute(ClassId, ATT_SCHEMA_ID_GUID, &ClassGuidLength, &ClassGuid);
if (!NT_SUCCESS(NtStatus)) { goto IsAccessControlGrantedError; }
//
// Setup Object List
//
ObjList[0].Level = ACCESS_OBJECT_GUID; ObjList[0].Sbz = 0; ObjList[0].ObjectType = &ClassGuid;
//
// Every control access guid is considered to be in it's own property
// set. To achieve this, we treat control access guids as property set
// guids.
//
ObjList[1].Level = ACCESS_PROPERTY_SET_GUID; ObjList[1].Sbz = 0; ObjList[1].ObjectType = ControlAccessRight;
//
// Assume full access
//
Results[0] = 0; Results[1] = 0;
//
// Impersonate the client
//
NtStatus = I_RpcMapWin32Status(RpcImpersonateClient(0));
if (!NT_SUCCESS(NtStatus)) { goto IsAccessControlGrantedError; }
//
// Set the desired access
//
DesiredAccess = RIGHT_DS_CONTROL_ACCESS;
//
// Map the desired access to contain no
// generic accesses.
//
MapGenericMask(&DesiredAccess, &GenericMapping);
NtStatus = NtAccessCheckByTypeResultListAndAuditAlarm( &LsapState.SubsystemName, // SubSystemName
NULL, // HandleId or NULL
&LsapDbObjectTypeNames[ObjectTypeId], // ObjectTypeName
&ObjectName, // ObjectName
pSD, // Domain NC head's SD
NULL, // Self SID
DesiredAccess, // Desired Access
AuditEventDirectoryServiceAccess, // Audit Type
0, // Flags
ObjList, // Object Type List
2, // Object Type List Length
&GenericMapping, // Generic Mapping
FALSE, // Object Creation
GrantedAccess, // Granted Status
Results, // Access Status
&bTemp); // Generate On Close
//
// Stop impersonating the client
//
SecondaryStatus = I_RpcMapWin32Status(RpcRevertToSelf()); if (NT_SUCCESS(NtStatus)) { NtStatus = SecondaryStatus; }
if (NT_SUCCESS(NtStatus)) { //
// Ok, we checked access, Now, access is granted if either
// we were granted access on the entire object (i.e. Results[0]
// is NULL ) or we were granted explicit rights on the access
// guid (i.e. Results[1] is NULL).
//
if ( Results[0] && Results[1] ) { NtStatus = STATUS_ACCESS_DENIED; } }
IsAccessControlGrantedError:
if (pSD) { LsapFreeLsaHeap(pSD); }
return NtStatus;
}
NTSTATUS LsapMakeNewSelfRelativeSecurityDescriptor( IN PSID Owner, IN PSID Group, IN PACL Dacl, IN PACL Sacl, OUT PULONG SecurityDescriptorLength, OUT PSECURITY_DESCRIPTOR * SecurityDescriptor ) /*++
Routine Description:
Given the 4 components of a security descriptor this routine makes a new self relative Security descriptor.
Parameters:
Owner -- The Sid of the owner Group -- The Sid of the group Dacl -- The Dacl to Use Sacl -- The Sacl to Use
Return Values:
STATUS_SUCCESS STATUS_INSUFFICIENT_RESOURCES STATUS_UNSUCCESSFUL --*/ {
SECURITY_DESCRIPTOR SdAbsolute; NTSTATUS NtStatus = STATUS_SUCCESS;
*SecurityDescriptorLength = 0; *SecurityDescriptor = NULL;
if (!InitializeSecurityDescriptor(&SdAbsolute,SECURITY_DESCRIPTOR_REVISION)) { NtStatus = STATUS_UNSUCCESSFUL; goto Error; }
//
// Set the owner, default the owner to administrators alias
//
if (NULL!=Owner) { if (!SetSecurityDescriptorOwner(&SdAbsolute,Owner,FALSE)) { NtStatus = STATUS_UNSUCCESSFUL; goto Error; } }
if (NULL!=Group) { if (!SetSecurityDescriptorGroup(&SdAbsolute,Group,FALSE)) { NtStatus = STATUS_UNSUCCESSFUL; goto Error; } }
//
// Set the Dacl if there is one
//
if (NULL!=Dacl) { if (!SetSecurityDescriptorDacl(&SdAbsolute,TRUE,Dacl,FALSE)) { NtStatus = STATUS_UNSUCCESSFUL; goto Error; } }
//
// Set the Sacl if there is one
//
if (NULL!=Sacl) { if (!SetSecurityDescriptorSacl(&SdAbsolute,TRUE,Sacl,FALSE)) { NtStatus = STATUS_UNSUCCESSFUL; goto Error; } }
//
// Make a new security Descriptor
//
*SecurityDescriptorLength = GetSecurityDescriptorLength(&SdAbsolute); *SecurityDescriptor = LsapAllocateLsaHeap(*SecurityDescriptorLength); if (NULL==*SecurityDescriptor) { NtStatus = STATUS_INSUFFICIENT_RESOURCES; goto Error; }
if (!MakeSelfRelativeSD(&SdAbsolute,*SecurityDescriptor,SecurityDescriptorLength)) { NtStatus = STATUS_UNSUCCESSFUL; if (*SecurityDescriptor) { LsapFreeLsaHeap(*SecurityDescriptor); *SecurityDescriptor = NULL; } }
Error:
return NtStatus; }
//
// The per user trust quota
//
#define LSAP_CHECK_TDO_QUOTA_USER 0x00000001
//
// The all users trust quota
//
#define LSAP_CHECK_TDO_QUOTA_GLOBAL 0x00000002
//
// The per user tombstone deletion check
//
#define LSAP_CHECK_TDO_QUOTA_USER_DELETED 0x00000004
NTSTATUS LsapCheckDelegatedTDOQuotas( IN PSID ClientSid OPTIONAL, IN ULONG Flags ) /*++
Routine Description:
This routine verifies the three quota relating to delegated trust creation and deletion.
Arguments:
ClientSid -- the SID to check against Flags -- see defn's above functions
Return Values:
STATUS_SUCCESS, a resource error otherwise
--*/ { NTSTATUS NtStatus = STATUS_SUCCESS; ULONG GlobalQuota = 0, PerUserQuota = 0, DeletedQuota = 0; ULONG GlobalCount = 0, UserCount = 0, DeletedCount = 0; PTOKEN_OWNER TokenOwnerInformation = NULL; PSID CreatorSid;
//
// Get the SID of the user
//
if (NULL == ClientSid) {
NtStatus = LsapGetCurrentOwnerAndPrimaryGroup(&TokenOwnerInformation, NULL); if (!NT_SUCCESS(NtStatus)) { goto CheckDelegatedTDOCreationQuotasExit; } CreatorSid = TokenOwnerInformation->Owner;
} else {
CreatorSid = ClientSid;
}
//
// Get the domain wide quota settings
//
NtStatus = LsapGetDelegatedTDOQuotas(&PerUserQuota, &GlobalQuota, &DeletedQuota); if (!NT_SUCCESS(NtStatus)) { goto CheckDelegatedTDOCreationQuotasExit; }
//
// Do the checks
//
if (Flags & LSAP_CHECK_TDO_QUOTA_USER) {
NtStatus = LsapGetDelegatedTDOCount(0, // no flags
CreatorSid, &UserCount); if ( NT_SUCCESS(NtStatus) && (UserCount >= PerUserQuota) ) { NtStatus = STATUS_PER_USER_TRUST_QUOTA_EXCEEDED; } if (!NT_SUCCESS(NtStatus)) { goto CheckDelegatedTDOCreationQuotasExit; } }
if (Flags & LSAP_CHECK_TDO_QUOTA_GLOBAL) {
NtStatus = LsapGetDelegatedTDOCount(0, // no flags
NULL, // all delegated TDO's
&GlobalCount); if ( NT_SUCCESS(NtStatus) && (GlobalCount >= GlobalQuota) ) { NtStatus = STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED; } if (!NT_SUCCESS(NtStatus)) { goto CheckDelegatedTDOCreationQuotasExit; } }
if (Flags & LSAP_CHECK_TDO_QUOTA_USER_DELETED) { NtStatus = LsapGetDelegatedTDOCount(LSAP_GET_DELEGATED_TDO_DELETED_ONLY, CreatorSid, &DeletedCount); if ( NT_SUCCESS(NtStatus) && (DeletedCount >= DeletedQuota) ) {
NtStatus = STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED; } if (!NT_SUCCESS(NtStatus)) { goto CheckDelegatedTDOCreationQuotasExit; } }
CheckDelegatedTDOCreationQuotasExit:
if (TokenOwnerInformation) { LsapFreeLsaHeap( TokenOwnerInformation ); }
return NtStatus; }
NTSTATUS LsapCheckTDOCreationByControlAccess( IN PLSAP_DB_OBJECT_INFORMATION ObjectInformation, IN PLSAP_DB_ATTRIBUTE Attributes, IN ULONG AttributeCount ) /*++
Routine Description:
This routine determines if the caller has access to create a trusted domain object by "right", as opposed to the standard DS access check model which is assumed to have failed at this point. The determination is made by the following rules: 1) If the trust is an inbound-only forest trust and 2) If the caller has the "right to create an inbound trust" on the domain object and 3) the user's quota for trust object creations hasn't been exceeded and 4) the global quota for trust object creations, created in this manner, hasn't been exceeded Then return STATUS_SUCCESS. Else, return a processing error, or STATUS_ACCESS_DENIED. Arguments:
ObjectInformation -- information about the trust being created (the name, etc) Attributes -- the requested attributes of the trust (inbound, forest, etc) AttributeCount -- the number of attributes Return Values:
NTSTATUS, see routine description.
--*/ { NTSTATUS Status = STATUS_SUCCESS; ULONG i; BOOLEAN ForestTrust = FALSE; BOOLEAN InboundOnlyTrust = FALSE;
//
// Is this an inbound only forest trust?
//
for (i = 0; i < AttributeCount; i++) { ULONG BitMask; if (Attributes[i].DbNameIndex == TrDmTrLA) { ASSERT(Attributes[i].AttribType == LsapDbAttribULong); BitMask = *((ULONG*)Attributes[i].AttributeValue); if (BitMask & TRUST_ATTRIBUTE_FOREST_TRANSITIVE) { ForestTrust = TRUE; } } if (Attributes[i].DbNameIndex == TrDmTrDi) { ASSERT(Attributes[i].AttribType == LsapDbAttribULong); BitMask = *((ULONG*)Attributes[i].AttributeValue); if ( (BitMask & TRUST_DIRECTION_INBOUND) && ((BitMask & TRUST_DIRECTION_OUTBOUND) == 0) ) { InboundOnlyTrust = TRUE; } } } if (!(ForestTrust && InboundOnlyTrust)) { Status = STATUS_ACCESS_DENIED; goto CheckTDOCreationByControlAccessError; }
//
// Does the caller have the control access right?
//
Status = LsapIsAccessControlGranted(LsaDsStateInfo.DsRoot, CLASS_DOMAIN_DNS, &LsapDsGuidList[LsapDsGuidDelegatedTrustCreation], ObjectInformation->ObjectTypeId ); if (!NT_SUCCESS(Status)) { goto CheckTDOCreationByControlAccessError; }
//
// Are the quota restrictions satisfied?
//
Status = LsapCheckDelegatedTDOQuotas(NULL, // we don't have the client sid
LSAP_CHECK_TDO_QUOTA_GLOBAL | LSAP_CHECK_TDO_QUOTA_USER); if (!NT_SUCCESS(Status)) { goto CheckTDOCreationByControlAccessError; }
CheckTDOCreationByControlAccessError:
return Status; }
NTSTATUS LsapUpdateTDOAttributesForCreation( IN PUNICODE_STRING ObjectName, IN PLSAP_DB_ATTRIBUTE Attributes, IN OUT ULONG* AttributeCount, IN ULONG AttributesAllocated ) /*++
Routine Description:
This routine adds the necessary attributes to a trusted domain object that is created by the control access right (aka easy trust). Specifically, it adds the DS-Creator-Sid (as the network caller) and gives the network caller access to write to the incoming auth blob.
Arguments:
ObjectName -- the name of the trust object in the DS Attributes -- the list of statically declared attributes to be set on the trust object. AttributeCount -- in, the number of attributes; out, the updated count AttributesAllocated -- the total number of attributes allocated, but not necessarily used.
Return Values:
STATUS_SUCCESS, a resource error otherwise
--*/ { NTSTATUS NtStatus = STATUS_SUCCESS; PLSAP_DB_ATTRIBUTE NextAttribute; PTOKEN_OWNER TokenOwnerInformation = NULL; DSNAME *DsName = NULL; PSECURITY_DESCRIPTOR pSD = NULL, pNewSD = NULL, pNewSDForAttr = NULL; ULONG cbNewSD = 0; ULONG Size; PSID CreatorSid = 0; PPOLICY_DNS_DOMAIN_INFO LocalDnsDomainInfo = NULL; BOOL fSuccess; ULONG DomainAdminsSidBuffer[SECURITY_MAX_SID_SIZE/sizeof( ULONG ) + 1 ]; PSID DomainAdminsSid = (PSID) DomainAdminsSidBuffer;
ASSERT(((*AttributeCount + 2) <= AttributesAllocated) && "Must preallocate more attributes for trusted domain creation");
//
// Generate the Domain Admin's SID to use as the security descriptor
// owner.
//
NtStatus = LsaIQueryInformationPolicyTrusted(PolicyDnsDomainInformation, (PLSAPR_POLICY_INFORMATION *) &LocalDnsDomainInfo); if (!NT_SUCCESS(NtStatus)) { goto UpdateTdoAttributesForCreationExit; }
Size = sizeof(DomainAdminsSidBuffer); fSuccess = CreateWellKnownSid(WinAccountDomainAdminsSid, LocalDnsDomainInfo->Sid, DomainAdminsSid, &Size); if (!fSuccess) { NtStatus = STATUS_NO_MEMORY; goto UpdateTdoAttributesForCreationExit; }
//
// Init the start of the new attributes
//
NextAttribute = &Attributes[(*AttributeCount)];
//
// Get the SID of the creator
//
NtStatus = LsapGetCurrentOwnerAndPrimaryGroup(&TokenOwnerInformation, NULL); if (!NT_SUCCESS(NtStatus)) { goto UpdateTdoAttributesForCreationExit; } Size = RtlLengthSid(TokenOwnerInformation->Owner); CreatorSid = midl_user_allocate(Size); if (NULL == CreatorSid) { NtStatus = STATUS_NO_MEMORY; goto UpdateTdoAttributesForCreationExit; } RtlCopySid(Size, CreatorSid, TokenOwnerInformation->Owner);
//
// Add the creator sid
//
LsapDbInitializeAttributeDs( NextAttribute, TrDmCrSid, CreatorSid, RtlLengthSid(CreatorSid), TRUE // to be freed
);
NextAttribute++; (*AttributeCount)++; CreatorSid = NULL;
//
// Generate the new security descriptor
//
//
// Build the DSName
//
NtStatus = LsapAllocAndInitializeDsNameFromUnicode( ObjectName, &DsName);
if (NT_SUCCESS(NtStatus)) {
NtStatus = LsapDsReadObjectSDByDsName(DsName, &pSD);
if (NT_SUCCESS(NtStatus)) {
//
// Set the owner to Administrators
//
NtStatus = LsapMakeNewSelfRelativeSecurityDescriptor( DomainAdminsSid, DomainAdminsSid, LsapGetDacl(pSD), LsapGetSacl(pSD), &cbNewSD, &pNewSD );
} }
if (!NT_SUCCESS(NtStatus)) { goto UpdateTdoAttributesForCreationExit; }
//
// Need to realloc
//
pNewSDForAttr = midl_user_allocate(cbNewSD); if (NULL == pNewSDForAttr) { NtStatus = STATUS_NO_MEMORY; goto UpdateTdoAttributesForCreationExit; } RtlCopyMemory(pNewSDForAttr, pNewSD, cbNewSD);
//
// Add the new security descriptor
//
LsapDbInitializeAttributeDs( NextAttribute, SecDesc, pNewSDForAttr, cbNewSD, TRUE // to be freed
);
pNewSDForAttr = NULL;
NextAttribute++; (*AttributeCount)++;
//
// Done
//
UpdateTdoAttributesForCreationExit:
//
// We shouldn't have added more attributes that are allocated
//
ASSERT((*AttributeCount) <= AttributesAllocated);
if (TokenOwnerInformation) { LsapFreeLsaHeap( TokenOwnerInformation ); } if (LocalDnsDomainInfo) { LsaIFree_LSAPR_POLICY_INFORMATION(PolicyDnsDomainInformation, (PLSAPR_POLICY_INFORMATION) LocalDnsDomainInfo); } if (DsName) { LsapDsFree(DsName); } if (pSD) { LsapFreeLsaHeap(pSD); } if (CreatorSid) { midl_user_free(CreatorSid); } if (pNewSD) { LsapFreeLsaHeap(pNewSD); } if (pNewSDForAttr) { midl_user_free(pNewSDForAttr); }
return NtStatus; }
NTSTATUS LsapCheckTDODeletionQuotas( IN LSAP_DB_HANDLE Handle ) /*++
Routine Description:
This routine checks to make sure that the client has not exceeded the number of deleted trusts they are allowed.
Arguments:
Handle -- the handle to the trust object
Return Values:
STATUS_SUCCESS, a resource error otherwise STATUS_QUOTA_EXCEEDED
--*/ { NTSTATUS Status = STATUS_SUCCESS; PSID ClientSid = NULL; PSID CreatorSid = NULL; PTOKEN_OWNER TokenOwnerInformation = NULL; ULONG Quota = 0, ClientUsage = 0; LSAP_DB_ATTRIBUTE Attribute;
//
// Get the trust creator SID, if any
//
LsapDbInitializeAttributeDs( &Attribute, TrDmCrSid, NULL, 0, FALSE );
Status = LsapDsReadAttributes(&Handle->PhysicalNameDs, LSAPDS_OP_NO_LOCK, &Attribute, 1);
if (STATUS_NOT_FOUND == Status) { //
// No creator? No quota
//
Status = STATUS_SUCCESS; goto CheckTDODeletionQuotasExit; } if (!NT_SUCCESS(Status)) { goto CheckTDODeletionQuotasExit; } CreatorSid = (PSID) Attribute.AttributeValue;
//
// Get the client SID
//
Status = LsapGetCurrentOwnerAndPrimaryGroup(&TokenOwnerInformation, NULL); if (!NT_SUCCESS(Status)) { goto CheckTDODeletionQuotasExit; } ClientSid = TokenOwnerInformation->Owner;
//
// Does it match the caller?
//
if (!RtlEqualSid(ClientSid, CreatorSid)) { //
// No quota enforced
//
goto CheckTDODeletionQuotasExit; }
//
// Do the quota check
//
Status = LsapCheckDelegatedTDOQuotas(ClientSid, LSAP_CHECK_TDO_QUOTA_USER_DELETED);
//
// Fall through to exit
//
CheckTDODeletionQuotasExit:
if (TokenOwnerInformation) { LsapFreeLsaHeap( TokenOwnerInformation ); }
if (CreatorSid) { MIDL_user_free( CreatorSid ); }
return Status; }
NTSTATUS LsapGetDelegatedTDOQuotas( OUT ULONG *PerUserQuota OPTIONAL, OUT ULONG *GlobalQuota OPTIONAL, OUT ULONG *PerUserDeletedQuota OPTIONAL ) /*++
Routine Description:
This routine returns the domain-wide delegatable TDO quotas.
Arguments:
PerUserQuota, GlobalQuota, PerUserDeletedQuota -- the quota to be filled in
Return Values:
STATUS_SUCCESS, a resource error otherwise
--*/ { NTSTATUS NtStatus = STATUS_SUCCESS; BOOLEAN ReleaseState = FALSE; ULONG LocalGlobalQuota = 0, LocalPerUserQuota = 0, LocalTombstoneQuota = 0; ATTRBLOCK ReadResAttrBlock = {0, NULL}; ATTRBLOCK ReadAttrBlock = {0, NULL}; ULONG i;
//
// Start a transaction, if necessary
//
NtStatus = LsapDsInitAllocAsNeededEx( LSAP_DB_READ_ONLY_TRANSACTION | LSAP_DB_DS_OP_TRANSACTION, NullObject, &ReleaseState ); if ( !NT_SUCCESS( NtStatus ) ) { goto GetDelegatedTDOQuotasError; }
//
// Read the attributes
//
ReadAttrBlock.attrCount = LsapDsTDOQuotaAttributesCount; ReadAttrBlock.pAttr = LsapDsTDOQuotaAttributes; NtStatus = LsapDsReadByDsName(LsaDsStateInfo.DsRoot, 0, &ReadAttrBlock, &ReadResAttrBlock);
if (NtStatus == STATUS_NOT_FOUND) { //
// Attributes aren't present; that's ok
//
NtStatus = STATUS_SUCCESS;
}
if (!NT_SUCCESS(NtStatus)) { goto GetDelegatedTDOQuotasError; }
//
// Extract the attribute
//
for (i = 0; i < ReadResAttrBlock.attrCount; i++) {
DWORD Value;
ASSERT(ReadResAttrBlock.pAttr[i].AttrVal.valCount == 1); ASSERT(ReadResAttrBlock.pAttr[i].AttrVal.pAVal[0].valLen == sizeof(DWORD));
Value = *((ULONG*)ReadResAttrBlock.pAttr[i].AttrVal.pAVal[0].pVal);
switch (ReadResAttrBlock.pAttr[i].attrTyp) { case ATT_MS_DS_PER_USER_TRUST_QUOTA: LocalPerUserQuota = Value; break;
case ATT_MS_DS_ALL_USERS_TRUST_QUOTA: LocalGlobalQuota = Value; break;
case ATT_MS_DS_PER_USER_TRUST_TOMBSTONES_QUOTA: LocalTombstoneQuota = Value; break;
} }
if (PerUserQuota) { *PerUserQuota = LocalPerUserQuota; } if (GlobalQuota) { *GlobalQuota = LocalGlobalQuota; } if (PerUserDeletedQuota) { *PerUserDeletedQuota = LocalTombstoneQuota; }
GetDelegatedTDOQuotasError:
if (ReleaseState) {
LsapDsDeleteAllocAsNeededEx( LSAP_DB_READ_ONLY_TRANSACTION | LSAP_DB_DS_OP_TRANSACTION, NullObject, ReleaseState );
}
return NtStatus; }
NTSTATUS LsapGetDelegatedTDOCount( IN ULONG Flags, IN PSID CreatorSid OPTIONAL, OUT ULONG *Count ) /*++
Routine Description:
This routine returns the number of TDO's that satisfy the input parameters.
Arguments:
Flags -- LSAP_GET_DELEGATED_TDO_DELETED_ONLY return only deleted objects CreatorSid -- if present, the TDO must have a msds-creator-sid attribute equal to this value Count -- the number of objects that match the request
Return Values:
STATUS_SUCCESS, a resource error otherwise
--*/ { NTSTATUS Status = STATUS_SUCCESS; SEARCHARG SearchArg; FILTER Filters[ 3 ], RootFilter; ENTINFSEL EntInfSel; ENTINFLIST *EntInfList; ULONG ClassId, FlagValue, i; SEARCHRES *SearchRes = NULL; BOOLEAN CloseTransaction = FALSE; ATTR AttrsToRead[] = {{ATT_OBJECT_GUID, {0, NULL}}}; ULONG LocalCount = 0; USHORT FilterCount = 0; BOOL True = TRUE;
RtlZeroMemory( &SearchArg, sizeof( SEARCHARG ) );
//
// See if we already have a transaction going
//
// If one already exists, we'll use the existing transaction and not
// delete the thread state at the end.
//
Status = LsapDsInitAllocAsNeededEx( LSAP_DB_READ_ONLY_TRANSACTION | LSAP_DB_DS_OP_TRANSACTION, NullObject, &CloseTransaction ); if (!NT_SUCCESS( Status)) { goto GetDelegatedTDOCountExit; }
//
// Build the filter.
//
ClassId = CLASS_TRUSTED_DOMAIN;
RtlZeroMemory( Filters, sizeof (Filters) ); RtlZeroMemory( &RootFilter, sizeof (RootFilter) );
//
// Match the msds-Creator-Sid
//
Filters[ 0 ].choice = FILTER_CHOICE_ITEM; if (CreatorSid) { Filters[ 0 ].FilterTypes.Item.choice = FI_CHOICE_EQUALITY; Filters[ 0 ].FilterTypes.Item.FilTypes.ava.type = ATT_MS_DS_CREATOR_SID; Filters[ 0 ].FilterTypes.Item.FilTypes.ava.Value.valLen = RtlLengthSid(CreatorSid); Filters[ 0 ].FilterTypes.Item.FilTypes.ava.Value.pVal = ( PUCHAR )CreatorSid; } else { Filters[ 0 ].FilterTypes.Item.choice = FI_CHOICE_PRESENT; Filters[ 0 ].FilterTypes.Item.FilTypes.present = ATT_MS_DS_CREATOR_SID; } FilterCount++;
//
// Only TDO's
//
Filters[ 0 ].pNextFilter = &Filters[ 1 ]; Filters[ 1 ].choice = FILTER_CHOICE_ITEM; Filters[ 1 ].FilterTypes.Item.choice = FI_CHOICE_EQUALITY; Filters[ 1 ].FilterTypes.Item.FilTypes.ava.type = ATT_OBJECT_CLASS; Filters[ 1 ].FilterTypes.Item.FilTypes.ava.Value.valLen = sizeof( ULONG ); Filters[ 1 ].FilterTypes.Item.FilTypes.ava.Value.pVal = ( PUCHAR )&ClassId; FilterCount++;
if (Flags & LSAP_GET_DELEGATED_TDO_DELETED_ONLY) {
//
// Only deleted TDO's
//
Filters[ 1 ].pNextFilter = &Filters[ 2 ]; Filters[ 2 ].choice = FILTER_CHOICE_ITEM; Filters[ 2 ].FilterTypes.Item.choice = FI_CHOICE_EQUALITY; Filters[ 2 ].FilterTypes.Item.FilTypes.ava.type = ATT_IS_DELETED; Filters[ 2 ].FilterTypes.Item.FilTypes.ava.Value.valLen = sizeof( BOOL ); Filters[ 2 ].FilterTypes.Item.FilTypes.ava.Value.pVal = ( PUCHAR )&True; FilterCount++;
//
// Search the NC, since deleted objects are moved in to the
// deleted objects container
//
SearchArg.pObject = LsaDsStateInfo.DsRoot; SearchArg.choice = SE_CHOICE_WHOLE_SUBTREE;
} else {
//
// Search just the system container
//
SearchArg.pObject = LsaDsStateInfo.DsSystemContainer; SearchArg.choice = SE_CHOICE_IMMED_CHLDRN; }
RootFilter.choice = FILTER_CHOICE_AND; RootFilter.FilterTypes.And.count = FilterCount; RootFilter.FilterTypes.And.pFirstFilter = Filters;
SearchArg.bOneNC = TRUE; SearchArg.pFilter = &RootFilter; SearchArg.searchAliases = FALSE; SearchArg.pSelection = &EntInfSel;
//
// Build the list of attributes to return
//
EntInfSel.attSel = EN_ATTSET_LIST; EntInfSel.AttrTypBlock.attrCount = 1; EntInfSel.AttrTypBlock.pAttr = AttrsToRead; EntInfSel.infoTypes = EN_INFOTYPES_TYPES_VALS;
//
// Build the Commarg structure
//
LsapDsInitializeStdCommArg( &( SearchArg.CommArg ), 0 ); if (Flags & LSAP_GET_DELEGATED_TDO_DELETED_ONLY) { SearchArg.CommArg.Svccntl.makeDeletionsAvail = TRUE; }
//
// There could be thousands of trusts; make a paged search
// to scale
//
SearchArg.CommArg.PagedResult.fPresent = TRUE; SearchArg.CommArg.ulSizeLimit = 100;
LsapDsSetDsaFlags( TRUE );
while (NT_SUCCESS(Status) && SearchArg.CommArg.PagedResult.fPresent) {
DirSearch( &SearchArg, &SearchRes ); LsapDsContinueTransaction(); if ( SearchRes ) { Status = LsapDsMapDsReturnToStatusEx( &SearchRes->CommRes ); } else { Status = STATUS_INSUFFICIENT_RESOURCES; }
if ( NT_SUCCESS( Status ) ) {
//
// Increment the count
//
LocalCount += SearchRes->count;
//
// See if we have to search again
//
SearchArg.CommArg.PagedResult.fPresent = SearchRes->PagedResult.fPresent;
SearchArg.CommArg.PagedResult.pRestart = SearchRes->PagedResult.pRestart;
} }
if (NT_SUCCESS(Status)) { *Count = LocalCount; }
GetDelegatedTDOCountExit:
if (CloseTransaction) {
LsapDsDeleteAllocAsNeededEx( LSAP_DB_READ_ONLY_TRANSACTION | LSAP_DB_DS_OP_TRANSACTION, NullObject, CloseTransaction ); }
return( Status );
}
|