/*++ 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 #include #include "ntlsa.h" #include #include #include #include #include #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; iGenericWrite | 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; iAceCount; 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; iLsaServerRole == 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); }