NT4/private/newsam2/server/alias.c
2020-09-30 17:12:29 +02:00

4285 lines
115 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
alias.c
Abstract:
This file contains services related to the SAM "alias" object.
Author:
Chad Schwitters (chads) 15-Jan-1992
Environment:
User Mode - Win32
Revision History:
7-1-96 - MURLIS - Modified to Use DS.
--*/
///////////////////////////////////////////////////////////////////////////////
// //
// Includes //
// //
///////////////////////////////////////////////////////////////////////////////
#include <samsrvp.h>
#include <msaudite.h>
#include <dslayer.h>
#include <dsmember.h>
///////////////////////////////////////////////////////////////////////////////
// //
// private service prototypes //
// //
///////////////////////////////////////////////////////////////////////////////
NTSTATUS
SampAddAccountToAlias(
IN PSAMP_OBJECT AccountContext,
IN PSID AccountSid
);
NTSTATUS
SampRemoveAccountFromAlias(
IN PSAMP_OBJECT AccountContext,
IN PSID AccountSid
);
NTSTATUS
SampAddAliasToAccountMembership(
IN ULONG AliasRid,
IN PSID AccountSid
);
NTSTATUS
SampRemoveAliasFromAccountMembership(
IN ULONG AliasRid,
IN PSID AccountSid
);
NTSTATUS
SampRemoveAliasFromAllAccounts(
IN PSAMP_OBJECT AliasContext
);
NTSTATUS
SampDeleteAliasKeys(
IN PSAMP_OBJECT Context
);
NTSTATUS
SampRetrieveAliasMembers(
IN PSAMP_OBJECT AliasContext,
IN PULONG MemberCount,
IN PSID **Members OPTIONAL
);
NTSTATUS
SampDeleteAliasMembershipKeysForAccount(
IN PSID AccountSid
);
NTSTATUS
SampAdjustAliasDomainsCount(
IN BOOLEAN Increment
);
NTSTATUS
SampValidateNewAliasMember(
IN PSID MemberId
);
NTSTATUS
SampChangeAliasAccountName(
IN PSAMP_OBJECT Context,
IN PUNICODE_STRING NewAccountName,
OUT PUNICODE_STRING OldAccountName
);
///////////////////////////////////////////////////////////////////////////////
// //
// Exposed RPC'able Services //
// //
///////////////////////////////////////////////////////////////////////////////
NTSTATUS
SamrOpenAlias(
IN SAM_HANDLE DomainHandle,
IN ACCESS_MASK DesiredAccess,
IN ULONG AliasId,
OUT PSAM_HANDLE AliasHandle
)
/*++
Routine Description:
This API opens an existing Alias object. The Alias is specified by
a ID value that is relative to the SID of the domain. The operations
that will be performed on the Alias must be declared at this time.
This call returns a handle to the newly opened Alias that may be used
for successive operations on the Alias. This handle may be closed
with the SamCloseHandle API.
Parameters:
DomainHandle - A domain handle returned from a previous call to
SamOpenDomain.
DesiredAccess - Is an access mask indicating which access types are
desired to the alias.
AliasId - Specifies the relative ID value of the Alias to be opened.
AliasHandle - Receives a handle referencing the newly opened Alias.
This handle will be required in successive calls to operate on
the Alias.
Return Values:
STATUS_SUCCESS - The Alias was successfully opened.
STATUS_ACCESS_DENIED - Caller does not have the appropriate access
to complete the operation.
STATUS_NO_SUCH_ALIAS - The specified Alias does not exist.
STATUS_INVALID_HANDLE - The domain handle passed is invalid.
--*/
{
NTSTATUS NtStatus;
SAMTRACE("SamrOpenAlias");
NtStatus = SampOpenAccount(
SampAliasObjectType,
DomainHandle,
DesiredAccess,
AliasId,
FALSE,
AliasHandle
);
return(NtStatus);
}
NTSTATUS
SamrQueryInformationAlias(
IN SAMPR_HANDLE AliasHandle,
IN ALIAS_INFORMATION_CLASS AliasInformationClass,
OUT PSAMPR_ALIAS_INFO_BUFFER *Buffer
)
/*++
Routine Description:
This API retrieves information on the alias specified.
Parameters:
AliasHandle - The handle of an opened alias to operate on.
AliasInformationClass - Class of information to retrieve. The
accesses required for each class is shown below:
Info Level Required Access Type
----------------------- ----------------------
AliasGeneralInformation ALIAS_READ_INFORMATION
AliasNameInformation ALIAS_READ_INFORMATION
AliasAdminInformation ALIAS_READ_INFORMATION
Buffer - Receives a pointer to a buffer containing the requested
information. When this information is no longer needed, this
buffer and any memory pointed to through this buffer must be
freed using SamFreeMemory().
Return Values:
STATUS_SUCCESS - The Service completed successfully.
STATUS_ACCESS_DENIED - Caller does not have the appropriate
access to complete the operation.
STATUS_INVALID_HANDLE - The handle passed is invalid.
STATUS_INVALID_INFO_CLASS - The class provided was invalid.
--*/
{
NTSTATUS NtStatus;
NTSTATUS IgnoreStatus;
PSAMP_OBJECT AccountContext;
SAMP_OBJECT_TYPE FoundType;
ACCESS_MASK DesiredAccess;
ULONG i;
//
// Used for tracking allocated blocks of memory - so we can deallocate
// them in case of error. Don't exceed this number of allocated buffers.
// ||
// vv
PVOID AllocatedBuffer[10];
ULONG AllocatedBufferCount = 0;
SAMTRACE("SamrQueryInformationAlias");
#define RegisterBuffer(Buffer) \
{ \
if ((Buffer) != NULL) { \
\
ASSERT(AllocatedBufferCount < \
sizeof(AllocatedBuffer) / sizeof(*AllocatedBuffer)); \
\
AllocatedBuffer[AllocatedBufferCount++] = (Buffer); \
} \
}
#define AllocateBuffer(NewBuffer, Size) \
{ \
(NewBuffer) = MIDL_user_allocate(Size); \
RegisterBuffer(NewBuffer); \
} \
//
// Make sure we understand what RPC is doing for (to) us.
//
ASSERT (Buffer != NULL);
ASSERT ((*Buffer) == NULL);
//
// Set the desired access based upon the Info class
//
switch (AliasInformationClass) {
case AliasGeneralInformation:
case AliasNameInformation:
case AliasAdminCommentInformation:
DesiredAccess = ALIAS_READ_INFORMATION;
break;
default:
(*Buffer) = NULL;
return(STATUS_INVALID_INFO_CLASS);
} // end_switch
//
// Allocate the info structure
//
AllocateBuffer( *Buffer, sizeof(SAMPR_ALIAS_INFO_BUFFER) );
if ((*Buffer) == NULL) {
return(STATUS_INSUFFICIENT_RESOURCES);
}
SampAcquireReadLock();
//
// Validate type of, and access to object.
//
AccountContext = (PSAMP_OBJECT)AliasHandle;
NtStatus = SampLookupContext(
AccountContext,
DesiredAccess,
SampAliasObjectType, // ExpectedType
&FoundType
);
if (NT_SUCCESS(NtStatus)) {
//
// case on the type information requested
//
switch (AliasInformationClass) {
case AliasGeneralInformation:
//
// Get the member count
//
NtStatus = SampRetrieveAliasMembers(
AccountContext,
&(*Buffer)->General.MemberCount,
NULL // Only need members
);
if (NT_SUCCESS(NtStatus)) {
//
// Get copies of the strings we must retrieve from
// the registry.
//
NtStatus = SampGetUnicodeStringAttribute(
AccountContext,
SAMP_ALIAS_NAME,
TRUE, // Make copy
(PUNICODE_STRING)&((*Buffer)->General.Name)
);
if (NT_SUCCESS(NtStatus)) {
RegisterBuffer((*Buffer)->General.Name.Buffer);
NtStatus = SampGetUnicodeStringAttribute(
AccountContext,
SAMP_ALIAS_ADMIN_COMMENT,
TRUE, // Make copy
(PUNICODE_STRING)&((*Buffer)->General.AdminComment)
);
if (NT_SUCCESS(NtStatus)) {
RegisterBuffer((*Buffer)->General.AdminComment.Buffer);
}
}
}
break;
case AliasNameInformation:
//
// Get copies of the strings we must retrieve from
// the registry.
//
NtStatus = SampGetUnicodeStringAttribute(
AccountContext,
SAMP_ALIAS_NAME,
TRUE, // Make copy
(PUNICODE_STRING)&((*Buffer)->Name.Name)
);
if (NT_SUCCESS(NtStatus)) {
RegisterBuffer((*Buffer)->Name.Name.Buffer);
}
break;
case AliasAdminCommentInformation:
//
// Get copies of the strings we must retrieve from
// the registry.
//
NtStatus = SampGetUnicodeStringAttribute(
AccountContext,
SAMP_ALIAS_ADMIN_COMMENT,
TRUE, // Make copy
(PUNICODE_STRING)&((*Buffer)->AdminComment.AdminComment)
);
if (NT_SUCCESS(NtStatus)) {
RegisterBuffer((*Buffer)->AdminComment.AdminComment.Buffer);
}
break;
} // end_switch
//
// De-reference the object, discard any changes
//
IgnoreStatus = SampDeReferenceContext( AccountContext, FALSE );
ASSERT(NT_SUCCESS(IgnoreStatus));
}
//
// Free the read lock
//
SampReleaseReadLock();
//
// If we didn't succeed, free any allocated memory
//
if (!NT_SUCCESS(NtStatus)) {
for ( i=0; i<AllocatedBufferCount ; i++ ) {
MIDL_user_free( AllocatedBuffer[i] );
}
(*Buffer) = NULL;
}
return(NtStatus);
}
NTSTATUS
SamrSetInformationAlias(
IN SAMPR_HANDLE AliasHandle,
IN ALIAS_INFORMATION_CLASS AliasInformationClass,
IN PSAMPR_ALIAS_INFO_BUFFER Buffer
)
/*++
Routine Description:
This API allows the caller to modify alias information.
Parameters:
AliasHandle - The handle of an opened alias to operate on.
AliasInformationClass - Class of information to retrieve. The
accesses required for each class is shown below:
Info Level Required Access Type
------------------------ -------------------------
AliasGeneralInformation (can't write)
AliasNameInformation ALIAS_WRITE_ACCOUNT
AliasAdminCommentInformation ALIAS_WRITE_ACCOUNT
Buffer - Buffer where information retrieved is placed.
Return Values:
STATUS_SUCCESS - The Service completed successfully.
STATUS_ACCESS_DENIED - Caller does not have the appropriate
access to complete the operation.
STATUS_INVALID_INFO_CLASS - The class provided was invalid.
STATUS_INVALID_HANDLE - The handle passed is invalid.
STATUS_NO_SUCH_ALIAS - The alias specified is unknown.
STATUS_SPECIAL_ALIAS - The alias specified is a special alias and
cannot be operated on in the requested fashion.
STATUS_INVALID_DOMAIN_STATE - The domain server is not in the
correct state (disabled or enabled) to perform the requested
operation. The domain server must be enabled for this
operation
STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the
incorrect role (primary or backup) to perform the requested
operation.
--*/
{
NTSTATUS NtStatus;
NTSTATUS TmpStatus;
NTSTATUS IgnoreStatus;
PSAMP_OBJECT AccountContext;
SAMP_OBJECT_TYPE FoundType;
PSAMP_DEFINED_DOMAINS Domain;
ACCESS_MASK DesiredAccess;
UNICODE_STRING OldAccountName;
ULONG AliasRid,
DomainIndex;
BOOLEAN Modified = FALSE;
SAMTRACE("SamrSetInformationAlias");
OldAccountName.Buffer = NULL;
//
// Make sure we understand what RPC is doing for (to) us.
//
if (Buffer == NULL) {
return(STATUS_INVALID_PARAMETER);
}
//
// Set the desired access based upon the Info class
//
switch (AliasInformationClass) {
case AliasNameInformation:
case AliasAdminCommentInformation:
DesiredAccess = ALIAS_WRITE_ACCOUNT;
break;
case AliasGeneralInformation:
default:
return(STATUS_INVALID_INFO_CLASS);
} // end_switch
//
// Grab the lock
//
NtStatus = SampAcquireWriteLock();
if (!NT_SUCCESS(NtStatus)) {
return(NtStatus);
}
//
// Validate type of, and access to object.
//
AccountContext = (PSAMP_OBJECT)AliasHandle;
NtStatus = SampLookupContext(
AccountContext,
DesiredAccess,
SampAliasObjectType, // ExpectedType
&FoundType
);
if (NT_SUCCESS(NtStatus)) {
//
// Get a pointer to the domain this object is in.
// This is used for auditing.
//
DomainIndex = AccountContext->DomainIndex;
Domain = &SampDefinedDomains[ DomainIndex ];
//
// case on the type information requested
//
switch (AliasInformationClass) {
case AliasNameInformation:
NtStatus = SampChangeAliasAccountName(
AccountContext,
(PUNICODE_STRING)&(Buffer->Name.Name),
&OldAccountName
);
if (!NT_SUCCESS(NtStatus)) {
OldAccountName.Buffer = NULL;
}
//
// Don't delete the old account name yet; we'll still need
// to pass it to Netlogon below.
//
break;
case AliasAdminCommentInformation:
NtStatus = SampSetUnicodeStringAttribute(
AccountContext,
SAMP_ALIAS_ADMIN_COMMENT,
(PUNICODE_STRING)&(Buffer->AdminComment.AdminComment)
);
break;
} // end_switch
//
// Generate an audit if necessary
//
if ((NT_SUCCESS(NtStatus) &&
SampDoAccountAuditing(DomainIndex))) {
UNICODE_STRING
AccountName;
IgnoreStatus = SampGetUnicodeStringAttribute(
AccountContext, // Context
SAMP_ALIAS_NAME, // AttributeIndex
FALSE, // MakeCopy
&AccountName // UnicodeAttribute
);
if (NT_SUCCESS(IgnoreStatus)) {
LsaIAuditSamEvent(
STATUS_SUCCESS,
SE_AUDITID_LOCAL_GROUP_CHANGE, // AuditId
Domain->Sid, // Domain SID
NULL, // Member Rid (not used)
NULL, // Member Sid (not used)
&AccountName, // Account Name
&Domain->ExternalName, // Domain
&AccountContext->TypeBody.Alias.Rid, // Account Rid
NULL // Privileges used
);
}
}
//
// Dereference the account context
//
if (NT_SUCCESS(NtStatus)) {
//
// Save object RID before dereferencing context.
// RID is used in SampNotifyNetlogonOfDelta() call.
//
AliasRid = AccountContext->TypeBody.Alias.Rid;
//
// De-reference the object, write out any change to current xaction.
//
NtStatus = SampDeReferenceContext( AccountContext, TRUE );
} else {
//
// De-reference the object, ignore changes
//
TmpStatus = SampDeReferenceContext( AccountContext, FALSE );
ASSERT(NT_SUCCESS(TmpStatus));
}
} //end_if
//
// Commit the transaction and notify netlogon of any changes
//
if ( NT_SUCCESS(NtStatus) ) {
NtStatus = SampCommitAndRetainWriteLock();
if ( NT_SUCCESS(NtStatus) ) {
if ( AliasInformationClass == AliasNameInformation ) {
SampNotifyNetlogonOfDelta(
SecurityDbRename,
SecurityDbObjectSamAlias,
AliasRid,
&OldAccountName,
(DWORD) FALSE, // Replicate immediately
NULL // Delta data
);
} else {
SampNotifyNetlogonOfDelta(
SecurityDbChange,
SecurityDbObjectSamAlias,
AliasRid,
NULL,
(DWORD) FALSE, // Replicate immediately
NULL // Delta data
);
}
}
}
//
// Free up our old account name if we have one
//
SampFreeUnicodeString( &OldAccountName );
//
// Now release the write lock and return, propogating any errors.
//
TmpStatus = SampReleaseWriteLock( FALSE );
ASSERT(NT_SUCCESS(TmpStatus));
if (NT_SUCCESS(NtStatus)) {
NtStatus = TmpStatus;
}
return(NtStatus);
}
NTSTATUS
SamrDeleteAlias(
IN SAM_HANDLE *AliasHandle
)
/*++
Routine Description:
This API deletes an Alias from the account database. The Alias does
not have to be empty.
Note that following this call, the AliasHandle is no longer valid.
Parameters:
AliasHandle - The handle of an opened Alias to operate on.
Return Values:
STATUS_SUCCESS - The Service completed successfully.
STATUS_ACCESS_DENIED - Caller does not have the appropriate
access to complete the operation.
STATUS_INVALID_HANDLE - The handle passed is invalid.
STATUS_INVALID_DOMAIN_STATE - The domain server is not in the
correct state (disabled or enabled) to perform the requested
operation. The domain server must be enabled for this
operation
STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the
incorrect role (primary or backup) to perform the requested
operation.
--*/
{
UNICODE_STRING AliasName;
NTSTATUS NtStatus, TmpStatus, IgnoreStatus;
PSAMP_OBJECT AccountContext;
PSAMP_DEFINED_DOMAINS Domain;
SAMP_OBJECT_TYPE FoundType;
ULONG AliasRid,
DomainIndex;
SAMTRACE("SamrDeleteAlias");
//
// Grab the lock
//
NtStatus = SampAcquireWriteLock();
if (!NT_SUCCESS(NtStatus)) {
return(NtStatus);
}
//
// Validate type of, and access to object.
//
AccountContext = (PSAMP_OBJECT)(*AliasHandle);
NtStatus = SampLookupContext(
AccountContext,
DELETE,
SampAliasObjectType, // ExpectedType
&FoundType
);
if (NT_SUCCESS(NtStatus)) {
AliasRid = AccountContext->TypeBody.Alias.Rid;
//
// Get a pointer to the domain this object is in.
// This is used for auditing.
//
DomainIndex = AccountContext->DomainIndex;
Domain = &SampDefinedDomains[ DomainIndex ];
//
// Make sure the account is one that can be deleted.
// Can't be a built-in account, unless caller is trusted.
//
if ( !AccountContext->TrustedClient ) {
NtStatus = SampIsAccountBuiltIn( AliasRid );
}
if (NT_SUCCESS(NtStatus)) {
//
// Remove this alias from every account's alias-membership list
//
NtStatus = SampRemoveAliasFromAllAccounts(AccountContext);
if (NT_SUCCESS(NtStatus)) {
//
// First get and save the account name for
// I_NetNotifyLogonOfDelta.
//
NtStatus = SampGetUnicodeStringAttribute(
AccountContext,
SAMP_ALIAS_NAME,
TRUE, // Make copy
&AliasName
);
if (NT_SUCCESS(NtStatus)) {
//
// This must be done before we invalidate contexts, because our
// own handle to the alias gets closed as well.
//
NtStatus = SampDeleteAliasKeys( AccountContext );
if (NT_SUCCESS(NtStatus)) {
//
// We must invalidate any open contexts to this alias
// This will close all handles to the alias's keys.
// THIS IS AN IRREVERSIBLE PROCESS.
//
SampInvalidateAliasContexts( AliasRid );
//
// Commit the whole mess
//
NtStatus = SampCommitAndRetainWriteLock();
if ( NT_SUCCESS( NtStatus ) ) {
//
// Update the Alias Information Cache
//
IgnoreStatus = SampAlDeleteAlias( AliasHandle );
//
// Audit the deletion before we free the write lock
// so that we have access to the context block.
//
if (SampDoAccountAuditing(DomainIndex) &&
NT_SUCCESS(NtStatus) ) {
LsaIAuditSamEvent(
STATUS_SUCCESS,
SE_AUDITID_LOCAL_GROUP_DELETED, // AuditId
Domain->Sid, // Domain SID
NULL, // Member Rid (not used)
NULL, // Member sid (not used)
&AliasName, // Account Name
&Domain->ExternalName, // Domain
&AliasRid, // Account Rid
NULL // Privileges used
);
}
//
// Notify netlogon of the change
//
SampNotifyNetlogonOfDelta(
SecurityDbDelete,
SecurityDbObjectSamAlias,
AliasRid,
&AliasName,
(DWORD) FALSE, // Replicate immediately
NULL // Delta data
);
//
// Do delete auditing
//
if (NT_SUCCESS(NtStatus)) {
(VOID) NtDeleteObjectAuditAlarm(
&SampSamSubsystem,
*AliasHandle,
AccountContext->AuditOnClose
);
}
}
}
SampFreeUnicodeString( &AliasName );
}
}
}
//
// De-reference the object, discard any changes
//
TmpStatus = SampDeReferenceContext( AccountContext, FALSE );
ASSERT(NT_SUCCESS(TmpStatus));
if ( NT_SUCCESS( NtStatus ) ) {
//
// If we actually deleted the alias, then delete the context
// and let RPC know that the handle is invalid.
//
SampDeleteContext( AccountContext );
(*AliasHandle) = NULL;
}
} //end_if
//
// Free the lock -
//
// Everything has already been committed above, so we must indicate
// no additional changes have taken place.
//
TmpStatus = SampReleaseWriteLock( FALSE );
if (NtStatus == STATUS_SUCCESS) {
NtStatus = TmpStatus;
}
return(NtStatus);
}
NTSTATUS
SamrAddMemberToAlias(
IN SAMPR_HANDLE AliasHandle,
IN PRPC_SID MemberId
)
/*++
Routine Description:
This API adds a member to an alias. Note that this API requires the
ALIAS_ADD_MEMBER access type for the alias.
Parameters:
AliasHandle - The handle of an opened alias to operate on.
MemberId - SID of the member to add.
Return Values:
STATUS_SUCCESS - The Service completed successfully.
STATUS_ACCESS_DENIED - Caller does not have the appropriate
access to complete the operation.
STATUS_INVALID_HANDLE - The handle passed is invalid.
STATUS_NO_SUCH_MEMBER - The member specified is unknown.
STATUS_MEMBER_IN_ALIAS - The member already belongs to the alias.
STATUS_INVALID_MEMBER - The member has the wrong account type.
STATUS_INVALID_SID - The member sid is corrupted.
STATUS_INVALID_DOMAIN_STATE - The domain server is not in the
correct state (disabled or enabled) to perform the requested
operation. The domain server must be enabled for this
operation
STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the
incorrect role (primary or backup) to perform the requested
operation.
--*/
{
NTSTATUS NtStatus, TmpStatus, IgnoreStatus;
PSAMP_OBJECT AccountContext;
SAMP_OBJECT_TYPE FoundType;
ULONG ObjectRid;
SAMP_MEMBERSHIP_DELTA AdminChange = NoChange;
SAMP_MEMBERSHIP_DELTA OperatorChange = NoChange;
SAMTRACE("SamrAddMemberToAlias");
//
// Grab the lock
//
NtStatus = SampAcquireWriteLock();
if (!NT_SUCCESS(NtStatus)) {
return(NtStatus);
}
//
// Validate type of, and access to object.
//
AccountContext = (PSAMP_OBJECT)(AliasHandle);
NtStatus = SampLookupContext(
AccountContext,
ALIAS_ADD_MEMBER,
SampAliasObjectType, // ExpectedType
&FoundType
);
if (NT_SUCCESS(NtStatus)) {
//
// Check the potential new member is OK
//
NtStatus = SampValidateNewAliasMember(MemberId);
//
// If the member is being added to an ADMIN alias, we must make
// sure the member ACL(s) don't allow access by account operators.
//
if ( NT_SUCCESS( NtStatus ) ) {
if ( AccountContext->TypeBody.Alias.Rid == DOMAIN_ALIAS_RID_ADMINS ) {
AdminChange = AddToAdmin;
} else if ( ( AccountContext->TypeBody.Alias.Rid == DOMAIN_ALIAS_RID_SYSTEM_OPS ) ||
( AccountContext->TypeBody.Alias.Rid == DOMAIN_ALIAS_RID_PRINT_OPS ) ||
( AccountContext->TypeBody.Alias.Rid == DOMAIN_ALIAS_RID_BACKUP_OPS ) ||
( AccountContext->TypeBody.Alias.Rid == DOMAIN_ALIAS_RID_ACCOUNT_OPS ) ) {
OperatorChange = AddToAdmin;
}
//
// If either of these are changing, change account operator
// access to this member
//
if ( ( OperatorChange != NoChange ) ||
( AdminChange != NoChange ) ) {
NtStatus = SampChangeAccountOperatorAccessToMember(
MemberId,
AdminChange,
OperatorChange
);
}
}
if (NT_SUCCESS(NtStatus)) {
//
// Perform the user object side of things
//
NtStatus = SampAddAliasToAccountMembership(
AccountContext->TypeBody.Alias.Rid,
MemberId
);
//
// Now perform the alias side of things
//
if (NT_SUCCESS(NtStatus)) {
//
// Add the user to the alias (should not fail)
//
NtStatus = SampAddAccountToAlias(
AccountContext,
MemberId
);
}
}
//
// Dereference the account context
//
if (NT_SUCCESS(NtStatus)) {
//
// Save object RID before dereferencing context.
// RID is used in SampNotifyNetlogonOfDelta() call.
//
ObjectRid = AccountContext->TypeBody.Alias.Rid;
//
// De-reference the object, write out any change to current xaction.
//
NtStatus = SampDeReferenceContext( AccountContext, TRUE );
} else {
//
// De-reference the object, ignore changes
//
TmpStatus = SampDeReferenceContext( AccountContext, FALSE );
ASSERT(NT_SUCCESS(TmpStatus));
}
}
if (NT_SUCCESS(NtStatus)) {
//
// Commit the whole mess
//
NtStatus = SampCommitAndRetainWriteLock();
if ( NT_SUCCESS( NtStatus ) ) {
SAM_DELTA_DATA DeltaData;
//
// Update the Alias Information Cache
//
SAMPR_PSID_ARRAY MemberSids;
MemberSids.Count = 1;
MemberSids.Sids = (PSAMPR_SID_INFORMATION) &MemberId;
IgnoreStatus = SampAlAddMembersToAlias(
AliasHandle,
0,
&MemberSids
);
//
// Fill in id of member being added
//
DeltaData.AliasMemberId.MemberSid = MemberId;
SampNotifyNetlogonOfDelta(
SecurityDbChangeMemberAdd,
SecurityDbObjectSamAlias,
ObjectRid,
(PUNICODE_STRING) NULL,
(DWORD) FALSE, // Replicate immediately
&DeltaData
);
}
}
TmpStatus = SampReleaseWriteLock( FALSE );
ASSERT(NT_SUCCESS(TmpStatus));
return(NtStatus);
}
NTSTATUS
SamrAddMultipleMembersToAlias(
IN SAMPR_HANDLE AliasHandle,
IN PSAMPR_PSID_ARRAY MembersBuffer
)
/*++
Routine Description:
This api adds multiple members to an alias.
NOTE: For now, this routine takes a brute force approach.
I tried to do it in a better (more efficient) manner,
but kept running into problems. Finally, when I ran
into problems in the way SAM uses RXACT, I gave up
and did this brute force approach.
Parameters:
AliasHandle - The handle of an opened Alias to operate on.
MembersBuffer - Contains a count of SIDs to be added to the
alias and a pointer to a buffer containing an array of
pointers to SIDs. These SIDs are the SIDs of the members to
be added to the Alias.
Return Values:
STATUS_SUCCESS - The Service completed successfully. All of the
listed members are now members of the alias. However, some of
the members may already have been members of the alias (this is
NOT an error or warning condition).
STATUS_ACCESS_DENIED - Caller does not have the object open for
the required access.
STATUS_INVALID_HANDLE - The handle passed is invalid.
STATUS_INVALID_MEMBER - The member has the wrong account type.
STATUS_INVALID_SID - The member sid is corrupted.
STATUS_INVALID_DOMAIN_STATE - The domain server is not in the
correct state (disabled or enabled) to perform the requested
operation. The domain server must be enabled for this
operation
STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the
incorrect role (primary or backup) to perform the requested
operation.
--*/
{
NTSTATUS
NtStatus;
LONG
MemberCount,
i;
PSID
*MemberId;
SAMTRACE("SamrAddMultipleMembersToAlias");
MemberCount = (LONG)MembersBuffer->Count;
MemberId = (PSID *)MembersBuffer->Sids;
//
// Set completion status in case there are no members
//
NtStatus = STATUS_SUCCESS;
//
// Loop through the SIDs, adding them to the alias.
// Ignore any status value indicating the member is already
// a member. Other errors, however, will cause us to abort.
//
for (i=0; i<MemberCount; i++) {
NtStatus = SamrAddMemberToAlias( AliasHandle, MemberId[i] );
if (NtStatus == STATUS_MEMBER_IN_ALIAS) {
NtStatus = STATUS_SUCCESS;
}
if (!NT_SUCCESS(NtStatus)) {
break; //for loop
}
} //end_for
return(NtStatus);
}
NTSTATUS
SamrRemoveMemberFromAlias(
IN SAMPR_HANDLE AliasHandle,
IN PRPC_SID MemberId
)
/*++
Routine Description:
This API removes a member from an alias. Note that this API requires the
ALIAS_REMOVE_MEMBER access type for the alias.
Parameters:
AliasHandle - The handle of an opened alias to operate on.
MemberId - SID of the member to remove.
Return Value:
????
--*/
{
NTSTATUS NtStatus, TmpStatus, IgnoreStatus;
PSAMP_OBJECT AccountContext;
SAMP_OBJECT_TYPE FoundType;
ULONG ObjectRid;
SAMP_MEMBERSHIP_DELTA AdminChange = NoChange;
SAMP_MEMBERSHIP_DELTA OperatorChange = NoChange;
SAMTRACE("SamrRemoveMemberFromAlias");
//
// Grab the lock
//
NtStatus = SampAcquireWriteLock();
if (!NT_SUCCESS(NtStatus)) {
return(NtStatus);
}
//
// Validate type of, and access to object.
//
AccountContext = (PSAMP_OBJECT)(AliasHandle);
NtStatus = SampLookupContext(
AccountContext,
ALIAS_REMOVE_MEMBER,
SampAliasObjectType, // ExpectedType
&FoundType
);
if (NT_SUCCESS(NtStatus)) {
//
// Validate the sid of the member.
//
if ((MemberId == NULL) || !RtlValidSid(MemberId)) {
NtStatus = STATUS_INVALID_SID;
}
if (NT_SUCCESS(NtStatus)) {
//
// Perform the user object side of things
//
NtStatus = SampRemoveAliasFromAccountMembership(
AccountContext->TypeBody.Alias.Rid,
(PSID)MemberId
);
//
// Now perform the alias side of things
//
if (NT_SUCCESS(NtStatus)) {
//
// Remove the user from the alias (should not fail)
//
NtStatus = SampRemoveAccountFromAlias(
AccountContext,
(PSID)MemberId
);
//
// If the member is being removed from an ADMIN alias, we must make
// sure the member ACL(s) allow access by account operators.
//
if ( NT_SUCCESS( NtStatus ) ) {
if ( AccountContext->TypeBody.Alias.Rid == DOMAIN_ALIAS_RID_ADMINS ) {
AdminChange = RemoveFromAdmin;
} else if ( ( AccountContext->TypeBody.Alias.Rid == DOMAIN_ALIAS_RID_SYSTEM_OPS ) ||
( AccountContext->TypeBody.Alias.Rid == DOMAIN_ALIAS_RID_PRINT_OPS ) ||
( AccountContext->TypeBody.Alias.Rid == DOMAIN_ALIAS_RID_BACKUP_OPS ) ||
( AccountContext->TypeBody.Alias.Rid == DOMAIN_ALIAS_RID_ACCOUNT_OPS ) ) {
OperatorChange = RemoveFromAdmin;
}
//
// If either of these are changing, change account operator
// access to this member
//
if ( ( OperatorChange != NoChange ) ||
( AdminChange != NoChange ) ) {
NtStatus = SampChangeAccountOperatorAccessToMember(
MemberId,
AdminChange,
OperatorChange
);
}
}
}
}
//
// Dereference the account context
//
if (NT_SUCCESS(NtStatus)) {
//
// Save object RID before dereferencing context.
// RID is used in SampNotifyNetlogonOfDelta() call.
//
ObjectRid = AccountContext->TypeBody.Alias.Rid;
//
// De-reference the object, write out any change to current xaction.
//
NtStatus = SampDeReferenceContext( AccountContext, TRUE );
} else {
//
// De-reference the object, ignore changes
//
TmpStatus = SampDeReferenceContext( AccountContext, FALSE );
ASSERT(NT_SUCCESS(TmpStatus));
}
}
if (NT_SUCCESS(NtStatus)) {
NtStatus = SampCommitAndRetainWriteLock();
if ( NT_SUCCESS( NtStatus ) ) {
SAM_DELTA_DATA DeltaData;
//
// Update the Alias Information Cache
//
SAMPR_PSID_ARRAY MemberSids;
MemberSids.Count = 1;
MemberSids.Sids = (PSAMPR_SID_INFORMATION) &MemberId;
IgnoreStatus = SampAlRemoveMembersFromAlias(
AliasHandle,
0,
&MemberSids
);
//
// Fill in id of member being deleted
//
DeltaData.AliasMemberId.MemberSid = MemberId;
SampNotifyNetlogonOfDelta(
SecurityDbChangeMemberDel,
SecurityDbObjectSamAlias,
ObjectRid,
(PUNICODE_STRING) NULL,
(DWORD) FALSE, // Replicate immediately
&DeltaData
);
}
}
TmpStatus = SampReleaseWriteLock( FALSE );
ASSERT(NT_SUCCESS(TmpStatus));
return(NtStatus);
}
NTSTATUS
SamrRemoveMultipleMembersFromAlias(
IN SAMPR_HANDLE AliasHandle,
IN PSAMPR_PSID_ARRAY MembersBuffer
)
/*++
Routine Description:
This API removes members from an alias. Note that this API requires
the ALIAS_REMOVE_MEMBER access type for the alias.
NOTE: This api currently uses a brute-force approach to adding
members to the alias. This is because of problems
encountered when trying to do "the right thing".
Parameters:
AliasHandle - The handle of an opened alias to operate on.
MembersBuffer - Contains a count of SIDs to be added to the
alias and a pointer to a buffer containing an array of
pointers to SIDs. These SIDs are the SIDs of the members to
be added to the Alias.
Return Values:
STATUS_SUCCESS - The Service completed successfully. All of the
listed members are now members of the alias. However, some of
the members may already have been members of the alias (this is
NOT an error or warning condition).
STATUS_ACCESS_DENIED - Caller does not have the object open for
the required access.
STATUS_INVALID_HANDLE - The handle passed is invalid.
STATUS_INVALID_SID - The member sid is corrupted.
STATUS_INVALID_DOMAIN_STATE - The domain server is not in the
correct state (disabled or enabled) to perform the requested
operation. The domain server must be enabled for this
operation
STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the
incorrect role (primary or backup) to perform the requested
operation.
--*/
{
NTSTATUS
NtStatus;
LONG
MemberCount,
i;
PSID
*MemberId;
SAMTRACE("SamrRemoveMultipleMembersFromAlias");
MemberCount = (LONG)MembersBuffer->Count;
MemberId = (PSID *)MembersBuffer->Sids;
//
// Set completion status in case there are no members
//
NtStatus = STATUS_SUCCESS;
//
// Loop through the SIDs, adding them to the alias.
// Ignore any status value indicating the member is already
// a member. Other errors, however, will cause us to abort.
//
for (i=0; i<MemberCount; i++) {
NtStatus = SamrAddMemberToAlias( AliasHandle, MemberId[i] );
if (NtStatus == STATUS_MEMBER_NOT_IN_ALIAS) {
NtStatus = STATUS_SUCCESS;
}
if (!NT_SUCCESS(NtStatus)) {
break; //for loop
}
} //end_for
return(NtStatus);
}
NTSTATUS
SamrGetMembersInAlias(
IN SAM_HANDLE AliasHandle,
OUT PSAMPR_PSID_ARRAY GetMembersBuffer
)
/*++
Routine Description:
This API lists all members in an Alias. This API requires
ALIAS_LIST_MEMBERS access to the Alias.
NOTE: This function does not use the Alias cache.
Parameters:
AliasHandle - The handle of an opened Alias to operate on.
MemberIds - Receives a pointer to a buffer containing an array of
pointers to SIDs. These SIDs are the SIDs of the members of the
Alias. When this information is no longer needed, this buffer
must be freed using SamFreeMemory().
MemberCount - number of members in the Alias (and, thus, the number
of relative IDs returned).
Return Values:
STATUS_SUCCESS - The Service completed successfully, and there are
no additional entries.
STATUS_ACCESS_DENIED - Caller does not have privilege required to
request that data.
STATUS_INVALID_HANDLE - The handle passed is invalid.
--*/
{
NTSTATUS NtStatus, IgnoreStatus;
PSAMP_OBJECT AccountContext;
SAMP_OBJECT_TYPE FoundType;
SAMTRACE("SamrGetMembersInAlias");
//
// Make sure we understand what RPC is doing for (to) us.
//
ASSERT (GetMembersBuffer != NULL);
//
// Grab the lock
//
SampAcquireReadLock();
//
// Validate type of, and access to object.
//
AccountContext = (PSAMP_OBJECT)AliasHandle;
NtStatus = SampLookupContext(
AccountContext,
ALIAS_LIST_MEMBERS,
SampAliasObjectType, // ExpectedType
&FoundType
);
if (NT_SUCCESS(NtStatus)) {
NtStatus = SampRetrieveAliasMembers(
AccountContext,
&(GetMembersBuffer->Count),
(PSID **)&(GetMembersBuffer->Sids)
);
//
// De-reference the object, discarding changes
//
IgnoreStatus = SampDeReferenceContext( AccountContext, FALSE );
ASSERT(NT_SUCCESS(IgnoreStatus));
}
//
// Free the read lock
//
SampReleaseReadLock();
//
// Tidy up on failure
//
if (!NT_SUCCESS(NtStatus)){
GetMembersBuffer->Count = 0;
GetMembersBuffer->Sids = NULL;
}
return(NtStatus);
}
///////////////////////////////////////////////////////////////////////////////
// //
// Internal Services Available For Use in Other SAM Modules //
// //
///////////////////////////////////////////////////////////////////////////////
NTSTATUS
SampRemoveAccountFromAllAliases(
IN PSID AccountSid,
IN BOOLEAN CheckAccess,
IN SAMPR_HANDLE DomainHandle OPTIONAL,
IN PULONG MembershipCount OPTIONAL,
IN PULONG *Membership OPTIONAL
)
/*++
Routine Description:
This routine removes the specified account from the member list of all
aliases in this domain.
The caller of this service is expected to be in the middle of a
RXACT transaction. This service simply adds some actions to that
RXACT transaction.
This routine is used while deleting a user or a group and hence should
never be called on a DS domain. ( The DS will maintain the cross consi-
stency when a user or group is deleted ).
Arguments:
AccountSid - The SID of the account being Removed.
CheckAccess - if TRUE, this routine will make sure that the caller
is allowed REMOVE_ALIAS_MEMBER access to this alias. If FALSE,
the caller is already known to have proper access.
DomainHandle - if CheckAccess is TRUE, this handle must be provided
to allow access to be checked.
MembershipCount - if CheckAccess is TRUE, this pointer must be
provided to receive the number of aliases the account was
deleted from.
Membership - if CheckAccess is TRUE, this pointer must be provided
to point to a list of aliases the account was removed from. The
caller must free this list with MIDL_user_free().
Return Value:
STATUS_SUCCESS - The user has been Removed from all aliases.
--*/
{
NTSTATUS NtStatus, IgnoreStatus;
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING DomainKeyName, AccountKeyName;
HANDLE TempHandle, AliasHandle;
ULONG LocalMembershipCount;
PULONG LocalMembership;
ULONG KeyValueLength;
ULONG i;
PSAMP_OBJECT AliasContext;
SAMTRACE("SampRemoveAccountFromAllAliases");
//
// We should never get this call for a DS domain
//
ASSERT(IsDsObject(
SampDefinedDomains[SampTransactionDomainIndex].Context
)== FALSE
);
//
// Get the alias membership for this account
//
NtStatus = SampBuildAliasMembersKeyName(
AccountSid,
&DomainKeyName,
&AccountKeyName
);
if (NT_SUCCESS(NtStatus)) {
InitializeObjectAttributes(
&ObjectAttributes,
&AccountKeyName,
OBJ_CASE_INSENSITIVE,
SampKey,
NULL
);
SampDumpNtOpenKey((KEY_READ), &ObjectAttributes, 0);
NtStatus = RtlpNtOpenKey(
&TempHandle,
(KEY_READ),
&ObjectAttributes,
0
);
if ((NtStatus == STATUS_OBJECT_PATH_NOT_FOUND) ||
(NtStatus == STATUS_OBJECT_NAME_NOT_FOUND) ) {
//
// This account is not a member of any of our aliases
//
NtStatus = STATUS_SUCCESS;
if ( CheckAccess ) {
//
// Return the list of aliases the account was
// removed from; in this case, none.
//
( *MembershipCount ) = 0;
( *Membership ) = NULL;
}
} else {
//
// Load in the alias membership list
//
if (NT_SUCCESS(NtStatus)) {
KeyValueLength = 0;
NtStatus = RtlpNtQueryValueKey( TempHandle,
&LocalMembershipCount,
NULL,
&KeyValueLength,
NULL);
SampDumpRtlpNtQueryValueKey(&LocalMembershipCount,
NULL,
&KeyValueLength,
NULL);
if (NT_SUCCESS(NtStatus)) {
ASSERT(LocalMembershipCount == 0);
}
if (NtStatus == STATUS_BUFFER_OVERFLOW) {
LocalMembership = MIDL_user_allocate( KeyValueLength );
if (LocalMembership == NULL) {
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
} else {
NtStatus = RtlpNtQueryValueKey(
TempHandle,
NULL,
LocalMembership,
&KeyValueLength,
NULL);
SampDumpRtlpNtQueryValueKey(NULL,
LocalMembership,
&KeyValueLength,
NULL);
if (NT_SUCCESS(NtStatus)) {
//
// Remove the account from each alias
//
for (i=0; i < LocalMembershipCount; i++) {
if ( CheckAccess ) {
//
// If account is being removed from
// the ADMIN alias, change ACL to
// allow account operators to access
// the account (unless account is an
// admin some other way). Kind of
// useless since the account is about
// to be deleted, but do it anyway
// in case something bad happens and
// it doesn't get deleted.
//
//
// BUGBUG: this may not do it - we may
// need to check the admin count on
// the group. MMS 9/5/95
//
if ( LocalMembership[i] ==
DOMAIN_ALIAS_RID_ADMINS ) {
NtStatus = SampChangeAccountOperatorAccessToMember(
AccountSid,
RemoveFromAdmin,
NoChange );
}
//
// Just open and close the alias
// to make sure we are allowed
// the necessary access.
//
SampTransactionWithinDomain = FALSE;
NtStatus = SampOpenAccount(
SampAliasObjectType,
DomainHandle,
ALIAS_REMOVE_MEMBER,
LocalMembership[i],
TRUE,
(SAMPR_HANDLE *)&AliasHandle
);
if (NT_SUCCESS(NtStatus)) {
SampDeleteContext(
(PSAMP_OBJECT)( AliasHandle ) );
}
}
if (!NT_SUCCESS(NtStatus)) {
break;
}
NtStatus = SampCreateAccountContext(
SampAliasObjectType,
LocalMembership[i],
TRUE, // Trusted client
TRUE, // Account exists
&AliasContext
);
if (NT_SUCCESS(NtStatus)) {
NtStatus = SampRemoveAccountFromAlias(
AliasContext,
AccountSid );
if (NT_SUCCESS(NtStatus)) {
//
// Save the alias changes we just
// made. We'll delete the context,
// so don't let RXACT use the open
// key handle in the context.
//
NtStatus = SampStoreObjectAttributes(
AliasContext,
FALSE
);
}
SampDeleteContext(AliasContext);
}
if (!NT_SUCCESS(NtStatus)) {
break;
}
}
//
// Delete the account membership keys
//
if (NT_SUCCESS(NtStatus)) {
NtStatus = SampDeleteAliasMembershipKeysForAccount(
AccountSid);
}
}
if ( CheckAccess ) {
//
// Return the list of aliases the account was
// removed from.
//
( *MembershipCount ) = LocalMembershipCount;
( *Membership ) = LocalMembership;
} else {
MIDL_user_free(LocalMembership);
}
}
}
IgnoreStatus = NtClose( TempHandle );
ASSERT( NT_SUCCESS(IgnoreStatus) );
}
}
SampFreeUnicodeString( &DomainKeyName );
SampFreeUnicodeString( &AccountKeyName );
}
return( NtStatus );
}
NTSTATUS
SampRetrieveAliasMembership(
IN PSID Account,
OUT PULONG MemberCount OPTIONAL,
IN OUT PULONG BufferSize OPTIONAL,
OUT PULONG Buffer OPTIONAL
)
/*++
Routine Description:
This service retrieves the number of aliases in the current domain
that the specified account is a member of. If desired it will also fill
in a buffer with the alias rids.
Note: THIS ROUTINE REFERENCES THE CURRENT TRANSACTION DOMAIN
(ESTABLISHED USING SampSetTransactioDomain()). THIS
SERVICE MAY ONLY BE CALLED AFTER SampSetTransactionDomain()
AND BEFORE SampReleaseWriteLock().
Arguments:
Account - the account whose membership we are interested in.
MemberCount - Receives the number of current-domain-aliases the
account is a member of.
BufferSize - (Optional) Specified the size of memory pointer to by buffer.
Buffer - (Otional) Is filled in with the list of alias membership rids.
If this value is NULL, then this information
is not returned. The returned buffer is allocated using
MIDL_user_allocate() and must be freed using MIDL_user_free() when
no longer needed.
Return Value:
STATUS_SUCCESS - The information has been retrieved.
STATUS_INSUFFICIENT_RESOURCES - Memory could not be allocated for the
string to be returned in.
Other status values that may be returned are those returned
by:
NtOpenKey()
NtQueryValueKey()
--*/
{
NTSTATUS NtStatus, IgnoreStatus;
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING DomainKeyName, AccountKeyName;
HANDLE TempHandle;
PSAMP_OBJECT DomainContext;
SAMTRACE("SampRetrieveAliasMembership");
DomainContext = SampDefinedDomains[SampTransactionDomainIndex].Context;
if (IsDsObject(DomainContext))
{
//
// DS Case
//
NtStatus = SampDsGetAliasMembershipOfAccount(
DomainContext->ObjectNameInDs,
Account,
MemberCount,
BufferSize,
Buffer
);
}
else
{
//
// Get the membership count for this account
//
NtStatus = SampBuildAliasMembersKeyName(
Account,
&DomainKeyName,
&AccountKeyName
);
if (NT_SUCCESS(NtStatus)) {
InitializeObjectAttributes(
&ObjectAttributes,
&AccountKeyName,
OBJ_CASE_INSENSITIVE,
SampKey,
NULL
);
SampDumpNtOpenKey((KEY_READ), &ObjectAttributes, 0);
NtStatus = RtlpNtOpenKey(
&TempHandle,
(KEY_READ),
&ObjectAttributes,
0
);
if ((NtStatus == STATUS_OBJECT_PATH_NOT_FOUND) ||
(NtStatus == STATUS_OBJECT_NAME_NOT_FOUND) ) {
//
// This account is not a member of any of our aliases
//
NtStatus = STATUS_SUCCESS;
if (ARGUMENT_PRESENT(MemberCount)) {
*MemberCount = 0;
}
if (ARGUMENT_PRESENT(BufferSize)) {
*BufferSize = 0;
}
} else {
if (NT_SUCCESS(NtStatus)) {
NtStatus = RtlpNtQueryValueKey( TempHandle,
MemberCount,
Buffer,
BufferSize,
NULL);
SampDumpRtlpNtQueryValueKey(MemberCount,
Buffer,
BufferSize,
NULL);
IgnoreStatus = NtClose( TempHandle );
ASSERT( NT_SUCCESS(IgnoreStatus) );
}
}
SampFreeUnicodeString( &DomainKeyName );
SampFreeUnicodeString( &AccountKeyName );
}
}
return( NtStatus );
}
///////////////////////////////////////////////////////////////////////////////
// //
// Services Private to this file //
// //
///////////////////////////////////////////////////////////////////////////////
NTSTATUS
SampAddAccountToAlias(
IN PSAMP_OBJECT AccountContext,
IN PSID AccountSid
)
/*++
Routine Description:
This service is used to add an account as a member of a specified alias
This is done by simply adding the account SID to the list of SIDs
in the MEMBERS attribute of the the specified alias
The caller of this service is expected to be in the middle of a
RXACT transaction. This service simply edits the in-memory copy of
the alias information.
Arguments:
AccountContext - Context block Describing the Alias
AccountSid - The Sid of the account being added as a new member.
Return Value:
STATUS_SUCCESS - The account was added.
--*/
{
NTSTATUS NtStatus;
ULONG MemberCount, i;
ULONG MemberArraySize;
PSID MemberArray;
SAMTRACE("SampAddAccountToAlias");
//
// Need to do different things for DS and Registry
//
if (IsDsObject(AccountContext))
{
DSNAME * DsNameOfAccount = NULL;
//
// DS based Domain
//
//
// Get the DSNAME corresponding to the given SID.
// This may result in a call to the GC server.
//
NtStatus = SampDsObjectFromSid(AccountSid,&DsNameOfAccount);
if NT_SUCCESS(NtStatus)
{
//
// Add this entry to the Ds
//
NtStatus = SampDsAddMembershipAttribute(
AccountContext->ObjectNameInDs,
SampAliasObjectType,
DsNameOfAccount
);
MIDL_user_free(DsNameOfAccount);
}
}
else
{
//
// Regisry based Domain
//
NtStatus = SampGetSidArrayAttribute(
AccountContext,
SAMP_ALIAS_MEMBERS,
FALSE, // Reference directly
&MemberArray,
&MemberArraySize,
&MemberCount
);
if (NT_SUCCESS(NtStatus)) {
PSID MemberPointer = MemberArray;
//
// Check the member is really new
//
for (i = 0; i<MemberCount ; i++ ) {
if (RtlEqualSid(MemberPointer, AccountSid)) {
NtStatus = STATUS_MEMBER_IN_ALIAS;
break;
}
((PCHAR)MemberPointer) += RtlLengthSid(MemberPointer);
}
if (NT_SUCCESS(NtStatus)) {
//
// MemberPointer now points at the byte beyond the end of the
// old member array
//
//
// Allocate a new membership buffer large enough for the existing
// member list and the new one.
//
ULONG OldTotalSize = ((PCHAR)MemberPointer) - ((PCHAR)MemberArray);
ULONG NewMemberSize = RtlLengthSid(AccountSid);
ULONG NewTotalSize = OldTotalSize + NewMemberSize;
PSID NewMemberArray;
NewMemberArray = MIDL_user_allocate( NewTotalSize );
if (NewMemberArray == NULL) {
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
} else {
//
// Copy the member list into the new array
//
RtlCopyMemory(NewMemberArray, MemberArray, OldTotalSize);
//
// Add the new member to the end
//
MemberCount += 1;
NtStatus = RtlCopySid(
NewMemberSize,
((PCHAR)NewMemberArray) + OldTotalSize,
AccountSid);
if (NT_SUCCESS(NtStatus)) {
//
// Update the alias with it's new member list
//
NtStatus = SampSetSidArrayAttribute(
AccountContext,
SAMP_ALIAS_MEMBERS,
NewMemberArray,
NewTotalSize,
MemberCount
);
}
//
// Free up the membership array we allocated
//
MIDL_user_free( NewMemberArray );
}
}
}
//
// End of Registry Part
//
}
//
// Account has been added to alias membership
// audit this, if necessary.
//
if (NT_SUCCESS(NtStatus) &&
SampDoAccountAuditing(AccountContext->DomainIndex)) {
UNICODE_STRING NameString;
PSAMP_DEFINED_DOMAINS Domain;
SAMP_OBJECT_TYPE ObjectType;
NTSTATUS Status;
Domain = &SampDefinedDomains[ AccountContext->DomainIndex ];
Status = SampLookupAccountName(
AccountContext->TypeBody.Alias.Rid,
&NameString,
&ObjectType
);
if ( !NT_SUCCESS( Status )) {
RtlInitUnicodeString( &NameString, L"-" );
}
LsaIAuditSamEvent(
STATUS_SUCCESS,
SE_AUDITID_LOCAL_GROUP_ADD, // AuditId
Domain->Sid, // Domain SID
NULL, // Member Rid
AccountSid, // Member sid
&NameString, // Account Name
&Domain->ExternalName, // Domain
&AccountContext->TypeBody.Alias.Rid, // Account Rid
NULL // Privileges used
);
if ( NT_SUCCESS( Status )) {
MIDL_user_free( NameString.Buffer );
}
}
return(NtStatus);
}
NTSTATUS
SampRemoveAccountFromAlias(
IN PSAMP_OBJECT AccountContext,
IN PSID AccountSid
)
/*++
Routine Description:
This routine is used to Remove an account from a specified alias.
This is done by simply Removing the user's Sid From the list of Sids
in the MEMBERS sub-key of the the specified alias.
It is the caller's responsibility to know that the user is, in fact,
currently a member of the alias.
The caller of this service is expected to be in the middle of a
RXACT transaction. This service simply adds some actions to that
RXACT transaction.
Arguments:
AliasRid - The RID of the alias the account is to be removed from.
AccountSid - The SID of the account being Removed.
Return Value:
STATUS_SUCCESS - The user has been Removed.
STATUS_MEMBER_NOT_IN_ALIAS - The account was not a member of the alias.
--*/
{
NTSTATUS NtStatus;
ULONG MemberCount, i;
ULONG MemberArraySize;
PSID MemberArray, Member, NextMember;
ULONG RemovedMemberSize = RtlLengthSid(AccountSid);
SAMTRACE("SampRemoveAccountFromAlias");
//
// Test wether we are DS based
//
if (IsDsObject(AccountContext))
{
DSNAME *DsNameOfAccount = NULL;
//
// Get the DS Name corresponding to the account
//
NtStatus = SampDsObjectFromSid(AccountSid, &DsNameOfAccount);
if (NT_SUCCESS(NtStatus))
{
//
// Remove the account from the membership list
//
NtStatus = SampDsRemoveMembershipAttribute(
AccountContext->ObjectNameInDs,
SampAliasObjectType,
DsNameOfAccount
);
}
}
else
{
//
// We are registry based
//
//
// Get a copy of the current member array.
//
NtStatus = SampGetSidArrayAttribute(
AccountContext,
SAMP_ALIAS_MEMBERS,
TRUE, // Make copy
&MemberArray,
&MemberArraySize,
&MemberCount
);
if (NT_SUCCESS(NtStatus)) {
//
// For each member sid, copy it from old to new member
// arrays if it is not the sid we're trying to delete
//
Member = MemberArray;
for (i = 0; i < MemberCount ; i++ ) {
NextMember = (PSID)(((PCHAR)Member) + RtlLengthSid(Member));
if (RtlEqualSid(Member, AccountSid)) {
//
// Found the member to delete. Shift subsequent members
//
while ((PCHAR)NextMember <
(((PCHAR)MemberArray) + MemberArraySize)) {
*((PCHAR)Member)++ = *((PCHAR)NextMember)++;
}
break;
}
//
// Advance the old pointer
//
Member = NextMember;
ASSERT((PCHAR)Member <= (((PCHAR)MemberArray) + MemberArraySize));
}
//
// If nothing was removed, we didn't find the account
//
if (i == MemberCount) {
NtStatus = STATUS_MEMBER_NOT_IN_ALIAS;
} else {
//
// The member has been removed, write out the new member list
//
ASSERT((PCHAR)Member ==
(((PCHAR)MemberArray)) + MemberArraySize - RemovedMemberSize);
NtStatus = SampSetSidArrayAttribute(
AccountContext,
SAMP_ALIAS_MEMBERS,
MemberArray,
MemberArraySize - RemovedMemberSize,
MemberCount - 1
);
//
// audit this, if necessary.
//
if (NT_SUCCESS(NtStatus) &&
SampDoAccountAuditing(AccountContext->DomainIndex)) {
UNICODE_STRING NameString;
SAMP_OBJECT_TYPE ObjectType;
NTSTATUS Status;
PSAMP_DEFINED_DOMAINS Domain;
Status = SampLookupAccountName(
AccountContext->TypeBody.Alias.Rid,
&NameString,
&ObjectType
);
if ( !NT_SUCCESS( Status )) {
RtlInitUnicodeString( &NameString, L"-" );
}
Domain = &SampDefinedDomains[ AccountContext->DomainIndex ];
LsaIAuditSamEvent(
STATUS_SUCCESS,
SE_AUDITID_LOCAL_GROUP_REM, // AuditId
Domain->Sid, // Domain SID
NULL, // Member Rid
AccountSid, // Member sid
&NameString, // Account Name
&Domain->ExternalName, // Domain
&AccountContext->TypeBody.Alias.Rid, // Account Rid
NULL // Privileges used
);
if ( NT_SUCCESS( Status )) {
MIDL_user_free( NameString.Buffer );
}
}
}
//
// Free up the member array
//
MIDL_user_free(MemberArray);
}
}
return(NtStatus);
}
NTSTATUS
SampAddAliasToAccountMembership(
IN ULONG AliasRid,
IN PSID AccountSid
)
/*++
Routine Description:
This service adds the specified alias to the account's membership
list. It is not assumed that the caller knows anything about
the target account. In particular, the caller doesn't know whether
the account exists or not, nor whether the account is already a member
of the alias.
THIS SERVICE MUST BE CALLED WITH THE TRANSACTION DOMAIN SET.
It is a No-Op for DS based accounts, as no reverse membership list
is being maintained.
Arguments:
AliasRid - The relative ID of the alias.
AccountSid - The SID of the account.
Return Value:
STATUS_SUCCESS - The information has been updated and added to the
RXACT.
STATUS_MEMBER_IN_ALIAS - The account is already a member of the
specified alias.
Other status values that may be returned are those returned
by:
NtOpenKey()
NtQueryValueKey()
RtlAddActionToRXact()
--*/
{
NTSTATUS NtStatus, IgnoreStatus;
UNICODE_STRING DomainKeyName;
UNICODE_STRING AccountKeyName;
HANDLE TempHandle;
ULONG MembershipCount, KeyValueLength;
ULONG DomainRidCount;
ULONG i;
PULONG MembershipArray;
OBJECT_ATTRIBUTES ObjectAttributes;
BOOLEAN NewAccount;
SAMTRACE("SampAddAliasToAccountMembership");
//
// There are 4 cases
//
// 1. If the Alias exists in the DS , Member Id corresponds to a SID
// in a registry based domain. Only builtin domains will be registry
// based and we do not support adding a security prinicpal from a
// built in domain into the membership list of an alias in a DS domain.
// This case should never happen. If it happens do nothing
//
// 2. Alias is in DS, Member Id is a SID in a DS based domain
// Operation is a No Op
//
// 3. Alias exists in Registry ( Builtin Domain Alias ), Member Id is
// a SID in a DS based domain. In this case do whatever the original
// code did
//
// 4. Alias exists in Registry ( builtin Domain Alias ). Member Id is a
// SID in a registry based domain ( Builtin Domain ). Do whatever
// the previous code did
//
if (!IsDsObject(SampDefinedDomains[SampTransactionDomainIndex].Context))
{
//
// Cases 3, 4
//
//
// Assume the account is a member of at least one of our aliases
//
NewAccount = FALSE;
NtStatus = SampBuildAliasMembersKeyName(
AccountSid,
&DomainKeyName,
&AccountKeyName
);
if (NT_SUCCESS(NtStatus)) {
//
// Try to open the domain alias/members/(domain) key for this account
//
InitializeObjectAttributes(
&ObjectAttributes,
&DomainKeyName,
OBJ_CASE_INSENSITIVE,
SampKey,
NULL
);
SampDumpNtOpenKey((KEY_READ), &ObjectAttributes, 0);
NtStatus = RtlpNtOpenKey(
&TempHandle,
(KEY_READ),
&ObjectAttributes,
0
);
if (NT_SUCCESS(NtStatus)) {
//
// Get the current domain rid count
//
NtStatus = RtlpNtQueryValueKey(
TempHandle,
&DomainRidCount,
NULL,
NULL,
NULL);
SampDumpRtlpNtQueryValueKey(&DomainRidCount,
NULL,
NULL,
NULL);
IgnoreStatus = NtClose(TempHandle);
ASSERT(NT_SUCCESS(IgnoreStatus));
} else {
if (NtStatus == STATUS_OBJECT_NAME_NOT_FOUND) {
//
// No other accounts in this domain are members of any of our
// aliases.
//
// Create a new key for this domain with no accounts (rids).
//
NewAccount = TRUE;
DomainRidCount = 0; // No accounts yet
NtStatus = RtlAddActionToRXact(
SampRXactContext,
RtlRXactOperationSetValue,
&DomainKeyName,
DomainRidCount,
NULL,
0
);
if (NT_SUCCESS(NtStatus)) {
//
// Keep our domain count uptodate
//
NtStatus = SampAdjustAliasDomainsCount(TRUE);
}
}
}
if (NT_SUCCESS(NtStatus)) {
if (!NewAccount) {
//
// Try to open the domain alias/members/(domain)/(account) key
//
InitializeObjectAttributes(
&ObjectAttributes,
&AccountKeyName,
OBJ_CASE_INSENSITIVE,
SampKey,
NULL
);
SampDumpNtOpenKey((KEY_READ), &ObjectAttributes, 0);
NtStatus = RtlpNtOpenKey(
&TempHandle,
(KEY_READ),
&ObjectAttributes,
0
);
}
if (NewAccount || (NtStatus == STATUS_OBJECT_NAME_NOT_FOUND)) {
//
// This account is not a member of any of our aliases yet.
//
NewAccount = TRUE;
//
// Set up it's initial membership
//
MembershipCount = 1;
MembershipArray = &AliasRid;
NtStatus = STATUS_SUCCESS; // We're doing fine
}
if (NT_SUCCESS(NtStatus) && !NewAccount) {
//
// This account already exists
//
// Get the current membership buffer and add the new alias
//
KeyValueLength = 0;
NtStatus = RtlpNtQueryValueKey(
TempHandle,
&MembershipCount,
NULL,
&KeyValueLength,
NULL);
SampDumpRtlpNtQueryValueKey(&MembershipCount,
NULL,
&KeyValueLength,
NULL);
if (NT_SUCCESS(NtStatus) || (NtStatus == STATUS_BUFFER_OVERFLOW)) {
ASSERT(KeyValueLength == (MembershipCount * sizeof(ULONG)));
//
// Allocate a membership buffer large enough for an
// additional member.
//
KeyValueLength += sizeof(ULONG);
MembershipArray = MIDL_user_allocate( KeyValueLength );
if (MembershipArray == NULL) {
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
} else {
NtStatus = RtlpNtQueryValueKey(
TempHandle,
NULL,
MembershipArray,
&KeyValueLength,
NULL);
SampDumpRtlpNtQueryValueKey(NULL,
MembershipArray,
&KeyValueLength,
NULL);
if (NT_SUCCESS(NtStatus)) {
//
// See if the account is already a member ...
//
for (i = 0; i<MembershipCount ; i++ ) {
if ( MembershipArray[i] == AliasRid ) {
NtStatus = STATUS_MEMBER_IN_ALIAS;
break;
}
}
if (NT_SUCCESS(NtStatus)) {
//
// Add the new alias's RID to the end
//
MembershipCount += 1;
MembershipArray[MembershipCount-1] = AliasRid;
}
}
}
}
//
// Close the account key handle
//
IgnoreStatus = NtClose( TempHandle );
ASSERT( NT_SUCCESS(IgnoreStatus) );
}
//
// We now have a new membership list desribed by :
// MembershipArray, MembershipCount
//
// Write it out and free it up
//
if (NT_SUCCESS(NtStatus)) {
KeyValueLength = MembershipCount * sizeof(ULONG);
NtStatus = RtlAddActionToRXact(
SampRXactContext,
RtlRXactOperationSetValue,
&AccountKeyName,
MembershipCount,
MembershipArray,
KeyValueLength
);
if (MembershipArray != &AliasRid) {
MIDL_user_free( MembershipArray );
}
}
//
// If this is a new account, we need to increment the rid count
// in the account domain.
//
if (NewAccount) {
//
// Increment the domain rid count
//
NtStatus = RtlAddActionToRXact(
SampRXactContext,
RtlRXactOperationSetValue,
&DomainKeyName,
DomainRidCount + 1,
NULL,
0
);
}
}
SampFreeUnicodeString( &DomainKeyName );
SampFreeUnicodeString( &AccountKeyName );
}
}
return( NtStatus );
}
NTSTATUS
SampRemoveAliasFromAccountMembership(
IN ULONG AliasRid,
IN PSID AccountSid
)
/*++
Routine Description:
This service removes the specified alias from the account's membership
list. It is not assumed that the caller knows anything about
the target account. In particular, the caller doesn't know whether
the account exists or not, nor whether the account is really a member
of the alias.
This routine removes the reference to the alias from the account's
membership list, removes the account key if there are no more aliases,
and removes the domain-sid key if this is the last account in the
domain.
THIS SERVICE MUST BE CALLED WITH THE TRANSACTION DOMAIN SET.
This routine is a No Op for the case where the Alias is in a domain
defined in the DS.
Arguments:
AliasRid - The relative ID of the alias.
AccountSid - The SID of the account.
Return Value:
STATUS_SUCCESS - The information has been updated and added to the
RXACT.
STATUS_NO_SUCH_USER - The account does not exist.
STATUS_MEMBER_NOT_IN_ALIAS - The account is not a member of the
specified alias.
Other status values that may be returned are those returned
by:
NtOpenKey()
NtQueryValueKey()
RtlAddActionToRXact()
--*/
{
NTSTATUS NtStatus, IgnoreStatus;
UNICODE_STRING DomainKeyName;
UNICODE_STRING AccountKeyName;
HANDLE TempHandle;
ULONG MembershipCount, KeyValueLength, i;
PULONG MembershipArray;
OBJECT_ATTRIBUTES ObjectAttributes;
SAMTRACE("SampRemoveAliasFromAccountMembership");
//
// We have 4 cases
// 1 Account and Alias both in DS - No Op because no reverse membership
// list is being maintained
//
// 2 Account in Registry, Alias in DS. This case should never occur as
// built in domain security principals should not be members of aliases
// in DS domain
//
// 3. Account in DS, Alias in Registry - No reverse membership
//
// 4. Account and ALias in Registry - DO what NT4 did
//
if (!IsDsObject(SampDefinedDomains[SampTransactionDomainIndex].Context))
{
//
// Get the account membership
//
NtStatus = SampBuildAliasMembersKeyName(
AccountSid,
&DomainKeyName,
&AccountKeyName
);
if (NT_SUCCESS(NtStatus)) {
InitializeObjectAttributes(
&ObjectAttributes,
&AccountKeyName,
OBJ_CASE_INSENSITIVE,
SampKey,
NULL
);
SampDumpNtOpenKey((KEY_READ), &ObjectAttributes, 0);
NtStatus = RtlpNtOpenKey(
&TempHandle,
(KEY_READ),
&ObjectAttributes,
0
);
if (NtStatus == STATUS_OBJECT_NAME_NOT_FOUND ||
NtStatus == STATUS_OBJECT_PATH_NOT_FOUND) {
NtStatus = STATUS_MEMBER_NOT_IN_ALIAS;
}
if (NT_SUCCESS(NtStatus)) {
//
// Retrieve the length of the membership buffer
//
KeyValueLength = 0;
NtStatus = RtlpNtQueryValueKey(
TempHandle,
&MembershipCount,
NULL,
&KeyValueLength,
NULL);
SampDumpRtlpNtQueryValueKey(&MembershipCount,
NULL,
&KeyValueLength,
NULL);
if (NT_SUCCESS(NtStatus)) {
ASSERT(MembershipCount == 0);
NtStatus = STATUS_MEMBER_NOT_IN_ALIAS;
}
if (NtStatus == STATUS_BUFFER_OVERFLOW) {
ASSERT(MembershipCount != 0);
ASSERT(KeyValueLength == (MembershipCount * sizeof(ULONG)));
MembershipArray = MIDL_user_allocate( KeyValueLength );
if (MembershipArray == NULL) {
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
} else {
NtStatus = RtlpNtQueryValueKey(
TempHandle,
NULL,
MembershipArray,
&KeyValueLength,
NULL);
SampDumpRtlpNtQueryValueKey(NULL,
MembershipArray,
&KeyValueLength,
NULL);
if (NT_SUCCESS(NtStatus)) {
//
// See if the account is a member ...
//
NtStatus = STATUS_MEMBER_NOT_IN_ALIAS;
for (i = 0; i<MembershipCount ; i++ ) {
if ( MembershipArray[i] == AliasRid ) {
NtStatus = STATUS_SUCCESS;
break;
}
}
if (NT_SUCCESS(NtStatus)) {
//
// Replace the removed alias information
// with the last entry's information.
// Then add it to the RXACT transaction
// to be written out.
//
MembershipCount -= 1;
KeyValueLength -= sizeof(ULONG);
if (MembershipCount > 0) {
MembershipArray[i] = MembershipArray[MembershipCount];
ASSERT(KeyValueLength == (MembershipCount * sizeof(ULONG)));
NtStatus = RtlAddActionToRXact(
SampRXactContext,
RtlRXactOperationSetValue,
&AccountKeyName,
MembershipCount,
MembershipArray,
KeyValueLength
);
} else {
//
// This is the last alias membership for
// this account. Delete the keys.
//
NtStatus = SampDeleteAliasMembershipKeysForAccount(
AccountSid);
}
}
}
MIDL_user_free( MembershipArray );
}
}
IgnoreStatus = NtClose( TempHandle );
ASSERT( NT_SUCCESS(IgnoreStatus) );
}
SampFreeUnicodeString( &DomainKeyName );
SampFreeUnicodeString( &AccountKeyName );
}
}
return( NtStatus );
}
NTSTATUS
SampRemoveAliasFromAllAccounts(
IN PSAMP_OBJECT AliasContext
)
/*++
Routine Description:
This service removes the specified alias from all account memberships
THIS SERVICE MUST BE CALLED WITH THE TRANSACTION DOMAIN SET.
This service leaves the alias membership list intact. It is assumed
that the caller will delete the alias member list as part of the
current transaction.
This routine is a No OP for the DS case
Arguments:
AliasRid - The relative ID of the alias.
Return Value:
STATUS_SUCCESS - The information has been updated and added to the
RXACT.
STATUS_NO_SUCH_ALIAS - The alias does not exist.
Other status values that may be returned are those returned
by:
NtOpenKey()
NtQueryValueKey()
RtlAddActionToRXact()
--*/
{
NTSTATUS NtStatus;
ULONG MemberCount, i;
PSID *MemberArray;
SAMTRACE("SampRemoveAliasFromAllAccounts");
if (!IsDsObject(AliasContext))
{
//
// Get the list of members in this alias
//
MemberArray = NULL;
NtStatus = SampRetrieveAliasMembers(
AliasContext,
&MemberCount,
&MemberArray);
if (NT_SUCCESS(NtStatus)) {
ASSERT((MemberCount != 0) == (MemberArray != NULL));
//
// Remove this alias from each of our members in turn
//
for (i = 0; i < MemberCount ; i++ ) {
ULONG AliasRid = AliasContext->TypeBody.Alias.Rid;
NtStatus = SampRemoveAliasFromAccountMembership(AliasRid, MemberArray[i]);
if (!NT_SUCCESS(NtStatus)) {
break;
}
}
if (MemberArray != NULL) {
MIDL_user_free( MemberArray );
}
}
}
return(NtStatus);
}
NTSTATUS
SampRetrieveAliasMembers(
IN PSAMP_OBJECT AliasContext,
OUT PULONG MemberCount,
OUT PSID **Members OPTIONAL
)
/*++
Routine Description:
This service retrieves the number of members in a alias. If desired,
it will also retrieve an array of SIDs of the members of the alias.
THIS SERVICE MUST BE CALLED WITH THE TRANSACTION DOMAIN SET.
Arguments:
Context - Points to the account context whose alias members are to
to be retrieved.
MemberCount - Receives the number of members currently in the alias.
Members - (Otional) Receives a pointer to a buffer containing an array
of member PSIDs. If this value is NULL, then this information
is not returned. The returned buffer is allocated using
MIDL_user_allocate() and must be freed using MIDL_user_free() when
no longer needed.
Return Value:
STATUS_SUCCESS - The information has been retrieved.
STATUS_INSUFFICIENT_RESOURCES - Memory could not be allocated for the
string to be returned in.
Other status values that may be returned are those returned
by:
NtOpenKey()
NtQueryValueKey()
If this routine returns failure, *MemberCount will be zero and
*Members will be NULL.
--*/
{
NTSTATUS NtStatus;
PSID MemberArray;
ULONG MemberArraySize;
ULONG i;
SAMTRACE("SampRetieveAliasMembers");
if (IsDsObject(AliasContext))
{
//
// DS case, this routine in DS layer does all the
// work
//
NtStatus = SampDsGetAliasMembershipList(
AliasContext->ObjectNameInDs,
MemberCount,
Members
);
}
else
{
//
// Registry based case
//
//
NtStatus = SampGetSidArrayAttribute(
AliasContext,
SAMP_ALIAS_MEMBERS,
FALSE, // Reference directly
&MemberArray,
&MemberArraySize,
MemberCount
);
if (NT_SUCCESS(NtStatus)) {
if (ARGUMENT_PRESENT(Members)) {
//
// Allocate memory for the sid array and sid data
//
ULONG SidArraySize = *MemberCount * sizeof(PSID);
ULONG SidDataSize = MemberArraySize;
if ( *MemberCount == 0 ) {
//
// Nothing to copy, just return success.
//
*Members = NULL;
return( NtStatus );
}
(*Members) = (PSID *)MIDL_user_allocate(SidArraySize + SidDataSize);
if ((*Members) == NULL) {
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
} else {
//
// Copy the sid data into the last part of the block
//
PSID SidData = (PSID)(&((*Members)[*MemberCount]));
RtlCopyMemory(SidData, MemberArray, MemberArraySize);
//
// Fill in the sid pointer array
//
for (i = 0; i < *MemberCount ; i++) {
(*Members)[i] = SidData;
((PCHAR)SidData) += RtlLengthSid(SidData);
}
ASSERT(SidData == ((PCHAR)(*Members)) + SidArraySize + SidDataSize);
}
}
}
}
return( NtStatus );
}
NTSTATUS
SampDeleteAliasKeys(
IN PSAMP_OBJECT Context
)
/*++
Routine Description:
This service deletes all registry keys related to a alias object.
Arguments:
Context - Points to the alias context whose registry keys are
being deleted.
Return Value:
STATUS_SUCCESS - The information has been retrieved.
Other status values that may be returned by:
RtlAddActionToRXact()
--*/
{
NTSTATUS NtStatus;
ULONG Rid;
UNICODE_STRING AccountName, KeyName;
SAMTRACE("SampDeleteAliasKeys");
Rid = Context->TypeBody.Alias.Rid;
//
// Aliases are arranged as follows:
//
// +-- Aliases [Count]
// ---+--
// +-- Names
// | --+--
// | +-- (AliasName) [AliasRid,]
// |
// +-- (AliasRid) [Revision,SecurityDescriptor]
// ---+-----
// +-- V1_Fixed [,SAM_V1_FIXED_LENGTH_ALIAS]
// +-- Name [,Name]
// +-- AdminComment [,unicode string]
// +-- Members [Count,(Member0Sid, (...), MemberX-1Sid)]
//
// This all needs to be deleted from the bottom up.
//
//
// Decrement the alias count
//
NtStatus = SampAdjustAccountCount(SampAliasObjectType, FALSE );
//
// Delete the registry key that has the alias's name to RID mapping.
//
if (NT_SUCCESS(NtStatus)) {
//
// Get the name
//
NtStatus = SampGetUnicodeStringAttribute(
Context,
SAMP_ALIAS_NAME,
TRUE, // Make copy
&AccountName
);
if (NT_SUCCESS(NtStatus)) {
NtStatus = SampBuildAccountKeyName(
SampAliasObjectType,
&KeyName,
&AccountName
);
SampFreeUnicodeString( &AccountName );
if (NT_SUCCESS(NtStatus)) {
NtStatus = RtlAddActionToRXact(
SampRXactContext,
RtlRXactOperationDelete,
&KeyName,
0,
NULL,
0
);
SampFreeUnicodeString( &KeyName );
}
}
}
//
// Delete the attribute keys
//
if (NT_SUCCESS(NtStatus)) {
NtStatus = SampDeleteAttributeKeys(
Context
);
}
//
// Delete the RID key
//
if (NT_SUCCESS(NtStatus)) {
NtStatus = SampBuildAccountSubKeyName(
SampAliasObjectType,
&KeyName,
Rid,
NULL
);
if (NT_SUCCESS(NtStatus)) {
NtStatus = RtlAddActionToRXact(
SampRXactContext,
RtlRXactOperationDelete,
&KeyName,
0,
NULL,
0
);
SampFreeUnicodeString( &KeyName );
}
}
return( NtStatus );
}
NTSTATUS
SampDeleteAliasMembershipKeysForAccount(
IN PSID AccountSid
)
/*++
Routine Description:
This service deletes the alias membership keys for the specified account.
This account rid key is deleted. If this was the last account-rid for
the domain then the domain keys is deleted also.
THIS SERVICE MUST BE CALLED WITH THE TRANSACTION DOMAIN SET.
It is assumed we are in the middle of a registry transaction.
Arguments:
AccountSid - The SID of the account.
Return Value:
STATUS_SUCCESS - The transactions have been added.
Other status values that may be returned are those returned
by:
NtOpenKey()
NtQueryValueKey()
RtlAddActionToRXact()
--*/
{
NTSTATUS NtStatus, IgnoreStatus;
UNICODE_STRING DomainKeyName;
UNICODE_STRING AccountKeyName;
HANDLE TempHandle;
ULONG MembershipCount;
OBJECT_ATTRIBUTES ObjectAttributes;
SAMTRACE("SampDeleteAliasMembershipKeysForAccount");
//
// Get the account membership key names
//
NtStatus = SampBuildAliasMembersKeyName(
AccountSid,
&DomainKeyName,
&AccountKeyName
);
if (NT_SUCCESS(NtStatus)) {
//
// Delete the account rid key
//
NtStatus = RtlAddActionToRXact(
SampRXactContext,
RtlRXactOperationDelete,
&AccountKeyName,
0,
NULL,
0
);
//
// Adjust the rid count for the domain
//
if (NT_SUCCESS(NtStatus)) {
InitializeObjectAttributes(
&ObjectAttributes,
&DomainKeyName,
OBJ_CASE_INSENSITIVE,
SampKey,
NULL
);
SampDumpNtOpenKey((KEY_READ), &ObjectAttributes, 0);
NtStatus = RtlpNtOpenKey(
&TempHandle,
(KEY_READ),
&ObjectAttributes,
0
);
ASSERT(NT_SUCCESS(NtStatus)); // We just opened a sub-key successfully !
if (NT_SUCCESS(NtStatus)) {
NtStatus = RtlpNtQueryValueKey(
TempHandle,
&MembershipCount,
NULL,
NULL,
NULL);
SampDumpRtlpNtQueryValueKey(&MembershipCount,
NULL,
NULL,
NULL);
if (NT_SUCCESS(NtStatus)) {
//
// Decrement the rid count, write out or delete key if 0
//
MembershipCount -= 1;
if (MembershipCount > 0) {
//
// Decrement the domain rid count
//
NtStatus = RtlAddActionToRXact(
SampRXactContext,
RtlRXactOperationSetValue,
&DomainKeyName,
MembershipCount,
NULL,
0
);
} else {
//
// Delete the domain key
//
NtStatus = RtlAddActionToRXact(
SampRXactContext,
RtlRXactOperationDelete,
&DomainKeyName,
0,
NULL,
0
);
//
// Adjust the count of domain keys
//
if (NT_SUCCESS(NtStatus)) {
NtStatus = SampAdjustAliasDomainsCount(FALSE);
}
}
}
//
// Close the domain key handle
//
IgnoreStatus = NtClose( TempHandle );
ASSERT( NT_SUCCESS(IgnoreStatus) );
}
}
SampFreeUnicodeString( &DomainKeyName );
SampFreeUnicodeString( &AccountKeyName );
}
return( NtStatus );
}
NTSTATUS
SampAdjustAliasDomainsCount(
IN BOOLEAN Increment
)
/*++
Routine Description:
This service increments or decrements the number of domains that have
at least one account that is a member of one of our aliases.
This value is contained in the type of \(domain)\ALIASES\MEMBERS
Note: THIS ROUTINE REFERENCES THE CURRENT TRANSACTION DOMAIN
(ESTABLISHED USING SampSetTransactioDomain()). THIS
SERVICE MAY ONLY BE CALLED AFTER SampSetTransactionDomain()
AND BEFORE SampReleaseWriteLock().
Arguments:
Increment - TRUE to increment, FALSE to decrement
Return Value:
STATUS_SUCCESS - The value has been adjusted and the new value added
to the current RXACT transaction.
STATUS_INSUFFICIENT_RESOURCES - Not enough memory could be allocated
to perform the requested operation.
Other values are unexpected errors. These may originate from
internal calls to:
NtOpenKey()
NtQueryInformationKey()
RtlAddActionToRXact()
--*/
{
//
// Don't maintain a count of domains for now
//
SAMTRACE("SampAdjustAliasDomainsCount");
return(STATUS_SUCCESS);
DBG_UNREFERENCED_PARAMETER(Increment);
}
NTSTATUS
SampValidateNewAliasMember(
IN PSID MemberId
)
/*++
Routine Description:
This service checks the passed Sid is acceptable as a potential new
member of one of the aliases in the current domain.
Note: THIS ROUTINE REFERENCES THE CURRENT TRANSACTION DOMAIN
(ESTABLISHED USING SampSetTransactioDomain()). THIS
SERVICE MAY ONLY BE CALLED AFTER SampSetTransactionDomain()
AND BEFORE SampReleaseWriteLock().
Arguments:
MemberId - the full Sid of the member to validate
Return Value:
STATUS_SUCCESS - MemberId is a valid potential alias member
STATUS_INVALID_MEMBER - MemberId has the wrong account type.
STATUS_NO_SUCH_MEMBER - MemberId is not a valid account.
STATUS_INVALID_SID - MemberId is not a valid sid.
--*/
{
NTSTATUS NtStatus;
PSID MemberDomainSid = NULL, CurrentDomainSid = NULL;
ULONG MemberRid;
SAMP_OBJECT_TYPE MemberType;
SAMTRACE("SampValidateNewAliasMember");
//
// Check the new member sid for structural soundness
//
if ((MemberId == NULL) || !RtlValidSid(MemberId)) {
return(STATUS_INVALID_SID);
}
//
// Get the current domain sid
//
ASSERT(SampTransactionWithinDomain);
CurrentDomainSid = SampDefinedDomains[SampTransactionDomainIndex].Sid;
//
// Break up the new member into domain and rid
//
NtStatus = SampSplitSid(MemberId, &MemberDomainSid, &MemberRid);
if (!NT_SUCCESS(NtStatus)) {
return(NtStatus);
}
//
// If the member isn't from this domain, then they're OK.
//
if (!RtlEqualSid(CurrentDomainSid, MemberDomainSid)) {
NtStatus = STATUS_SUCCESS;
} else {
//
// The member is in our domain - check that the type of
// account is acceptable.
//
NtStatus = SampLookupAccountName(
MemberRid,
NULL,
&MemberType
);
if (NT_SUCCESS(NtStatus)) {
switch (MemberType) {
case SampUserObjectType:
case SampGroupObjectType:
NtStatus = STATUS_SUCCESS;
break;
case SampUnknownObjectType:
NtStatus = STATUS_NO_SUCH_MEMBER;
break;
default:
NtStatus = STATUS_INVALID_MEMBER;
break;
}
}
}
MIDL_user_free(MemberDomainSid);
return(NtStatus);
}
NTSTATUS
SampChangeAliasAccountName(
IN PSAMP_OBJECT Context,
IN PUNICODE_STRING NewAccountName,
OUT PUNICODE_STRING OldAccountName
)
/*++
Routine Description:
This routine changes the account name of an alias account.
THIS SERVICE MUST BE CALLED WITH THE TRANSACTION DOMAIN SET.
Arguments:
Context - Points to the account context whose name is to be changed.
NewAccountName - New name to give this account
OldAccountName - old name is returned here. The buffer should be freed
by calling MIDL_user_free.
Return Value:
STATUS_SUCCESS - The information has been retrieved.
Other status values that may be returned by:
SampGetUnicodeStringAttribute()
SampSetUnicodeStringAttribute()
SampValidateAccountNameChange()
RtlAddActionToRXact()
--*/
{
NTSTATUS NtStatus;
UNICODE_STRING KeyName;
SAMTRACE("SampChangeAliasAccountName");
//
// Get the current name so we can delete the old Name->Rid
// mapping key.
//
NtStatus = SampGetUnicodeStringAttribute(
Context,
SAMP_ALIAS_NAME,
TRUE, // Make copy
OldAccountName
);
//
// Make sure the name is valid and not already in use
//
if (NT_SUCCESS(NtStatus)) {
NtStatus = SampValidateAccountNameChange(
NewAccountName,
OldAccountName
);
if (!(IsDsObject(Context))) {
//
// For registry based Aliases re-create the
// Name to Rid mapping keys
//
/////////////////////////////////////////////////////////////
// There are two copies of the name of each account. //
// one is under the DOMAIN\(domainName)\ALIAS\NAMES key, //
// one is the value of the //
// DOMAIN\(DomainName)\ALIAS\(rid)\NAME key //
/////////////////////////////////////////////////////////////
//
// Delete the old name key
//
if (NT_SUCCESS(NtStatus)) {
NtStatus = SampBuildAccountKeyName(
SampAliasObjectType,
&KeyName,
OldAccountName
);
if (NT_SUCCESS(NtStatus)) {
NtStatus = RtlAddActionToRXact(
SampRXactContext,
RtlRXactOperationDelete,
&KeyName,
0,
NULL,
0
);
SampFreeUnicodeString( &KeyName );
}
}
//
//
// Create the new Name->Rid mapping key
//
if (NT_SUCCESS(NtStatus)) {
NtStatus = SampBuildAccountKeyName(
SampAliasObjectType,
&KeyName,
NewAccountName
);
if (NT_SUCCESS(NtStatus)) {
ULONG AliasRid = Context->TypeBody.Alias.Rid;
NtStatus = RtlAddActionToRXact(
SampRXactContext,
RtlRXactOperationSetValue,
&KeyName,
AliasRid,
(PVOID)NULL,
0
);
SampFreeUnicodeString( &KeyName );
}
}
}
//
// replace the account's name
//
if (NT_SUCCESS(NtStatus)) {
NtStatus = SampSetUnicodeStringAttribute(
Context,
SAMP_ALIAS_NAME,
NewAccountName
);
}
//
// Free up the old account name if we failed
//
if (!NT_SUCCESS(NtStatus)) {
SampFreeUnicodeString(OldAccountName);
}
}
return(NtStatus);
}