5261 lines
143 KiB
C
5261 lines
143 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
bldsam3.c
|
||
|
||
Abstract:
|
||
|
||
This module provides an initialization capability to SAM.
|
||
|
||
|
||
Approach
|
||
--------
|
||
|
||
This code has gone through a number of migrations that make it
|
||
less than obvious what is going on. To leverage off existing
|
||
code and yet extend it to the initialization of two domains,
|
||
with aliases, the following aproach has been taken:
|
||
|
||
(1) Obtain the name and SID of the account domain.
|
||
|
||
(2) Build the various security descriptors needed
|
||
in the two domains. These are kept in an array
|
||
and the index is used to specify which applies
|
||
to each new account.
|
||
|
||
(3) Build up a list of alias memberships. These, too,
|
||
are selected by index, with one entry being the
|
||
empty set.
|
||
|
||
|
||
Author:
|
||
|
||
Jim Kelly 3-May-1991.
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include <nt.h>
|
||
#include <ntsam.h>
|
||
#include "ntlsa.h"
|
||
#include <ntrtl.h>
|
||
#include <nturtl.h>
|
||
|
||
#include <stdlib.h>
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
#include "samsrvp.h"
|
||
|
||
|
||
|
||
|
||
|
||
|
||
//
|
||
// Macro to round up a ULONG value to be dword aligned
|
||
// (i.e., be a multipleof 4).
|
||
// Note, this does not handle the case where the Ulong is greater
|
||
// than 0xfffffffc.
|
||
//
|
||
|
||
#define SampDwordAlignUlong( v ) (((v)+3) & 0xfffffffc)
|
||
|
||
//
|
||
// Constants used for Sam Global Data string buffers
|
||
//
|
||
|
||
#define SAMP_MAXIMUM_INTERNAL_NAME_LENGTH ((USHORT) 0x00000200L)
|
||
|
||
|
||
|
||
|
||
|
||
//
|
||
// domain selector
|
||
//
|
||
|
||
typedef enum _SAMP_DOMAIN_SELECTOR {
|
||
|
||
DomainBuiltin = 0,
|
||
DomainAccount
|
||
|
||
} SAMP_DOMAIN_SELECTOR, *PSAMP_DOMAIN_SELECTOR;
|
||
|
||
//
|
||
// Types of protection that may be assigned to accounts
|
||
//
|
||
|
||
typedef ULONG SAMP_ACCOUNT_PROTECTION;
|
||
|
||
#define SAMP_PROT_SAM_SERVER (0L)
|
||
#define SAMP_PROT_BUILTIN_DOMAIN (1L)
|
||
#define SAMP_PROT_ACCOUNT_DOMAIN (2L)
|
||
#define SAMP_PROT_ADMIN_ALIAS (3L)
|
||
#define SAMP_PROT_PWRUSER_ACCESSIBLE_ALIAS (4L)
|
||
#define SAMP_PROT_NORMAL_ALIAS (5L)
|
||
#define SAMP_PROT_ADMIN_GROUP (6L)
|
||
#define SAMP_PROT_NORMAL_GROUP (7L)
|
||
#define SAMP_PROT_ADMIN_USER (8L)
|
||
#define SAMP_PROT_NORMAL_USER (9L)
|
||
#define SAMP_PROT_GUEST_ACCOUNT (10L)
|
||
#define SAMP_PROT_TYPES (11L)
|
||
|
||
//
|
||
// Protection information for SAM objects
|
||
//
|
||
|
||
typedef struct _SAMP_PROTECTION {
|
||
|
||
ULONG Length;
|
||
PSECURITY_DESCRIPTOR Descriptor;
|
||
PULONG RidToReplace;
|
||
BOOLEAN RidReplacementRequired;
|
||
|
||
} SAMP_PROTECTION, *PSAMP_PROTECTION;
|
||
|
||
|
||
|
||
|
||
|
||
///////////////////////////////////////////////////////////////////////
|
||
// //
|
||
// Global variables //
|
||
// //
|
||
///////////////////////////////////////////////////////////////////////
|
||
|
||
static LSA_HANDLE SampBldPolicyHandle; //Handle to LSA policy object
|
||
|
||
static NTSTATUS Status;
|
||
|
||
static BOOLEAN SampRealSetupWasRun; //Indicates a real setup was run
|
||
static BOOLEAN SampDeveloperSetup; //Indicates a developer setup is running
|
||
|
||
NT_PRODUCT_TYPE SampProductType;
|
||
static DOMAIN_SERVER_ROLE SampServerRole;
|
||
static PPOLICY_PRIMARY_DOMAIN_INFO SampBldPrimaryDomain = NULL;
|
||
|
||
|
||
|
||
static PSID WorldSid,
|
||
LocalSystemSid,
|
||
AdminsAliasSid,
|
||
UsersAliasSid,
|
||
PowerUsersAliasSid,
|
||
AccountAliasSid,
|
||
AnySidInAccountDomain;
|
||
|
||
|
||
static PACL TokenDefaultDaclInformation;
|
||
static ULONG TokenDefaultDaclInformationSize;
|
||
|
||
//
|
||
// Handle to the registry key in which the SAM database resides
|
||
//
|
||
|
||
static HANDLE SamParentKey = NULL;
|
||
|
||
//
|
||
// Handle to the root SAM key.
|
||
// This is the key that has the RXACT applied to it.
|
||
//
|
||
|
||
static HANDLE SamKey = NULL;
|
||
|
||
static PRTL_RXACT_CONTEXT SamRXactContext;
|
||
|
||
//
|
||
// Assorted names, buffers, and values used during registry key creation
|
||
//
|
||
|
||
static PSID DomainSid;
|
||
static PUNICODE_STRING DomainNameU, FullDomainNameU;
|
||
static UNICODE_STRING AccountInternalDomainNameU, BuiltinInternalDomainNameU;
|
||
static UNICODE_STRING AccountExternalDomainNameU, BuiltinExternalDomainNameU;
|
||
static UNICODE_STRING FullAccountInternalDomainNameU, FullBuiltinInternalDomainNameU;
|
||
static UNICODE_STRING DomainNamePrefixU, TemporaryNamePrefixU, KeyNameU, TempStringU;
|
||
|
||
static WCHAR KeyNameBuffer[2000];
|
||
static WCHAR TempStringBuffer[2000];
|
||
static SID_IDENTIFIER_AUTHORITY BuiltinAuthority = SECURITY_NT_AUTHORITY;
|
||
|
||
|
||
|
||
|
||
//
|
||
// Values that get placed in registry keys...
|
||
//
|
||
|
||
static LARGE_INTEGER DomainMaxPasswordAge = { 0, - 6L * 7L * 24L * 60L / 7L }; // 6 weeks
|
||
static LARGE_INTEGER ModifiedCount = {0,0};
|
||
static UNICODE_STRING NullUnicodeString;
|
||
|
||
|
||
|
||
//
|
||
// Array of protection information for SAM objects
|
||
//
|
||
|
||
static SAMP_PROTECTION SampProtection[SAMP_PROT_TYPES];
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
//
|
||
// Internal routine definitions
|
||
//
|
||
|
||
|
||
|
||
VOID
|
||
SampGetDomainPolicy( VOID );
|
||
|
||
VOID
|
||
SampGetServerRole( VOID );
|
||
|
||
VOID
|
||
SampGetPrimaryDomainInfo( VOID );
|
||
|
||
|
||
VOID
|
||
GetDomainSids( VOID );
|
||
|
||
VOID
|
||
SetDomainName(
|
||
IN BOOLEAN BuiltinDomain
|
||
);
|
||
|
||
|
||
VOID
|
||
Usage ( VOID );
|
||
|
||
|
||
NTSTATUS
|
||
Initialize( VOID );
|
||
|
||
BOOLEAN
|
||
InitializeSecurityDescriptors( VOID );
|
||
|
||
NTSTATUS
|
||
SampCreateDatabaseProtection(
|
||
PISECURITY_DESCRIPTOR SD
|
||
);
|
||
|
||
NTSTATUS
|
||
SampBuildNewProtection(
|
||
IN ULONG AceCount,
|
||
IN PSID *AceSid,
|
||
IN ACCESS_MASK *AceMask,
|
||
IN PGENERIC_MAPPING GenericMap,
|
||
IN BOOLEAN UserObject,
|
||
OUT PSAMP_PROTECTION Result
|
||
);
|
||
|
||
NTSTATUS
|
||
InitializeSam( VOID );
|
||
|
||
NTSTATUS
|
||
PrepDomain(
|
||
IN SAMP_DOMAIN_SELECTOR Domain
|
||
);
|
||
|
||
|
||
VOID
|
||
SetCurrentDomain(
|
||
IN SAMP_DOMAIN_SELECTOR Domain
|
||
);
|
||
|
||
|
||
NTSTATUS
|
||
CreateBuiltinDomain( VOID );
|
||
|
||
NTSTATUS
|
||
CreateAccountDomain( VOID );
|
||
|
||
|
||
|
||
NTSTATUS
|
||
CreateAlias(
|
||
IN PUNICODE_STRING AccountNameU,
|
||
IN PUNICODE_STRING AccountCommentU,
|
||
IN BOOLEAN SpecialAccount,
|
||
IN ULONG Rid,
|
||
IN ULONG ProtectionIndex
|
||
);
|
||
|
||
|
||
NTSTATUS
|
||
CreateGroup(
|
||
IN PUNICODE_STRING AccountNameU,
|
||
IN PUNICODE_STRING AccountCommentU,
|
||
IN BOOLEAN SpecialAccount,
|
||
IN ULONG Rid,
|
||
IN BOOLEAN Admin
|
||
);
|
||
|
||
|
||
NTSTATUS
|
||
CreateUser(
|
||
IN PUNICODE_STRING AccountNameU,
|
||
IN PUNICODE_STRING AccountCommentU,
|
||
IN BOOLEAN SpecialAccount,
|
||
IN ULONG UserRid,
|
||
IN ULONG PrimaryGroup,
|
||
IN BOOLEAN Admin,
|
||
IN ULONG UserControl,
|
||
IN ULONG ProtectionIndex
|
||
);
|
||
|
||
|
||
|
||
NTSTATUS
|
||
UpdateAliasXReference(
|
||
IN ULONG AliasRid,
|
||
IN PSID Sid
|
||
);
|
||
|
||
|
||
NTSTATUS
|
||
OpenAliasMember(
|
||
IN PSID Sid,
|
||
OUT PHANDLE KeyHandle
|
||
);
|
||
|
||
|
||
PSID
|
||
BuildPrimaryDomainSid(
|
||
ULONG Rid
|
||
);
|
||
|
||
PSID
|
||
BuildAccountSid(
|
||
SAMP_DOMAIN_SELECTOR Domain,
|
||
ULONG Rid
|
||
);
|
||
|
||
|
||
NTSTATUS
|
||
OpenOrCreateAccountRidKey(
|
||
IN PSID Sid,
|
||
IN HANDLE AliasDomainHandle,
|
||
OUT PHANDLE KeyHandle
|
||
);
|
||
|
||
NTSTATUS
|
||
OpenOrCreateAliasDomainKey(
|
||
IN PSID Sid,
|
||
OUT PHANDLE KeyHandle
|
||
);
|
||
|
||
NTSTATUS
|
||
AppendAliasDomainNameToUnicodeString(
|
||
IN OUT PUNICODE_STRING Destination,
|
||
IN PSID Sid
|
||
);
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SampInitilializeRegistry ( VOID );
|
||
|
||
|
||
NTSTATUS
|
||
SampDetermineSetupEnvironment( VOID );
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SampGetMessageStrings(
|
||
LPVOID Resource,
|
||
DWORD Index1,
|
||
PUNICODE_STRING String1,
|
||
DWORD Index2,
|
||
PUNICODE_STRING String2 OPTIONAL
|
||
);
|
||
|
||
|
||
|
||
|
||
///////////////////////////////////////////////////////////////////////
|
||
// //
|
||
// Routines //
|
||
// //
|
||
///////////////////////////////////////////////////////////////////////
|
||
|
||
|
||
VOID
|
||
Usage (
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
|
||
Routine Description:
|
||
|
||
This routine prints the "Usage:" message.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
|
||
#if DBG
|
||
BldPrint( "\n");
|
||
BldPrint( "\n");
|
||
|
||
BldPrint( "We offer no assistance in this suicide.\n");
|
||
BldPrint( "\n");
|
||
BldPrint( "\n");
|
||
BldPrint( "\n");
|
||
#endif
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
UnexpectedProblem (
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
|
||
Routine Description:
|
||
|
||
This routine prints a message indicating that an unexpected
|
||
problem has occured.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
|
||
#if DBG
|
||
BldPrint( "\n");
|
||
BldPrint( "\n");
|
||
BldPrint( " An unexpected problem has prevented the command from\n");
|
||
BldPrint( " completing successfully. Please contact one of the\n");
|
||
BldPrint( " members of the security group for assistance.\n");
|
||
BldPrint( "\n");
|
||
#endif
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
Initialize(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
|
||
Routine Description:
|
||
|
||
This routine performs initialization operations before creating
|
||
each domain.
|
||
|
||
This includes:
|
||
|
||
- Setting the correct default owner and DACL for registry key
|
||
operations.
|
||
|
||
- opening the parent registry key for the SAM database.
|
||
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
TRUE - Indicates initialization was successful.
|
||
|
||
FALSE - Indicates initialization was not successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
OBJECT_ATTRIBUTES SamParentAttributes, PolicyObjectAttributes;
|
||
UNICODE_STRING SamParentNameU;
|
||
ULONG Disposition;
|
||
HANDLE Token;
|
||
TOKEN_OWNER LocalSystemOwner;
|
||
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
|
||
SID_IDENTIFIER_AUTHORITY WorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY;
|
||
PACL Dacl;
|
||
TOKEN_DEFAULT_DACL DefaultDacl;
|
||
BOOLEAN CompletionStatus;
|
||
BOOLEAN ProductTypeRetrieved;
|
||
|
||
//
|
||
// Set up some of the well known account SIDs for use...
|
||
//
|
||
|
||
WorldSid = (PSID)RtlAllocateHeap(RtlProcessHeap(), 0,RtlLengthRequiredSid( 1 ));
|
||
ASSERT(WorldSid != NULL);
|
||
RtlInitializeSid( WorldSid, &WorldSidAuthority, 1 );
|
||
*(RtlSubAuthoritySid( WorldSid, 0 )) = SECURITY_WORLD_RID;
|
||
|
||
AdminsAliasSid = RtlAllocateHeap(RtlProcessHeap(), 0,RtlLengthRequiredSid( 2 ));
|
||
ASSERT(AdminsAliasSid != NULL);
|
||
RtlInitializeSid( AdminsAliasSid, &BuiltinAuthority, 2 );
|
||
*(RtlSubAuthoritySid( AdminsAliasSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID;
|
||
*(RtlSubAuthoritySid( AdminsAliasSid, 1 )) = DOMAIN_ALIAS_RID_ADMINS;
|
||
|
||
PowerUsersAliasSid = RtlAllocateHeap(RtlProcessHeap(), 0,RtlLengthRequiredSid( 2 ));
|
||
ASSERT(PowerUsersAliasSid != NULL);
|
||
RtlInitializeSid( PowerUsersAliasSid, &BuiltinAuthority, 2 );
|
||
*(RtlSubAuthoritySid( PowerUsersAliasSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID;
|
||
*(RtlSubAuthoritySid( PowerUsersAliasSid, 1 )) = DOMAIN_ALIAS_RID_POWER_USERS;
|
||
|
||
UsersAliasSid = RtlAllocateHeap(RtlProcessHeap(), 0,RtlLengthRequiredSid( 2 ));
|
||
ASSERT(UsersAliasSid != NULL);
|
||
RtlInitializeSid( UsersAliasSid, &BuiltinAuthority, 2 );
|
||
*(RtlSubAuthoritySid( UsersAliasSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID;
|
||
*(RtlSubAuthoritySid( UsersAliasSid, 1 )) = DOMAIN_ALIAS_RID_USERS;
|
||
|
||
AccountAliasSid = RtlAllocateHeap(RtlProcessHeap(), 0,RtlLengthRequiredSid( 2 ));
|
||
ASSERT(AccountAliasSid != NULL);
|
||
RtlInitializeSid( AccountAliasSid, &BuiltinAuthority, 2 );
|
||
*(RtlSubAuthoritySid( AccountAliasSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID;
|
||
*(RtlSubAuthoritySid( AccountAliasSid, 1 )) = DOMAIN_ALIAS_RID_ACCOUNT_OPS;
|
||
|
||
LocalSystemSid = RtlAllocateHeap(RtlProcessHeap(), 0,RtlLengthRequiredSid( 1 ));
|
||
ASSERT(LocalSystemSid != NULL);
|
||
RtlInitializeSid( LocalSystemSid, &NtAuthority, 1 );
|
||
*(RtlSubAuthoritySid( LocalSystemSid, 0 )) = SECURITY_LOCAL_SYSTEM_RID;
|
||
|
||
//
|
||
// Setup a buffer to use for all our key-name constructions
|
||
//
|
||
|
||
KeyNameU.MaximumLength = 2000;
|
||
KeyNameU.Buffer = KeyNameBuffer;
|
||
|
||
//
|
||
// Setup temporary Unicode string buffer.
|
||
//
|
||
|
||
TempStringU.Buffer = TempStringBuffer;
|
||
TempStringU.MaximumLength = 2000;
|
||
|
||
//
|
||
// Get a handle to the LSA Policy object
|
||
//
|
||
|
||
InitializeObjectAttributes(
|
||
&PolicyObjectAttributes,
|
||
NULL, // Name
|
||
0, // Attributes
|
||
NULL, // Root
|
||
NULL // Security Descriptor
|
||
);
|
||
|
||
Status = LsaIOpenPolicyTrusted( &SampBldPolicyHandle );
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
KdPrint(("newsam\\server\\bldsam3: Couldn't open LSA Policy object.\n"
|
||
" Status: 0x%lx\n\n", Status));
|
||
return(Status);
|
||
}
|
||
|
||
//
|
||
// Get the product type.
|
||
//
|
||
|
||
ProductTypeRetrieved = RtlGetNtProductType( &SampProductType );
|
||
|
||
if (!ProductTypeRetrieved) {
|
||
|
||
KdPrint(("Couldn't retrieve product type\n"));
|
||
return(STATUS_UNSUCCESSFUL);
|
||
}
|
||
|
||
//
|
||
// Figure out if we are being initialized following a real
|
||
// setup, or it this is a developer setup.
|
||
//
|
||
|
||
SampDetermineSetupEnvironment();
|
||
|
||
//
|
||
// Domain name prefix is required by SampGetDomainPolicy() and
|
||
// so must be initialized before that call.
|
||
//
|
||
|
||
RtlInitUnicodeString( &DomainNamePrefixU, L"Domains");
|
||
|
||
//
|
||
// Set up domain names/Sids.
|
||
//
|
||
|
||
SampGetDomainPolicy();
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
return(Status);
|
||
}
|
||
|
||
//
|
||
// Get the role of this machine.
|
||
//
|
||
|
||
SampGetServerRole();
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
return(Status);
|
||
}
|
||
|
||
//
|
||
// Get the primary domain info.
|
||
//
|
||
|
||
SampGetPrimaryDomainInfo();
|
||
|
||
//
|
||
// Set the default owner to SYSTEM
|
||
//
|
||
|
||
Status = NtOpenProcessToken(
|
||
NtCurrentProcess(),
|
||
(TOKEN_ADJUST_DEFAULT | TOKEN_QUERY),
|
||
&Token
|
||
);
|
||
SUCCESS_ASSERT(Status, " Couldn't open process token.\n");
|
||
|
||
Status = NtQueryInformationToken(
|
||
Token,
|
||
TokenDefaultDacl,
|
||
NULL,
|
||
0,
|
||
&TokenDefaultDaclInformationSize
|
||
);
|
||
|
||
if (Status != STATUS_BUFFER_TOO_SMALL) {
|
||
|
||
KdPrint(("Error: Unable to query default ACL information\n"));
|
||
return( Status );
|
||
}
|
||
|
||
TokenDefaultDaclInformation = RtlAllocateHeap(RtlProcessHeap(), 0, TokenDefaultDaclInformationSize );
|
||
|
||
Status = NtQueryInformationToken(
|
||
Token,
|
||
TokenDefaultDacl,
|
||
TokenDefaultDaclInformation,
|
||
TokenDefaultDaclInformationSize,
|
||
&TokenDefaultDaclInformationSize
|
||
);
|
||
|
||
SUCCESS_ASSERT(Status, " Couldn't query default ACL information.\n");
|
||
|
||
LocalSystemOwner.Owner = LocalSystemSid;
|
||
|
||
Status = NtSetInformationToken(
|
||
Token,
|
||
TokenOwner,
|
||
&LocalSystemOwner,
|
||
(ULONG)sizeof(TOKEN_OWNER)
|
||
);
|
||
|
||
if (Status == STATUS_INVALID_OWNER) {
|
||
#if DBG
|
||
BldPrint(" This command must be issued from an account that may\n");
|
||
BldPrint(" assign the default SYSTEM id as the owner of objects.\n");
|
||
BldPrint(" \n");
|
||
BldPrint(" Try logging on to the system account and issuing this\n");
|
||
BldPrint(" command again.\n");
|
||
BldPrint(" \n");
|
||
#endif
|
||
return(Status);
|
||
}
|
||
|
||
//
|
||
// Set our default protection so that only system and ADMINISTRATORS
|
||
// can get to the registry keys created.
|
||
// The protection established is
|
||
//
|
||
// Grant:LocalSystem:Read|Write|Execute|Delete
|
||
// Grant:Administrators:Read|Execute|Delete|write_dacl
|
||
//
|
||
//
|
||
//
|
||
|
||
Dacl = RtlAllocateHeap( RtlProcessHeap(), 0, 256 );
|
||
if (Dacl == NULL) {
|
||
#if DBG
|
||
BldPrint(" Exhausted Heap Space Allocating Dacl\n");
|
||
#endif
|
||
return(STATUS_NO_MEMORY);
|
||
}
|
||
|
||
Status = RtlCreateAcl( Dacl, 256, ACL_REVISION2);
|
||
SUCCESS_ASSERT(Status, " Failed to initialize Dacl\n");
|
||
|
||
Status = RtlAddAccessAllowedAce(
|
||
Dacl,
|
||
ACL_REVISION2,
|
||
(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | DELETE ),
|
||
LocalSystemSid
|
||
);
|
||
SUCCESS_ASSERT(Status, " Failed to add SYSTEM ACE to Dacl\n");
|
||
DefaultDacl.DefaultDacl = Dacl;
|
||
|
||
Status = RtlAddAccessAllowedAce(
|
||
Dacl,
|
||
ACL_REVISION2,
|
||
(GENERIC_READ |
|
||
GENERIC_EXECUTE |
|
||
DELETE |
|
||
WRITE_DAC),
|
||
AdminsAliasSid
|
||
);
|
||
SUCCESS_ASSERT(Status, " Failed to add ADMIN alias ACE to Dacl\n");
|
||
DefaultDacl.DefaultDacl = Dacl;
|
||
|
||
Status = NtSetInformationToken(
|
||
Token,
|
||
TokenDefaultDacl,
|
||
&DefaultDacl,
|
||
(ULONG)sizeof(TOKEN_DEFAULT_DACL)
|
||
);
|
||
SUCCESS_ASSERT(Status, " Couldn't assign default Dacl\n");
|
||
Status = NtClose( Token );
|
||
|
||
RtlFreeHeap( RtlProcessHeap(), 0, Dacl ); // No longer needed
|
||
|
||
//
|
||
// Open a handle to the parent of the SAM registry location.
|
||
// This parent must already exist.
|
||
//
|
||
|
||
RtlInitUnicodeString( &SamParentNameU, L"\\Registry\\Machine\\Security" );
|
||
|
||
InitializeObjectAttributes(
|
||
&SamParentAttributes,
|
||
&SamParentNameU,
|
||
OBJ_CASE_INSENSITIVE,
|
||
NULL,
|
||
NULL
|
||
);
|
||
Status = RtlpNtCreateKey(
|
||
&SamParentKey,
|
||
(KEY_READ | KEY_CREATE_SUB_KEY),
|
||
&SamParentAttributes,
|
||
0,
|
||
NULL,
|
||
&Disposition
|
||
);
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
#if DBG
|
||
BldPrint( "\n" );
|
||
BldPrint( "\n" );
|
||
BldPrint( " We seem to be having trouble opening the registry\n" );
|
||
BldPrint( " database key in which the Security Account Manager\n" );
|
||
BldPrint( " information resides. This registry key should have been\n" );
|
||
BldPrint( " created at system startup time. Please see one of the\n" );
|
||
BldPrint( " security group developers for assistance in analyzing the\n" );
|
||
BldPrint( " the problem.\n" );
|
||
BldPrint( " Indicate that the registry key creation status is 0x%lx \n", Status);
|
||
BldPrint( "\n" );
|
||
BldPrint( "\n" );
|
||
#endif
|
||
|
||
return(Status);
|
||
}
|
||
|
||
//
|
||
// Set up some values, names, and buffers for later use
|
||
//
|
||
|
||
|
||
NullUnicodeString.Buffer = NULL;
|
||
NullUnicodeString.Length = 0;
|
||
NullUnicodeString.MaximumLength = 0;
|
||
|
||
|
||
|
||
TemporaryNamePrefixU.Buffer = RtlAllocateHeap(RtlProcessHeap(), 0, 256);
|
||
TemporaryNamePrefixU.Length = 0;
|
||
TemporaryNamePrefixU.MaximumLength = 256;
|
||
|
||
KeyNameU.Buffer = RtlAllocateHeap(RtlProcessHeap(), 0, 256);
|
||
KeyNameU.Length = 0;
|
||
KeyNameU.MaximumLength = 256;
|
||
|
||
//
|
||
// Set up Security Descriptors needed for initialization...
|
||
//
|
||
|
||
CompletionStatus = InitializeSecurityDescriptors();
|
||
|
||
if (CompletionStatus) {
|
||
Status = STATUS_SUCCESS;
|
||
} else {
|
||
Status = STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
return(Status);
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
InitializeSecurityDescriptors(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes security descriptors needed to create
|
||
a SAM database.
|
||
|
||
|
||
This routine expects all SIDs to be previously initialized.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
TRUE - Indicates initialization was successful.
|
||
|
||
FALSE - Indicates initialization was not successful.
|
||
|
||
|
||
The security descriptors are pointed to by global variables.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSID AceSid[10]; // Don't expect more than 10 ACEs in any of these.
|
||
ACCESS_MASK AceMask[10]; // Access masks corresponding to Sids
|
||
|
||
ACCESS_MASK NotForThisProductType; // Used to mask product-specific access restrictions
|
||
|
||
GENERIC_MAPPING SamServerMap = {SAM_SERVER_READ,
|
||
SAM_SERVER_WRITE,
|
||
SAM_SERVER_EXECUTE,
|
||
SAM_SERVER_ALL_ACCESS
|
||
};
|
||
|
||
GENERIC_MAPPING DomainMap = {DOMAIN_READ,
|
||
DOMAIN_WRITE,
|
||
DOMAIN_EXECUTE,
|
||
DOMAIN_ALL_ACCESS
|
||
};
|
||
|
||
GENERIC_MAPPING AliasMap = {ALIAS_READ,
|
||
ALIAS_WRITE,
|
||
ALIAS_EXECUTE,
|
||
ALIAS_ALL_ACCESS
|
||
};
|
||
|
||
GENERIC_MAPPING GroupMap = {GROUP_READ,
|
||
GROUP_WRITE,
|
||
GROUP_EXECUTE,
|
||
GROUP_ALL_ACCESS
|
||
};
|
||
|
||
GENERIC_MAPPING UserMap = {USER_READ,
|
||
USER_WRITE,
|
||
USER_EXECUTE,
|
||
USER_ALL_ACCESS
|
||
};
|
||
|
||
//
|
||
// We need a number of different security descriptors:
|
||
//
|
||
|
||
//
|
||
//
|
||
// The following security is assigned to
|
||
//
|
||
// - Builtin DOMAIN objects
|
||
//
|
||
//
|
||
// Owner: Administrators Alias
|
||
// Group: Administrators Alias
|
||
//
|
||
// Dacl: Grant Grant
|
||
// WORLD Administrators
|
||
// (Execute | Read) GenericRead |
|
||
// GenericExecute |
|
||
// DOMAIN_READ_OTHER_PARAMETERS |
|
||
// DOMAIN_ADMINISTER_SERVER |
|
||
// DOMAIN_CREATE_ALIAS
|
||
//
|
||
// Sacl: Audit
|
||
// Success | Fail
|
||
// WORLD
|
||
// (Write | Delete | WriteDacl | AccessSystemSecurity)
|
||
//
|
||
//
|
||
//
|
||
//
|
||
//
|
||
// The following security is assigned to
|
||
//
|
||
// - SAM_SERVER object
|
||
// - Account DOMAIN objects
|
||
// - The Administrators alias.
|
||
// - All groups in the ACCOUNT or BUILTIN domain that are
|
||
// made a member of the Administrators alias.
|
||
//
|
||
// Note: on WinNt systems, the ACLs do not grant DOMAIN_CREATE_GROUP.
|
||
//
|
||
//
|
||
// Owner: Administrators Alias
|
||
// Group: Administrators Alias
|
||
//
|
||
// Dacl: Grant Grant
|
||
// WORLD Administrators
|
||
// (Execute | Read) GenericAll
|
||
//
|
||
// Sacl: Audit
|
||
// Success | Fail
|
||
// WORLD
|
||
// (Write | Delete | WriteDacl | AccessSystemSecurity)
|
||
//
|
||
//
|
||
//
|
||
// All other aliases and groups must be assigned the following
|
||
// security:
|
||
//
|
||
// Owner: Administrators Alias
|
||
// Group: Administrators Alias
|
||
//
|
||
// Dacl: Grant Grant Grant
|
||
// WORLD Administrators AccountOperators Alias
|
||
// (Execute | Read) GenericAll GenericAll
|
||
//
|
||
// Sacl: Audit
|
||
// Success | Fail
|
||
// WORLD
|
||
// (Write | Delete | WriteDacl | AccessSystemSecurity)
|
||
//
|
||
//
|
||
// - All users in the ACCOUNT or BUILTIN domain that are
|
||
// made a member of the Administratos alias. This includes
|
||
// direct inclusion or indirect inclusion through group
|
||
// membership.
|
||
//
|
||
//
|
||
// The following security is assigned to:
|
||
//
|
||
// - All users in the ACCOUNT or BUILTIN domain that are
|
||
// made a member of the Administrators alias. This includes
|
||
// direct inclusion or indirect inclusion through group
|
||
// membership.
|
||
//
|
||
//
|
||
// Owner: Administrators Alias
|
||
// Group: Administrators Alias
|
||
//
|
||
// Dacl: Grant Grant Grant
|
||
// WORLD Administrators User's SID
|
||
// (Execute | Read) GenericAll GenericWrite
|
||
//
|
||
// Sacl: Audit
|
||
// Success | Fail
|
||
// WORLD
|
||
// (Write | Delete | WriteDacl | AccessSystemSecurity)
|
||
//
|
||
//
|
||
//
|
||
//
|
||
// All other users must be assigned the following
|
||
// security:
|
||
//
|
||
// Owner: AccountOperators Alias
|
||
// Group: AccountOperators Alias
|
||
//
|
||
// Dacl: Grant Grant Grant Grant
|
||
// WORLD Administrators Account Operators Alias User's SID
|
||
// (Execute | Read) GenericAll GenericAll GenericWrite
|
||
//
|
||
// Sacl: Audit
|
||
// Success | Fail
|
||
// WORLD
|
||
// (Write | Delete | WriteDacl | AccessSystemSecurity)
|
||
//
|
||
// except builtin GUEST, who can't change their own account info.
|
||
//
|
||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||
//
|
||
// Note, however, that because we are going to cram these ACLs
|
||
// directly into the backing store, we must map the generic accesses
|
||
// beforehand.
|
||
//
|
||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||
//
|
||
|
||
|
||
|
||
|
||
|
||
|
||
//
|
||
// Sam Server SD
|
||
//
|
||
|
||
AceSid[0] = WorldSid;
|
||
AceMask[0] = (SAM_SERVER_EXECUTE | SAM_SERVER_READ);
|
||
|
||
AceSid[1] = AdminsAliasSid;
|
||
AceMask[1] = (SAM_SERVER_ALL_ACCESS);
|
||
|
||
|
||
Status = SampBuildNewProtection(
|
||
2, // AceCount
|
||
&AceSid[0], // AceSid array
|
||
&AceMask[0], // Ace Mask array
|
||
&SamServerMap, // GenericMap
|
||
FALSE, // Not user object
|
||
&SampProtection[SAMP_PROT_SAM_SERVER] // Result
|
||
);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
//
|
||
// Builtin Domain SD
|
||
//
|
||
|
||
AceSid[0] = WorldSid;
|
||
AceMask[0] = (DOMAIN_EXECUTE | DOMAIN_READ);
|
||
|
||
AceSid[1] = AdminsAliasSid;
|
||
AceMask[1] = (DOMAIN_EXECUTE | DOMAIN_READ |
|
||
DOMAIN_READ_OTHER_PARAMETERS |
|
||
DOMAIN_ADMINISTER_SERVER |
|
||
DOMAIN_CREATE_ALIAS);
|
||
|
||
|
||
Status = SampBuildNewProtection(
|
||
2, // AceCount
|
||
&AceSid[0], // AceSid array
|
||
&AceMask[0], // Ace Mask array
|
||
&DomainMap, // GenericMap
|
||
FALSE, // Not user object
|
||
&SampProtection[SAMP_PROT_BUILTIN_DOMAIN] // Result
|
||
);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
//
|
||
// Account Domain SD
|
||
//
|
||
|
||
if (SampProductType == NtProductLanManNt) {
|
||
NotForThisProductType = 0;
|
||
} else {
|
||
NotForThisProductType = DOMAIN_CREATE_GROUP;
|
||
}
|
||
|
||
AceSid[0] = WorldSid;
|
||
AceMask[0] = (DOMAIN_EXECUTE | DOMAIN_READ) & ~NotForThisProductType;
|
||
|
||
AceSid[1] = UsersAliasSid;
|
||
AceMask[1] = (DOMAIN_EXECUTE | DOMAIN_READ | DOMAIN_CREATE_ALIAS)
|
||
& ~NotForThisProductType;
|
||
|
||
AceSid[2] = AdminsAliasSid;
|
||
AceMask[2] = (DOMAIN_ALL_ACCESS) & ~NotForThisProductType;
|
||
|
||
AceSid[3] = PowerUsersAliasSid;
|
||
AceMask[3] = (DOMAIN_EXECUTE | DOMAIN_READ | DOMAIN_CREATE_USER |
|
||
DOMAIN_CREATE_ALIAS)
|
||
& ~NotForThisProductType;
|
||
|
||
AceSid[4] = AccountAliasSid;
|
||
AceMask[4] = (DOMAIN_EXECUTE | DOMAIN_READ | DOMAIN_CREATE_USER |
|
||
DOMAIN_CREATE_GROUP |
|
||
DOMAIN_CREATE_ALIAS)
|
||
& ~NotForThisProductType;
|
||
|
||
|
||
Status = SampBuildNewProtection(
|
||
5, // AceCount
|
||
&AceSid[0], // AceSid array
|
||
&AceMask[0], // Ace Mask array
|
||
&DomainMap, // GenericMap
|
||
FALSE, // Not user object
|
||
&SampProtection[SAMP_PROT_ACCOUNT_DOMAIN] // Result
|
||
);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
|
||
|
||
//
|
||
// Admin Alias SD
|
||
//
|
||
|
||
AceSid[0] = WorldSid;
|
||
AceMask[0] = (ALIAS_EXECUTE | ALIAS_READ);
|
||
|
||
AceSid[1] = AdminsAliasSid;
|
||
AceMask[1] = (ALIAS_ALL_ACCESS);
|
||
|
||
|
||
Status = SampBuildNewProtection(
|
||
2, // AceCount
|
||
&AceSid[0], // AceSid array
|
||
&AceMask[0], // Ace Mask array
|
||
&AliasMap, // GenericMap
|
||
FALSE, // Not user object
|
||
&SampProtection[SAMP_PROT_ADMIN_ALIAS] // Result
|
||
);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
|
||
|
||
//
|
||
// Normal Alias SD
|
||
//
|
||
|
||
AceSid[0] = WorldSid;
|
||
AceMask[0] = (ALIAS_EXECUTE | ALIAS_READ);
|
||
|
||
AceSid[1] = AdminsAliasSid;
|
||
AceMask[1] = (ALIAS_ALL_ACCESS);
|
||
|
||
AceSid[2] = AccountAliasSid;
|
||
AceMask[2] = (ALIAS_ALL_ACCESS);
|
||
|
||
|
||
Status = SampBuildNewProtection(
|
||
3, // AceCount
|
||
&AceSid[0], // AceSid array
|
||
&AceMask[0], // Ace Mask array
|
||
&AliasMap, // GenericMap
|
||
FALSE, // Not user object
|
||
&SampProtection[SAMP_PROT_NORMAL_ALIAS] // Result
|
||
);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
|
||
|
||
|
||
//
|
||
// Power User accessible Alias SD
|
||
//
|
||
|
||
AceSid[0] = WorldSid;
|
||
AceMask[0] = (ALIAS_EXECUTE | ALIAS_READ);
|
||
|
||
AceSid[1] = AdminsAliasSid;
|
||
AceMask[1] = (ALIAS_ALL_ACCESS);
|
||
|
||
AceSid[2] = AccountAliasSid;
|
||
AceMask[2] = (ALIAS_ALL_ACCESS);
|
||
|
||
AceSid[3] = PowerUsersAliasSid;
|
||
AceMask[3] = (ALIAS_ALL_ACCESS);
|
||
|
||
|
||
Status = SampBuildNewProtection(
|
||
4, // AceCount
|
||
&AceSid[0], // AceSid array
|
||
&AceMask[0], // Ace Mask array
|
||
&AliasMap, // GenericMap
|
||
FALSE, // Not user object
|
||
&SampProtection[SAMP_PROT_PWRUSER_ACCESSIBLE_ALIAS] // Result
|
||
);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
|
||
|
||
|
||
//
|
||
// Admin Group SD
|
||
//
|
||
|
||
AceSid[0] = WorldSid;
|
||
AceMask[0] = (GROUP_EXECUTE | GROUP_READ);
|
||
|
||
AceSid[1] = AdminsAliasSid;
|
||
AceMask[1] = (GROUP_ALL_ACCESS);
|
||
|
||
|
||
Status = SampBuildNewProtection(
|
||
2, // AceCount
|
||
&AceSid[0], // AceSid array
|
||
&AceMask[0], // Ace Mask array
|
||
&GroupMap, // GenericMap
|
||
FALSE, // Not user object
|
||
&SampProtection[SAMP_PROT_ADMIN_GROUP] // Result
|
||
);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
|
||
|
||
//
|
||
// Normal GROUP SD
|
||
//
|
||
|
||
AceSid[0] = WorldSid;
|
||
AceMask[0] = (GROUP_EXECUTE | GROUP_READ);
|
||
|
||
AceSid[1] = AdminsAliasSid;
|
||
AceMask[1] = (GROUP_ALL_ACCESS);
|
||
|
||
AceSid[2] = AccountAliasSid;
|
||
AceMask[2] = (GROUP_ALL_ACCESS);
|
||
|
||
|
||
Status = SampBuildNewProtection(
|
||
3, // AceCount
|
||
&AceSid[0], // AceSid array
|
||
&AceMask[0], // Ace Mask array
|
||
&GroupMap, // GenericMap
|
||
FALSE, // Not user object
|
||
&SampProtection[SAMP_PROT_NORMAL_GROUP] // Result
|
||
);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
|
||
|
||
//
|
||
// Admin User SD
|
||
//
|
||
|
||
AceSid[0] = WorldSid;
|
||
AceMask[0] = (USER_EXECUTE | USER_READ);
|
||
|
||
AceSid[1] = AdminsAliasSid;
|
||
AceMask[1] = (USER_ALL_ACCESS);
|
||
|
||
AceSid[2] = AnySidInAccountDomain;
|
||
AceMask[2] = (USER_WRITE);
|
||
|
||
|
||
Status = SampBuildNewProtection(
|
||
3, // AceCount
|
||
&AceSid[0], // AceSid array
|
||
&AceMask[0], // Ace Mask array
|
||
&UserMap, // GenericMap
|
||
TRUE, // user object (rid replacement)
|
||
&SampProtection[SAMP_PROT_ADMIN_USER] // Result
|
||
);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
|
||
//
|
||
// Normal User SD
|
||
//
|
||
|
||
AceSid[0] = WorldSid;
|
||
AceMask[0] = (USER_EXECUTE | USER_READ);
|
||
|
||
AceSid[1] = AdminsAliasSid;
|
||
AceMask[1] = (USER_ALL_ACCESS);
|
||
|
||
AceSid[2] = AccountAliasSid;
|
||
AceMask[2] = (USER_ALL_ACCESS);
|
||
|
||
AceSid[3] = AnySidInAccountDomain;
|
||
AceMask[3] = (USER_WRITE);
|
||
|
||
|
||
Status = SampBuildNewProtection(
|
||
4, // AceCount
|
||
&AceSid[0], // AceSid array
|
||
&AceMask[0], // Ace Mask array
|
||
&UserMap, // GenericMap
|
||
TRUE, // user object (rid replacement)
|
||
&SampProtection[SAMP_PROT_NORMAL_USER] // Result
|
||
);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
|
||
|
||
//
|
||
// Builtin Guest Account SD
|
||
// Can't change own password or other setable fields
|
||
//
|
||
|
||
AceSid[0] = WorldSid;
|
||
AceMask[0] = (USER_READ | USER_EXECUTE & ~(USER_CHANGE_PASSWORD));
|
||
|
||
AceSid[1] = AdminsAliasSid;
|
||
AceMask[1] = (USER_ALL_ACCESS);
|
||
|
||
AceSid[2] = AccountAliasSid;
|
||
AceMask[2] = (USER_ALL_ACCESS);
|
||
|
||
|
||
|
||
|
||
Status = SampBuildNewProtection(
|
||
3, // AceCount
|
||
&AceSid[0], // AceSid array
|
||
&AceMask[0], // Ace Mask array
|
||
&UserMap, // GenericMap
|
||
FALSE, // no rid replacement
|
||
&SampProtection[SAMP_PROT_GUEST_ACCOUNT] // Result
|
||
);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
|
||
|
||
return(TRUE);
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SampBuildNewProtection(
|
||
IN ULONG AceCount,
|
||
IN PSID *AceSid,
|
||
IN ACCESS_MASK *AceMask,
|
||
IN PGENERIC_MAPPING GenericMap,
|
||
IN BOOLEAN UserObject,
|
||
OUT PSAMP_PROTECTION Result
|
||
)
|
||
|
||
/*++
|
||
|
||
|
||
Routine Description:
|
||
|
||
This routine builds a self-relative security descriptor ready
|
||
to be applied to one of the SAM objects.
|
||
|
||
If so indicated, a pointer to the last RID of the SID in the last
|
||
ACE of the DACL is returned and a flag set indicating that the RID
|
||
must be replaced before the security descriptor is applied to an object.
|
||
This is to support USER object protection, which must grant some
|
||
access to the user represented by the object.
|
||
|
||
The owner and group of each security descriptor will be set
|
||
to:
|
||
|
||
Owner: Administrators Alias
|
||
Group: Administrators Alias
|
||
|
||
|
||
The SACL of each of these objects will be set to:
|
||
|
||
|
||
Audit
|
||
Success | Fail
|
||
WORLD
|
||
(Write | Delete | WriteDacl | AccessSystemSecurity) & !ReadControl
|
||
|
||
|
||
|
||
Arguments:
|
||
|
||
AceCount - The number of ACEs to be included in the DACL.
|
||
|
||
AceSid - Points to an array of SIDs to be granted access by the DACL.
|
||
If the target SAM object is a User object, then the last entry
|
||
in this array is expected to be the SID of an account within the
|
||
domain with the last RID not yet set. The RID will be set during
|
||
actual account creation.
|
||
|
||
AceMask - Points to an array of accesses to be granted by the DACL.
|
||
The n'th entry of this array corresponds to the n'th entry of
|
||
the AceSid array. These masks should not include any generic
|
||
access types.
|
||
|
||
GenericMap - Points to a generic mapping for the target object type.
|
||
|
||
|
||
UserObject - Indicates whether the target SAM object is a User object
|
||
or not. If TRUE (it is a User object), then the resultant
|
||
protection will be set up indicating Rid replacement is necessary.
|
||
|
||
Result - Receives a pointer to the resultant protection information.
|
||
All access masks in ACLs in the result are mapped to standard and
|
||
specific accesses.
|
||
|
||
|
||
Return Value:
|
||
|
||
TBS.
|
||
|
||
--*/
|
||
{
|
||
|
||
|
||
|
||
SECURITY_DESCRIPTOR Absolute;
|
||
PSECURITY_DESCRIPTOR Relative;
|
||
PACL TmpAcl;
|
||
PACCESS_ALLOWED_ACE TmpAce;
|
||
PSID TmpSid;
|
||
ULONG Length, i;
|
||
PULONG RidLocation;
|
||
BOOLEAN IgnoreBoolean;
|
||
ACCESS_MASK MappedMask;
|
||
|
||
//
|
||
// The approach is to set up an absolute security descriptor that
|
||
// looks like what we want and then copy it to make a self-relative
|
||
// security descriptor.
|
||
//
|
||
|
||
|
||
Status = RtlCreateSecurityDescriptor(
|
||
&Absolute,
|
||
SECURITY_DESCRIPTOR_REVISION1
|
||
);
|
||
ASSERT( NT_SUCCESS(Status) );
|
||
|
||
|
||
|
||
//
|
||
// Owner
|
||
//
|
||
|
||
Status = RtlSetOwnerSecurityDescriptor (&Absolute, AdminsAliasSid, FALSE );
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
|
||
|
||
//
|
||
// Group
|
||
//
|
||
|
||
Status = RtlSetGroupSecurityDescriptor (&Absolute, AdminsAliasSid, FALSE );
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
|
||
|
||
|
||
//
|
||
// Discretionary ACL
|
||
//
|
||
// Calculate its length,
|
||
// Allocate it,
|
||
// Initialize it,
|
||
// Add each ACE
|
||
// Add it to the security descriptor
|
||
//
|
||
|
||
Length = (ULONG)sizeof(ACL);
|
||
for (i=0; i<AceCount; i++) {
|
||
|
||
Length += RtlLengthSid( AceSid[i] ) +
|
||
(ULONG)sizeof(ACCESS_ALLOWED_ACE) -
|
||
(ULONG)sizeof(ULONG); //Subtract out SidStart field length
|
||
}
|
||
|
||
TmpAcl = RtlAllocateHeap( RtlProcessHeap(), 0, Length );
|
||
ASSERT(TmpAcl != NULL);
|
||
|
||
|
||
Status = RtlCreateAcl( TmpAcl, Length, ACL_REVISION2);
|
||
ASSERT( NT_SUCCESS(Status) );
|
||
|
||
for (i=0; i<AceCount; i++) {
|
||
MappedMask = AceMask[i];
|
||
RtlMapGenericMask( &MappedMask, GenericMap );
|
||
Status = RtlAddAccessAllowedAce (
|
||
TmpAcl,
|
||
ACL_REVISION2,
|
||
MappedMask,
|
||
AceSid[i]
|
||
);
|
||
ASSERT( NT_SUCCESS(Status) );
|
||
}
|
||
|
||
Status = RtlSetDaclSecurityDescriptor (&Absolute, TRUE, TmpAcl, FALSE );
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
|
||
|
||
|
||
//
|
||
// Sacl
|
||
//
|
||
|
||
|
||
Length = (ULONG)sizeof(ACL) +
|
||
RtlLengthSid( WorldSid ) +
|
||
(ULONG)sizeof(SYSTEM_AUDIT_ACE) -
|
||
(ULONG)sizeof(ULONG); //Subtract out SidStart field length
|
||
TmpAcl = RtlAllocateHeap( RtlProcessHeap(), 0, Length );
|
||
ASSERT(TmpAcl != NULL);
|
||
|
||
Status = RtlCreateAcl( TmpAcl, Length, ACL_REVISION2);
|
||
ASSERT( NT_SUCCESS(Status) );
|
||
|
||
Status = RtlAddAuditAccessAce (
|
||
TmpAcl,
|
||
ACL_REVISION2,
|
||
(GenericMap->GenericWrite | DELETE | WRITE_DAC | ACCESS_SYSTEM_SECURITY) & ~READ_CONTROL,
|
||
WorldSid,
|
||
TRUE, //AuditSuccess,
|
||
TRUE //AuditFailure
|
||
);
|
||
ASSERT( NT_SUCCESS(Status) );
|
||
|
||
Status = RtlSetSaclSecurityDescriptor (&Absolute, TRUE, TmpAcl, FALSE );
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
|
||
|
||
|
||
|
||
|
||
//
|
||
// Convert the Security Descriptor to Self-Relative
|
||
//
|
||
// Get the length needed
|
||
// Allocate that much memory
|
||
// Copy it
|
||
// Free the generated absolute ACLs
|
||
//
|
||
|
||
Length = 0;
|
||
Status = RtlAbsoluteToSelfRelativeSD( &Absolute, NULL, &Length );
|
||
ASSERT(Status == STATUS_BUFFER_TOO_SMALL);
|
||
|
||
Relative = RtlAllocateHeap( RtlProcessHeap(), 0, Length );
|
||
ASSERT(Relative != NULL);
|
||
Status = RtlAbsoluteToSelfRelativeSD(&Absolute, Relative, &Length );
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
|
||
RtlFreeHeap( RtlProcessHeap(), 0, Absolute.Dacl );
|
||
RtlFreeHeap( RtlProcessHeap(), 0, Absolute.Sacl );
|
||
|
||
|
||
|
||
|
||
//
|
||
// If the object is a user object, then get the address of the
|
||
// last RID of the SID in the last ACE in the DACL.
|
||
//
|
||
|
||
if (UserObject == TRUE) {
|
||
|
||
Status = RtlGetDaclSecurityDescriptor(
|
||
Relative,
|
||
&IgnoreBoolean,
|
||
&TmpAcl,
|
||
&IgnoreBoolean
|
||
);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
Status = RtlGetAce ( TmpAcl, AceCount-1, (PVOID *)&TmpAce );
|
||
ASSERT(NT_SUCCESS(Status));
|
||
TmpSid = (PSID)(&TmpAce->SidStart),
|
||
|
||
RidLocation = RtlSubAuthoritySid(
|
||
TmpSid,
|
||
(ULONG)(*RtlSubAuthorityCountSid( TmpSid ) - 1)
|
||
);
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
//
|
||
// Set the result information
|
||
//
|
||
|
||
Result->Length = Length;
|
||
Result->Descriptor = Relative;
|
||
Result->RidToReplace = RidLocation;
|
||
Result->RidReplacementRequired = UserObject;
|
||
|
||
|
||
|
||
return(Status);
|
||
|
||
}
|
||
|
||
VOID
|
||
SampGetDomainPolicy(
|
||
)
|
||
/*++
|
||
|
||
|
||
Routine Description:
|
||
|
||
This routine builds the name strings for domains.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
ULONG Size;
|
||
PPOLICY_ACCOUNT_DOMAIN_INFO PolicyAccountDomainInfo;
|
||
|
||
//
|
||
// Builtin domain - Well-known External Name and Sid
|
||
// - Internal Name matches External Name
|
||
|
||
RtlInitUnicodeString( &BuiltinInternalDomainNameU, L"Builtin");
|
||
FullBuiltinInternalDomainNameU.Buffer = RtlAllocateHeap(RtlProcessHeap(), 0, 256);
|
||
FullBuiltinInternalDomainNameU.Length = 0;
|
||
FullBuiltinInternalDomainNameU.MaximumLength = 256;
|
||
RtlCopyUnicodeString( &FullBuiltinInternalDomainNameU, &DomainNamePrefixU );
|
||
Status = RtlAppendUnicodeToString( &FullBuiltinInternalDomainNameU, L"\\" );
|
||
RtlAppendUnicodeStringToString( &FullBuiltinInternalDomainNameU, &BuiltinInternalDomainNameU );
|
||
|
||
BuiltinExternalDomainNameU = BuiltinInternalDomainNameU;
|
||
|
||
SampBuiltinDomainSid = RtlAllocateHeap(RtlProcessHeap(), 0,RtlLengthRequiredSid( 1 ));
|
||
ASSERT( SampBuiltinDomainSid != NULL );
|
||
RtlInitializeSid( SampBuiltinDomainSid, &BuiltinAuthority, 1 );
|
||
*(RtlSubAuthoritySid( SampBuiltinDomainSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID;
|
||
|
||
//
|
||
// Account domain - Configurable External Name and Sid.
|
||
//
|
||
// The External Name and Sid are obtained from the
|
||
// Lsa Policy Object (PolicyAccountDomainInformation
|
||
// information class). For a DC, the External Name
|
||
// is the Domain Name and for a Wksta, the External
|
||
// Name is the Computer Name as at the time of the
|
||
// system load.
|
||
//
|
||
// For DC's the Internal Name is the Domain Name
|
||
// - For Wksta's the Internal Name is the constant name
|
||
// "Account".
|
||
//
|
||
// NOTE: The reason for these choices of Internal Name
|
||
// is to avoid having to change the SAM Database.
|
||
//
|
||
|
||
Status = SampGetAccountDomainInfo( &PolicyAccountDomainInfo );
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
KdPrint(( "BLDSAM3: Couldn't retrieve policy information from LSA.\n"
|
||
" Status = 0x%lx\n", Status));
|
||
return;
|
||
}
|
||
|
||
SampAccountDomainSid = PolicyAccountDomainInfo->DomainSid;
|
||
|
||
AccountExternalDomainNameU = PolicyAccountDomainInfo->DomainName;
|
||
|
||
RtlInitUnicodeString( &AccountInternalDomainNameU, L"Account");
|
||
|
||
FullAccountInternalDomainNameU.Buffer = RtlAllocateHeap(RtlProcessHeap(), 0, 256);
|
||
FullAccountInternalDomainNameU.Length = 0;
|
||
FullAccountInternalDomainNameU.MaximumLength = SAMP_MAXIMUM_INTERNAL_NAME_LENGTH;
|
||
RtlCopyUnicodeString( &FullAccountInternalDomainNameU, &DomainNamePrefixU );
|
||
Status = RtlAppendUnicodeToString( &FullAccountInternalDomainNameU, L"\\" );
|
||
RtlAppendUnicodeStringToString( &FullAccountInternalDomainNameU, &AccountInternalDomainNameU );
|
||
|
||
//
|
||
// Now initialize a SID that can be used to represent accounts
|
||
// in this domain. Same as SampAccountDomainSid except with one
|
||
// extra sub-authority. It doesn't matter what the value of the
|
||
// last RID is because it is always replaced before use.
|
||
//
|
||
|
||
Size = RtlLengthSid( SampAccountDomainSid ) + sizeof(ULONG);
|
||
AnySidInAccountDomain = RtlAllocateHeap( RtlProcessHeap(), 0, Size);
|
||
ASSERT( AnySidInAccountDomain != NULL );
|
||
Status = RtlCopySid( Size, AnySidInAccountDomain, SampAccountDomainSid );
|
||
ASSERT(NT_SUCCESS(Status));
|
||
(*RtlSubAuthorityCountSid( AnySidInAccountDomain )) += 1;
|
||
|
||
|
||
//
|
||
// Set builtin as "current" domain
|
||
//
|
||
|
||
SetCurrentDomain( DomainBuiltin );
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
SetCurrentDomain(
|
||
IN SAMP_DOMAIN_SELECTOR Domain
|
||
)
|
||
/*++
|
||
|
||
|
||
Routine Description:
|
||
|
||
This routine sets the current domain to be
|
||
either the account or builtin domain.
|
||
|
||
Arguments:
|
||
|
||
Domain - Specifies either builtin or account domain.
|
||
(DomainBuiltin or DomainAccount).
|
||
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
|
||
|
||
if (Domain == DomainBuiltin) {
|
||
|
||
DomainNameU = &BuiltinInternalDomainNameU;
|
||
FullDomainNameU = &FullBuiltinInternalDomainNameU;
|
||
DomainSid = SampBuiltinDomainSid;
|
||
|
||
} else {
|
||
|
||
DomainNameU = &AccountInternalDomainNameU;
|
||
FullDomainNameU = &FullAccountInternalDomainNameU;
|
||
DomainSid = SampAccountDomainSid;
|
||
|
||
}
|
||
|
||
|
||
|
||
return;
|
||
}
|
||
|
||
NTSTATUS
|
||
InitializeSam(
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes the SAM-level registry information.
|
||
It does not initialize any domains in the SAM.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSAMP_V1_FIXED_LENGTH_SERVER ServerFixedAttributes;
|
||
PSAMP_VARIABLE_LENGTH_ATTRIBUTE ServerVariableAttributeArray;
|
||
PVOID ServerVariableData;
|
||
OBJECT_ATTRIBUTES SamAttributes;
|
||
UNICODE_STRING SamNameU;
|
||
ULONG Disposition;
|
||
ULONG ServerAttributeLength;
|
||
SECURITY_DESCRIPTOR SecurityDescriptor;
|
||
BOOLEAN IgnoreBoolean;
|
||
PACL Dacl;
|
||
|
||
//
|
||
// Build a system default Dacl to protect the SAM database
|
||
// with.
|
||
//
|
||
|
||
Status = SampCreateDatabaseProtection( &SecurityDescriptor );
|
||
if (!NT_SUCCESS(Status)) {
|
||
return(Status);
|
||
}
|
||
|
||
//
|
||
// See if a remnant of a SAM database already exists
|
||
//
|
||
|
||
RtlInitUnicodeString( &SamNameU, L"SAM" );
|
||
|
||
InitializeObjectAttributes(
|
||
&SamAttributes,
|
||
&SamNameU,
|
||
OBJ_CASE_INSENSITIVE,
|
||
SamParentKey,
|
||
&SecurityDescriptor
|
||
);
|
||
Status = RtlpNtCreateKey(
|
||
&SamKey,
|
||
(KEY_READ | KEY_CREATE_SUB_KEY | KEY_WRITE),
|
||
&SamAttributes,
|
||
0,
|
||
NULL,
|
||
&Disposition
|
||
);
|
||
|
||
Status = RtlGetDaclSecurityDescriptor(
|
||
&SecurityDescriptor,
|
||
&IgnoreBoolean,
|
||
&Dacl,
|
||
&IgnoreBoolean
|
||
);
|
||
|
||
if (Dacl != NULL) {
|
||
RtlFreeHeap( RtlProcessHeap(), 0, Dacl );
|
||
}
|
||
ASSERT(SecurityDescriptor.Sacl == NULL);
|
||
ASSERT(SecurityDescriptor.Owner == NULL);
|
||
ASSERT(SecurityDescriptor.Group == NULL);
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
|
||
#if DBG
|
||
BldPrint( "\n" );
|
||
BldPrint( "\n" );
|
||
BldPrint( " We seem to be having trouble creating the registry\n" );
|
||
BldPrint( " database key in which the Security Account Manager\n" );
|
||
BldPrint( " information resides. Please see one of the security\n" );
|
||
BldPrint( " group developers for assistance in analyzing the\n" );
|
||
BldPrint( " the problem.\n" );
|
||
BldPrint( "\n" );
|
||
BldPrint( "\n" );
|
||
#endif
|
||
|
||
return(Status);
|
||
}
|
||
|
||
if ( Disposition != REG_CREATED_NEW_KEY ) {
|
||
|
||
#if DBG
|
||
BldPrint( "\n" );
|
||
BldPrint( "\n" );
|
||
BldPrint( " I'm terribly sorry, but you have specified that a SAM\n" );
|
||
BldPrint( " database be initialized and yet there is already a SAM\n" );
|
||
BldPrint( " database in existance. If the SAM database is corrupt\n" );
|
||
BldPrint( " or you would like to replace the existing domain anyway,\n" );
|
||
BldPrint( " please delnode the existing database and re-issue this \n");
|
||
BldPrint( " command. \n");
|
||
BldPrint( " The SAM database is in ...\\registry\\Machine\\security\\sam.\n" );
|
||
BldPrint( " Thank you.\n" );
|
||
BldPrint( "\n" );
|
||
BldPrint( "\n" );
|
||
#endif
|
||
|
||
Usage();
|
||
|
||
Status = NtClose( SamKey );
|
||
|
||
|
||
return(Status);
|
||
}
|
||
|
||
|
||
//
|
||
// Initialize the registry transaction structure for SAM.
|
||
//
|
||
|
||
Status = RtlInitializeRXact( SamKey, FALSE, &SamRXactContext );
|
||
|
||
if ( Status != STATUS_RXACT_STATE_CREATED ) {
|
||
#if DBG
|
||
BldPrint("\n");
|
||
BldPrint(" The SAM database already has a structure in place.\n");
|
||
BldPrint(" This indicates multiple initializations being performed\n");
|
||
BldPrint(" simultaneously. Please be sure no other initializations\n");
|
||
BldPrint(" are being performed and issue this command again.\n");
|
||
BldPrint("\n");
|
||
BldPrint("\n");
|
||
#endif
|
||
|
||
if ( Status == STATUS_SUCCESS ) {
|
||
|
||
//
|
||
// Shouldn't happen, but let's program defensively.
|
||
//
|
||
|
||
Status = STATUS_RXACT_INVALID_STATE;
|
||
}
|
||
|
||
return(Status);
|
||
}
|
||
|
||
//
|
||
// Start an RXACT to do the rest in ...
|
||
//
|
||
|
||
Status = RtlStartRXact( SamRXactContext );
|
||
SUCCESS_ASSERT(Status, " Starting transaction\n");
|
||
|
||
//
|
||
// Set the server's fixed and variable attributes
|
||
//
|
||
|
||
ServerAttributeLength = sizeof( SAMP_V1_FIXED_LENGTH_SERVER ) +
|
||
( SAMP_SERVER_VARIABLE_ATTRIBUTES *
|
||
sizeof( SAMP_VARIABLE_LENGTH_ATTRIBUTE ) ) +
|
||
SampProtection[SAMP_PROT_SAM_SERVER].Length;
|
||
|
||
ServerFixedAttributes = (PSAMP_V1_FIXED_LENGTH_SERVER)RtlAllocateHeap(
|
||
RtlProcessHeap(), 0,
|
||
ServerAttributeLength
|
||
);
|
||
|
||
if ( ServerFixedAttributes == NULL ) {
|
||
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
SUCCESS_ASSERT(Status, " Failed to create server attributes\n");
|
||
|
||
//
|
||
// The server revision on the a new SAM database may not be the same
|
||
// as the revision on the rest of SAM. This allows the server revision
|
||
// to indicate which bugs have been fixed in this SAM.
|
||
//
|
||
|
||
ServerFixedAttributes->RevisionLevel = SAMP_SERVER_REVISION;
|
||
|
||
ServerVariableAttributeArray = (PSAMP_VARIABLE_LENGTH_ATTRIBUTE)
|
||
((PUCHAR)(ServerFixedAttributes) +
|
||
sizeof( SAMP_V1_FIXED_LENGTH_SERVER ) );
|
||
|
||
ServerVariableAttributeArray->Offset = 0;
|
||
ServerVariableAttributeArray->Length =
|
||
SampProtection[SAMP_PROT_SAM_SERVER].Length;
|
||
ServerVariableAttributeArray->Qualifier = SAMP_REVISION;
|
||
|
||
ServerVariableData = (PVOID)( (PUCHAR)(ServerVariableAttributeArray) +
|
||
sizeof( SAMP_VARIABLE_LENGTH_ATTRIBUTE ) );
|
||
|
||
RtlCopyMemory(
|
||
ServerVariableData,
|
||
SampProtection[SAMP_PROT_SAM_SERVER].Descriptor,
|
||
SampProtection[SAMP_PROT_SAM_SERVER].Length
|
||
);
|
||
|
||
//
|
||
// Now write out the attributes via the RXACT.
|
||
//
|
||
|
||
RtlInitUnicodeString( &SamNameU, NULL );
|
||
|
||
Status = RtlAddAttributeActionToRXact(
|
||
SamRXactContext,
|
||
RtlRXactOperationSetValue,
|
||
&SamNameU,
|
||
INVALID_HANDLE_VALUE,
|
||
&SampCombinedAttributeName,
|
||
REG_BINARY,
|
||
(PVOID)ServerFixedAttributes,
|
||
ServerAttributeLength
|
||
);
|
||
|
||
SUCCESS_ASSERT(Status, " Failed to write out server attributes\n" );
|
||
|
||
RtlFreeHeap( RtlProcessHeap(), 0, ServerFixedAttributes );
|
||
|
||
//
|
||
// Create SAM\Domains
|
||
//
|
||
|
||
Status = RtlAddActionToRXact(
|
||
SamRXactContext,
|
||
RtlRXactOperationSetValue,
|
||
&DomainNamePrefixU,
|
||
0,
|
||
NULL,
|
||
0
|
||
);
|
||
|
||
SUCCESS_ASSERT(Status, " Failed to add domain key to log\n");
|
||
|
||
Status = RtlApplyRXactNoFlush( SamRXactContext );
|
||
SUCCESS_ASSERT(Status, " Committing SAM INIT transaction\n");
|
||
|
||
return(Status);
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SampCreateDatabaseProtection(
|
||
PISECURITY_DESCRIPTOR Sd
|
||
)
|
||
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function allocates and initializes protection to assign to
|
||
the SAM database.
|
||
|
||
Upon return, any non-zero pointers in the security descriptors
|
||
point to memory allocated from process heap. It is the caller's
|
||
responsibility to free this memory.
|
||
|
||
|
||
Protection is:
|
||
|
||
System: All Access
|
||
Admin: ReadControl | WriteDac
|
||
|
||
Arguments:
|
||
|
||
Sd - Address of a security descriptor to initialize.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - The Security descriptor has been initialize.
|
||
|
||
STATUS_NO_MEMORY - couldn't allocate memory for the protection info.
|
||
|
||
--*/
|
||
|
||
|
||
{
|
||
NTSTATUS
|
||
Status;
|
||
|
||
ULONG
|
||
Length;
|
||
|
||
USHORT
|
||
i;
|
||
|
||
PACL
|
||
Dacl;
|
||
|
||
PACE_HEADER
|
||
Ace;
|
||
|
||
|
||
//
|
||
// Initialize the security descriptor.
|
||
// This call should not fail.
|
||
//
|
||
|
||
Status = RtlCreateSecurityDescriptor( Sd, SECURITY_DESCRIPTOR_REVISION1 );
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
Length = (ULONG)sizeof(ACL) +
|
||
(2*((ULONG)sizeof(ACCESS_ALLOWED_ACE))) +
|
||
RtlLengthSid( LocalSystemSid ) +
|
||
RtlLengthSid( AdminsAliasSid ) +
|
||
8; // The 8 is just for good measure
|
||
|
||
|
||
Dacl = RtlAllocateHeap( RtlProcessHeap(), 0, Length );
|
||
|
||
if (Dacl == NULL) {
|
||
return(STATUS_NO_MEMORY);
|
||
}
|
||
|
||
|
||
Status = RtlCreateAcl (Dacl, Length, ACL_REVISION2 );
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
//
|
||
// Add ACEs to the ACL...
|
||
// These calls should not be able to fail.
|
||
//
|
||
|
||
Status = RtlAddAccessAllowedAce(
|
||
Dacl,
|
||
ACL_REVISION2,
|
||
(GENERIC_ALL ),
|
||
LocalSystemSid
|
||
);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
Status = RtlAddAccessAllowedAce(
|
||
Dacl,
|
||
ACL_REVISION2,
|
||
(READ_CONTROL | WRITE_DAC),
|
||
AdminsAliasSid
|
||
);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
|
||
//
|
||
// Now mark the ACEs as inheritable...
|
||
//
|
||
|
||
for ( i=0; i<Dacl->AceCount; i++) {
|
||
|
||
//
|
||
// Get the address of the next ACE
|
||
// (Shouldn't fail)
|
||
//
|
||
|
||
Status = RtlGetAce( Dacl, (ULONG)i, &Ace );
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
Ace->AceFlags |= (CONTAINER_INHERIT_ACE);
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// And add the ACL to the security descriptor.
|
||
// This call should not fail.
|
||
//
|
||
|
||
Status = RtlSetDaclSecurityDescriptor(
|
||
Sd,
|
||
TRUE, // DaclPresent
|
||
Dacl, // Dacl OPTIONAL
|
||
FALSE // DaclDefaulted OPTIONAL
|
||
);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
|
||
|
||
return(STATUS_SUCCESS);
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
CreateBuiltinDomain (
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine creates a new builtin domain.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
UNICODE_STRING Name, Comment;
|
||
HMODULE AccountNamesResource;
|
||
|
||
//
|
||
// Get the message resource we need to get the account names from
|
||
//
|
||
|
||
AccountNamesResource = (HMODULE) LoadLibrary( L"SAMSRV.DLL" );
|
||
if (AccountNamesResource == NULL) {
|
||
return(STATUS_RESOURCE_DATA_NOT_FOUND);
|
||
}
|
||
|
||
|
||
|
||
|
||
//
|
||
// Prep the standard domain registry structure for this domain
|
||
//
|
||
|
||
Status = PrepDomain(DomainBuiltin);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
return(Status);
|
||
}
|
||
|
||
//
|
||
// Create the alias accounts with no members
|
||
// (Common to LanManNT and WinNT products)
|
||
//
|
||
|
||
|
||
Status = SampGetMessageStrings(
|
||
AccountNamesResource,
|
||
SAMP_ALIAS_NAME_ADMINS,
|
||
&Name,
|
||
SAMP_ALIAS_COMMENT_ADMINS,
|
||
&Comment
|
||
); ASSERT(NT_SUCCESS(Status));
|
||
|
||
Status = CreateAlias(&Name, // AccountName
|
||
&Comment, // AccountComment
|
||
TRUE, // SpecialAccount
|
||
DOMAIN_ALIAS_RID_ADMINS, // Rid
|
||
SAMP_PROT_ADMIN_ALIAS // Protection
|
||
); ASSERT(NT_SUCCESS(Status));
|
||
LocalFree( Name.Buffer );
|
||
LocalFree( Comment.Buffer );
|
||
|
||
Status = SampGetMessageStrings(
|
||
AccountNamesResource,
|
||
SAMP_ALIAS_NAME_USERS,
|
||
&Name,
|
||
SAMP_ALIAS_COMMENT_USERS,
|
||
&Comment
|
||
); ASSERT(NT_SUCCESS(Status));
|
||
|
||
Status = CreateAlias(&Name, // AccountName
|
||
&Comment, // AccountComment
|
||
TRUE, // SpecialAccount
|
||
DOMAIN_ALIAS_RID_USERS, // Rid
|
||
SAMP_PROT_PWRUSER_ACCESSIBLE_ALIAS // Protection
|
||
); ASSERT(NT_SUCCESS(Status));
|
||
|
||
LocalFree( Name.Buffer );
|
||
LocalFree( Comment.Buffer );
|
||
|
||
Status = SampGetMessageStrings(
|
||
AccountNamesResource,
|
||
SAMP_ALIAS_NAME_GUESTS,
|
||
&Name,
|
||
SAMP_ALIAS_COMMENT_GUESTS,
|
||
&Comment
|
||
); ASSERT(NT_SUCCESS(Status));
|
||
|
||
Status = CreateAlias(&Name, // AccountName
|
||
&Comment, // AccountComment
|
||
TRUE, // SpecialAccount
|
||
DOMAIN_ALIAS_RID_GUESTS, // Rid
|
||
SAMP_PROT_PWRUSER_ACCESSIBLE_ALIAS // Protection
|
||
); ASSERT(NT_SUCCESS(Status));
|
||
|
||
LocalFree( Name.Buffer );
|
||
LocalFree( Comment.Buffer );
|
||
|
||
Status = SampGetMessageStrings(
|
||
AccountNamesResource,
|
||
SAMP_ALIAS_NAME_BACKUP_OPS,
|
||
&Name,
|
||
SAMP_ALIAS_COMMENT_BACKUP_OPS,
|
||
&Comment
|
||
); ASSERT(NT_SUCCESS(Status));
|
||
|
||
Status = CreateAlias(&Name, // AccountName
|
||
&Comment, // AccountComment
|
||
TRUE, // SpecialAccount
|
||
DOMAIN_ALIAS_RID_BACKUP_OPS, // Rid
|
||
SAMP_PROT_ADMIN_ALIAS // Protection
|
||
); ASSERT(NT_SUCCESS(Status));
|
||
|
||
LocalFree( Name.Buffer );
|
||
LocalFree( Comment.Buffer );
|
||
|
||
Status = SampGetMessageStrings(
|
||
AccountNamesResource,
|
||
SAMP_ALIAS_NAME_REPLICATOR,
|
||
&Name,
|
||
SAMP_ALIAS_COMMENT_REPLICATOR,
|
||
&Comment
|
||
); ASSERT(NT_SUCCESS(Status));
|
||
|
||
Status = CreateAlias(&Name, // AccountName
|
||
&Comment, // AccountComment
|
||
TRUE, // SpecialAccount
|
||
DOMAIN_ALIAS_RID_REPLICATOR, // Rid
|
||
SAMP_PROT_NORMAL_ALIAS // Protection
|
||
); ASSERT(NT_SUCCESS(Status));
|
||
|
||
LocalFree( Name.Buffer );
|
||
LocalFree( Comment.Buffer );
|
||
|
||
|
||
if (SampProductType == NtProductLanManNt) {
|
||
|
||
//
|
||
// specific to LanManNT products
|
||
//
|
||
|
||
Status = SampGetMessageStrings(
|
||
AccountNamesResource,
|
||
SAMP_ALIAS_NAME_SERVER_OPS,
|
||
&Name,
|
||
SAMP_ALIAS_COMMENT_SERVER_OPS,
|
||
&Comment
|
||
); ASSERT(NT_SUCCESS(Status));
|
||
|
||
Status = CreateAlias(&Name, // AccountName
|
||
&Comment, // AccountComment
|
||
TRUE, // SpecialAccount
|
||
DOMAIN_ALIAS_RID_SYSTEM_OPS, // Rid
|
||
SAMP_PROT_ADMIN_ALIAS // Protection
|
||
); ASSERT(NT_SUCCESS(Status));
|
||
|
||
LocalFree( Name.Buffer );
|
||
LocalFree( Comment.Buffer );
|
||
|
||
|
||
Status = SampGetMessageStrings(
|
||
AccountNamesResource,
|
||
SAMP_ALIAS_NAME_ACCOUNT_OPS,
|
||
&Name,
|
||
SAMP_ALIAS_COMMENT_ACCOUNT_OPS,
|
||
&Comment
|
||
); ASSERT(NT_SUCCESS(Status));
|
||
|
||
Status = CreateAlias(&Name, // AccountName
|
||
&Comment, // AccountComment
|
||
TRUE, // SpecialAccount
|
||
DOMAIN_ALIAS_RID_ACCOUNT_OPS, // Rid
|
||
SAMP_PROT_ADMIN_ALIAS // Protection
|
||
); ASSERT(NT_SUCCESS(Status));
|
||
|
||
LocalFree( Name.Buffer );
|
||
LocalFree( Comment.Buffer );
|
||
|
||
|
||
Status = SampGetMessageStrings(
|
||
AccountNamesResource,
|
||
SAMP_ALIAS_NAME_PRINT_OPS,
|
||
&Name,
|
||
SAMP_ALIAS_COMMENT_PRINT_OPS,
|
||
&Comment
|
||
); ASSERT(NT_SUCCESS(Status));
|
||
|
||
Status = CreateAlias(&Name, // AccountName
|
||
&Comment, // AccountComment
|
||
TRUE, // SpecialAccount
|
||
DOMAIN_ALIAS_RID_PRINT_OPS, // Rid
|
||
SAMP_PROT_ADMIN_ALIAS // Protection
|
||
); ASSERT(NT_SUCCESS(Status));
|
||
|
||
LocalFree( Name.Buffer );
|
||
LocalFree( Comment.Buffer );
|
||
|
||
|
||
} else {
|
||
|
||
//
|
||
// specific to WinNT products
|
||
//
|
||
|
||
Status = SampGetMessageStrings(
|
||
AccountNamesResource,
|
||
SAMP_ALIAS_NAME_POWER_USERS,
|
||
&Name,
|
||
SAMP_ALIAS_COMMENT_POWER_USERS,
|
||
&Comment
|
||
); ASSERT(NT_SUCCESS(Status));
|
||
|
||
Status = CreateAlias(&Name, // AccountName
|
||
&Comment, // AccountComment
|
||
TRUE, // SpecialAccount
|
||
DOMAIN_ALIAS_RID_POWER_USERS, // Rid
|
||
SAMP_PROT_PWRUSER_ACCESSIBLE_ALIAS // Protection
|
||
); ASSERT(NT_SUCCESS(Status));
|
||
|
||
LocalFree( Name.Buffer );
|
||
LocalFree( Comment.Buffer );
|
||
|
||
|
||
}
|
||
|
||
return(Status);
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
CreateAccountDomain (
|
||
)
|
||
/*++
|
||
|
||
|
||
Routine Description:
|
||
|
||
This routine creates a new account domain using information
|
||
from the configuration database and based upon the system's
|
||
product type.
|
||
|
||
If the product is a WinNt system, then the domain's name is
|
||
"Account". If the product is a LanManNT system, then the
|
||
domain's name is retrieved from the configuration information.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
NTSTATUS Status;
|
||
UNICODE_STRING Name, Comment;
|
||
HMODULE AccountNamesResource;
|
||
ULONG AccountControl;
|
||
ULONG PrimaryGroup;
|
||
|
||
|
||
//
|
||
// Get the message resource we need to get the account names from
|
||
//
|
||
|
||
AccountNamesResource = (HMODULE) LoadLibrary( L"SAMSRV.DLL" );
|
||
if (AccountNamesResource == NULL) {
|
||
DbgPrint("BLDSAM3: Error loading library - error is 0x%lx", GetLastError());
|
||
return(STATUS_RESOURCE_DATA_NOT_FOUND);
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// Prep the standard domain registry structure for this domain
|
||
//
|
||
|
||
Status = PrepDomain(DomainAccount);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
return(Status);
|
||
}
|
||
|
||
//
|
||
// Create the group accounts with no members
|
||
//
|
||
|
||
if ((SampProductType == NtProductWinNt) ||
|
||
(SampProductType == NtProductServer)) {
|
||
|
||
//
|
||
// WinNt systems only have one group (called 'None').
|
||
// This group has the same RID as the 'Domain Users' group.
|
||
//
|
||
|
||
Status = SampGetMessageStrings(
|
||
AccountNamesResource,
|
||
SAMP_GROUP_NAME_NONE,
|
||
&Name,
|
||
SAMP_GROUP_COMMENT_NONE,
|
||
&Comment
|
||
); ASSERT(NT_SUCCESS(Status));
|
||
|
||
Status = CreateGroup(&Name, // AccountName
|
||
&Comment, // AccountComment
|
||
TRUE, // SpecialAccount
|
||
DOMAIN_GROUP_RID_USERS, // Rid
|
||
FALSE // Admin
|
||
); ASSERT(NT_SUCCESS(Status));
|
||
|
||
LocalFree( Name.Buffer );
|
||
LocalFree( Comment.Buffer );
|
||
|
||
} else {
|
||
|
||
//
|
||
// LanManNT
|
||
//
|
||
|
||
//
|
||
// USERS global group
|
||
//
|
||
|
||
Status = SampGetMessageStrings(
|
||
AccountNamesResource,
|
||
SAMP_GROUP_NAME_USERS,
|
||
&Name,
|
||
SAMP_GROUP_COMMENT_USERS,
|
||
&Comment
|
||
); ASSERT(NT_SUCCESS(Status));
|
||
|
||
Status = CreateGroup(&Name, // AccountName
|
||
&Comment, // AccountComment
|
||
TRUE, // SpecialAccount
|
||
DOMAIN_GROUP_RID_USERS, // Rid
|
||
FALSE // Admin
|
||
); ASSERT(NT_SUCCESS(Status));
|
||
|
||
LocalFree( Name.Buffer );
|
||
LocalFree( Comment.Buffer );
|
||
|
||
//
|
||
// ADMINS global group
|
||
//
|
||
|
||
Status = SampGetMessageStrings(
|
||
AccountNamesResource,
|
||
SAMP_GROUP_NAME_ADMINS,
|
||
&Name,
|
||
SAMP_GROUP_COMMENT_ADMINS,
|
||
&Comment
|
||
); ASSERT(NT_SUCCESS(Status));
|
||
|
||
Status = CreateGroup(&Name, // AccountName
|
||
&Comment, // AccountComment
|
||
TRUE, // SpecialAccount
|
||
DOMAIN_GROUP_RID_ADMINS, // Rid
|
||
TRUE // Admin
|
||
); ASSERT(NT_SUCCESS(Status));
|
||
|
||
LocalFree( Name.Buffer );
|
||
LocalFree( Comment.Buffer );
|
||
|
||
|
||
//
|
||
// GUESTS global group
|
||
//
|
||
|
||
Status = SampGetMessageStrings(
|
||
AccountNamesResource,
|
||
SAMP_GROUP_NAME_GUESTS,
|
||
&Name,
|
||
SAMP_GROUP_COMMENT_GUESTS,
|
||
&Comment
|
||
); ASSERT(NT_SUCCESS(Status));
|
||
|
||
Status = CreateGroup(&Name, // AccountName
|
||
&Comment, // AccountComment
|
||
TRUE, // SpecialAccount
|
||
DOMAIN_GROUP_RID_GUESTS, // Rid
|
||
FALSE // Admin
|
||
); ASSERT(NT_SUCCESS(Status));
|
||
|
||
LocalFree( Name.Buffer );
|
||
LocalFree( Comment.Buffer );
|
||
|
||
}
|
||
|
||
//
|
||
// create the user accounts ...
|
||
// These are automatically added to the "Domain Users" group
|
||
// (except for Guest).
|
||
//
|
||
|
||
Status = SampGetMessageStrings(
|
||
AccountNamesResource,
|
||
SAMP_USER_NAME_ADMIN,
|
||
&Name,
|
||
SAMP_USER_COMMENT_ADMIN,
|
||
&Comment
|
||
); ASSERT(NT_SUCCESS(Status));
|
||
|
||
Status = CreateUser( &Name, // AccountName
|
||
&Comment, // AccountComment
|
||
TRUE, // SpecialAccount
|
||
DOMAIN_USER_RID_ADMIN, // UserRid
|
||
DOMAIN_GROUP_RID_USERS, // PrimaryGroup
|
||
TRUE, // Admin flag
|
||
USER_NORMAL_ACCOUNT |
|
||
USER_DONT_EXPIRE_PASSWORD, // AccountControl
|
||
SAMP_PROT_ADMIN_USER // ProtectionIndex
|
||
); ASSERT(NT_SUCCESS(Status));
|
||
|
||
LocalFree( Name.Buffer );
|
||
LocalFree( Comment.Buffer );
|
||
|
||
|
||
Status = SampGetMessageStrings(
|
||
AccountNamesResource,
|
||
SAMP_USER_NAME_GUEST,
|
||
&Name,
|
||
SAMP_USER_COMMENT_GUEST,
|
||
&Comment
|
||
); ASSERT(NT_SUCCESS(Status));
|
||
|
||
//
|
||
// Disable user account on all NT systems
|
||
//
|
||
|
||
AccountControl = USER_NORMAL_ACCOUNT |
|
||
USER_DONT_EXPIRE_PASSWORD |
|
||
USER_ACCOUNT_DISABLED;
|
||
|
||
if (SampProductType == NtProductLanManNt) {
|
||
|
||
//
|
||
// Guest group is in GUESTS global group for LmNT systems.
|
||
//
|
||
|
||
PrimaryGroup = DOMAIN_GROUP_RID_GUESTS;
|
||
|
||
} else {
|
||
|
||
//
|
||
// There isn't a GUESTS global group on WinNt systems.
|
||
// Put the guest in the NONE group (same as USERS group).
|
||
//
|
||
|
||
PrimaryGroup = DOMAIN_GROUP_RID_USERS;
|
||
|
||
}
|
||
|
||
|
||
Status = CreateUser( &Name, // AccountName
|
||
&Comment, // AccountComment
|
||
TRUE, // SpecialAccount
|
||
DOMAIN_USER_RID_GUEST, // UserRid
|
||
PrimaryGroup, // PrimaryGroup
|
||
FALSE, // Admin flag
|
||
AccountControl, // AccountControl
|
||
SAMP_PROT_GUEST_ACCOUNT // ProtectionIndex
|
||
); ASSERT(NT_SUCCESS(Status));
|
||
|
||
LocalFree( Name.Buffer );
|
||
LocalFree( Comment.Buffer );
|
||
|
||
return(Status);
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
PrepDomain(
|
||
IN SAMP_DOMAIN_SELECTOR Domain
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine adds the domain level definitions to the operation log.
|
||
|
||
Arguments:
|
||
|
||
Domain - Indicates which domain is being prep'd
|
||
|
||
Return Value:
|
||
|
||
TBS
|
||
|
||
--*/
|
||
|
||
{
|
||
PSAMP_V1_0A_FIXED_LENGTH_DOMAIN DomainFixedAttributes;
|
||
PSAMP_VARIABLE_LENGTH_ATTRIBUTE DomainVariableAttributeArray;
|
||
PSAMP_VARIABLE_LENGTH_ATTRIBUTE DomainVariableAttributeArrayStart;
|
||
PVOID DomainVariableData;
|
||
ULONG DomainAttributeLength;
|
||
ULONG ProtectionIndex;
|
||
ULONG UserCount, GroupCount, AliasCount;
|
||
|
||
//
|
||
// Set current domain
|
||
//
|
||
|
||
SetCurrentDomain( Domain );
|
||
|
||
//
|
||
// Select correct protection, and the number of accounts we're going
|
||
// to create
|
||
//
|
||
|
||
if (Domain == DomainBuiltin) {
|
||
|
||
ProtectionIndex = SAMP_PROT_BUILTIN_DOMAIN;
|
||
|
||
UserCount = 0;
|
||
GroupCount = 0;
|
||
|
||
if (SampProductType == NtProductLanManNt) {
|
||
|
||
//
|
||
// Admins, BackupOps, Guests, Replicator, Users, SysOps,
|
||
// AcctOps, PrintOps
|
||
//
|
||
|
||
AliasCount = 8;
|
||
|
||
} else {
|
||
//
|
||
// Admins, BackupOps, Guests, Replicator, Users, Power Users
|
||
//
|
||
|
||
AliasCount = 6;
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
ProtectionIndex = SAMP_PROT_ACCOUNT_DOMAIN;
|
||
|
||
AliasCount = 0;
|
||
UserCount = 2; // Administrator, Guest
|
||
|
||
if (SampProductType == NtProductLanManNt) {
|
||
|
||
GroupCount = 3; // Users, Administrators, Guests
|
||
|
||
} else {
|
||
|
||
GroupCount = 1; // "None"
|
||
}
|
||
}
|
||
|
||
//
|
||
// Use a transaction.
|
||
//
|
||
|
||
Status = RtlStartRXact( SamRXactContext );
|
||
SUCCESS_ASSERT(Status, " Starting transaction\n");
|
||
|
||
//
|
||
// Create SAM\Domains\(DomainName) (KeyValueType is revision level)
|
||
//
|
||
|
||
RtlCopyUnicodeString( &KeyNameU, FullDomainNameU );
|
||
|
||
//
|
||
// Set the domain's fixed and variable attributes.
|
||
//
|
||
|
||
DomainFixedAttributes = (PSAMP_V1_0A_FIXED_LENGTH_DOMAIN)RtlAllocateHeap(
|
||
RtlProcessHeap(), 0,
|
||
sizeof( SAMP_V1_0A_FIXED_LENGTH_DOMAIN )
|
||
);
|
||
|
||
if ( DomainFixedAttributes == NULL ) {
|
||
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
SUCCESS_ASSERT(Status, " Failed to create domain fixed attributes\n");
|
||
|
||
DomainFixedAttributes->Revision = SAMP_REVISION;
|
||
DomainFixedAttributes->MinPasswordLength = 0;
|
||
DomainFixedAttributes->PasswordHistoryLength = 0;
|
||
DomainFixedAttributes->PasswordProperties = 0L;
|
||
DomainFixedAttributes->NextRid = 1000;
|
||
DomainFixedAttributes->ServerState = DomainServerEnabled;
|
||
DomainFixedAttributes->ServerRole = SampServerRole;
|
||
NtQuerySystemTime( &(DomainFixedAttributes->CreationTime) );
|
||
DomainFixedAttributes->ModifiedCount = ModifiedCount;
|
||
DomainFixedAttributes->MaxPasswordAge = DomainMaxPasswordAge;
|
||
DomainFixedAttributes->MinPasswordAge = SampImmediatelyDeltaTime;
|
||
DomainFixedAttributes->ForceLogoff = SampNeverDeltaTime;
|
||
DomainFixedAttributes->UasCompatibilityRequired = TRUE;
|
||
DomainFixedAttributes->LockoutDuration.LowPart = 0xCF1DCC00; // 30 minutes - low part
|
||
DomainFixedAttributes->LockoutDuration.HighPart = 0XFFFFFFFB; // 30 minutes - high part
|
||
DomainFixedAttributes->LockoutObservationWindow.LowPart = 0xCF1DCC00; // 30 minutes - low part
|
||
DomainFixedAttributes->LockoutObservationWindow.HighPart = 0XFFFFFFFB; // 30 minutes - high part
|
||
DomainFixedAttributes->LockoutThreshold = 0; // Disabled
|
||
DomainFixedAttributes->ModifiedCountAtLastPromotion = ModifiedCount;
|
||
|
||
DomainAttributeLength = SampDwordAlignUlong(RtlLengthSid( DomainSid ) ) +
|
||
( SAMP_DOMAIN_VARIABLE_ATTRIBUTES *
|
||
sizeof( SAMP_VARIABLE_LENGTH_ATTRIBUTE ) ) +
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length);
|
||
|
||
DomainVariableAttributeArrayStart = (PSAMP_VARIABLE_LENGTH_ATTRIBUTE)
|
||
RtlAllocateHeap(
|
||
RtlProcessHeap(), 0,
|
||
DomainAttributeLength
|
||
);
|
||
|
||
if ( DomainVariableAttributeArrayStart == NULL ) {
|
||
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
SUCCESS_ASSERT(Status, " Failed to create domain variable attributes\n");
|
||
|
||
DomainVariableAttributeArray = DomainVariableAttributeArrayStart;
|
||
|
||
DomainVariableAttributeArray->Offset = 0;
|
||
DomainVariableAttributeArray->Length =
|
||
SampProtection[ProtectionIndex].Length;
|
||
DomainVariableAttributeArray->Qualifier = SAMP_REVISION;
|
||
|
||
DomainVariableAttributeArray++;
|
||
|
||
DomainVariableAttributeArray->Offset =
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length);
|
||
DomainVariableAttributeArray->Length =
|
||
RtlLengthSid( DomainSid );
|
||
DomainVariableAttributeArray->Qualifier = 0;
|
||
|
||
DomainVariableAttributeArray++;
|
||
|
||
DomainVariableAttributeArray->Offset =
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) +
|
||
SampDwordAlignUlong(RtlLengthSid( DomainSid ));
|
||
DomainVariableAttributeArray->Length = 0;
|
||
DomainVariableAttributeArray->Qualifier = 0;
|
||
|
||
DomainVariableAttributeArray++;
|
||
|
||
DomainVariableAttributeArray->Offset =
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) +
|
||
SampDwordAlignUlong(RtlLengthSid( DomainSid ));
|
||
DomainVariableAttributeArray->Length = 0;
|
||
DomainVariableAttributeArray->Qualifier = 0;
|
||
|
||
DomainVariableData = (PVOID)( (PUCHAR)(DomainVariableAttributeArray) +
|
||
sizeof( SAMP_VARIABLE_LENGTH_ATTRIBUTE ) );
|
||
|
||
RtlCopyMemory(
|
||
DomainVariableData,
|
||
SampProtection[ProtectionIndex].Descriptor,
|
||
SampProtection[ProtectionIndex].Length
|
||
);
|
||
|
||
RtlCopyMemory(
|
||
(PVOID)((PUCHAR)(DomainVariableData) +
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length)),
|
||
DomainSid,
|
||
RtlLengthSid( DomainSid )
|
||
);
|
||
|
||
//
|
||
// Now write out the attributes via the RXACT.
|
||
//
|
||
|
||
Status = RtlAddAttributeActionToRXact(
|
||
SamRXactContext,
|
||
RtlRXactOperationSetValue,
|
||
&KeyNameU,
|
||
INVALID_HANDLE_VALUE,
|
||
&SampFixedAttributeName,
|
||
REG_BINARY,
|
||
(PVOID)DomainFixedAttributes,
|
||
sizeof( SAMP_V1_0A_FIXED_LENGTH_DOMAIN )
|
||
);
|
||
|
||
SUCCESS_ASSERT(Status, " Failed to write out domain fixed attributes\n" );
|
||
RtlFreeHeap( RtlProcessHeap(), 0, DomainFixedAttributes );
|
||
|
||
Status = RtlAddAttributeActionToRXact(
|
||
SamRXactContext,
|
||
RtlRXactOperationSetValue,
|
||
&KeyNameU,
|
||
INVALID_HANDLE_VALUE,
|
||
&SampVariableAttributeName,
|
||
REG_BINARY,
|
||
(PVOID)DomainVariableAttributeArrayStart,
|
||
DomainAttributeLength
|
||
);
|
||
|
||
RtlFreeHeap( RtlProcessHeap(), 0, DomainVariableAttributeArrayStart );
|
||
SUCCESS_ASSERT(Status, " Failed to write out domain variable attributes\n" );
|
||
|
||
//
|
||
// Create SAM\Domains\(DomainName)\Users
|
||
//
|
||
|
||
RtlCopyUnicodeString( &KeyNameU, FullDomainNameU );
|
||
Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Users" );
|
||
SUCCESS_ASSERT(Status, " Failed to append to unicode: \\Users\n" );
|
||
|
||
Status = RtlAddActionToRXact(
|
||
SamRXactContext,
|
||
RtlRXactOperationSetValue,
|
||
&KeyNameU,
|
||
UserCount,
|
||
NULL,
|
||
0
|
||
);
|
||
|
||
SUCCESS_ASSERT(Status, " Failed to add Users key to log\n");
|
||
|
||
//
|
||
// Create SAM\Domains\(DomainName)\Users\Names
|
||
//
|
||
|
||
RtlCopyUnicodeString( &KeyNameU, FullDomainNameU );
|
||
Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Users\\Names" );
|
||
SUCCESS_ASSERT(Status, " Failed to append to unicode: \\Users\\Names\n" );
|
||
Status = RtlAddActionToRXact(
|
||
SamRXactContext,
|
||
RtlRXactOperationSetValue,
|
||
&KeyNameU,
|
||
0,
|
||
NULL,
|
||
0
|
||
);
|
||
SUCCESS_ASSERT(Status, " Failed to add Users/Names key to log\n");
|
||
|
||
//
|
||
// Create SAM\Domains\(DomainName)\Groups
|
||
//
|
||
|
||
RtlCopyUnicodeString( &KeyNameU, FullDomainNameU );
|
||
Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Groups" );
|
||
SUCCESS_ASSERT(Status, " Failed to append Groups key name to unicode\n" );
|
||
Status = RtlAddActionToRXact(
|
||
SamRXactContext,
|
||
RtlRXactOperationSetValue,
|
||
&KeyNameU,
|
||
GroupCount,
|
||
NULL,
|
||
0
|
||
);
|
||
SUCCESS_ASSERT(Status, " Failed to add Groups key to log\n");
|
||
|
||
//
|
||
// Create SAM\Domains\(DomainName)\Groups\Names
|
||
//
|
||
|
||
RtlCopyUnicodeString( &KeyNameU, FullDomainNameU );
|
||
Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Groups\\Names" );
|
||
SUCCESS_ASSERT(Status, " Failed to append Groups key name to unicode\n" );
|
||
Status = RtlAddActionToRXact(
|
||
SamRXactContext,
|
||
RtlRXactOperationSetValue,
|
||
&KeyNameU,
|
||
0,
|
||
NULL,
|
||
0
|
||
);
|
||
SUCCESS_ASSERT(Status, " Failed to add Groups key to log\n");
|
||
|
||
//
|
||
// Create SAM\Domains\(DomainName)\Aliases
|
||
//
|
||
|
||
RtlCopyUnicodeString( &KeyNameU, FullDomainNameU );
|
||
Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Aliases" );
|
||
SUCCESS_ASSERT(Status, " Failed to append Aliases key name to unicode\n" );
|
||
Status = RtlAddActionToRXact(
|
||
SamRXactContext,
|
||
RtlRXactOperationSetValue,
|
||
&KeyNameU,
|
||
AliasCount,
|
||
NULL,
|
||
0
|
||
);
|
||
SUCCESS_ASSERT(Status, " Failed to add aliases key to log\n");
|
||
|
||
//
|
||
// Create SAM\Domains\(DomainName)\Aliases\Names
|
||
//
|
||
|
||
RtlCopyUnicodeString( &KeyNameU, FullDomainNameU );
|
||
Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Aliases\\Names" );
|
||
SUCCESS_ASSERT(Status, " Failed to append Aliases key name to unicode\n" );
|
||
Status = RtlAddActionToRXact(
|
||
SamRXactContext,
|
||
RtlRXactOperationSetValue,
|
||
&KeyNameU,
|
||
0,
|
||
NULL,
|
||
0
|
||
);
|
||
SUCCESS_ASSERT(Status, " Failed to add Aliases\\Names key to log\n");
|
||
|
||
//
|
||
// Create SAM\Domains\(DomainName)\Aliases\Members
|
||
//
|
||
|
||
RtlCopyUnicodeString( &KeyNameU, FullDomainNameU );
|
||
Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Aliases\\Members" );
|
||
SUCCESS_ASSERT(Status, " Failed to append Aliases\\Members key name to unicode\n" );
|
||
Status = RtlAddActionToRXact(
|
||
SamRXactContext,
|
||
RtlRXactOperationSetValue,
|
||
&KeyNameU,
|
||
0, // Domain Count
|
||
NULL,
|
||
0
|
||
);
|
||
SUCCESS_ASSERT(Status, " Failed to add Aliases\\Members key to log\n");
|
||
|
||
//
|
||
// Commit these additions...
|
||
//
|
||
|
||
Status = RtlApplyRXactNoFlush( SamRXactContext );
|
||
SUCCESS_ASSERT(Status, " Failed to commit domain initialization.\n");
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
CreateAlias(
|
||
IN PUNICODE_STRING AccountNameU,
|
||
IN PUNICODE_STRING AccountCommentU,
|
||
IN BOOLEAN SpecialAccount,
|
||
IN ULONG Rid,
|
||
IN ULONG ProtectionIndex
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine adds the keys necessary to create an alias. It also applies
|
||
the appropriate protection to the alias.
|
||
|
||
Arguments:
|
||
|
||
AccountNameU - The Unicode name of the Alias.
|
||
|
||
AccountCommentU - A Unicode comment to put in the object's variable data.
|
||
|
||
SpecialAccount - A boolean indicating whether or not the account
|
||
is special. Special accounts are marked as such and can not
|
||
be deleted.
|
||
|
||
Rid - The RID of the account.
|
||
|
||
|
||
Admin - Indicates whether the account is in the Administrators alias
|
||
or not. TRUE means it is, FALSE means it isn't.
|
||
|
||
Return Value:
|
||
|
||
TBS
|
||
|
||
--*/
|
||
|
||
{
|
||
PSAMP_V1_FIXED_LENGTH_ALIAS AliasFixedAttributes;
|
||
PSAMP_VARIABLE_LENGTH_ATTRIBUTE AliasVariableAttributeArray;
|
||
PVOID AliasVariableData;
|
||
PSID Sid1, Sid2;
|
||
PSID AliasMembers = NULL;
|
||
ULONG MemberCount, TotalLength, AliasAttributeLength;
|
||
UNICODE_STRING AliasNameU, AliasCommentU;
|
||
|
||
AliasNameU = *AccountNameU;
|
||
AliasCommentU = *AccountCommentU;
|
||
|
||
//
|
||
// Set the account specific RID in the DACL's if necessary
|
||
//
|
||
|
||
if ( SampProtection[ProtectionIndex].RidReplacementRequired == TRUE ) {
|
||
|
||
(*SampProtection[ProtectionIndex].RidToReplace) = Rid;
|
||
}
|
||
|
||
//
|
||
// Use a transaction.
|
||
//
|
||
|
||
Status = RtlStartRXact( SamRXactContext );
|
||
SUCCESS_ASSERT(Status, " Failed to start Alias addition transaction\n");
|
||
|
||
//
|
||
// Add Aliases\Names\(AccountName) [ Rid, ]
|
||
//
|
||
|
||
RtlCopyUnicodeString( &KeyNameU, FullDomainNameU );
|
||
Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Aliases\\Names\\" );
|
||
SUCCESS_ASSERT(Status, " Failed to append \\aliases\\names to keyname\n");
|
||
Status = RtlAppendUnicodeStringToString( &KeyNameU, &AliasNameU);
|
||
SUCCESS_ASSERT(Status, " Failed to append Alias account name to\n");
|
||
Status = RtlAddActionToRXact(
|
||
SamRXactContext,
|
||
RtlRXactOperationSetValue,
|
||
&KeyNameU,
|
||
Rid,
|
||
NULL,
|
||
0
|
||
);
|
||
SUCCESS_ASSERT(Status, " Failed to add Aliases\\Names\\(AliasName) to log\n");
|
||
|
||
|
||
//
|
||
// Set the Members attribute. We know which accounts are supposed
|
||
// to be members of which aliases, so we'll build the memberships in
|
||
// automatically.
|
||
//
|
||
// Each domain has a list of SIDs that are members of its aliases.
|
||
// We'll update these values at the same time that we set the alias
|
||
// members by calling UpdateAliasXReference(). Currently, that only
|
||
// happens in the builtin domain, where things look like this:
|
||
//
|
||
// BuiltinDomainSid
|
||
// AdminUserRid - Admins alias (WinNt + primary domain)
|
||
// UserUserRid - Users alias (WinNt + developer setup),
|
||
// Power users alias (WinNt + developer setup)
|
||
// AccountDomainSid
|
||
// AdminUserRid - Admins alias (always)
|
||
// GuestUserRid - Guests alias (always)
|
||
// UserGroupRid - Users alias, (always)
|
||
// Power users alias (WinNt + developer setup)
|
||
// AdminGroupRid - Admins alias (LanManNt only)
|
||
//
|
||
|
||
MemberCount = 0;
|
||
TotalLength = 0;
|
||
|
||
switch ( Rid ) {
|
||
|
||
case DOMAIN_ALIAS_RID_ADMINS: {
|
||
|
||
MemberCount = 1;
|
||
|
||
Sid1 = BuildAccountSid( DomainAccount, DOMAIN_USER_RID_ADMIN );
|
||
if ( Sid1 == NULL ) {
|
||
SUCCESS_ASSERT( STATUS_INSUFFICIENT_RESOURCES, " Could not allocate Sid\n" );
|
||
}
|
||
|
||
if ( SampProductType == NtProductLanManNt ) {
|
||
|
||
MemberCount = 2;
|
||
|
||
Sid2 = BuildAccountSid( DomainAccount, DOMAIN_GROUP_RID_ADMINS );
|
||
if ( Sid2 == NULL ) {
|
||
SUCCESS_ASSERT( STATUS_INSUFFICIENT_RESOURCES, " Could not allocate Sid\n" );
|
||
}
|
||
}
|
||
|
||
if ( ( SampProductType != NtProductLanManNt ) &&
|
||
( SampBldPrimaryDomain != NULL ) ) {
|
||
|
||
MemberCount = 2;
|
||
|
||
Sid2 = BuildPrimaryDomainSid( DOMAIN_GROUP_RID_ADMINS );
|
||
if ( Sid2 == NULL ) {
|
||
SUCCESS_ASSERT( STATUS_INSUFFICIENT_RESOURCES, " Could not allocate Sid\n" );
|
||
}
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
case DOMAIN_ALIAS_RID_USERS: {
|
||
|
||
MemberCount = 0;
|
||
|
||
if ( (SampProductType == NtProductWinNt)
|
||
|| (SampProductType == NtProductServer) ) {
|
||
|
||
if ( SampBldPrimaryDomain != NULL ) {
|
||
|
||
MemberCount = 1;
|
||
Sid1 = BuildPrimaryDomainSid( DOMAIN_GROUP_RID_USERS );
|
||
|
||
if ( Sid1 == NULL ) {
|
||
|
||
SUCCESS_ASSERT( STATUS_INSUFFICIENT_RESOURCES, " Could not allocate Sid\n" );
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
//
|
||
if (SampProductType == NtProductLanManNt ) {
|
||
|
||
//
|
||
// NTAS systems have the USERS global group in
|
||
// the USERS alias.
|
||
//
|
||
|
||
MemberCount = 1;
|
||
Sid1 = BuildAccountSid(
|
||
DomainAccount,
|
||
DOMAIN_GROUP_RID_USERS
|
||
);
|
||
|
||
if ( Sid1 == NULL ) {
|
||
SUCCESS_ASSERT( STATUS_INSUFFICIENT_RESOURCES, " Could not allocate Sid\n" );
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// WinNT systems have the ADMINISTRATOR user account
|
||
// in the USERS alias. The None group is NOT in this
|
||
// alias because even guests are in the None group.
|
||
//
|
||
|
||
MemberCount = 1;
|
||
Sid1 = BuildAccountSid(
|
||
DomainAccount,
|
||
DOMAIN_USER_RID_ADMIN
|
||
);
|
||
|
||
if ( Sid1 == NULL ) {
|
||
SUCCESS_ASSERT( STATUS_INSUFFICIENT_RESOURCES, " Could not allocate Sid\n" );
|
||
}
|
||
}
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
case DOMAIN_ALIAS_RID_GUESTS: {
|
||
|
||
|
||
if ( (SampProductType == NtProductWinNt)
|
||
|| (SampProductType == NtProductServer) ) {
|
||
|
||
//
|
||
// WinNT system - make our GUEST user account a member of
|
||
// the GUESTS alias.
|
||
//
|
||
|
||
MemberCount = 1;
|
||
Sid1 = BuildAccountSid( DomainAccount, DOMAIN_USER_RID_GUEST );
|
||
|
||
|
||
//
|
||
// If we are in a primary domain, then add that domain's
|
||
// GUESTS global group to the alias as well.
|
||
//
|
||
|
||
if ( SampBldPrimaryDomain != NULL ) {
|
||
|
||
MemberCount += 1;
|
||
Sid2 = BuildPrimaryDomainSid( DOMAIN_GROUP_RID_GUESTS );
|
||
if ( Sid2 == NULL ) {
|
||
SUCCESS_ASSERT( STATUS_INSUFFICIENT_RESOURCES, " Could not allocate Sid\n" );
|
||
}
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// NTAS System - Just make the GUESTS global group
|
||
// a member of the GUESTS alias.
|
||
//
|
||
|
||
MemberCount = 1;
|
||
Sid1 = BuildPrimaryDomainSid( DOMAIN_GROUP_RID_GUESTS );
|
||
if ( Sid1 == NULL ) {
|
||
SUCCESS_ASSERT( STATUS_INSUFFICIENT_RESOURCES, " Could not allocate Sid\n" );
|
||
}
|
||
}
|
||
|
||
|
||
break;
|
||
}
|
||
|
||
case DOMAIN_ALIAS_RID_POWER_USERS: {
|
||
|
||
if ( ( SampProductType != NtProductLanManNt ) &&
|
||
( SampDeveloperSetup ) ) {
|
||
|
||
MemberCount = 1;
|
||
|
||
Sid1 = BuildAccountSid( DomainAccount, DOMAIN_GROUP_RID_USERS );
|
||
|
||
if ( Sid1 == NULL ) {
|
||
SUCCESS_ASSERT( STATUS_INSUFFICIENT_RESOURCES, " Could not allocate Sid\n" );
|
||
}
|
||
|
||
if ( SampBldPrimaryDomain != NULL ) {
|
||
|
||
MemberCount = 2;
|
||
|
||
Sid2 = BuildPrimaryDomainSid( DOMAIN_GROUP_RID_USERS );
|
||
|
||
if ( Sid2 == NULL ) {
|
||
|
||
SUCCESS_ASSERT( STATUS_INSUFFICIENT_RESOURCES, " Could not allocate Sid\n" );
|
||
}
|
||
}
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
case DOMAIN_ALIAS_RID_ACCOUNT_OPS:
|
||
case DOMAIN_ALIAS_RID_SYSTEM_OPS:
|
||
case DOMAIN_ALIAS_RID_PRINT_OPS:
|
||
case DOMAIN_ALIAS_RID_BACKUP_OPS:
|
||
case DOMAIN_ALIAS_RID_REPLICATOR: {
|
||
|
||
break;
|
||
}
|
||
|
||
default: {
|
||
|
||
SUCCESS_ASSERT(STATUS_UNSUCCESSFUL, " Bad Alias RID\n");
|
||
break;
|
||
}
|
||
};
|
||
|
||
if ( MemberCount > 0 ) {
|
||
|
||
TotalLength = RtlLengthSid( Sid1 );
|
||
if ( MemberCount == 2 ) {
|
||
|
||
TotalLength += RtlLengthSid( Sid2 );
|
||
}
|
||
|
||
AliasMembers = RtlAllocateHeap( RtlProcessHeap(), 0, TotalLength );
|
||
if ( AliasMembers == NULL ) {
|
||
SUCCESS_ASSERT( STATUS_INSUFFICIENT_RESOURCES, " Could not allocate AliasMembers\n" );
|
||
}
|
||
|
||
Status = RtlCopySid( RtlLengthSid( Sid1 ), AliasMembers, Sid1 );
|
||
SUCCESS_ASSERT( Status, " Couldn't copy Sid1\n" );
|
||
|
||
Status = UpdateAliasXReference( Rid, Sid1 );
|
||
SUCCESS_ASSERT( Status, " Couldn't update alias xref\n" );
|
||
|
||
if ( MemberCount == 2 ) {
|
||
|
||
Status = RtlCopySid(
|
||
RtlLengthSid( Sid2 ),
|
||
(PSID)((PUCHAR)AliasMembers + RtlLengthSid( Sid1 ) ),
|
||
Sid2 );
|
||
SUCCESS_ASSERT( Status, " Couldn't copy Sid2\n" );
|
||
|
||
Status = UpdateAliasXReference( Rid, Sid2 );
|
||
RtlFreeHeap( RtlProcessHeap(), 0, Sid2 );
|
||
SUCCESS_ASSERT( Status, " Couldn't update alias xref\n" );
|
||
}
|
||
|
||
RtlFreeHeap( RtlProcessHeap(), 0, Sid1 );
|
||
}
|
||
|
||
|
||
//
|
||
// Set the alias's fixed and variable attributes
|
||
//
|
||
|
||
AliasAttributeLength = sizeof( SAMP_V1_FIXED_LENGTH_ALIAS ) +
|
||
( SAMP_ALIAS_VARIABLE_ATTRIBUTES *
|
||
sizeof( SAMP_VARIABLE_LENGTH_ATTRIBUTE ) ) +
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) +
|
||
SampDwordAlignUlong(AccountNameU->Length) +
|
||
SampDwordAlignUlong(AccountCommentU->Length) +
|
||
TotalLength;
|
||
|
||
AliasFixedAttributes = (PSAMP_V1_FIXED_LENGTH_ALIAS)RtlAllocateHeap(
|
||
RtlProcessHeap(), 0,
|
||
AliasAttributeLength
|
||
);
|
||
|
||
if ( AliasFixedAttributes == NULL ) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
SUCCESS_ASSERT(Status, " Failed to create alias attributes\n");
|
||
|
||
AliasFixedAttributes->RelativeId = Rid;
|
||
|
||
AliasVariableAttributeArray = (PSAMP_VARIABLE_LENGTH_ATTRIBUTE)
|
||
((PUCHAR)(AliasFixedAttributes) +
|
||
sizeof( SAMP_V1_FIXED_LENGTH_ALIAS ) );
|
||
|
||
AliasVariableAttributeArray->Offset = 0;
|
||
AliasVariableAttributeArray->Length =
|
||
SampProtection[ProtectionIndex].Length;
|
||
AliasVariableAttributeArray->Qualifier = SAMP_REVISION;
|
||
|
||
AliasVariableAttributeArray++;
|
||
|
||
AliasVariableAttributeArray->Offset =
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length);
|
||
AliasVariableAttributeArray->Length = AliasNameU.Length;
|
||
AliasVariableAttributeArray->Qualifier = 0;
|
||
|
||
AliasVariableAttributeArray++;
|
||
|
||
AliasVariableAttributeArray->Offset =
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) +
|
||
SampDwordAlignUlong(AccountNameU->Length);
|
||
AliasVariableAttributeArray->Length = AliasCommentU.Length;
|
||
AliasVariableAttributeArray->Qualifier = 0;
|
||
|
||
AliasVariableAttributeArray++;
|
||
|
||
AliasVariableAttributeArray->Offset =
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) +
|
||
SampDwordAlignUlong(AliasNameU.Length) +
|
||
SampDwordAlignUlong(AliasCommentU.Length);
|
||
AliasVariableAttributeArray->Length = TotalLength;
|
||
AliasVariableAttributeArray->Qualifier = MemberCount;
|
||
|
||
AliasVariableData = (PVOID)( (PUCHAR)(AliasVariableAttributeArray) +
|
||
sizeof( SAMP_VARIABLE_LENGTH_ATTRIBUTE ) );
|
||
|
||
RtlCopyMemory(
|
||
AliasVariableData,
|
||
SampProtection[ProtectionIndex].Descriptor,
|
||
SampProtection[ProtectionIndex].Length
|
||
);
|
||
|
||
RtlCopyMemory(
|
||
(PVOID)((PUCHAR)(AliasVariableData) +
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length)),
|
||
AccountNameU->Buffer,
|
||
AccountNameU->Length
|
||
);
|
||
|
||
RtlCopyMemory(
|
||
(PVOID)((PUCHAR)(AliasVariableData) +
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) +
|
||
SampDwordAlignUlong(AliasNameU.Length)),
|
||
AccountCommentU->Buffer,
|
||
AccountCommentU->Length
|
||
);
|
||
|
||
RtlCopyMemory(
|
||
(PVOID)((PUCHAR)(AliasVariableData) +
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) +
|
||
SampDwordAlignUlong(AliasNameU.Length) +
|
||
SampDwordAlignUlong(AliasCommentU.Length)),
|
||
AliasMembers,
|
||
TotalLength
|
||
);
|
||
|
||
if ( AliasMembers != NULL ) {
|
||
|
||
RtlFreeHeap( RtlProcessHeap(), 0, AliasMembers );
|
||
}
|
||
|
||
//
|
||
// Create Aliases\(AliasRid) [Revision,] key
|
||
//
|
||
|
||
RtlCopyUnicodeString( &KeyNameU, FullDomainNameU );
|
||
Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Aliases\\" );
|
||
SUCCESS_ASSERT(Status, " Failed to append \\aliases\\ to keyname\n");
|
||
|
||
//
|
||
// Convert the Rid to a Unicode String with leading zero's
|
||
//
|
||
|
||
Status = SampRtlConvertUlongToUnicodeString(
|
||
Rid,
|
||
16,
|
||
8,
|
||
FALSE,
|
||
&KeyNameU
|
||
);
|
||
|
||
SUCCESS_ASSERT(Status, " CreateAlias' SampRtlConvertUlongToUnicodeString failed\n");
|
||
|
||
//
|
||
// Now write out the attributes via the RXACT.
|
||
//
|
||
|
||
Status = RtlAddAttributeActionToRXact(
|
||
SamRXactContext,
|
||
RtlRXactOperationSetValue,
|
||
&KeyNameU,
|
||
INVALID_HANDLE_VALUE,
|
||
&SampCombinedAttributeName,
|
||
REG_BINARY,
|
||
(PVOID)AliasFixedAttributes,
|
||
AliasAttributeLength
|
||
);
|
||
SUCCESS_ASSERT(Status, " Failed to write out alias attributes\n" );
|
||
|
||
RtlFreeHeap( RtlProcessHeap(), 0, AliasFixedAttributes );
|
||
|
||
//
|
||
// Commit these additions...
|
||
//
|
||
|
||
Status = RtlApplyRXactNoFlush( SamRXactContext );
|
||
SUCCESS_ASSERT(Status, " Failed to commit Alias addition.\n");
|
||
|
||
return Status;
|
||
DBG_UNREFERENCED_PARAMETER(SpecialAccount);
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
CreateGroup(
|
||
IN PUNICODE_STRING AccountNameU,
|
||
IN PUNICODE_STRING AccountCommentU,
|
||
IN BOOLEAN SpecialAccount,
|
||
IN ULONG Rid,
|
||
IN BOOLEAN Admin
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine adds the keys necessary to create a group. It also applies
|
||
the appropriate protection to the group.
|
||
|
||
Arguments:
|
||
|
||
AccountNameU - The Unicode name of the group.
|
||
|
||
AccountCommentU - A Unicode comment to put in the object's variable data.
|
||
|
||
SpecialAccount - A boolean indicating whether or not the account
|
||
is special. Special accounts are marked as such and can not
|
||
be deleted.
|
||
|
||
Rid - The RID of the account.
|
||
|
||
Admin - Indicates whether the account is in the Administrators alias
|
||
or not. TRUE means it is, FALSE means it isn't.
|
||
|
||
Return Value:
|
||
|
||
TBS
|
||
|
||
--*/
|
||
|
||
{
|
||
PSAMP_V1_0A_FIXED_LENGTH_GROUP GroupFixedAttributes;
|
||
PSAMP_VARIABLE_LENGTH_ATTRIBUTE GroupVariableAttributeArray;
|
||
PVOID GroupVariableData;
|
||
|
||
ULONG Attributes, ProtectionIndex, GroupCount, GroupAttributeLength;
|
||
ULONG GroupMembers[2];
|
||
|
||
UNICODE_STRING GroupNameU, GroupCommentU;
|
||
|
||
GroupNameU = *AccountNameU;
|
||
GroupCommentU = *AccountCommentU;
|
||
|
||
Attributes = (SE_GROUP_MANDATORY |
|
||
SE_GROUP_ENABLED_BY_DEFAULT |
|
||
SE_GROUP_ENABLED);
|
||
|
||
//
|
||
// Set the correct protection.
|
||
//
|
||
|
||
if (Admin == TRUE) {
|
||
|
||
ProtectionIndex = SAMP_PROT_ADMIN_GROUP;
|
||
|
||
} else {
|
||
|
||
ProtectionIndex = SAMP_PROT_NORMAL_GROUP;
|
||
}
|
||
|
||
//
|
||
// Set the account specific RID in the DACL's if necessary
|
||
//
|
||
|
||
if ( SampProtection[ProtectionIndex].RidReplacementRequired == TRUE ) {
|
||
|
||
(*SampProtection[ProtectionIndex].RidToReplace) = Rid;
|
||
}
|
||
|
||
//
|
||
// Use a transaction
|
||
//
|
||
|
||
Status = RtlStartRXact( SamRXactContext );
|
||
SUCCESS_ASSERT(Status, " Failed to start group addition transaction\n");
|
||
|
||
//
|
||
// Add Groups\Names\(GroupName) [ Rid, ]
|
||
//
|
||
|
||
RtlCopyUnicodeString( &KeyNameU, FullDomainNameU );
|
||
Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Groups\\Names\\" );
|
||
SUCCESS_ASSERT(Status, " Failed to append \\Groups\\Names\n");
|
||
Status = RtlAppendUnicodeStringToString( &KeyNameU, AccountNameU);
|
||
SUCCESS_ASSERT(Status, " Failed to append AccountName\n");
|
||
|
||
Status = RtlAddActionToRXact(
|
||
SamRXactContext,
|
||
RtlRXactOperationSetValue,
|
||
&KeyNameU,
|
||
Rid,
|
||
NULL,
|
||
0
|
||
);
|
||
|
||
SUCCESS_ASSERT(Status, " Failed to add Groups\\Names\\(GroupName) to log\n");
|
||
|
||
//
|
||
// Create Groups\(GroupRid) [Revision,] key
|
||
//
|
||
|
||
RtlCopyUnicodeString( &KeyNameU, FullDomainNameU );
|
||
Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Groups\\" );
|
||
SUCCESS_ASSERT(Status, " Failed to append \\Groups\\\n");
|
||
|
||
Status = SampRtlConvertUlongToUnicodeString(
|
||
Rid,
|
||
16,
|
||
8,
|
||
FALSE,
|
||
&KeyNameU
|
||
);
|
||
|
||
SUCCESS_ASSERT(Status, "CreateGroup: Failed to append Rid Name\n");
|
||
|
||
//
|
||
// Set the Members attribute. The Admin and Guest users are always
|
||
// members of the Users group. If there is an Admins group, then the
|
||
// Admin user is a member of it.
|
||
//
|
||
|
||
GroupCount = 0;
|
||
if ( (Rid == DOMAIN_GROUP_RID_USERS) ||
|
||
(Rid == DOMAIN_GROUP_RID_ADMINS) ) {
|
||
|
||
GroupMembers[GroupCount] = DOMAIN_USER_RID_ADMIN;
|
||
GroupCount++;
|
||
|
||
}
|
||
|
||
//
|
||
// Guests are only members of the Guest group on NTAS systems.
|
||
// On WinNT systems they are members of NONE (which is the sam
|
||
// as USERS
|
||
//
|
||
|
||
if ( (Rid == DOMAIN_GROUP_RID_GUESTS) &&
|
||
(SampProductType == NtProductLanManNt) ) {
|
||
|
||
GroupMembers[GroupCount] = DOMAIN_USER_RID_GUEST;
|
||
GroupCount++;
|
||
}
|
||
|
||
if ( (Rid == DOMAIN_GROUP_RID_USERS) &&
|
||
((SampProductType == NtProductWinNt)
|
||
|| (SampProductType == NtProductServer)) ) {
|
||
|
||
GroupMembers[GroupCount] = DOMAIN_USER_RID_GUEST;
|
||
GroupCount++;
|
||
}
|
||
|
||
//
|
||
// Set the group's fixed and variable attributes
|
||
//
|
||
|
||
GroupAttributeLength = sizeof( SAMP_V1_0A_FIXED_LENGTH_GROUP ) +
|
||
( SAMP_GROUP_VARIABLE_ATTRIBUTES *
|
||
sizeof( SAMP_VARIABLE_LENGTH_ATTRIBUTE ) ) +
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length)
|
||
+ SampDwordAlignUlong(GroupNameU.Length) +
|
||
SampDwordAlignUlong(GroupCommentU.Length) +
|
||
( GroupCount * sizeof( ULONG ) );
|
||
|
||
GroupFixedAttributes = (PSAMP_V1_0A_FIXED_LENGTH_GROUP)RtlAllocateHeap(
|
||
RtlProcessHeap(), 0,
|
||
GroupAttributeLength
|
||
);
|
||
|
||
if ( GroupFixedAttributes == NULL ) {
|
||
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
SUCCESS_ASSERT(Status, " Failed to create group attributes\n");
|
||
|
||
GroupFixedAttributes->RelativeId = Rid;
|
||
GroupFixedAttributes->Attributes = Attributes;
|
||
GroupFixedAttributes->AdminCount = Admin ? 1 : 0;
|
||
GroupFixedAttributes->OperatorCount = 0;
|
||
GroupFixedAttributes->Revision = SAMP_REVISION;
|
||
|
||
GroupVariableAttributeArray = (PSAMP_VARIABLE_LENGTH_ATTRIBUTE)
|
||
((PUCHAR)(GroupFixedAttributes) +
|
||
sizeof( SAMP_V1_0A_FIXED_LENGTH_GROUP ) );
|
||
|
||
GroupVariableAttributeArray->Offset = 0;
|
||
GroupVariableAttributeArray->Length =
|
||
SampProtection[ProtectionIndex].Length;
|
||
GroupVariableAttributeArray->Qualifier = SAMP_REVISION;
|
||
|
||
GroupVariableAttributeArray++;
|
||
|
||
GroupVariableAttributeArray->Offset =
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length);
|
||
GroupVariableAttributeArray->Length = GroupNameU.Length;
|
||
GroupVariableAttributeArray->Qualifier = 0;
|
||
|
||
GroupVariableAttributeArray++;
|
||
|
||
GroupVariableAttributeArray->Offset =
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) +
|
||
SampDwordAlignUlong(GroupNameU.Length);
|
||
GroupVariableAttributeArray->Length = GroupCommentU.Length;
|
||
GroupVariableAttributeArray->Qualifier = 0;
|
||
|
||
GroupVariableAttributeArray++;
|
||
|
||
GroupVariableAttributeArray->Offset =
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) +
|
||
SampDwordAlignUlong(GroupNameU.Length) +
|
||
SampDwordAlignUlong(GroupCommentU.Length);
|
||
GroupVariableAttributeArray->Length = GroupCount * sizeof( ULONG );
|
||
GroupVariableAttributeArray->Qualifier = GroupCount;
|
||
|
||
GroupVariableData = (PVOID)( (PUCHAR)(GroupVariableAttributeArray) +
|
||
sizeof( SAMP_VARIABLE_LENGTH_ATTRIBUTE ) );
|
||
|
||
RtlCopyMemory(
|
||
GroupVariableData,
|
||
SampProtection[ProtectionIndex].Descriptor,
|
||
SampProtection[ProtectionIndex].Length
|
||
);
|
||
|
||
RtlCopyMemory(
|
||
(PVOID)((PUCHAR)(GroupVariableData) +
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length)),
|
||
GroupNameU.Buffer,
|
||
GroupNameU.Length
|
||
);
|
||
|
||
RtlCopyMemory(
|
||
(PVOID)((PUCHAR)(GroupVariableData) +
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) +
|
||
SampDwordAlignUlong(GroupNameU.Length)),
|
||
GroupCommentU.Buffer,
|
||
GroupCommentU.Length
|
||
);
|
||
|
||
RtlCopyMemory(
|
||
(PVOID)((PUCHAR)(GroupVariableData) +
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) +
|
||
SampDwordAlignUlong(GroupNameU.Length) +
|
||
SampDwordAlignUlong(GroupCommentU.Length)),
|
||
GroupMembers,
|
||
GroupCount * sizeof( ULONG )
|
||
);
|
||
|
||
//
|
||
// Now write out the attributes via the RXACT.
|
||
//
|
||
|
||
Status = RtlAddAttributeActionToRXact(
|
||
SamRXactContext,
|
||
RtlRXactOperationSetValue,
|
||
&KeyNameU,
|
||
INVALID_HANDLE_VALUE,
|
||
&SampCombinedAttributeName,
|
||
REG_BINARY,
|
||
(PVOID)GroupFixedAttributes,
|
||
GroupAttributeLength
|
||
);
|
||
SUCCESS_ASSERT(Status, " Failed to write out group attributes\n" );
|
||
|
||
RtlFreeHeap( RtlProcessHeap(), 0, GroupFixedAttributes );
|
||
|
||
//
|
||
// Commit these additions...
|
||
//
|
||
|
||
Status = RtlApplyRXactNoFlush( SamRXactContext );
|
||
SUCCESS_ASSERT(Status, " Failed to commit group addition.\n");
|
||
|
||
return Status;
|
||
DBG_UNREFERENCED_PARAMETER(SpecialAccount);
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
CreateUser(
|
||
IN PUNICODE_STRING AccountNameU,
|
||
IN PUNICODE_STRING AccountCommentU,
|
||
IN BOOLEAN SpecialAccount,
|
||
IN ULONG UserRid,
|
||
IN ULONG PrimaryGroup,
|
||
IN BOOLEAN Admin,
|
||
IN ULONG UserControl,
|
||
IN ULONG ProtectionIndex
|
||
)
|
||
|
||
/*++
|
||
|
||
|
||
Routine Description:
|
||
|
||
This routine adds keys for a single user.
|
||
This routine adds the keys necessary to create a user. It also applies
|
||
the appropriate protection to the user (protection differs for some
|
||
standard users).
|
||
|
||
|
||
Arguments:
|
||
|
||
AccountNameU - The Unicode name of the user.
|
||
|
||
AccountCommentU - A Unicode comment to put in the object's variable data.
|
||
|
||
SpecialAccount - A boolean indicating whether or not the account
|
||
is special. Special accounts are marked as such and can not
|
||
be deleted.
|
||
|
||
UserRid - The RID of the user account.
|
||
|
||
PrimaryGroup - The RID of the account's primary group. The user
|
||
does not have to be a member of the group. In fact, it doesn't
|
||
have to be a group. In fact, no checking is done to see if it
|
||
is even a valid account.
|
||
|
||
Admin - Indicates whether the account is in the Administrators alias
|
||
or not. TRUE means it is, FALSE means it isn't.
|
||
|
||
ProtectionIndex - Indicates which security descriptor to use to protect
|
||
this object.
|
||
|
||
|
||
Return Value:
|
||
|
||
TBS
|
||
|
||
--*/
|
||
|
||
{
|
||
PSAMP_V1_0A_FIXED_LENGTH_USER UserFixedAttributes;
|
||
PSAMP_VARIABLE_LENGTH_ATTRIBUTE UserVariableAttributeArray;
|
||
PSAMP_VARIABLE_LENGTH_ATTRIBUTE UserVariableAttributeArrayStart;
|
||
PVOID UserVariableData;
|
||
GROUP_MEMBERSHIP GroupMembership[2];
|
||
|
||
WCHAR RidNameBuffer[9], GroupIndexNameBuffer[9];
|
||
ULONG GroupCount, UserAttributeLength;
|
||
BOOLEAN DomainAdminMember = FALSE;
|
||
|
||
UNICODE_STRING UserNameU, UserCommentU;
|
||
|
||
UserNameU = *AccountNameU;
|
||
UserCommentU = *AccountCommentU;
|
||
|
||
|
||
//
|
||
// Set the account specific RID in the DACL's if necessary
|
||
//
|
||
|
||
if ( SampProtection[ProtectionIndex].RidReplacementRequired == TRUE ) {
|
||
|
||
(*SampProtection[ProtectionIndex].RidToReplace) = UserRid;
|
||
}
|
||
|
||
//
|
||
// Use a transaction.
|
||
//
|
||
|
||
Status = RtlStartRXact( SamRXactContext );
|
||
SUCCESS_ASSERT(Status, " Failed to start user addition transaction\n");
|
||
|
||
RidNameBuffer[8] = 0;
|
||
GroupIndexNameBuffer[8] = 0;
|
||
|
||
RtlCopyUnicodeString( &KeyNameU, FullDomainNameU );
|
||
Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Users\\Names\\" );
|
||
SUCCESS_ASSERT(Status, " Failed to append \\Users\\Names\\\n");
|
||
Status = RtlAppendUnicodeStringToString( &KeyNameU, &UserNameU);
|
||
SUCCESS_ASSERT(Status, " Failed to append User Account Name\n");
|
||
Status = RtlAddActionToRXact(
|
||
SamRXactContext,
|
||
RtlRXactOperationSetValue,
|
||
&KeyNameU,
|
||
UserRid,
|
||
NULL,
|
||
0
|
||
);
|
||
SUCCESS_ASSERT(Status, " Failed to add Users\\Names\\(Name) to log\n");
|
||
|
||
//
|
||
// Create Users\(UserRid) key
|
||
// (KeyValueType is revision, KeyValue is SecurityDescriptor)
|
||
//
|
||
|
||
RtlCopyUnicodeString( &KeyNameU, FullDomainNameU );
|
||
Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Users\\" );
|
||
SUCCESS_ASSERT(Status, " Failed to append \\Users\\\n");
|
||
|
||
Status = SampRtlConvertUlongToUnicodeString(
|
||
UserRid,
|
||
16,
|
||
8,
|
||
FALSE,
|
||
&KeyNameU
|
||
);
|
||
|
||
SUCCESS_ASSERT(Status, " CreateUser: Failed to append UserRid Name\n");
|
||
|
||
//
|
||
// Set the Groups attribute.
|
||
// Everybody except GUEST is a member of the Users group.
|
||
// On WindowsNT systems (as opposed to NTAS systems) even GUEST
|
||
// is a member of the Users group.
|
||
// On LanManNt systems, the Admin is a member of the Admins group.
|
||
//
|
||
|
||
GroupCount = 0;
|
||
|
||
if ( (UserRid != DOMAIN_USER_RID_GUEST) ||
|
||
(SampProductType != NtProductLanManNt) ) {
|
||
|
||
GroupMembership[GroupCount].RelativeId = DOMAIN_GROUP_RID_USERS;
|
||
GroupMembership[GroupCount].Attributes = SE_GROUP_MANDATORY |
|
||
SE_GROUP_ENABLED_BY_DEFAULT |
|
||
SE_GROUP_ENABLED;
|
||
GroupCount++;
|
||
}
|
||
|
||
if ( (UserRid == DOMAIN_USER_RID_GUEST) &&
|
||
(SampProductType == NtProductLanManNt) ) {
|
||
|
||
GroupMembership[GroupCount].RelativeId = DOMAIN_GROUP_RID_GUESTS;
|
||
GroupMembership[GroupCount].Attributes = SE_GROUP_MANDATORY |
|
||
SE_GROUP_ENABLED_BY_DEFAULT |
|
||
SE_GROUP_ENABLED;
|
||
GroupCount++;
|
||
}
|
||
|
||
|
||
if ( ( UserRid == DOMAIN_USER_RID_ADMIN ) &&
|
||
( SampProductType == NtProductLanManNt ) ) {
|
||
|
||
GroupMembership[GroupCount].RelativeId = DOMAIN_GROUP_RID_ADMINS;
|
||
GroupMembership[GroupCount].Attributes = SE_GROUP_MANDATORY |
|
||
SE_GROUP_ENABLED_BY_DEFAULT |
|
||
SE_GROUP_ENABLED;
|
||
GroupCount++;
|
||
DomainAdminMember = TRUE;
|
||
}
|
||
|
||
//
|
||
// Set the user's fixed and variable attributes
|
||
//
|
||
|
||
UserFixedAttributes = (PSAMP_V1_0A_FIXED_LENGTH_USER)RtlAllocateHeap(
|
||
RtlProcessHeap(), 0,
|
||
sizeof( SAMP_V1_0A_FIXED_LENGTH_USER )
|
||
);
|
||
|
||
if ( UserFixedAttributes == NULL ) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
SUCCESS_ASSERT(Status, " Failed to create user fixed attributes\n");
|
||
|
||
UserFixedAttributes->Revision = SAMP_REVISION;
|
||
|
||
UserFixedAttributes->CountryCode = 0;
|
||
UserFixedAttributes->CodePage = 0;
|
||
UserFixedAttributes->BadPasswordCount = 0;
|
||
UserFixedAttributes->LogonCount = 0;
|
||
UserFixedAttributes->OperatorCount = 0;
|
||
UserFixedAttributes->Unused1 = 0;
|
||
UserFixedAttributes->Unused2 = 0;
|
||
|
||
if ( Admin ) {
|
||
|
||
//
|
||
// If the user is an admin and a member of Domain Admins, set the count
|
||
// to two.
|
||
//
|
||
|
||
if ( DomainAdminMember ) {
|
||
UserFixedAttributes->AdminCount = 2;
|
||
} else {
|
||
UserFixedAttributes->AdminCount = 1;
|
||
}
|
||
|
||
|
||
} else {
|
||
|
||
UserFixedAttributes->AdminCount = 0;
|
||
}
|
||
|
||
UserFixedAttributes->UserAccountControl = UserControl;
|
||
UserFixedAttributes->UserId = UserRid;
|
||
UserFixedAttributes->PrimaryGroupId = PrimaryGroup;
|
||
UserFixedAttributes->LastLogon = SampHasNeverTime;
|
||
UserFixedAttributes->LastLogoff = SampHasNeverTime;
|
||
UserFixedAttributes->PasswordLastSet = SampHasNeverTime;
|
||
UserFixedAttributes->AccountExpires = SampWillNeverTime;
|
||
UserFixedAttributes->LastBadPasswordTime = SampHasNeverTime;
|
||
|
||
|
||
UserAttributeLength = SampDwordAlignUlong(UserNameU.Length) +
|
||
SampDwordAlignUlong(UserCommentU.Length) +
|
||
SampDwordAlignUlong( GroupCount *
|
||
sizeof( GROUP_MEMBERSHIP ) ) +
|
||
( SAMP_USER_VARIABLE_ATTRIBUTES *
|
||
sizeof( SAMP_VARIABLE_LENGTH_ATTRIBUTE ) ) +
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length);
|
||
|
||
UserVariableAttributeArrayStart = (PSAMP_VARIABLE_LENGTH_ATTRIBUTE)
|
||
RtlAllocateHeap(
|
||
RtlProcessHeap(), 0,
|
||
UserAttributeLength
|
||
);
|
||
|
||
if ( UserVariableAttributeArrayStart == NULL ) {
|
||
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
SUCCESS_ASSERT(Status, " Failed to create user variable attributes\n");
|
||
|
||
UserVariableAttributeArray = UserVariableAttributeArrayStart;
|
||
|
||
UserVariableAttributeArray->Offset = 0;
|
||
UserVariableAttributeArray->Length =
|
||
SampProtection[ProtectionIndex].Length;
|
||
UserVariableAttributeArray->Qualifier = SAMP_REVISION;
|
||
|
||
UserVariableAttributeArray++;
|
||
|
||
UserVariableAttributeArray->Offset =
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length);
|
||
UserVariableAttributeArray->Length = UserNameU.Length;
|
||
UserVariableAttributeArray->Qualifier = 0;
|
||
|
||
UserVariableAttributeArray++;
|
||
|
||
UserVariableAttributeArray->Offset =
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) +
|
||
SampDwordAlignUlong(UserNameU.Length);
|
||
UserVariableAttributeArray->Length = 0;
|
||
UserVariableAttributeArray->Qualifier = 0;
|
||
|
||
UserVariableAttributeArray++;
|
||
|
||
UserVariableAttributeArray->Offset =
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) +
|
||
SampDwordAlignUlong(UserNameU.Length);
|
||
UserVariableAttributeArray->Length = UserCommentU.Length;
|
||
UserVariableAttributeArray->Qualifier = 0;
|
||
|
||
UserVariableAttributeArray++;
|
||
|
||
UserVariableAttributeArray->Offset =
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) +
|
||
SampDwordAlignUlong(UserNameU.Length) +
|
||
SampDwordAlignUlong(UserCommentU.Length);
|
||
UserVariableAttributeArray->Length = 0;
|
||
UserVariableAttributeArray->Qualifier = 0;
|
||
|
||
UserVariableAttributeArray++;
|
||
|
||
UserVariableAttributeArray->Offset =
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) +
|
||
SampDwordAlignUlong(UserNameU.Length) +
|
||
SampDwordAlignUlong(UserCommentU.Length);
|
||
UserVariableAttributeArray->Length = 0;
|
||
UserVariableAttributeArray->Qualifier = 0;
|
||
|
||
UserVariableAttributeArray++;
|
||
|
||
UserVariableAttributeArray->Offset =
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) +
|
||
SampDwordAlignUlong(UserNameU.Length) +
|
||
SampDwordAlignUlong(UserCommentU.Length);
|
||
UserVariableAttributeArray->Length = 0;
|
||
UserVariableAttributeArray->Qualifier = 0;
|
||
|
||
UserVariableAttributeArray++;
|
||
|
||
UserVariableAttributeArray->Offset =
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) +
|
||
SampDwordAlignUlong(UserNameU.Length) +
|
||
SampDwordAlignUlong(UserCommentU.Length);
|
||
UserVariableAttributeArray->Length = 0;
|
||
UserVariableAttributeArray->Qualifier = 0;
|
||
|
||
UserVariableAttributeArray++;
|
||
|
||
UserVariableAttributeArray->Offset =
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) +
|
||
SampDwordAlignUlong(UserNameU.Length) +
|
||
SampDwordAlignUlong(UserCommentU.Length);
|
||
UserVariableAttributeArray->Length = 0;
|
||
UserVariableAttributeArray->Qualifier = 0;
|
||
|
||
UserVariableAttributeArray++;
|
||
|
||
UserVariableAttributeArray->Offset =
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) +
|
||
SampDwordAlignUlong(UserNameU.Length) +
|
||
SampDwordAlignUlong(UserCommentU.Length);
|
||
UserVariableAttributeArray->Length = 0;
|
||
UserVariableAttributeArray->Qualifier = 0;
|
||
|
||
UserVariableAttributeArray++;
|
||
|
||
UserVariableAttributeArray->Offset =
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) +
|
||
SampDwordAlignUlong(UserNameU.Length) +
|
||
SampDwordAlignUlong(UserCommentU.Length);
|
||
UserVariableAttributeArray->Length = 0;
|
||
UserVariableAttributeArray->Qualifier = 0;
|
||
|
||
UserVariableAttributeArray++;
|
||
|
||
UserVariableAttributeArray->Offset =
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) +
|
||
SampDwordAlignUlong(UserNameU.Length) +
|
||
SampDwordAlignUlong(UserCommentU.Length);
|
||
UserVariableAttributeArray->Length = 0;
|
||
UserVariableAttributeArray->Qualifier = 0;
|
||
|
||
UserVariableAttributeArray++;
|
||
|
||
UserVariableAttributeArray->Offset =
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) +
|
||
SampDwordAlignUlong(UserNameU.Length) +
|
||
SampDwordAlignUlong(UserCommentU.Length);
|
||
UserVariableAttributeArray->Length = GroupCount * sizeof( GROUP_MEMBERSHIP );
|
||
UserVariableAttributeArray->Qualifier = GroupCount;
|
||
|
||
UserVariableAttributeArray++;
|
||
|
||
UserVariableAttributeArray->Offset =
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) +
|
||
SampDwordAlignUlong(UserNameU.Length) +
|
||
SampDwordAlignUlong(UserCommentU.Length) +
|
||
SampDwordAlignUlong((GroupCount * sizeof( GROUP_MEMBERSHIP )));
|
||
UserVariableAttributeArray->Length = 0;
|
||
UserVariableAttributeArray->Qualifier = 0;
|
||
|
||
UserVariableAttributeArray++;
|
||
|
||
UserVariableAttributeArray->Offset =
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) +
|
||
SampDwordAlignUlong(UserNameU.Length) +
|
||
SampDwordAlignUlong(UserCommentU.Length) +
|
||
SampDwordAlignUlong((GroupCount * sizeof( GROUP_MEMBERSHIP )));
|
||
UserVariableAttributeArray->Length = 0;
|
||
UserVariableAttributeArray->Qualifier = 0;
|
||
|
||
UserVariableAttributeArray++;
|
||
|
||
UserVariableAttributeArray->Offset =
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) +
|
||
SampDwordAlignUlong(UserNameU.Length) +
|
||
SampDwordAlignUlong(UserCommentU.Length) +
|
||
SampDwordAlignUlong((GroupCount * sizeof( GROUP_MEMBERSHIP )));
|
||
UserVariableAttributeArray->Length = 0;
|
||
UserVariableAttributeArray->Qualifier = 0;
|
||
|
||
UserVariableAttributeArray++;
|
||
|
||
UserVariableAttributeArray->Offset =
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) +
|
||
SampDwordAlignUlong(UserNameU.Length) +
|
||
SampDwordAlignUlong(UserCommentU.Length) +
|
||
SampDwordAlignUlong((GroupCount * sizeof( GROUP_MEMBERSHIP )));
|
||
UserVariableAttributeArray->Length = 0;
|
||
UserVariableAttributeArray->Qualifier = 0;
|
||
|
||
UserVariableData = (PVOID)( (PUCHAR)(UserVariableAttributeArray) +
|
||
sizeof( SAMP_VARIABLE_LENGTH_ATTRIBUTE ) );
|
||
|
||
RtlCopyMemory(
|
||
UserVariableData,
|
||
SampProtection[ProtectionIndex].Descriptor,
|
||
SampProtection[ProtectionIndex].Length
|
||
);
|
||
|
||
RtlCopyMemory(
|
||
(PVOID)((PUCHAR)(UserVariableData) +
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length)),
|
||
UserNameU.Buffer,
|
||
UserNameU.Length
|
||
);
|
||
|
||
RtlCopyMemory(
|
||
(PVOID)((PUCHAR)(UserVariableData) +
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) +
|
||
SampDwordAlignUlong(UserNameU.Length)),
|
||
UserCommentU.Buffer,
|
||
UserCommentU.Length
|
||
);
|
||
|
||
RtlCopyMemory(
|
||
(PVOID)((PUCHAR)(UserVariableData) +
|
||
SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) +
|
||
SampDwordAlignUlong(UserNameU.Length) +
|
||
SampDwordAlignUlong(UserCommentU.Length)),
|
||
&GroupMembership,
|
||
GroupCount * sizeof( GROUP_MEMBERSHIP )
|
||
);
|
||
|
||
//
|
||
// Now write out the attributes via the RXACT.
|
||
//
|
||
|
||
Status = RtlAddAttributeActionToRXact(
|
||
SamRXactContext,
|
||
RtlRXactOperationSetValue,
|
||
&KeyNameU,
|
||
INVALID_HANDLE_VALUE,
|
||
&SampFixedAttributeName,
|
||
REG_BINARY,
|
||
(PVOID)UserFixedAttributes,
|
||
sizeof( SAMP_V1_0A_FIXED_LENGTH_USER )
|
||
);
|
||
SUCCESS_ASSERT(Status, " Failed to write out user fixed attributes\n" );
|
||
|
||
RtlFreeHeap( RtlProcessHeap(), 0, UserFixedAttributes );
|
||
|
||
Status = RtlAddAttributeActionToRXact(
|
||
SamRXactContext,
|
||
RtlRXactOperationSetValue,
|
||
&KeyNameU,
|
||
INVALID_HANDLE_VALUE,
|
||
&SampVariableAttributeName,
|
||
REG_BINARY,
|
||
(PVOID)UserVariableAttributeArrayStart,
|
||
UserAttributeLength
|
||
);
|
||
SUCCESS_ASSERT(Status, " Failed to write out user variable attributes\n" );
|
||
|
||
RtlFreeHeap( RtlProcessHeap(), 0, UserVariableAttributeArrayStart );
|
||
|
||
//
|
||
// Commit these additions...
|
||
//
|
||
|
||
Status = RtlApplyRXactNoFlush( SamRXactContext );
|
||
SUCCESS_ASSERT(Status, " Failed to commit user addition.\n");
|
||
|
||
return Status;
|
||
|
||
DBG_UNREFERENCED_PARAMETER(SpecialAccount);
|
||
DBG_UNREFERENCED_PARAMETER(UserControl);
|
||
}
|
||
|
||
|
||
|
||
PSID
|
||
BuildPrimaryDomainSid(
|
||
ULONG Rid
|
||
)
|
||
{
|
||
NTSTATUS Status;
|
||
PSID SourceDomainSid, NewSid;
|
||
ULONG SidLength, SubAuthorityCount;
|
||
|
||
SourceDomainSid = SampBldPrimaryDomain->Sid;
|
||
|
||
SidLength = RtlLengthSid( SourceDomainSid ) + sizeof(ULONG);
|
||
NewSid = RtlAllocateHeap( RtlProcessHeap(), 0, SidLength );
|
||
if (NewSid != NULL) {
|
||
|
||
Status = RtlCopySid (SidLength, NewSid, SourceDomainSid );
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
(*RtlSubAuthorityCountSid( NewSid )) += 1;
|
||
SubAuthorityCount = (ULONG)(*RtlSubAuthorityCountSid( NewSid ));
|
||
(*RtlSubAuthoritySid( NewSid, SubAuthorityCount-1)) = Rid;
|
||
|
||
}
|
||
|
||
|
||
return(NewSid);
|
||
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
PSID
|
||
BuildAccountSid(
|
||
SAMP_DOMAIN_SELECTOR Domain,
|
||
ULONG Rid
|
||
)
|
||
{
|
||
NTSTATUS Status;
|
||
PSID SourceDomainSid, NewSid;
|
||
ULONG SidLength, SubAuthorityCount;
|
||
|
||
|
||
if (Domain == DomainBuiltin) {
|
||
SourceDomainSid = SampBuiltinDomainSid;
|
||
} else {
|
||
SourceDomainSid = SampAccountDomainSid;
|
||
}
|
||
|
||
SidLength = RtlLengthSid( SourceDomainSid ) + sizeof(ULONG);
|
||
NewSid = RtlAllocateHeap( RtlProcessHeap(), 0, SidLength );
|
||
if (NewSid != NULL) {
|
||
|
||
Status = RtlCopySid (SidLength, NewSid, SourceDomainSid );
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
(*RtlSubAuthorityCountSid( NewSid )) += 1;
|
||
SubAuthorityCount = (ULONG)(*RtlSubAuthorityCountSid( NewSid ));
|
||
(*RtlSubAuthoritySid( NewSid, SubAuthorityCount-1)) = Rid;
|
||
|
||
}
|
||
|
||
|
||
return(NewSid);
|
||
|
||
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
UpdateAliasXReference(
|
||
IN ULONG AliasRid,
|
||
IN PSID Sid
|
||
)
|
||
|
||
/*++
|
||
|
||
|
||
Routine Description:
|
||
|
||
This routine updates the set of alias member SIDs either by adding
|
||
specified SID (if it isn't already an alias member) or incrementing
|
||
its count (if it is already an alias member).
|
||
|
||
|
||
The BUILTIN domain is updated.
|
||
|
||
|
||
|
||
Arguments:
|
||
|
||
|
||
Sid - member Sid to update.
|
||
|
||
|
||
|
||
|
||
Return Value:
|
||
|
||
TBS
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS IgnoreStatus;
|
||
|
||
HANDLE KeyHandle;
|
||
|
||
|
||
|
||
if (RtlSubAuthorityCountSid( Sid ) == 0) {
|
||
return(STATUS_INVALID_SID);
|
||
}
|
||
|
||
|
||
//
|
||
// Open the domain key for this alias member.
|
||
//
|
||
|
||
SetCurrentDomain( DomainBuiltin );
|
||
Status = OpenAliasMember( Sid, &KeyHandle );
|
||
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
|
||
ULONG MembershipCount,
|
||
KeyValueLength,
|
||
OldKeyValueLength,
|
||
i;
|
||
PULONG MembershipArray;
|
||
|
||
//
|
||
// Retrieve the length of the current membership buffer
|
||
// and allocate one large enough for that plus another member.
|
||
//
|
||
|
||
KeyValueLength = 0;
|
||
Status = RtlpNtQueryValueKey( KeyHandle,
|
||
&MembershipCount,
|
||
NULL,
|
||
&KeyValueLength,
|
||
NULL);
|
||
|
||
if (NT_SUCCESS(Status) || (Status == STATUS_BUFFER_OVERFLOW)) {
|
||
|
||
KeyValueLength += sizeof(ULONG);
|
||
MembershipArray = RtlAllocateHeap( RtlProcessHeap(), 0, KeyValueLength );
|
||
|
||
|
||
if (MembershipArray == NULL) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
} else {
|
||
|
||
OldKeyValueLength = KeyValueLength;
|
||
Status = RtlpNtQueryValueKey(
|
||
KeyHandle,
|
||
NULL,
|
||
MembershipArray,
|
||
&OldKeyValueLength,
|
||
NULL);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
|
||
//
|
||
// See if the account is already a member ...
|
||
//
|
||
|
||
for (i = 0; i<MembershipCount ; i++ ) {
|
||
if ( MembershipArray[i] == AliasRid )
|
||
{
|
||
Status = STATUS_MEMBER_IN_ALIAS;
|
||
}
|
||
}
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
|
||
//
|
||
// Add the Aliasrid to the end
|
||
//
|
||
|
||
MembershipCount += 1;
|
||
MembershipArray[MembershipCount-1] = AliasRid;
|
||
|
||
//
|
||
// And write it out.
|
||
//
|
||
|
||
Status = RtlpNtSetValueKey(
|
||
KeyHandle,
|
||
MembershipCount,
|
||
MembershipArray,
|
||
KeyValueLength
|
||
);
|
||
}
|
||
}
|
||
|
||
RtlFreeHeap(RtlProcessHeap(), 0, MembershipArray);
|
||
}
|
||
|
||
}
|
||
|
||
IgnoreStatus = NtClose( KeyHandle );
|
||
ASSERT( NT_SUCCESS(IgnoreStatus) );
|
||
|
||
}
|
||
|
||
|
||
|
||
return( Status );
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
OpenAliasMember(
|
||
IN PSID Sid,
|
||
OUT PHANDLE KeyHandle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine opens the registry key containing the alias
|
||
xreference for the specified SID. If either this key, or
|
||
its corresponding parent key doesn't exist, it (they) will
|
||
be created.
|
||
|
||
If a new domain-level key is created, the DomainCount in the
|
||
ALIASES\MEMBERS key is incremented as well.
|
||
|
||
|
||
Arguments:
|
||
|
||
Sid - The SID that is an alias member.
|
||
|
||
KeyHandle - Receives a handle to the registry key for this alias
|
||
member account xreference.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
NTSTATUS IgnoreStatus;
|
||
HANDLE AliasDomainHandle;
|
||
|
||
//
|
||
// Open or create the domain-level key.
|
||
//
|
||
|
||
|
||
Status = OpenOrCreateAliasDomainKey( Sid, &AliasDomainHandle );
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
|
||
|
||
//
|
||
// Open or create the account-rid key
|
||
//
|
||
|
||
Status = OpenOrCreateAccountRidKey( Sid,
|
||
AliasDomainHandle,
|
||
KeyHandle
|
||
);
|
||
|
||
IgnoreStatus = NtClose( AliasDomainHandle );
|
||
ASSERT(NT_SUCCESS(IgnoreStatus));
|
||
}
|
||
|
||
return(Status);
|
||
|
||
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
OpenOrCreateAccountRidKey(
|
||
IN PSID Sid,
|
||
IN HANDLE AliasDomainHandle,
|
||
OUT PHANDLE KeyHandle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine opens an account xreference key for an alias
|
||
member SID.
|
||
|
||
If this key doesn't exist, it will be created.
|
||
|
||
If a new key is created, the RidCount in the AliasDomainHandle
|
||
key is incremented as well.
|
||
|
||
|
||
Arguments:
|
||
|
||
Sid - The SID that is an alias member.
|
||
|
||
AliasDomainHandle
|
||
|
||
KeyHandle - Receives a handle to the registry key for this alias
|
||
member domain xreference.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
ULONG Disposition;
|
||
ULONG Rid;
|
||
|
||
if (RtlSubAuthorityCountSid( Sid ) == 0) {
|
||
return(STATUS_INVALID_SID);
|
||
}
|
||
|
||
Rid = (*RtlSubAuthoritySid(Sid, (ULONG)(*RtlSubAuthorityCountSid(Sid))-1));
|
||
|
||
//
|
||
// Build the Unicode Key for this Rid.
|
||
//
|
||
|
||
KeyNameU.Length = (USHORT) 0;
|
||
|
||
Status = SampRtlConvertUlongToUnicodeString(
|
||
Rid,
|
||
16,
|
||
8,
|
||
FALSE,
|
||
&KeyNameU
|
||
);
|
||
|
||
//
|
||
// Open this key relative to the alias domain key
|
||
//
|
||
|
||
InitializeObjectAttributes(
|
||
&ObjectAttributes,
|
||
&KeyNameU,
|
||
OBJ_CASE_INSENSITIVE,
|
||
AliasDomainHandle,
|
||
NULL
|
||
);
|
||
Status = RtlpNtCreateKey(
|
||
KeyHandle,
|
||
(KEY_READ | KEY_WRITE),
|
||
&ObjectAttributes,
|
||
0, //Options
|
||
NULL, //Provider
|
||
&Disposition
|
||
);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
|
||
if (Disposition == REG_CREATED_NEW_KEY) {
|
||
|
||
//
|
||
// Update the AccountRid count in the alias domain key
|
||
//
|
||
|
||
ULONG MembershipCount;
|
||
|
||
|
||
//
|
||
// Retrieve the current domain count and increment it by 1.
|
||
//
|
||
|
||
Status = RtlpNtQueryValueKey( AliasDomainHandle,
|
||
&MembershipCount,
|
||
NULL,
|
||
NULL,
|
||
NULL);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
|
||
MembershipCount += 1;
|
||
|
||
//
|
||
// Write it back out.
|
||
//
|
||
|
||
Status = RtlpNtSetValueKey(
|
||
AliasDomainHandle,
|
||
MembershipCount,
|
||
NULL,
|
||
0
|
||
);
|
||
}
|
||
|
||
//
|
||
// Now write out the AccountRid key info
|
||
//
|
||
|
||
Status = RtlpNtSetValueKey(
|
||
*KeyHandle,
|
||
0, //Not yet a member of any aliases
|
||
NULL,
|
||
0
|
||
);
|
||
|
||
}
|
||
}
|
||
|
||
return(Status);
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
OpenOrCreateAliasDomainKey(
|
||
IN PSID Sid,
|
||
OUT PHANDLE KeyHandle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine opens a domain xreference key for an alias
|
||
member SID.
|
||
|
||
If this key doesn't exist, it will be created.
|
||
|
||
If a new key is created, the DomainCount in the
|
||
ALIASES\MEMBERS key is incremented as well.
|
||
|
||
|
||
Arguments:
|
||
|
||
Sid - The SID that is an alias member.
|
||
|
||
KeyHandle - Receives a handle to the registry key for this alias
|
||
member domain xreference.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS IgnoreStatus;
|
||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
ULONG Disposition;
|
||
|
||
RtlCopyUnicodeString( &KeyNameU, FullDomainNameU );
|
||
Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Aliases" );
|
||
Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Members\\" );
|
||
Status = AppendAliasDomainNameToUnicodeString( &KeyNameU, Sid );
|
||
|
||
InitializeObjectAttributes(
|
||
&ObjectAttributes,
|
||
&KeyNameU,
|
||
OBJ_CASE_INSENSITIVE,
|
||
SamKey,
|
||
NULL
|
||
);
|
||
Status = RtlpNtCreateKey(
|
||
KeyHandle,
|
||
(KEY_READ | KEY_WRITE),
|
||
&ObjectAttributes,
|
||
0, //Options
|
||
NULL, //Provider
|
||
&Disposition
|
||
);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
|
||
if (Disposition == REG_CREATED_NEW_KEY) {
|
||
|
||
HANDLE TmpHandle;
|
||
|
||
//
|
||
// Update the Domain count
|
||
//
|
||
|
||
RtlCopyUnicodeString( &KeyNameU, FullDomainNameU );
|
||
Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Aliases" );
|
||
Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Members\\" );
|
||
|
||
InitializeObjectAttributes(
|
||
&ObjectAttributes,
|
||
&KeyNameU,
|
||
OBJ_CASE_INSENSITIVE,
|
||
SamKey,
|
||
NULL
|
||
);
|
||
Status = RtlpNtOpenKey(
|
||
&TmpHandle,
|
||
(KEY_READ | KEY_WRITE),
|
||
&ObjectAttributes,
|
||
0
|
||
);
|
||
ASSERT(NT_SUCCESS(Status));
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
|
||
ULONG MembershipCount;
|
||
|
||
|
||
//
|
||
// Retrieve the current domain count and increment it by 1.
|
||
//
|
||
|
||
Status = RtlpNtQueryValueKey( TmpHandle,
|
||
&MembershipCount,
|
||
NULL,
|
||
NULL,
|
||
NULL);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
|
||
MembershipCount += 1;
|
||
|
||
//
|
||
// Write it back out.
|
||
//
|
||
|
||
Status = RtlpNtSetValueKey(
|
||
TmpHandle,
|
||
MembershipCount,
|
||
NULL,
|
||
0
|
||
);
|
||
}
|
||
|
||
IgnoreStatus = NtClose( TmpHandle );
|
||
ASSERT( NT_SUCCESS(IgnoreStatus) );
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
return(Status);
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
AppendAliasDomainNameToUnicodeString(
|
||
IN OUT PUNICODE_STRING Destination,
|
||
IN PSID Sid
|
||
)
|
||
|
||
{
|
||
UCHAR OriginalCount;
|
||
|
||
//
|
||
// Save the current sub-authority count and decrement it by one.
|
||
//
|
||
|
||
OriginalCount = (*RtlSubAuthorityCountSid(Sid));
|
||
(*RtlSubAuthorityCountSid(Sid)) = OriginalCount -1;
|
||
|
||
//
|
||
// Convert the Sid to a Unicode String and place it in the global
|
||
// temporary Unicode String buffer.
|
||
//
|
||
|
||
Status = RtlConvertSidToUnicodeString( &TempStringU, Sid, TRUE);
|
||
|
||
(*RtlSubAuthorityCountSid(Sid)) = OriginalCount;
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
|
||
Status = RtlAppendUnicodeStringToString( Destination, &TempStringU );
|
||
}
|
||
|
||
return(Status);
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
SampGetServerRole(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine retrieves the server role from the LSA policy database
|
||
and places it in the global variable SampServerRole.
|
||
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
|
||
Return Value:
|
||
|
||
(placed in the global variable (Status) )
|
||
|
||
STATUS_SUCCESS - Succeeded.
|
||
|
||
Other status values that may be returned from:
|
||
|
||
LsarQueryInformationPolicy()
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS IgnoreStatus;
|
||
PPOLICY_LSA_SERVER_ROLE_INFO ServerRoleInfo = NULL;
|
||
|
||
//
|
||
// Query the server role information
|
||
//
|
||
|
||
Status = LsarQueryInformationPolicy(
|
||
SampBldPolicyHandle,
|
||
PolicyLsaServerRoleInformation,
|
||
(PLSAPR_POLICY_INFORMATION *)&ServerRoleInfo
|
||
);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
|
||
if (ServerRoleInfo->LsaServerRole == PolicyServerRolePrimary) {
|
||
|
||
SampServerRole = DomainServerRolePrimary;
|
||
|
||
} else {
|
||
|
||
SampServerRole = DomainServerRoleBackup;
|
||
}
|
||
|
||
IgnoreStatus = LsaFreeMemory( ServerRoleInfo );
|
||
ASSERT(NT_SUCCESS(IgnoreStatus));
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
|
||
|
||
VOID
|
||
SampGetPrimaryDomainInfo(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine retrieves the primary domain name/sid from the
|
||
LSA policy database and places it in the global variable
|
||
SampBldPrimaryDomain.
|
||
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
|
||
Return Value:
|
||
|
||
(placed in the global variable (Status) )
|
||
|
||
STATUS_SUCCESS - Succeeded.
|
||
|
||
Other status values that may be returned from:
|
||
|
||
LsarQueryInformationPolicy()
|
||
|
||
NOTE: The Rdr and Bowser components of the LanmanWorkstation
|
||
service rely on there always being a primary domain name.
|
||
For this reason Network SETUP always supplies a default
|
||
"workgroup" name, which is set as the primary domain name.
|
||
In this case, the name is present but the SID is NULL;
|
||
this is equivalent to NOT having a primary domain at all.
|
||
|
||
--*/
|
||
|
||
{
|
||
SampBldPrimaryDomain = NULL;
|
||
|
||
Status = LsarQueryInformationPolicy(
|
||
SampBldPolicyHandle,
|
||
PolicyPrimaryDomainInformation,
|
||
(PLSAPR_POLICY_INFORMATION *) &SampBldPrimaryDomain
|
||
);
|
||
|
||
if (NT_SUCCESS(Status) && ( SampBldPrimaryDomain->Sid == NULL )) {
|
||
|
||
LsaIFree_LSAPR_POLICY_INFORMATION(
|
||
PolicyPrimaryDomainInformation,
|
||
(PLSAPR_POLICY_INFORMATION) SampBldPrimaryDomain
|
||
);
|
||
|
||
SampBldPrimaryDomain = NULL;
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
|
||
|
||
VOID
|
||
SampRestoreDefaultDacl(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Restores the saved default DACL to the current token.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
HANDLE Token;
|
||
|
||
Status = NtOpenProcessToken(
|
||
NtCurrentProcess(),
|
||
(TOKEN_ADJUST_DEFAULT),
|
||
&Token
|
||
);
|
||
|
||
|
||
ASSERT(NT_SUCCESS( Status ));
|
||
|
||
Status = NtSetInformationToken (
|
||
Token,
|
||
TokenDefaultDacl,
|
||
TokenDefaultDaclInformation,
|
||
TokenDefaultDaclInformationSize
|
||
);
|
||
|
||
ASSERT(NT_SUCCESS( Status ));
|
||
|
||
NtClose( Token );
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SampDetermineSetupEnvironment( VOID )
|
||
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function checks to see whether we are running folloing
|
||
a formal SETUP. If not, it is assumed we are running to
|
||
perform a developer's setup.
|
||
|
||
Global variables are set to indicate our setup environment.
|
||
|
||
|
||
BOOLEAN SampRealSetupWasRun; //Indicates a real setup was run
|
||
BOOLEAN SampDeveloperSetup; //Indicates a developer setup is running
|
||
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS NtStatus, TmpStatus;
|
||
HANDLE InstallationEvent;
|
||
OBJECT_ATTRIBUTES EventAttributes;
|
||
UNICODE_STRING EventName;
|
||
|
||
SampRealSetupWasRun = FALSE;
|
||
SampDeveloperSetup = FALSE;
|
||
|
||
//
|
||
// If the following event exists, it is an indication that
|
||
// a real setup was run.
|
||
//
|
||
|
||
RtlInitUnicodeString( &EventName, L"\\INSTALLATION_SECURITY_HOLD");
|
||
InitializeObjectAttributes( &EventAttributes, &EventName, 0, 0, NULL );
|
||
|
||
NtStatus = NtOpenEvent(
|
||
&InstallationEvent,
|
||
SYNCHRONIZE,
|
||
&EventAttributes
|
||
);
|
||
|
||
if ( NT_SUCCESS(NtStatus)) {
|
||
|
||
//
|
||
// The event exists - installation created it and will signal it
|
||
// when it is ok to proceed with security initialization.
|
||
//
|
||
|
||
SampRealSetupWasRun = TRUE;
|
||
|
||
TmpStatus = NtClose( InstallationEvent );
|
||
ASSERT(NT_SUCCESS(TmpStatus));
|
||
|
||
} else {
|
||
SampDeveloperSetup = TRUE;
|
||
}
|
||
|
||
|
||
|
||
return(NtStatus);
|
||
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SampInitializeRegistry (
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes a SAM database in the registry.
|
||
|
||
Arguments:
|
||
|
||
NONE
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS or an error received along the way.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS IgnoreStatus;
|
||
|
||
Status = Initialize();
|
||
if (!NT_SUCCESS(Status)) {
|
||
return( Status );
|
||
}
|
||
|
||
//
|
||
// Initialize SAM-level registry structures
|
||
//
|
||
|
||
Status = InitializeSam( );
|
||
if (!NT_SUCCESS(Status)) {return(Status);}
|
||
|
||
//
|
||
// OK, we have a SAM key.
|
||
// Create each of the domains.
|
||
//
|
||
|
||
Status = CreateBuiltinDomain( ); if (!NT_SUCCESS(Status)) {return(Status);}
|
||
Status = CreateAccountDomain( ); if (!NT_SUCCESS(Status)) {return(Status);}
|
||
|
||
//
|
||
// all done
|
||
//
|
||
|
||
//
|
||
// Close our handle to LSA. Ignore any errors.
|
||
//
|
||
|
||
IgnoreStatus = LsarClose( (PLSAPR_HANDLE) &SampBldPolicyHandle );
|
||
SampBldPolicyHandle = NULL;
|
||
|
||
//
|
||
// We modified the default DACL in the token, put it back to what
|
||
// it was here.
|
||
//
|
||
|
||
SampRestoreDefaultDacl();
|
||
|
||
//
|
||
// Free up the transaction context we created
|
||
//
|
||
|
||
RtlFreeHeap( RtlProcessHeap(), 0, SamRXactContext );
|
||
SamRXactContext = NULL;
|
||
|
||
//
|
||
// Close the database root key after flushing all the changes we made.
|
||
//
|
||
|
||
Status = NtFlushKey( SamKey );
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
|
||
IgnoreStatus = NtClose( SamKey );
|
||
ASSERT(NT_SUCCESS(IgnoreStatus));
|
||
|
||
} else {
|
||
|
||
KdPrint(("SAMSRV: FlushKey failed, database not built. Status: 0x%lx\n", Status));
|
||
IgnoreStatus = NtClose( SamKey );
|
||
}
|
||
|
||
SamKey = NULL;
|
||
|
||
//
|
||
// Close the database root parent key
|
||
//
|
||
|
||
if (SamParentKey != NULL) {
|
||
IgnoreStatus = NtClose( SamParentKey );
|
||
}
|
||
|
||
return( Status );
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SampGetMessageStrings(
|
||
LPVOID Resource,
|
||
DWORD Index1,
|
||
PUNICODE_STRING String1,
|
||
DWORD Index2,
|
||
PUNICODE_STRING String2 OPTIONAL
|
||
)
|
||
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This gets 1 or 2 message strings values from a resource message table.
|
||
The string buffers are allocated and the strings initialized properly.
|
||
|
||
The string buffers must be freed using LocalFree() when no longer needed.
|
||
|
||
Arguments:
|
||
|
||
Resource - points to the resource table.
|
||
|
||
Index1 - Index of first message to retrieve.
|
||
|
||
String1 - Points to a UNICODE_STRING structure to receive the first
|
||
message string.
|
||
|
||
Index2 - Index of second message to retrieve.
|
||
|
||
String2 - Points to a UNICODE_STRING structure to receive the first
|
||
message string. If this parameter is NULL, then only one message
|
||
string is retrieved.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
|
||
{
|
||
|
||
String1->Buffer = NULL;
|
||
|
||
String1->MaximumLength = (USHORT) FormatMessage(FORMAT_MESSAGE_FROM_HMODULE |
|
||
FORMAT_MESSAGE_ALLOCATE_BUFFER,
|
||
Resource,
|
||
Index1,
|
||
0, // Use caller's language
|
||
(LPWSTR)&(String1->Buffer),
|
||
0,
|
||
NULL
|
||
);
|
||
|
||
if (String1->Buffer == NULL) {
|
||
return(STATUS_RESOURCE_DATA_NOT_FOUND);
|
||
} else {
|
||
|
||
//
|
||
// Note that we are retrieving a message from a message file.
|
||
// This message will have a cr/lf tacked on the end of it
|
||
// (0x0d 0x0a) that we don't want to be part of our returned
|
||
// strings. Also note that FormatMessage() returns a character
|
||
// count, not a byte count. So, we have to do some adjusting
|
||
// to make the string lengths correct.
|
||
//
|
||
|
||
String1->MaximumLength -= 2; // For the cr/lf we don't want.
|
||
String1->MaximumLength *= sizeof(WCHAR); // to make it a byte count
|
||
String1->Length = String1->MaximumLength;
|
||
}
|
||
|
||
|
||
if (!ARGUMENT_PRESENT(String2)) {
|
||
return(STATUS_SUCCESS);
|
||
}
|
||
|
||
String2->Buffer = NULL;
|
||
String2->MaximumLength = (USHORT) FormatMessage(FORMAT_MESSAGE_FROM_HMODULE |
|
||
FORMAT_MESSAGE_ALLOCATE_BUFFER,
|
||
Resource,
|
||
Index2,
|
||
0, // Use caller's language
|
||
(LPWSTR)&(String2->Buffer),
|
||
0,
|
||
NULL
|
||
);
|
||
|
||
if (String2->Buffer == NULL) {
|
||
LocalFree( String1->Buffer );
|
||
return(STATUS_RESOURCE_DATA_NOT_FOUND);
|
||
} else {
|
||
|
||
//
|
||
// Note that we are retrieving a message from a message file.
|
||
// This message will have a cr/lf tacked on the end of it
|
||
// (0x0d 0x0a) that we don't want to be part of our returned
|
||
// strings. Also note that FormatMessage() returns a character
|
||
// count, not a byte count. So, we have to do some adjusting
|
||
// to make the string lengths correct.
|
||
//
|
||
|
||
String2->MaximumLength -= 2; // For the cr/lf we don't want.
|
||
String2->MaximumLength *= sizeof(WCHAR); // to make it a byte count
|
||
String2->Length = String2->MaximumLength;
|
||
}
|
||
|
||
|
||
|
||
return(STATUS_SUCCESS);
|
||
|
||
}
|