NT4/private/newsam2/server/samss.c
2020-09-30 17:12:29 +02:00

1862 lines
47 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
samss.c
Abstract:
This is the main routine for the Security Account Manager Server process.
Author:
Jim Kelly (JimK) 4-July-1991
Environment:
User Mode - Win32
Revision History:
--*/
///////////////////////////////////////////////////////////////////////////////
// //
// Includes //
// //
///////////////////////////////////////////////////////////////////////////////
#include <samsrvp.h>
#include <dslayer.h>
///////////////////////////////////////////////////////////////////////////////
// //
// Module Private defines //
// //
///////////////////////////////////////////////////////////////////////////////
#define SAM_AUTO_BUILD
//
// Enable this define to compile in code to SAM that allows for the
// simulation of SAM initialization/installation failures. See
// SampInitializeForceError() below for details.
//
// #define SAMP_SETUP_FAILURE_TEST 1
///////////////////////////////////////////////////////////////////////////////
// //
// private service prototypes //
// //
///////////////////////////////////////////////////////////////////////////////
NTSTATUS
SampInitialize(
OUT PULONG Revision
);
NTSTATUS
SampInitializeWellKnownSids( VOID );
VOID
SampLoadPasswordFilterDll( VOID );
NTSTATUS
SampEnableAuditPrivilege( VOID );
NTSTATUS
SampFixGroupCount( VOID );
#ifdef SAMP_SETUP_FAILURE_TEST
NTSTATUS
SampInitializeForceError(
OUT PNTSTATUS ForcedStatus
);
#endif //SAMP_SETUP_FAILURE_TEST
#if SAMP_DIAGNOSTICS
VOID
SampActivateDebugProcess( VOID );
NTSTATUS
SampActivateDebugProcessWrkr(
IN PVOID ThreadParameter
);
#endif //SAMP_DIAGNOSTICS
NTSTATUS
SampUserModeSamRpcInit( VOID );
///////////////////////////////////////////////////////////////////////////////
// //
// Routines //
// //
///////////////////////////////////////////////////////////////////////////////
NTSTATUS
SamIInitialize (
VOID
)
/*++
Routine Description:
This is the initialization control routine for the Security Account
Manager Server. A mechanism is provided for simulating initialization
errors.
Arguments:
None.
Return Value:
NTSTATUS - Standard Nt Result Code
STATUS_SUCCESS - The call completed successfully
Simulated errors
Errors from called routines.
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
NTSTATUS IgnoreStatus;
HANDLE EventHandle = NULL;
ULONG Revision = 0;
SAMTRACE("SamIInitialize");
//
// The following conditional code is used to generate artifical errors
// during SAM installation for the purpose of testing setup.exe error
// handling. This code should remain permanently, since it provides a
// way of testing against regressions in the setup error handling code.
//
#ifdef SAMP_SETUP_FAILURE_TEST
NTSTATUS ForcedStatus;
//
// Read an error code from the Registry.
//
NtStatus = SampInitializeForceError( &ForcedStatus);
if (!NT_SUCCESS(NtStatus)) {
KdPrint(("SAMSS: Attempt to force error failed 0x%lx\n", NtStatus));
KdPrint(("SAM will try to initialize normally\n"));
NtStatus = STATUS_SUCCESS;
} else {
//
// Use the status returned
//
NtStatus = ForcedStatus;
}
#endif // SAMP_SETUP_FAILURE_TEST
//
// For DS case build the root object Name
//
SampDsBuildRootObjectName();
//
// Initialize SAM if no error was forced.
//
if (NT_SUCCESS(NtStatus)) {
NtStatus = SampInitialize( &Revision );
}
//
// Register our shutdown routine
//
if (!SetConsoleCtrlHandler(SampShutdownNotification, TRUE)) {
KdPrint(("SAM Server: SetConsoleCtrlHandler call failed %d\n",GetLastError()));
}
if (!SetProcessShutdownParameters(SAMP_SHUTDOWN_LEVEL,SHUTDOWN_NORETRY)) {
KdPrint(("SAM Server: SetProcessShutdownParameters call failed %d\n",GetLastError()));
}
//
// Try to load the cached Alias Membership information and turn on caching.
// If unsuccessful, caching remains disabled forever.
//
// MURLIS 6/7/96, Disable Alias Caching
#if 0
IgnoreStatus = SampAlBuildAliasInformation();
if (!NT_SUCCESS(IgnoreStatus)) {
KdPrint(("SAM Server: Build Alias Cache access violation handled"));
KdPrint(("SAM Server: Alias Caching turned off\n"));
}
#endif
//
// Perform any necessary upgrades.
//
if (NT_SUCCESS(NtStatus)) {
NtStatus = SampUpgradeSamDatabase(
Revision
);
if (!NT_SUCCESS(NtStatus)) {
KdPrint(("SAM Server: Failed to upgrade SAM database: 0x%x\n",NtStatus));
}
}
//
// Everyone is initialized, start processing calls.
//
SampServiceState = SampServiceEnabled;
//
// If requested, activate a diagnostic process.
// This is a debug aid expected to be used for SETUP testing.
//
#if SAMP_DIAGNOSTICS
IF_SAMP_GLOBAL( ACTIVATE_DEBUG_PROC ) {
SampActivateDebugProcess();
}
#endif //SAMP_DIAGNOSTICS
return(NtStatus);
}
NTSTATUS
SampInitialize(
OUT PULONG Revision
)
/*++
Routine Description:
This routine does the actual initialization of the SAM server. This includes:
- Initializing well known global variable values
- Creating the registry exclusive access lock,
- Opening the registry and making sure it includes a SAM database
with a known revision level,
- Starting the RPC server,
- Add the SAM services to the list of exported RPC interfaces
Arguments:
Revision - receives the revision of the database.
Return Value:
STATUS_SUCCESS - Initialization has successfully completed.
STATUS_UNKNOWN_REVISION - The SAM database has an unknown revision.
--*/
{
NTSTATUS NtStatus;
LPWSTR ServiceName;
PSAMP_OBJECT ServerContext;
OBJECT_ATTRIBUTES SamAttributes;
UNICODE_STRING SamNameU;
PULONG RevisionLevel;
BOOLEAN ProductExplicitlySpecified;
PPOLICY_AUDIT_EVENTS_INFO PolicyAuditEventsInfo = NULL;
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
CHAR NullLmPassword = 0;
RPC_STATUS RpcStatus;
HANDLE ThreadHandle;
ULONG ThreadId;
SAMTRACE("SampInitialize");
//
// Set the state of our service to "initializing" until everything
// is initialized.
//
SampServiceState = SampServiceInitializing;
//
// Set up some useful well-known sids
//
NtStatus = SampInitializeWellKnownSids();
if (!NT_SUCCESS(NtStatus)) {
return(NtStatus);
}
//
// Get the product type
//
ProductExplicitlySpecified = RtlGetNtProductType(&SampProductType);
//
// Set the number of currently active opens
//
SampActiveContextCount = 0;
//
// Initialize the server/domain context list
//
InitializeListHead(&SampContextListHead);
//
// Initialize the attribute field information of the object
// information structures.
//
SampInitObjectInfoAttributes();
//
// Set up the generic mappings for the SAM object types
//
SampObjectInformation[ SampServerObjectType ].GenericMapping.GenericRead
= SAM_SERVER_READ;
SampObjectInformation[ SampServerObjectType ].GenericMapping.GenericWrite
= SAM_SERVER_WRITE;
SampObjectInformation[ SampServerObjectType ].GenericMapping.GenericExecute
= SAM_SERVER_EXECUTE;
SampObjectInformation[ SampServerObjectType ].GenericMapping.GenericAll
= SAM_SERVER_ALL_ACCESS;
SampObjectInformation[ SampDomainObjectType ].GenericMapping.GenericRead
= DOMAIN_READ;
SampObjectInformation[ SampDomainObjectType ].GenericMapping.GenericWrite
= DOMAIN_WRITE;
SampObjectInformation[ SampDomainObjectType ].GenericMapping.GenericExecute
= DOMAIN_EXECUTE;
SampObjectInformation[ SampDomainObjectType ].GenericMapping.GenericAll
= DOMAIN_ALL_ACCESS;
SampObjectInformation[ SampGroupObjectType ].GenericMapping.GenericRead
= GROUP_READ;
SampObjectInformation[ SampGroupObjectType ].GenericMapping.GenericWrite
= GROUP_WRITE;
SampObjectInformation[ SampGroupObjectType ].GenericMapping.GenericExecute
= GROUP_EXECUTE;
SampObjectInformation[ SampGroupObjectType ].GenericMapping.GenericAll
= GROUP_ALL_ACCESS;
SampObjectInformation[ SampAliasObjectType ].GenericMapping.GenericRead
= ALIAS_READ;
SampObjectInformation[ SampAliasObjectType ].GenericMapping.GenericWrite
= ALIAS_WRITE;
SampObjectInformation[ SampAliasObjectType ].GenericMapping.GenericExecute
= ALIAS_EXECUTE;
SampObjectInformation[ SampAliasObjectType ].GenericMapping.GenericAll
= ALIAS_ALL_ACCESS;
SampObjectInformation[ SampUserObjectType ].GenericMapping.GenericRead
= USER_READ;
SampObjectInformation[ SampUserObjectType ].GenericMapping.GenericWrite
= USER_WRITE;
SampObjectInformation[ SampUserObjectType ].GenericMapping.GenericExecute
= USER_EXECUTE;
SampObjectInformation[ SampUserObjectType ].GenericMapping.GenericAll
= USER_ALL_ACCESS;
//
// Set mask of INVALID accesses for an access mask that is already mapped.
//
SampObjectInformation[ SampServerObjectType ].InvalidMappedAccess
= (ULONG)(~(SAM_SERVER_ALL_ACCESS | ACCESS_SYSTEM_SECURITY | MAXIMUM_ALLOWED));
SampObjectInformation[ SampDomainObjectType ].InvalidMappedAccess
= (ULONG)(~(DOMAIN_ALL_ACCESS | ACCESS_SYSTEM_SECURITY | MAXIMUM_ALLOWED));
SampObjectInformation[ SampGroupObjectType ].InvalidMappedAccess
= (ULONG)(~(GROUP_ALL_ACCESS | ACCESS_SYSTEM_SECURITY | MAXIMUM_ALLOWED));
SampObjectInformation[ SampAliasObjectType ].InvalidMappedAccess
= (ULONG)(~(ALIAS_ALL_ACCESS | ACCESS_SYSTEM_SECURITY | MAXIMUM_ALLOWED));
SampObjectInformation[ SampUserObjectType ].InvalidMappedAccess
= (ULONG)(~(USER_ALL_ACCESS | ACCESS_SYSTEM_SECURITY | MAXIMUM_ALLOWED));
//
// Set a mask of write operations for the object types. Strip
// out READ_CONTROL, which doesn't allow writing but is defined
// in all of the standard write accesses.
// This is used to enforce correct role semantics (e.g., only
// trusted clients can perform write operations when a domain
// role isn't Primary).
//
// Note that USER_WRITE isn't good enough for user objects. That's
// because USER_WRITE allows users to modify portions of their
// account information, but other portions can only be modified by
// an administrator.
//
SampObjectInformation[ SampServerObjectType ].WriteOperations
= (SAM_SERVER_WRITE & ~READ_CONTROL) | DELETE;
SampObjectInformation[ SampDomainObjectType ].WriteOperations
= (DOMAIN_WRITE & ~READ_CONTROL) | DELETE;
SampObjectInformation[ SampGroupObjectType ].WriteOperations
= (GROUP_WRITE & ~READ_CONTROL) | DELETE;
SampObjectInformation[ SampAliasObjectType ].WriteOperations
= (ALIAS_WRITE & ~READ_CONTROL) | DELETE;
SampObjectInformation[ SampUserObjectType ].WriteOperations
= ( USER_WRITE & ~READ_CONTROL ) | USER_WRITE_ACCOUNT |
USER_FORCE_PASSWORD_CHANGE | USER_WRITE_GROUP_INFORMATION | DELETE;
// Set up the names of the SAM defined object types.
// These names are used for auditing purposes.
RtlInitUnicodeString( &SamNameU, L"SAM_SERVER" );
SampObjectInformation[ SampServerObjectType ].ObjectTypeName = SamNameU;
RtlInitUnicodeString( &SamNameU, L"SAM_DOMAIN" );
SampObjectInformation[ SampDomainObjectType ].ObjectTypeName = SamNameU;
RtlInitUnicodeString( &SamNameU, L"SAM_GROUP" );
SampObjectInformation[ SampGroupObjectType ].ObjectTypeName = SamNameU;
RtlInitUnicodeString( &SamNameU, L"SAM_ALIAS" );
SampObjectInformation[ SampAliasObjectType ].ObjectTypeName = SamNameU;
RtlInitUnicodeString( &SamNameU, L"SAM_USER" );
SampObjectInformation[ SampUserObjectType ].ObjectTypeName = SamNameU;
//
// Set up the name of the SAM server object itself (rather than its type)
//
RtlInitUnicodeString( &SampServerObjectName, L"SAM" );
//
// Set up the name of the SAM server for auditing purposes
//
RtlInitUnicodeString( &SampSamSubsystem, L"Security Account Manager" );
//
// Set up the names of well known registry keys
//
RtlInitUnicodeString( &SampFixedAttributeName, L"F" );
RtlInitUnicodeString( &SampVariableAttributeName, L"V" );
RtlInitUnicodeString( &SampCombinedAttributeName, L"C" );
RtlInitUnicodeString(&SampNameDomains, L"DOMAINS" );
RtlInitUnicodeString(&SampNameDomainGroups, L"Groups" );
RtlInitUnicodeString(&SampNameDomainAliases, L"Aliases" );
RtlInitUnicodeString(&SampNameDomainAliasesMembers, L"Members" );
RtlInitUnicodeString(&SampNameDomainUsers, L"Users" );
RtlInitUnicodeString(&SampNameDomainAliasesNames, L"Names" );
RtlInitUnicodeString(&SampNameDomainGroupsNames, L"Names" );
RtlInitUnicodeString(&SampNameDomainUsersNames, L"Names" );
//
// Initialize other useful characters and strings
//
RtlInitUnicodeString(&SampBackSlash, L"\\");
RtlInitUnicodeString(&SampNullString, L"");
//
// Initialize some useful time values
//
SampImmediatelyDeltaTime.LowPart = 0;
SampImmediatelyDeltaTime.HighPart = 0;
SampNeverDeltaTime.LowPart = 0;
SampNeverDeltaTime.HighPart = MINLONG;
SampHasNeverTime.LowPart = 0;
SampHasNeverTime.HighPart = 0;
SampWillNeverTime.LowPart = MAXULONG;
SampWillNeverTime.HighPart = MAXLONG;
//
// Initialize useful encryption constants
//
NtStatus = RtlCalculateLmOwfPassword(&NullLmPassword, &SampNullLmOwfPassword);
ASSERT( NT_SUCCESS(NtStatus) );
RtlInitUnicodeString(&SamNameU, NULL);
NtStatus = RtlCalculateNtOwfPassword(&SamNameU, &SampNullNtOwfPassword);
ASSERT( NT_SUCCESS(NtStatus) );
//
// Initialize variables for the hive flushing thread
//
LastUnflushedChange.LowPart = 0;
LastUnflushedChange.HighPart = 0;
FlushThreadCreated = FALSE;
FlushImmediately = FALSE;
SampFlushThreadMinWaitSeconds = 30;
SampFlushThreadMaxWaitSeconds = 600;
SampFlushThreadExitDelaySeconds = 120;
//
// Enable the audit privilege (needed to use NtAccessCheckAndAuditAlarm)
//
NtStatus = SampEnableAuditPrivilege();
if (!NT_SUCCESS(NtStatus)) {
KdPrint((" SAM SERVER: The SAM Server could not enable the audit Privilege.\n"
" Failing to initialize SAM.\n"));
return( NtStatus );
}
//
// Get Auditing Information from the LSA and save information
// relevant to SAM.
//
NtStatus = LsaIQueryInformationPolicyTrusted(
PolicyAuditEventsInformation,
(PLSAPR_POLICY_INFORMATION *) &PolicyAuditEventsInfo
);
if (NT_SUCCESS(NtStatus)) {
SampSetAuditingInformation( PolicyAuditEventsInfo );
} else {
//
// Failed to query Audit Information from LSA. Allow SAM to
// continue initializing wuth SAM Account auditing turned off.
//
KdPrint((" SAM SERVER: Query Audit Info from LSA returned 0x%lX\n",
NtStatus));
KdPrint((" SAM SERVER: Sam Account Auditing is not enabled"));
SampAccountAuditingEnabled = FALSE;
NtStatus = STATUS_SUCCESS;
}
//
// We no longer need the Lsa Audit Events Info data.
//
if (PolicyAuditEventsInfo != NULL) {
LsaIFree_LSAPR_POLICY_INFORMATION(
PolicyAuditEventsInformation,
(PLSAPR_POLICY_INFORMATION) PolicyAuditEventsInfo
);
}
//
// Create the internal data structure and backstore lock ...
//
RtlInitializeResource(&SampLock);
//
// Open the registry and make sure it includes a SAM database.
// Also make sure this SAM database has been initialized and is
// at a revision level we understand.
//
#ifdef USER_MODE_SAM
RtlInitUnicodeString( &SamNameU, L"\\Registry\\Machine\\Software\\SECURITY\\SAM" );
#else
RtlInitUnicodeString( &SamNameU, L"\\Registry\\Machine\\Security\\SAM" );
#endif
ASSERT( NT_SUCCESS(NtStatus) );
InitializeObjectAttributes(
&SamAttributes,
&SamNameU,
OBJ_CASE_INSENSITIVE,
0,
NULL
);
SampDumpNtOpenKey((KEY_READ | KEY_WRITE), &SamAttributes, 0);
NtStatus = RtlpNtOpenKey(
&SampKey,
(KEY_READ | KEY_WRITE),
&SamAttributes,
0
);
if ( NtStatus == STATUS_OBJECT_NAME_NOT_FOUND ) {
#ifndef SAM_AUTO_BUILD
KdPrint((" NEWSAM\\SERVER: Sam database not found in registry.\n"
" Failing to initialize\n"));
return(NtStatus);
#endif //SAM_AUTO_BUILD
#if DBG
KdPrint((" NEWSAM\\SERVER: Initializing SAM registry database for\n"));
if (SampProductType == NtProductWinNt) {
DbgPrint(" WinNt product.\n");
} else if ( SampProductType == NtProductLanManNt ) {
DbgPrint(" LanManNt product.\n");
} else {
DbgPrint(" Dedicated Server product.\n");
}
#endif //DBG
//
// Change the flush thread timeouts. This is necessary because
// the reboot following an installation does not call
// ExitWindowsEx() and so our shutdown notification routine does
// not get called. Consequently, it does not have a chance to
// flush any changes that were obtained by syncing with a PDC.
// If there are a large number of accounts, it could be
// extremely expensive to do another full re-sync. So, close
// the flush thread wait times so that it is pretty sure to
// have time to flush.
//
SampFlushThreadMinWaitSeconds = 5;
NtStatus = SampInitializeRegistry();
if (!NT_SUCCESS(NtStatus)) {
return(NtStatus);
}
SampDumpNtOpenKey((KEY_READ | KEY_WRITE), &SamAttributes, 0);
NtStatus = RtlpNtOpenKey(
&SampKey,
(KEY_READ | KEY_WRITE),
&SamAttributes,
0
);
}
if (!NT_SUCCESS(NtStatus)) {
KdPrint(("SAM Server: Could not access the SAM database.\n"
" Status is 0x%lx \n", NtStatus));
KdPrint((" Failing to initialize SAM.\n"));
return(NtStatus);
}
//
// The following subroutine may be removed from the code
// following the Daytona release. By then it will have fixed
// the group count.
//
NtStatus = SampFixGroupCount();
//
// We need to read the fixed attributes of the server objects.
// Create a context to do that.
//
ServerContext = SampCreateContext( SampServerObjectType, TRUE );
if ( ServerContext == NULL ) {
KdPrint(("SAM Server: Could not create server context.\n"
" Failing to initialize SAM.\n"));
return(STATUS_INSUFFICIENT_RESOURCES);
}
//
// The RootKey for a SERVER object is the root of the SAM database.
// This key should not be closed when the context is deleted.
//
ServerContext->RootKey = SampKey;
//
// Get the FIXED attributes, which just consists of the revision level.
//
//
// BUGBUG: this does not actually return the fixed attributes. Find
// out why. MMS 9/10/95
//
NtStatus = SampGetFixedAttributes(
ServerContext,
FALSE,
(PVOID *)&RevisionLevel
);
if (NtStatus != STATUS_SUCCESS) {
KdPrint(("SAM Server: Could not access the SAM database revision level.\n"));
KdPrint((" Status is 0x%lx \n", NtStatus));
KdPrint((" Failing to initialize SAM.\n"));
return(NtStatus);
}
*Revision = *RevisionLevel;
if ( ((*Revision && 0xFFFF0000) > SAMP_MAJOR_REVISION) ||
(*Revision > SAMP_SERVER_REVISION) ) {
KdPrint(("SAM Server: The SAM database revision level is not one supported\n"));
KdPrint((" by this version of the SAM server code. The highest revision\n"));
KdPrint((" level supported is 0x%lx. The SAM Database revision is 0x%lx \n",
(ULONG)SAMP_SERVER_REVISION, *Revision));
KdPrint((" Failing to initialize SAM.\n"));
return(STATUS_UNKNOWN_REVISION);
}
SampDeleteContext( ServerContext );
//
// If necessary, commit a partially commited transaction.
//
NtStatus = RtlInitializeRXact( SampKey, TRUE, &SampRXactContext );
if ( NtStatus == STATUS_RXACT_STATE_CREATED ) {
KdPrint((" SAM SERVER: RXACT state of the SAM database didn't yet exist.\n"
" Failing to initialize SAM.\n"));
return(NtStatus);
} else if (!NT_SUCCESS(NtStatus)) {
KdPrint((" SAM SERVER: RXACT state of the SAM database didn't initialize properly.\n"));
KdPrint((" Status is 0x%lx \n", NtStatus));
KdPrint((" Failing to initialize SAM.\n"));
return(NtStatus);
}
if ( NtStatus == STATUS_RXACT_COMMITTED ) {
KdPrint((" SAM SERVER: Previously aborted backstore commit was completed\n"
" during SAM initialization. This is not a cause\n"
" for alarm.\n"
" Continuing with SAM initialization.\n"));
}
//
// Start the RPC server...
//
//
// Publish the sam server interface package...
//
// NOTE: Now all RPC servers in lsass.exe (now winlogon) share the same
// pipe name. However, in order to support communication with
// version 1.0 of WinNt, it is necessary for the Client Pipe name
// to remain the same as it was in version 1.0. Mapping to the new
// name is performed in the Named Pipe File System code.
//
#ifdef USER_MODE_SAM
NtStatus = SampUserModeSamRpcInit();
if (!NT_SUCCESS(NtStatus)) {
KdPrint(("SAMSS: Could Not Start RPC Server.\n"
" Failing to initialize SAM Server.\n"
" Status is: 0x%lx\n", NtStatus));
return(NtStatus);
}
#else
ServiceName = L"lsass";
NtStatus = RpcpAddInterface( ServiceName, samr_ServerIfHandle);
if (!NT_SUCCESS(NtStatus)) {
KdPrint(("SAMSS: Could Not Start RPC Server.\n"
" Failing to initialize SAM Server.\n"
" Status is: 0x%lx\n", NtStatus));
return(NtStatus);
}
//
// If we are running as a netware server, for Small World or FPNW,
// register an SPX endpoint and some authentication info.
//
//
// Build null session token handle if a Netware server is
// installed.
//
if (SampStartNonNamedPipeTransports()) {
NtStatus = SampCreateNullToken();
if (!NT_SUCCESS(NtStatus)) {
KdPrint(("SAMSS: Unable to create NULL token: 0x%x\n",
NtStatus));
return(NtStatus);
}
}
//
// Create a thread to start authenticated RPC.
//
ThreadHandle = CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE) SampSecureRpcInit,
NULL,
0,
&ThreadId
);
if (ThreadHandle == NULL) {
KdPrint(("SAMSS: Unable to create thread: %d\n",
GetLastError()));
return(STATUS_INVALID_HANDLE);
}
#endif
//
// Load the password-change notification packages.
//
NtStatus = SampLoadNotificationPackages( );
if (!NT_SUCCESS(NtStatus)) {
KdPrint(("SAMSS: Failed to load notification packagees: 0x%x.\n"
" Failing to initialize SAM Server.\n", NtStatus));
return(NtStatus);
}
//
// Allow each sub-component of SAM a chance to initialize
//
// SampInitializeServerObject();
if (!SampInitializeDomainObject()) {
KdPrint(("SAMSS: Domain Object Intialization Failed.\n"
" Failing to initialize SAM Server.\n"));
return(STATUS_INVALID_DOMAIN_STATE);
}
// SampInitializeGroupObject();
// SampInitializeUserObject();
//
// Load the password filter DLL if there is one
//
SampLoadPasswordFilterDll();
return(NtStatus);
}
NTSTATUS
SampInitializeWellKnownSids( VOID )
/*++
Routine Description:
This routine initializes some global well-known sids.
Arguments:
None.
Return Value:
STATUS_SUCCESS - Initialization has successfully completed.
STATUS_NO_MEMORY - Couldn't allocate memory for the sids.
--*/
{
NTSTATUS
NtStatus;
PPOLICY_ACCOUNT_DOMAIN_INFO
DomainInfo;
//
// WORLD is s-1-1-0
// ANONYMOUS is s-1-5-7
//
SID_IDENTIFIER_AUTHORITY
WorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY,
NtAuthority = SECURITY_NT_AUTHORITY;
SAMTRACE("SampInitializeWellKnownSids");
NtStatus = RtlAllocateAndInitializeSid(
&NtAuthority,
1,
SECURITY_ANONYMOUS_LOGON_RID,
0, 0, 0, 0, 0, 0, 0,
&SampAnonymousSid
);
if (NT_SUCCESS(NtStatus)) {
NtStatus = RtlAllocateAndInitializeSid(
&WorldSidAuthority,
1, //Sub authority count
SECURITY_WORLD_RID, //Sub authorities (up to 8)
0, 0, 0, 0, 0, 0, 0,
&SampWorldSid
);
if (NT_SUCCESS(NtStatus)) {
NtStatus = RtlAllocateAndInitializeSid(
&NtAuthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&SampAdministratorsAliasSid
);
if (NT_SUCCESS(NtStatus)) {
NtStatus = SampGetAccountDomainInfo( &DomainInfo );
if (NT_SUCCESS(NtStatus)) {
NtStatus = SampCreateFullSid( DomainInfo->DomainSid,
DOMAIN_USER_RID_ADMIN,
&SampAdministratorUserSid
);
MIDL_user_free( DomainInfo );
}
}
}
}
return(NtStatus);
}
VOID
SampLoadPasswordFilterDll(
VOID
)
/*++
Routine Description:
This function loads a DLL to do password filtering. This DLL is
optional and is expected to be used by ISVs or customers to do
things like dictionary lookups and other simple algorithms to
reject any password deemed too risky to allow a user to use.
For example, user initials or easily guessed password might be
rejected.
Arguments:
None.
Return Value:
None.
--*/
{
#if NOT_YET_SUPPORTED
NTSTATUS Status, IgnoreStatus, MsProcStatus;
PVOID ModuleHandle;
STRING ProcedureName;
UNICODE_STRING FileName;
PSAM_PF_INITIALIZE InitializeRoutine;
//
// Indicate the dll has not yet been loaded.
//
SampPasswordFilterDllRoutine = NULL;
RtlInitUnicodeString( &FileName, L"PwdFiltr" );
Status = LdrLoadDll( NULL, NULL, &FileName, &ModuleHandle );
if (!NT_SUCCESS(Status)) {
return;
}
KdPrint(("Samss: Loading Password Filter DLL - %Z\n", &FileName ));
//
// Now get the address of the password filter DLL routines
//
RtlInitString( &ProcedureName, SAM_PF_NAME_INITIALIZE );
Status = LdrGetProcedureAddress(
ModuleHandle,
&ProcedureName,
0,
(PVOID *)&InitializeRoutine
);
if (!NT_SUCCESS(Status)) {
//
// We found the DLL, but couldn't get its initialization routine
// address
//
// FIX, FIX - Log an error
KdPrint(("Samss: Couldn't get password filter DLL init routine address.\n"
" Status is: 0x%lx\n", Status));
IgnoreStatus = LdrUnloadDll( ModuleHandle );
return;
}
RtlInitString( &ProcedureName, SAM_PF_NAME_PASSWORD_FILTER );
Status = LdrGetProcedureAddress(
ModuleHandle,
&ProcedureName,
0,
(PVOID *)&SampPasswordFilterDllRoutine
);
if (!NT_SUCCESS(Status)) {
//
// We found the DLL, but couldn't get its password filter routine
// address
//
// FIX, FIX - Log an error
KdPrint(("Samss: Couldn't get password filter routine address from loaded DLL.\n"
" Status is: 0x%lx\n", Status));
IgnoreStatus = LdrUnloadDll( ModuleHandle );
return;
}
//
// Now initialize the DLL
//
Status = (InitializeRoutine)();
if (!NT_SUCCESS(Status)) {
//
// We found the DLL and loaded its routine addresses, but it returned
// and error from its initialize routine.
//
// FIX, FIX - Log an error
KdPrint(("Samss: Password filter DLL returned error from initialization routine.\n");
" Status is: 0x%lx\n", Status));
SampPasswordFilterDllRoutine = NULL;
IgnoreStatus = LdrUnloadDll( ModuleHandle );
return;
}
#endif // NOT_YET_SUPPORTED
return;
}
NTSTATUS
SampEnableAuditPrivilege( VOID )
/*++
Routine Description:
This routine enables the SAM process's AUDIT privilege.
This privilege is necessary to use the NtAccessCheckAndAuditAlarm()
service.
Arguments:
None.
Return Value:
--*/
{
NTSTATUS NtStatus, IgnoreStatus;
HANDLE Token;
LUID AuditPrivilege;
PTOKEN_PRIVILEGES NewState;
ULONG ReturnLength;
SAMTRACE("SampEnableAuditPrivilege");
//
// Open our own token
//
NtStatus = NtOpenProcessToken(
NtCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES,
&Token
);
if (!NT_SUCCESS(NtStatus)) {
return(NtStatus);
}
//
// Initialize the adjustment structure
//
AuditPrivilege =
RtlConvertLongToLuid(SE_AUDIT_PRIVILEGE);
ASSERT( (sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)) < 100);
NewState = RtlAllocateHeap(RtlProcessHeap(), 0, 100 );
NewState->PrivilegeCount = 1;
NewState->Privileges[0].Luid = AuditPrivilege;
NewState->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
//
// Set the state of the privilege to ENABLED.
//
NtStatus = NtAdjustPrivilegesToken(
Token, // TokenHandle
FALSE, // DisableAllPrivileges
NewState, // NewState
0, // BufferLength
NULL, // PreviousState (OPTIONAL)
&ReturnLength // ReturnLength
);
//
// Clean up some stuff before returning
//
RtlFreeHeap( RtlProcessHeap(), 0, NewState );
IgnoreStatus = NtClose( Token );
ASSERT(NT_SUCCESS(IgnoreStatus));
return NtStatus;
}
NTSTATUS
SampFixGroupCount( VOID )
/*++
Routine Description:
This routine fixes the group count of the account domain.
A bug in early Daytona beta systems left the group count
too low (by one). This routine fixes that problem by
setting the value according to however many groups are found
in the registry.
Arguments:
None - uses the gobal variable "SampKey".
Return Value:
The status value of the registry services needed to query
and set the group count.
--*/
{
NTSTATUS
NtStatus,
IgnoreStatus;
OBJECT_ATTRIBUTES
ObjectAttributes;
UNICODE_STRING
KeyName,
NullName;
HANDLE
AccountHandle;
ULONG
ResultLength,
GroupCount;
PKEY_FULL_INFORMATION
KeyInfo;
SAMTRACE("SampFixGroupCount");
RtlInitUnicodeString( &KeyName,
L"DOMAINS\\Account\\Groups"
);
//
// Open this key.
// Query the number of sub-keys in the key.
// The number of groups is one less than the number
// of values (because there is one key called "Names").
//
InitializeObjectAttributes( &ObjectAttributes,
&KeyName,
OBJ_CASE_INSENSITIVE,
SampKey,
NULL
);
SampDumpNtOpenKey((KEY_READ | KEY_WRITE), &ObjectAttributes, 0);
NtStatus = RtlpNtOpenKey(
&AccountHandle,
(KEY_READ | KEY_WRITE),
&ObjectAttributes,
0
);
if (NT_SUCCESS(NtStatus)) {
NtStatus = NtQueryKey(
AccountHandle,
KeyFullInformation,
NULL, // Buffer
0, // Length
&ResultLength
);
SampDumpNtQueryKey(KeyFullInformation,
NULL,
0,
&ResultLength);
if (NtStatus == STATUS_BUFFER_OVERFLOW ||
NtStatus == STATUS_BUFFER_TOO_SMALL) {
KeyInfo = RtlAllocateHeap( RtlProcessHeap(), 0, ResultLength);
if (KeyInfo == NULL) {
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
} else {
NtStatus = NtQueryKey(
AccountHandle,
KeyFullInformation,
KeyInfo, // Buffer
ResultLength, // Length
&ResultLength
);
SampDumpNtQueryKey(KeyFullInformation,
KeyInfo,
ResultLength,
&ResultLength);
if (NT_SUCCESS(NtStatus)) {
GroupCount = (KeyInfo->SubKeys - 1);
}
RtlFreeHeap( RtlProcessHeap(), 0, KeyInfo );
}
}
if (NT_SUCCESS(NtStatus)) {
RtlInitUnicodeString( &NullName, NULL );
NtStatus = NtSetValueKey(
AccountHandle,
&NullName, // Null value name
0, // Title Index
GroupCount, // Count goes in Type field
NULL, // No data
0
);
}
IgnoreStatus = NtClose( AccountHandle );
ASSERT( NT_SUCCESS(IgnoreStatus) );
}
return(NtStatus);
}
#ifdef SAMP_SETUP_FAILURE_TEST
NTSTATUS
SampInitializeForceError(
OUT PNTSTATUS ForcedStatus
)
/*++
Routine Description:
This function forces an error to occur in the SAM initialization/installation.
The error to be simulated is specified by storing the desired Nt Status
value to be simulated in the REG_DWORD registry key valie PhonyLsaError
in HKEY_LOCAL_MACHINE\System\Setup.
Arguments:
ForcedStatus - Receives the Nt status code to be simulated. If set to a
non-success status, SAM initialization is bypassed and the specified
status code is set instead. If STATUS_SUCCESS is returned, no
simulation takes place and SAM initializes as it would normally.
Return Values:
NTSTATUS - Standard Nt Result Code
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
NTSTATUS OutputForcedStatus = STATUS_SUCCESS;
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE KeyHandle = NULL;
PKEY_VALUE_FULL_INFORMATION KeyValueInformation = NULL;
ULONG KeyValueInfoLength;
ULONG ResultLength;
UNICODE_STRING KeyPath;
UNICODE_STRING ValueName;
SAMTRACE("SampInitializeForceError");
RtlInitUnicodeString( &KeyPath, L"\\Registry\\Machine\\System\\Setup" );
RtlInitUnicodeString( &ValueName, L"PhonyLsaError" );
InitializeObjectAttributes( &ObjectAttributes,
&KeyPath,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
SampDumpNtOpenKey((MAXIMUM_ALLOWED), &ObjectAttributes, 0);
NtStatus = NtOpenKey( &KeyHandle, MAXIMUM_ALLOWED, &ObjectAttributes);
if (!NT_SUCCESS( NtStatus )) {
//
// If the error is simply that the registry key does not exist,
// do not simulate an error and allow SAM initialization to
// proceed.
//
if (NtStatus != STATUS_OBJECT_NAME_NOT_FOUND) {
KdPrint(("SAMSS: NtOpenKey for Phony Lsa Error failed 0x%lx\n", NtStatus));
goto InitializeForceErrorError;
}
NtStatus = STATUS_SUCCESS;
goto InitializeForceErrorFinish;
}
KeyValueInfoLength = 256;
NtStatus = STATUS_NO_MEMORY;
KeyValueInformation = RtlAllocateHeap(
RtlProcessHeap(),
0,
KeyValueInfoLength
);
if (KeyValueInformation == NULL) {
goto InitializeForceErrorError;
}
NtStatus = NtQueryValueKey(
KeyHandle,
&ValueName,
KeyValueFullInformation,
KeyValueInformation,
KeyValueInfoLength,
&ResultLength
);
SampDumpNtQueryValueKey(&ValueName,
KeyValueFullInformation,
KeyValueInformation,
KeyValueInfoLength,
&ResultLength);
if (!NT_SUCCESS(NtStatus)) {
//
// If the error is simply that that the PhonyLsaError value has not
// been set, do not simulate an error and instead allow SAM initialization
// to proceed.
//
if (NtStatus != STATUS_OBJECT_NAME_NOT_FOUND) {
KdPrint(("SAMSS: NtQueryValueKey for Phony Lsa Error failed 0x%lx\n", NtStatus));
goto InitializeForceErrorError;
}
NtStatus = STATUS_SUCCESS;
goto InitializeForceErrorFinish;
}
NtStatus = STATUS_INVALID_PARAMETER;
if (KeyValueInformation->Type != REG_DWORD) {
KdPrint(("SAMSS: Key for Phony Lsa Error is not REG_DWORD type"));
goto InitializeForceErrorError;
}
NtStatus = STATUS_SUCCESS;
//
// Obtain the error code stored as the registry key value
//
OutputForcedStatus = *((NTSTATUS *)((PCHAR)KeyValueInformation + KeyValueInformation->DataOffset));
InitializeForceErrorFinish:
//
// Clean up our resources.
//
if (KeyValueInformation != NULL) {
RtlFreeHeap( RtlProcessHeap(), 0, KeyValueInformation );
}
if (KeyHandle != NULL) {
NtClose( KeyHandle );
}
*ForcedStatus = OutputForcedStatus;
return(NtStatus);
InitializeForceErrorError:
goto InitializeForceErrorFinish;
}
#endif // SAMP_SETUP_FAILURE_TEST
#if SAMP_DIAGNOSTICS
VOID
SampActivateDebugProcess( VOID )
/*++
Routine Description:
This function activates a process with a time delay.
The point of this action is to provide some diagnostic capabilities
during SETUP. This originated out of the need to run dh.exe (to get
a heap dump of LSASS.exe) during setup.
Arguments:
Arguments are provided via global variables. The debug user is
given an opportunity to change these string values before the
process is activated.
Return Values:
None.
--*/
{
NTSTATUS
NtStatus;
HANDLE
Thread;
DWORD
ThreadId;
IF_NOT_SAMP_GLOBAL( ACTIVATE_DEBUG_PROC ) {
return;
}
//
// Do all the work in another thread so that it can wait before
// activating the debug process.
//
Thread = CreateThread(
NULL,
0L,
(LPTHREAD_START_ROUTINE)SampActivateDebugProcessWrkr,
0L,
0L,
&ThreadId
);
if (Thread != NULL) {
(VOID) CloseHandle( Thread );
}
return;
}
NTSTATUS
SampActivateDebugProcessWrkr(
IN PVOID ThreadParameter
)
/*++
Routine Description:
This function activates a process with a time delay.
The point of this action is to provide some diagnostic capabilities
during SETUP. This originated out of the need to run dh.exe (to get
a heap dump of LSASS.exe) during setup.
The user is given the opportunity to change any or all of the
following values before the process is activated (and before
we wait):
Seconds until activation
Image to activate
Command line to image
Arguments:
ThreadParameter - Not used.
Return Values:
STATUS_SUCCESS
--*/
{
NTSTATUS
NtStatus;
UNICODE_STRING
CommandLine;
ULONG
Delay = 30; // Number of seconds
SECURITY_ATTRIBUTES
ProcessSecurityAttributes;
STARTUPINFO
StartupInfo;
PROCESS_INFORMATION
ProcessInformation;
SECURITY_DESCRIPTOR
SD;
BOOL
Result;
RtlInitUnicodeString( &CommandLine,
TEXT("dh.exe -p 33") );
//
// Give the user an opportunity to change parameter strings...
//
SampDiagPrint( ACTIVATE_DEBUG_PROC,
("SAM: Diagnostic flags are set to activate a debug process...\n"
" The following parameters are being used:\n\n"
" Command Line [0x%lx]: *%wZ*\n"
" Seconds to activation [address: 0x%lx]: %d\n\n"
" Change parameters if necessary and then proceed.\n"
" Use |# command at the ntsd prompt to see the process ID\n"
" of lsass.exe\n",
&CommandLine, &CommandLine,
&Delay, Delay) );
DbgBreakPoint();
//
// Wait for Delay seconds ...
//
Sleep( Delay*1000 );
SampDiagPrint( ACTIVATE_DEBUG_PROC,
("SAM: Activating debug process %wZ\n",
&CommandLine) );
//
// Initialize process security info
//
InitializeSecurityDescriptor( &SD ,SECURITY_DESCRIPTOR_REVISION1 );
ProcessSecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
ProcessSecurityAttributes.lpSecurityDescriptor = &SD;
ProcessSecurityAttributes.bInheritHandle = FALSE;
//
// Initialize process startup info
//
RtlZeroMemory( &StartupInfo, sizeof(StartupInfo) );
StartupInfo.cb = sizeof(STARTUPINFO);
StartupInfo.lpReserved = CommandLine.Buffer;
StartupInfo.lpTitle = CommandLine.Buffer;
StartupInfo.dwX =
StartupInfo.dwY =
StartupInfo.dwXSize =
StartupInfo.dwYSize = 0L;
StartupInfo.dwFlags = STARTF_FORCEOFFFEEDBACK;
StartupInfo.wShowWindow = SW_SHOW; // let it be seen if possible
StartupInfo.lpReserved2 = NULL;
StartupInfo.cbReserved2 = 0;
//
// Now create the diagnostic process...
//
Result = CreateProcess(
NULL, // Image name
CommandLine.Buffer,
&ProcessSecurityAttributes,
NULL, // ThreadSecurityAttributes
FALSE, // InheritHandles
CREATE_UNICODE_ENVIRONMENT, //Flags
NULL, //Environment,
NULL, //CurrentDirectory,
&StartupInfo,
&ProcessInformation);
if (!Result) {
SampDiagPrint( ACTIVATE_DEBUG_PROC,
("SAM: Couldn't activate diagnostic process.\n"
" Error: 0x%lx (%d)\n\n",
GetLastError(), GetLastError()) );
}
return(STATUS_SUCCESS); // Exit this thread
}
#endif // SAMP_DIAGNOSTICS
NTSTATUS
SampUserModeSamRpcInit(
VOID
)
/*++
Routine Description
This routine initializes RPC for the User Mode SAM
case
Arguments:
None
Return Values:
STATUS_SUCCESS on success
various RPC error codes in case of Error
--*/
{
NTSTATUS NtStatus;
RPC_BINDING_VECTOR * BindingVector = NULL;
//
// Specify Protocol sequences to use and Register End Points
//
NtStatus = RpcServerUseAllProtseqs(
100, // Max Calls
NULL // security descriptor
);
//
// Register EndPoints
//
NtStatus = RpcServerInqBindings(&BindingVector);
if (!NT_SUCCESS(NtStatus))
goto Error;
NtStatus = RpcEpRegister(
samr_ServerIfHandle,
BindingVector,
NULL, // no uuid vector
L"" // no annotation
);
#if 0
//
// Export to the RPC Name Service Database
//
NtStatus = RpcNsBindingExport(
RPC_C_NS_SYNTAX_DEFAULT,
NULL,
samr_ServerIfHandle,
BindingVector,
NULL
);
if (!NT_SUCCESS(NtStatus))
goto Error;
#endif
//
// Register the Interface
//
NtStatus = RpcServerRegisterIf(samr_ServerIfHandle, NULL, NULL);
Error:
if (BindingVector)
RpcBindingVectorFree(&BindingVector);
return (NtStatus);
}