3159 lines
78 KiB
C
3159 lines
78 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1990 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
group.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This file contains services related to the SAM "group" object.
|
|||
|
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Jim Kelly (JimK) 4-July-1991
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
User Mode - Win32
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
// //
|
|||
|
// Includes //
|
|||
|
// //
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
#include <samsrvp.h>
|
|||
|
#include <msaudite.h>
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
// //
|
|||
|
// private service prototypes //
|
|||
|
// //
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampDeleteGroupKeys(
|
|||
|
IN PSAMP_OBJECT Context
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampChangeGroupAccountName(
|
|||
|
IN PSAMP_OBJECT Context,
|
|||
|
IN PUNICODE_STRING NewAccountName,
|
|||
|
OUT PUNICODE_STRING OldAccountName
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampReplaceGroupMembers(
|
|||
|
IN PSAMP_OBJECT GroupContext,
|
|||
|
IN ULONG MemberCount,
|
|||
|
IN PULONG Members
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampAddAccountToGroupMembers(
|
|||
|
IN PSAMP_OBJECT GroupContext,
|
|||
|
IN ULONG UserRid
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampRemoveAccountFromGroupMembers(
|
|||
|
IN PSAMP_OBJECT GroupContext,
|
|||
|
IN ULONG AccountRid
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
// //
|
|||
|
// Exposed RPC'able Services //
|
|||
|
// //
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamrOpenGroup(
|
|||
|
IN SAMPR_HANDLE DomainHandle,
|
|||
|
IN ACCESS_MASK DesiredAccess,
|
|||
|
IN ULONG GroupId,
|
|||
|
OUT SAMPR_HANDLE *GroupHandle
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API opens an existing group in the account database. The group
|
|||
|
is specified by a ID value that is relative to the SID of the
|
|||
|
domain. The operations that will be performed on the group must be
|
|||
|
declared at this time.
|
|||
|
|
|||
|
This call returns a handle to the newly opened group that may be
|
|||
|
used for successive operations on the group. 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 group. These access types are reconciled
|
|||
|
with the Discretionary Access Control list of the group to
|
|||
|
determine whether the accesses will be granted or denied.
|
|||
|
|
|||
|
GroupId - Specifies the relative ID value of the group to be
|
|||
|
opened.
|
|||
|
|
|||
|
GroupHandle - Receives a handle referencing the newly opened
|
|||
|
group. This handle will be required in successive calls to
|
|||
|
operate on the group.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The group was successfully opened.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have the appropriate
|
|||
|
access to complete the operation.
|
|||
|
|
|||
|
STATUS_NO_SUCH_GROUP - The specified group does not exist.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The domain handle passed is invalid.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
NtStatus = SampOpenAccount(
|
|||
|
SampGroupObjectType,
|
|||
|
DomainHandle,
|
|||
|
DesiredAccess,
|
|||
|
GroupId,
|
|||
|
FALSE,
|
|||
|
GroupHandle
|
|||
|
);
|
|||
|
|
|||
|
return(NtStatus);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamrQueryInformationGroup(
|
|||
|
IN SAMPR_HANDLE GroupHandle,
|
|||
|
IN GROUP_INFORMATION_CLASS GroupInformationClass,
|
|||
|
OUT PSAMPR_GROUP_INFO_BUFFER *Buffer
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API retrieves information on the group specified.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
GroupHandle - The handle of an opened group to operate on.
|
|||
|
|
|||
|
GroupInformationClass - Class of information to retrieve. The
|
|||
|
accesses required for each class is shown below:
|
|||
|
|
|||
|
Info Level Required Access Type
|
|||
|
----------------------- ----------------------
|
|||
|
|
|||
|
GroupGeneralInformation GROUP_READ_INFORMATION
|
|||
|
GroupNameInformation GROUP_READ_INFORMATION
|
|||
|
GroupAttributeInformation GROUP_READ_INFORMATION
|
|||
|
GroupAdminInformation GROUP_READ_INFORMATION
|
|||
|
|
|||
|
Buffer - Receives a pointer to a buffer containing the requested
|
|||
|
information. When this information is no longer needed, 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;
|
|||
|
SAMP_V1_0A_FIXED_LENGTH_GROUP V1Fixed;
|
|||
|
|
|||
|
//
|
|||
|
// 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;
|
|||
|
|
|||
|
#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 (GroupInformationClass) {
|
|||
|
|
|||
|
case GroupGeneralInformation:
|
|||
|
case GroupNameInformation:
|
|||
|
case GroupAttributeInformation:
|
|||
|
case GroupAdminCommentInformation:
|
|||
|
|
|||
|
DesiredAccess = GROUP_READ_INFORMATION;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
(*Buffer) = NULL;
|
|||
|
return(STATUS_INVALID_INFO_CLASS);
|
|||
|
} // end_switch
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Allocate the info structure
|
|||
|
//
|
|||
|
|
|||
|
(*Buffer) = MIDL_user_allocate( sizeof(SAMPR_GROUP_INFO_BUFFER) );
|
|||
|
if ((*Buffer) == NULL) {
|
|||
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|||
|
}
|
|||
|
RegisterBuffer(*Buffer);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
SampAcquireReadLock();
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Validate type of, and access to object.
|
|||
|
//
|
|||
|
|
|||
|
AccountContext = (PSAMP_OBJECT)GroupHandle;
|
|||
|
NtStatus = SampLookupContext(
|
|||
|
AccountContext,
|
|||
|
DesiredAccess,
|
|||
|
SampGroupObjectType, // ExpectedType
|
|||
|
&FoundType
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// If the information level requires, retrieve the V1_FIXED record
|
|||
|
// from the registry.
|
|||
|
//
|
|||
|
|
|||
|
switch (GroupInformationClass) {
|
|||
|
|
|||
|
case GroupGeneralInformation:
|
|||
|
case GroupAttributeInformation:
|
|||
|
|
|||
|
NtStatus = SampRetrieveGroupV1Fixed(
|
|||
|
AccountContext,
|
|||
|
&V1Fixed
|
|||
|
);
|
|||
|
break; //out of switch
|
|||
|
|
|||
|
default:
|
|||
|
NtStatus = STATUS_SUCCESS;
|
|||
|
|
|||
|
} // end_switch
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
//
|
|||
|
// case on the type information requested
|
|||
|
//
|
|||
|
|
|||
|
switch (GroupInformationClass) {
|
|||
|
|
|||
|
case GroupGeneralInformation:
|
|||
|
|
|||
|
|
|||
|
(*Buffer)->General.Attributes = V1Fixed.Attributes;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Get the member count
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampRetrieveGroupMembers(
|
|||
|
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_GROUP_NAME,
|
|||
|
TRUE, // Make copy
|
|||
|
(PUNICODE_STRING)&((*Buffer)->General.Name)
|
|||
|
);
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
RegisterBuffer((*Buffer)->General.Name.Buffer);
|
|||
|
|
|||
|
NtStatus = SampGetUnicodeStringAttribute(
|
|||
|
AccountContext,
|
|||
|
SAMP_GROUP_ADMIN_COMMENT,
|
|||
|
TRUE, // Make copy
|
|||
|
(PUNICODE_STRING)&((*Buffer)->General.AdminComment)
|
|||
|
);
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
RegisterBuffer((*Buffer)->General.AdminComment.Buffer);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
|
|||
|
case GroupNameInformation:
|
|||
|
|
|||
|
//
|
|||
|
// Get copies of the strings we must retrieve from
|
|||
|
// the registry.
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampGetUnicodeStringAttribute(
|
|||
|
AccountContext,
|
|||
|
SAMP_GROUP_NAME,
|
|||
|
TRUE, // Make copy
|
|||
|
(PUNICODE_STRING)&((*Buffer)->Name.Name)
|
|||
|
);
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
RegisterBuffer((*Buffer)->Name.Name.Buffer);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
|
|||
|
case GroupAdminCommentInformation:
|
|||
|
|
|||
|
//
|
|||
|
// Get copies of the strings we must retrieve from
|
|||
|
// the registry.
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampGetUnicodeStringAttribute(
|
|||
|
AccountContext,
|
|||
|
SAMP_GROUP_ADMIN_COMMENT,
|
|||
|
TRUE, // Make copy
|
|||
|
(PUNICODE_STRING)&((*Buffer)->AdminComment.AdminComment)
|
|||
|
);
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
RegisterBuffer((*Buffer)->AdminComment.AdminComment.Buffer);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
|
|||
|
case GroupAttributeInformation:
|
|||
|
|
|||
|
|
|||
|
(*Buffer)->Attribute.Attributes = V1Fixed.Attributes;
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
} // end_switch
|
|||
|
|
|||
|
|
|||
|
} // end_if
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// De-reference the object, discarding 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
|
|||
|
SamrSetInformationGroup(
|
|||
|
IN SAMPR_HANDLE GroupHandle,
|
|||
|
IN GROUP_INFORMATION_CLASS GroupInformationClass,
|
|||
|
IN PSAMPR_GROUP_INFO_BUFFER Buffer
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API allows the caller to modify group information.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
GroupHandle - The handle of an opened group to operate on.
|
|||
|
|
|||
|
GroupInformationClass - Class of information to retrieve. The
|
|||
|
accesses required for each class is shown below:
|
|||
|
|
|||
|
Info Level Required Access Type
|
|||
|
------------------------ -------------------------
|
|||
|
|
|||
|
GroupGeneralInformation (can't write)
|
|||
|
|
|||
|
GroupNameInformation GROUP_WRITE_ACCOUNT
|
|||
|
GroupAttributeInformation GROUP_WRITE_ACCOUNT
|
|||
|
GroupAdminInformation GROUP_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_GROUP - The group specified is unknown.
|
|||
|
|
|||
|
STATUS_SPECIAL_GROUP - The group specified is a special group 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,
|
|||
|
TmpStatus,
|
|||
|
IgnoreStatus;
|
|||
|
|
|||
|
PSAMP_OBJECT AccountContext;
|
|||
|
|
|||
|
SAMP_OBJECT_TYPE FoundType;
|
|||
|
|
|||
|
PSAMP_DEFINED_DOMAINS Domain;
|
|||
|
|
|||
|
ACCESS_MASK DesiredAccess;
|
|||
|
|
|||
|
SAMP_V1_0A_FIXED_LENGTH_GROUP V1Fixed;
|
|||
|
|
|||
|
UNICODE_STRING OldAccountName,
|
|||
|
NewAdminComment,
|
|||
|
NewAccountName,
|
|||
|
NewFullName;
|
|||
|
|
|||
|
ULONG ObjectRid,
|
|||
|
OldGroupAttributes,
|
|||
|
DomainIndex;
|
|||
|
|
|||
|
BOOLEAN Modified = FALSE,
|
|||
|
MustUpdateAccountDisplay = FALSE;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Make sure we understand what RPC is doing for (to) us.
|
|||
|
//
|
|||
|
|
|||
|
if (Buffer == NULL) {
|
|||
|
return(STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Reset any strings that we'll be freeing in clean-up code
|
|||
|
//
|
|||
|
|
|||
|
RtlInitUnicodeString(&OldAccountName, NULL);
|
|||
|
RtlInitUnicodeString(&NewAccountName, NULL);
|
|||
|
RtlInitUnicodeString(&NewFullName, NULL);
|
|||
|
RtlInitUnicodeString(&NewAdminComment, NULL);
|
|||
|
|
|||
|
//
|
|||
|
// Set the desired access based upon the Info class
|
|||
|
//
|
|||
|
|
|||
|
switch (GroupInformationClass) {
|
|||
|
|
|||
|
case GroupNameInformation:
|
|||
|
case GroupAttributeInformation:
|
|||
|
case GroupAdminCommentInformation:
|
|||
|
|
|||
|
DesiredAccess = GROUP_WRITE_ACCOUNT;
|
|||
|
break;
|
|||
|
|
|||
|
|
|||
|
case GroupGeneralInformation:
|
|||
|
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)GroupHandle;
|
|||
|
ObjectRid = AccountContext->TypeBody.Group.Rid;
|
|||
|
NtStatus = SampLookupContext(
|
|||
|
AccountContext,
|
|||
|
DesiredAccess,
|
|||
|
SampGroupObjectType, // 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 ];
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// If the information level requires, retrieve the V1_FIXED record
|
|||
|
// from the registry. This includes anything that will cause
|
|||
|
// us to update the display cache.
|
|||
|
//
|
|||
|
|
|||
|
switch (GroupInformationClass) {
|
|||
|
|
|||
|
case GroupAdminCommentInformation:
|
|||
|
case GroupNameInformation:
|
|||
|
case GroupAttributeInformation:
|
|||
|
|
|||
|
NtStatus = SampRetrieveGroupV1Fixed(
|
|||
|
AccountContext,
|
|||
|
&V1Fixed
|
|||
|
);
|
|||
|
|
|||
|
MustUpdateAccountDisplay = TRUE;
|
|||
|
OldGroupAttributes = V1Fixed.Attributes;
|
|||
|
break; //out of switch
|
|||
|
|
|||
|
|
|||
|
default:
|
|||
|
NtStatus = STATUS_SUCCESS;
|
|||
|
|
|||
|
} // end_switch
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
//
|
|||
|
// case on the type information requested
|
|||
|
//
|
|||
|
|
|||
|
switch (GroupInformationClass) {
|
|||
|
|
|||
|
case GroupNameInformation:
|
|||
|
|
|||
|
NtStatus = SampChangeGroupAccountName(
|
|||
|
AccountContext,
|
|||
|
(PUNICODE_STRING)&(Buffer->Name.Name),
|
|||
|
&OldAccountName
|
|||
|
);
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
OldAccountName.Buffer = NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Don't free OldAccountName yet; we'll need it at the
|
|||
|
// very end.
|
|||
|
//
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
|
|||
|
case GroupAdminCommentInformation:
|
|||
|
|
|||
|
//
|
|||
|
// build the key name
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampSetUnicodeStringAttribute(
|
|||
|
AccountContext,
|
|||
|
SAMP_GROUP_ADMIN_COMMENT,
|
|||
|
(PUNICODE_STRING)&(Buffer->AdminComment.AdminComment)
|
|||
|
);
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
|
|||
|
case GroupAttributeInformation:
|
|||
|
|
|||
|
MustUpdateAccountDisplay = TRUE;
|
|||
|
|
|||
|
V1Fixed.Attributes = Buffer->Attribute.Attributes;
|
|||
|
|
|||
|
NtStatus = SampReplaceGroupV1Fixed(
|
|||
|
AccountContext, // ParentKey
|
|||
|
&V1Fixed
|
|||
|
);
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
|
|||
|
} // end_switch
|
|||
|
|
|||
|
|
|||
|
} // end_if
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Go fetch any data we'll need to update the display cache
|
|||
|
// Do this before we dereference the context
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
if ( MustUpdateAccountDisplay ) {
|
|||
|
|
|||
|
NtStatus = SampGetUnicodeStringAttribute(
|
|||
|
AccountContext,
|
|||
|
SAMP_GROUP_NAME,
|
|||
|
TRUE, // Make copy
|
|||
|
&NewAccountName
|
|||
|
);
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
NtStatus = SampGetUnicodeStringAttribute(
|
|||
|
AccountContext,
|
|||
|
SAMP_GROUP_ADMIN_COMMENT,
|
|||
|
TRUE, // Make copy
|
|||
|
&NewAdminComment
|
|||
|
);
|
|||
|
//
|
|||
|
// If the account name has changed, then OldAccountName
|
|||
|
// is already filled in. If the account name hasn't changed
|
|||
|
// then the OldAccountName is the same as the new!
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus) && (OldAccountName.Buffer == NULL)) {
|
|||
|
|
|||
|
NtStatus = SampDuplicateUnicodeString(
|
|||
|
&OldAccountName,
|
|||
|
&NewAccountName);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Generate an audit if necessary
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus) &&
|
|||
|
SampDoAccountAuditing(DomainIndex)) {
|
|||
|
|
|||
|
UNICODE_STRING
|
|||
|
AccountName;
|
|||
|
|
|||
|
IgnoreStatus = SampGetUnicodeStringAttribute(
|
|||
|
AccountContext, // Context
|
|||
|
SAMP_GROUP_NAME, // AttributeIndex
|
|||
|
FALSE, // MakeCopy
|
|||
|
&AccountName // UnicodeAttribute
|
|||
|
);
|
|||
|
if (NT_SUCCESS(IgnoreStatus)) {
|
|||
|
LsaIAuditSamEvent(
|
|||
|
STATUS_SUCCESS,
|
|||
|
SE_AUDITID_GLOBAL_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.Group.Rid, // Account Rid
|
|||
|
NULL // Privileges used
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Dereference the account context
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
//
|
|||
|
// 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, update the display cache,
|
|||
|
// and notify netlogon of the changes
|
|||
|
//
|
|||
|
|
|||
|
if ( NT_SUCCESS(NtStatus) ) {
|
|||
|
|
|||
|
NtStatus = SampCommitAndRetainWriteLock();
|
|||
|
|
|||
|
|
|||
|
if ( NT_SUCCESS(NtStatus) ) {
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Update the display information if the cache may be affected
|
|||
|
//
|
|||
|
|
|||
|
if ( MustUpdateAccountDisplay ) {
|
|||
|
|
|||
|
SAMP_ACCOUNT_DISPLAY_INFO OldAccountInfo;
|
|||
|
SAMP_ACCOUNT_DISPLAY_INFO NewAccountInfo;
|
|||
|
|
|||
|
OldAccountInfo.Name = OldAccountName;
|
|||
|
OldAccountInfo.Rid = ObjectRid;
|
|||
|
OldAccountInfo.AccountControl = OldGroupAttributes;
|
|||
|
RtlInitUnicodeString(&OldAccountInfo.Comment, NULL);
|
|||
|
RtlInitUnicodeString(&OldAccountInfo.FullName, NULL); // Not used for groups
|
|||
|
|
|||
|
NewAccountInfo.Name = NewAccountName;
|
|||
|
NewAccountInfo.Rid = ObjectRid;
|
|||
|
NewAccountInfo.AccountControl = V1Fixed.Attributes;
|
|||
|
NewAccountInfo.Comment = NewAdminComment;
|
|||
|
NewAccountInfo.FullName = NewFullName;
|
|||
|
|
|||
|
IgnoreStatus = SampUpdateDisplayInformation(&OldAccountInfo,
|
|||
|
&NewAccountInfo,
|
|||
|
SampGroupObjectType);
|
|||
|
ASSERT(NT_SUCCESS(IgnoreStatus));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
if ( GroupInformationClass == GroupNameInformation ) {
|
|||
|
|
|||
|
SampNotifyNetlogonOfDelta(
|
|||
|
SecurityDbRename,
|
|||
|
SecurityDbObjectSamGroup,
|
|||
|
ObjectRid,
|
|||
|
&OldAccountName,
|
|||
|
(DWORD) FALSE, // Replicate immediately
|
|||
|
NULL // Delta data
|
|||
|
);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
SampNotifyNetlogonOfDelta(
|
|||
|
SecurityDbChange,
|
|||
|
SecurityDbObjectSamGroup,
|
|||
|
ObjectRid,
|
|||
|
(PUNICODE_STRING) NULL,
|
|||
|
(DWORD) FALSE, // Replicate immediately
|
|||
|
NULL // Delta data
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Release the write lock
|
|||
|
//
|
|||
|
|
|||
|
TmpStatus = SampReleaseWriteLock( FALSE );
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
NtStatus = TmpStatus;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Clean up strings
|
|||
|
//
|
|||
|
|
|||
|
SampFreeUnicodeString( &OldAccountName );
|
|||
|
SampFreeUnicodeString( &NewAccountName );
|
|||
|
SampFreeUnicodeString( &NewFullName );
|
|||
|
SampFreeUnicodeString( &NewAdminComment );
|
|||
|
|
|||
|
return(NtStatus);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamrAddMemberToGroup(
|
|||
|
IN SAMPR_HANDLE GroupHandle,
|
|||
|
IN ULONG MemberId,
|
|||
|
IN ULONG Attributes
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API adds a member to a group. Note that this API requires the
|
|||
|
GROUP_ADD_MEMBER access type for the group.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
GroupHandle - The handle of an opened group to operate on.
|
|||
|
|
|||
|
MemberId - Relative ID of the member to add.
|
|||
|
|
|||
|
Attributes - The attributes of the group assigned to the user.
|
|||
|
The attributes assigned here may have any value. However,
|
|||
|
at logon time these attributes are minimized by the
|
|||
|
attributes of the group as a whole.
|
|||
|
|
|||
|
Mandatory - If the Mandatory attribute is assigned to
|
|||
|
the group as a whole, then it will be assigned to
|
|||
|
the group for each member of the group.
|
|||
|
|
|||
|
EnabledByDefault - This attribute may be set to any value
|
|||
|
for each member of the group. It does not matter
|
|||
|
what the attribute value for the group as a whole
|
|||
|
is.
|
|||
|
|
|||
|
Enabled - This attribute may be set to any value for each
|
|||
|
member of the group. It does not matter what the
|
|||
|
attribute value for the group as a whole is.
|
|||
|
|
|||
|
Owner - If the Owner attribute of the group as a
|
|||
|
whole is not set, then the value assigned to
|
|||
|
members is ignored.
|
|||
|
|
|||
|
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_GROUP - The member already belongs to the group.
|
|||
|
|
|||
|
STATUS_INVALID_GROUP_ATTRIBUTES - Indicates the group attribute
|
|||
|
values being assigned to the member are not compatible with
|
|||
|
the attribute values of the group as a whole.
|
|||
|
|
|||
|
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.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
SAMP_V1_0A_FIXED_LENGTH_GROUP GroupV1Fixed;
|
|||
|
NTSTATUS NtStatus, TmpStatus;
|
|||
|
PSAMP_OBJECT AccountContext;
|
|||
|
SAMP_OBJECT_TYPE FoundType;
|
|||
|
ULONG ObjectRid;
|
|||
|
BOOLEAN UserAccountActive;
|
|||
|
UNICODE_STRING GroupName;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Initialize buffers we will cleanup at the end
|
|||
|
//
|
|||
|
|
|||
|
RtlInitUnicodeString(&GroupName, NULL);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Grab the lock
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampAcquireWriteLock();
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
return(NtStatus);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Validate type of, and access to object.
|
|||
|
//
|
|||
|
|
|||
|
AccountContext = (PSAMP_OBJECT)(GroupHandle);
|
|||
|
ObjectRid = AccountContext->TypeBody.Group.Rid;
|
|||
|
NtStatus = SampLookupContext(
|
|||
|
AccountContext,
|
|||
|
GROUP_ADD_MEMBER,
|
|||
|
SampGroupObjectType, // ExpectedType
|
|||
|
&FoundType
|
|||
|
);
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
NtStatus = SampRetrieveGroupV1Fixed(
|
|||
|
AccountContext,
|
|||
|
&GroupV1Fixed
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
//
|
|||
|
// Perform the user object side of things
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampAddGroupToUserMembership(
|
|||
|
ObjectRid,
|
|||
|
Attributes,
|
|||
|
MemberId,
|
|||
|
(GroupV1Fixed.AdminCount == 0) ? NoChange : AddToAdmin,
|
|||
|
(GroupV1Fixed.OperatorCount == 0) ? NoChange : AddToAdmin,
|
|||
|
&UserAccountActive
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Now perform the group side of things
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
//
|
|||
|
// Add the user to the group (should not fail)
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampAddAccountToGroupMembers(
|
|||
|
AccountContext,
|
|||
|
MemberId
|
|||
|
);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
//
|
|||
|
// Get and save the account name for
|
|||
|
// I_NetNotifyLogonOfDelta.
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampGetUnicodeStringAttribute(
|
|||
|
AccountContext,
|
|||
|
SAMP_GROUP_NAME,
|
|||
|
TRUE, // Make copy
|
|||
|
&GroupName
|
|||
|
);
|
|||
|
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
RtlInitUnicodeString(&GroupName, NULL);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Dereference the account context
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
//
|
|||
|
// 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));
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Commit the transaction and notify net logon of the changes
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
NtStatus = SampCommitAndRetainWriteLock();
|
|||
|
|
|||
|
if ( NT_SUCCESS( NtStatus ) ) {
|
|||
|
|
|||
|
SAM_DELTA_DATA DeltaData;
|
|||
|
|
|||
|
//
|
|||
|
// Fill in id of member being added
|
|||
|
//
|
|||
|
|
|||
|
DeltaData.GroupMemberId.MemberRid = MemberId;
|
|||
|
|
|||
|
SampNotifyNetlogonOfDelta(
|
|||
|
SecurityDbChangeMemberAdd,
|
|||
|
SecurityDbObjectSamGroup,
|
|||
|
ObjectRid,
|
|||
|
&GroupName,
|
|||
|
(DWORD) FALSE, // Replicate immediately
|
|||
|
&DeltaData
|
|||
|
);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Free up the group name
|
|||
|
//
|
|||
|
|
|||
|
SampFreeUnicodeString(&GroupName);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
TmpStatus = SampReleaseWriteLock( FALSE );
|
|||
|
ASSERT(NT_SUCCESS(TmpStatus));
|
|||
|
|
|||
|
return(NtStatus);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamrDeleteGroup(
|
|||
|
IN SAMPR_HANDLE *GroupHandle
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API removes a group from the account database. There may be no
|
|||
|
members in the group or the deletion request will be rejected. Note
|
|||
|
that this API requires DELETE access to the specific group being
|
|||
|
deleted.
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
GroupHandle - The handle of an opened group 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. This may be
|
|||
|
because someone has deleted the group while it was open.
|
|||
|
|
|||
|
STATUS_SPECIAL_ACCOUNT - The group specified is a special group 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.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
UNICODE_STRING GroupName;
|
|||
|
NTSTATUS NtStatus, TmpStatus;
|
|||
|
PSAMP_OBJECT AccountContext;
|
|||
|
PSAMP_DEFINED_DOMAINS Domain;
|
|||
|
PSID AccountSid;
|
|||
|
SAMP_OBJECT_TYPE FoundType;
|
|||
|
ULONG MemberCount,
|
|||
|
ObjectRid,
|
|||
|
DomainIndex;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Grab the lock
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampAcquireWriteLock();
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
return(NtStatus);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Validate type of, and access to object.
|
|||
|
//
|
|||
|
|
|||
|
AccountContext = (PSAMP_OBJECT)(*GroupHandle);
|
|||
|
NtStatus = SampLookupContext(
|
|||
|
AccountContext,
|
|||
|
DELETE,
|
|||
|
SampGroupObjectType, // ExpectedType
|
|||
|
&FoundType
|
|||
|
);
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
ObjectRid = AccountContext->TypeBody.Group.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 the caller is trusted.
|
|||
|
//
|
|||
|
|
|||
|
if ( !AccountContext->TrustedClient ) {
|
|||
|
|
|||
|
NtStatus = SampIsAccountBuiltIn( ObjectRid );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
if (NT_SUCCESS( NtStatus) ) {
|
|||
|
|
|||
|
//
|
|||
|
// and it can't have any members
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampRetrieveGroupMembers(
|
|||
|
AccountContext,
|
|||
|
&MemberCount,
|
|||
|
NULL // Only need member count (not list)
|
|||
|
);
|
|||
|
|
|||
|
if (MemberCount != 0) {
|
|||
|
NtStatus = STATUS_MEMBER_IN_GROUP;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
//
|
|||
|
// Remove this account from all aliases
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
NtStatus = SampCreateAccountSid(AccountContext, &AccountSid);
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
NtStatus = SampRemoveAccountFromAllAliases(
|
|||
|
AccountSid,
|
|||
|
FALSE,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Looks promising.
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
//
|
|||
|
// First get and save the account name for
|
|||
|
// I_NetNotifyLogonOfDelta.
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampGetUnicodeStringAttribute(
|
|||
|
AccountContext,
|
|||
|
SAMP_GROUP_NAME,
|
|||
|
TRUE, // Make copy
|
|||
|
&GroupName
|
|||
|
);
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
//
|
|||
|
// This must be done before we invalidate contexts, because our
|
|||
|
// own handle to the group gets closed as well.
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampDeleteGroupKeys( AccountContext );
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
//
|
|||
|
// We must invalidate any open contexts to this group.
|
|||
|
// This will close all handles to the group's keys.
|
|||
|
// THIS IS AN IRREVERSIBLE PROCESS.
|
|||
|
//
|
|||
|
|
|||
|
SampInvalidateGroupContexts( ObjectRid );
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Commit the whole mess
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampCommitAndRetainWriteLock();
|
|||
|
|
|||
|
if ( NT_SUCCESS( NtStatus ) ) {
|
|||
|
|
|||
|
SAMP_ACCOUNT_DISPLAY_INFO AccountInfo;
|
|||
|
|
|||
|
//
|
|||
|
// Update the Cached Alias Information
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampAlRemoveAccountFromAllAliases(
|
|||
|
AccountSid,
|
|||
|
FALSE,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
MIDL_user_free(AccountSid);
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Update the display information
|
|||
|
//
|
|||
|
|
|||
|
AccountInfo.Name = GroupName;
|
|||
|
AccountInfo.Rid = ObjectRid;
|
|||
|
AccountInfo.AccountControl = 0; // Don't care about this value for delete
|
|||
|
RtlInitUnicodeString(&AccountInfo.Comment, NULL);
|
|||
|
RtlInitUnicodeString(&AccountInfo.FullName, NULL);
|
|||
|
|
|||
|
TmpStatus = SampUpdateDisplayInformation(&AccountInfo,
|
|||
|
NULL,
|
|||
|
SampGroupObjectType);
|
|||
|
ASSERT(NT_SUCCESS(TmpStatus));
|
|||
|
|
|||
|
//
|
|||
|
// 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_GLOBAL_GROUP_DELETED, // AuditId
|
|||
|
Domain->Sid, // Domain SID
|
|||
|
NULL, // Member Rid (not used)
|
|||
|
NULL, // Member Sid (not used)
|
|||
|
&GroupName, // Account Name
|
|||
|
&Domain->ExternalName, // Domain
|
|||
|
&ObjectRid, // Account Rid
|
|||
|
NULL // Privileges used
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Do delete auditing
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
(VOID) NtDeleteObjectAuditAlarm(
|
|||
|
&SampSamSubsystem,
|
|||
|
*GroupHandle,
|
|||
|
AccountContext->AuditOnClose
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Notify netlogon of the change
|
|||
|
//
|
|||
|
|
|||
|
SampNotifyNetlogonOfDelta(
|
|||
|
SecurityDbDelete,
|
|||
|
SecurityDbObjectSamGroup,
|
|||
|
ObjectRid,
|
|||
|
&GroupName,
|
|||
|
(DWORD) FALSE, // Replicate immediately
|
|||
|
NULL // Delta data
|
|||
|
);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
SampFreeUnicodeString( &GroupName );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// De-reference the object, discarding changes
|
|||
|
//
|
|||
|
|
|||
|
TmpStatus = SampDeReferenceContext( AccountContext, FALSE );
|
|||
|
ASSERT(NT_SUCCESS(TmpStatus));
|
|||
|
|
|||
|
|
|||
|
if ( NT_SUCCESS( NtStatus ) ) {
|
|||
|
|
|||
|
//
|
|||
|
// If we actually deleted the group, delete the context and
|
|||
|
// let RPC know that the handle is invalid.
|
|||
|
//
|
|||
|
|
|||
|
SampDeleteContext( AccountContext );
|
|||
|
|
|||
|
(*GroupHandle) = 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
|
|||
|
SamrRemoveMemberFromGroup(
|
|||
|
IN SAMPR_HANDLE GroupHandle,
|
|||
|
IN ULONG MemberId
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This service
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
????
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
|
|||
|
????
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
SAMP_V1_0A_FIXED_LENGTH_GROUP GroupV1Fixed;
|
|||
|
NTSTATUS NtStatus, TmpStatus;
|
|||
|
PSAMP_OBJECT AccountContext;
|
|||
|
SAMP_OBJECT_TYPE FoundType;
|
|||
|
ULONG ObjectRid;
|
|||
|
BOOLEAN UserAccountActive;
|
|||
|
UNICODE_STRING GroupName;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Initialize buffers to be cleaned up at the end
|
|||
|
//
|
|||
|
|
|||
|
RtlInitUnicodeString(&GroupName, NULL);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Grab the lock
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampAcquireWriteLock();
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
return(NtStatus);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Validate type of, and access to object.
|
|||
|
//
|
|||
|
|
|||
|
AccountContext = (PSAMP_OBJECT)(GroupHandle);
|
|||
|
ObjectRid = AccountContext->TypeBody.Group.Rid;
|
|||
|
NtStatus = SampLookupContext(
|
|||
|
AccountContext,
|
|||
|
GROUP_REMOVE_MEMBER,
|
|||
|
SampGroupObjectType, // ExpectedType
|
|||
|
&FoundType
|
|||
|
);
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
NtStatus = SampRetrieveGroupV1Fixed(
|
|||
|
AccountContext,
|
|||
|
&GroupV1Fixed
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
//
|
|||
|
// Perform the user object side of things
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampRemoveMembershipUser(
|
|||
|
ObjectRid,
|
|||
|
MemberId,
|
|||
|
(GroupV1Fixed.AdminCount == 0) ? NoChange : RemoveFromAdmin,
|
|||
|
(GroupV1Fixed.OperatorCount == 0) ? NoChange : RemoveFromAdmin,
|
|||
|
&UserAccountActive
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Now perform the group side of things
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
//
|
|||
|
// Remove the user from the group (should not fail)
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampRemoveAccountFromGroupMembers(
|
|||
|
AccountContext,
|
|||
|
MemberId
|
|||
|
);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
//
|
|||
|
// Get and save the account name for
|
|||
|
// I_NetNotifyLogonOfDelta.
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampGetUnicodeStringAttribute(
|
|||
|
AccountContext,
|
|||
|
SAMP_GROUP_NAME,
|
|||
|
TRUE, // Make copy
|
|||
|
&GroupName
|
|||
|
);
|
|||
|
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
RtlInitUnicodeString(&GroupName, NULL);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Dereference the account context
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
//
|
|||
|
// 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;
|
|||
|
|
|||
|
//
|
|||
|
// Fill in id of member being deleted
|
|||
|
//
|
|||
|
|
|||
|
DeltaData.GroupMemberId.MemberRid = MemberId;
|
|||
|
|
|||
|
SampNotifyNetlogonOfDelta(
|
|||
|
SecurityDbChangeMemberDel,
|
|||
|
SecurityDbObjectSamGroup,
|
|||
|
ObjectRid,
|
|||
|
&GroupName,
|
|||
|
(DWORD) FALSE, // Replicate immediately
|
|||
|
&DeltaData
|
|||
|
);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Free up the group name
|
|||
|
//
|
|||
|
|
|||
|
SampFreeUnicodeString(&GroupName);
|
|||
|
|
|||
|
|
|||
|
TmpStatus = SampReleaseWriteLock( FALSE );
|
|||
|
ASSERT(NT_SUCCESS(TmpStatus));
|
|||
|
|
|||
|
return(NtStatus);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamrGetMembersInGroup(
|
|||
|
IN SAMPR_HANDLE GroupHandle,
|
|||
|
OUT PSAMPR_GET_MEMBERS_BUFFER *GetMembersBuffer
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This API lists all the members in a group. This API may be called
|
|||
|
repeatedly, passing a returned context handle, to retrieve large
|
|||
|
amounts of data. This API requires GROUP_LIST_MEMBERS access to the
|
|||
|
group.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
GroupHandle - The handle of an opened group to operate on.
|
|||
|
GROUP_LIST_MEMBERS access is needed to the group.
|
|||
|
|
|||
|
GetMembersBuffer - Receives a pointer to a set of returned structures
|
|||
|
with the following format:
|
|||
|
|
|||
|
+-------------+
|
|||
|
--------->| MemberCount |
|
|||
|
|-------------+ +-------+
|
|||
|
| Members --|------------------->| Rid-0 |
|
|||
|
|-------------| +------------+ | ... |
|
|||
|
| Attributes-|-->| Attribute0 | | |
|
|||
|
+-------------+ | ... | | Rid-N |
|
|||
|
| AttributeN | +-------+
|
|||
|
+------------+
|
|||
|
|
|||
|
Each block individually allocated with MIDL_user_allocate.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully, and there
|
|||
|
are no addition entries.
|
|||
|
|
|||
|
STATUS_ACCESS_DENIED - Caller does not have privilege required to
|
|||
|
request that data.
|
|||
|
|
|||
|
STATUS_INVALID_HANDLE - The handle passed is invalid.
|
|||
|
This service
|
|||
|
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
|
|||
|
NTSTATUS NtStatus;
|
|||
|
NTSTATUS IgnoreStatus;
|
|||
|
ULONG i;
|
|||
|
ULONG ObjectRid;
|
|||
|
PSAMP_OBJECT AccountContext;
|
|||
|
SAMP_OBJECT_TYPE FoundType;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Make sure we understand what RPC is doing for (to) us.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT (GetMembersBuffer != NULL);
|
|||
|
|
|||
|
if ((*GetMembersBuffer) != NULL) {
|
|||
|
return(STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Allocate the first of the return buffers
|
|||
|
//
|
|||
|
|
|||
|
(*GetMembersBuffer) = MIDL_user_allocate( sizeof(SAMPR_GET_MEMBERS_BUFFER) );
|
|||
|
|
|||
|
if ( (*GetMembersBuffer) == NULL) {
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Grab the lock
|
|||
|
//
|
|||
|
|
|||
|
SampAcquireReadLock();
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Validate type of, and access to object.
|
|||
|
//
|
|||
|
|
|||
|
AccountContext = (PSAMP_OBJECT)GroupHandle;
|
|||
|
ObjectRid = AccountContext->TypeBody.Group.Rid;
|
|||
|
NtStatus = SampLookupContext(
|
|||
|
AccountContext,
|
|||
|
GROUP_LIST_MEMBERS,
|
|||
|
SampGroupObjectType, // ExpectedType
|
|||
|
&FoundType
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
NtStatus = SampRetrieveGroupMembers(
|
|||
|
AccountContext,
|
|||
|
&(*GetMembersBuffer)->MemberCount,
|
|||
|
&(*GetMembersBuffer)->Members
|
|||
|
);
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
//
|
|||
|
// Allocate a buffer for the attributes - which we get from
|
|||
|
// the individual user records
|
|||
|
//
|
|||
|
|
|||
|
(*GetMembersBuffer)->Attributes = MIDL_user_allocate((*GetMembersBuffer)->MemberCount * sizeof(ULONG) );
|
|||
|
if ((*GetMembersBuffer)->Attributes == NULL) {
|
|||
|
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
for ( i=0; (i<((*GetMembersBuffer)->MemberCount) && NT_SUCCESS(NtStatus)); i++) {
|
|||
|
|
|||
|
(*GetMembersBuffer)->Attributes[i] = SAMP_DEFAULT_GROUP_ATTRIBUTES;
|
|||
|
|
|||
|
//
|
|||
|
// Don't call the user code to get the attribute - this is too
|
|||
|
// expensive. Since group attributes are not really used we
|
|||
|
// hardwire the result to be the default.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// NtStatus = SampRetrieveUserGroupAttribute(
|
|||
|
// (*GetMembersBuffer)->Members[i],
|
|||
|
// ObjectRid,
|
|||
|
// &(*GetMembersBuffer)->Attributes[i]
|
|||
|
// );
|
|||
|
}
|
|||
|
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
MIDL_user_free( (*GetMembersBuffer)->Members );
|
|||
|
if ((*GetMembersBuffer)->Attributes != NULL) {
|
|||
|
MIDL_user_free( (*GetMembersBuffer)->Attributes );
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// De-reference the object, discarding changes
|
|||
|
//
|
|||
|
|
|||
|
IgnoreStatus = SampDeReferenceContext( AccountContext, FALSE );
|
|||
|
ASSERT(NT_SUCCESS(IgnoreStatus));
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Free the read lock
|
|||
|
//
|
|||
|
|
|||
|
SampReleaseReadLock();
|
|||
|
|
|||
|
|
|||
|
if (!NT_SUCCESS(NtStatus) || ((*GetMembersBuffer)->MemberCount == 0)){
|
|||
|
|
|||
|
(*GetMembersBuffer)->MemberCount = 0;
|
|||
|
(*GetMembersBuffer)->Members = NULL;
|
|||
|
(*GetMembersBuffer)->Attributes = NULL;
|
|||
|
}
|
|||
|
|
|||
|
return( NtStatus );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SamrSetMemberAttributesOfGroup(
|
|||
|
IN SAMPR_HANDLE GroupHandle,
|
|||
|
IN ULONG MemberId,
|
|||
|
IN ULONG Attributes
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
|
|||
|
This routine modifies the group attributes of a member of the group.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
Parameters:
|
|||
|
|
|||
|
GroupHandle - The handle of an opened group to operate on.
|
|||
|
|
|||
|
MemberId - Contains the relative ID of member whose attributes
|
|||
|
are to be modified.
|
|||
|
|
|||
|
Attributes - The group attributes to set for the member. These
|
|||
|
attributes must not conflict with the attributes of the group
|
|||
|
as a whole. See SamAddMemberToGroup() for more information
|
|||
|
on compatible attribute settings.
|
|||
|
|
|||
|
Return Values:
|
|||
|
|
|||
|
STATUS_SUCCESS - The Service completed successfully, and there
|
|||
|
are no addition entries.
|
|||
|
|
|||
|
STATUS_INVALID_INFO_CLASS - The class provided was invalid.
|
|||
|
|
|||
|
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_USER - The user specified does not exist.
|
|||
|
|
|||
|
STATUS_MEMBER_NOT_IN_GROUP - Indicates the specified relative ID
|
|||
|
is not a member of the group.
|
|||
|
|
|||
|
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;
|
|||
|
PSAMP_OBJECT AccountContext;
|
|||
|
SAMP_OBJECT_TYPE FoundType;
|
|||
|
ULONG ObjectRid;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Grab the lock
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampAcquireWriteLock();
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
return(NtStatus);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Validate type of, and access to object.
|
|||
|
//
|
|||
|
|
|||
|
AccountContext = (PSAMP_OBJECT)(GroupHandle);
|
|||
|
ObjectRid = AccountContext->TypeBody.Group.Rid;
|
|||
|
NtStatus = SampLookupContext(
|
|||
|
AccountContext,
|
|||
|
GROUP_ADD_MEMBER,
|
|||
|
SampGroupObjectType, // ExpectedType
|
|||
|
&FoundType
|
|||
|
);
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
//
|
|||
|
// Update user object
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampSetGroupAttributesOfUser(
|
|||
|
ObjectRid,
|
|||
|
Attributes,
|
|||
|
MemberId
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Dereference the account context
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
//
|
|||
|
// 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 ) ) {
|
|||
|
|
|||
|
SampNotifyNetlogonOfDelta(
|
|||
|
SecurityDbChange,
|
|||
|
SecurityDbObjectSamGroup,
|
|||
|
ObjectRid,
|
|||
|
(PUNICODE_STRING) NULL,
|
|||
|
(DWORD) FALSE, // Replicate immediately
|
|||
|
NULL // Delta data
|
|||
|
);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
TmpStatus = SampReleaseWriteLock( FALSE );
|
|||
|
|
|||
|
|
|||
|
return(NtStatus);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
// //
|
|||
|
// Internal Services Available For Use in Other SAM Modules //
|
|||
|
// //
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampAddUserToGroup(
|
|||
|
IN ULONG GroupRid,
|
|||
|
IN ULONG UserRid
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This service is expected to be used when a user is being created.
|
|||
|
It is used to add that user as a member to a specified group.
|
|||
|
This is done by simply adding the user's ID to the list of IDs
|
|||
|
in the MEMBERS sub-key of the the specified group.
|
|||
|
|
|||
|
|
|||
|
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.
|
|||
|
|
|||
|
|
|||
|
If the group is the DOMAIN_ADMIN group, the caller is responsible
|
|||
|
for updating the ActiveAdminCount (if appropriate).
|
|||
|
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
GroupRid - The RID of the group the user is to be made a member of.
|
|||
|
|
|||
|
UserRid - The RID of the user being added as a new member.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
|
|||
|
STATUS_SUCCESS - The user has been added.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
PSAMP_OBJECT GroupContext;
|
|||
|
|
|||
|
|
|||
|
NtStatus = SampCreateAccountContext(
|
|||
|
SampGroupObjectType,
|
|||
|
GroupRid,
|
|||
|
TRUE, // Trusted client
|
|||
|
TRUE, // Account exists
|
|||
|
&GroupContext
|
|||
|
);
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
//
|
|||
|
// Add the user to the group member list.
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampAddAccountToGroupMembers(
|
|||
|
GroupContext,
|
|||
|
UserRid
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Write out any changes to the group account
|
|||
|
// Don't use the open key handle since we'll be deleting the context.
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
NtStatus = SampStoreObjectAttributes(GroupContext, FALSE);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Clean up the group context
|
|||
|
//
|
|||
|
|
|||
|
SampDeleteContext(GroupContext);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return(NtStatus);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampRemoveUserFromGroup(
|
|||
|
IN ULONG GroupRid,
|
|||
|
IN ULONG UserRid
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is used to Remove a user from a specified group.
|
|||
|
This is done by simply Removing the user's ID From the list of IDs
|
|||
|
in the MEMBERS sub-key of the the specified group.
|
|||
|
|
|||
|
It is the caller's responsibility to know that the user is, in fact,
|
|||
|
currently a member of the group.
|
|||
|
|
|||
|
|
|||
|
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.
|
|||
|
|
|||
|
|
|||
|
If the group is the DOMAIN_ADMIN group, the caller is responsible
|
|||
|
for updating the ActiveAdminCount (if appropriate).
|
|||
|
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
GroupRid - The RID of the group the user is to be removed from.
|
|||
|
|
|||
|
UserRid - The RID of the user being Removed.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
|
|||
|
STATUS_SUCCESS - The user has been Removed.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
PSAMP_OBJECT GroupContext;
|
|||
|
|
|||
|
|
|||
|
NtStatus = SampCreateAccountContext(
|
|||
|
SampGroupObjectType,
|
|||
|
GroupRid,
|
|||
|
TRUE, // Trusted client
|
|||
|
TRUE, // Account exists
|
|||
|
&GroupContext
|
|||
|
);
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
//
|
|||
|
// Remove the user from the group member list.
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampRemoveAccountFromGroupMembers(
|
|||
|
GroupContext,
|
|||
|
UserRid
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Write out any changes to the group account
|
|||
|
// Don't use the open key handle since we'll be deleting the context.
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
NtStatus = SampStoreObjectAttributes(GroupContext, FALSE);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Clean up the group context
|
|||
|
//
|
|||
|
|
|||
|
SampDeleteContext(GroupContext);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return(NtStatus);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
// //
|
|||
|
// Services Private to this file //
|
|||
|
// //
|
|||
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampRetrieveGroupV1Fixed(
|
|||
|
IN PSAMP_OBJECT GroupContext,
|
|||
|
IN PSAMP_V1_0A_FIXED_LENGTH_GROUP V1Fixed
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This service retrieves the V1 fixed length information related to
|
|||
|
a specified group.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
GroupRootKey - Root key for the group whose V1_FIXED information is
|
|||
|
to be retrieved.
|
|||
|
|
|||
|
V1Fixed - Is a buffer into which the information is to be returned.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
|
|||
|
STATUS_SUCCESS - The information has been retrieved.
|
|||
|
|
|||
|
Other status values that may be returned are those returned
|
|||
|
by:
|
|||
|
|
|||
|
SampGetFixedAttributes()
|
|||
|
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
PVOID FixedData;
|
|||
|
|
|||
|
|
|||
|
NtStatus = SampGetFixedAttributes(
|
|||
|
GroupContext,
|
|||
|
FALSE, // Don't make copy
|
|||
|
&FixedData
|
|||
|
);
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
//
|
|||
|
// Copy data into return buffer
|
|||
|
// *V1Fixed = *((PSAMP_V1_0A_FIXED_LENGTH_GROUP)FixedData);
|
|||
|
//
|
|||
|
|
|||
|
RtlMoveMemory(
|
|||
|
V1Fixed,
|
|||
|
FixedData,
|
|||
|
sizeof(SAMP_V1_0A_FIXED_LENGTH_GROUP)
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
return( NtStatus );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampReplaceGroupV1Fixed(
|
|||
|
IN PSAMP_OBJECT Context,
|
|||
|
IN PSAMP_V1_0A_FIXED_LENGTH_GROUP V1Fixed
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This service replaces the current V1 fixed length information related to
|
|||
|
a specified group.
|
|||
|
|
|||
|
The change is made to the in-memory object data only.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Context - Points to the account context whose V1_FIXED information is
|
|||
|
to be replaced.
|
|||
|
|
|||
|
V1Fixed - Is a buffer containing the new V1_FIXED information.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
|
|||
|
STATUS_SUCCESS - The information has been replaced.
|
|||
|
|
|||
|
Other status values that may be returned are those returned
|
|||
|
by:
|
|||
|
|
|||
|
SampSetFixedAttributes()
|
|||
|
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
NtStatus = SampSetFixedAttributes(
|
|||
|
Context,
|
|||
|
(PVOID)V1Fixed
|
|||
|
);
|
|||
|
|
|||
|
return( NtStatus );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampRetrieveGroupMembers(
|
|||
|
IN PSAMP_OBJECT GroupContext,
|
|||
|
IN PULONG MemberCount,
|
|||
|
IN PULONG *Members OPTIONAL
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This service retrieves the number of members in a group. If desired,
|
|||
|
it will also retrieve an array of RIDs of the members of the group.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
GroupContext - Group context block
|
|||
|
|
|||
|
MemberCount - Receives the number of members currently in the group.
|
|||
|
|
|||
|
Members - (Optional) Receives a pointer to a buffer containing an array
|
|||
|
of member Relative IDs. 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.
|
|||
|
|
|||
|
The Members array returned always includes space for one new entry.
|
|||
|
|
|||
|
|
|||
|
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:
|
|||
|
|
|||
|
SampGetUlongArrayAttribute()
|
|||
|
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
PULONG Array;
|
|||
|
ULONG LengthCount;
|
|||
|
|
|||
|
NtStatus = SampGetUlongArrayAttribute(
|
|||
|
GroupContext,
|
|||
|
SAMP_GROUP_MEMBERS,
|
|||
|
FALSE, // Reference data directly
|
|||
|
&Array,
|
|||
|
MemberCount,
|
|||
|
&LengthCount
|
|||
|
);
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
//
|
|||
|
// Fill in return info
|
|||
|
//
|
|||
|
|
|||
|
if (Members != NULL) {
|
|||
|
|
|||
|
//
|
|||
|
// Allocate a buffer large enough to hold the existing membership
|
|||
|
// data plus one.
|
|||
|
//
|
|||
|
|
|||
|
ULONG BytesNow = (*MemberCount) * sizeof(ULONG);
|
|||
|
ULONG BytesRequired = BytesNow + sizeof(ULONG);
|
|||
|
|
|||
|
*Members = MIDL_user_allocate(BytesRequired);
|
|||
|
|
|||
|
if (*Members == NULL) {
|
|||
|
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
} else {
|
|||
|
RtlCopyMemory(*Members, Array, BytesNow);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return( NtStatus );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampReplaceGroupMembers(
|
|||
|
IN PSAMP_OBJECT GroupContext,
|
|||
|
IN ULONG MemberCount,
|
|||
|
IN PULONG Members
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This service sets the members of a group.
|
|||
|
|
|||
|
The information is updated in the in-memory copy of the group's data only.
|
|||
|
The data is not written out by this routine.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
GroupContext - The group whose member list is to be replaced
|
|||
|
|
|||
|
MemberCount - The number of new members
|
|||
|
|
|||
|
Membership - A pointer to a buffer containing an array of account rids.
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
|
|||
|
STATUS_SUCCESS - The information has been set.
|
|||
|
|
|||
|
Other status values that may be returned are those returned
|
|||
|
by:
|
|||
|
|
|||
|
SampSetUlongArrayAttribute()
|
|||
|
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus = STATUS_SUCCESS;
|
|||
|
PULONG LocalMembers;
|
|||
|
ULONG LengthCount;
|
|||
|
ULONG SmallListGrowIncrement = 25;
|
|||
|
ULONG BigListGrowIncrement = 250;
|
|||
|
ULONG BigListSize = 800;
|
|||
|
|
|||
|
//
|
|||
|
// These group user lists can get pretty big, and grow many
|
|||
|
// times by a very small amount as each user is added. The
|
|||
|
// registry doesn't like that kind of behaviour (it tends to
|
|||
|
// eat up free space something fierce) so we'll try to pad
|
|||
|
// out the list size.
|
|||
|
//
|
|||
|
|
|||
|
if ( MemberCount < BigListSize ) {
|
|||
|
|
|||
|
//
|
|||
|
// If less than 800 users, make the list size the smallest
|
|||
|
// possible multiple of 25 users.
|
|||
|
//
|
|||
|
|
|||
|
LengthCount = ( ( MemberCount + SmallListGrowIncrement - 1 ) /
|
|||
|
SmallListGrowIncrement ) *
|
|||
|
SmallListGrowIncrement;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// If 800 users or more, make the list size the smallest
|
|||
|
// possible multiple of 250 users.
|
|||
|
//
|
|||
|
|
|||
|
LengthCount = ( ( MemberCount + BigListGrowIncrement - 1 ) /
|
|||
|
BigListGrowIncrement ) *
|
|||
|
BigListGrowIncrement;
|
|||
|
}
|
|||
|
|
|||
|
ASSERT( LengthCount >= MemberCount );
|
|||
|
|
|||
|
if ( LengthCount == MemberCount ) {
|
|||
|
|
|||
|
//
|
|||
|
// Just the right size. Use the buffer that was passed in.
|
|||
|
//
|
|||
|
|
|||
|
LocalMembers = Members;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// We need to allocate a larger buffer before we set the attribute.
|
|||
|
//
|
|||
|
|
|||
|
LocalMembers = MIDL_user_allocate( LengthCount * sizeof(ULONG));
|
|||
|
|
|||
|
if ( LocalMembers == NULL ) {
|
|||
|
|
|||
|
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Copy the old buffer to the larger buffer, and zero out the
|
|||
|
// empty stuff at the end.
|
|||
|
//
|
|||
|
|
|||
|
RtlCopyMemory( LocalMembers, Members, MemberCount * sizeof(ULONG));
|
|||
|
|
|||
|
RtlZeroMemory(
|
|||
|
(LocalMembers + MemberCount),
|
|||
|
(LengthCount - MemberCount) * sizeof(ULONG)
|
|||
|
);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if ( NT_SUCCESS( NtStatus ) ) {
|
|||
|
|
|||
|
NtStatus = SampSetUlongArrayAttribute(
|
|||
|
GroupContext,
|
|||
|
SAMP_GROUP_MEMBERS,
|
|||
|
LocalMembers,
|
|||
|
MemberCount,
|
|||
|
LengthCount
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
if ( LocalMembers != Members ) {
|
|||
|
|
|||
|
//
|
|||
|
// We must have allocated a larger local buffer, so free it.
|
|||
|
//
|
|||
|
|
|||
|
MIDL_user_free( LocalMembers );
|
|||
|
}
|
|||
|
|
|||
|
return( NtStatus );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampDeleteGroupKeys(
|
|||
|
IN PSAMP_OBJECT Context
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This service deletes all registry keys related to a group object.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Context - Points to the group 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;
|
|||
|
|
|||
|
|
|||
|
Rid = Context->TypeBody.Group.Rid;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Groups are arranged as follows:
|
|||
|
//
|
|||
|
// +-- Groups [Count]
|
|||
|
// ---+--
|
|||
|
// +-- Names
|
|||
|
// | --+--
|
|||
|
// | +-- (GroupName) [GroupRid,]
|
|||
|
// |
|
|||
|
// +-- (GroupRid) [Revision,SecurityDescriptor]
|
|||
|
// ---+-----
|
|||
|
// +-- V1_Fixed [,SAM_V1_0A_FIXED_LENGTH_GROUP]
|
|||
|
// +-- Name [,Name]
|
|||
|
// +-- AdminComment [,unicode string]
|
|||
|
// +-- Members [Count,(Member0Rid, (...), MemberX-1Rid)]
|
|||
|
//
|
|||
|
// This all needs to be deleted from the bottom up.
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Decrement the group count
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampAdjustAccountCount(SampGroupObjectType, FALSE );
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Delete the registry key that has the group's name to RID mapping.
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
//
|
|||
|
// Get the name
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampGetUnicodeStringAttribute(
|
|||
|
Context,
|
|||
|
SAMP_GROUP_NAME,
|
|||
|
TRUE, // Make copy
|
|||
|
&AccountName
|
|||
|
);
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
NtStatus = SampBuildAccountKeyName(
|
|||
|
SampGroupObjectType,
|
|||
|
&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(
|
|||
|
SampGroupObjectType,
|
|||
|
&KeyName,
|
|||
|
Rid,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
|
|||
|
NtStatus = RtlAddActionToRXact(
|
|||
|
SampRXactContext,
|
|||
|
RtlRXactOperationDelete,
|
|||
|
&KeyName,
|
|||
|
0,
|
|||
|
NULL,
|
|||
|
0
|
|||
|
);
|
|||
|
SampFreeUnicodeString( &KeyName );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
return( NtStatus );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampChangeGroupAccountName(
|
|||
|
IN PSAMP_OBJECT Context,
|
|||
|
IN PUNICODE_STRING NewAccountName,
|
|||
|
OUT PUNICODE_STRING OldAccountName
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine changes the account name of a group account.
|
|||
|
|
|||
|
THIS SERVICE MUST BE CALLED WITH THE TRANSACTION DOMAIN SET.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Context - Points to the group 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;
|
|||
|
|
|||
|
/////////////////////////////////////////////////////////////
|
|||
|
// There are two copies of the name of each account. //
|
|||
|
// one is under the DOMAIN\(domainName)\GROUP\NAMES key, //
|
|||
|
// one is the value of the //
|
|||
|
// DOMAIN\(DomainName)\GROUP\(rid)\NAME key //
|
|||
|
/////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
//
|
|||
|
// Get the current name so we can delete the old Name->Rid
|
|||
|
// mapping key.
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampGetUnicodeStringAttribute(
|
|||
|
Context,
|
|||
|
SAMP_GROUP_NAME,
|
|||
|
TRUE, // Make copy
|
|||
|
OldAccountName
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Make sure the name is valid and not already in use
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
NtStatus = SampValidateAccountNameChange(
|
|||
|
NewAccountName,
|
|||
|
OldAccountName
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Delete the old name key
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
NtStatus = SampBuildAccountKeyName(
|
|||
|
SampGroupObjectType,
|
|||
|
&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(
|
|||
|
SampGroupObjectType,
|
|||
|
&KeyName,
|
|||
|
NewAccountName
|
|||
|
);
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
ULONG GroupRid = Context->TypeBody.Group.Rid;
|
|||
|
|
|||
|
NtStatus = RtlAddActionToRXact(
|
|||
|
SampRXactContext,
|
|||
|
RtlRXactOperationSetValue,
|
|||
|
&KeyName,
|
|||
|
GroupRid,
|
|||
|
(PVOID)NULL,
|
|||
|
0
|
|||
|
);
|
|||
|
SampFreeUnicodeString( &KeyName );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// replace the account's name
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
NtStatus = SampSetUnicodeStringAttribute(
|
|||
|
Context,
|
|||
|
SAMP_GROUP_NAME,
|
|||
|
NewAccountName
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Free up the old account name if we failed
|
|||
|
//
|
|||
|
|
|||
|
if (!NT_SUCCESS(NtStatus)) {
|
|||
|
SampFreeUnicodeString(OldAccountName);
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
return(NtStatus);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampAddAccountToGroupMembers(
|
|||
|
IN PSAMP_OBJECT GroupContext,
|
|||
|
IN ULONG AccountRid
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This service adds the specified account rid to the member list
|
|||
|
for the specified group. This is a low-level function that
|
|||
|
simply edits the member attribute of the group context passed.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
GroupContext - The group whose member list will be modified
|
|||
|
|
|||
|
AccountRid - The RID of the account being added as a new member.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
|
|||
|
STATUS_SUCCESS - The account has been added.
|
|||
|
|
|||
|
STATUS_MEMBER_IN_GROUP - The account is already a member
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
ULONG MemberCount, i;
|
|||
|
PULONG MemberArray;
|
|||
|
|
|||
|
//
|
|||
|
// Get the existing member list
|
|||
|
// Note that the member array always includes space
|
|||
|
// for one new member
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampRetrieveGroupMembers(
|
|||
|
GroupContext,
|
|||
|
&MemberCount,
|
|||
|
&MemberArray
|
|||
|
);
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
//
|
|||
|
// Fail if the account is already a member
|
|||
|
//
|
|||
|
|
|||
|
for (i = 0; i<MemberCount ; i++ ) {
|
|||
|
|
|||
|
if ( MemberArray[i] == AccountRid ) {
|
|||
|
|
|||
|
ASSERT(FALSE);
|
|||
|
NtStatus = STATUS_MEMBER_IN_GROUP;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
//
|
|||
|
// Add the user's RID to the end of the list
|
|||
|
//
|
|||
|
|
|||
|
MemberArray[MemberCount] = AccountRid;
|
|||
|
MemberCount += 1;
|
|||
|
|
|||
|
//
|
|||
|
// Set the new group member list
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampReplaceGroupMembers(
|
|||
|
GroupContext,
|
|||
|
MemberCount,
|
|||
|
MemberArray
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// audit this, if necessary.
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus) &&
|
|||
|
SampDoAccountAuditing(GroupContext->DomainIndex)) {
|
|||
|
|
|||
|
PSAMP_DEFINED_DOMAINS Domain;
|
|||
|
UNICODE_STRING NameString;
|
|||
|
SAMP_OBJECT_TYPE ObjectType;
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
Domain = &SampDefinedDomains[ GroupContext->DomainIndex ];
|
|||
|
|
|||
|
Status = SampLookupAccountName(
|
|||
|
GroupContext->TypeBody.Alias.Rid,
|
|||
|
&NameString,
|
|||
|
&ObjectType
|
|||
|
);
|
|||
|
|
|||
|
if ( !NT_SUCCESS( Status )) {
|
|||
|
RtlInitUnicodeString( &NameString, L"-" );
|
|||
|
}
|
|||
|
|
|||
|
LsaIAuditSamEvent(
|
|||
|
STATUS_SUCCESS,
|
|||
|
SE_AUDITID_GLOBAL_GROUP_ADD, // AuditId
|
|||
|
Domain->Sid, // Domain SID
|
|||
|
&AccountRid, // Member Rid
|
|||
|
NULL, // Member Sid (not used)
|
|||
|
&NameString, // Account Name
|
|||
|
&Domain->ExternalName, // Domain
|
|||
|
&GroupContext->TypeBody.Group.Rid, // Account Rid
|
|||
|
NULL // Privileges used
|
|||
|
);
|
|||
|
|
|||
|
if ( NT_SUCCESS( Status )) {
|
|||
|
MIDL_user_free( NameString.Buffer );
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Free up the member list
|
|||
|
//
|
|||
|
|
|||
|
MIDL_user_free( MemberArray );
|
|||
|
}
|
|||
|
|
|||
|
return(NtStatus);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SampRemoveAccountFromGroupMembers(
|
|||
|
IN PSAMP_OBJECT GroupContext,
|
|||
|
IN ULONG AccountRid
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This service removes the specified account rid from the member list
|
|||
|
for the specified group. This is a low-level function that
|
|||
|
simply edits the member attribute of the group context passed.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
GroupContext - The group whose member list will be modified
|
|||
|
|
|||
|
AccountRid - The RID of the account being added as a new member.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
|
|||
|
STATUS_SUCCESS - The account has been added.
|
|||
|
|
|||
|
STATUS_MEMBER_NOT_IN_GROUP - The account is not a member of the group.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS NtStatus;
|
|||
|
ULONG MemberCount, i;
|
|||
|
PULONG MemberArray;
|
|||
|
|
|||
|
//
|
|||
|
// Get the existing member list
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampRetrieveGroupMembers(
|
|||
|
GroupContext,
|
|||
|
&MemberCount,
|
|||
|
&MemberArray
|
|||
|
);
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
//
|
|||
|
// Remove the account
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = STATUS_MEMBER_NOT_IN_GROUP;
|
|||
|
|
|||
|
for (i = 0; i<MemberCount ; i++ ) {
|
|||
|
|
|||
|
if (MemberArray[i] == AccountRid) {
|
|||
|
|
|||
|
MemberArray[i] = MemberArray[MemberCount-1];
|
|||
|
MemberCount -=1;
|
|||
|
|
|||
|
NtStatus = STATUS_SUCCESS;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus)) {
|
|||
|
|
|||
|
//
|
|||
|
// Set the new group member list
|
|||
|
//
|
|||
|
|
|||
|
NtStatus = SampReplaceGroupMembers(
|
|||
|
GroupContext,
|
|||
|
MemberCount,
|
|||
|
MemberArray
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// audit this, if necessary.
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(NtStatus) &&
|
|||
|
SampDoAccountAuditing(GroupContext->DomainIndex)) {
|
|||
|
|
|||
|
PSAMP_DEFINED_DOMAINS Domain;
|
|||
|
UNICODE_STRING NameString;
|
|||
|
SAMP_OBJECT_TYPE ObjectType;
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
Status = SampLookupAccountName(
|
|||
|
GroupContext->TypeBody.Alias.Rid,
|
|||
|
&NameString,
|
|||
|
&ObjectType
|
|||
|
);
|
|||
|
|
|||
|
if ( !NT_SUCCESS( Status )) {
|
|||
|
RtlInitUnicodeString( &NameString, L"-" );
|
|||
|
}
|
|||
|
Domain = &SampDefinedDomains[ GroupContext->DomainIndex ];
|
|||
|
|
|||
|
LsaIAuditSamEvent(
|
|||
|
STATUS_SUCCESS,
|
|||
|
SE_AUDITID_GLOBAL_GROUP_REM, // AuditId
|
|||
|
Domain->Sid, // Domain SID
|
|||
|
&AccountRid, // Member Rid
|
|||
|
NULL, // Member Sid (not used)
|
|||
|
&NameString, // Account Name
|
|||
|
&Domain->ExternalName, // Domain
|
|||
|
&GroupContext->TypeBody.Group.Rid, // Account Rid
|
|||
|
NULL // Privileges used
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
if ( NT_SUCCESS( Status )) {
|
|||
|
MIDL_user_free( NameString.Buffer );
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Free up the member list
|
|||
|
//
|
|||
|
|
|||
|
MIDL_user_free( MemberArray );
|
|||
|
}
|
|||
|
|
|||
|
return(NtStatus);
|
|||
|
}
|