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