/*++ Copyright (c) 1991 Microsoft Corporation Module Name: dbinit.c Abstract: Local Security Authority - Database Server Initialization This module contains functions which perform initialization of the Database Server. Certain information is obtained from the LSA database and is set up in global data for easy retrieval. Author: Scott Birrell (ScottBi) July 25, 1991 Environment: User Mode Revision History: 12-Nov-1997 MikeSw Added lsa policy handle cache for interdomain lookups --*/ // // Define this to allocate all globals in this module // #include #include "lsasrvp.h" #include "dbp.h" #include #include #include #include #include #ifdef DS_LOOKUP #include #endif #include #include #include #include #include #include // // Well known Sids for security packages include a subauthority equal to // the associated package's RPC Id. We'll cause a compile time error if // these numbers ever change. // #if (SECURITY_PACKAGE_NTLM_RID != RPC_C_AUTHN_WINNT) #error SECURITY_PACKAGE_NTLM_RID definition must be equal to \ RPC_C_AUTHN_WINNT #endif #if (SECURITY_PACKAGE_DIGEST_RID != RPC_C_AUTHN_DIGEST) #error SECURITY_PACKAGE_DIGEST_RID definition must equal RPC_C_AUTHN_DIGEST #endif #if (SECURITY_PACKAGE_SCHANNEL_RID != RPC_C_AUTHN_GSS_SCHANNEL) #error SECURITY_PACKAGE_SCHANNEL_RID definition must equal \ RPC_C_AUTHN_GSS_SCHANNEL #endif extern LSAP_DB_TRUSTED_DOMAIN_LIST LsapDbTrustedDomainList; NTSTATUS LsapDbBuildObjectCaches( ); NTSTATUS LsapAssignInitialHiveProtection( HANDLE HiveRoot ); NTSTATUS LsapCreateDatabaseProtection( PISECURITY_DESCRIPTOR Sd ); NTSTATUS LsapGenerateRandomDomainSid( OUT PSID NewDomainSid ); NTSTATUS LsapSetupInitialize( VOID ); NTSTATUS LsapUpdateDatabaseProtection( IN ULONG Revision ); NTSTATUS LsapDsInitFixupQueue( VOID ); static UCHAR SyskeyBuffer[LSAP_SYSKEY_SIZE]; static UCHAR OldSyskeyBuffer[LSAP_SYSKEY_SIZE]; NTSTATUS LsapDbInitializeServer( IN ULONG Pass ) /*++ Routine Description: This function initializes the LSA Database Server. The following steps are performed: o Initialize the LSA Database Lock o Acquire the LSA Database Lock o Initialize the Unicode Strings for the fixed names within the LSA Database, e.g. LSA Database object attributes and well-known object names. o Initialize the Unicode Strings for the LSA Database Object constant and well known names, e.g SubKeys, fixed object names. o Initialize the Unicode Strings for LSA Object Containing Dirs o Initialize the Generic Mappings for Database Object Types o Initialize the Lsa Database Handle Table o Install the LSA Database if necessary - Creates the Lsa Database o and Manager account objects, and initializes the transaction subtree o Initialize the abs min, abs max and installation default quota limits o Release the LSA Database Lock Arguments: None. Return Value: NTSTATUS - Standard Nt Result Code All Result Codes are generated by called routines. --*/ { NTSTATUS Status = STATUS_SUCCESS, IgnoreStatus; BOOL BooleanStatus = TRUE; BOOLEAN AcquiredLock = FALSE; BOOLEAN FreeComputerName = FALSE; LSAP_DB_OBJECT_INFORMATION ObjectInformation; PLSAPR_POLICY_ACCOUNT_DOM_INFO PolicyAccountDomainInfo = NULL; PLSAPR_POLICY_DNS_DOMAIN_INFO PolicyDnsDomainInfo = NULL; UNICODE_STRING ComputerName, CipherKey; ULONG Length; ULONG Revision, RevisionLength = sizeof( ULONG ); DWORD WinStatus; // // Initialize the LSA Database Lock and set it into the locked state // if (Pass == 1 ) { LsapDsDebugInitialize(); RtlZeroMemory( &LsaDsStateInfo, sizeof( LsaDsStateInfo ) ); LsapDbState.DbServerInitialized = FALSE; #if DBG LsapDbState.RegistryTransactionOpen = FALSE; #endif // // Disable Replicator Notifications. // LsapDbDisableReplicatorNotification(); // // This function call will initialize all of the global or well known locks used by // the Lsa // Status = LsapDbInitializeLock(); if (!NT_SUCCESS(Status)) { goto InitializeServerError; } Status = LsapDbInitHandleTables(); if (!NT_SUCCESS(Status)) { goto InitializeServerError; } // // Initialize the forest trust cache. // The cache is not usable until populated. // Status = LsapForestTrustCacheInitialize(); if (!NT_SUCCESS(Status)) { goto InitializeServerError; } // // Initialize the policy change notification mechanism // Status = LsapInitializeNotifiyList(); if ( !NT_SUCCESS( Status ) ) { goto InitializeServerError ; } // // Initialize the Fixup queue // Status = LsapDsInitFixupQueue(); if ( !NT_SUCCESS( Status )) { goto InitializeServerError ; } // // Initialize the binding handle cache // Status = LsapInitBindingCache(); if (!NT_SUCCESS(Status)) { goto InitializeServerError; } } // // Acquire the LSA Database Lock. This allows subroutines to // assert that the LSA Database is locked. Otherwise, it is // not actually necessary, given that no other thread can access the // LSA until initialization is complete. // if (Pass == 1) { // // Initialize the Unicode Strings for the fixed names within the // LSA Database, e.g. LSA Database object attributes and well-known // object names. // Status = LsapDbInitializeUnicodeNames(); if (!NT_SUCCESS(Status)) { goto InitializeServerError; } // // Initialize the Unicode Strings for the Containing Directories for // each LSA Database Object Type. // Status = LsapDbInitializeContainingDirs(); if (!NT_SUCCESS(Status)) { goto InitializeServerError; } // // Initialize the LSA Subsystem name string. This is needed for // NtAccessCheckAuditAlarm calls // RtlInitUnicodeString(&LsapState.SubsystemName, L"LSA"); // // Initialize the Shutdown Pending state. // LsapState.SystemShutdownPending = FALSE; // // Initialize the Database Object Types. Information stored // includes the Generic mappings and Object Counts. // Status = LsapDbInitializeObjectTypes(); if (!NT_SUCCESS(Status)) { goto InitializeServerError; } // // Open the LSA Database root Registry subkey. This stays // open for use in adding transactions. // Status = LsapDbOpenRootRegistryKey(); if (!NT_SUCCESS(Status)) { goto InitializeServerError; } // // Initialize the Lsa Database Cipher Key // This hard-coded key is used for retrieving old pre-NT4 SP4 // encrypted keys only // RtlInitUnicodeString( &CipherKey, L"823543" ); Status = LsapDbInitializeCipherKey( &CipherKey, &LsapDbCipherKey ); if (!NT_SUCCESS(Status)) { goto InitializeServerError; } // // Initialize the LSA Database Transaction Subtree, creating it if // one does not already exist. If the Transaction Subtree exists, // commit any partially committed transaction if appropriate. // Status = RtlInitializeRXact( LsapDbState.DbRootRegKeyHandle, TRUE, (PRTL_RXACT_CONTEXT *) &LsapDbState.RXactContext ); if (!NT_SUCCESS(Status)) { if (Status != STATUS_RXACT_STATE_CREATED) { LsapLogError( "LsapDbInitializeServer: Registry Transaction Init returned 0x%lx\n", Status ); goto InitializeServerError; } LsapLogError( "LsapDbInitializeServer: Registry Transaction State Did Not Exist\n", Status ); goto InitializeServerError; } // // Setup attributes for opening the Policy object. // ObjectInformation.ObjectTypeId = PolicyObject; ObjectInformation.ContainerTypeId = 0; ObjectInformation.Sid = NULL; ObjectInformation.ObjectAttributeNameOnly = FALSE; ObjectInformation.DesiredObjectAccess = 0; InitializeObjectAttributes( &ObjectInformation.ObjectAttributes, &LsapDbNames[Policy], 0L, NULL, NULL ); // // Now try to open the root LSA Database object (Policy). This is a // trusted call, so no access checking or impersonation will be done. // Note that the handle obtained will remain open indefinitely. It is // used after initialization for all internally generated accesses to // the Policy object // Status = LsapDbOpenObject( &ObjectInformation, 0L, LSAP_DB_TRUSTED, &LsapDbHandle ); if (!NT_SUCCESS(Status)) { // // Open of LSA Database object failed. If any error other than // object not found, there is a serious error which prevents the // LSA from functioning, so abort. // if (Status != STATUS_OBJECT_NAME_NOT_FOUND) { LsapLogError( "LsapDbInitializeServer: Open failed 0x%lx\n" "The Lsa Database must be reinstalled or manually\n" "erased before using the system\n", Status ); goto InitializeServerError; } // // The Lsa Database object was not found. Run the database installation // routine so that people can boot without having to run the // installation applet first. // LsapDatabaseSetupPerformed = TRUE; Status = LsapDbInstallLsaDatabase(1); if (!NT_SUCCESS(Status)) { goto InitializeServerError; } } // // The Lsa Database object was successfully opened, possibly after // having just been created. Proceed with the rest of server // initialization. First, setup in-memory copies of the Installation // Default, Absolute Min and Absolute Max system quota limits. // // // Make the policy handle available throughout LSA // LsapPolicyHandle = LsapDbHandle; if (!NT_SUCCESS(Status)) { goto InitializeServerError; } // // Bring the database up to the current revision level, // if necessary. This is not a syskey upgrade. // Status = LsapDbUpgradeRevision(FALSE,FALSE); if (!NT_SUCCESS(Status)) { goto InitializeServerError; } // // Read the revision attribute. If the revision is greater than // LSAP_DB_REVSION_1_5 then obtain the syskey from winlogon. In previous // revisions SAM would have obtained the syskey from winlogon if the machine // had been syskey'd // Status = LsapDbReadAttributeObject( LsapDbHandle, &LsapDbNames[PolRevision], (PVOID) &Revision, &RevisionLength ); if ( !NT_SUCCESS(Status) ) { goto InitializeServerError; } // // Query the syskey from winlogon. Do so only if the revision is greater than 1_5. // This is because in previous builds SAM used to manage the syskey. From this // release onwards. The below routine also intializes the LSA encryption key // if (Revision >= LSAP_DB_REVISION_1_5) { Status = LsapDbGetSyskeyFromWinlogon(); if (!NT_SUCCESS(Status)) { goto InitializeServerError; } } // // Initialize privilege object related code // Status = LsapDbInitializePrivilegeObject(); if (!NT_SUCCESS(Status)) { goto InitializeServerError; } // // Perform initialization for the Replicator. Replications // are still disabled at this point. // Status = LsapDbInitializeReplication(); if (!NT_SUCCESS(Status)) { goto InitializeServerError; } // // Initialize the data for the new APIs (user rights) // Status = LsapDbInitializeRights(); if (!NT_SUCCESS(Status)) { goto InitializeServerError; } } else if (Pass == 2) { BOOLEAN ExpectTrue; OSVERSIONINFOEX OsVersionInfoEx = { 0 }; // // Perform the second stage of database initialization. // This is the initialization that depends on the product type. // First, get the product type. Note that the Product Type may // have already been retrieved from a number of routines that // may be called during early installation, including // LsarSetInformationPolicy() and LsarCreateTrustedDomain(). // ExpectTrue = RtlGetNtProductType(&LsapProductType); ASSERT( ExpectTrue == TRUE ); // // find out the product suite mask. // this is used later to determine if we are running // on a specific product suite such the small business server // OsVersionInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); ExpectTrue = (BOOLEAN) GetVersionEx((OSVERSIONINFO*) &OsVersionInfoEx); ASSERT( ExpectTrue == TRUE ); LsapProductSuiteMask = OsVersionInfoEx.wSuiteMask; // // If necessary, install the rest of our database. // if (LsapDatabaseSetupPerformed == TRUE) { Status = LsapDbInstallLsaDatabase(2); if (!NT_SUCCESS(Status)) { goto InitializeServerError; } } // // If this is a Win Nt product, set the SAM Accounts Domain // Name equal to the Computer Name, which may have been changed // since the last boot. // // // If this is setup, do nothing, since we've set it elsewhere.. // If this is safe mode mode, don't set it either // if ( !LsaISetupWasRun() ) { if ( ((LsapProductType == NtProductWinNt) || (LsapProductType == NtProductServer)) && !LsaISafeMode() ) { Status = LsarQueryInformationPolicy( LsapPolicyHandle, PolicyAccountDomainInformation, (PLSAPR_POLICY_INFORMATION *) &PolicyAccountDomainInfo ); if (!NT_SUCCESS(Status)) { goto InitializeServerError; } Length = (ULONG) 0; ComputerName.Buffer = UNICODE_NULL; FreeComputerName = FALSE; BooleanStatus = GetComputerNameW( (LPWSTR) ComputerName.Buffer, (LPDWORD) &Length ); WinStatus = GetLastError(); if (WinStatus != ERROR_BUFFER_OVERFLOW) { KdPrint(("LsapDbInitializeServer: Failed to get Computer Name Length\n" "Using default MACHINENAME instead\n")); RtlInitUnicodeString( &ComputerName, LSAP_DB_DEFAULT_COMPUTER_NAME ); Length = (ULONG) ComputerName.Length; } else if (Length <= 1) { KdPrint(("LsapDbInitializeServer: Null Computer Name\n" "Using default MACHINENAME instead\n")); RtlInitUnicodeString( &ComputerName, LSAP_DB_DEFAULT_COMPUTER_NAME ); Length = (ULONG) ComputerName.Length; } else { ComputerName.Length = (USHORT) ((Length - 1) * sizeof (WCHAR)); ComputerName.MaximumLength = (USHORT) (Length * sizeof(WCHAR)); ComputerName.Buffer = MIDL_user_allocate( ComputerName.MaximumLength ); if ( ComputerName.Buffer == NULL ) { Status = STATUS_INSUFFICIENT_RESOURCES; goto InitializeServerError; } FreeComputerName = TRUE; } if (!GetComputerNameW( (LPWSTR) ComputerName.Buffer, (LPDWORD) &Length )) { KdPrint(("LsapDbInitializeServer: Failed to get Computer Name\n" "Using default MACHINENAME instead\n")); RtlInitUnicodeString( &ComputerName, LSAP_DB_DEFAULT_COMPUTER_NAME ); } PolicyAccountDomainInfo->DomainName = *((PLSAPR_UNICODE_STRING) &ComputerName); Status = LsarSetInformationPolicy( LsapPolicyHandle, PolicyAccountDomainInformation, (PLSAPR_POLICY_INFORMATION) PolicyAccountDomainInfo ); if ( FreeComputerName ) { MIDL_user_free( ComputerName.Buffer ); } if (!NT_SUCCESS(Status)) { goto InitializeServerError; } } Status = RpcMgmtEnableIdleCleanup(); if ( !NT_SUCCESS( Status )) { goto InitializeServerError; } } else { Status = LsapSetupInitialize(); if (!NT_SUCCESS(Status)) { goto InitializeServerError; } } // Perform initialization for Lookup Sids and Names, including // initialization of the Trusted Domain List. // Status = LsapDbLookupInitialize(); if (!NT_SUCCESS(Status)) { goto InitializeServerError; } // // Load the object caches. Any that fail to load have caching // permanently turned off. // IgnoreStatus = LsapDbBuildObjectCaches(); // // Find out if this machine is a DC in a root domain // Status = LsapDbQueryInformationPolicy( LsapPolicyHandle, PolicyDnsDomainInformation, ( PLSAPR_POLICY_INFORMATION * )&PolicyDnsDomainInfo ); if ( !NT_SUCCESS( Status )) { goto InitializeServerError; } if ( DnsNameCompareEqual == DnsNameCompareEx_W( PolicyDnsDomainInfo->DnsDomainName.Buffer, PolicyDnsDomainInfo->DnsForestName.Buffer, 0 )) { DcInRootDomain = TRUE; } else { DcInRootDomain = FALSE; } LsaIFree_LSAPR_POLICY_INFORMATION( PolicyDnsDomainInformation, ( PLSAPR_POLICY_INFORMATION )PolicyDnsDomainInfo ); // // Mark the Server as being completely initialized. // LsapDbState.DbServerInitialized = TRUE; LsapDbEnableReplicatorNotification(); } InitializeServerFinish: return(Status); InitializeServerError: goto InitializeServerFinish; } NTSTATUS LsapDbUpgradeRevision( IN BOOLEAN SyskeyUpgrade, IN BOOLEAN GenerateNewSyskey ) /*++ Routine Description: This function brings the LSA policy database up to date if necessary. Arguments: SyskeyUpgrade -- This paramter is set to true when this function is called a second time around when upgrading NT4 or Win2K B3 machines from LsaIHealthCheck GenerateNewSyskey -- This parameter is set to true when this function is called a second time around when upgrading NT4 or Win2k B3 machines from LsaIHealthCheck and the system is not already syskey'd Return Value: NTSTATUS - Standard Nt Result Code All Result Codes are generated by called routines. --*/ { NTSTATUS Status; ULONG Revision = LSAP_DB_REVISION_1_0, RevisionLength = sizeof( ULONG ); LSAP_DB_ATTRIBUTE Attributes[20]; PLSAP_DB_ATTRIBUTE NextAttribute; ULONG AttributeCount = 0; BOOLEAN PolRevisionWritten = FALSE; NextAttribute = Attributes; // // Read the Revision Info from the PolRevision attribute // of the Policy object in the LSA Database. // Status = LsapDbReadAttributeObject( LsapDbHandle, &LsapDbNames[PolRevision], (PVOID) &Revision, &RevisionLength ); if ( !NT_SUCCESS(Status) ) { Revision = LSAP_DB_REVISION_1_0; if (Status == STATUS_OBJECT_NAME_NOT_FOUND) { // // attribute doesn't exist. // This means the database is an NT1.0 format. // Upgrade it to the current revision. // Status = STATUS_SUCCESS; } } // // Revison 1_1 created the ModifiedIdAtLastPromotion attribute on the policy object. // This attribute is no longer used for anything so don't create it. // // // Revision 1_2 corresponded to an encryption of secrets that shipped with NT4 that was // incompatible with NT5. Therefore skip this revision level. The incompatible encryption // of secrets would be handled by revision number update // // // Update the security descriptor to revision 1.3 // if ( NT_SUCCESS( Status ) && (Revision < LSAP_DB_REVISION_1_3) ) { Status = LsapUpdateDatabaseProtection( LSAP_DB_REVISION_1_3 ); if ( NT_SUCCESS( Status ) ) { Revision = LSAP_DB_REVISION_1_3; if ( !PolRevisionWritten ) { LsapDbInitializeAttribute( NextAttribute, &LsapDbNames[PolRevision], &Revision, sizeof (ULONG), FALSE ); NextAttribute++; AttributeCount++; PolRevisionWritten = TRUE; ASSERT( AttributeCount < ( sizeof( Attributes ) / sizeof( LSAP_DB_ATTRIBUTE ) ) ); } } else { Status = STATUS_SUCCESS; } } if ( NT_SUCCESS( Status ) && (Revision < LSAP_DB_REVISION_1_5) && (SyskeyUpgrade)) { BOOLEAN IsUpgrade = FALSE; PVOID Syskey = NULL; ULONG SyskeyLength = 0; LSAP_DB_ENCRYPTION_KEY NewEncryptionKey; // // NT4 SP4 shipped with an encryption of secret's that is incompatible with // NT5 if the syskey'd setting was turned on. // Therefore we walk over all secrets and patch them back. It is not necessary // that we have all the key's setup at this point. The way this works is we attempt, // to patch back all the secrets, and if we did not have the right key to decrypt them // we will error out. In Syskey'd machines , SAM will have the right key and will call back // into LSA when it has obtained the key. During SAM's callback this routine will be executed // again and we will call upgrade all secrets. // // This upgrade involves a secret upgrade only. We simply have to read them and // write them back out and everything will be kosher... // Note if the secret upgrade fails for any reason, we will not update the database revision. // // // First obtain the syskey from the SAM hive. Admittedly and ahsamedly this does read the SAM // hive directly // // // If we are a syskey'd machine the syskey should have been passed to us by now. // ASSERT((NULL!=LsapDbSysKey) || (GenerateNewSyskey)); // // Initialize the key for secret encryption // Status = LsapDbGenerateNewKey( &NewEncryptionKey ); if (NT_SUCCESS(Status)) { if (GenerateNewSyskey) { ULONG SyskeyLength = sizeof(SyskeyBuffer); // Boot option is WxStored. NewEncryptionKey.BootType = WxStored; // // A new syskey is being generated // Status = LsapDbSetupInitialSyskey( &SyskeyLength, &LsapDbSysKey ); } else { // // Since we are upgrading from a syskey'd machine, get the boot option from SAM // SAM is initialized enough at this point as it makes the callout to LSA supplying it with // the syskey. It is O.K to pass in a value of 0, because then the default account domain // is used. // Status = SamIGetBootKeyInformation( (SAMPR_HANDLE) 0, (SAMPR_BOOT_TYPE*)&NewEncryptionKey.BootType ); } } if (NT_SUCCESS(Status)) { // // Setup the secret cipher key // Ordinarily, the key used for reading equals the key used for writing // LsapDbInitializeSecretCipherKeyRead( &NewEncryptionKey ); LsapDbInitializeSecretCipherKeyWrite( &NewEncryptionKey ); // // Encrypt the key with syskey // LsapDbEncryptKeyWithSyskey( &NewEncryptionKey, LsapDbSysKey, LSAP_SYSKEY_SIZE ); LsapDbInitializeAttribute( NextAttribute, &LsapDbNames[PolSecretEncryptionKey], &NewEncryptionKey, sizeof (NewEncryptionKey), FALSE ); NextAttribute++; AttributeCount++; } // // The secret upgrade is executed only during GUI setup upgrade of a workstation, or a DC being upgraded // from NT4. In all other cases we simply patch the revision number up to the correct value. // if ((NT_SUCCESS(Status)) && ( (SamIIsDownlevelDcUpgrade()) || ((LsapProductType != NtProductLanManNt) && (SamIIsSetupInProgress(&IsUpgrade)) && (IsUpgrade)))) { // // Ignore the return code below, we still want to move the revision // level to 1.5, and this code will not be retried anyways // LsapDbUpgradeSecretForKeyChange(); } if ( NT_SUCCESS( Status ) ) { Revision = LSAP_DB_REVISION_1_5; if ( !PolRevisionWritten ) { LsapDbInitializeAttribute( NextAttribute, &LsapDbNames[PolRevision], &Revision, sizeof (ULONG), FALSE ); NextAttribute++; AttributeCount++; PolRevisionWritten = TRUE; // // Revision is now 1.5 // ASSERT( AttributeCount < ( sizeof( Attributes ) / sizeof( LSAP_DB_ATTRIBUTE ) ) ); } } } // // Only upgrade past revision 1.5 if we've upgraded to revison 1.5. // // We don't upgrade to revision 1.5 in the mainline LSA initialization code. // Rather, we upgrade to revision 1.5 in a callback from SAM. If then we moved on // to revisions greater than 1.5 during LSA initialization, we'd never have a chance to // do the revision 1.5 upgrade code. // if ( Revision >= LSAP_DB_REVISION_1_5 ) { // // Update the security descriptor to revision 1.6 // if ( NT_SUCCESS( Status ) && (Revision < LSAP_DB_REVISION_1_6) ) { Status = LsapUpdateDatabaseProtection( LSAP_DB_REVISION_1_6 ); if ( NT_SUCCESS( Status ) ) { Revision = LSAP_DB_REVISION_1_6; if ( !PolRevisionWritten ) { LsapDbInitializeAttribute( NextAttribute, &LsapDbNames[PolRevision], &Revision, sizeof (ULONG), FALSE ); NextAttribute++; AttributeCount++; PolRevisionWritten = TRUE; // // Revision is now 1.6 // ASSERT( AttributeCount < ( sizeof( Attributes ) / sizeof( LSAP_DB_ATTRIBUTE ) ) ); } } else { Status = STATUS_SUCCESS; } } // // Update the security descriptor to revision 1.7 // if ( NT_SUCCESS( Status ) && (Revision < LSAP_DB_REVISION_1_7) ) { Status = LsapUpdateDatabaseProtection( LSAP_DB_REVISION_1_7 ); if ( NT_SUCCESS( Status ) ) { Revision = LSAP_DB_REVISION_1_7; if ( !PolRevisionWritten ) { LsapDbInitializeAttribute( NextAttribute, &LsapDbNames[PolRevision], &Revision, sizeof (ULONG), FALSE ); NextAttribute++; AttributeCount++; PolRevisionWritten = TRUE; // // Revision is now 1.7 // ASSERT( AttributeCount < ( sizeof( Attributes ) / sizeof( LSAP_DB_ATTRIBUTE ) ) ); } } else { Status = STATUS_SUCCESS; } } // // In the future, revision updates can be made // by adding "if" blocks similar to the one above. // // Remember, however, that the attributes are pointing // to values in local variables. Any local variable // value changed before the attribute is written out // will cause that attribute value to be changed. // // } // // Now write out all attributes that have been added (if any) // if (AttributeCount > 0) { Status = LsapDbReferenceObject( LsapDbHandle, 0, PolicyObject, PolicyObject, LSAP_DB_LOCK | LSAP_DB_START_TRANSACTION ); if (NT_SUCCESS(Status)) { ASSERT( AttributeCount < ( sizeof( Attributes ) / sizeof( LSAP_DB_ATTRIBUTE ) ) ); Status = LsapDbWriteAttributesObject( LsapDbHandle, Attributes, AttributeCount ); // // No attributes are replicatable. // (That's good, too, since SAM hasn't told Netlogon our role yet.) Status = LsapDbDereferenceObject( &LsapDbHandle, PolicyObject, PolicyObject, (LSAP_DB_LOCK | LSAP_DB_FINISH_TRANSACTION | LSAP_DB_OMIT_REPLICATOR_NOTIFICATION ), SecurityDbChange, Status ); } } return( Status ); } NTSTATUS LsapDbBuildObjectCaches( ) /*++ Routine Description: This function builds caches for Lsa objects. These caches contain a subset of the information for some object types. Arguments: None Return Values: NTSTATUS - Standard Nt Result Code. --*/ { NTSTATUS IgnoreStatus; LSAP_DB_OBJECT_TYPE_ID ObjectTypeId; // // Initialize all the caches. // for (ObjectTypeId = PolicyObject; ObjectTypeId <= SecretObject; ObjectTypeId++) { IgnoreStatus = LsapDbRebuildCache( ObjectTypeId ); } return(STATUS_SUCCESS); } NTSTATUS LsapDbInitializeObjectTypes( ) /*++ Routine Description: This function initializes the information pertinent to each object type in the LSA Database. This information includes the following: o Generic Mapping Arrays The Generic Mapping array for each object defines the list of object-type-specific access types that correspond to the generic access types GENERIC_READ, GENERIC_WRITE, GENERIC_EXECUTE and GENERIC_ALL for the object type. o Object Count Information The Object Count Information includes a count of the number of objects that exist for each type, the upper limit on this number (if any) for each object type, and the error code to return when that limit is reached. o Write Operation Masks These specify which access types are update operations o Default accesses granted to World and Admin aliases o Invalid access masks for each object type These masks specify the bits in an access mask that are invalid for a given object type. o Initial owners of each object type o Object caching supported for each object type. Arguments: None. The Generic Mapping arrays are held the LsapDbState structure. Return Value: NTSTATUS - Standard Nt Result Code. Currently, there are no error situations in this code, so STATUS_SUCCESS is always returned. --*/ { NTSTATUS Status = STATUS_SUCCESS; PGENERIC_MAPPING GenericMapping; PLSAP_DB_OBJECT_TYPE ObjectType; LSAP_DB_OBJECT_TYPE_ID ObjectTypeId; // // Initialize the Generic Mapping Array for the PolicyObject Object Type // Note that there is only one object of this type and objects of this // type can neither be created nor destroyed. // GenericMapping = &LsapDbState.DbObjectTypes[PolicyObject].GenericMapping; GenericMapping->GenericRead = STANDARD_RIGHTS_READ | POLICY_VIEW_AUDIT_INFORMATION | POLICY_GET_PRIVATE_INFORMATION; GenericMapping->GenericWrite = STANDARD_RIGHTS_WRITE | POLICY_TRUST_ADMIN | POLICY_CREATE_ACCOUNT | POLICY_CREATE_SECRET | POLICY_CREATE_PRIVILEGE | POLICY_SET_DEFAULT_QUOTA_LIMITS | POLICY_SET_AUDIT_REQUIREMENTS | POLICY_AUDIT_LOG_ADMIN | POLICY_SERVER_ADMIN; GenericMapping->GenericExecute = STANDARD_RIGHTS_EXECUTE | POLICY_VIEW_LOCAL_INFORMATION | POLICY_LOOKUP_NAMES; GenericMapping->GenericAll = POLICY_ALL_ACCESS | POLICY_NOTIFICATION; // // Initialize the Generic Mapping Array for the Account Object Type // Note that Account Objects can be created and destroyed. // GenericMapping = &LsapDbState.DbObjectTypes[AccountObject].GenericMapping; GenericMapping->GenericRead = STANDARD_RIGHTS_READ | ACCOUNT_VIEW; GenericMapping->GenericWrite = STANDARD_RIGHTS_WRITE | ACCOUNT_ADJUST_PRIVILEGES | ACCOUNT_ADJUST_QUOTAS | ACCOUNT_ADJUST_SYSTEM_ACCESS; GenericMapping->GenericExecute = STANDARD_RIGHTS_EXECUTE; GenericMapping->GenericAll = ACCOUNT_ALL_ACCESS; // // Initialize the Generic Mapping Array for the TrustedDomain Object // Type. // GenericMapping = &LsapDbState.DbObjectTypes[TrustedDomainObject].GenericMapping; GenericMapping->GenericRead = STANDARD_RIGHTS_READ | TRUSTED_QUERY_DOMAIN_NAME; GenericMapping->GenericWrite = STANDARD_RIGHTS_WRITE | TRUSTED_SET_CONTROLLERS | TRUSTED_SET_POSIX; GenericMapping->GenericExecute = STANDARD_RIGHTS_EXECUTE | TRUSTED_QUERY_CONTROLLERS | TRUSTED_QUERY_POSIX; GenericMapping->GenericAll = TRUSTED_ALL_ACCESS; // // Initialize the Generic Mapping Array for the Secret Object // Type. // GenericMapping = &LsapDbState.DbObjectTypes[SecretObject].GenericMapping; GenericMapping->GenericRead = STANDARD_RIGHTS_READ | SECRET_QUERY_VALUE; GenericMapping->GenericWrite = STANDARD_RIGHTS_WRITE | SECRET_SET_VALUE; GenericMapping->GenericExecute = STANDARD_RIGHTS_EXECUTE; GenericMapping->GenericAll = SECRET_ALL_ACCESS; // // Initialize the Object Count Information to defaults // ObjectType = &(LsapDbState.DbObjectTypes[PolicyObject]); for (ObjectTypeId = PolicyObject; ObjectTypeId < DummyLastObject; ObjectTypeId++) { ObjectType->ObjectCount = 0; ObjectType->ObjectCountLimited = FALSE; ObjectType->ObjectCountError = STATUS_SUCCESS; ObjectType->MaximumObjectCount = 0; } // // Set specific limits for Secret Object Type. This is the only // object type so far to have limits. // ObjectType = &(LsapDbState.DbObjectTypes[SecretObject]); ObjectType->ObjectCountLimited = TRUE; ObjectType->ObjectCountError = STATUS_TOO_MANY_SECRETS; ObjectType->MaximumObjectCount = LSA_SECRET_MAXIMUM_COUNT; // // Initialize the write operations for each object type // LsapDbState.DbObjectTypes[PolicyObject].WriteOperations = LSAP_POLICY_WRITE_OPS; LsapDbState.DbObjectTypes[TrustedDomainObject].WriteOperations = LSAP_TRUSTED_WRITE_OPS; LsapDbState.DbObjectTypes[AccountObject].WriteOperations = LSAP_ACCOUNT_WRITE_OPS; LsapDbState.DbObjectTypes[SecretObject].WriteOperations = LSAP_SECRET_WRITE_OPS; // // Initialize the default accesses granted to Domain Admins alias // LsapDbState.DbObjectTypes[PolicyObject].AliasAdminsAccess = GENERIC_ALL; LsapDbState.DbObjectTypes[TrustedDomainObject].AliasAdminsAccess = GENERIC_ALL | DELETE; LsapDbState.DbObjectTypes[AccountObject].AliasAdminsAccess = GENERIC_ALL | DELETE; LsapDbState.DbObjectTypes[SecretObject].AliasAdminsAccess = GENERIC_ALL | DELETE; // // Initialize the default accesses granted to World alias // LsapDbState.DbObjectTypes[PolicyObject].WorldAccess = GENERIC_EXECUTE; LsapDbState.DbObjectTypes[TrustedDomainObject].WorldAccess = GENERIC_EXECUTE; LsapDbState.DbObjectTypes[AccountObject].WorldAccess = GENERIC_EXECUTE; LsapDbState.DbObjectTypes[SecretObject].WorldAccess = GENERIC_EXECUTE; // // Initialize the default accesses granted to AnonymousLogon alias // LsapDbState.DbObjectTypes[PolicyObject].AnonymousLogonAccess = POLICY_VIEW_LOCAL_INFORMATION | POLICY_LOOKUP_NAMES; LsapDbState.DbObjectTypes[TrustedDomainObject].AnonymousLogonAccess = 0; LsapDbState.DbObjectTypes[AccountObject].AnonymousLogonAccess = 0; LsapDbState.DbObjectTypes[SecretObject].AnonymousLogonAccess = 0; // // Initialize the default accesses granted to LocalService and NetworkService // LsapDbState.DbObjectTypes[PolicyObject].LocalServiceAccess = POLICY_NOTIFICATION; LsapDbState.DbObjectTypes[TrustedDomainObject].LocalServiceAccess = 0; LsapDbState.DbObjectTypes[AccountObject].LocalServiceAccess = 0; LsapDbState.DbObjectTypes[SecretObject].LocalServiceAccess = 0; LsapDbState.DbObjectTypes[PolicyObject].NetworkServiceAccess = POLICY_NOTIFICATION; LsapDbState.DbObjectTypes[TrustedDomainObject].NetworkServiceAccess = 0; LsapDbState.DbObjectTypes[AccountObject].NetworkServiceAccess = 0; LsapDbState.DbObjectTypes[SecretObject].NetworkServiceAccess = 0; // // Initialize the Invalid Access masks for each object type // LsapDbState.DbObjectTypes[PolicyObject].InvalidMappedAccess = ((ACCESS_MASK)(~(POLICY_ALL_ACCESS | POLICY_NOTIFICATION | ACCESS_SYSTEM_SECURITY | MAXIMUM_ALLOWED))); LsapDbState.DbObjectTypes[TrustedDomainObject].InvalidMappedAccess = ((ACCESS_MASK)(~(TRUSTED_ALL_ACCESS | ACCESS_SYSTEM_SECURITY | MAXIMUM_ALLOWED))); LsapDbState.DbObjectTypes[AccountObject].InvalidMappedAccess = ((ACCESS_MASK)(~(ACCOUNT_ALL_ACCESS | ACCESS_SYSTEM_SECURITY | MAXIMUM_ALLOWED))); LsapDbState.DbObjectTypes[SecretObject].InvalidMappedAccess = ((ACCESS_MASK)(~(SECRET_ALL_ACCESS | ACCESS_SYSTEM_SECURITY | MAXIMUM_ALLOWED))); // // Initialize the Initial Owners for new objects of each type // LsapDbState.DbObjectTypes[PolicyObject].InitialOwnerSid = LsapAliasAdminsSid; LsapDbState.DbObjectTypes[TrustedDomainObject].InitialOwnerSid = LsapAliasAdminsSid; LsapDbState.DbObjectTypes[AccountObject].InitialOwnerSid = LsapAliasAdminsSid; LsapDbState.DbObjectTypes[SecretObject].InitialOwnerSid = LsapAliasAdminsSid; // // Specify method of access to objects of the type. Currently, all objects // of a given type are accessed in the same way, either by Sid or by Name // but not both. // LsapDbState.DbObjectTypes[PolicyObject].AccessedByName = TRUE; LsapDbState.DbObjectTypes[TrustedDomainObject].AccessedByName = FALSE; LsapDbState.DbObjectTypes[AccountObject].AccessedByName = FALSE; LsapDbState.DbObjectTypes[SecretObject].AccessedByName = TRUE; LsapDbState.DbObjectTypes[PolicyObject].AccessedBySid = FALSE; LsapDbState.DbObjectTypes[TrustedDomainObject].AccessedBySid = TRUE; LsapDbState.DbObjectTypes[AccountObject].AccessedBySid = TRUE; LsapDbState.DbObjectTypes[SecretObject].AccessedBySid = FALSE; // // Specify the object types for which caching is supported (in full // or in part) and turn caching off initially for all object types. // Object types for which caching is supported have ther caches set // to the "Invalid" state. Automatic restore is allowed for caches // in this state. Object types for which caching is not supported // are set to the "Not supported" state. Note that a cache is // also placed in the "not supported" state if an attempt to restore // it fails. // LsapDbMakeCacheInvalid( PolicyObject ); LsapDbMakeCacheInvalid( TrustedDomainObject ); LsapDbMakeCacheInvalid( AccountObject ); LsapDbMakeCacheUnsupported( SecretObject ); return(Status); } NTSTATUS LsapDbInitializeUnicodeNames() /*++ Routine Description: This function initializes two arrays of Unicode Strings. The LsapDbNames array contains Unicode Strings for all of the constant names in the Lsa Database. The LsapDbObjectTypeNames is indexed by Object Type Id and contains the Unicode Strings for all of the LSA Database object types. Arguments: None. Return Value: NTSTATUS - Standard Nt Result Code All Result Codes are generated by called routines. --*/ { NTSTATUS Status = STATUS_SUCCESS; LSAP_DB_NAMES Index; LSAP_DB_OBJECT_TYPE_ID ObjectTypeId; PCWSTR UnicodeNames[DummyLastName + 1] = { L"SecDesc", L"Privilgs", L"Sid", L"Name", L"AdminMod", L"OperMode", L"QuotaLim", L"DefQuota", L"PrDomain", L"Policy", L"Accounts", L"Domains", L"Secrets", L"CurrVal", L"OldVal", L"CupdTime", L"OupdTime", L"PolAdtLg", L"PolAdtEv", L"PolAcDmN", L"PolAcDmS", L"PolDnDDN", L"PolDnTrN", L"PolDnDmG", L"PolEfDat", L"PolPrDmN", L"PolPrDmS", L"PolPdAcN", L"PolRepSc", L"PolRepAc", L"PolRevision", L"PolMod", L"PolState", L"ActSysAc", L"TrDmName", L"TrDmTrPN", // Netbios name of trust partner L"TrDmSid", L"TrDmAcN", L"TrDmCtN", L"TrDmPxOf", L"TrDmCtEn", NULL, // TrDmTrTy NULL, // TrDmTrDi L"TrDmTrLA", // TrDmTrLA L"TrDmTrPr", // Trust partner L"TrDmTrRt", // Root partner L"TrDmSAI", // Incoming auth. info L"TrDmSAO", // Outgoing auth. info L"TrDmForT", // Forest trust info L"TrDmCrSid", // Creator Sid L"KerOpts", // Kerberos authentication options L"KerMinT", // Kerberos Minimum ticket age L"KerMaxT", // Kerberos maximum ticket age L"KerMaxR", // Kerberos maximum renewal age L"KerProxy", // Kerberos proxy lifetime L"KerLogoff", // Kerberos force logoff duration L"BhvrVers", // Behavior-Version L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Eventlog\\Security", L"MaxSize", L"Retention", L"PseudoSystemCritical", L"PolSecretEncryptionKey", L"XRefDnsRoot", L"XRefNetbiosName", L"DummyLastName" }; PCWSTR UnicodeObjectTypeNames[DummyLastObject] = { L"NullObject", L"PolicyObject", L"TrustedDomainObject", L"UserAccountObject", L"SecretObject", L"AllObject", L"NewTrustedDomainObject" }; // // Referenced by LsapDbDsAttInfo // static LSAP_DB_DS_INFO StaticLsapDbDsAttInfo[DummyLastName + 1] = { {ATT_NT_SECURITY_DESCRIPTOR, LsapDbAttribSecDesc, LsapDsLocDs}, // Security Descriptor {0, LsapDbAttribUnknown, LsapDsLocUnknown }, // Privileges {ATT_SECURITY_IDENTIFIER, LsapDbAttribSid, LsapDsLocDs}, // Sid {0, LsapDbAttribUnknown, LsapDsLocDs}, // Name {0, LsapDbAttribUnknown, LsapDsLocUnknown}, // AdminMod {0, LsapDbAttribUnknown, LsapDsLocUnknown}, // OperMode {0, LsapDbAttribUnknown, LsapDsLocUnknown}, // QuotaLim {0, LsapDbAttribUnknown, LsapDsLocUnknown}, // DefQuota {0, LsapDbAttribUnknown, LsapDsLocUnknown}, // PrDomain {0, LsapDbAttribUnknown, LsapDsLocUnknown}, // Policy {0, LsapDbAttribUnknown, LsapDsLocUnknown}, // Accounts {0, LsapDbAttribUnknown, LsapDsLocUnknown}, // Domains {0, LsapDbAttribUnknown, LsapDsLocUnknown}, // Secrets {ATT_CURRENT_VALUE, LsapDbAttribPByte, LsapDsLocDs}, // CurrVal {ATT_PRIOR_VALUE, LsapDbAttribPByte, LsapDsLocDs}, // OldVal {ATT_LAST_SET_TIME, LsapDbAttribPByte, LsapDsLocDs}, // CupdTime {ATT_PRIOR_SET_TIME, LsapDbAttribPByte, LsapDsLocDs}, // OupdTime {0, LsapDbAttribUnknown, LsapDsLocUnknown}, // PolAdtLg {ATT_AUDITING_POLICY, LsapDbAttribPByte, LsapDsLocRegistry}, // PolAdtEv {0, LsapDbAttribUnknown, LsapDsLocRegistry}, // PolAcDmN {0, LsapDbAttribUnknown, LsapDsLocRegistry}, // PolAcDmS {0, LsapDbAttribUnknown, LsapDsLocRegistry}, // PolDnDDN {0, LsapDbAttribUnknown, LsapDsLocRegistry}, // PolDnTrN {0, LsapDbAttribUnknown, LsapDsLocRegistry}, // PolDnDmG {ATT_EFSPOLICY, LsapDbAttribPByte, LsapDsLocRegistry}, // PolEfDat {0, LsapDbAttribUnknown, LsapDsLocRegistry}, // PolPrDmN {0, LsapDbAttribUnknown, LsapDsLocRegistry}, // PolPrDmS {0, LsapDbAttribUnknown, LsapDsLocRegistry}, // PolPdAcN {0, LsapDbAttribUnknown, LsapDsLocRegistry}, // PolRepSc {0, LsapDbAttribUnknown, LsapDsLocRegistry}, // PolRepAc {0, LsapDbAttribUnknown, LsapDsLocUnknown}, // PolRevision {0, LsapDbAttribUnknown, LsapDsLocRegistry}, // PolMod {0, LsapDbAttribUnknown, LsapDsLocRegistry}, // PolState {0, LsapDbAttribUnknown, LsapDsLocUnknown}, // ActSysAc {ATT_TRUST_PARTNER, LsapDbAttribUnicode, LsapDsLocDs}, // TrDmName {ATT_FLAT_NAME, LsapDbAttribUnicode, LsapDsLocDs}, // TrDmTrPN {ATT_SECURITY_IDENTIFIER, LsapDbAttribSid, LsapDsLocDs}, // TrDmSid {0, LsapDbAttribUnknown, LsapDsLocUnknown}, // TrDmAcN {0, LsapDbAttribUnicode, LsapDsLocRegistry}, // TrDmCtN {ATT_TRUST_POSIX_OFFSET, LsapDbAttribULong, LsapDsLocDs}, // TrDmPxOf {0, LsapDbAttribUnicode, LsapDsLocRegistry}, // TrDmCtEn {ATT_TRUST_TYPE, LsapDbAttribULong, LsapDsLocDs}, // TrDmTrTy {ATT_TRUST_DIRECTION, LsapDbAttribULong, LsapDsLocDs}, // TrDmTrDi {ATT_TRUST_ATTRIBUTES, LsapDbAttribULong, LsapDsLocDs}, // TrDmTrLA {ATT_DOMAIN_CROSS_REF, LsapDbAttribDsName, LsapDsLocDs}, // TrDmTrPr {ATT_ROOT_TRUST, LsapDbAttribDsName, LsapDsLocDs}, // TrDmTrRt {ATT_TRUST_AUTH_INCOMING, LsapDbAttribPByte, LsapDsLocDs}, // TrDmSAI {ATT_TRUST_AUTH_OUTGOING, LsapDbAttribPByte, LsapDsLocDs}, // TrDmSAO {ATT_MS_DS_TRUST_FOREST_TRUST_INFO, LsapDbAttribPByte, LsapDsLocDs}, // TrDmForT {ATT_MS_DS_CREATOR_SID, LsapDbAttribSid, LsapDsLocDs}, // TrDmCrSid {ATT_AUTHENTICATION_OPTIONS, LsapDbAttribULong, LsapDsLocRegistry}, // KerOpts, {ATT_MIN_TICKET_AGE, LsapDbAttribPByte, LsapDsLocRegistry}, // KerMinT, {ATT_MAX_TICKET_AGE, LsapDbAttribPByte, LsapDsLocRegistry}, // KerMaxT, {ATT_MAX_RENEW_AGE, LsapDbAttribPByte, LsapDsLocRegistry}, // KerMaxR, {ATT_PROXY_LIFETIME, LsapDbAttribPByte, LsapDsLocRegistry}, // KerProxy, {ATT_FORCE_LOGOFF, LsapDbAttribPByte, LsapDsLocRegistry}, // KerLogoff {ATT_MS_DS_BEHAVIOR_VERSION, LsapDbAttribULong, LsapDsLocDs}, // BhvrVers {0, LsapDbAttribUnknown, LsapDsLocUnknown}, // Audit log {0, LsapDbAttribUnknown, LsapDsLocUnknown}, // Audit log size {0, LsapDbAttribUnknown, LsapDsLocUnknown}, // Audit Recored Retention Period {ATT_IS_CRITICAL_SYSTEM_OBJECT, LsapDbAttribULong, LsapDsLocDs}, // system critical component. {0, LsapDbAttribPByte, LsapDsLocRegistry},//PolSecretEncryptionKey {ATT_DNS_ROOT, LsapDbAttribUnicode, LsapDsLocDs}, // DNS name of cross-ref {ATT_NETBIOS_NAME, LsapDbAttribUnicode, LsapDsLocDs}, // Netbios domain of cross-ref {0, LsapDbAttribUnknown, LsapDsLocUnknown} // Dummy Last Name }; // // Initialize general array of Unicode Names // for (Index = SecDesc; Index < DummyLastName; Index++) { RtlInitUnicodeString( &LsapDbNames[Index], UnicodeNames[Index] ); } // // Initialize array of Unicode Names for Lsa Database Object Types // for (ObjectTypeId = NullObject; ObjectTypeId < DummyLastObject; ObjectTypeId++) { RtlInitUnicodeString( &LsapDbObjectTypeNames[ObjectTypeId], UnicodeObjectTypeNames[ObjectTypeId] ); } LsapDbDsAttInfo = StaticLsapDbDsAttInfo; return(Status); } NTSTATUS LsapDbInitializeContainingDirs() /*++ Routine Description: This function initializes Unicode strings for the names of the Containing directories for each object type. The Containing Directory is the Registry Key under which all objects of the given type are created and is relative to the LSA Database root. Note that objects of a given type all exist under a single Registry node, that is, the type of an object uniquely determines the name of its containing directory. NOTE: Containing Directories are used to produce Physical Object Names from Logical Object Names. The Physical Object Name is simply the Logical Object Name prepended with the Containing Directory Name and a "\". Arguments: None. Return Value: NTSTATUS - Standard Nt Result Code All Result Codes are generated by called routines. --*/ { NTSTATUS Status = STATUS_SUCCESS; LSAP_DB_OBJECT_TYPE_ID ObjectTypeId; PWSTR ContainingDirectories[DummyLastObject] = { L"", L"", L"Domains", L"Accounts", L"Secrets" }; // // Initialize the array of Unicode Strings indexed by object type setting // the Containing Directory name for each object type. // for (ObjectTypeId = PolicyObject; ObjectTypeId < DummyLastObject; ObjectTypeId++) { RtlInitUnicodeString( &LsapDbContDirs[ObjectTypeId], ContainingDirectories[ ObjectTypeId ] ); } return(Status); } NTSTATUS LsapDbInitializeReplication( ) /*++ Routine Description: This function performes LSA initialization for replication and turns on notification of LSA Database updates to the LSA Database Replicator. Arguments: None. Return Value: NTSTATUS - Standard Nt Result Code All Result Codes are generated by called routines. --*/ { NTSTATUS Status; ULONG PolicyModificationInfoLength = sizeof (POLICY_MODIFICATION_INFO); ULONG PolicyLsaServerRoleInfoLength = sizeof(POLICY_LSA_SERVER_ROLE_INFO); ULONG LargeIntegerLength = sizeof( LARGE_INTEGER ); // // Read the Policy Modification Info from the PolMod attribute // of the Policy object in the LSA Database. // Status = LsapDbReadAttributeObject( LsapDbHandle, &LsapDbNames[PolMod], (PVOID) &LsapDbState.PolicyModificationInfo, &PolicyModificationInfoLength ); if (!NT_SUCCESS(Status)) { goto InitializeReplicationError; } InitializeReplicationFinish: return(Status); InitializeReplicationError: goto InitializeReplicationFinish; } NTSTATUS LsapDbInitializeCipherKey( IN PUNICODE_STRING CipherSeed, IN PLSAP_CR_CIPHER_KEY *CipherKey ) /*++ Routine Description: This function initializes the LSA Database Cipher Key. Arguments: None. Return Value: NTSTATUS - Standard Nt Result Code All Result Codes are generated by called routines. --*/ { NTSTATUS Status; LSAP_CR_CLEAR_VALUE ClearCipherKey; PLSAP_CR_CIPHER_VALUE CipherCipherKey; *CipherKey = NULL; // // Initialize the Cipher key to a hardwired constant // encrypted with itself. // LsapCrUnicodeToClearValue( CipherSeed, &ClearCipherKey); Status = LsapCrEncryptValue( &ClearCipherKey, (PLSAP_CR_CIPHER_KEY) &ClearCipherKey, &CipherCipherKey ); if (!NT_SUCCESS(Status)) { LsapLogError( "LsapDbInitializeReplication: NtQuerySystemTime returned 0x%lx\n", Status ); } else { *CipherKey = ( PLSAP_CR_CIPHER_KEY )CipherCipherKey; } return(Status); } NTSTATUS LsapDbOpenRootRegistryKey( ) /*++ Routine Description: This function opens the LSA Database Root Registry Key. This has the fixed name \Registry\Machine\Security. Arguments: None. Return Value: NTSTATUS - Standard Nt Result Code All Result Codes are generated by called routines. --*/ { NTSTATUS Status; UNICODE_STRING DbRootRegKeyNameU; OBJECT_ATTRIBUTES DbAttributes; RtlInitUnicodeString( &DbRootRegKeyNameU, LSAP_DB_ROOT_REG_KEY_NAME ); InitializeObjectAttributes( &DbAttributes, &DbRootRegKeyNameU, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = RtlpNtOpenKey( (PHANDLE) &LsapDbState.DbRootRegKeyHandle, (KEY_READ | KEY_CREATE_SUB_KEY | KEY_ENUMERATE_SUB_KEYS | WRITE_DAC), &DbAttributes, 0 ); if (!NT_SUCCESS(Status)) { LsapLogError( "LsapDbOpenRootRegistryKey: Open Root Key for LSA Policy Database returned 0x%lx\n", Status ); goto OpenRootRegistryKeyError; } // // If there are no sub-keys, then we are in system-install. // Assign the initial protection of this hive. // Status = LsapAssignInitialHiveProtection( LsapDbState.DbRootRegKeyHandle ); if (!NT_SUCCESS(Status)) { LsapLogError( "LsapDbOpenRootRegistryKey: Couldn't assign initial hive protection 0x%lx\n", Status ); goto OpenRootRegistryKeyError; } OpenRootRegistryKeyFinish: return(Status); OpenRootRegistryKeyError: goto OpenRootRegistryKeyFinish; } NTSTATUS LsapAssignInitialHiveProtection( HANDLE HiveRoot ) /*++ Routine Description: This function assigns inheritable protection to the hive root key. It will only do this if the hive root has no sub-keys. This condition will only exist during system installation. WARNING - THIS ROUTINE IS TAILORED TO OPERATE ON THE \REGISTRY\SECURITY HIVE. As such, it expects the Root key to have exactly one sub-key (a link to the SAM hive) if the the database has NOT been initialized. Otherwise, it expects the LSA policy database keys to be present. Arguments: None. Return Value: STATUS_SUCCESS - Everything went fine. No indication of whether protection was necessarily assigned or not. All other status values are generated by called routines. --*/ { NTSTATUS Status; KEY_BASIC_INFORMATION DummyBuffer; ULONG IgnoreRequiredLength; SECURITY_DESCRIPTOR Sd; // // See if the hive has more than 1 sub-keys. // // Status = NtEnumerateKey( HiveRoot, 1, // Index - 0 is the SAM link, 1 is LSA policy database stuff KeyBasicInformation, // Name of key &DummyBuffer, sizeof(DummyBuffer), &IgnoreRequiredLength ); if (Status == STATUS_NO_MORE_ENTRIES) { // // We are initializing the system... // Apply a reasonable ACL to the hive root. // Status = LsapCreateDatabaseProtection( &Sd ); if (NT_SUCCESS(Status)) { Status = NtSetSecurityObject( HiveRoot, // Object to apply to DACL_SECURITY_INFORMATION, // Information to set (PSECURITY_DESCRIPTOR)&Sd // Descriptor ); } } else { Status = STATUS_SUCCESS; } return(Status); } NTSTATUS LsapCreateDatabaseProtection( 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( LsapLocalSystemSid ) + RtlLengthSid( LsapAliasAdminsSid ) + 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), LsapLocalSystemSid ); ASSERT(NT_SUCCESS(Status)); Status = RtlAddAccessAllowedAce( Dacl, ACL_REVISION2, (READ_CONTROL | WRITE_DAC), LsapAliasAdminsSid ); 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 LsapUpdateDatabaseProtection( IN ULONG Revision ) /*++ Routine Description: This function allocates and updates protection to assign to the LSA database. Arguments: Revision - New database revision level LSAP_DB_REVISION_1_3 -- grant POLICY_NOTIFICATION access to Administrators LSAP_DB_REVISION_1_6 -- grant POLICY_VIEW_LOCAL_INFORMATION | POLICY_LOOKUP_NAMES to AnonymousLogonSid LSAP_DB_REVISION_1_7 -- grant POLICY_NOTIFICATION to LocalService/NetworkService Return Value: STATUS_SUCCESS - The Security descriptor has been initialize. STATUS_NO_MEMORY - couldn't allocate memory for the protection info. --*/ { NTSTATUS Status; NTSTATUS TempStatus; PSECURITY_DESCRIPTOR CurrentSd = NULL; PSECURITY_DESCRIPTOR RelativeSd = NULL; ULONG RelativeSdLength; PSECURITY_DESCRIPTOR NewSd; SECURITY_DESCRIPTOR NewSdBuffer; USHORT i; PACL Dacl, NewDacl = NULL, TempAcl; PACE_HEADER Ace; PSID AceSid; BOOLEAN AdminAceFound = FALSE; BOOLEAN UpdatedSd = FALSE; // // First, read the initial security descriptor... // Status = LsapRegReadObjectSD( LsapPolicyHandle, &CurrentSd ); if ( !NT_SUCCESS( Status ) ) { goto Cleanup; } ASSERT( Revision == LSAP_DB_REVISION_1_3 || Revision == LSAP_DB_REVISION_1_6 || Revision == LSAP_DB_REVISION_1_7 ); NewSd = CurrentSd; if ( Revision == LSAP_DB_REVISION_1_3 || Revision == LSAP_DB_REVISION_1_6 || Revision == LSAP_DB_REVISION_1_7 ) { Dacl = RtlpDaclAddrSecurityDescriptor( ( PISECURITY_DESCRIPTOR )CurrentSd ); if ( Dacl ) { // // We'll have to find the ace for local system // Ace = ( PACE_HEADER )FirstAce( Dacl ); for(i = 0; i < Dacl->AceCount; i++, Ace = ( PACE_HEADER )NextAce( Ace ) ) { if ( IsObjectAceType( Ace ) ) { AceSid = RtlObjectAceSid( Ace ); } else { AceSid = &( ( PKNOWN_ACE )Ace )->SidStart; } // // When upgrading to revision 1_3, update the administrators ACE // if ( Revision == LSAP_DB_REVISION_1_3 && RtlEqualSid( AceSid, LsapAliasAdminsSid ) ) { // // Get the access mask and or in our new bit // if ( IsObjectAceType( Ace ) ) { if (( ((PKNOWN_OBJECT_ACE)Ace)->Mask & POLICY_NOTIFICATION ) == 0 ) { ((PKNOWN_OBJECT_ACE)Ace)->Mask |= POLICY_NOTIFICATION; UpdatedSd = TRUE; } } else { if (( ((PKNOWN_ACE)Ace)->Mask & POLICY_NOTIFICATION ) == 0 ) { ((PKNOWN_ACE)Ace)->Mask |= POLICY_NOTIFICATION; UpdatedSd = TRUE; } } AdminAceFound = TRUE; } } } // // If we didn't find an ACE to update, make sure to add it... // if ( !AdminAceFound ) { ULONG NewDaclLength; UpdatedSd = TRUE; NewDaclLength = Dacl ? Dacl->AclSize : 0; if ( Revision == LSAP_DB_REVISION_1_3 ) { NewDaclLength += ( ULONG )sizeof( ACCESS_ALLOWED_ACE ) + RtlLengthSid( LsapAliasAdminsSid ); } else if ( Revision == LSAP_DB_REVISION_1_6 ) { NewDaclLength += ( ULONG )sizeof( ACCESS_ALLOWED_ACE ) + RtlLengthSid( LsapAnonymousSid ); } else { ASSERT( Revision == LSAP_DB_REVISION_1_7 ); NewDaclLength += 2 * ( ULONG )sizeof( ACCESS_ALLOWED_ACE ) + RtlLengthSid( LsapLocalServiceSid ) + RtlLengthSid( LsapNetworkServiceSid ); } NewDacl = LsapAllocateLsaHeap( NewDaclLength ); if ( NewDacl == NULL ) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } if ( Dacl ) { RtlCopyMemory( NewDacl, Dacl, Dacl->AclSize ); NewDacl->AclSize = ( USHORT )NewDaclLength; } else { Status = RtlCreateAcl ( NewDacl, NewDaclLength, ACL_REVISION2 ); if ( !NT_SUCCESS(Status) ) { goto Cleanup; } } // // For version 1.3, grant administrators All access and Policy notification access // if ( Revision == LSAP_DB_REVISION_1_3 ) { Status = RtlAddAccessAllowedAce( NewDacl, ACL_REVISION2, POLICY_ALL_ACCESS | POLICY_NOTIFICATION, LsapAliasAdminsSid ); if ( !NT_SUCCESS(Status) ) { goto Cleanup; } // // For version 1.6, grant anonymous logon View Local and Lookup names access. // } else if ( Revision == LSAP_DB_REVISION_1_6) { Status = RtlAddAccessAllowedAce( NewDacl, ACL_REVISION2, POLICY_VIEW_LOCAL_INFORMATION | POLICY_LOOKUP_NAMES, LsapAnonymousSid ); if ( !NT_SUCCESS(Status) ) { goto Cleanup; } // // For version 1.7, grant Policy notification access to LocalService // and NetworkService // } else { ASSERT( Revision == LSAP_DB_REVISION_1_7 ); Status = RtlAddAccessAllowedAce( NewDacl, ACL_REVISION2, POLICY_NOTIFICATION, LsapLocalServiceSid ); if ( !NT_SUCCESS(Status) ) { goto Cleanup; } Status = RtlAddAccessAllowedAce( NewDacl, ACL_REVISION2, POLICY_NOTIFICATION, LsapNetworkServiceSid ); if ( !NT_SUCCESS(Status) ) { goto Cleanup; } } // // If the current security descriptor is self relative, // convert it to absolute so I can set the DACL on it. // if ( RtlpAreControlBitsSet( ( PISECURITY_DESCRIPTOR )CurrentSd, SE_SELF_RELATIVE ) ) { NewSd = &NewSdBuffer; Status = RtlCreateSecurityDescriptor( NewSd, SECURITY_DESCRIPTOR_REVISION ); if ( !NT_SUCCESS(Status) ) { goto Cleanup; } ((PISECURITY_DESCRIPTOR)NewSd)->Control = ((PISECURITY_DESCRIPTOR)CurrentSd)->Control; AceSid = RtlpOwnerAddrSecurityDescriptor( ( PISECURITY_DESCRIPTOR )CurrentSd ); if ( AceSid ) { ( (PISECURITY_DESCRIPTOR)NewSd )->Owner = AceSid; } AceSid = RtlpGroupAddrSecurityDescriptor( ( PISECURITY_DESCRIPTOR )CurrentSd ); if ( AceSid ) { ( ( PISECURITY_DESCRIPTOR )NewSd )->Group = AceSid; } TempAcl = RtlpSaclAddrSecurityDescriptor( ( PISECURITY_DESCRIPTOR )CurrentSd ); if ( TempAcl ) { ( ( PISECURITY_DESCRIPTOR )NewSd )->Sacl = TempAcl; } RtlpClearControlBits( ( PISECURITY_DESCRIPTOR )NewSd, SE_SELF_RELATIVE ); } // // Put the computed DACL onto the SD. // Status = RtlSetDaclSecurityDescriptor( NewSd, TRUE, NewDacl, FALSE ); if ( !NT_SUCCESS(Status) ) { goto Cleanup; } // // Convert the SD to self relative before writing it to the database. // RelativeSdLength = 0; Status = RtlMakeSelfRelativeSD( NewSd, NULL, &RelativeSdLength ); if (Status != STATUS_BUFFER_TOO_SMALL) { // This is the expected case if ( NT_SUCCESS(Status) ) { Status = STATUS_INTERNAL_ERROR; } goto Cleanup; } RelativeSd = LsapAllocateLsaHeap( RelativeSdLength ); if ( RelativeSd == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } Status = RtlMakeSelfRelativeSD( NewSd, RelativeSd, &RelativeSdLength ); if ( !NT_SUCCESS(Status) ) { goto Cleanup; } NewSd = RelativeSd; } } if ( UpdatedSd ) { ULONG NewSdLength; // // Set the security descriptor back on the object // NewSdLength = RtlLengthSecurityDescriptor( NewSd ); // // Add a Registry transaction to write the Security Descriptor as the // value of the new object's SecDesc subkey. // Status = LsapDbReferenceObject( LsapDbHandle, 0, PolicyObject, PolicyObject, LSAP_DB_LOCK | LSAP_DB_START_TRANSACTION ); if ( !NT_SUCCESS(Status) ) { goto Cleanup; } Status = LsapDbWriteAttributeObject( LsapPolicyHandle, &LsapDbNames[ SecDesc ], NewSd, NewSdLength ); TempStatus = LsapDbDereferenceObject( &LsapDbHandle, PolicyObject, PolicyObject, (LSAP_DB_LOCK | LSAP_DB_OMIT_REPLICATOR_NOTIFICATION | // Each BDC should upgrade its own database LSAP_DB_FINISH_TRANSACTION), (SECURITY_DB_DELTA_TYPE) 0, Status ); if ( !NT_SUCCESS(TempStatus) ) { if (NT_SUCCESS(Status) ) { Status = TempStatus; } } } Cleanup: // // Free the security descriptor // LsapFreeLsaHeap( CurrentSd ); LsapFreeLsaHeap( RelativeSd ); LsapFreeLsaHeap( NewDacl ); return( Status ); } NTSTATUS LsapDbInitializeLock( ) /*++ Routine Description: This function initializes the LSA Database Lock. It is called once only, during LSA Database initialization. Arguments: None. Return Value: NTSTATUS - Standard Nt Result Code All Result Codes are generated by called routines. --*/ { NTSTATUS Status; Status = SafeInitializeCriticalSection( &LsapDbState.AccountLock, ( DWORD )ACCOUNT_LOCK_ENUM ); if (!NT_SUCCESS(Status) ) { goto Cleanup; } Status = SafeInitializeCriticalSection( &LsapDbState.PolicyLock, ( DWORD )POLICY_LOCK_ENUM ); if (!NT_SUCCESS(Status) ) { goto Cleanup; } Status = SafeInitializeCriticalSection( &LsapDbState.SecretLock, ( DWORD )SECRET_LOCK_ENUM ); if (!NT_SUCCESS(Status) ) { goto Cleanup; } Status = SafeInitializeCriticalSection( &LsapDbState.RegistryLock, ( DWORD )REGISTRY_LOCK_ENUM ); if (!NT_SUCCESS(Status) ) { goto Cleanup; } Status = SafeInitializeCriticalSection( &LsapDbState.HandleTableLock, ( DWORD )HANDLE_TABLE_LOCK_ENUM ); if (!NT_SUCCESS(Status) ) { goto Cleanup; } try { SafeInitializeResource( &LsapDbState.PolicyCacheLock, ( DWORD )POLICY_CACHE_LOCK_ENUM ); Status = STATUS_SUCCESS; } except( EXCEPTION_EXECUTE_HANDLER ) { Status = GetExceptionCode(); } if (!NT_SUCCESS(Status) ) { goto Cleanup; } // // Initialize the Resource for the Trusted Domain List. // InitializeListHead( &LsapDbTrustedDomainList.ListHead ); LsapDbTrustedDomainList.TrustedDomainCount = 0; LsapDbTrustedDomainList.CurrentSequenceNumber = 0; LsapDbMakeCacheInvalid( TrustedDomainObject ); try { SafeInitializeResource( &LsapDbTrustedDomainList.Resource, ( DWORD )TRUST_LOCK_ENUM ); Status = STATUS_SUCCESS ; } except (EXCEPTION_EXECUTE_HANDLER ) { Status = GetExceptionCode(); } if ( !NT_SUCCESS( Status ) ) { goto Cleanup; } try { // // do not use the safelock library for ScePolicyLock // because it is usually acquired on one thread and // released on another // RtlInitializeResource( &LsapDbState.ScePolicyLock ); Status = STATUS_SUCCESS ; } except ( EXCEPTION_EXECUTE_HANDLER ) { Status = GetExceptionCode(); } if (!NT_SUCCESS( Status )) { goto Cleanup; } LsapDbState.SceSyncEvent = CreateEvent( NULL, TRUE, TRUE, NULL ); if ( LsapDbState.SceSyncEvent == NULL ) { Status = GetLastError(); goto Cleanup; } #if DBG try { SafeInitializeResource( &LsapDsThreadInfoListResource, ( DWORD )THREAD_INFO_LIST_LOCK_ENUM ); Status = STATUS_SUCCESS ; } except ( EXCEPTION_EXECUTE_HANDLER ) { Status = GetExceptionCode(); } if ( !NT_SUCCESS( Status ) ) { goto Cleanup; } RtlZeroMemory( &LsapDsThreadInfoList, sizeof( LSADS_THREAD_INFO_NODE ) * LSAP_THREAD_INFO_LIST_MAX ); #endif Status = STATUS_SUCCESS; Cleanup: return( Status ); } NTSTATUS LsapDbInitializeWellKnownValues( ) /*++ Routine Description: This function initializes the well-known values used by LSA. Arguments: None. Return Value: NTSTATUS - Standard Nt Result Code All Result Codes are generated by called routines. --*/ { NTSTATUS Status; BOOLEAN b; // // Initialize the Well Known Sids // b = LsapDbInitializeWellKnownSids( &WellKnownSids ); if (!b ) { Status = STATUS_UNSUCCESSFUL; goto InitializeWellKnownValuesError; } // // Initialize the well known privilege values // Status = LsapDbInitializeWellKnownPrivs(); if (!NT_SUCCESS(Status)) { goto InitializeWellKnownValuesError; } InitializeWellKnownValuesFinish: return(Status); InitializeWellKnownValuesError: goto InitializeWellKnownValuesFinish; } NTSTATUS LsapDbInitializeWellKnownPrivs( ) /*++ Routine Description: This function initializes the well-known privilege values. Arguments: None. Return Value: NTSTATUS - Standard Nt Result Code Currently, only STATUS_SUCCESS is returned. --*/ { LsapTcbPrivilege = RtlConvertLongToLuid(SE_TCB_PRIVILEGE); return STATUS_SUCCESS; } BOOLEAN LsapDbInitializeWellKnownSids( OUT PLSAP_WELL_KNOWN_SID_ENTRY *WellKnownSids ) /*++ Routine Description: This function initializes the Well-Known Sids Arguments: WellKnownSids - Receives a pointer to a newly created table of the Well Known Sids. Return Value: BOOLEAN - TRUE if successful, else FALSE. --*/ { NTSTATUS Status; BOOLEAN BooleanStatus = TRUE; LSAP_WELL_KNOWN_SID_INDEX WellKnownSidIndex; ULONG SubAuthorities[LSAP_WELL_KNOWN_MAX_SUBAUTH_LEVEL]; ULONG OutputWellKnownSidsLength; PLSAP_WELL_KNOWN_SID_ENTRY OutputWellKnownSids = NULL; UNICODE_STRING SidName, NtAuthorityName, UsersName; HMODULE StringsResource; SID_IDENTIFIER_AUTHORITY InternetSiteAuthority = SECURITY_INTERNETSITE_AUTHORITY; // // Get the message resource we need to get the SID names from // StringsResource = (HMODULE) LoadLibrary( L"LSASRV.DLL" ); if (StringsResource == NULL) { return(FALSE); } Status = LsapGetMessageStrings( StringsResource, LSAP_SID_NAME_NT_AUTHORITY, &NtAuthorityName, 0, NULL ); ASSERT(NT_SUCCESS(Status)); // // Allocate memory for the table of Sids. // OutputWellKnownSidsLength = LsapDummyLastSidIndex * sizeof(LSAP_WELL_KNOWN_SID_ENTRY); OutputWellKnownSids = RtlAllocateHeap( RtlProcessHeap(), 0, OutputWellKnownSidsLength ); if (OutputWellKnownSids == NULL) { goto InitializeWellKnownSidsError; } // // Allocate and initialize the universal SIDs // Status = LsapGetMessageStrings( StringsResource, LSAP_SID_NAME_NULL, &SidName, 0, NULL ); ASSERT(NT_SUCCESS(Status)); SubAuthorities[0] = SECURITY_NULL_RID; if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapNullSidIndex, &LsapNullSidAuthority, 1, SubAuthorities, SidName.Buffer, L"", SidTypeWellKnownGroup )) { goto InitializeWellKnownSidsError; } Status = LsapGetMessageStrings( StringsResource, LSAP_SID_NAME_WORLD, &SidName, 0, NULL ); ASSERT(NT_SUCCESS(Status)); SubAuthorities[0] = SECURITY_WORLD_RID; if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapWorldSidIndex, &LsapWorldSidAuthority, 1, SubAuthorities, SidName.Buffer, L"", SidTypeWellKnownGroup )) { goto InitializeWellKnownSidsError; } Status = LsapGetMessageStrings( StringsResource, LSAP_SID_NAME_LOCAL, &SidName, 0, NULL ); ASSERT(NT_SUCCESS(Status)); SubAuthorities[0] = SECURITY_LOCAL_RID; if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapLocalSidIndex, &LsapLocalSidAuthority, 1, SubAuthorities, SidName.Buffer, L"", SidTypeWellKnownGroup )) { goto InitializeWellKnownSidsError; } Status = LsapGetMessageStrings( StringsResource, LSAP_SID_NAME_CREATOR_OWNER, &SidName, 0, NULL ); ASSERT(NT_SUCCESS(Status)); SubAuthorities[0] = SECURITY_CREATOR_OWNER_RID; if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapCreatorOwnerSidIndex, &LsapCreatorSidAuthority, 1, SubAuthorities, SidName.Buffer, L"", SidTypeWellKnownGroup )) { goto InitializeWellKnownSidsError; } Status = LsapGetMessageStrings( StringsResource, LSAP_SID_NAME_CREATOR_GROUP, &SidName, 0, NULL ); ASSERT(NT_SUCCESS(Status)); SubAuthorities[0] = SECURITY_CREATOR_GROUP_RID; if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapCreatorGroupSidIndex, &LsapCreatorSidAuthority, 1, SubAuthorities, SidName.Buffer, L"", SidTypeWellKnownGroup )) { goto InitializeWellKnownSidsError; } Status = LsapGetMessageStrings( StringsResource, LSAP_SID_NAME_CREATOR_OWNER_SERVER, &SidName, 0, NULL ); ASSERT(NT_SUCCESS(Status)); SubAuthorities[0] = SECURITY_CREATOR_OWNER_SERVER_RID; if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapCreatorOwnerServerSidIndex, &LsapCreatorSidAuthority, 1, SubAuthorities, SidName.Buffer, L"", SidTypeWellKnownGroup )) { goto InitializeWellKnownSidsError; } Status = LsapGetMessageStrings( StringsResource, LSAP_SID_NAME_CREATOR_GROUP_SERVER, &SidName, 0, NULL ); ASSERT(NT_SUCCESS(Status)); SubAuthorities[0] = SECURITY_CREATOR_GROUP_SERVER_RID; if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapCreatorGroupServerSidIndex, &LsapCreatorSidAuthority, 1, SubAuthorities, SidName.Buffer, L"", SidTypeWellKnownGroup )) { goto InitializeWellKnownSidsError; } // // Initialize the Nt well-known Sids // Status = LsapGetMessageStrings( StringsResource, LSAP_SID_NAME_NT_DOMAIN, &SidName, 0, NULL ); ASSERT(NT_SUCCESS(Status)); if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapNtAuthoritySidIndex, &LsapNtAuthority, 0, NULL, L"", SidName.Buffer, SidTypeDomain )) { goto InitializeWellKnownSidsError; } Status = LsapGetMessageStrings( StringsResource, LSAP_SID_NAME_DIALUP, &SidName, 0, NULL ); ASSERT(NT_SUCCESS(Status)); SubAuthorities[0] = SECURITY_DIALUP_RID; if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapDialupSidIndex, &LsapNtAuthority, 1, SubAuthorities, SidName.Buffer, NtAuthorityName.Buffer, SidTypeWellKnownGroup )) { goto InitializeWellKnownSidsError; } Status = LsapGetMessageStrings( StringsResource, LSAP_SID_NAME_NETWORK, &SidName, 0, NULL ); ASSERT(NT_SUCCESS(Status)); SubAuthorities[0] = SECURITY_NETWORK_RID; if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapNetworkSidIndex, &LsapNtAuthority, 1, SubAuthorities, SidName.Buffer, NtAuthorityName.Buffer, SidTypeWellKnownGroup )) { goto InitializeWellKnownSidsError; } Status = LsapGetMessageStrings( StringsResource, LSAP_SID_NAME_BATCH, &SidName, 0, NULL ); ASSERT(NT_SUCCESS(Status)); SubAuthorities[0] = SECURITY_BATCH_RID; if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapBatchSidIndex, &LsapNtAuthority, 1, SubAuthorities, SidName.Buffer, NtAuthorityName.Buffer, SidTypeWellKnownGroup )) { goto InitializeWellKnownSidsError; } Status = LsapGetMessageStrings( StringsResource, LSAP_SID_NAME_INTERACTIVE, &SidName, 0, NULL ); ASSERT(NT_SUCCESS(Status)); SubAuthorities[0] = SECURITY_INTERACTIVE_RID; if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapInteractiveSidIndex, &LsapNtAuthority, 1, SubAuthorities, SidName.Buffer, NtAuthorityName.Buffer, SidTypeWellKnownGroup )) { goto InitializeWellKnownSidsError; } Status = LsapGetMessageStrings( StringsResource, LSAP_SID_NAME_REMOTE_INTERACTIVE, &SidName, 0, NULL); ASSERT(NT_SUCCESS(Status)); SubAuthorities[0] = SECURITY_REMOTE_LOGON_RID; if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapRemoteInteractiveSidIndex, &LsapNtAuthority, 1, SubAuthorities, SidName.Buffer, NtAuthorityName.Buffer, SidTypeWellKnownGroup )) { goto InitializeWellKnownSidsError; } Status = LsapGetMessageStrings( StringsResource, LSAP_SID_NAME_THIS_ORGANIZATION, &SidName, 0, NULL); ASSERT(NT_SUCCESS(Status)); SubAuthorities[0] = SECURITY_THIS_ORGANIZATION_RID; if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapThisOrganizationSidIndex, &LsapNtAuthority, 1, SubAuthorities, SidName.Buffer, NtAuthorityName.Buffer, SidTypeWellKnownGroup )) { goto InitializeWellKnownSidsError; } Status = LsapGetMessageStrings( StringsResource, LSAP_SID_NAME_OTHER_ORGANIZATION, &SidName, 0, NULL); ASSERT(NT_SUCCESS(Status)); SubAuthorities[0] = SECURITY_OTHER_ORGANIZATION_RID; if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapOtherOrganizationSidIndex, &LsapNtAuthority, 1, SubAuthorities, SidName.Buffer, NtAuthorityName.Buffer, SidTypeWellKnownGroup )) { goto InitializeWellKnownSidsError; } Status = LsapGetMessageStrings( StringsResource, LSAP_SID_NAME_TERMINAL_SERVER, &SidName, 0, NULL ); ASSERT(NT_SUCCESS(Status)); SubAuthorities[0] = SECURITY_TERMINAL_SERVER_RID; if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapTerminalServerSidIndex, &LsapNtAuthority, 1, SubAuthorities, SidName.Buffer, NtAuthorityName.Buffer, SidTypeWellKnownGroup )) { goto InitializeWellKnownSidsError; } Status = LsapGetMessageStrings( StringsResource, LSAP_SID_NAME_SERVICE, &SidName, 0, NULL ); ASSERT(NT_SUCCESS(Status)); SubAuthorities[0] = SECURITY_SERVICE_RID; if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapServiceSidIndex, &LsapNtAuthority, 1, SubAuthorities, SidName.Buffer, NtAuthorityName.Buffer, SidTypeWellKnownGroup )) { goto InitializeWellKnownSidsError; } Status = LsapGetMessageStrings( StringsResource, LSAP_SID_NAME_ANONYMOUS, &SidName, 0, NULL ); ASSERT(NT_SUCCESS(Status)); SubAuthorities[0] = SECURITY_ANONYMOUS_LOGON_RID; if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapAnonymousSidIndex, &LsapNtAuthority, 1, SubAuthorities, SidName.Buffer, NtAuthorityName.Buffer, SidTypeWellKnownGroup )) { goto InitializeWellKnownSidsError; } Status = LsapGetMessageStrings( StringsResource, LSAP_SID_NAME_PROXY, &SidName, 0, NULL ); ASSERT(NT_SUCCESS(Status)); SubAuthorities[0] = SECURITY_PROXY_RID; if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapProxySidIndex, &LsapNtAuthority, 1, SubAuthorities, SidName.Buffer, NtAuthorityName.Buffer, SidTypeWellKnownGroup )) { goto InitializeWellKnownSidsError; } Status = LsapGetMessageStrings( StringsResource, LSAP_SID_NAME_SERVER, &SidName, 0, NULL ); ASSERT(NT_SUCCESS(Status)); SubAuthorities[0] = SECURITY_SERVER_LOGON_RID; if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapServerSidIndex, &LsapNtAuthority, 1, SubAuthorities, SidName.Buffer, NtAuthorityName.Buffer, SidTypeWellKnownGroup )) { goto InitializeWellKnownSidsError; } Status = LsapGetMessageStrings( StringsResource, LSAP_SID_NAME_SELF, &SidName, 0, NULL ); ASSERT(NT_SUCCESS(Status)); SubAuthorities[0] = SECURITY_PRINCIPAL_SELF_RID; if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapSelfSidIndex, &LsapNtAuthority, 1, SubAuthorities, SidName.Buffer, NtAuthorityName.Buffer, SidTypeWellKnownGroup )) { goto InitializeWellKnownSidsError; } Status = LsapGetMessageStrings( StringsResource, LSAP_SID_NAME_AUTHENTICATED_USER, &SidName, 0, NULL ); ASSERT(NT_SUCCESS(Status)); SubAuthorities[0] = SECURITY_AUTHENTICATED_USER_RID; if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapAuthenticatedUserSidIndex, &LsapNtAuthority, 1, SubAuthorities, SidName.Buffer, NtAuthorityName.Buffer, SidTypeWellKnownGroup )) { goto InitializeWellKnownSidsError; } // // Add any Logon Id well known sids here. // SubAuthorities[0] = SECURITY_LOGON_IDS_RID; SubAuthorities[1] = 0; SubAuthorities[2] = 0; if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapLogonSidIndex, &LsapNtAuthority, 3, SubAuthorities, L"", NtAuthorityName.Buffer, SidTypeWellKnownGroup )) { goto InitializeWellKnownSidsError; } Status = LsapGetMessageStrings( StringsResource, LSAP_SID_NAME_SYSTEM, &SidName, 0, NULL ); ASSERT(NT_SUCCESS(Status)); SubAuthorities[0] = SECURITY_LOCAL_SYSTEM_RID; if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapLocalSystemSidIndex, &LsapNtAuthority, 1, SubAuthorities, SidName.Buffer, NtAuthorityName.Buffer, SidTypeWellKnownGroup )) { goto InitializeWellKnownSidsError; } Status = LsapGetMessageStrings( StringsResource, LSAP_SID_NAME_LOCALSERVICE, &SidName, 0, NULL ); ASSERT(NT_SUCCESS(Status)); SubAuthorities[0] = SECURITY_LOCAL_SERVICE_RID; if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapLocalServiceSidIndex, &LsapNtAuthority, 1, SubAuthorities, SidName.Buffer, NtAuthorityName.Buffer, SidTypeWellKnownGroup )) { goto InitializeWellKnownSidsError; } Status = LsapGetMessageStrings( StringsResource, LSAP_SID_NAME_NETWORKSERVICE, &SidName, 0, NULL ); ASSERT(NT_SUCCESS(Status)); SubAuthorities[0] = SECURITY_NETWORK_SERVICE_RID; if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapNetworkServiceSidIndex, &LsapNtAuthority, 1, SubAuthorities, SidName.Buffer, NtAuthorityName.Buffer, SidTypeWellKnownGroup )) { goto InitializeWellKnownSidsError; } Status = LsapGetMessageStrings( StringsResource, LSAP_SID_NAME_RESTRICTED, &SidName, 0, NULL ); ASSERT(NT_SUCCESS(Status)); SubAuthorities[0] = SECURITY_RESTRICTED_CODE_RID; if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapRestrictedSidIndex, &LsapNtAuthority, 1, SubAuthorities, SidName.Buffer, NtAuthorityName.Buffer, SidTypeWellKnownGroup )) { goto InitializeWellKnownSidsError; } Status = LsapGetMessageStrings( StringsResource, LSAP_SID_NAME_INTERNET, &SidName, 0, NULL ); ASSERT(NT_SUCCESS(Status)); if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapInternetDomainIndex, &InternetSiteAuthority, 0, SubAuthorities, L"", SidName.Buffer, SidTypeDomain )) { goto InitializeWellKnownSidsError; } // // Add any security package well known sids // SubAuthorities[0] = SECURITY_PACKAGE_BASE_RID; SubAuthorities[1] = RPC_C_AUTHN_WINNT; Status = LsapGetMessageStrings( StringsResource, LSAP_SID_NAME_NTLM_AUTH, &SidName, 0, NULL ); ASSERT(NT_SUCCESS(Status)); if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapNTLMAuthenticationSidIndex, &LsapNtAuthority, SECURITY_PACKAGE_RID_COUNT, SubAuthorities, SidName.Buffer, NtAuthorityName.Buffer, SidTypeWellKnownGroup )) { goto InitializeWellKnownSidsError; } SubAuthorities[1] = RPC_C_AUTHN_DIGEST; Status = LsapGetMessageStrings( StringsResource, LSAP_SID_NAME_DIGEST_AUTH, &SidName, 0, NULL ); ASSERT(NT_SUCCESS(Status)); if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapDigestAuthenticationSidIndex, &LsapNtAuthority, SECURITY_PACKAGE_RID_COUNT, SubAuthorities, SidName.Buffer, NtAuthorityName.Buffer, SidTypeWellKnownGroup )) { goto InitializeWellKnownSidsError; } SubAuthorities[1] = RPC_C_AUTHN_GSS_SCHANNEL; Status = LsapGetMessageStrings( StringsResource, LSAP_SID_NAME_SCHANNEL_AUTH, &SidName, 0, NULL ); ASSERT(NT_SUCCESS(Status)); if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapSChannelAuthenticationSidIndex, &LsapNtAuthority, SECURITY_PACKAGE_RID_COUNT, SubAuthorities, SidName.Buffer, NtAuthorityName.Buffer, SidTypeWellKnownGroup )) { goto InitializeWellKnownSidsError; } // // Initialize SIDs from the BUILTIN domain. Leave the Name // field blank for the aliases as they can be renamed while // the system is running (and the lookup code will fall // through to SAM for these). // Status = LsapGetMessageStrings( StringsResource, LSAP_SID_NAME_BUILTIN, &SidName, 0, NULL ); ASSERT(NT_SUCCESS(Status)); SubAuthorities[0] = SECURITY_BUILTIN_DOMAIN_RID; if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapBuiltInDomainSidIndex, &LsapNtAuthority, 1, SubAuthorities, L"", SidName.Buffer, SidTypeDomain )) { goto InitializeWellKnownSidsError; } SubAuthorities[1] = DOMAIN_ALIAS_RID_USERS; if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapAliasUsersSidIndex, &LsapNtAuthority, 2, SubAuthorities, L"", SidName.Buffer, SidTypeAlias )) { goto InitializeWellKnownSidsError; } SubAuthorities[1] = DOMAIN_ALIAS_RID_ADMINS; if (!LsapDbInitializeWellKnownSid( OutputWellKnownSids, LsapAliasAdminsSidIndex, &LsapNtAuthority, 2, SubAuthorities, L"", SidName.Buffer, SidTypeAlias )) { goto InitializeWellKnownSidsError; } Status = LsapGetMessageStrings( StringsResource, LSAP_SID_NAME_USERS, &SidName, 0, NULL ); ASSERT(NT_SUCCESS(Status)); // // Check if all Sids initialized. // #ifdef LSAP_DEBUG_MESSAGE_STRINGS DbgPrint("\nLSA (dbinit): Displaying all well known sids...\n\n"); #endif //LSAP_DEBUG_MESSAGE_STRINGS for (WellKnownSidIndex = LsapNullSidIndex; WellKnownSidIndex < LsapDummyLastSidIndex; WellKnownSidIndex++) { #ifdef LSAP_DEBUG_MESSAGE_STRINGS DbgPrint(" *%wZ* : *%wZ*\n", &OutputWellKnownSids[WellKnownSidIndex].DomainName, &OutputWellKnownSids[WellKnownSidIndex].Name); #endif //LSAP_DEBUG_MESSAGE_STRINGS if (OutputWellKnownSids[WellKnownSidIndex].Sid == NULL) { #if DBG DbgPrint( "Well Known Sid Index %d not initialized\n", WellKnownSidIndex ); #endif //DBG } } *WellKnownSids = OutputWellKnownSids; return(TRUE); InitializeWellKnownSidsError: return(FALSE); } BOOLEAN LsapDbInitializeWellKnownSid( OUT PLSAP_WELL_KNOWN_SID_ENTRY WellKnownSids, IN LSAP_WELL_KNOWN_SID_INDEX WellKnownSidIndex, IN PSID_IDENTIFIER_AUTHORITY IdentifierAuthority, IN UCHAR SubAuthorityCount, IN PULONG SubAuthorities, IN PWSTR Name, IN PWSTR DomainName, IN SID_NAME_USE Use ) /*++ Routine Description: This function initializes an entry in the specified well-known Sid table. The entry contains the well known Sid and its name. Arguments: WellKnownSids - Pointer to the first entry in the Well Known Sid table. WellKnownSidIndex - Index into table of Well Known Sids. Sid - Receives a pointer to a Sid with the correct size for the number of subauthorities specified. IdentifierAuthority - Pointer to Identifier authority. SubAuthorityCount - Count of SubAuthorities SubAuthorities - Array of SubAuthorities. Name - Pointer to Unicode Name buffer containing the Sid's Name DomainName - Pointer to Unicode Name buffer containing the Sids Domain Name (if any) or descriptive text, such as "Well Known Group" for Sids of Well Known Groups SidNameUse - Specifies code for Sid's Use. The following values may be specified: SidTypeUser SidTypeGroup SidTypeDomain SidTypeAlias SidTypeWellKnownGroup SidTypeDeletedAccount SidTypeInvalid SidTypeUnknown Return Value: BOOLEAN - TRUE if Sid initialized, else FALSE. --*/ { PLSAP_WELL_KNOWN_SID_ENTRY WellKnownSidEntry = &WellKnownSids[WellKnownSidIndex]; PSID OutputSid = NULL; OutputSid = RtlAllocateHeap( RtlProcessHeap(), 0, RtlLengthRequiredSid(SubAuthorityCount) ); if (OutputSid == NULL) { goto InitializeWellKnownSidError; } RtlInitializeSid( OutputSid, IdentifierAuthority, SubAuthorityCount); if (SubAuthorityCount != 0) { RtlCopyMemory( RtlSubAuthoritySid( OutputSid, 0 ), SubAuthorities, SubAuthorityCount * sizeof(ULONG) ); } WellKnownSidEntry->Sid = OutputSid; // // Fill in the Domain Name // RtlInitUnicodeString( &WellKnownSidEntry->DomainName, DomainName ); // // Fill in the Use and Name. // WellKnownSidEntry->Use = Use; RtlInitUnicodeString( &WellKnownSidEntry->Name, Name ); return(TRUE); InitializeWellKnownSidError: #if DBG DbgPrint("LSA Initialization of Well Known Sids Failed\n"); DbgPrint("Insufficient memory resources\n"); #endif // DBG return(FALSE); } NTSTATUS LsapGetMessageStrings( 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 strings will be NULL terminated. 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. The string will be null terminated. 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. The string will be null terminated. Return Value: None. --*/ { #ifdef LSAP_DEBUG_MESSAGE_STRINGS DbgPrint("LSA (dbinit): String 1 -\n"); DbgPrint(" Index: 0x%lx\n", Index1); #endif //LSAP_DEBUG_MESSAGE_STRINGS 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. However, we do need to null terminate our string // so we will convert the 0x0d into a null terminator. // // 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. // ASSERT(String1->MaximumLength >= 2); // We always expect cr/lf on our strings // // Adjust character count // String1->MaximumLength -= 1; // For the lf - we'll convert the cr. // // Set null terminator // String1->Buffer[String1->MaximumLength - 1] = 0; // // Change lengths to byte count instead of character count // String1->MaximumLength *= sizeof(WCHAR); // to make it a byte count String1->Length = String1->MaximumLength - sizeof(WCHAR); #ifdef LSAP_DEBUG_MESSAGE_STRINGS DbgPrint(" String: %wZ\n", String1); DbgPrint(" Max: (0x%lx)\n", String1->MaximumLength); DbgPrint(" Cur: (0x%lx)\n", String1->Length); DbgPrint(" "); { ULONG i; for (i=0; iMaximumLength; i++) { DbgPrint("%2x ", (*((PUCHAR)String1->Buffer)+i)); } DbgPrint("\n"); } #endif //LSAP_DEBUG_MESSAGE_STRINGS } 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. However, we do need to null terminate our string // so we will convert the 0x0d into a null terminator. // // 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. // ASSERT(String2->MaximumLength >= 2); // We always expect cr/lf on our strings // // Adjust character count // String2->MaximumLength -= 1; // For the lf - we'll convert the cr. // // Set null terminator // String2->Buffer[String2->MaximumLength - 1] = 0; // // Change lengths to byte count instead of character count // String2->MaximumLength *= sizeof(WCHAR); // to make it a byte count String2->Length = String2->MaximumLength - sizeof(WCHAR); #ifdef LSAP_DEBUG_MESSAGE_STRINGS DbgPrint(" String: %wZ\n", String2); DbgPrint(" Max: (0x%lx)\n", String2->MaximumLength); DbgPrint(" Cur: (0x%lx)\n", String2->Length); DbgPrint(" "); { ULONG i; for (i=0; iMaximumLength; i++) { DbgPrint("%2x ", (*((PUCHAR)String2->Buffer)+i)); } DbgPrint("\n"); } #endif //LSAP_DEBUG_MESSAGE_STRINGS } return(STATUS_SUCCESS); } #if defined(REMOTE_BOOT) VOID LsapDbInitializeRemoteBootState( ) /*++ Routine Description: This function initializes the remote boot state used by LSA. Arguments: None. Return Value: NTSTATUS - Standard Nt Result Code All Result Codes are generated by called routines. --*/ { NTSTATUS Status ; HANDLE RdrDevice ; UNICODE_STRING String ; OBJECT_ATTRIBUTES ObjA ; IO_STATUS_BLOCK IoStatus ; // // This is the default if anything goes wrong. // LsapDbState.RemoteBootState = LSAP_DB_REMOTE_BOOT_NO_NOTIFICATION; // // Open the redirector device. // RtlInitUnicodeString( &String, DD_NFS_DEVICE_NAME_U ); InitializeObjectAttributes( &ObjA, &String, 0, 0, 0); Status = NtOpenFile( &RdrDevice, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, &ObjA, &IoStatus, FILE_SHARE_READ | FILE_SHARE_WRITE, 0 ); if ( !NT_SUCCESS( Status ) ) { DebugLog(( DEB_TRACE, "FAILED to open %ws, status %x\n", String.Buffer, Status )); return; } Status = NtFsControlFile( RdrDevice, NULL, NULL, NULL, &IoStatus, FSCTL_LMMR_RI_IS_PASSWORD_SETTABLE, NULL, 0, NULL, 0 ); if ( Status == STATUS_SUCCESS ) { LsapDbState.RemoteBootState = LSAP_DB_REMOTE_BOOT_NOTIFY; } else if ( Status == STATUS_UNSUCCESSFUL ) { LsapDbState.RemoteBootState = LSAP_DB_REMOTE_BOOT_CANT_NOTIFY; } NtClose(RdrDevice); } #endif // defined(REMOTE_BOOT) NTSTATUS LsapGenerateRandomDomainSid( OUT PSID NewDomainSid ) /*++ Routine Description: This function will generate a random sid to be used for the new account domain sid during setup. Arguments: NewDomainSid - Where the new domain sid is returned. Freed via RtlFreeSid() Return Values: STATUS_SUCCESS -- Success. STATUS_INSUFFICIENT_RESOURCES -- A memory allocation failed --*/ { NTSTATUS Status = STATUS_UNSUCCESSFUL; ULONG SubAuth1, SubAuth2, SubAuth3; SID_IDENTIFIER_AUTHORITY IdentifierAuthority = SECURITY_NT_AUTHORITY; if(!RtlGenRandom( &SubAuth1, sizeof(SubAuth1) )) { ASSERT( NT_SUCCESS(Status) ); return Status; } if(!RtlGenRandom( &SubAuth2, sizeof(SubAuth2) )) { ASSERT( NT_SUCCESS(Status) ); return Status; } if(!RtlGenRandom( &SubAuth3, sizeof(SubAuth3) )) { ASSERT( NT_SUCCESS(Status) ); return Status; } Status = RtlAllocateAndInitializeSid( &IdentifierAuthority, 4, 0x15, SubAuth1, SubAuth2, SubAuth3, 0, 0, 0, 0, NewDomainSid ); return( Status ); } NTSTATUS LsapSetupInitialize( VOID ) /*++ Routine Description: This function will generate a random sid to be used for the new account domain sid during setup. Arguments: NewDomainSid - Where the new domain sid is returned. Freed via RtlFreeSid() Return Values: STATUS_SUCCESS -- Success. STATUS_INSUFFICIENT_RESOURCES -- A memory allocation failed --*/ { NTSTATUS Status = STATUS_SUCCESS; PSID NewDomainSid = NULL; HMODULE StringsResource; LSAPR_POLICY_ACCOUNT_DOM_INFO AccountDomainInfo; Status = LsapGenerateRandomDomainSid( &NewDomainSid ); if ( NT_SUCCESS( Status ) ) { // // We can use GetModuleHandle, since we are getting it on ourselves. // StringsResource = (HMODULE) GetModuleHandle( L"LSASRV.DLL" ); ASSERT( StringsResource ); Status = LsapGetMessageStrings( StringsResource, LSAP_DEFAULT_DOMAIN_NAME, ( PUNICODE_STRING )&AccountDomainInfo.DomainName, 0, NULL ); } // // Ok, if we got this far, then we can initialize the account domain // if ( NT_SUCCESS( Status ) ) { AccountDomainInfo.DomainSid = NewDomainSid; Status = LsarSetInformationPolicy( LsapPolicyHandle, PolicyAccountDomainInformation, ( PLSAPR_POLICY_INFORMATION )&AccountDomainInfo ); LocalFree( AccountDomainInfo.DomainName.Buffer ); } if ( NewDomainSid ) { RtlFreeSid( NewDomainSid ); } return( Status ); } static GUID LsapDbPasswordAuthenticator = {0xf0ce3a80,0x155f,0x11d3,0xb7,0xe6,0x00,0x80,0x5f,0x48,0xca,0xeb}; NTSTATUS LsapDbGenerateNewKey( IN LSAP_DB_ENCRYPTION_KEY * NewEncryptionKey ) /*++ Routine Description This routine generates a new Encryption key that can be used for encrypting secrets. Parameters NewEncryptionKey -- Pointer to a structure that contains the new key Return Values STATUS_SUCCESS STATUS_UNSUCCESSFUL --*/ { NTSTATUS Status = STATUS_SUCCESS; // // generate a random number for the key // if (!RtlGenRandom(NewEncryptionKey->Key,sizeof(NewEncryptionKey->Key))) { return(STATUS_UNSUCCESSFUL); } // // Copy in the GUID for the authenticator // NewEncryptionKey->Authenticator = LsapDbPasswordAuthenticator; // // Set the version # // NewEncryptionKey->Revision = LSAP_DB_ENCRYPTION_KEY_VERSION ; NewEncryptionKey->Flags = 0; // // Generate a Salt // if (!RtlGenRandom(NewEncryptionKey->Salt,sizeof(NewEncryptionKey->Salt))) { return(STATUS_UNSUCCESSFUL); } // // Generate a random value for the old syskey // if (!RtlGenRandom(NewEncryptionKey->OldSyskey,sizeof(NewEncryptionKey->OldSyskey))) { return(STATUS_UNSUCCESSFUL); } // // Set the boot type // NewEncryptionKey->BootType = WxStored; return(STATUS_SUCCESS); } VOID LsapDbEncryptKeyWithSyskey( OUT LSAP_DB_ENCRYPTION_KEY * KeyToEncrypt, IN PVOID Syskey, IN ULONG SyskeyLength ) /*++ This routine encrypts the KeyToEncrypt parameter with the syskey passed in Arguments KeyToEncrypt -- the key to encrypt Syskey -- The syskey passed in SyskeyLength -- The length of the syskey Return Values None, void function --*/ { MD5_CTX Md5Context; struct RC4_KEYSTRUCT Rc4Key; ULONG i; // // Create an MD5 hash of the key and salt // MD5Init(&Md5Context); MD5Update( &Md5Context, Syskey, SyskeyLength ); // // Hash in the salt many many times. This slows down // attackers employing a brute force approach to attack // for (i=0;i<1000;i++) { MD5Update( &Md5Context, KeyToEncrypt->Salt, sizeof(KeyToEncrypt->Salt) ); } MD5Final( &Md5Context ); // // Initialize the RC4 key sequence. // rc4_key( &Rc4Key, MD5DIGESTLEN, Md5Context.digest ); rc4( &Rc4Key, sizeof(KeyToEncrypt->Key)+ sizeof(KeyToEncrypt->Authenticator)+sizeof(KeyToEncrypt->OldSyskey), (PUCHAR) &KeyToEncrypt->Authenticator ); } NTSTATUS LsapDbDecryptKeyWithSyskey( IN LSAP_DB_ENCRYPTION_KEY * KeyToDecrypt, IN PVOID Syskey, IN ULONG SyskeyLength ) /*++ This function provides a decryption using the syskey. Since RC4 is symmetric encryption algorithm, this function simply calls the previous encrypt routine Arguments KeyToDecrypt -- the key to dencrypt Syskey -- The syskey passed in SyskeyLength -- The length of the syskey Return Values STATUS_SUCCESS , on successful decryption STATUS_WRONG_PASSWORD on unsuccessful decryption --*/ { LsapDbEncryptKeyWithSyskey(KeyToDecrypt,Syskey,SyskeyLength); if (!RtlEqualMemory(&KeyToDecrypt->Authenticator,&LsapDbPasswordAuthenticator,sizeof(GUID))) { return(STATUS_WRONG_PASSWORD); } return(STATUS_SUCCESS); } NTSTATUS LsapDbSetupInitialSyskey( OUT PULONG SyskeyLength, OUT PVOID *Syskey ) /*++ This generates a new syskey and changes the winlogon state such that winlogon recognizes the new syskey and the boot option of system saves syskey. Arguments SyskeyLength -- The length of the syskey is returned in here Syskey -- The syskey itself is returned in here Return Values STATUS_SUCCESS Other resource error codes --*/ { NTSTATUS NtStatus = STATUS_SUCCESS; *Syskey = LsapAllocateLsaHeap(LSAP_SYSKEY_SIZE ); if (NULL==*Syskey) { NtStatus = STATUS_INSUFFICIENT_RESOURCES; goto Error; } *SyskeyLength = LSAP_SYSKEY_SIZE; // // Generate the syskey // if (!RtlGenRandom( *Syskey, *SyskeyLength)) { NtStatus = STATUS_UNSUCCESSFUL; goto Error; } // // Save the syskey in the registry // If this operation fails, then no state is changed, machine remains // un syskey-d till next boot // NtStatus = WxSaveSysKey(*SyskeyLength, *Syskey); if (!NT_SUCCESS(NtStatus)) { goto Error; } // // Set the boot option in the registry // If this operation fails, still no problem. Machine remains unsyskey'd // and the boot key that has been saved will be reset on next boot. // NtStatus = WxSaveBootOption(WxStored); if (!NT_SUCCESS(NtStatus)) { goto Error; } Error: if (!NT_SUCCESS(NtStatus)) { *SyskeyLength = 0; if (NULL!=*Syskey) { MIDL_user_free(*Syskey); } } return(NtStatus); } NTSTATUS LsapDbGetSyskeyFromWinlogon() /*++ Routine Description This routine obtains the syskey from winlogon and decrypts the Password encryption key in the LSA policy database. The global variable LsapDbSecretCipherKey is set with the password encryption key and the the global variable LsapDbSyskey is set with the syskey. This value is then queried by SAM/DS to decrypt their respective password encryption keys and then cleared before the end of SamIInitialize. The special case handled inside this routine is of the case of a fresh install. In this particular case winlogon is not yet setup to expect a query of syskey from the lsass process. However since the LSA install code has been called just moments, before, that code sets the winlogon state and also fills the global LsapDbSyskey. Therefore if LsapDbSyskey is not NULL then this would be the fresh install case. Arguments None Return Values STATUS_SUCCESS STATUS_UNSUCCESSFUL --*/ { NTSTATUS Status = STATUS_SUCCESS; NTSTATUS DecryptStatus = STATUS_SUCCESS; ULONG DecryptionKeyLength = 0; HANDLE WinlogonHandle=NULL; ULONG Tries = 0; LSAP_DB_ENCRYPTION_KEY StoredEncryptionKeyData; ULONG StoredEncryptionKeyDataLength = sizeof( LSAP_DB_ENCRYPTION_KEY ); ULONG SyskeyLen=LSAP_SYSKEY_SIZE; BOOLEAN FreshInstall=FALSE; // // Read the attribute information in the LSA policy database // Status = LsapDbReadAttributeObject( LsapDbHandle, &LsapDbNames[PolSecretEncryptionKey], (PVOID) &StoredEncryptionKeyData, &StoredEncryptionKeyDataLength ); if ( !NT_SUCCESS(Status) ) { goto Cleanup; } if (NULL!=LsapDbSysKey) { // // In the fresh install case , code in dbinstal.c already // sets LsapDbSyskey to the syskey value. // FreshInstall = TRUE; Status = LsapDbDecryptKeyWithSyskey( &StoredEncryptionKeyData , LsapDbSysKey, LSAP_SYSKEY_SIZE ); } else { // // because LsapDbDecryptKeyWithSyskey() is an in-place // operation, so we'd better save the Encrypted Syskey first // LSAP_DB_ENCRYPTION_KEY TempStoredEncryptionKeyData; TempStoredEncryptionKeyData = StoredEncryptionKeyData; // // Call Winlogon to obtain the key information. // Status = WxConnect( &WinlogonHandle ); if (!NT_SUCCESS(Status)) { // // Winlogon may fail if secret encryption is not enabled. In those // cases continue. Else Fail the boot // if (WxNone==StoredEncryptionKeyData.BootType) { Status = STATUS_SUCCESS; } goto Cleanup; } for (Tries = 0; Tries < LSAP_BOOT_KEY_RETRY_COUNT ; Tries++ ) { // // restore the data which need to be decrypted. // StoredEncryptionKeyData = TempStoredEncryptionKeyData; // // Retry this RETRY_COUNT_TIMES, this allows the user a chance // to correct himself, in case he entered a wrong boot password // if (WxNone!=StoredEncryptionKeyData.BootType) { // // Get the key to be used to decrypt the PEK list // Status = WxGetKeyData( WinlogonHandle, StoredEncryptionKeyData.BootType, LSAP_SYSKEY_SIZE, SyskeyBuffer, &SyskeyLen ); if (!NT_SUCCESS(Status)) { goto Cleanup; } ASSERT(SyskeyLen==LSAP_SYSKEY_SIZE); // // Decrypt the Blob passed in with the key supplied by winlogon // Status = LsapDbDecryptKeyWithSyskey( &StoredEncryptionKeyData , SyskeyBuffer, LSAP_SYSKEY_SIZE ); if (!NT_SUCCESS(Status)) { DecryptStatus = STATUS_WRONG_PASSWORD; } else { // // We successfully decrypted, break out of the loop // DecryptStatus = STATUS_SUCCESS; break; } } else { break; } } // // Tell winlogon regarding success or failure of the scheme // Status = WxReportResults( WinlogonHandle, DecryptStatus ); if (!NT_SUCCESS(Status)) { goto Cleanup; } Status = DecryptStatus; } if (NT_SUCCESS(Status)) { // // Initialize the new secret cipher key // The keys used for reading and writing secrets are ordinarily the same // LsapDbInitializeSecretCipherKeyRead( &StoredEncryptionKeyData ); LsapDbInitializeSecretCipherKeyWrite( &StoredEncryptionKeyData ); if (!FreshInstall) { // // Set the global variable LsapDBSysKey // LsapDbSysKey = SyskeyBuffer; // // Set up the old syskey for recovery cases // by SAM and LSA // RtlCopyMemory( OldSyskeyBuffer, StoredEncryptionKeyData.OldSyskey, sizeof(StoredEncryptionKeyData.OldSyskey) ); LsapDbOldSysKey = OldSyskeyBuffer; } } Cleanup: if (WinlogonHandle != NULL) { NtClose(WinlogonHandle); } return(Status); } VOID LsapDbInitializeSecretCipherKeyRead( PLSAP_DB_ENCRYPTION_KEY PassedInEncryptionKeyData ) /*++ Routine Description Given a pointer to the encryption key ( as in the struct ), this routine initializes a CipherKey structure that can be used by the LSA's secret encryption and decryption routines. Arguments PassedInEncryptionKeyData -- The struct representing the key Return Values Void function --*/ { static LSAP_DB_ENCRYPTION_KEY StaticEncryptionKeyData; static LSAP_CR_CIPHER_KEY DecryptedSecretCipherKey; RtlCopyMemory(&StaticEncryptionKeyData,PassedInEncryptionKeyData,sizeof(LSAP_DB_ENCRYPTION_KEY)); DecryptedSecretCipherKey.Buffer = StaticEncryptionKeyData.Key; DecryptedSecretCipherKey.Length = DecryptedSecretCipherKey.MaximumLength = sizeof(StaticEncryptionKeyData.Key); LsapDbSecretCipherKeyRead = &DecryptedSecretCipherKey; } VOID LsapDbInitializeSecretCipherKeyWrite( PLSAP_DB_ENCRYPTION_KEY PassedInEncryptionKeyData ) /*++ Routine Description Given a pointer to the encryption key ( as in the struct ), this routine initializes a CipherKey structure that can be used by the LSA's secret encryption and decryption routines. Arguments PassedInEncryptionKeyData -- The struct representing the key CipherKey -- The structure that needs to be initialzed with the key for LSA's secret encryption Return Values Void function --*/ { static LSAP_DB_ENCRYPTION_KEY StaticEncryptionKeyData; static LSAP_CR_CIPHER_KEY DecryptedSecretCipherKey; RtlCopyMemory(&StaticEncryptionKeyData,PassedInEncryptionKeyData,sizeof(LSAP_DB_ENCRYPTION_KEY)); DecryptedSecretCipherKey.Buffer = StaticEncryptionKeyData.Key; DecryptedSecretCipherKey.Length = DecryptedSecretCipherKey.MaximumLength = sizeof(StaticEncryptionKeyData.Key); LsapDbSecretCipherKeyWrite = &DecryptedSecretCipherKey; } VOID LsapDbSetSyskey( PVOID Syskey, ULONG SyskeyLength ) /*++ This function sets the syskey in the global syskey buffer --*/ { ASSERT(LSAP_SYSKEY_SIZE==SyskeyLength); RtlCopyMemory(SyskeyBuffer,Syskey,SyskeyLength); LsapDbSysKey = SyskeyBuffer; } NTSTATUS LsaISetBootOption( IN ULONG BootOption, IN PVOID OldKey, IN ULONG OldKeyLength, IN PVOID NewKey, IN ULONG NewKeyLength ) /*++ This function is used to change the syskey value in the LSA, or change the boot option type. Arguments BootOption -- New Boot Option OldKey -- Old key, used to verify value OldKeyLength -- Length of old key NewKey -- New key, LSA's password encryption key is encrypted using this value NewKeyLength -- Length of new key Return Values --*/ { NTSTATUS Status = STATUS_SUCCESS; ULONG StoredEncryptionKeyDataLength = sizeof( LSAP_DB_ENCRYPTION_KEY ); LSAP_DB_ENCRYPTION_KEY StoredEncryptionKeyData; ULONG SyskeyLen=0; LSAP_DB_ATTRIBUTE Attributes[20]; PLSAP_DB_ATTRIBUTE NextAttribute; ULONG AttributeCount = 0; NextAttribute = Attributes; // // Validate Some parameters // if ((NewKeyLength != LSAP_SYSKEY_SIZE ) || (OldKeyLength != LSAP_SYSKEY_SIZE)) { return (STATUS_INVALID_PARAMETER); } if ((NULL==NewKey) || (NULL==OldKey)) { return(STATUS_INVALID_PARAMETER); } // // Read the attribute information in the LSA policy database // Status = LsapDbReadAttributeObject( LsapDbHandle, &LsapDbNames[PolSecretEncryptionKey], (PVOID) &StoredEncryptionKeyData, &StoredEncryptionKeyDataLength ); if (!NT_SUCCESS(Status) ) { goto Cleanup; } // // Decrypt the data // Status = LsapDbDecryptKeyWithSyskey( &StoredEncryptionKeyData, OldKey, OldKeyLength ); if (!NT_SUCCESS(Status)) { goto Cleanup; } // // Change the boot option // StoredEncryptionKeyData.BootType = BootOption; // // Save the old key ( for recovery ) // ASSERT(sizeof(StoredEncryptionKeyData.OldSyskey) == OldKeyLength); RtlCopyMemory( &StoredEncryptionKeyData.OldSyskey, OldKey, OldKeyLength ); // // Re-encrypt the data using the new key // LsapDbEncryptKeyWithSyskey( &StoredEncryptionKeyData, NewKey, NewKeyLength ); LsapDbInitializeAttribute( NextAttribute, &LsapDbNames[PolSecretEncryptionKey], &StoredEncryptionKeyData, sizeof (StoredEncryptionKeyData), FALSE ); NextAttribute++; AttributeCount++; // // Now write out all attributes that have been added (if any) // if (AttributeCount > 0) { Status = LsapDbReferenceObject( LsapDbHandle, 0, PolicyObject, PolicyObject, LSAP_DB_LOCK | LSAP_DB_START_TRANSACTION ); if (NT_SUCCESS(Status)) { ASSERT( AttributeCount < ( sizeof( Attributes ) / sizeof( LSAP_DB_ATTRIBUTE ) ) ); Status = LsapDbWriteAttributesObject( LsapDbHandle, Attributes, AttributeCount ); // // No attributes are replicatable. // (That's good, too, since SAM hasn't told Netlogon our role yet.) Status = LsapDbDereferenceObject( &LsapDbHandle, PolicyObject, PolicyObject, (LSAP_DB_LOCK | LSAP_DB_FINISH_TRANSACTION | LSAP_DB_OMIT_REPLICATOR_NOTIFICATION ), SecurityDbChange, Status ); } } Cleanup: return(Status); } NTSTATUS LsaIGetBootOption( OUT PULONG BootOption ) /*++ This function is used to obtain the boot option from LSA Arguments BootOption -- New Boot is passed in here Return Values --*/ { NTSTATUS Status = STATUS_SUCCESS; ULONG StoredEncryptionKeyDataLength = sizeof( LSAP_DB_ENCRYPTION_KEY ); LSAP_DB_ENCRYPTION_KEY StoredEncryptionKeyData; // // Read the attribute information in the LSA policy database // Status = LsapDbReadAttributeObject( LsapDbHandle, &LsapDbNames[PolSecretEncryptionKey], (PVOID) &StoredEncryptionKeyData, &StoredEncryptionKeyDataLength ); if (!NT_SUCCESS(Status) ) { goto Cleanup; } *BootOption = StoredEncryptionKeyData.BootType; Cleanup: return(Status); }