diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/newsam2 | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/newsam2')
94 files changed, 139866 insertions, 0 deletions
diff --git a/private/newsam2/client/bind.c b/private/newsam2/client/bind.c new file mode 100644 index 000000000..7b69f178f --- /dev/null +++ b/private/newsam2/client/bind.c @@ -0,0 +1,273 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + wrappers.c + +Abstract: + + This file contains all SAM rpc binding routines. + +Author: + + Jim Kelly (JimK) 4-July-1991 + +Environment: + + User Mode - Win32 + +Revision History: + + +--*/ + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "samclip.h" + + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// private service prototypes // +// // +/////////////////////////////////////////////////////////////////////////////// + + + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Routines // +// // +/////////////////////////////////////////////////////////////////////////////// + + + +RPC_BINDING_HANDLE +PSAMPR_SERVER_NAME_bind ( + PSAMPR_SERVER_NAME ServerName + ) + +/*++ + +Routine Description: + + This routine calls a common bind routine that is shared by all services. + This routine is called from SamConnect server stub to connect to the + server. + +Arguments: + + ServerName - A pointer to a string containing the name of the server + to bind with. + +Return Value: + + The binding handle is returned to the stub routine. If the + binding is unsuccessful, a NULL will be returned. + +--*/ +{ + RPC_BINDING_HANDLE BindingHandle; + WCHAR *StringBinding; + RPC_STATUS RpcStatus; + +#ifdef USER_MODE_SAM + + RpcStatus = RpcStringBindingComposeW( + NULL, // Object UUID + L"ncacn_np", // Protocol Sequence, Use Named Pipes + ServerName, // Network Address + NULL, // EndPoint, Server called RpcEpRegister + 0, // Options + &StringBinding + ); + if NT_SUCCESS(RpcStatus) + { + RpcStatus = RpcBindingFromStringBindingW( + StringBinding, + &BindingHandle + ); + + RpcStringFreeW(&StringBinding); + } + +#else + + RpcpBindRpc ( + ServerName, + L"samr", + 0, + &BindingHandle + ); + +#endif + + return( BindingHandle); +} + + + +void +PSAMPR_SERVER_NAME_unbind ( + PSAMPR_SERVER_NAME ServerName, + RPC_BINDING_HANDLE BindingHandle + ) + +/*++ + +Routine Description: + + This routine calls a common unbind routine that is shared by + all services. + + This routine is called from the SamConnect client stub to + unbind from the SAM client. + + +Arguments: + + ServerName - This is the name of the server from which to unbind. + + BindingHandle - This is the binding handle that is to be closed. + +Return Value: + + none. + +--*/ +{ + UNREFERENCED_PARAMETER(ServerName); // This parameter is not used + + + RpcpUnbindRpc ( BindingHandle ); + return; +} + +RPC_BINDING_HANDLE +SampSecureBind( + LPWSTR ServerName, + ULONG AuthnLevel + ) + +/*++ + +Routine Description: + + This routine calls a common bind routine that is shared by all services. + This routine is called from SamConnect server stub to connect to the + server. + +Arguments: + + ServerName - A pointer to a string containing the name of the server + to bind with. + + AuthnLevel - Authentication level to bind with. + +Return Value: + + The binding handle is returned to the stub routine. If the + binding is unsuccessful, a NULL will be returned. + +--*/ +{ + RPC_BINDING_HANDLE BindingHandle = NULL; + RPC_STATUS RpcStatus; + +#if 1 + RpcpBindRpc ( ServerName, + L"samr", + 0, + &BindingHandle + ); +#else + LPWSTR StringBinding; + RpcStatus = RpcStringBindingComposeW( + 0, + L"ncacn_spx", + ServerName+2, + NULL, // dynamic endpoint + NULL, // no options + &StringBinding + ); + if (RpcStatus != 0) + { + return(NULL); + } + RpcStatus = RpcBindingFromStringBindingW( + StringBinding, + &BindingHandle + ); + RpcStringFreeW(&StringBinding); + +#endif + + + if ( (BindingHandle != NULL) && + (AuthnLevel != RPC_C_AUTHN_LEVEL_NONE) ) { + + RpcStatus = RpcBindingSetAuthInfoW( + BindingHandle, + NULL, // server principal name + AuthnLevel, + RPC_C_AUTHN_WINNT, + NULL, + RPC_C_AUTHZ_DCE + ); + if (RpcStatus != 0) { + RpcBindingFree(&BindingHandle); + } + + } + + + + return( BindingHandle); +} + + + +void +SampSecureUnbind ( + RPC_BINDING_HANDLE BindingHandle + ) + +/*++ + +Routine Description: + + This routine calls a common unbind routine that is shared by + all services. + + This routine is called from the SamConnect client stub to + unbind from the SAM client. + + +Arguments: + + BindingHandle - This is the binding handle that is to be closed. + +Return Value: + + none. + +--*/ +{ + + + RpcpUnbindRpc ( BindingHandle ); + return; +} + diff --git a/private/newsam2/client/makefile b/private/newsam2/client/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/newsam2/client/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/newsam2/client/sam_rev.rc b/private/newsam2/client/sam_rev.rc new file mode 100644 index 000000000..ee07fbbeb --- /dev/null +++ b/private/newsam2/client/sam_rev.rc @@ -0,0 +1,10 @@ +#include <windows.h> +#include <ntverp.h> + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "SAM Library DLL" +#define VER_INTERNALNAME_STR "SAMLib.DLL" +#define VER_ORIGINALFILENAME_STR "SAMLib.DLL" + +#include "common.ver" diff --git a/private/newsam2/client/samclip.h b/private/newsam2/client/samclip.h new file mode 100644 index 000000000..ca8319833 --- /dev/null +++ b/private/newsam2/client/samclip.h @@ -0,0 +1,96 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + samclip.h + +Abstract: + + This file contains definitions needed by SAM client stubs. + +Author: + + Jim Kelly (JimK) 4-July-1991 + +Environment: + + User Mode - Win32 + +Revision History: + + +--*/ + +#ifndef _NTSAMP_CLIENT_ +#define _NTSAMP_CLIENT_ + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include <nt.h> +#include <ntrtl.h> // DbgPrint prototype +#include <rpc.h> // DataTypes and runtime APIs +#include <nturtl.h> // needed for winbase.h +#include <windows.h> // LocalAlloc +//#include <winbase.h> // LocalAlloc + +#include <string.h> // strlen +#include <stdio.h> // sprintf +//#include <tstring.h> // Unicode string macros + +#include <ntrpcp.h> // prototypes for MIDL user functions +#include <samrpc_c.h> // midl generated client SAM RPC definitions +#include <lmcons.h> // To get LM password length +#include <ntsam.h> +#include <ntsamp.h> +#include <ntlsa.h> // for LsaOpenPolicy... +#include <rc4.h> // rc4, rc4_key +#include <rpcndr.h> // RpcSsDestroyContext + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Defines // +// // +/////////////////////////////////////////////////////////////////////////////// + + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// data types // +// // +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Prototypes // +// // +/////////////////////////////////////////////////////////////////////////////// + + +void +SampSecureUnbind ( + RPC_BINDING_HANDLE BindingHandle + ); + +RPC_BINDING_HANDLE +SampSecureBind( + LPWSTR ServerName, + ULONG AuthnLevel + ); + +#endif // _NTSAMP_CLIENT_ diff --git a/private/newsam2/client/samlib.def b/private/newsam2/client/samlib.def new file mode 100644 index 000000000..572b141b4 --- /dev/null +++ b/private/newsam2/client/samlib.def @@ -0,0 +1,70 @@ +LIBRARY SAMLIB + +DESCRIPTION 'Security Accounts Manager User-Mode Client Library' + +EXPORTS + +; +; Dave Hart (DaveHart) 28-Mar-92 +; +; Exported Public Sam APIs +; + SamFreeMemory + SamSetSecurityObject + SamQuerySecurityObject + SamCloseHandle + SamConnect + SamShutdownSamServer + SamLookupDomainInSamServer + SamEnumerateDomainsInSamServer + SamOpenDomain + SamQueryInformationDomain + SamSetInformationDomain + SamCreateGroupInDomain + SamEnumerateGroupsInDomain + SamCreateUserInDomain + SamCreateUser2InDomain + SamEnumerateUsersInDomain + SamCreateAliasInDomain + SamEnumerateAliasesInDomain + SamRemoveMemberFromForeignDomain + SamGetAliasMembership + SamLookupNamesInDomain + SamLookupIdsInDomain + SamOpenGroup + SamQueryInformationGroup + SamSetInformationGroup + SamAddMemberToGroup + SamDeleteGroup + SamRemoveMemberFromGroup + SamGetMembersInGroup + SamSetMemberAttributesOfGroup + SamOpenAlias + SamQueryInformationAlias + SamSetInformationAlias + SamDeleteAlias + SamAddMemberToAlias + SamAddMultipleMembersToAlias + SamRemoveMemberFromAlias + SamRemoveMultipleMembersFromAlias + SamGetMembersInAlias + SamOpenUser + SamDeleteUser + SamQueryInformationUser + SamSetInformationUser + SamChangePasswordUser + SamChangePasswordUser2 + SamGetGroupsForUser + SamQueryDisplayInformation + SamGetDisplayEnumerationIndex + + SamTestPrivateFunctionsDomain + SamTestPrivateFunctionsUser + + SamiChangePasswordUser + SamiLmChangePasswordUser + SamiChangePasswordUser2 + SamiOemChangePasswordUser2 + SamiEncryptPasswords + + diff --git a/private/newsam2/client/sources b/private/newsam2/client/sources new file mode 100644 index 000000000..e5d7f4adc --- /dev/null +++ b/private/newsam2/client/sources @@ -0,0 +1,66 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Jim Kelly (JimK) 4-July-1991 + + +Revision History: + +!ENDIF + +DS_BUILD=1 + +MAJORCOMP = SAM +MINORCOMP = client + +TARGETNAME= samlib +TARGETPATH=$(BASEDIR)\public\sdk\lib2 +TARGETTYPE=DYNLINK + +TARGETLIBS= \ + $(BASEDIR)\public\sdk\lib\*\advapi32.lib \ + $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \ + $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \ + $(BASEDIR)\public\sdk\lib\*\rpcndr.lib \ + $(BASEDIR)\Public\Sdk\Lib\*\kernel32.lib \ + ..\..\lsa\crypt\engine\obj\*\rc4c.obj + + +INCLUDES=.;..;..\inc;..\..\inc;..\..\lsa\crypt\engine + + + +SOURCES=\ + bind.c \ + sam_rev.rc \ + samrpc_c.c \ + wrappers.c + + +UMTYPE=console +UMTEST=temp*tconnect*tshut*tsamobj*tchgpwd*tdisplay +C_DEFINES=$(C_DEFINES) -DRPC_NO_WINDOWS_H +UMLIBS= \ + $(BASEDIR)\public\sdk\lib\*\samlib.lib \ + $(BASEDIR)\public\sdk\lib\*\advapi32.lib \ + $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \ + $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \ + $(BASEDIR)\public\sdk\lib\*\rpcndr.lib \ + $(BASEDIR)\public\sdk\lib\*\advapi32.lib \ + $(BASEDIR)\public\sdk\lib\*\ntdll.lib \ + $(BASEDIR)\Public\Sdk\Lib\*\kernel32.lib diff --git a/private/newsam2/client/tchgpwd.c b/private/newsam2/client/tchgpwd.c new file mode 100644 index 000000000..5177e05dc --- /dev/null +++ b/private/newsam2/client/tchgpwd.c @@ -0,0 +1,210 @@ +// Test changing a SAM password + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include <stdio.h> +#include <nt.h> +#include <ntsam.h> +#include <ntsamp.h> +#include <ntlsa.h> +#include <ntrpcp.h> // prototypes for MIDL user functions +#include <seopaque.h> +#include <string.h> + + +VOID +main (argc, argv) +int argc; +char **argv; + +{ + NTSTATUS Status; + OBJECT_ATTRIBUTES ObjectAttributes; + SECURITY_QUALITY_OF_SERVICE SecurityQos; + PSID DomainSid = NULL; + PULONG UserId = NULL; + PSID_NAME_USE NameUse = NULL; + SAM_HANDLE SamHandle = NULL; + SAM_HANDLE DomainHandle = NULL; + SAM_HANDLE UserHandle = NULL; + WCHAR UserNameBuffer[80]; + WCHAR OldPasswordBuffer[80]; + WCHAR NewPasswordBuffer[80]; + UNICODE_STRING UserName; + UNICODE_STRING Domain; + UNICODE_STRING OldPassword; + UNICODE_STRING NewPassword; + ANSI_STRING AnsiString; + + UserName.Buffer = UserNameBuffer; + UserName.MaximumLength = sizeof(UserNameBuffer); + + OldPassword.Buffer = OldPasswordBuffer; + OldPassword.MaximumLength = sizeof(OldPasswordBuffer); + + NewPassword.Buffer = NewPasswordBuffer; + NewPassword.MaximumLength = sizeof(NewPasswordBuffer); + + RtlInitUnicodeString(&Domain, L"Account"); + + + RtlInitAnsiString(&AnsiString, argv[1]); + RtlAnsiStringToUnicodeString(&UserName, &AnsiString, FALSE); + + if (*(argv[2]) == '-') { + *(argv[2]) = 0; + } + RtlInitAnsiString(&AnsiString, argv[2]); + RtlAnsiStringToUnicodeString(&OldPassword, &AnsiString, FALSE); + + if (*(argv[3]) == '-') { + *(argv[3]) = 0; + } + RtlInitAnsiString(&AnsiString, argv[3]); + RtlAnsiStringToUnicodeString(&NewPassword, &AnsiString, FALSE); + + // + // Setup ObjectAttributes for SamConnect call. + // + + InitializeObjectAttributes(&ObjectAttributes, NULL, 0, 0, NULL); + ObjectAttributes.SecurityQualityOfService = &SecurityQos; + + SecurityQos.Length = sizeof(SecurityQos); + SecurityQos.ImpersonationLevel = SecurityIdentification; + SecurityQos.ContextTrackingMode = SECURITY_STATIC_TRACKING; + SecurityQos.EffectiveOnly = FALSE; + + Status = SamConnect( + NULL, + &SamHandle, + GENERIC_EXECUTE, + &ObjectAttributes + ); + + if ( !NT_SUCCESS(Status) ) { + DbgPrint("MspChangePasswordSam: SamConnect failed, status %8.8x\n", Status); + goto Cleanup; + } + + + Status = SamLookupDomainInSamServer( + SamHandle, + &Domain, + &DomainSid + ); + + if ( !NT_SUCCESS(Status) ) { + DbgPrint("MspChangePasswordSam: Cannot find account domain, status %8.8x\n", Status); + Status = STATUS_CANT_ACCESS_DOMAIN_INFO; + goto Cleanup; + } + + Status = SamOpenDomain( + SamHandle, + GENERIC_EXECUTE, + DomainSid, + &DomainHandle + ); + + if ( !NT_SUCCESS(Status) ) { + DbgPrint("MspChangePasswordSam: Cannot open account domain, status %8.8x\n", Status); + Status = STATUS_CANT_ACCESS_DOMAIN_INFO; + goto Cleanup; + } + + Status = SamLookupNamesInDomain( + DomainHandle, + 1, + &UserName, + &UserId, + &NameUse + ); + + if ( !NT_SUCCESS(Status) ) { + DbgPrint("MspChangePasswordSam: Cannot lookup user %wZ, status %8.8x\n", &UserName, Status); + goto Cleanup; + } + + Status = SamOpenUser( + DomainHandle, + USER_CHANGE_PASSWORD, + *UserId, + &UserHandle + ); + + if ( !NT_SUCCESS(Status) ) { + DbgPrint("MspChangePasswordSam: Cannot open user %wZ, status %8.8x\n", + &UserName, Status); + goto Cleanup; + } + + Status = SamChangePasswordUser( + UserHandle, + &OldPassword, + &NewPassword + ); + + if ( !NT_SUCCESS(Status) ) { + DbgPrint("MspChangePasswordSam: Failed to change user password, status %8.8x\n", Status); + } + + +Cleanup: + + // + // Free DomainSid if used. + // + + if (DomainSid) { + SamFreeMemory(DomainSid); + } + + // + // Free UserId if used. + // + + if (UserId) { + SamFreeMemory(UserId); + } + + // + // Free NameUse if used. + // + + if (NameUse) { + SamFreeMemory(NameUse); + } + + // + // Close UserHandle if open. + // + + if (UserHandle) { + SamCloseHandle(UserHandle); + } + + // + // Close DomainHandle if open. + // + + if (DomainHandle) { + SamCloseHandle(DomainHandle); + } + + // + // Close SamHandle if open. + // + + if (SamHandle) { + SamCloseHandle(SamHandle); + } + +} + diff --git a/private/newsam2/client/tconnect.c b/private/newsam2/client/tconnect.c new file mode 100644 index 000000000..03d621d24 --- /dev/null +++ b/private/newsam2/client/tconnect.c @@ -0,0 +1,98 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + tconnect.c + +Abstract: + + This is the file for a simple connection test to SAM. + +Author: + + Jim Kelly (JimK) 4-July-1991 + +Environment: + + User Mode - Win32 + +Revision History: + + +--*/ + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include <nt.h> +#include <ntsam.h> +#include <ntrtl.h> // DbgPrint() + + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// private service prototypes // +// // +/////////////////////////////////////////////////////////////////////////////// + + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Routines // +// // +/////////////////////////////////////////////////////////////////////////////// + +VOID +main ( + VOID + ) + +/*++ + +Routine Description: + + This is the main entry routine for this test. + +Arguments: + + None. + +Return Value: + + + Note: + + +--*/ +{ + NTSTATUS NtStatus; + SAM_HANDLE ServerHandle; + OBJECT_ATTRIBUTES ObjectAttributes; + + + InitializeObjectAttributes( &ObjectAttributes, NULL, 0, 0, NULL ); + + + NtStatus = SamConnect( + NULL, // ServerName (Local machine) + &ServerHandle, + SAM_SERVER_ALL_ACCESS, + &ObjectAttributes + ); + + DbgPrint("SAM TEST (Connect): Status of SamConnect() is: 0x%lx\n", NtStatus); + + + return; +} diff --git a/private/newsam2/client/tdisplay.c b/private/newsam2/client/tdisplay.c new file mode 100644 index 000000000..7ac570969 --- /dev/null +++ b/private/newsam2/client/tdisplay.c @@ -0,0 +1,638 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + tdisplay.c + +Abstract: + + This file is a temporary test for the Display query apis. + +Author: + + Jim Kelly (JimK) 14-Feb-1992 + +Environment: + + User Mode - Win32 + +Revision History: + + +--*/ + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + + +#include <nt.h> +#include <ntsam.h> +#include <ntrtl.h> + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Global Variables // +// // +/////////////////////////////////////////////////////////////////////////////// + + +SAM_HANDLE SamHandle; +SAM_HANDLE DomainHandle; +PSID DomainSid; + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Routines // +// // +/////////////////////////////////////////////////////////////////////////////// + + + +VOID _CRTAPI1 +main( VOID ) + +{ + NTSTATUS Status; + OBJECT_ATTRIBUTES ObjectAttributes; + SECURITY_QUALITY_OF_SERVICE SecurityQos; + UNICODE_STRING Domain; + + UNICODE_STRING TestString; + ULONG TestIndex; + + + ULONG TotalAvailable, TotalReturned, ReturnedEntryCount, i; + PDOMAIN_DISPLAY_USER SortedUsers; + PDOMAIN_DISPLAY_MACHINE SortedMachines; + PDOMAIN_DISPLAY_GROUP SortedGroups; + PDOMAIN_DISPLAY_OEM_USER SortedOemUsers; + PDOMAIN_DISPLAY_OEM_GROUP SortedOemGroups; + + + + SamHandle = NULL; + DomainHandle = NULL; + DomainSid = NULL; + + DbgPrint("\n\n\nSAM TEST: Testing SamQueryDisplayInformation() api\n"); + + // + // Setup ObjectAttributes for SamConnect call. + // + + InitializeObjectAttributes(&ObjectAttributes, NULL, 0, 0, NULL); + ObjectAttributes.SecurityQualityOfService = &SecurityQos; + + SecurityQos.Length = sizeof(SecurityQos); + SecurityQos.ImpersonationLevel = SecurityIdentification; + SecurityQos.ContextTrackingMode = SECURITY_STATIC_TRACKING; + SecurityQos.EffectiveOnly = FALSE; + + Status = SamConnect( + NULL, + &SamHandle, + GENERIC_EXECUTE, + &ObjectAttributes + ); + + if ( !NT_SUCCESS(Status) ) { + DbgPrint("SamConnect failed, status %8.8x\n", Status); + goto Cleanup; + } + + RtlInitUnicodeString(&Domain, L"JIMK_DOM2"); + + Status = SamLookupDomainInSamServer( + SamHandle, + &Domain, + &DomainSid + ); + + if ( !NT_SUCCESS(Status) ) { + DbgPrint("Cannot find account domain, status %8.8x\n", Status); + Status = STATUS_CANT_ACCESS_DOMAIN_INFO; + goto Cleanup; + } + + Status = SamOpenDomain( + SamHandle, + GENERIC_EXECUTE, + DomainSid, + &DomainHandle + ); + + if ( !NT_SUCCESS(Status) ) { + DbgPrint("Cannot open account domain, status %8.8x\n", Status); + Status = STATUS_CANT_ACCESS_DOMAIN_INFO; + goto Cleanup; + } + + + // + // normal users ... + // + + DbgPrint("Query users - zero index...\n"); + Status = SamQueryDisplayInformation ( + DomainHandle, + DomainDisplayUser, + 0, //Index + 10, // Entries + 1000, //PreferredMaximumLength, + &TotalAvailable, + &TotalReturned, + &ReturnedEntryCount, + &((PVOID)SortedUsers) + ); + + + DbgPrint("Completion Status: 0x%lx\n", Status); + if (NT_SUCCESS(Status)) { + DbgPrint(" Buffer Address: 0x%lx\n", SortedUsers); + DbgPrint(" TotalAvailable: 0x%lx\n", TotalAvailable); + DbgPrint(" TotalReturned: 0x%lx\n", TotalReturned); + DbgPrint(" Entries Returned: %d\n", ReturnedEntryCount); + + DbgPrint("\n\n"); + + for (i=0;i<ReturnedEntryCount ; i++) { + + DbgPrint("Array entry: [%d]\n", i); + DbgPrint(" Index: %d\n",SortedUsers[i].Index); + DbgPrint(" Rid: %d\n", SortedUsers[i].Rid); + DbgPrint(" Logon Name: *%Z*\n", &SortedUsers[i].LogonName); + DbgPrint(" Full Name: *%Z*\n", &SortedUsers[i].FullName); + DbgPrint("Admin Comment: *%Z*\n\n\n", &SortedUsers[i].AdminComment); + } + + Status = SamFreeMemory( SortedUsers ); + if (!NT_SUCCESS(Status)) { + DbgPrint("\n\n\n ******** SamFreeMemory() failed. *********\n"); + DbgPrint("\n\n\n ******** Status: 0x%lx *********\n", Status); + } + } + + DbgPrint("Query users - Nonzero index (index = 2)...\n"); + Status = SamQueryDisplayInformation ( + DomainHandle, + DomainDisplayUser, + 2, // Index + 10, // Entries + 100, // PreferredMaximumLength, + &TotalAvailable, + &TotalReturned, + &ReturnedEntryCount, + &((PVOID)SortedUsers) + ); + + + DbgPrint("Completion Status: 0x%lx\n", Status); + if (NT_SUCCESS(Status)) { + DbgPrint(" Buffer Address: 0x%lx\n", SortedUsers); + DbgPrint(" TotalAvailable: 0x%lx\n", TotalAvailable); + DbgPrint(" TotalReturned: 0x%lx\n", TotalReturned); + DbgPrint(" Entries Returned: %d\n", ReturnedEntryCount); + + DbgPrint("\n\n"); + + for (i=0;i<ReturnedEntryCount ; i++) { + + DbgPrint("Array entry: [%d]\n", i); + DbgPrint(" Index: %d\n",SortedUsers[i].Index); + DbgPrint(" Rid: %d\n", SortedUsers[i].Rid); + DbgPrint(" Logon Name: *%Z*\n", &SortedUsers[i].LogonName); + DbgPrint(" Full Name: *%Z*\n", &SortedUsers[i].FullName); + DbgPrint("Admin Comment: *%Z*\n\n\n", &SortedUsers[i].AdminComment); + } + + Status = SamFreeMemory( SortedUsers ); + if (!NT_SUCCESS(Status)) { + DbgPrint("\n\n\n ******** SamFreeMemory() failed. *********\n"); + DbgPrint("\n\n\n ******** Status: 0x%lx *********\n", Status); + } + } + + + DbgPrint("Get enumeration index...\n"); + + RtlInitUnicodeString(&TestString, L"BString"); + + Status = SamGetDisplayEnumerationIndex ( + DomainHandle, + DomainDisplayUser, + &TestString, + &TestIndex + ); + + + DbgPrint("Completion Status: 0x%lx\n", Status); + if (NT_SUCCESS(Status)) { + DbgPrint(" Enumeration index for %wZ is %d\n", &TestString, TestIndex); + } + + + + // + // Machine accounts ... + // + + DbgPrint("\n\nQuery Machines - zero index...\n"); + Status = SamQueryDisplayInformation ( + DomainHandle, + DomainDisplayMachine, + 0, //Index + 10, // Entries + 1000, //PreferredMaximumLength, + &TotalAvailable, + &TotalReturned, + &ReturnedEntryCount, + &((PVOID)SortedMachines) + ); + + + DbgPrint("Completion Status: 0x%lx\n", Status); + if (NT_SUCCESS(Status)) { + DbgPrint(" Buffer Address: 0x%lx\n", SortedMachines); + DbgPrint(" TotalAvailable: 0x%lx\n", TotalAvailable); + DbgPrint(" TotalReturned: 0x%lx\n", TotalReturned); + DbgPrint(" Entries Returned: %d\n", ReturnedEntryCount); + + DbgPrint("\n\n"); + + for (i=0;i<ReturnedEntryCount ; i++) { + + DbgPrint("Array entry: [%d]\n", i); + DbgPrint(" Index: %d\n",SortedMachines[i].Index); + DbgPrint(" Rid: %d\n", SortedMachines[i].Rid); + DbgPrint(" Machine: *%Z*\n", &SortedMachines[i].Machine); + DbgPrint(" Comment: *%Z*\n\n\n", &SortedMachines[i].Comment); + } + + Status = SamFreeMemory( SortedMachines ); + if (!NT_SUCCESS(Status)) { + DbgPrint("\n\n\n ******** SamFreeMemory() failed. *********\n"); + DbgPrint("\n\n\n ******** Status: 0x%lx *********\n", Status); + } + } + + DbgPrint("Query Machines - Nonzero index (index = 1)...\n"); + Status = SamQueryDisplayInformation ( + DomainHandle, + DomainDisplayMachine, + 1, //Index + 10, // Entries + 1000, //PreferredMaximumLength, + &TotalAvailable, + &TotalReturned, + &ReturnedEntryCount, + &((PVOID)SortedMachines) + ); + + + DbgPrint("Completion Status: 0x%lx\n", Status); + if (NT_SUCCESS(Status)) { + DbgPrint(" Buffer Address: 0x%lx\n", SortedMachines); + DbgPrint(" TotalAvailable: 0x%lx\n", TotalAvailable); + DbgPrint(" TotalReturned: 0x%lx\n", TotalReturned); + DbgPrint(" Entries Returned: %d\n", ReturnedEntryCount); + + DbgPrint("\n\n"); + + for (i=0;i<ReturnedEntryCount ; i++) { + + DbgPrint("Array entry: [%d]\n", i); + DbgPrint(" Index: %d\n",SortedMachines[i].Index); + DbgPrint(" Rid: %d\n", SortedMachines[i].Rid); + DbgPrint(" Machine: *%Z*\n", &SortedMachines[i].Machine); + DbgPrint(" Comment: *%Z*\n\n\n", &SortedMachines[i].Comment); + } + + Status = SamFreeMemory( SortedMachines ); + if (!NT_SUCCESS(Status)) { + DbgPrint("\n\n\n ******** SamFreeMemory() failed. *********\n"); + DbgPrint("\n\n\n ******** Status: 0x%lx *********\n", Status); + } + } + + + DbgPrint("Get enumeration index...\n"); + + RtlInitUnicodeString(&TestString, L"BString"); + + Status = SamGetDisplayEnumerationIndex ( + DomainHandle, + DomainDisplayMachine, + &TestString, + &TestIndex + ); + + + DbgPrint("Completion Status: 0x%lx\n", Status); + if (NT_SUCCESS(Status)) { + DbgPrint(" Enumeration index for %wZ is %d\n", &TestString, TestIndex); + } + + + + + // + // normal Groups ... + // + + DbgPrint("Query Groups - zero index...\n"); + Status = SamQueryDisplayInformation ( + DomainHandle, + DomainDisplayGroup, + 0, //Index + 10, // Entries + 1000, //PreferredMaximumLength, + &TotalAvailable, + &TotalReturned, + &ReturnedEntryCount, + &((PVOID)SortedGroups) + ); + + + DbgPrint("Completion Status: 0x%lx\n", Status); + if (NT_SUCCESS(Status)) { + DbgPrint(" Buffer Address: 0x%lx\n", SortedGroups); + DbgPrint(" TotalAvailable: 0x%lx\n", TotalAvailable); + DbgPrint(" TotalReturned: 0x%lx\n", TotalReturned); + DbgPrint(" Entries Returned: %d\n", ReturnedEntryCount); + + DbgPrint("\n\n"); + + for (i=0;i<ReturnedEntryCount ; i++) { + + DbgPrint("Array entry: [%d]\n", i); + DbgPrint(" Index: %d\n",SortedGroups[i].Index); + DbgPrint(" Rid: %d\n", SortedGroups[i].Rid); + DbgPrint(" Name: *%Z*\n", &SortedGroups[i].Group); + DbgPrint("Admin Comment: *%Z*\n\n\n", &SortedGroups[i].Comment); + } + + Status = SamFreeMemory( SortedGroups ); + if (!NT_SUCCESS(Status)) { + DbgPrint("\n\n\n ******** SamFreeMemory() failed. *********\n"); + DbgPrint("\n\n\n ******** Status: 0x%lx *********\n", Status); + } + } + + DbgPrint("Query Groups - Nonzero index (index = 2)...\n"); + Status = SamQueryDisplayInformation ( + DomainHandle, + DomainDisplayGroup, + 2, // Index + 10, // Entries + 100, // PreferredMaximumLength, + &TotalAvailable, + &TotalReturned, + &ReturnedEntryCount, + &((PVOID)SortedGroups) + ); + + + DbgPrint("Completion Status: 0x%lx\n", Status); + if (NT_SUCCESS(Status)) { + DbgPrint(" Buffer Address: 0x%lx\n", SortedGroups); + DbgPrint(" TotalAvailable: 0x%lx\n", TotalAvailable); + DbgPrint(" TotalReturned: 0x%lx\n", TotalReturned); + DbgPrint(" Entries Returned: %d\n", ReturnedEntryCount); + + DbgPrint("\n\n"); + + for (i=0;i<ReturnedEntryCount ; i++) { + + DbgPrint("Array entry: [%d]\n", i); + DbgPrint(" Index: %d\n",SortedGroups[i].Index); + DbgPrint(" Rid: %d\n", SortedGroups[i].Rid); + DbgPrint(" Name: *%Z*\n", &SortedGroups[i].Group); + DbgPrint("Admin Comment: *%Z*\n\n\n", &SortedGroups[i].Comment); + } + + Status = SamFreeMemory( SortedGroups ); + if (!NT_SUCCESS(Status)) { + DbgPrint("\n\n\n ******** SamFreeMemory() failed. *********\n"); + DbgPrint("\n\n\n ******** Status: 0x%lx *********\n", Status); + } + } + + + DbgPrint("Get enumeration index...\n"); + + RtlInitUnicodeString(&TestString, L"BString"); + + Status = SamGetDisplayEnumerationIndex ( + DomainHandle, + DomainDisplayGroup, + &TestString, + &TestIndex + ); + + + DbgPrint("Completion Status: 0x%lx\n", Status); + if (NT_SUCCESS(Status)) { + DbgPrint(" Enumeration index for %wZ is %d\n", &TestString, TestIndex); + } + + + // + // OEM user ... + // + + DbgPrint("Query OEM users - zero index...\n"); + Status = SamQueryDisplayInformation ( + DomainHandle, + DomainDisplayOemUser, + 0, //Index + 10, // Entries + 1000, //PreferredMaximumLength, + &TotalAvailable, + &TotalReturned, + &ReturnedEntryCount, + &((PVOID)SortedOemUsers) + ); + + + DbgPrint("Completion Status: 0x%lx\n", Status); + if (NT_SUCCESS(Status)) { + DbgPrint(" Buffer Address: 0x%lx\n", SortedOemUsers); + DbgPrint(" TotalAvailable: 0x%lx (should be garbage)\n", TotalAvailable); + DbgPrint(" TotalReturned: 0x%lx\n", TotalReturned); + DbgPrint(" Entries Returned: %d\n", ReturnedEntryCount); + + + DbgPrint("\n\n"); + + for (i=0;i<ReturnedEntryCount ; i++) { + + DbgPrint("Array entry: [%d]\n", i); + DbgPrint(" Index: %d\n",SortedOemUsers[i].Index); + DbgPrint(" User: *%Z*\n", &SortedOemUsers[i].User); + } + + Status = SamFreeMemory( SortedOemUsers ); + if (!NT_SUCCESS(Status)) { + DbgPrint("\n\n\n ******** SamFreeMemory() failed. *********\n"); + DbgPrint("\n\n\n ******** Status: 0x%lx *********\n", Status); + } + } + + DbgPrint("Query OEM users - Nonzero index (index = 2)...\n"); + Status = SamQueryDisplayInformation ( + DomainHandle, + DomainDisplayOemUser, + 2, // Index + 10, // Entries + 100, // PreferredMaximumLength, + &TotalAvailable, + &TotalReturned, + &ReturnedEntryCount, + &((PVOID)SortedOemUsers) + ); + + + DbgPrint("Completion Status: 0x%lx\n", Status); + if (NT_SUCCESS(Status)) { + DbgPrint(" Buffer Address: 0x%lx\n", SortedOemUsers); + DbgPrint(" TotalAvailable: 0x%lx (should be garbage)\n", TotalAvailable); + DbgPrint(" TotalReturned: 0x%lx\n", TotalReturned); + DbgPrint(" Entries Returned: %d\n", ReturnedEntryCount); + + DbgPrint("\n\n"); + + + for (i=0;i<ReturnedEntryCount ; i++) { + + DbgPrint("Array entry: [%d]\n", i); + DbgPrint(" Index: %d\n",SortedOemUsers[i].Index); + DbgPrint(" User: *%Z*\n", &SortedOemUsers[i].User); + } + + Status = SamFreeMemory( SortedOemUsers ); + if (!NT_SUCCESS(Status)) { + DbgPrint("\n\n\n ******** SamFreeMemory() failed. *********\n"); + DbgPrint("\n\n\n ******** Status: 0x%lx *********\n", Status); + } + } + + + + + // + // OEM groups ... + // + + DbgPrint("Query OEM groups - zero index...\n"); + Status = SamQueryDisplayInformation ( + DomainHandle, + DomainDisplayOemGroup, + 0, //Index + 10, // Entries + 1000, //PreferredMaximumLength, + &TotalAvailable, + &TotalReturned, + &ReturnedEntryCount, + &((PVOID)SortedOemGroups) + ); + + + DbgPrint("Completion Status: 0x%lx\n", Status); + if (NT_SUCCESS(Status)) { + DbgPrint(" Buffer Address: 0x%lx\n", SortedOemGroups); + DbgPrint(" TotalAvailable: 0x%lx (should be garbage)\n", TotalAvailable); + DbgPrint(" TotalReturned: 0x%lx\n", TotalReturned); + DbgPrint(" Entries Returned: %d\n", ReturnedEntryCount); + + + DbgPrint("\n\n"); + + for (i=0;i<ReturnedEntryCount ; i++) { + + DbgPrint("Array entry: [%d]\n", i); + DbgPrint(" Index: %d\n",SortedOemGroups[i].Index); + DbgPrint(" Group: *%Z*\n", &SortedOemGroups[i].Group); + } + + Status = SamFreeMemory( SortedOemGroups ); + if (!NT_SUCCESS(Status)) { + DbgPrint("\n\n\n ******** SamFreeMemory() failed. *********\n"); + DbgPrint("\n\n\n ******** Status: 0x%lx *********\n", Status); + } + } + + DbgPrint("Query OEM Groups - Nonzero index (index = 2)...\n"); + Status = SamQueryDisplayInformation ( + DomainHandle, + DomainDisplayOemGroup, + 2, // Index + 10, // Entries + 100, // PreferredMaximumLength, + &TotalAvailable, + &TotalReturned, + &ReturnedEntryCount, + &((PVOID)SortedGroups) + ); + + + DbgPrint("Completion Status: 0x%lx\n", Status); + if (NT_SUCCESS(Status)) { + DbgPrint(" Buffer Address: 0x%lx\n", SortedGroups); + DbgPrint(" TotalAvailable: 0x%lx (should be garbage)\n", TotalAvailable); + DbgPrint(" TotalReturned: 0x%lx\n", TotalReturned); + DbgPrint(" Entries Returned: %d\n", ReturnedEntryCount); + + DbgPrint("\n\n"); + + for (i=0;i<ReturnedEntryCount ; i++) { + + DbgPrint("Array entry: [%d]\n", i); + DbgPrint(" Index: %d\n",SortedOemGroups[i].Index); + DbgPrint(" Group: *%Z*\n", &SortedOemGroups[i].Group); + } + + Status = SamFreeMemory( SortedOemGroups ); + if (!NT_SUCCESS(Status)) { + DbgPrint("\n\n\n ******** SamFreeMemory() failed. *********\n"); + DbgPrint("\n\n\n ******** Status: 0x%lx *********\n", Status); + } + } + + + + + + + DbgPrint("\n\n Th Tha That's all folks\n"); + + +Cleanup: + + // + // Close DomainHandle if open. + // + + if (DomainHandle) { + SamCloseHandle(DomainHandle); + } + + // + // Close SamHandle if open. + // + + if (SamHandle) { + SamCloseHandle(SamHandle); + } + +} + diff --git a/private/newsam2/client/temp.c b/private/newsam2/client/temp.c new file mode 100644 index 000000000..0b7405346 --- /dev/null +++ b/private/newsam2/client/temp.c @@ -0,0 +1,571 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + temp.c + +Abstract: + + This file contains temporary SAM rpc wrapper routines. + +Author: + + Jim Kelly (JimK) 14-Feb-1992 + +Environment: + + User Mode - Win32 + +Revision History: + + +--*/ + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "samclip.h" + + +typedef struct _SAMP_TEMP_USER_STRINGS { + ULONG Rid; + WCHAR LogonName[14]; + WCHAR FullName[24]; + WCHAR AdminComment[24]; +} SAMP_TEMP_USER_STRINGS, *PSAMP_TEMP_USER_STRINGS; + + +#define SAMP_TEMP_USER_COUNT (40) +#define SAMP_TEMP_USER1 (25) +#define SAMP_TEMP_USER2 (15) + + +typedef struct _SAMP_TEMP_MACHINE_STRINGS { + ULONG Rid; + WCHAR Machine[14]; + WCHAR Comment[24]; +} SAMP_TEMP_MACHINE_STRINGS, *PSAMP_TEMP_MACHINE_STRINGS; + + +#define SAMP_TEMP_MACHINE_COUNT (40) +#define SAMP_TEMP_MACHINE1 (16) +#define SAMP_TEMP_MACHINE2 (24) + + +SAMP_TEMP_USER_STRINGS DummyUsers[SAMP_TEMP_USER_COUNT] = { + + {1031, L"Abba" , L"Abb Abb" , L"Admin Comment Field"}, + {1021, L"Acea" , L"Ace Abb" , L"Value Admin Comment"}, + {1526, L"beverlyE" , L"Beverly Eng" , L"Field Value Admin"}, + {1743, L"BorisB" , L"Boris Borsch" , L"Comment Field Value"}, + {1734, L"BruceK" , L"Bruce Kane" , L"Comment Field Value"}, + {1289, L"BullS" , L"Bull Shiite" , L"Comment Field Value"}, + {1830, L"CallieW" , L"Callie Wilson" , L"Comment Field Value"}, + {1628, L"CarrieT" , L"Carrie Tibbits" , L"Comment Field Value"}, + {1943, L"ChrisR" , L"Christopher Robin" , L"40 acre woods"}, + {1538, L"CorneliaG" , L"Cornelia Gutierrez" , L"Comment Field Value"}, + {1563, L"CoryA" , L"Cory Ander" , L"Comment Field Value"}, + {1758, L"DanielJ" , L"Daniel John" , L"Comment Field Value"}, + {1249, L"Dory" , L"Dory" , L"Comment Field Value"}, + {1957, L"EltonJ" , L"Elton John" , L"Comment Field Value"}, + {1555, L"HarrisonF" , L"Harrison Ford" , L"Comment Field Value"}, + {1795, L"HarryB" , L"Harry Belafonte" , L"Comment Field Value"}, + {1458, L"IngridB" , L"Ingrid Bergman" , L"Comment Field Value"}, + {1672, L"Ingris" , L"Ingris" , L"Comment Field Value"}, + {1571, L"JenniferB" , L"Jennifer Black" , L"Comment Field Value"}, + {1986, L"JoyceG" , L"Joyce Gerace" , L"Comment Field Value"}, + {1267, L"KristinM" , L"Kristin McKay" , L"Comment Field Value"}, + {1321, L"LeahD" , L"Leah Dootson" , L"The Lovely Miss D"}, + {2021, L"LisaP" , L"Lisa Perazzoli" , L"Wild On Skis"}, + {1212, L"MeganB" , L"Megan Bombeck" , L"M1"}, + {2758, L"MelisaB" , L"Melisa Bombeck" , L"M3"}, + {2789, L"MichaelB" , L"Michael Bombeck" , L"M2"}, + {2682, L"PanelopiP" , L"Panelopi Pitstop" , L"Comment Field Value"}, + {2438, L"Prudence" , L"Prudence Peackock" , L"Comment Field Value"}, + {2648, L"QwertyU" , L"Qwerty Uiop" , L"Comment Field Value"}, + {2681, L"ReaddyE" , L"Readdy Eddy" , L""}, + {2456, L"SovietA" , L"Soviet Union - NOT" , L"Soviet Union Aint"}, + {1753, L"TAAAA" , L"TTT AAAA" , L"Comment Field Value"}, + {1357, L"TBBB" , L"Ingris" , L"Comment Field Value"}, + {1951, L"TCCCCC" , L"Jennifer Black" , L"Comment Field Value"}, + {1159, L"TCAAAAAA" , L"Joyce Gerace" , L"Comment Field Value"}, + {1654, L"Ulga" , L"Ulga Bulga" , L"Comment Field Value"}, + {1456, L"UnixY" , L"Unix Yuck" , L"Unix - why ask why?"}, + {1852, L"Vera" , L"Vera Pensicola" , L""}, + {1258, L"WinP" , L"Winnie The Pooh" , L"Comment Field Value"}, + {2821, L"Zoro" , L"Zoro" , L"The sign of the Z"} +}; + + + + + +SAMP_TEMP_MACHINE_STRINGS DummyMachines[SAMP_TEMP_MACHINE_COUNT] = { + + {1031, L"WKS$abba" , L"Admin Comment Field"}, + {1021, L"WKS$Acea" , L"Value Admin Comment"}, + {1526, L"WKS$beverlyE" , L"Field Value Admin"}, + {1743, L"WKS$BorisB" , L"Comment Field Value"}, + {1734, L"WKS$BruceK" , L"Comment Field Value"}, + {1289, L"WKS$BullS" , L"Comment Field Value"}, + {1830, L"WKS$CallieW" , L"Comment Field Value"}, + {1628, L"WKS$CarrieT" , L"Comment Field Value"}, + {1943, L"WKS$ChrisR" , L"40 acre woods Server"}, + {1538, L"WKS$CorneliaG" , L"Comment Field Value"}, + {1563, L"WKS$CoryA" , L"Comment Field Value"}, + {1758, L"WKS$DanielJ" , L"Comment Field Value"}, + {1249, L"WKS$Dory" , L"Comment Field Value"}, + {1957, L"WKS$EltonJ" , L"Comment Field Value"}, + {1555, L"WKS$HarrisonF" , L"Comment Field Value"}, + {1795, L"WKS$HarryB" , L"Comment Field Value"}, + {1458, L"WKS$IngridB" , L"Comment Field Value"}, + {1672, L"WKS$Ingris" , L"Comment Field Value"}, + {1571, L"WKS$JenniferB" , L"Comment Field Value"}, + {1986, L"WKS$JoyceG" , L"Comment Field Value"}, + {1267, L"WKS$KristinM" , L"Comment Field Value"}, + {1321, L"WKS$LeahD" , L"The Lovely Miss D's"}, + {2021, L"WKS$LisaP" , L"Wild On Skis Server"}, + {1212, L"WKS$MeganB" , L"M1 Machine"}, + {2758, L"WKS$MelisaB" , L"M3 Machine"}, + {2789, L"WKS$MichaelB" , L"M2 Machine"}, + {2682, L"WKS$PanelopiP" , L"Comment Field Value"}, + {2438, L"WKS$Prudence" , L"Comment Field Value"}, + {2648, L"WKS$QwertyU" , L"Comment Field Value"}, + {2681, L"WKS$ReaddyE" , L"Ready Eddy Computer"}, + {2456, L"WKS$SovietA" , L"Soviet Union Aint"}, + {1753, L"WKS$TAAAA" , L"Comment Field Value"}, + {1357, L"WKS$TBBB" , L"Comment Field Value"}, + {1951, L"WKS$TCCCCC" , L"Comment Field Value"}, + {1159, L"WKS$TCAAAAAA" , L"Comment Field Value"}, + {1654, L"WKS$Ulga" , L"Comment Field Value"}, + {1456, L"WKS$UnixY" , L"Unix - why ask why?"}, + {1852, L"WKS$Vera" , L"Vera tissue"}, + {1258, L"WKS$WinP" , L"Comment Field Value"}, + {2821, L"WKS$Zoro" , L"The sign of the Z"} +}; + + + + +VOID +SampBuildDummyAccounts( + IN DOMAIN_DISPLAY_INFORMATION DisplayInformation, + IN ULONG Index, + OUT PULONG TotalAvailable, + OUT PULONG TotalReturned, + OUT PULONG ReturnedEntryCount, + OUT PVOID *SortedBuffer + ); + + + +VOID +SampBuildDummyAccounts( + IN DOMAIN_DISPLAY_INFORMATION DisplayInformation, + IN ULONG Index, + OUT PULONG TotalAvailable, + OUT PULONG TotalReturned, + OUT PULONG ReturnedEntryCount, + OUT PVOID *SortedBuffer + ) + +{ + ULONG AccountCount, Account1, Account2; + ULONG i, j, BeginIndex, EndIndex; + ULONG ReturnStructSize, ArrayLength, StringLengths; + PCHAR NextByte; + UNICODE_STRING Us; + + + + ASSERT (SAMP_TEMP_USER1 != 0); + ASSERT (SAMP_TEMP_USER2 != 0); + ASSERT (SAMP_TEMP_MACHINE1 != 0); + ASSERT (SAMP_TEMP_MACHINE2 != 0); + + if (DisplayInformation == DomainDisplayUser) { + + ReturnStructSize = sizeof(DOMAIN_DISPLAY_USER); + Account1 = SAMP_TEMP_USER1; + Account2 = SAMP_TEMP_USER2; + AccountCount = SAMP_TEMP_USER_COUNT; + + } else { + + ReturnStructSize = sizeof(DOMAIN_DISPLAY_MACHINE); + Account1 = SAMP_TEMP_MACHINE1; + Account2 = SAMP_TEMP_MACHINE2; + AccountCount = SAMP_TEMP_MACHINE_COUNT; + + } + + + + // + // Build up a number of dummy accounts in a single buffer. + // + + + if (Index < Account1) { + + // + // Give the first group of accounts + // + + ArrayLength = ReturnStructSize * Account1; + BeginIndex = 0; + EndIndex = Account1; + + + } else { + + // + // Give the second group of accounts + // + + ArrayLength = ReturnStructSize * Account2; + BeginIndex = Account1; + EndIndex = AccountCount; + + } + + + + // + // Figure out how large a buffer is needed. + // + + StringLengths = 0; + for (i=BeginIndex; i<EndIndex; i++) { + + if (DisplayInformation == DomainDisplayUser) { + + RtlInitUnicodeString( &Us, DummyUsers[i].LogonName); + StringLengths += Us.Length; + RtlInitUnicodeString( &Us, DummyUsers[i].FullName); + StringLengths += Us.Length; + RtlInitUnicodeString( &Us, DummyUsers[i].AdminComment); + StringLengths += Us.Length; + + } else { + + RtlInitUnicodeString( &Us, DummyMachines[i].Machine); + StringLengths += Us.Length; + RtlInitUnicodeString( &Us, DummyMachines[i].Comment); + StringLengths += Us.Length; + + } + + } + (*SortedBuffer) = MIDL_user_allocate( ArrayLength + StringLengths ); + ASSERT(SortedBuffer != NULL); + + + // + // First free byte in the return buffer + // + + NextByte = (PCHAR)((ULONG)(*SortedBuffer) + (ULONG)ArrayLength); + + + // + // Now copy the structures + + if (DisplayInformation == DomainDisplayUser) { + + PDOMAIN_DISPLAY_USER r; + r = (PDOMAIN_DISPLAY_USER)(*SortedBuffer); + + j=0; + for (i=BeginIndex; i<EndIndex; i++) { + + r[j].AccountControl = USER_NORMAL_ACCOUNT; + r[j].Index = i; + r[j].Rid = DummyUsers[i].Rid; + + + // + // copy the logon name + // + + RtlInitUnicodeString( &Us, DummyUsers[i].LogonName); + r[j].LogonName.MaximumLength = Us.Length; + r[j].LogonName.Length = Us.Length; + r[j].LogonName.Buffer = (PWSTR)NextByte; + RtlMoveMemory(NextByte, Us.Buffer, r[j].LogonName.Length); + NextByte += r[j].LogonName.Length; + + // + // copy the full name + // + + RtlInitUnicodeString( &Us, DummyUsers[i].FullName); + r[j].FullName.MaximumLength = Us.Length; + r[j].FullName.Length = Us.Length; + r[j].FullName.Buffer = (PWSTR)NextByte; + RtlMoveMemory(NextByte, Us.Buffer, r[j].FullName.Length); + NextByte += r[j].FullName.Length; + + // + // copy the admin comment + // + + RtlInitUnicodeString( &Us, DummyUsers[i].AdminComment); + r[j].AdminComment.MaximumLength = Us.Length; + r[j].AdminComment.Length = Us.Length; + r[j].AdminComment.Buffer = (PWSTR)NextByte; + RtlMoveMemory(NextByte, Us.Buffer, r[j].AdminComment.Length); + NextByte += r[j].AdminComment.Length; + + j++; + + } + + } else { + + PDOMAIN_DISPLAY_MACHINE r; + r = (PDOMAIN_DISPLAY_MACHINE)(*SortedBuffer); + + j=0; + for (i=BeginIndex; i<EndIndex; i++) { + + + r[j].AccountControl = USER_WORKSTATION_TRUST_ACCOUNT; + r[j].Index = i; + r[j].Rid = DummyMachines[i].Rid; + + + // + // copy the logon name + // + + RtlInitUnicodeString( &Us, DummyMachines[i].Machine); + r[j].Machine.MaximumLength = Us.Length; + r[j].Machine.Length = Us.Length; + r[j].Machine.Buffer = (PWSTR)NextByte; + RtlMoveMemory(NextByte, Us.Buffer, r[j].Machine.Length); + NextByte += r[j].Machine.Length; + + + // + // copy the admin comment + // + + RtlInitUnicodeString( &Us, DummyMachines[i].Comment); + r[j].Comment.MaximumLength = Us.Length; + r[j].Comment.Length = Us.Length; + r[j].Comment.Buffer = (PWSTR)NextByte; + RtlMoveMemory(NextByte, Us.Buffer, r[j].Comment.Length); + NextByte += r[j].Comment.Length; + + j++; + + } + + + } + + (*TotalAvailable) = 6*1024; // A lie, but just a little lie. + (*TotalReturned) = ArrayLength + StringLengths; + (*ReturnedEntryCount) = EndIndex - BeginIndex; + + + return; + + +} + + + +NTSTATUS +SamQueryDisplayInformation ( + IN SAM_HANDLE DomainHandle, + IN DOMAIN_DISPLAY_INFORMATION DisplayInformation, + IN ULONG Index, + IN ULONG PreferredMaximumLength, + OUT PULONG TotalAvailable, + OUT PULONG TotalReturned, + OUT PULONG ReturnedEntryCount, + OUT PVOID *SortedBuffer + ) + +/*++ + +Routine Description: + + This routine provides fast return of information commonly + needed to be displayed in user interfaces. + + NT User Interface has a requirement for quick enumeration of SAM + accounts for display in list boxes. (Replication has similar but + broader requirements.) + + The netui listboxes all contain similar information. That is: + + o AccountControl, the bits that identify the account type, + eg, HOME, REMOTE, SERVER, WORKSTATION, etc. + + o Logon name (machine name for computers) + + o Full name (not used for computers) + + o Comment (admin comment for users) + + SAM maintains this data locally in two sorted indexed cached + lists identified by infolevels. + + o DomainDisplayUser: HOME and REMOTE user accounts only + + o DomainDisplayMachine: SERVER and WORKSTATION accounts only + + Note that trust accounts, groups, and aliases are not in either of + these lists. + +Parameters: + + DomainHandle - A handle to an open domain for DOMAIN_LIST_ACCOUNTS. + + DisplayInformation - Indicates which information is to be enumerated. + + Index - The index of the first entry to be retrieved. + + PreferedMaximumLength - A recommended upper limit to the number of + bytes to be returned. The returned information is allocated by + this routine. + + TotalAvailable - Total number of bytes availabe in the specified info + class. + + TotalReturned - Number of bytes actually returned for this call. Zero + indicates there are no entries with an index as large as that + specified. + + ReturnedEntryCount - Number of entries returned by this call. Zero + indicates there are no entries with an index as large as that + specified. + + + SortedBuffer - Receives a pointer to a buffer containing a sorted + list of the requested information. This buffer is allocated + by this routine and contains the following structure: + + + DomainDisplayMachine --> An array of ReturnedEntryCount elements + of type DOMAIN_DISPLAY_USER. This is + followed by the bodies of the various + strings pointed to from within the + DOMAIN_DISPLAY_USER structures. + + DomainDisplayMachine --> An array of ReturnedEntryCount elements + of type DOMAIN_DISPLAY_MACHINE. This is + followed by the bodies of the various + strings pointed to from within the + DOMAIN_DISPLAY_MACHINE structures. + +Return Values: + + STATUS_SUCCESS - normal, successful completion. + + STATUS_ACCESS_DENIED - The specified handle was not opened for + the necessary access. + + STATUS_INVALID_HANDLE - The specified handle is not that of an + opened Domain object. + + STATUS_INVALID_INFO_CLASS - The requested class of information + is not legitimate for this service. + + + + + +--*/ +{ + + + +// if ((DisplayInformation != DomainDisplayUser) && +// (DisplayInformation != DomainDisplayMachine) ) { +// return( STATUS_INVALID_INFO_CLASS ); +// +// } + + + + SampBuildDummyAccounts( DisplayInformation, + Index, + TotalAvailable, + TotalReturned, + ReturnedEntryCount, + SortedBuffer); + + return(STATUS_SUCCESS); + + DBG_UNREFERENCED_PARAMETER(DomainHandle); + DBG_UNREFERENCED_PARAMETER(PreferredMaximumLength); + +} + + +NTSTATUS +SamGetDisplayEnumerationIndex ( + IN SAM_HANDLE DomainHandle, + IN DOMAIN_DISPLAY_INFORMATION DisplayInformation, + IN PUNICODE_STRING Prefix, + OUT PULONG Index + ) + +/*++ + +Routine Description: + + This routine returns the index of the entry which alphabetically + immediatly preceeds a specified prefix. If no such entry exists, + then zero is returned as the index. + +Parameters: + + DomainHandle - A handle to an open domain for DOMAIN_LIST_ACCOUNTS. + + DisplayInformation - Indicates which sorted information class is + to be searched. + + Prefix - The prefix to compare. + + Index - Receives the index of the entry of the information class + with a LogonName (or MachineName) which immediatly preceeds the + provided prefix string. If there are no elements which preceed + the prefix, then zero is returned. + + +Return Values: + + STATUS_SUCCESS - normal, successful completion. + + STATUS_ACCESS_DENIED - The specified handle was not opened for + the necessary access. + + STATUS_INVALID_HANDLE - The specified handle is not that of an + opened Domain object. + + +--*/ +{ + + (*Index) = 0; + + return(STATUS_SUCCESS); + + + DBG_UNREFERENCED_PARAMETER(DomainHandle); + DBG_UNREFERENCED_PARAMETER(DisplayInformation); + DBG_UNREFERENCED_PARAMETER(Prefix); + +} + + diff --git a/private/newsam2/client/tmachine.c b/private/newsam2/client/tmachine.c new file mode 100644 index 000000000..32b91a1d2 --- /dev/null +++ b/private/newsam2/client/tmachine.c @@ -0,0 +1,902 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + tmachine.c + +Abstract: + + This module tests the machine account creation facilities + of SAM. + +Author: + + Jim Kelly (JimK) 7-Feb-1994 + +Environment: + + User Mode - Win32 + +Revision History: + + +--*/ + + + + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include <stdio.h> +#include <nt.h> +#include <ntsam.h> +#include <ntsamp.h> +#include <ntlsa.h> +#include <ntrpcp.h> // prototypes for MIDL user functions +#include <seopaque.h> +#include <string.h> + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Macros // +// // +/////////////////////////////////////////////////////////////////////////////// + + +#ifndef SHIFT +#define SHIFT(c,v) {c--; v++;} +#endif //SHIFT + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Routines // +// // +/////////////////////////////////////////////////////////////////////////////// + + +NTSTATUS +TSampGetLsaDomainInfo( + IN PUNICODE_STRING ServerName, + OUT PPOLICY_ACCOUNT_DOMAIN_INFO *PolicyAccountDomainInfo + ) + +/*++ + +Routine Description: + + This routine retrieves ACCOUNT domain information from the LSA + policy database. + + +Arguments: + + ServerName - name of machine to get account domain information + from. + + PolicyAccountDomainInfo - Receives a pointer to a + POLICY_ACCOUNT_DOMAIN_INFO structure containing the account + domain info. + + +Return Value: + + STATUS_SUCCESS - Succeeded. + + Other status values that may be returned from: + + LsaOpenPolicy() + LsaQueryInformationPolicy() +--*/ + +{ + NTSTATUS + NtStatus, + IgnoreStatus; + + LSA_HANDLE + PolicyHandle; + + OBJECT_ATTRIBUTES + PolicyObjectAttributes; + + // + // Open the policy database + // + + InitializeObjectAttributes( &PolicyObjectAttributes, + NULL, // Name + 0, // Attributes + NULL, // Root + NULL ); // Security Descriptor + + NtStatus = LsaOpenPolicy( ServerName, + &PolicyObjectAttributes, + POLICY_VIEW_LOCAL_INFORMATION, + &PolicyHandle ); + + if ( NT_SUCCESS(NtStatus) ) { + + // + // Query the account domain information + // + + NtStatus = LsaQueryInformationPolicy( PolicyHandle, + PolicyAccountDomainInformation, + (PVOID *)PolicyAccountDomainInfo ); + + + IgnoreStatus = LsaClose( PolicyHandle ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + return(NtStatus); +} + + +NTSTATUS +TSampConnectToServer( + IN PUNICODE_STRING ServerName, + IN ACCESS_MASK DomainAccess, + OUT PHANDLE ServerHandle, + OUT PHANDLE DomainHandle, + OUT PSID *DomainSid + ) + +/*++ + +Routine Description: + + Open a handle to the SAM server on the specified server + and then open the account domain on that same server. + +Arguments: + + ServerName - Name of server to connect to. + + DomainAccess - accesses needed to the account domain. + + ServerHandle - Receives a handle to the SAM server on the specified + system. + + DomainHandle - Receives a handle to the account domain. + + DomainSid - Receives a pointer to the SID of the account domain. + + +Return Value: + + + + +--*/ +{ + NTSTATUS + NtStatus; + + OBJECT_ATTRIBUTES + ObjectAttributes; + + PPOLICY_ACCOUNT_DOMAIN_INFO + AccountDomainInfo; + + // + // get account domain info + // + + NtStatus = TSampGetLsaDomainInfo( ServerName, + &AccountDomainInfo); + + if (!NT_SUCCESS(NtStatus)) { + printf("SAM TEST: Failed to get lsa domain info...\n" + " Completion status is 0x%lx\n", NtStatus); + return(NtStatus); + } + printf("SAM TEST: Target domain is %wZ\n", &AccountDomainInfo->DomainName); + + (*DomainSid) = AccountDomainInfo->DomainSid; + + InitializeObjectAttributes( &ObjectAttributes, NULL, 0, 0, NULL ); + + + NtStatus = SamConnect( + ServerName, + ServerHandle, + SAM_SERVER_READ | SAM_SERVER_EXECUTE, + &ObjectAttributes + ); + + + if (!NT_SUCCESS(NtStatus)) { + printf("SAM TEST: Failed to connect...\n" + " Completion status is 0x%lx\n", NtStatus); + return(NtStatus); + } + + + NtStatus = SamOpenDomain( + (*ServerHandle), + DomainAccess, + *DomainSid, + DomainHandle + ); + + if (!NT_SUCCESS(NtStatus)) { + printf("Failed account domain open\n" + " Completion status is 0x%lx\n", NtStatus); + return(NtStatus); + } + + return(STATUS_SUCCESS); + + +} + + +BOOLEAN +TSampEnableMachinePrivilege( VOID ) + +/*++ + +Routine Description: + + This function enabled the SeMachineAccountPrivilege privilege. + +Arguments: + + None. + +Return Value: + + TRUE if privilege successfully enabled. + FALSE if not successfully enabled. + +--*/ +{ + + NTSTATUS Status; + HANDLE Token; + LUID SecurityPrivilege; + PTOKEN_PRIVILEGES NewState; + ULONG ReturnLength; + + + // + // Open our own token + // + + Status = NtOpenProcessToken( + NtCurrentProcess(), + TOKEN_ADJUST_PRIVILEGES, + &Token + ); + if (!NT_SUCCESS(Status)) { + printf("SAM TEST: Can't open process token to enable Privilege.\n" + " Completion status of NtOpenProcessToken() is: 0x%lx\n", Status); + return(FALSE); + } + + + // + // Initialize the adjustment structure + // + + SecurityPrivilege = + RtlConvertLongToLargeInteger(SE_MACHINE_ACCOUNT_PRIVILEGE); + + ASSERT( (sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)) < 100); + NewState = RtlAllocateHeap( RtlProcessHeap(), 0, 100 ); + + NewState->PrivilegeCount = 1; + NewState->Privileges[0].Luid = SecurityPrivilege; + NewState->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + + + // + // Set the state of the privilege to ENABLED. + // + + Status = NtAdjustPrivilegesToken( + Token, // TokenHandle + FALSE, // DisableAllPrivileges + NewState, // NewState + 0, // BufferLength + NULL, // PreviousState (OPTIONAL) + &ReturnLength // ReturnLength + ); + // don't use NT_SUCCESS here because STATUS_NOT_ALL_ASSIGNED is a success status + if (Status != STATUS_SUCCESS) { + return(FALSE); + } + + + // + // Clean up some stuff before returning + // + + RtlFreeHeap( RtlProcessHeap(), 0, NewState ); + Status = NtClose( Token ); + ASSERT(NT_SUCCESS(Status)); + + + return TRUE; + +} + + +NTSTATUS +TSampCreateMachine( + IN SAM_HANDLE DomainHandle, + IN PUNICODE_STRING AccountName + ) + +/*++ + +Routine Description: + + This routine attempts to create a machine account. + + One of two cases may be tested: + + 1) DomainHandle is open for DOMAIN_CREATE_USER, + or + 2) DomainHandle is open for DOMAIN_LOOKUP and + the SeMachineAccountPrivilege privilege is + enabled. + + It is the caller's responsibility to establish the + correct case criteria before calling. + +Arguments: + + DomainHandle - handle to domain to create account in. + + AccountName - Name of the account to create. + + +Return Value: + +--*/ + +{ + NTSTATUS + NtStatus, + IgnoreStatus; + + SAM_HANDLE + UserHandle; + + ACCESS_MASK + GrantedAccess; + + ULONG + Rid; + + NtStatus = SamCreateUser2InDomain( DomainHandle, + AccountName, + USER_WORKSTATION_TRUST_ACCOUNT, + MAXIMUM_ALLOWED, + &UserHandle, + &GrantedAccess, + &Rid); + + if (NT_SUCCESS(NtStatus)) { + IgnoreStatus = SamCloseHandle( UserHandle ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + printf("SAM TEST: Machine account created.\n" + " GrantedAccess: 0x%lx\n" + " Rid: %d (0x%lx)\n", + GrantedAccess, Rid, Rid); + } else { + printf("SAM TEST: Machine account creation failed.\n" + " Status: 0x%lx\n", NtStatus); + } + + + return(NtStatus); + + +} + + +NTSTATUS +TSampSetPasswordMachine( + IN SAM_HANDLE DomainHandle, + IN PUNICODE_STRING AccountName, + IN PUNICODE_STRING Password + ) + +/*++ + +Routine Description: + + This routine attempts to set the password of a machine account. + + +Arguments: + + DomainHandle - handle to domain account is in. + + AccountName - Name of the account to set password. + + Password - New password. + +Return Value: + +--*/ + +{ + NTSTATUS + NtStatus; + + SAM_HANDLE + UserHandle; + + PULONG + RelativeIds; + + PSID_NAME_USE + Use; + + USER_SET_PASSWORD_INFORMATION + PasswordInfo; + + + PasswordInfo.Password = (*Password); + PasswordInfo.PasswordExpired = FALSE; + + NtStatus = SamLookupNamesInDomain( DomainHandle, + 1, + AccountName, + &RelativeIds, + &Use); + if (!NT_SUCCESS(NtStatus)) { + printf("SAM TEST: Couldn't find account to set password.\n" + " Lookup status: 0x%lx\n", NtStatus); + return(NtStatus); + } + + + NtStatus = SamOpenUser( DomainHandle, + USER_FORCE_PASSWORD_CHANGE, + RelativeIds[0], + &UserHandle); + + if (!NT_SUCCESS(NtStatus)) { + printf("SAM TEST: Couldn't open user account for FORCE_PASSWORD_CHANGE.\n" + " Lookup status: 0x%lx\n", NtStatus); + return(NtStatus); + } + + NtStatus = SamSetInformationUser( UserHandle, + UserSetPasswordInformation, + &PasswordInfo + ); + if (!NT_SUCCESS(NtStatus)) { + printf("SAM TEST: Couldn't set password on user account.\n" + " Set Info status: 0x%lx\n", NtStatus); + return(NtStatus); + } + + + return(STATUS_SUCCESS); + + +} + + +NTSTATUS +TSampDeleteMachine( + IN SAM_HANDLE DomainHandle, + IN PUNICODE_STRING AccountName + ) + +/*++ + +Routine Description: + + This routine attempts to delete a machine account. + + +Arguments: + + DomainHandle - handle to domain to delete account from. + + AccountName - Name of the account to delete. + + +Return Value: + +--*/ + +{ + NTSTATUS + NtStatus; + + SAM_HANDLE + UserHandle; + + PULONG + RelativeIds; + + PSID_NAME_USE + Use; + + NtStatus = SamLookupNamesInDomain( DomainHandle, + 1, + AccountName, + &RelativeIds, + &Use); + if (!NT_SUCCESS(NtStatus)) { + printf("SAM TEST: Couldn't find account to delete.\n" + " Lookup status: 0x%lx\n", NtStatus); + return(NtStatus); + } + + + NtStatus = SamOpenUser( DomainHandle, + DELETE, + RelativeIds[0], + &UserHandle); + + if (!NT_SUCCESS(NtStatus)) { + printf("SAM TEST: Couldn't open user account for delete.\n" + " Open status: 0x%lx\n", NtStatus); + return(NtStatus); + } + + NtStatus = SamDeleteUser( UserHandle ); + if (!NT_SUCCESS(NtStatus)) { + printf("SAM TEST: Couldn't delete user account.\n" + " DeleteUser status: 0x%lx\n", NtStatus); + return(NtStatus); + } + + + return(STATUS_SUCCESS); + + +} + + +VOID +TSampPrintYesOrNo( + IN BOOLEAN b + ) +{ + if (b) { + printf("Yes\n"); + } else { + printf("No\n"); + } +} + + + +VOID +TSampUsage( VOID ) +{ + + printf("\n\n Command format:\n"); + printf(" tmachine [/c] [/p] [/d] <account-name> <machine> [<password>]\n"); + printf("\n"); + printf(" Switches\n"); + printf(" /c - create account\n"); + printf(" /p - set password on account\n"); + printf(" /d - delete account\n"); + printf("\n"); + printf(" if multiple switches are specified, they are attempted in\n"); + printf(" the order listed above. An error in any attempt will prevent\n"); + printf(" any further attempts.\n"); + printf("\n"); + return; +} + + +VOID +main (c,v) +int c; +char **v; + +/*++ + +Routine Description: + + This is the main entry routine for this test. + +Arguments: + + Argv[1] - account name to create or delete + + Argv[2] - domain controller machine name + + Argv[3] - 'D' to delete account, otherwise account is created. + + + +Return Value: + + + + +--*/ +{ + NTSTATUS + NtStatus, + IgnoreStatus; + + UNICODE_STRING + AccountName, + ControllerName, + Password; + + WCHAR + AccountNameBuffer[80], + ControllerNameBuffer[80], + PasswordBuffer[80]; + + ANSI_STRING + AnsiString; + + SAM_HANDLE + ServerHandle, + ServerHandle2, + DomainHandle, + DomainHandle2; + + PSID + DomainSid; + + BOOLEAN + Create = FALSE, + SetPassword = FALSE, + Delete = FALSE; + + ULONG + ArgNum = 0; + + PCHAR + p; + + CHAR + ch; + + AccountName.Length = 0; + ControllerName.Length = 0; + Password.Length = 0; + + + + // + // Command format: + // + // tmachine [/c] [/p] [/d] <account-name> <machine> [<password>] + // + // Switches + // /c - create account + // /p - set password on account + // /d - delete account + // + // if multiple switches are specified, they are attempted in + // the order listed above. An error in any attempt will prevent + // any further attempts. + // + + SHIFT (c,v); + while ((c > 0) && ((ch = *v[0]))) { + p = *v; + if (ch == '/') { + while (*++p != '\0') { + if ((*p == 'c') || (*p == 'C')) { + Create = TRUE; +// printf("Create\n"); + } else if ((*p == 'p') || (*p == 'P')) { + SetPassword = TRUE; +// printf("SetPassword\n"); + } else if ((*p == 'd') || (*p == 'D')) { + Delete = TRUE; +// printf("Delete\n"); + } else { + TSampUsage(); + return; + } + } + } else { + + switch (ArgNum) { + case 0: + + // + // collecting account name + // + + AccountName.Buffer = AccountNameBuffer; + AccountName.MaximumLength = sizeof(AccountNameBuffer); + RtlInitAnsiString(&AnsiString, (*v)); + RtlAnsiStringToUnicodeString(&AccountName, &AnsiString, FALSE); + +// printf("account: %wZ\n", &AccountName); + break; + + case 1: + + // + // collecting machine name + // + + ControllerName.Buffer = ControllerNameBuffer; + ControllerName.MaximumLength = sizeof(ControllerNameBuffer); + RtlInitAnsiString(&AnsiString, (*v)); + RtlAnsiStringToUnicodeString(&ControllerName, &AnsiString, FALSE); + +// printf("machine: %wZ\n", &ControllerName); + break; + + + case 2: + + // + // collecting password name + // + + Password.Buffer = PasswordBuffer; + Password.MaximumLength = sizeof(PasswordBuffer); + RtlInitAnsiString(&AnsiString, (*v)); + RtlAnsiStringToUnicodeString(&Password, &AnsiString, FALSE); + +// printf("password: %wZ\n", &Password); + break; + + default: + + // + // collecting garbage. + // + + break; + } + + ArgNum++; + } + SHIFT(c,v); + } + + + + printf("parameters:\n"); + printf(" Create Account: "); TSampPrintYesOrNo( Create ); + printf(" Set Password : "); TSampPrintYesOrNo( SetPassword ); + printf(" Delete Account: "); TSampPrintYesOrNo( Delete ); + printf(" Account : *%wZ*\n", &AccountName); + printf(" Machine : *%wZ*\n", &ControllerName); + printf(" Password : *%wZ*\n", &Password); + + + // + // Make sure we don't have conflicting parameters + // + // Rules: + // + // 1) account name is always required. + // 2) password and machine are required if /P was specified. + // 3) machine is optional if /P not specified. + // + // + + if ( (AccountName.Length == 0) || + ( SetPassword && (ControllerName.Length == 0) ) || + ( SetPassword && (Password.Length == 0) ) ) { + TSampUsage(); + return; + } + + + // + // Open the server and the account domain + // + + NtStatus = TSampConnectToServer(&ControllerName, + DOMAIN_LOOKUP | DOMAIN_READ_PASSWORD_PARAMETERS, + &ServerHandle, + &DomainHandle, + &DomainSid); + + + if (Create) { + // + // try to create the machine account with privilege. + // + + + printf("SAM TEST: Creating machine account with privilege.\n"); + TSampEnableMachinePrivilege(); + NtStatus = TSampCreateMachine( DomainHandle, &AccountName ); + if (NT_SUCCESS(NtStatus)) { + printf(" Status: successful\n"); + } else { + + if (NtStatus == STATUS_ACCESS_DENIED) { + // + // We didn't have the privilege, and didn't have + // the domain open so that it would work without + // the privilege. + // + + printf(" Couldn't create account with privilege (0x%lx)\n" + " Attempting normal creation (without privilege)\n" + , NtStatus); + NtStatus = TSampConnectToServer(&ControllerName, + DOMAIN_LOOKUP | + DOMAIN_READ_PASSWORD_PARAMETERS | + DOMAIN_CREATE_USER, + &ServerHandle2, + &DomainHandle2, + &DomainSid); + if (!NT_SUCCESS(NtStatus)) { + printf(" Can't open domain for CREATE_USER access (0x%lx)\n", NtStatus); + } else { + NtStatus = TSampCreateMachine( DomainHandle2, + &AccountName ); + if (NT_SUCCESS(NtStatus)) { + printf(" Status: successful\n"); + } else { + printf(" Failed: 0x%lx\n", NtStatus); + } + + IgnoreStatus = SamCloseHandle( DomainHandle2 ); + + } + } + if (!NT_SUCCESS(NtStatus)) { + printf(" Status: 0x%lx", NtStatus); + return; + } + } + } + + + if (SetPassword) { + + // + // Try to set the password on the account + // + + printf("SAM TEST: Setting password of account ...\n"); + NtStatus = TSampSetPasswordMachine( DomainHandle, &AccountName, &Password ); + if (NT_SUCCESS(NtStatus)) { + printf(" Status: successful\n"); + } else { + printf(" Status: 0x%lx", NtStatus); + return; + } + } + + + if (Delete) { + + printf("SAM TEST: Deleting account ...\n"); + NtStatus = TSampDeleteMachine( DomainHandle, &AccountName ); + if (NT_SUCCESS(NtStatus)) { + printf(" Status: successful\n"); + } else { + printf(" Status: 0x%lx", NtStatus); + return; + } + } + + + return; +} + diff --git a/private/newsam2/client/tmultipl.c b/private/newsam2/client/tmultipl.c new file mode 100644 index 000000000..25f93d97c --- /dev/null +++ b/private/newsam2/client/tmultipl.c @@ -0,0 +1,574 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + tmultipl.c + +Abstract: + + This module tests the addition and removal of multiple + alias members. + +Author: + + Jim Kelly (JimK) 11-Oct-1994 + +Environment: + + User Mode - Win32 + +Revision History: + + +--*/ + + + + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include <stdio.h> +#include <nt.h> +#include <ntsam.h> +#include <ntsamp.h> +#include <ntlsa.h> +#include <ntrpcp.h> // prototypes for MIDL user functions +#include <seopaque.h> +#include <string.h> + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Macros and defines // +// // +/////////////////////////////////////////////////////////////////////////////// + +#define TSAMP_MEMBER_COUNT 35 + +#ifndef SHIFT +#define SHIFT(c,v) {c--; v++;} +#endif //SHIFT + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Routines // +// // +/////////////////////////////////////////////////////////////////////////////// + + +VOID +TSampUsage( VOID ) +{ + + printf("\n\n Test multiple member operations on alias\n"); + printf("\n\n Command format:\n"); + printf(" tmultipl [/a] [/r]\n"); + printf("\n"); + printf(" Switches\n"); + printf(" /a - causes members to be added to an alias\n"); + printf(" /r - causes members to be removed from alias\n"); + printf("\n"); + printf(" If multiple switches are specified, first adding will be attempted\n"); + printf(" and then removal.\n"); + printf(" Defaults to Account Operator alias.\n"); + printf("\n"); + return; +} + + +VOID +TSampParseCommandLine( + IN int c, + IN char **v, + OUT PBOOLEAN Add, + OUT PBOOLEAN Remove + ) + +{ + PCHAR + p; + + CHAR + ch; + + + // + // Command format: + // + // tmultipl [/a] [/r] + // + // Switches + // /a - causes members to be added to an alias\n"); + // /r - causes members to be removed from alias\n"); + // + // if multiple switches are specified, first adding will be + // attempted and then removal. + // + + (*Add) = FALSE; + (*Remove) = FALSE; + + SHIFT (c,v); + while ((c > 0) && ((ch = *v[0]))) { + p = *v; + if (ch == '/') { + while (*++p != '\0') { + if ((*p == 'a') || (*p == 'A')) { + (*Add) = TRUE; + printf("Add\n"); + } else if ((*p == 'r') || (*p == 'R')) { + (*Remove) = TRUE; + printf("Remove\n"); + } else { + TSampUsage(); + return; + } + } + } + SHIFT(c,v); + } +} + +NTSTATUS +TSampGetLsaDomainInfo( + IN PUNICODE_STRING ServerName, + OUT PPOLICY_ACCOUNT_DOMAIN_INFO *PolicyAccountDomainInfo + ) + +/*++ + +Routine Description: + + This routine retrieves ACCOUNT domain information from the LSA + policy database. + + +Arguments: + + ServerName - name of machine to get account domain information + from. + + PolicyAccountDomainInfo - Receives a pointer to a + POLICY_ACCOUNT_DOMAIN_INFO structure containing the account + domain info. + + +Return Value: + + STATUS_SUCCESS - Succeeded. + + Other status values that may be returned from: + + LsaOpenPolicy() + LsaQueryInformationPolicy() +--*/ + +{ + NTSTATUS + NtStatus, + IgnoreStatus; + + LSA_HANDLE + PolicyHandle; + + OBJECT_ATTRIBUTES + PolicyObjectAttributes; + + // + // Open the policy database + // + + InitializeObjectAttributes( &PolicyObjectAttributes, + NULL, // Name + 0, // Attributes + NULL, // Root + NULL ); // Security Descriptor + + NtStatus = LsaOpenPolicy( ServerName, + &PolicyObjectAttributes, + POLICY_VIEW_LOCAL_INFORMATION, + &PolicyHandle ); + + if ( NT_SUCCESS(NtStatus) ) { + + // + // Query the account domain information + // + + NtStatus = LsaQueryInformationPolicy( PolicyHandle, + PolicyAccountDomainInformation, + (PVOID *)PolicyAccountDomainInfo ); + + + IgnoreStatus = LsaClose( PolicyHandle ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + return(NtStatus); +} + + +NTSTATUS +TSampConnectToServer( + IN PUNICODE_STRING ServerName, + IN ACCESS_MASK DomainAccess, + OUT PHANDLE ServerHandle, + OUT PHANDLE AccountDomain OPTIONAL, + OUT PSID *AccountDomainSid OPTIONAL, + OUT PHANDLE BuiltinDomain OPTIONAL, + OUT PSID *BuiltinDomainSid OPTIONAL + ) + +/*++ + +Routine Description: + + Open a handle to the SAM server on the specified server + and then open the account and builtin domains on that same + server. + +Arguments: + + ServerName - Name of server to connect to. + + DomainAccess - accesses needed to the account domain. + + ServerHandle - Receives a handle to the SAM server on the specified + system. + + AccountDomain - Receives a handle to the account domain. + + AccountDomainSid - Receives a pointer to the SID of the account domain. + Must be present if AccountDomain is present. + + BuiltinDomain - Receives a handle to the Builtin domain. + + BuiltinDomainSid - Receives a pointer to the SID of the Builtin domain. + Must be present if BuiltinDomain is present. + + +Return Value: + + + + +--*/ +{ + NTSTATUS + NtStatus; + + OBJECT_ATTRIBUTES + ObjectAttributes; + + PPOLICY_ACCOUNT_DOMAIN_INFO + AccountDomainInfo; + + SID_IDENTIFIER_AUTHORITY + BuiltinAuthority = SECURITY_NT_AUTHORITY; + + // + // Connect to the server + // + + InitializeObjectAttributes( &ObjectAttributes, NULL, 0, 0, NULL ); + + + NtStatus = SamConnect( + ServerName, + ServerHandle, + SAM_SERVER_READ | SAM_SERVER_EXECUTE, + &ObjectAttributes + ); + + + if (!NT_SUCCESS(NtStatus)) { + printf("SAM TEST: Failed to connect...\n" + " Completion status is 0x%lx\n", NtStatus); + return(NtStatus); + } + + + // + // Get account domain handle and sid + // + + if (ARGUMENT_PRESENT(AccountDomain)) { + // + // get account domain info + // + + NtStatus = TSampGetLsaDomainInfo( ServerName, + &AccountDomainInfo); + + if (!NT_SUCCESS(NtStatus)) { + printf("SAM TEST: Failed to get lsa domain info...\n" + " Completion status is 0x%lx\n", NtStatus); + return(NtStatus); + } + + (*AccountDomainSid) = AccountDomainInfo->DomainSid; + + + NtStatus = SamOpenDomain( + (*ServerHandle), + DomainAccess, + *AccountDomainSid, + AccountDomain + ); + + if (!NT_SUCCESS(NtStatus)) { + printf("Failed account domain open\n" + " Completion status is 0x%lx\n", NtStatus); + return(NtStatus); + } + } //end_if + + + // + // Get builtin domain handle and sid + // + + if (ARGUMENT_PRESENT(BuiltinDomain)) { + + NtStatus = RtlAllocateAndInitializeSid( + &BuiltinAuthority, + 1, //SubAuthorities + SECURITY_BUILTIN_DOMAIN_RID, + 0, 0, 0, 0, 0, 0, 0, + BuiltinDomainSid + ); + + if (!NT_SUCCESS(NtStatus)) { + printf("SAM TEST: Failed to allocate and init builtin domain sid...\n" + " status is 0x%lx\n", NtStatus); + return(NtStatus); + } + + NtStatus = SamOpenDomain( + (*ServerHandle), + DomainAccess, + *BuiltinDomainSid, + BuiltinDomain + ); + + if (!NT_SUCCESS(NtStatus)) { + printf("Failed builtin domain open\n" + " Completion status is 0x%lx\n", NtStatus); + return(NtStatus); + } + } //end_if + + return(STATUS_SUCCESS); +} + + + + +VOID +TSampInitializeSids( + OUT PSID *MemberSids, + IN ULONG MemberCount + ) +{ + + // + // Return and array of sids. + // + + NTSTATUS + NtStatus; + + ULONG + i; + + SID_IDENTIFIER_AUTHORITY + BuiltinAuthority = SECURITY_NT_AUTHORITY, + UnusedSidAuthority = {0, 0, 0, 0, 0, 6}; //Authority that isn't used + + // + // Fill MemberSids with MemberCount SIDs + // + + for (i=0; i<MemberCount; i++) { + + NtStatus = RtlAllocateAndInitializeSid( + &UnusedSidAuthority, + 3, //SubAuthorityCount + 72549230, + i, + i*17, + 0, 0, 0, 0, 0, + &MemberSids[i] + ); + if (!NT_SUCCESS(NtStatus)) { + printf("Tsamp: Couldn't allocate or initialize sid %d, status: 0x%lx\n", NtStatus); + return; + } + } // end_for + + + return; + +} + + +NTSTATUS +TSampTestAddMembers( + IN SAM_HANDLE AliasHandle, + IN PSID *MemberSids, + IN ULONG MemberCount + ) +{ + NTSTATUS + NtStatus; + + NtStatus = SamAddMultipleMembersToAlias( + AliasHandle, + MemberSids, + MemberCount + ); + printf("TSamp: Added %d members to alias. Status: 0x%lx\n", + MemberCount, NtStatus); + return(NtStatus); +} + + +NTSTATUS +TSampTestRemoveMembers( + IN SAM_HANDLE AliasHandle, + IN PSID *MemberSids, + IN ULONG MemberCount + ) +{ + NTSTATUS + NtStatus; + + NtStatus = SamRemoveMultipleMembersFromAlias( + AliasHandle, + MemberSids, + MemberCount + ); + printf("TSamp: Removed %d members from alias. Status: 0x%lx\n", + MemberCount, NtStatus); + return(NtStatus); +} + + +//VOID +__cdecl +main(c,v) +int c; +char **v; + +/*++ + +Routine Description: + + This is the main entry routine for this test. + +Arguments: + + + + +Return Value: + + + + +--*/ +{ + NTSTATUS + NtStatus; + + BOOLEAN + Add, + Remove; + + UNICODE_STRING + ControllerName; + + WCHAR + ControllerNameBuffer[80]; + + SAM_HANDLE + ServerHandle, + AccountDomainHandle, + BuiltinHandle, + AliasHandle; + + ULONG + MemberCount = TSAMP_MEMBER_COUNT; + + PSID + MemberSids[TSAMP_MEMBER_COUNT], + AccountDomainSid, + BuiltinSid; + + + ControllerName.Length = 0; + ControllerName.Buffer = ControllerNameBuffer; + ControllerName.MaximumLength = sizeof(ControllerNameBuffer); + + + TSampParseCommandLine( c, v, &Add, &Remove ); + + if (!Add && !Remove) { + TSampUsage(); + return; + } + + // + // Open the server and its domains + // + + NtStatus = TSampConnectToServer(&ControllerName, + DOMAIN_LOOKUP | DOMAIN_READ_PASSWORD_PARAMETERS, + &ServerHandle, + &AccountDomainHandle, + &AccountDomainSid, + &BuiltinHandle, + &BuiltinSid); + ASSERT(NT_SUCCESS(NtStatus)); + + // + // Initialize a bunch of SIDs to add to the alias. + // + + TSampInitializeSids( MemberSids, MemberCount ); + + // + // Open the alias we are going to play with + // + + NtStatus = SamOpenAlias( BuiltinHandle, + (ALIAS_ADD_MEMBER | ALIAS_REMOVE_MEMBER), + DOMAIN_ALIAS_RID_ACCOUNT_OPS, + &AliasHandle); + + if (Add) { + NtStatus = TSampTestAddMembers( AliasHandle, MemberSids, MemberCount ); + } + + if (Remove) { + NtStatus = TSampTestRemoveMembers( AliasHandle, MemberSids, MemberCount ); + } + + + + + return(0); +} + diff --git a/private/newsam2/client/toempass.c b/private/newsam2/client/toempass.c new file mode 100644 index 000000000..a3461b348 --- /dev/null +++ b/private/newsam2/client/toempass.c @@ -0,0 +1,231 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + toempass.c + +Abstract: + + This file contains test code for the oem password change routine. + +Author: + + Mike Swift (MikeSw) 4-January-1995 + +Environment: + + User Mode - Win32 + +Revision History: + + +--*/ + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "samclip.h" + + +NTSTATUS +SampEncryptLmPasswords( + IN LPSTR OldPassword, + IN LPSTR NewPassword, + OUT PSAMPR_ENCRYPTED_USER_PASSWORD NewEncryptedWithOldLm, + OUT PENCRYPTED_NT_OWF_PASSWORD OldLmOwfEncryptedWithNewLm +) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + +--*/ +{ + LM_OWF_PASSWORD OldLmOwfPassword; + LM_OWF_PASSWORD NewLmOwfPassword; + PSAMPR_USER_PASSWORD NewLm = (PSAMPR_USER_PASSWORD) NewEncryptedWithOldLm; + struct RC4_KEYSTRUCT Rc4Key; + NTSTATUS NtStatus; + CHAR LocalNewPassword[SAM_MAX_PASSWORD_LENGTH]; + CHAR LocalOldPassword[SAM_MAX_PASSWORD_LENGTH]; + + if ((lstrlenA(OldPassword) > SAM_MAX_PASSWORD_LENGTH - 1) || + (lstrlenA(NewPassword) > SAM_MAX_PASSWORD_LENGTH - 1) ) + { + return(STATUS_PASSWORD_RESTRICTION); + } + + // + // Upcase the passwords + // + lstrcpyA(LocalOldPassword,OldPassword); + lstrcpyA(LocalNewPassword,NewPassword); + + strupr(LocalOldPassword); + strupr(LocalNewPassword); + + + + // + // Calculate the LM OWF passwords + // + + + NtStatus = RtlCalculateLmOwfPassword( + LocalOldPassword, + &OldLmOwfPassword + ); + + + if (NT_SUCCESS(NtStatus)) { + NtStatus = RtlCalculateLmOwfPassword( + LocalNewPassword, + &NewLmOwfPassword + ); + } + + + + // + // Calculate the encrypted old passwords + // + + if (NT_SUCCESS(NtStatus)) { + NtStatus = RtlEncryptLmOwfPwdWithLmOwfPwd( + &OldLmOwfPassword, + &NewLmOwfPassword, + OldLmOwfEncryptedWithNewLm + ); + } + + + // + // Calculate the encrypted new passwords + // + + if (NT_SUCCESS(NtStatus)) { + + ASSERT(sizeof(SAMPR_ENCRYPTED_USER_PASSWORD) == sizeof(SAMPR_USER_PASSWORD)); + + + // + // Compute the encrypted new password with LM key. + // + + + rc4_key( + &Rc4Key, + LM_OWF_PASSWORD_LENGTH, + (PUCHAR) &OldLmOwfPassword + ); + + RtlCopyMemory( + ((PUCHAR) NewLm->Buffer) + + (SAM_MAX_PASSWORD_LENGTH * sizeof(WCHAR)) - + strlen(NewPassword), + NewPassword, + strlen(NewPassword) + ); + + NewLm->Length = strlen(NewPassword); + rc4(&Rc4Key, + sizeof(SAMPR_USER_PASSWORD), + (PUCHAR) NewEncryptedWithOldLm + ); + + + } + + return(NtStatus); + +} + + + +NTSTATUS +SamOemChangePassword( + LPWSTR ServerName, + LPSTR UserName, + LPSTR OldPassword, + LPSTR NewPassword + ) +{ + handle_t BindingHandle = NULL; + NTSTATUS Status; + SAMPR_ENCRYPTED_USER_PASSWORD NewLmEncryptedWithOldLm; + ENCRYPTED_NT_OWF_PASSWORD OldLmOwfEncryptedWithNewLm; + STRING UserString; + UNICODE_STRING ServerUnicodeString; + STRING ServerString; + + RtlInitUnicodeString( + &ServerUnicodeString, + ServerName + ); + + Status = RtlUnicodeStringToOemString( + &ServerString, + &ServerUnicodeString, + TRUE + ); + if (!NT_SUCCESS(Status)) { + return(Status); + } + + + RtlInitString( + &UserString, + UserName + ); + + Status = SampEncryptLmPasswords( + OldPassword, + NewPassword, + &NewLmEncryptedWithOldLm, + &OldLmOwfEncryptedWithNewLm + ); + + if (!NT_SUCCESS(Status)) { + RtlFreeOemString(&ServerString); + return(Status); + } + BindingHandle = SampSecureBind( + ServerName, + RPC_C_AUTHN_LEVEL_PKT_PRIVACY + ); + if (BindingHandle == NULL) { + RtlFreeOemString(&ServerString); + return(RPC_NT_INVALID_BINDING); + } + + RpcTryExcept{ + + Status = SamrOemChangePasswordUser2( + BindingHandle, + (PRPC_STRING) &ServerString, + (PRPC_STRING) &UserString, + &NewLmEncryptedWithOldLm, + &OldLmOwfEncryptedWithNewLm + ); + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + Status = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + RtlFreeOemString(&ServerString); + return(Status); + + +} diff --git a/private/newsam2/client/tsamobj.c b/private/newsam2/client/tsamobj.c new file mode 100644 index 000000000..4448f16c1 --- /dev/null +++ b/private/newsam2/client/tsamobj.c @@ -0,0 +1,12350 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + tsamobj.c + +Abstract: + + This is the primary SAM object test. + It contains a suite of tests for each type of SAM object. + +Author: + + Jim Kelly (JimK) 12-July-1991 + +Environment: + + User Mode - Win32 + +Revision History: + + +--*/ + + + + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include <stdio.h> +#include <nt.h> +#include <ntsam.h> +#include <ntsamp.h> +#include <ntlsa.h> +#include <ntrpcp.h> // prototypes for MIDL user functions +#include <seopaque.h> +#include <string.h> + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Definitions // +// // +/////////////////////////////////////////////////////////////////////////////// + + + + + +#define TMPP_USER_NAME_ADMIN "Administrator" +#define TMPP_USER_NAME_GUEST "Guest" +#define TMPP_GROUP_NAME_ADMINS "Domain Admins" +#define TMPP_GROUP_NAME_USERS "Domain Users" +#define TMPP_GROUP_NAME_NONE "None" +#define TMPP_ALIAS_NAME_ADMINS "Administrators" +#define TMPP_ALIAS_NAME_SYSTEM_OPS "System Operators" +#define TMPP_ALIAS_NAME_POWER_USERS "Power Users" +#define TMPP_ALIAS_NAME_USERS "Users" +#define TMPP_ALIAS_NAME_GUESTS "Guests" +#define TMPP_ALIAS_NAME_ACCOUNT_OPS "Account Operators" +#define TMPP_ALIAS_NAME_PRINT_OPS "Print Operators" +#define TMPP_ALIAS_NAME_BACKUP_OPS "Backup Operators" + + + +#define GROUP_NAME1 "GROUP1" +#define ALIAS_NAME1 "ALIAS1" +#define ALIAS_NAME2 "ALIAS2" +#define USER_NAME1 "USER1" +#define USER_NAME2 "USER2" +#define USER_NAME3 "USER3" + +// Keep these names not longer than 8 char's until long registry names supported +#define DUMMY_NAME1 "DName1" +#define DUMMY_NAME2 "2emaNuD" + +#define DUMMY_STRING1 "This is test string 1" +#define DUMMY_STRING2 "Test String2 - test string 2 - tEST sTRING 2" + +#define ALL_NAMES_COUNT (3) +#define SOME_NAMES_COUNT (7) +#define NO_NAMES_COUNT (2) + +#define LOOKUP_KNOWN_NAME0 TMPP_USER_NAME_ADMIN +#define LOOKUP_KNOWN_NAME1_A TMPP_GROUP_NAME_NONE +#define LOOKUP_KNOWN_NAME2_A TMPP_GROUP_NAME_NONE +#define LOOKUP_KNOWN_NAME1_P TMPP_GROUP_NAME_USERS +#define LOOKUP_KNOWN_NAME2_P TMPP_GROUP_NAME_USERS + +#define LOOKUP_KNOWN_NAME0_RID DOMAIN_USER_RID_ADMIN +#define LOOKUP_KNOWN_NAME1_RID DOMAIN_GROUP_RID_USERS +#define LOOKUP_KNOWN_NAME2_RID DOMAIN_GROUP_RID_USERS + +#define LOOKUP_UNKNOWN_NAME0 "JoeJoe" +#define LOOKUP_UNKNOWN_NAME1 "Tanya" +#define LOOKUP_UNKNOWN_NAME2 "Fred" +#define LOOKUP_UNKNOWN_NAME3 "Anyone" + +#define LOOKUP_KNOWN_NAME0_USE (SidTypeUser) +#define LOOKUP_KNOWN_NAME1_USE (SidTypeGroup) +#define LOOKUP_KNOWN_NAME2_USE (SidTypeGroup) + + +// +// This byte is expected to be different in the DummyLogonHours and +// NoRestrictionLogonHours. +// + +#define LOGON_HOURS_DIFFERENT_OFFSET (5) + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Global variables // +// // +/////////////////////////////////////////////////////////////////////////////// + +LARGE_INTEGER LargeInteger1, + LargeInteger2; + +UNICODE_STRING DummyName1, + DummyName2, + DummyString1, + DummyString2; + +STRING DummyAnsiString1, + DummyAnsiString2; + +LOGON_HOURS NoLogonRestriction, + DummyLogonHours; + +CHAR NoLogonRestrictionBitMask[21], + DummyLogonHoursBitMask[21]; + + +UNICODE_STRING AllNames[ALL_NAMES_COUNT], + SomeNames[SOME_NAMES_COUNT], + NoNames[NO_NAMES_COUNT]; + + +SID_NAME_USE AllUses[ALL_NAMES_COUNT], + SomeUses[SOME_NAMES_COUNT], + NoUses[NO_NAMES_COUNT]; + +ULONG AllRids[ALL_NAMES_COUNT], + SomeRids[SOME_NAMES_COUNT], + NoRids[NO_NAMES_COUNT]; + + +PSID BuiltinDomainSid, + AccountDomainSid, + PrimaryDomainSid, + WorldSid, + AdminsAliasSid, + AccountAliasSid; + + +UNICODE_STRING BuiltinDomainName, + AccountDomainName, + PrimaryDomainName; + +BOOLEAN AccountDomainIsNotPrimaryDomain; + + +// +// These are NOT mutually exclusive +// + +BOOLEAN BuiltinDomainTest, // Test the builting domain + SecurityOperatorTest, // Test auditing accessibility + AccountOpAliasTest, // Test account operator functions + AdminsAliasTest; // Test domain admin functions + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// private macros // +// // +/////////////////////////////////////////////////////////////////////////////// + + +// +// VOID +// TST_SUCCESS_ASSERT( IN NTSTATUS S ); +// + +#define TST_SUCCESS_ASSERT( S ) \ +{ \ + if ( !NT_SUCCESS((S)) ) { \ + printf("\n** SUCCESS STATUS ASSERTION FAILURE **\n"); \ + printf(" Status is: 0x%lx\n", (S) ); \ + ASSERT(NT_SUCCESS((S))); \ + } \ +} + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// private service prototypes // +// // +/////////////////////////////////////////////////////////////////////////////// + + + + +BOOLEAN +TInitialize( VOID ); + +BOOLEAN +EnableSecurityPrivilege( VOID ); + +VOID +DetermineTestsToRun( VOID ); + +VOID +SeeIfSidIsSpecial( + IN PSID Sid + ); + +BOOLEAN +ServerTestSuite( + PHANDLE ServerHandle, + PHANDLE DomainHandle, + PHANDLE BuiltinDomainHandle, + PSID *DomainSid + ); + +BOOLEAN +SecurityTestSuite( + HANDLE ServerHandle, + HANDLE DomainHandle, + ULONG Pass + ); + +BOOLEAN +CheckReturnedSD( + IN SECURITY_INFORMATION SI, + IN PSECURITY_DESCRIPTOR SD, + IN BOOLEAN PrintTestSuccess + ); + + +BOOLEAN +DomainTestSuite( + HANDLE DomainHandle + ); + +BOOLEAN +GroupTestSuite( + HANDLE DomainHandle, + ULONG Pass + ); + +BOOLEAN +AliasTestSuite( + HANDLE DomainHandle, + HANDLE BuiltinDomainHandle, + PSID DomainSid, + ULONG Pass + ); + +BOOLEAN +UserTestSuite( + HANDLE DomainHandle, + ULONG Pass + ); + + +NTSTATUS +SampSetDomainPolicy( VOID ); + + +NTSTATUS +SampGetLsaDomainInfo( + PPOLICY_ACCOUNT_DOMAIN_INFO *PolicyAccountDomainInfo, + PPOLICY_PRIMARY_DOMAIN_INFO *PolicyPrimaryDomainInfo + ); + + +// +// The following are in WRAPPERS.C, but are prototyped here since this +// test is the only thing that should ever call them. +// + +NTSTATUS +SamTestPrivateFunctionsDomain( + IN HANDLE DomainHandle + ); + +NTSTATUS +SamTestPrivateFunctionsUser( + IN HANDLE UserHandle + ); + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Routines // +// // +/////////////////////////////////////////////////////////////////////////////// + +VOID +_CRTAPI1 main ( + VOID + ) + +/*++ + +Routine Description: + + This is the main entry routine for this test. + +Arguments: + + None. + +Return Value: + + + Note: + + +--*/ +{ + NTSTATUS NtStatus; + SAM_HANDLE ServerHandle, DomainHandle, BuiltinDomainHandle; + PSID DomainSid; + BOOLEAN TestSucceeded = FALSE; + + + printf("\n\n\n\n"); + printf(" Test: TSAMOBJ\n\n"); + printf(" Test Date: \n"); + printf(" Test Time: \n"); + + + + + // + // Initialize and determine which flavor test(s) to run + // + + if (!TInitialize()) { + return; + } + + if (ServerTestSuite( &ServerHandle, &DomainHandle, &BuiltinDomainHandle, &DomainSid )) { + + // + // Do security manipulation tests on domain object + // + + if (SecurityTestSuite( ServerHandle, DomainHandle, 1)) { + + if (AdminsAliasTest) { + + // + // Do individual tests for domain, group, and user objects. + // + + if (DomainTestSuite( DomainHandle )) { + + if (AdminsAliasTest) { + + if (GroupTestSuite( DomainHandle, 1)) { + + if (AliasTestSuite( DomainHandle, BuiltinDomainHandle, DomainSid, 1)) { + + if (UserTestSuite( DomainHandle, 1 )) { + + if (SecurityTestSuite( ServerHandle, DomainHandle, 2)) { + + if (GroupTestSuite( DomainHandle, 2)) { + + if (AliasTestSuite( DomainHandle, BuiltinDomainHandle, DomainSid, 2)) { + + if (UserTestSuite( DomainHandle, 2)) { + + TestSucceeded = TRUE; + } + } + } + } + } + } + } + } + } + } else { + TestSucceeded = TRUE; + } + } + + SamFreeMemory(DomainSid); + + NtStatus = SamCloseHandle( DomainHandle ); + if (!NT_SUCCESS(NtStatus)) { + printf("SAM TEST: Status of SamCloseHandle(Domain) is: 0x%lx\n", NtStatus); + DbgBreakPoint(); + return; + } + + NtStatus = SamCloseHandle( ServerHandle ); + if (!NT_SUCCESS(NtStatus)) { + printf("SAM TEST: Status of SamCloseHandle(Server) is: 0x%lx\n", NtStatus); + DbgBreakPoint(); + return; + } + + } + + printf("\n"); + printf("\n"); + printf(" SAM Test: "); + if (TestSucceeded) { + printf("Succeeded\n"); + } else { + printf("** Failed **\n"); + } + + + return; +} + + +BOOLEAN +TInitialize ( + VOID + ) + +/*++ + +Routine Description: + + Initialize test variables, et cetera. + +Arguments: + + None. + +Return Value: + + + Note: + + +--*/ +{ + NTSTATUS NtStatus; + STRING Name; + ULONG i; + + SID_IDENTIFIER_AUTHORITY WorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY; + SID_IDENTIFIER_AUTHORITY DomainSidAuthority = {0,0,0,0,0,0}; + SID_IDENTIFIER_AUTHORITY BuiltinAuthority = SECURITY_NT_AUTHORITY; + + + // + // Get the domain SIDs from the policy database... + // + + NtStatus = SampSetDomainPolicy(); + ASSERT(NT_SUCCESS(NtStatus)); + + + // + // A random large integer value.. + // + + LargeInteger1.LowPart = 1234; + LargeInteger1.HighPart = 0; + + LargeInteger2.LowPart = 4321; + LargeInteger2.HighPart = 0; + + + RtlInitString( &Name, DUMMY_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &DummyName1, &Name, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + RtlInitString( &Name, DUMMY_NAME2 ); + NtStatus = RtlAnsiStringToUnicodeString( &DummyName2, &Name, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + + RtlInitString( &DummyAnsiString1, DUMMY_STRING1 ); + NtStatus = RtlAnsiStringToUnicodeString( &DummyString1, &DummyAnsiString1, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + RtlInitString( &DummyAnsiString2, DUMMY_STRING2 ); + NtStatus = RtlAnsiStringToUnicodeString( &DummyString2, &DummyAnsiString2, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + + DummyLogonHours.UnitsPerWeek = SAM_HOURS_PER_WEEK; + DummyLogonHours.LogonHours = &DummyLogonHoursBitMask[0]; + DummyLogonHoursBitMask[LOGON_HOURS_DIFFERENT_OFFSET] = 103; // Any non-zero value + + NoLogonRestriction.UnitsPerWeek = SAM_HOURS_PER_WEEK; + NoLogonRestriction.LogonHours = &NoLogonRestrictionBitMask[0]; + for ( i=0; i<(ULONG)((NoLogonRestriction.UnitsPerWeek+7)/8); i++) { + NoLogonRestrictionBitMask[0] = 0; + } + + + + // + // Initialize some SIDs + // + + + WorldSid = RtlAllocateHeap( RtlProcessHeap(), 0, RtlLengthRequiredSid(1) ); + ASSERT(WorldSid != NULL); + RtlInitializeSid( WorldSid, &WorldSidAuthority, 1 ); + *(RtlSubAuthoritySid( WorldSid, 0 )) = SECURITY_WORLD_RID; + + AdminsAliasSid = RtlAllocateHeap(RtlProcessHeap(), 0,RtlLengthRequiredSid( 2 )); + ASSERT(AdminsAliasSid != NULL); + RtlInitializeSid( AdminsAliasSid, &BuiltinAuthority, 2 ); + *(RtlSubAuthoritySid( AdminsAliasSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID; + *(RtlSubAuthoritySid( AdminsAliasSid, 1 )) = DOMAIN_ALIAS_RID_ADMINS; + + AccountAliasSid = RtlAllocateHeap(RtlProcessHeap(), 0,RtlLengthRequiredSid( 2 )); + ASSERT(AccountAliasSid != NULL); + RtlInitializeSid( AccountAliasSid, &BuiltinAuthority, 2 ); + *(RtlSubAuthoritySid( AccountAliasSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID; + *(RtlSubAuthoritySid( AccountAliasSid, 1 )) = DOMAIN_ALIAS_RID_ACCOUNT_OPS; + + + + + // + // Initialize some stuff for SID and NAME lookup operations + // + + RtlInitString( &Name, LOOKUP_KNOWN_NAME0 ); + + AllUses[0] = LOOKUP_KNOWN_NAME0_USE; AllRids[0] = LOOKUP_KNOWN_NAME0_RID; + NtStatus = RtlAnsiStringToUnicodeString( &AllNames[0], &Name, TRUE ); TST_SUCCESS_ASSERT(NtStatus); + SomeUses[0] = LOOKUP_KNOWN_NAME0_USE; SomeRids[0] = LOOKUP_KNOWN_NAME0_RID; + NtStatus = RtlAnsiStringToUnicodeString( &SomeNames[0], &Name, TRUE ); TST_SUCCESS_ASSERT(NtStatus); + + + if (AccountDomainIsNotPrimaryDomain == TRUE) { + RtlInitString( &Name, LOOKUP_KNOWN_NAME1_A ); + } else { + RtlInitString( &Name, LOOKUP_KNOWN_NAME1_P ); + } + AllUses[1] = LOOKUP_KNOWN_NAME1_USE; AllRids[1] = LOOKUP_KNOWN_NAME1_RID; + NtStatus = RtlAnsiStringToUnicodeString( &AllNames[1], &Name, TRUE ); TST_SUCCESS_ASSERT(NtStatus); + SomeUses[1] = LOOKUP_KNOWN_NAME1_USE; SomeRids[1] = LOOKUP_KNOWN_NAME1_RID; + NtStatus = RtlAnsiStringToUnicodeString( &SomeNames[1], &Name, TRUE ); TST_SUCCESS_ASSERT(NtStatus); + + RtlInitString( &Name, LOOKUP_UNKNOWN_NAME0 ); + + SomeUses[2] = SidTypeUnknown; + NtStatus = RtlAnsiStringToUnicodeString( &SomeNames[2], &Name, TRUE ); TST_SUCCESS_ASSERT(NtStatus); + NoUses[0] = SidTypeUnknown; + NtStatus = RtlAnsiStringToUnicodeString( &NoNames[0], &Name, TRUE ); TST_SUCCESS_ASSERT(NtStatus); + + + RtlInitString( &Name, LOOKUP_UNKNOWN_NAME1 ); + + SomeUses[3] = SidTypeUnknown; + NtStatus = RtlAnsiStringToUnicodeString( &SomeNames[3], &Name, TRUE ); TST_SUCCESS_ASSERT(NtStatus); + NoUses[1] = SidTypeUnknown; + NtStatus = RtlAnsiStringToUnicodeString( &NoNames[1], &Name, TRUE ); TST_SUCCESS_ASSERT(NtStatus); + + + + RtlInitString( &Name, LOOKUP_UNKNOWN_NAME2 ); + + SomeUses[4] = SidTypeUnknown; + NtStatus = RtlAnsiStringToUnicodeString( &SomeNames[4], &Name, TRUE ); TST_SUCCESS_ASSERT(NtStatus); + + + if (AccountDomainIsNotPrimaryDomain == TRUE) { + RtlInitString( &Name, LOOKUP_KNOWN_NAME2_A ); + } else { + RtlInitString( &Name, LOOKUP_KNOWN_NAME2_P ); + } + AllUses[2] = LOOKUP_KNOWN_NAME2_USE; AllRids[2] = LOOKUP_KNOWN_NAME2_RID; + NtStatus = RtlAnsiStringToUnicodeString( &AllNames[2], &Name, TRUE ); TST_SUCCESS_ASSERT(NtStatus); + SomeUses[5] = LOOKUP_KNOWN_NAME2_USE; SomeRids[5] = LOOKUP_KNOWN_NAME2_RID; + NtStatus = RtlAnsiStringToUnicodeString( &SomeNames[5], &Name, TRUE ); TST_SUCCESS_ASSERT(NtStatus); + + + + RtlInitString( &Name, LOOKUP_UNKNOWN_NAME3 ); + + SomeUses[6] = SidTypeUnknown; + NtStatus = RtlAnsiStringToUnicodeString( &SomeNames[6], &Name, TRUE ); TST_SUCCESS_ASSERT(NtStatus); + + + DetermineTestsToRun(); + + return(TRUE); +} + + +NTSTATUS +SampSetDomainPolicy( + ) +/*++ + + +Routine Description: + + This routine sets the names and SIDs for the builtin and account domains. + The builtin account domain has a well known name and SID. + The account domain has these stored in the Policy database. + + + It places the information for these domains in: + + BuiltinDomainSid + BuiltinDomainName + AccountDomainSid + AccountDomainName + PrimaryDomainSid + PrimaryDomainName + + It also sets the boolean: + + AccountDomainIsNotPrimaryDomain + + to TRUE if the account domain is found to be different from the + Primary Domain. + +Arguments: + + None. + +Return Value: + +--*/ + +{ + NTSTATUS NtStatus; + PPOLICY_ACCOUNT_DOMAIN_INFO PolicyAccountDomainInfo; + PPOLICY_PRIMARY_DOMAIN_INFO PolicyPrimaryDomainInfo; + SID_IDENTIFIER_AUTHORITY BuiltinAuthority = SECURITY_NT_AUTHORITY; + + // + // Builtin domain - well-known name and SID + // + + RtlInitUnicodeString( &BuiltinDomainName, L"Builtin"); + + BuiltinDomainSid = RtlAllocateHeap(RtlProcessHeap(), 0,RtlLengthRequiredSid( 1 )); + ASSERT( BuiltinDomainSid != NULL ); + RtlInitializeSid( BuiltinDomainSid, &BuiltinAuthority, 1 ); + *(RtlSubAuthoritySid( BuiltinDomainSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID; + + // + // Account domain + // + + NtStatus = SampGetLsaDomainInfo( + &PolicyAccountDomainInfo, + &PolicyPrimaryDomainInfo + ); + + if (!NT_SUCCESS(NtStatus)) { + + return(NtStatus); + } + + AccountDomainSid = PolicyAccountDomainInfo->DomainSid; + AccountDomainName = PolicyAccountDomainInfo->DomainName; + + PrimaryDomainSid = PolicyPrimaryDomainInfo->Sid; + PrimaryDomainName = PolicyPrimaryDomainInfo->Name; + + // + // Determine whether the account domain is a primary domain. + // + + AccountDomainIsNotPrimaryDomain = + !RtlEqualUnicodeString( &PrimaryDomainName, &AccountDomainName, TRUE); + + return(NtStatus);; +} + + + +NTSTATUS +SampGetLsaDomainInfo( + PPOLICY_ACCOUNT_DOMAIN_INFO *PolicyAccountDomainInfo, + PPOLICY_PRIMARY_DOMAIN_INFO *PolicyPrimaryDomainInfo + ) + +/*++ + +Routine Description: + + This routine retrieves ACCOUNT domain information from the LSA + policy database. + + +Arguments: + + PolicyAccountDomainInfo - Receives a pointer to a + POLICY_ACCOUNT_DOMAIN_INFO structure containing the account + domain info. + + PolicyPrimaryDomainInfo - Receives a pointer to a + POLICY_PRIMARY_DOMAIN_INFO structure containing the Primary + domain info. + + +Return Value: + + STATUS_SUCCESS - Succeeded. + + Other status values that may be returned from: + + LsaOpenPolicy() + LsaQueryInformationPolicy() +--*/ + +{ + NTSTATUS Status, IgnoreStatus; + + LSA_HANDLE PolicyHandle; + OBJECT_ATTRIBUTES PolicyObjectAttributes; + + // + // Open the policy database + // + + InitializeObjectAttributes( &PolicyObjectAttributes, + NULL, // Name + 0, // Attributes + NULL, // Root + NULL ); // Security Descriptor + + Status = LsaOpenPolicy( NULL, + &PolicyObjectAttributes, + POLICY_VIEW_LOCAL_INFORMATION, + &PolicyHandle ); + if ( NT_SUCCESS(Status) ) { + + // + // Query the account domain information + // + + Status = LsaQueryInformationPolicy( PolicyHandle, + PolicyAccountDomainInformation, + (PVOID *)PolicyAccountDomainInfo ); +#if DBG + if ( NT_SUCCESS(Status) ) { + ASSERT( (*PolicyAccountDomainInfo) != NULL ); + ASSERT( (*PolicyAccountDomainInfo)->DomainSid != NULL ); + ASSERT( (*PolicyAccountDomainInfo)->DomainName.Buffer != NULL ); + } +#endif \\DBG + + // + // Query the Primary domain information + // + + Status = LsaQueryInformationPolicy( PolicyHandle, + PolicyPrimaryDomainInformation, + (PVOID *)PolicyPrimaryDomainInfo ); +#if DBG + if ( NT_SUCCESS(Status) ) { + ASSERT( (*PolicyPrimaryDomainInfo) != NULL ); + ASSERT( (*PolicyPrimaryDomainInfo)->Sid != NULL ); + ASSERT( (*PolicyPrimaryDomainInfo)->Name.Buffer != NULL ); + } +#endif \\DBG + + IgnoreStatus = LsaClose( PolicyHandle ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + return(Status); +} + + + + +PSID +CreateUserSid( + PSID DomainSid, + ULONG Rid + ) + +/*++ + +Routine Description: + + This function creates a domain account sid given a domain sid and + the relative id of the account within the domain. + +Arguments: + + None. + +Return Value: + + Pointer to Sid, or NULL on failure. + The returned Sid must be freed with DeleteUserSid + +--*/ +{ + + NTSTATUS IgnoreStatus; + PSID AccountSid; + UCHAR AccountSubAuthorityCount = *RtlSubAuthorityCountSid(DomainSid) + (UCHAR)1; + ULONG AccountSidLength = RtlLengthRequiredSid(AccountSubAuthorityCount); + PULONG RidLocation; + + // Temp sanity check + ASSERT(AccountSidLength == RtlLengthSid(DomainSid) + sizeof(ULONG)); + + // + // Allocate space for the account sid + // + + AccountSid = MIDL_user_allocate(AccountSidLength); + + if (AccountSid != NULL) { + + // + // Copy the domain sid into the first part of the account sid + // + + IgnoreStatus = RtlCopySid(AccountSidLength, AccountSid, DomainSid); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + // + // Increment the account sid sub-authority count + // + + *RtlSubAuthorityCountSid(AccountSid) = AccountSubAuthorityCount; + + // + // Add the rid as the final sub-authority + // + + RidLocation = RtlSubAuthoritySid(AccountSid, AccountSubAuthorityCount - 1); + *RidLocation = Rid; + } + + return(AccountSid); +} + + + +VOID +DeleteUserSid( + PSID UserSid + ) + +/*++ + +Routine Description: + + Frees a sid returned by CreateUserSid. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + MIDL_user_free(UserSid); +} + + + +BOOLEAN +EnableSecurityPrivilege( + VOID + ) + +/*++ + +Routine Description: + + This function enabled the SeSecurityPrivilege privilege. + +Arguments: + + None. + +Return Value: + + TRUE if privilege successfully enabled. + FALSE if not successfully enabled. + +--*/ +{ + + NTSTATUS Status; + HANDLE Token; + LUID SecurityPrivilege; + PTOKEN_PRIVILEGES NewState; + ULONG ReturnLength; + + + // + // Open our own token + // + + Status = NtOpenProcessToken( + NtCurrentProcess(), + TOKEN_ADJUST_PRIVILEGES, + &Token + ); + if (!NT_SUCCESS(Status)) { + printf(" \n\n\n"); + printf("Tsamobj: Can't open process token to enable Security Privilege.\n"); + printf(" Completion status of NtOpenProcessToken() is: 0x%lx\n", Status); + printf("\n"); + return(FALSE); + } + + + // + // Initialize the adjustment structure + // + + SecurityPrivilege = + RtlConvertLongToLargeInteger(SE_SECURITY_PRIVILEGE); + + ASSERT( (sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)) < 100); + NewState = RtlAllocateHeap( RtlProcessHeap(), 0, 100 ); + + NewState->PrivilegeCount = 1; + NewState->Privileges[0].Luid = SecurityPrivilege; + NewState->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + + + // + // Set the state of the privilege to ENABLED. + // + + Status = NtAdjustPrivilegesToken( + Token, // TokenHandle + FALSE, // DisableAllPrivileges + NewState, // NewState + 0, // BufferLength + NULL, // PreviousState (OPTIONAL) + &ReturnLength // ReturnLength + ); + // don't use NT_SUCCESS here because STATUS_NOT_ALL_ASSIGNED is a success status + if (Status != STATUS_SUCCESS) { + return(FALSE); + } + + + // + // Clean up some stuff before returning + // + + RtlFreeHeap( RtlProcessHeap(), 0, NewState ); + Status = NtClose( Token ); + ASSERT(NT_SUCCESS(Status)); + + + return TRUE; + +} + + + +VOID +printfSid( + PSID Sid + ) + +/*++ + +Routine Description: + + Prints a sid + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + UCHAR Buffer[128]; + UCHAR String[128]; + UCHAR i; + ULONG Tmp; + PISID iSid = (PISID)Sid; // pointer to opaque structure + PSID NextSid = (PSID)Buffer; + + ASSERT(sizeof(Buffer) >= RtlLengthRequiredSid(1)); + + { + SID_IDENTIFIER_AUTHORITY SidAuthority = SECURITY_WORLD_SID_AUTHORITY; + RtlInitializeSid(NextSid, &SidAuthority, 1 ); + *(RtlSubAuthoritySid(NextSid, 0)) = SECURITY_WORLD_RID; + if (RtlEqualSid(Sid, NextSid)) { + printf("World"); + return; + } + } + + { + SID_IDENTIFIER_AUTHORITY SidAuthority = SECURITY_LOCAL_SID_AUTHORITY; + RtlInitializeSid(NextSid, &SidAuthority, 1 ); + *(RtlSubAuthoritySid(NextSid, 0)) = SECURITY_LOCAL_RID; + if (RtlEqualSid(Sid, NextSid)) { + printf("Local"); + return; + } + } + + { + SID_IDENTIFIER_AUTHORITY SidAuthority = SECURITY_CREATOR_SID_AUTHORITY; + RtlInitializeSid(NextSid, &SidAuthority, 1 ); + *(RtlSubAuthoritySid(NextSid, 0)) = SECURITY_CREATOR_OWNER_RID; + if (RtlEqualSid(Sid, NextSid)) { + printf("Creator"); + return; + } + } + + { + SID_IDENTIFIER_AUTHORITY SidAuthority = SECURITY_NT_AUTHORITY; + RtlInitializeSid(NextSid, &SidAuthority, 1 ); + *(RtlSubAuthoritySid(NextSid, 0)) = SECURITY_DIALUP_RID; + if (RtlEqualSid(Sid, NextSid)) { + printf("Dialup"); + return; + } + } + + { + SID_IDENTIFIER_AUTHORITY SidAuthority = SECURITY_NT_AUTHORITY; + RtlInitializeSid(NextSid, &SidAuthority, 1 ); + *(RtlSubAuthoritySid(NextSid, 0)) = SECURITY_NETWORK_RID; + if (RtlEqualSid(Sid, NextSid)) { + printf("Network"); + return; + } + } + + { + SID_IDENTIFIER_AUTHORITY SidAuthority = SECURITY_NT_AUTHORITY; + RtlInitializeSid(NextSid, &SidAuthority, 1 ); + *(RtlSubAuthoritySid(NextSid, 0)) = SECURITY_BATCH_RID; + if (RtlEqualSid(Sid, NextSid)) { + printf("Batch"); + return; + } + } + + { + SID_IDENTIFIER_AUTHORITY SidAuthority = SECURITY_NT_AUTHORITY; + RtlInitializeSid(NextSid, &SidAuthority, 1 ); + *(RtlSubAuthoritySid(NextSid, 0)) = SECURITY_INTERACTIVE_RID; + if (RtlEqualSid(Sid, NextSid)) { + printf("Interactive"); + return; + } + } + + + { + SID_IDENTIFIER_AUTHORITY SidAuthority = SECURITY_NT_AUTHORITY; + RtlInitializeSid(NextSid, &SidAuthority, 1 ); + *(RtlSubAuthoritySid(NextSid, 0)) = SECURITY_LOCAL_SYSTEM_RID; + if (RtlEqualSid(Sid, NextSid)) { + printf("Local System"); + return; + } + } + + + + sprintf(Buffer, "S-%u-", (USHORT)iSid->Revision ); + strcpy(String, Buffer); + + if ( (iSid->IdentifierAuthority.Value[0] != 0) || + (iSid->IdentifierAuthority.Value[1] != 0) ){ + sprintf(Buffer, "0x%02hx%02hx%02hx%02hx%02hx%02hx", + (USHORT)iSid->IdentifierAuthority.Value[0], + (USHORT)iSid->IdentifierAuthority.Value[1], + (USHORT)iSid->IdentifierAuthority.Value[2], + (USHORT)iSid->IdentifierAuthority.Value[3], + (USHORT)iSid->IdentifierAuthority.Value[4], + (USHORT)iSid->IdentifierAuthority.Value[5] ); + strcat(String, Buffer); + } else { + Tmp = (ULONG)iSid->IdentifierAuthority.Value[5] + + (ULONG)(iSid->IdentifierAuthority.Value[4] << 8) + + (ULONG)(iSid->IdentifierAuthority.Value[3] << 16) + + (ULONG)(iSid->IdentifierAuthority.Value[2] << 24); + sprintf(Buffer, "%lu", Tmp); + strcat(String, Buffer); + } + + + for (i=0;i<iSid->SubAuthorityCount ;i++ ) { + sprintf(Buffer, "-%lu", iSid->SubAuthority[i]); + strcat(String, Buffer); + } + + printf(Buffer); + + return; +} + + +VOID +DetermineTestsToRun( + VOID + ) + +/*++ + +Routine Description: + + This function determines which tests are to be run. + + +Arguments: + + None. + +Return Value: + + None. + + +--*/ +{ + + NTSTATUS Status; + HANDLE Token; + + PTOKEN_USER User; + PTOKEN_GROUPS Groups; + + ULONG ReturnLength, + i; + + + + // + // See if we can play with auditing information + // + + SecurityOperatorTest = EnableSecurityPrivilege(); + + + // + // Open our own token + // + + Status = NtOpenProcessToken( + NtCurrentProcess(), + TOKEN_QUERY, + &Token + ); + if (!NT_SUCCESS(Status)) { + printf(" \n\n\n"); + printf("Tsamobj: Can't open process token to query owner.\n"); + printf(" Completion status of NtOpenProcessToken() is: 0x%lx\n", Status); + printf("\n"); + return; + } + + + // + // Query the user id + // + + User = RtlAllocateHeap( RtlProcessHeap(), 0, 1000 ); // should be plenty big + Status = NtQueryInformationToken( Token, TokenUser, User, 1000, &ReturnLength ); + ASSERT(NT_SUCCESS(Status)); + + // + // See if the ID is one of the special IDs (e.g., local admin, + // domain account operator, or domain admin) + // + + SeeIfSidIsSpecial( User->User.Sid ); + + + + // + // Query the group ids + // + + Groups = RtlAllocateHeap( RtlProcessHeap(), 0, 1000 ); // should be plenty big + Status = NtQueryInformationToken( Token, TokenGroups, Groups, 1000, &ReturnLength ); + ASSERT(NT_SUCCESS(Status)); + + // + // See if any of these IDs are special IDs + // + + for (i=0; i<Groups->GroupCount; i++) { + SeeIfSidIsSpecial( Groups->Groups[i].Sid ); + } + + + + + + // + // Clean up some stuff before returning + // + + RtlFreeHeap( RtlProcessHeap(), 0, User ); + RtlFreeHeap( RtlProcessHeap(), 0, Groups ); + Status = NtClose( Token ); + ASSERT(NT_SUCCESS(Status)); + + + + printf("\n\n\n\nPerforming:\n\n"); + + printf(" Administrator Alias Test. . . . . "); + if (AdminsAliasTest) { + printf("Yes\n\n"); + } else { + printf("No\n\n"); + } + + printf(" Account Operator Alias Test . . "); + if (AccountOpAliasTest) { + printf("Yes\n\n"); + } else { + printf("No\n\n"); + } + + printf(" Security Operator Test . . . . . "); + if (SecurityOperatorTest) { + printf("Yes\n\n"); + } else { + printf("No\n\n"); + } + + printf("\n\n\n"); + + + + return; + +} + + +VOID +SeeIfSidIsSpecial( + IN PSID Sid + ) + +/*++ + +Routine Description: + + This function determines whether the passed SID is one of the special + SIDs, such as ADMINISTRATORS alias, or DomainAccountOperator, and + sets test flags accordingly. + + +Arguments: + + Sid - Pointer to the SID to check. + +Return Value: + + None. + + +--*/ +{ + + + + + if (RtlEqualSid( Sid, AdminsAliasSid )){ + AdminsAliasTest = TRUE; + } + + if (RtlEqualSid( Sid, AccountAliasSid )){ + AccountOpAliasTest = TRUE; + } + + return; + +} + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Server Object Test Suite // +// // +/////////////////////////////////////////////////////////////////////////////// + + +BOOLEAN +ServerTestSuite( + PHANDLE ServerHandle, + PHANDLE DomainHandle, + PHANDLE BuiltinDomainHandle, + PSID *DomainSid + ) + +{ + NTSTATUS NtStatus; + OBJECT_ATTRIBUTES ObjectAttributes; + BOOLEAN TestStatus = TRUE; + ULONG CountReturned; + SAM_ENUMERATE_HANDLE EnumerationContext; + PSAM_RID_ENUMERATION EnumerationBuffer; + PSID BuiltinDomainSid; + ACCESS_MASK ServerAccessMask, DomainAccessMask; + + + + + + printf("\n"); + printf("\n"); + printf(" Server Object Test\n"); + + /////////////////////////////////////////////////////////////////////////// + // // + // Connect To Server // + // // + /////////////////////////////////////////////////////////////////////////// + + printf("\n"); + printf(" Connect / Disconnect. . . . . . . . . . . . . . . . . Suite\n"); + + printf(" Connect . . . . . . . . . . . . . . . . . . . . . . "); + + + ServerAccessMask = SAM_SERVER_READ | SAM_SERVER_EXECUTE; + if (AdminsAliasTest) { + ServerAccessMask |= SAM_SERVER_ALL_ACCESS; + } + if (SecurityOperatorTest) { + ServerAccessMask |= ACCESS_SYSTEM_SECURITY; + } + + InitializeObjectAttributes( &ObjectAttributes, NULL, 0, 0, NULL ); + + + NtStatus = SamConnect( + NULL, // ServerName (Local machine) + ServerHandle, + ServerAccessMask, + &ObjectAttributes + ); + + + if (!NT_SUCCESS(NtStatus)) { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } else { + printf("Succeeded\n"); + } + + + if (NT_SUCCESS(NtStatus)) { + + printf(" Disconnect . . . . . . . . . . . . . . . . . . . . "); + + NtStatus = SamCloseHandle( (*ServerHandle) ); + + if (!NT_SUCCESS(NtStatus)) { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } else { + printf("Succeeded\n"); + } + } + + + + printf(" Re-Connect . . . . . . . . . . . . . . . . . . . . "); + + + NtStatus = SamConnect( + NULL, // ServerName (Local machine) + ServerHandle, + ServerAccessMask, + &ObjectAttributes + ); + + + if (!NT_SUCCESS(NtStatus)) { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } else { + printf("Succeeded\n"); + } + + + /////////////////////////////////////////////////////////////////////////// + // // + // Lookup/Enumerate Domains Suite // + // // + /////////////////////////////////////////////////////////////////////////// + + + printf("\n"); + printf(" Domain Lookup/Enumerate/Open . . . . . . . . . . . . Suite\n"); + + + + if (NT_SUCCESS(NtStatus)) { + + printf(" Lookup Account Domain . . . . . . . . . . . . . . . "); + + + NtStatus = SamLookupDomainInSamServer( + (*ServerHandle), + &AccountDomainName, + DomainSid + ); + + if (!NT_SUCCESS(NtStatus)) { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } else { + if ( TRUE != RtlEqualSid((*DomainSid), AccountDomainSid)) { + printf("Failed\n"); + printf(" The SID retrieved from the policy database did not\n"); + printf(" match the SID retrieved from SAM for the account\n"); + printf(" domain.\n"); + printf(" Sid from Policy Database is: "); + printfSid( AccountDomainSid ); printf("\n"); + printf(" Sid from SAM is: "); + printfSid( (*DomainSid) ); printf("\n"); + TestStatus = FALSE; + } else { + printf("Succeeded\n"); + } + } + + } + + + + + + + if (NT_SUCCESS(NtStatus)) { + + printf(" Enumerate Domain . . . . . . . . . . . . . . . . . "); + + + EnumerationContext = 0; + EnumerationBuffer = NULL; + NtStatus = SamEnumerateDomainsInSamServer( + (*ServerHandle), + &EnumerationContext, + (PVOID *)&EnumerationBuffer, + 1024, // PreferedMaximumLength + &CountReturned + ); + + if (!NT_SUCCESS(NtStatus)) { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } else { + + if (CountReturned == 0) { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + printf(" CountReturned is: 0x%lx\n", CountReturned); + printf(" EnumerationContext is: 0x%lx\n", EnumerationContext); + printf(" EnumerationBuffer Address is: 0x%lx\n", (ULONG)EnumerationBuffer); + TestStatus = FALSE; + + } else { + printf("Succeeded\n"); + } + + SamFreeMemory( EnumerationBuffer ); + } + + } + + + + + + + if (NT_SUCCESS(NtStatus)) { + + printf(" Open Account Domain . . . . . . . . . . . . . . . . "); + + if (NT_SUCCESS(NtStatus)) { + + DomainAccessMask = DOMAIN_READ | DOMAIN_EXECUTE; + if (AccountOpAliasTest) { + DomainAccessMask |= DOMAIN_READ | DOMAIN_WRITE | DOMAIN_EXECUTE; + } + if (AdminsAliasTest) { + DomainAccessMask |= DOMAIN_ALL_ACCESS; + } + if (SecurityOperatorTest) { + DomainAccessMask |= ACCESS_SYSTEM_SECURITY; + } + NtStatus = SamOpenDomain( + (*ServerHandle), + DomainAccessMask, + *DomainSid, + DomainHandle + ); + + if (!NT_SUCCESS(NtStatus)) { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } else { + printf("Succeeded\n"); + } + } + + } + + if (NT_SUCCESS(NtStatus)) { + + printf(" Open Builtin Domain . . . . . . . . . . . . . . . . "); + + NtStatus = SamLookupDomainInSamServer( + (*ServerHandle), + &BuiltinDomainName, + &BuiltinDomainSid + ); + + if (NT_SUCCESS(NtStatus)) { + + DomainAccessMask = DOMAIN_READ | DOMAIN_EXECUTE; + if (AccountOpAliasTest) { + DomainAccessMask |= DOMAIN_READ | DOMAIN_WRITE | DOMAIN_EXECUTE; + } + if (AdminsAliasTest) { + DomainAccessMask |= (DOMAIN_EXECUTE | DOMAIN_READ | + DOMAIN_READ_OTHER_PARAMETERS | + DOMAIN_ADMINISTER_SERVER | + DOMAIN_CREATE_ALIAS); + } +// if (SecurityOperatorTest) { +// DomainAccessMask |= ACCESS_SYSTEM_SECURITY; +// } + NtStatus = SamOpenDomain( + (*ServerHandle), + DomainAccessMask, + BuiltinDomainSid, + BuiltinDomainHandle + ); + + if (!NT_SUCCESS(NtStatus)) { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } else { + printf("Succeeded\n"); + } + } + + } + + return(TestStatus); + + +} + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Security Manipulation Test Suite // +// // +/////////////////////////////////////////////////////////////////////////////// + + +BOOLEAN +SecurityTestSuite( + HANDLE ServerHandle, + HANDLE DomainHandle, + ULONG Pass + ) +{ + + BOOLEAN TestStatus = TRUE; + NTSTATUS NtStatus; + + PSECURITY_DESCRIPTOR OriginalServerSD, + OriginalDomainSD, + OriginalUserSD, + OriginalGroupSD, + SD1; + + SECURITY_INFORMATION SI1; + PVOID TmpPointer1; + + SECURITY_DESCRIPTOR SD1_Body; + + HANDLE GroupHandle, + UserHandle; + + + + + printf("\n"); + printf("\n"); + printf("\n"); + + if (Pass == 1) { + + printf(" Security Manipulation (Pass #1) Test\n"); + + /////////////////////////////////////////////////////////////////////////// + // // + // Query Suite // + // // + /////////////////////////////////////////////////////////////////////////// + + printf("\n"); + printf(" Query Security . . . . . . . . . . . . . . . . . . . Suite\n"); + + + // + // Get Server's original SD + // + + + SI1 = 0; + if (AdminsAliasTest) { + SI1 |= OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | + DACL_SECURITY_INFORMATION; + } + if (SecurityOperatorTest) { + SI1 |= SACL_SECURITY_INFORMATION; + } + if (SI1 != 0) { + printf(" Query Server Security Descriptor . . . . . . . . . . "); + SD1 = NULL; + NtStatus = SamQuerySecurityObject( + ServerHandle, + SI1, + &SD1 + ); + if (NT_SUCCESS(NtStatus)) { + + TestStatus = CheckReturnedSD( SI1, SD1, TRUE ); + + // + // Normally we would do a "SamFreeMemory( SD1 )" here. + // However, we want to save this SD for future reference + // and use. + // + + OriginalServerSD = SD1; + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + } + + + + + + // + // Get domain's original SD + // + + + SI1 = 0; + if (AdminsAliasTest) { + SI1 |= OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | + DACL_SECURITY_INFORMATION; + } + if (SecurityOperatorTest) { + SI1 |= SACL_SECURITY_INFORMATION; + } + if (SI1 != 0) { + printf(" Query Domain Security Descriptor . . . . . . . . . . "); + SD1 = NULL; + NtStatus = SamQuerySecurityObject( + DomainHandle, + SI1, + &SD1 + ); + if (NT_SUCCESS(NtStatus)) { + + TestStatus = CheckReturnedSD( SI1, SD1, TRUE ); + + // + // Normally we would do a "SamFreeMemory( SD1 )" here. + // However, we want to save this SD for future reference + // and use. + // + + OriginalDomainSD = SD1; + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + } + + + + + + + // + // Make sure the wrapper doesn't choke on a non-null pointer being passed + // (assuming we have allocated memory). + // + + + SI1 = 0; + if (AdminsAliasTest) { + SI1 |= OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | + DACL_SECURITY_INFORMATION; + } + if (SecurityOperatorTest) { + SI1 |= SACL_SECURITY_INFORMATION; + } + if (SI1 != 0) { + printf(" Query Passing Non-null return buffer . . . . . . . . "); + SD1 = RtlAllocateHeap( RtlProcessHeap(), 0, 1000 ); ASSERT(SD1 != NULL); + TmpPointer1 = SD1; + NtStatus = SamQuerySecurityObject( + DomainHandle, + SI1, + &SD1 + ); + if (NT_SUCCESS(NtStatus)) { + if (SD1 != TmpPointer1) { + + TestStatus = CheckReturnedSD( SI1, SD1, TRUE ); + if (TestStatus) { + SamFreeMemory( SD1 ); + } + + + } else { + printf("Failed\n"); + printf(" Passed buffer address used on return.\n"); + printf(" RPC should have allocated another buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + RtlFreeHeap( RtlProcessHeap(), 0, TmpPointer1 ); + + } + + + + + + + // + // Make sure we can query nothing + // + + printf(" Query Nothing . . . . . . . . . . . . . . . . . . . . "); + + SI1 = 0; + SD1 = NULL; + NtStatus = SamQuerySecurityObject( + DomainHandle, + SI1, + &SD1 + ); + if (NT_SUCCESS(NtStatus)) { + + TestStatus = CheckReturnedSD( SI1, SD1, TRUE ); + if (TestStatus) { + SamFreeMemory( SD1 ); + } + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + + + // + // Query owner + // + + + if (AdminsAliasTest) { + printf(" Query Owner (Server Object) . . . . . . . . . . . . . "); + SI1 = OWNER_SECURITY_INFORMATION; + SD1 = NULL; + NtStatus = SamQuerySecurityObject( + ServerHandle, + SI1, + &SD1 + ); + if (NT_SUCCESS(NtStatus)) { + + TestStatus = CheckReturnedSD( SI1, SD1, TRUE ); + if (TestStatus) { + SamFreeMemory( SD1 ); + } + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + } + + + + + if (AdminsAliasTest) { + printf(" Query Owner (Domain Object) . . . . . . . . . . . . . "); + SI1 = OWNER_SECURITY_INFORMATION; + SD1 = NULL; + NtStatus = SamQuerySecurityObject( + DomainHandle, + SI1, + &SD1 + ); + if (NT_SUCCESS(NtStatus)) { + + TestStatus = CheckReturnedSD( SI1, SD1, TRUE ); + if (TestStatus) { + SamFreeMemory( SD1 ); + } + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + } + + + + + + if (AdminsAliasTest) { + + // + // Query Group + // + + printf(" Query Group . . . . . . . . . . . . . . . . . . . . . "); + + SI1 = GROUP_SECURITY_INFORMATION; + SD1 = NULL; + NtStatus = SamQuerySecurityObject( + DomainHandle, + SI1, + &SD1 + ); + if (NT_SUCCESS(NtStatus)) { + + TestStatus = CheckReturnedSD( SI1, SD1, TRUE ); + if (TestStatus) { + SamFreeMemory( SD1 ); + } + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + + + // + // Query Dacl + // + + printf(" Query DACL . . . . . . . . . . . . . . . . . . . . . "); + + SI1 = DACL_SECURITY_INFORMATION; + SD1 = NULL; + NtStatus = SamQuerySecurityObject( + DomainHandle, + SI1, + &SD1 + ); + if (NT_SUCCESS(NtStatus)) { + + TestStatus = CheckReturnedSD( SI1, SD1, TRUE ); + if (TestStatus) { + SamFreeMemory( SD1 ); + } + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + + + // + // Query Sacl + // + + printf(" Query SACL . . . . . . . . . . . . . . . . . . . . . "); + + SI1 = SACL_SECURITY_INFORMATION; + SD1 = NULL; + NtStatus = SamQuerySecurityObject( + DomainHandle, + SI1, + &SD1 + ); + if (NT_SUCCESS(NtStatus)) { + + TestStatus = CheckReturnedSD( SI1, SD1, TRUE ); + if (TestStatus) { + SamFreeMemory( SD1 ); + } + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + } // end_if (AdminsAliasTest) + + + + + + /////////////////////////////////////////////////////////////////////////// + // // + // Set Suite // + // // + /////////////////////////////////////////////////////////////////////////// + + printf("\n"); + printf(" Set Security . . . . . . . . . . . . . . . . . . . . Suite\n"); + + + // + // Make sure we can set nothing + // + + printf(" Set Nothing . . . . . . . . . . . . . . . . . . . . . "); + + SI1 = 0; + SD1 = &SD1_Body; + NtStatus = RtlCreateSecurityDescriptor( SD1, SECURITY_DESCRIPTOR_REVISION1 ); + ASSERT( NT_SUCCESS(NtStatus) ); + NtStatus = SamSetSecurityObject( + DomainHandle, + SI1, // <------ This is invalid + SD1 + ); + if (NtStatus == STATUS_INVALID_PARAMETER) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + + + + // + // set something not passed + // + + printf(" Set something not passed. . . . . . . . . . . . . . . "); + + SI1 = GROUP_SECURITY_INFORMATION; + SD1 = &SD1_Body; + NtStatus = RtlCreateSecurityDescriptor( SD1, SECURITY_DESCRIPTOR_REVISION1 ); + ASSERT( NT_SUCCESS(NtStatus) ); + NtStatus = SamSetSecurityObject( + DomainHandle, + SI1, + SD1 + ); + if (NtStatus == STATUS_BAD_DESCRIPTOR_FORMAT) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + + + + // + // set a non-existant DACL + // + + if (AdminsAliasTest) { + printf(" Set non-existant DACL (Server object) . . . . . . . . "); + + SI1 = DACL_SECURITY_INFORMATION; + SD1 = &SD1_Body; + NtStatus = RtlCreateSecurityDescriptor( SD1, SECURITY_DESCRIPTOR_REVISION1 ); + SD1_Body.Control = SE_DACL_PRESENT; + ASSERT( NT_SUCCESS(NtStatus) ); + NtStatus = SamSetSecurityObject( + ServerHandle, + SI1, + SD1 + ); + if (NT_SUCCESS(NtStatus)) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + } + + + + if (AdminsAliasTest) { + printf(" Set non-existant DACL (Domain Object) . . . . . . . . "); + + SI1 = DACL_SECURITY_INFORMATION; + SD1 = &SD1_Body; + NtStatus = RtlCreateSecurityDescriptor( SD1, SECURITY_DESCRIPTOR_REVISION1 ); + SD1_Body.Control = SE_DACL_PRESENT; + ASSERT( NT_SUCCESS(NtStatus) ); + NtStatus = SamSetSecurityObject( + DomainHandle, + SI1, + SD1 + ); + if (NT_SUCCESS(NtStatus)) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + } + + + + + + // + // set original DACL (From original SD) + // + + if (AdminsAliasTest) { + + printf(" Set original DACL (Server Object) . . . . . . . . . . "); + + SI1 = DACL_SECURITY_INFORMATION; + SD1 = OriginalServerSD; + NtStatus = SamSetSecurityObject( + ServerHandle, + SI1, + SD1 + ); + if (NT_SUCCESS(NtStatus)) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + } + + + + if (AdminsAliasTest) { + + printf(" Set original DACL (Domain Object) . . . . . . . . . . "); + + SI1 = DACL_SECURITY_INFORMATION; + SD1 = OriginalDomainSD; + NtStatus = SamSetSecurityObject( + DomainHandle, + SI1, + SD1 + ); + if (NT_SUCCESS(NtStatus)) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + } + + + + + + if (AdminsAliasTest) { + + // + // set a non-existant SACL + // + + printf(" Set non-existant SACL . . . . . . . . . . . . . . . . "); + + SI1 = SACL_SECURITY_INFORMATION; + SD1 = &SD1_Body; + NtStatus = RtlCreateSecurityDescriptor( SD1, SECURITY_DESCRIPTOR_REVISION1 ); + SD1_Body.Control = SE_SACL_PRESENT; + ASSERT( NT_SUCCESS(NtStatus) ); + NtStatus = SamSetSecurityObject( + DomainHandle, + SI1, + SD1 + ); + if (NT_SUCCESS(NtStatus)) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + + + + // + // set original SACL (From original SD) + // + + printf(" Set original SACL . . . . . . . . . . . . . . . . . . "); + + SI1 = SACL_SECURITY_INFORMATION; + SD1 = OriginalDomainSD; + NtStatus = SamSetSecurityObject( + DomainHandle, + SI1, + SD1 + ); + if (NT_SUCCESS(NtStatus)) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + + + // + // set a owner to null + // + + printf(" Set null Owner . . . . . . . . . . . . . . . . . . . "); + + SI1 = OWNER_SECURITY_INFORMATION; + SD1 = &SD1_Body; + NtStatus = RtlCreateSecurityDescriptor( SD1, SECURITY_DESCRIPTOR_REVISION1 ); + SD1_Body.Owner = NULL; + ASSERT( NT_SUCCESS(NtStatus) ); + NtStatus = SamSetSecurityObject( + DomainHandle, + SI1, + SD1 + ); + if (NtStatus = STATUS_BAD_DESCRIPTOR_FORMAT) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + + + // + // set owner to invalid value + // + + printf(" Set owner to invalid value . . . . . . . . . . . . . "); + + SI1 = OWNER_SECURITY_INFORMATION; + SD1 = &SD1_Body; + NtStatus = RtlCreateSecurityDescriptor( SD1, SECURITY_DESCRIPTOR_REVISION1 ); + SD1_Body.Owner = WorldSid; + ASSERT( NT_SUCCESS(NtStatus) ); + NtStatus = SamSetSecurityObject( + DomainHandle, + SI1, + SD1 + ); + if (NtStatus = STATUS_INVALID_OWNER) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + + // + // set a owner to valid value + // + + printf(" Set owner to valid value . . . . . . . . . . . . . . "); + + printf("Untested\n"); + + + + + + // + // set group to null + // + + printf(" Set null Group . . . . . . . . . . . . . . . . . . . "); + + SI1 = GROUP_SECURITY_INFORMATION; + SD1 = &SD1_Body; + NtStatus = RtlCreateSecurityDescriptor( SD1, SECURITY_DESCRIPTOR_REVISION1 ); + SD1_Body.Group = NULL; + ASSERT( NT_SUCCESS(NtStatus) ); + NtStatus = SamSetSecurityObject( + DomainHandle, + SI1, + SD1 + ); + if (NtStatus = STATUS_BAD_DESCRIPTOR_FORMAT) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + + + + // + // set Group to valid value + // + + printf(" Set Group to valid value . . . . . . . . . . . . . . "); + + SI1 = GROUP_SECURITY_INFORMATION; + SD1 = &SD1_Body; + NtStatus = RtlCreateSecurityDescriptor( SD1, SECURITY_DESCRIPTOR_REVISION1 ); + SD1_Body.Group = WorldSid; + ASSERT( NT_SUCCESS(NtStatus) ); + NtStatus = SamSetSecurityObject( + DomainHandle, + SI1, + SD1 + ); + if (NT_SUCCESS(NtStatus)) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + + + // + // set Group back to original value + // + + printf(" Set Group to original value . . . . . . . . . . . . . "); + + SI1 = GROUP_SECURITY_INFORMATION; + SD1 = OriginalDomainSD; + NtStatus = SamSetSecurityObject( + DomainHandle, + SI1, + SD1 + ); + if (NT_SUCCESS(NtStatus)) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + } + + + + + } // end Pass1 + + + if (Pass == 2) { + + ACCESS_MASK AccessMask; + PSID_NAME_USE LookedUpUses; + PULONG LookedUpRids; + UNICODE_STRING AccountNames[10]; + STRING AccountNameAnsi; + + + // + // This pass depends upon user and group accounts established in pass #1 + // + + + + + + if (AdminsAliasTest) { + + + printf(" Security Manipulation (Pass #2) Test\n"); + + /////////////////////////////////////////////////////////////////////////// + // // + // Query Suite // + // // + /////////////////////////////////////////////////////////////////////////// + + printf("\n"); + printf(" Query Security (User Object). . . . . . . . . . . . . Suite\n"); + + + AccessMask = READ_CONTROL; + if (SecurityOperatorTest) { + AccessMask |= ACCESS_SYSTEM_SECURITY; + } + + // + // Open the user created in pass #1 + // + + RtlInitString( &AccountNameAnsi, USER_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountNames[0], &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + NtStatus = SamLookupNamesInDomain( + DomainHandle, + 1, + &AccountNames[0], + &LookedUpRids, + &LookedUpUses + ); + RtlFreeUnicodeString( &AccountNames[0] ); + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(LookedUpUses[0] == SidTypeUser); + NtStatus = SamOpenUser( + DomainHandle, + AccessMask, + LookedUpRids[0], + &UserHandle); + SamFreeMemory( LookedUpUses ); SamFreeMemory( LookedUpRids ); + if (!NT_SUCCESS(NtStatus)) { + printf("Failed to open user account created in pass #1\n"); + } + TST_SUCCESS_ASSERT(NT_SUCCESS(NtStatus)); + + + + // + // Get user's original SD + // + + SI1 |= OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | + DACL_SECURITY_INFORMATION; + if (SecurityOperatorTest) { + SI1 |= SACL_SECURITY_INFORMATION; + } + + printf(" Query User Security Descriptor . . . . . . . . . . . "); + SD1 = NULL; + NtStatus = SamQuerySecurityObject( + UserHandle, + SI1, + &SD1 + ); + if (NT_SUCCESS(NtStatus)) { + + TestStatus = CheckReturnedSD( SI1, SD1, TRUE ); + + // + // Normally we would do a "SamFreeMemory( SD1 )" here. + // However, we want to save this SD for future reference + // and use. + // + + OriginalUserSD = SD1; + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + + + NtStatus = SamCloseHandle( UserHandle ); + TST_SUCCESS_ASSERT( UserHandle ); + + + + /////////////////////////////////////////////////////////////////////////// + // // + // Set Suite // + // // + /////////////////////////////////////////////////////////////////////////// + + printf("\n"); + printf(" Set Security (User Object) . . . . . . . . . . . . . Suite\n"); + + AccessMask = WRITE_DAC | WRITE_OWNER; + if (SecurityOperatorTest) { + AccessMask |= ACCESS_SYSTEM_SECURITY; + } + + // + // Open the user created in pass #1 + // + + RtlInitString( &AccountNameAnsi, USER_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountNames[0], &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + NtStatus = SamLookupNamesInDomain( + DomainHandle, + 1, + &AccountNames[0], + &LookedUpRids, + &LookedUpUses + ); + RtlFreeUnicodeString( &AccountNames[0] ); + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(LookedUpUses[0] == SidTypeUser); + NtStatus = SamOpenUser( + DomainHandle, + AccessMask, + LookedUpRids[0], + &UserHandle); + SamFreeMemory( LookedUpUses ); SamFreeMemory( LookedUpRids ); + if (!NT_SUCCESS(NtStatus)) { + printf("Failed to open user account created in pass #1\n"); + } + TST_SUCCESS_ASSERT(NT_SUCCESS(NtStatus)); + + + // + // Make sure we can set nothing + // + + printf(" Set Nothing . . . . . . . . . . . . . . . . . . . . . "); + + SI1 = 0; + SD1 = &SD1_Body; + NtStatus = RtlCreateSecurityDescriptor( SD1, SECURITY_DESCRIPTOR_REVISION1 ); + ASSERT( NT_SUCCESS(NtStatus) ); + NtStatus = SamSetSecurityObject( + UserHandle, + SI1, // <------ This is invalid + SD1 + ); + if (NtStatus == STATUS_INVALID_PARAMETER) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + + + + // + // set something not passed + // + + printf(" Set something not passed. . . . . . . . . . . . . . . "); + + SI1 = GROUP_SECURITY_INFORMATION; + SD1 = &SD1_Body; + NtStatus = RtlCreateSecurityDescriptor( SD1, SECURITY_DESCRIPTOR_REVISION1 ); + ASSERT( NT_SUCCESS(NtStatus) ); + NtStatus = SamSetSecurityObject( + UserHandle, + SI1, + SD1 + ); + if (NtStatus == STATUS_BAD_DESCRIPTOR_FORMAT) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + + + + + + printf(" Set non-existant DACL . . . . . . . . . . . . . . . . "); + + SI1 = DACL_SECURITY_INFORMATION; + SD1 = &SD1_Body; + NtStatus = RtlCreateSecurityDescriptor( SD1, SECURITY_DESCRIPTOR_REVISION1 ); + SD1_Body.Control = SE_DACL_PRESENT; + ASSERT( NT_SUCCESS(NtStatus) ); + NtStatus = SamSetSecurityObject( + UserHandle, + SI1, + SD1 + ); + if (NT_SUCCESS(NtStatus)) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + + + + // + // set original DACL (From original SD) + // + + + printf(" Set original DACL . . . . . . . . . . . . . . . . . . "); + + SI1 = DACL_SECURITY_INFORMATION; + SD1 = OriginalUserSD; + NtStatus = SamSetSecurityObject( + UserHandle, + SI1, + SD1 + ); + if (NT_SUCCESS(NtStatus)) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + + + + + NtStatus = SamCloseHandle( UserHandle ); + TST_SUCCESS_ASSERT( UserHandle ); + + + + } + + DBG_UNREFERENCED_LOCAL_VARIABLE( GroupHandle ); + DBG_UNREFERENCED_LOCAL_VARIABLE( OriginalGroupSD ); + } + + + + + + return TestStatus; +} + + +BOOLEAN +CheckReturnedSD( + IN SECURITY_INFORMATION SI, + IN PSECURITY_DESCRIPTOR SD, + IN BOOLEAN PrintTestSuccess + ) + + +{ + NTSTATUS NtStatus; + + BOOLEAN Failed = FALSE, + IgnoreBoolean, + AclPresent, + TestStatus = TRUE; + + PSID SID; + PACL ACL; + + + + // + // Check a returned security descriptor agains the information requested. + // + + if (SD == NULL) { + TestStatus = FALSE; + if (PrintTestSuccess) { + printf("Failed\n"); + Failed = TRUE; + printf(" The SecurityDescriptor return address was not properly\n"); + printf(" set.\n"); + } + } + + + if (TestStatus) { + + // + // Check owner + // + + NtStatus = RtlGetOwnerSecurityDescriptor ( SD, &SID, &IgnoreBoolean); + ASSERT(NT_SUCCESS(NtStatus)); + if (SI & OWNER_SECURITY_INFORMATION) { + if (SID == NULL) { + if (PrintTestSuccess) { + if (!Failed) { + printf("Failed\n"); + printf(" Security descriptor address is 0x%lx\n", SD ); + Failed = TRUE; + } + printf(" An owner was requested but the owner field of the\n"); + printf(" security descriptor is not set.\n"); + TestStatus = FALSE; + + } + } + } else { // Owner not specified + if (SID != NULL) { + if (PrintTestSuccess) { + if (!Failed) { + printf("Failed\n"); + printf(" Security descriptor address is 0x%lx\n", SD ); + Failed = TRUE; + } + printf(" An owner was not requested but the owner field of the\n"); + printf(" security descriptor is set.\n"); + TestStatus = FALSE; + } + } + } + + + + + // + // Check group + // + + NtStatus = RtlGetGroupSecurityDescriptor ( SD, &SID, &IgnoreBoolean); + ASSERT(NT_SUCCESS(NtStatus)); + if (SI & GROUP_SECURITY_INFORMATION) { + if (SID == NULL) { + if (PrintTestSuccess) { + if (!Failed) { + printf("Failed\n"); + printf(" Security descriptor address is 0x%lx\n", SD ); + Failed = TRUE; + } + printf(" A group was requested but the group field of the\n"); + printf(" security descriptor is not set.\n"); + TestStatus = FALSE; + + } + } + } else { // Group not specified + if (SID != NULL) { + if (PrintTestSuccess) { + if (!Failed) { + printf("Failed\n"); + printf(" Security descriptor address is 0x%lx\n", SD ); + Failed = TRUE; + } + printf(" A group was not requested but the group field of the\n"); + printf(" security descriptor is set.\n"); + TestStatus = FALSE; + } + } + } + + + + + // + // Check sacl + // + + NtStatus = RtlGetSaclSecurityDescriptor ( SD, &AclPresent, &ACL, &IgnoreBoolean); + ASSERT(NT_SUCCESS(NtStatus)); + if (SI & SACL_SECURITY_INFORMATION) { + if (!AclPresent) { + if (PrintTestSuccess) { + if (!Failed) { + printf("Failed\n"); + printf(" Security descriptor address is 0x%lx\n", SD ); + Failed = TRUE; + } + printf(" An SACL was requested but the SaclPresent flag\n"); + printf(" of the security descriptor is not set.\n"); + TestStatus = FALSE; + + } + } + } else { // sacl not specified + if (AclPresent) { + if (PrintTestSuccess) { + if (!Failed) { + printf("Failed\n"); + printf(" Security descriptor address is 0x%lx\n", SD ); + Failed = TRUE; + } + printf(" An SACL was not requested but the SaclPresent flag\n"); + printf(" of the security descriptor is set.\n"); + TestStatus = FALSE; + } + } + } + + + + + + // + // Check Dacl + // + + NtStatus = RtlGetDaclSecurityDescriptor ( SD, &AclPresent, &ACL, &IgnoreBoolean); + ASSERT(NT_SUCCESS(NtStatus)); + if (SI & DACL_SECURITY_INFORMATION) { + if (!AclPresent) { + if (PrintTestSuccess) { + if (!Failed) { + printf("Failed\n"); + printf(" Security descriptor address is 0x%lx\n", SD ); + Failed = TRUE; + } + printf(" A DACL was requested but the DaclPresent flag\n"); + printf(" of the security descriptor is not set.\n"); + TestStatus = FALSE; + + } + } + } else { // Dacl not specified + if (AclPresent) { + if (PrintTestSuccess) { + if (!Failed) { + printf("Failed\n"); + printf(" Security descriptor address is 0x%lx\n", SD ); + Failed = TRUE; + } + printf(" A DACL was not requested but the DaclPresent flag\n"); + printf(" of the security descriptor is set.\n"); + TestStatus = FALSE; + } + } + } + + + + + + } + + + + + if (PrintTestSuccess) { + if (TestStatus) { + printf("Succeeded\n"); + } + } + + + + return(TestStatus); +} + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Domain Object Test Suite // +// // +/////////////////////////////////////////////////////////////////////////////// + + +BOOLEAN +DomainTestSuite( + HANDLE DomainHandle + ) +{ + + BOOLEAN TestStatus = TRUE; + NTSTATUS NtStatus, IgnoreStatus; + PVOID Buffer, Buffer1, Buffer2; + CHAR UnusedBuffer[20]; + UNICODE_STRING AccountName; + STRING AccountNameAnsi; + HANDLE GroupHandle = NULL; + HANDLE AliasHandle = NULL; + HANDLE UserHandle = NULL; + HANDLE ValidUserHandle = NULL; + ULONG GroupRid, AliasRid, UserRid, SavedGroupRid, SavedAliasRid, AccountCount, i; + SAM_ENUMERATE_HANDLE EnumerationContext; + ULONG CountReturned; + USHORT NameLength; + PUNICODE_STRING LookedUpNames; + PSID_NAME_USE LookedUpUses; + PULONG LookedUpRids; + + + printf("\n"); + printf("\n"); + printf("\n"); + printf(" Domain Test\n"); + + /////////////////////////////////////////////////////////////////////////// + // // + // Query Suite // + // // + /////////////////////////////////////////////////////////////////////////// + + printf("\n"); + printf(" Query Information . . . . . . . . . . . . . . . . . . Suite\n"); + + + // + // Make sure the wrapper doesn't choke on a non-null pointer being passed + // (assuming we have allocated memory). + // + + printf(" Query Buffer Allocation Test . . . . . . . . . . . . "); + + Buffer = &UnusedBuffer[0]; + NtStatus = SamQueryInformationDomain( + DomainHandle, + DomainStateInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != &UnusedBuffer[0]) { + if (Buffer != NULL) { + printf("Succeeded\n"); + SamFreeMemory( Buffer ); + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Passed buffer address used on return.\n"); + printf(" RPC should have allocated another buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + // + // Query all the fixed length info levels + // Query - Password, Logoff, ServerRole, DomainState, ModifiedCount, LockoutInfo + // + + printf(" Query DomainState . . . . . . . . . . . . . . . . . . "); + + NtStatus = SamQueryInformationDomain( + DomainHandle, + DomainStateInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + printf("Succeeded\n"); + SamFreeMemory( Buffer ); + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + + } + + + printf(" Query ServerRole . . . . . . . . . . . . . . . . . . "); + NtStatus = SamQueryInformationDomain( + DomainHandle, + DomainServerRoleInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + printf("Succeeded\n"); + SamFreeMemory( Buffer ); + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + + } + + + printf(" Query Password Information . . . . . . . . . . . . . "); + NtStatus = SamQueryInformationDomain( + DomainHandle, + DomainPasswordInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + printf("Succeeded\n"); + SamFreeMemory( Buffer ); + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + + } + + + printf(" Query Logoff Information . . . . . . . . . . . . . . "); + NtStatus = SamQueryInformationDomain( + DomainHandle, + DomainLogoffInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + printf("Succeeded\n"); + SamFreeMemory( Buffer ); + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + + } + + + printf(" Query Modified . . . . . . . . . . . . . . . . . . . "); + NtStatus = SamQueryInformationDomain( + DomainHandle, + DomainModifiedInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + printf("Succeeded\n"); + SamFreeMemory( Buffer ); + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + + } + + + printf(" Query Lockout . . . . . . . . . . . . . . . . . . . . "); + NtStatus = SamQueryInformationDomain( + DomainHandle, + DomainLockoutInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + printf("Succeeded\n"); + SamFreeMemory( Buffer ); + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + + } + + + + + + // + // Query the name of the domain ... + // + + printf(" Query Domain Name . . . . . . . . . . . . . . . . . . "); + + Buffer = NULL; + NtStatus = SamQueryInformationDomain( + DomainHandle, + DomainNameInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + if ( (((DOMAIN_NAME_INFORMATION *)Buffer)->DomainName.MaximumLength > 0) && + (((DOMAIN_NAME_INFORMATION *)Buffer)->DomainName.Buffer != NULL) ) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" String body returned and allocated,\n"); + printf(" but character buffer pointer is NULL.\n"); + TestStatus = FALSE; + } + SamFreeMemory( Buffer ); + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + // + // Query whatever is in the OEM Information field ... + // + + printf(" Query OEM Information . . . . . . . . . . . . . . . . "); + + Buffer = NULL; + NtStatus = SamQueryInformationDomain( + DomainHandle, + DomainOemInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + if ( (((DOMAIN_OEM_INFORMATION *)Buffer)->OemInformation.MaximumLength >= 0) && + (((DOMAIN_OEM_INFORMATION *)Buffer)->OemInformation.Buffer != NULL) ) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" String body returned and allocated,\n"); + printf(" but character buffer pointer is NULL.\n"); + TestStatus = FALSE; + } + SamFreeMemory( Buffer ); + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + // + // Query whatever is in the Replication Information field ... + // + + printf(" Query Replication Information . . . . . . . . . . . . "); + + Buffer = NULL; + NtStatus = SamQueryInformationDomain( + DomainHandle, + DomainReplicationInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + if ( (((DOMAIN_REPLICATION_INFORMATION *)Buffer)->ReplicaSourceNodeName.MaximumLength >= 0) && + (((DOMAIN_REPLICATION_INFORMATION *)Buffer)->ReplicaSourceNodeName.Buffer != NULL) ) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" String body returned and allocated,\n"); + printf(" but character buffer pointer is NULL.\n"); + TestStatus = FALSE; + } + SamFreeMemory( Buffer ); + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + // + // Query domain general Information... + // + + printf(" Query General Information . . . . . . . . . . . . . . "); + + Buffer = NULL; + NtStatus = SamQueryInformationDomain( + DomainHandle, + DomainGeneralInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + + printf("Succeeded\n"); + printf(" Number of Users is: 0x%lx\n", + ((DOMAIN_GENERAL_INFORMATION *)Buffer)->UserCount ); + printf(" Number of groups is: 0x%lx\n", + ((DOMAIN_GENERAL_INFORMATION *)Buffer)->GroupCount); + printf(" Number of aliases is: 0x%lx\n", + ((DOMAIN_GENERAL_INFORMATION *)Buffer)->AliasCount); + + + SamFreeMemory( Buffer ); + + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + // + // Query domain general Information... + // + + printf(" Query General Information 2 . . . . . . . . . . . . . "); + + Buffer = NULL; + NtStatus = SamQueryInformationDomain( + DomainHandle, + DomainGeneralInformation2, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + + printf("Succeeded\n"); + printf(" Number of Users is: 0x%lx\n", + ((DOMAIN_GENERAL_INFORMATION2 *)Buffer)->I1.UserCount ); + printf(" Number of groups is: 0x%lx\n", + ((DOMAIN_GENERAL_INFORMATION2 *)Buffer)->I1.GroupCount); + printf(" Number of aliases is: 0x%lx\n", + ((DOMAIN_GENERAL_INFORMATION2 *)Buffer)->I1.AliasCount); + + + SamFreeMemory( Buffer ); + + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + + + + + + /////////////////////////////////////////////////////////////////////////// + // // + // Set Suite // + // // + /////////////////////////////////////////////////////////////////////////// + + printf(" Set Information . . . . . . . . . . . . . . . . . . . Suite\n"); + + // + // Set all the fixed length info levels + // - Password, Logoff, ServerRole, DomainState, ModifiedCount + // + +/* + * CANT TEST SERVER STATE SETTING WITHOUT BREAKING THE REST OF THE TEST. + * THE REASON IS, ONCE THE STATE IS CHANGED, NOTHING ELSE CAN BE DONE. + * + * printf(" Set DomainState . . . . . . . . . . . . . . . . . . . "); + * + * // + * // Get the current value... + * // + * + * NtStatus = SamQueryInformationDomain( + * DomainHandle, + * DomainStateInformation, + * &Buffer1 + * ); + * ASSERT( NT_SUCCESS(NtStatus) ); + * + * // + * // Change the field to a new value and write it out. + * // + * + * if ( ((DOMAIN_STATE_INFORMATION *)Buffer1)->DomainServerState == + * DomainServerEnabled ) { + * ((DOMAIN_STATE_INFORMATION *)Buffer1)->DomainServerState = + * DomainServerDisabled; + * } else { + * ((DOMAIN_STATE_INFORMATION *)Buffer1)->DomainServerState = + * DomainServerEnabled; + * } + * + * NtStatus = SamSetInformationDomain( + * DomainHandle, + * DomainStateInformation, + * Buffer1 + * ); + * if ( NT_SUCCESS(NtStatus) ) { + * + * // + * // Now check that the change was really made... + * // + * + * NtStatus = SamQueryInformationDomain( + * DomainHandle, + * DomainStateInformation, + * &Buffer2 + * ); + * ASSERT(NT_SUCCESS( NtStatus ) ); + * if (((DOMAIN_STATE_INFORMATION *)Buffer1)->DomainServerState == + * ((DOMAIN_STATE_INFORMATION *)Buffer2)->DomainServerState ) { + * + * printf("Succeeded\n"); + * + * } else { + * + * printf("Failed\n"); + * printf(" Value queried doesn't match value written\n"); + * printf(" Value Written is 0x%lx\n", + * (ULONG)((DOMAIN_STATE_INFORMATION *)Buffer1)->DomainServerState); + * printf(" Value Retrieved is 0x%lx\n", + * (ULONG)((DOMAIN_STATE_INFORMATION *)Buffer2)->DomainServerState); + * + * TestStatus = FALSE; + * + * } + * + * SamFreeMemory( Buffer1 ); + * SamFreeMemory( Buffer2 ); + * + * } else { + * printf("Failed\n"); + * printf(" Completion status is 0x%lx\n", NtStatus); + * TestStatus = FALSE; + * SamFreeMemory( Buffer1 ); + * + * } + */ + + + +/* + * CANT TEST SERVER ROLE SETTING WITHOUT BREAKING THE REST OF THE TEST. + * THE REASON IS, ONCE THE ROLE IS SET TO BACKUP, NOTHING ELSE CAN BE + * SET. + * + * printf(" Set ServerRole . . . . . . . . . . . . . . . . . . . "); + * + * // + * // Get the current value... + * // + * + * NtStatus = SamQueryInformationDomain( + * DomainHandle, + * DomainServerRoleInformation, + * &Buffer1 + * ); + * ASSERT( NT_SUCCESS(NtStatus) ); + * + * // + * // Change the field to a new value and write it out. + * // + * + * if ( ((DOMAIN_SERVER_ROLE_INFORMATION *)Buffer1)->DomainServerRole == + * DomainServerRolePrimary ) { + * ((DOMAIN_SERVER_ROLE_INFORMATION *)Buffer1)->DomainServerRole = + * DomainServerRoleBackup; + * } else { + * ((DOMAIN_SERVER_ROLE_INFORMATION *)Buffer1)->DomainServerRole = + * DomainServerRolePrimary; + * } + * + * NtStatus = SamSetInformationDomain( + * DomainHandle, + * DomainServerRoleInformation, + * Buffer1 + * ); + * if ( NT_SUCCESS(NtStatus) ) { + * + * // + * // Now check that the change was really made... + * // + * + * NtStatus = SamQueryInformationDomain( + * DomainHandle, + * DomainServerRoleInformation, + * &Buffer2 + * ); + * ASSERT(NT_SUCCESS( NtStatus ) ); + * if (((DOMAIN_SERVER_ROLE_INFORMATION *)Buffer1)->DomainServerRole == + * ((DOMAIN_SERVER_ROLE_INFORMATION *)Buffer2)->DomainServerRole ) { + * + * printf("Succeeded\n"); + * + * } else { + * + * printf("Failed\n"); + * printf(" Value queried doesn't match value written\n"); + * printf(" Value Written is 0x%lx\n", + * (ULONG)((DOMAIN_SERVER_ROLE_INFORMATION *)Buffer1)->DomainServerRole); + * printf(" Value Retrieved is 0x%lx\n", + * (ULONG)((DOMAIN_SERVER_ROLE_INFORMATION *)Buffer2)->DomainServerRole); + * + * TestStatus = FALSE; + * + * } + * + * SamFreeMemory( Buffer1 ); + * SamFreeMemory( Buffer2 ); + * + * } else { + * printf("Failed\n"); + * printf(" Completion status is 0x%lx\n", NtStatus); + * TestStatus = FALSE; + * SamFreeMemory( Buffer1 ); + * + * } + */ + + + + printf(" Set Password Information . . . . . . . . . . . . . . "); + + + // + // Get the current value... + // + + NtStatus = SamQueryInformationDomain( + DomainHandle, + DomainPasswordInformation, + &Buffer1 + ); + ASSERT( NT_SUCCESS(NtStatus) ); + + // + // Change a field to a new value and write it out. + // + + if ( ((DOMAIN_PASSWORD_INFORMATION *)Buffer1)->MinPasswordLength == 0 ) { + ((DOMAIN_PASSWORD_INFORMATION *)Buffer1)->MinPasswordLength = 6; + } else { + ((DOMAIN_PASSWORD_INFORMATION *)Buffer1)->MinPasswordLength = 0; + } + + // + // Set PasswordProperties to COMPLEX so that tests run after this one + // are a little more interesting. + // + + ((DOMAIN_PASSWORD_INFORMATION *)Buffer1)->PasswordProperties |= DOMAIN_PASSWORD_COMPLEX; + + NtStatus = SamSetInformationDomain( + DomainHandle, + DomainPasswordInformation, + Buffer1 + ); + if ( NT_SUCCESS(NtStatus) ) { + + // + // Now check that the change was really made... + // + + NtStatus = SamQueryInformationDomain( + DomainHandle, + DomainPasswordInformation, + &Buffer2 + ); + ASSERT(NT_SUCCESS( NtStatus ) ); + if (((DOMAIN_PASSWORD_INFORMATION *)Buffer1)->MinPasswordLength == + ((DOMAIN_PASSWORD_INFORMATION *)Buffer2)->MinPasswordLength ) { + + printf("Succeeded\n"); + + } else { + + printf("Failed\n"); + printf(" Value queried doesn't match value written\n"); + printf(" Value Written is 0x%lx\n", + (ULONG)((DOMAIN_PASSWORD_INFORMATION *)Buffer1)->MinPasswordLength); + printf(" Value Retrieved is 0x%lx\n", + (ULONG)((DOMAIN_PASSWORD_INFORMATION *)Buffer2)->MinPasswordLength); + + TestStatus = FALSE; + + } + + SamFreeMemory( Buffer1 ); + SamFreeMemory( Buffer2 ); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + SamFreeMemory( Buffer1 ); + + } + + + + printf(" Set Logoff Information . . . . . . . . . . . . . . . "); + + // + // Get the current value... + // + + NtStatus = SamQueryInformationDomain( + DomainHandle, + DomainLogoffInformation, + &Buffer1 + ); + ASSERT( NT_SUCCESS(NtStatus) ); + + // + // Change the field to a new value and write it out. + // + + if ( ((DOMAIN_LOGOFF_INFORMATION *)Buffer1)->ForceLogoff.LowPart == 0 ) { + ((DOMAIN_LOGOFF_INFORMATION *)Buffer1)->ForceLogoff.LowPart = 1000; + } else { + ((DOMAIN_LOGOFF_INFORMATION *)Buffer1)->ForceLogoff.LowPart = 0; + } + + NtStatus = SamSetInformationDomain( + DomainHandle, + DomainLogoffInformation, + Buffer1 + ); + if ( NT_SUCCESS(NtStatus) ) { + + // + // Now check that the change was really made... + // + + NtStatus = SamQueryInformationDomain( + DomainHandle, + DomainLogoffInformation, + &Buffer2 + ); + ASSERT(NT_SUCCESS( NtStatus ) ); + if (((DOMAIN_LOGOFF_INFORMATION *)Buffer1)->ForceLogoff.LowPart == + ((DOMAIN_LOGOFF_INFORMATION *)Buffer2)->ForceLogoff.LowPart ) { + + printf("Succeeded\n"); + + } else { + + printf("Failed\n"); + printf(" Value queried doesn't match value written\n"); + printf(" Value Written is 0x%lx\n", + (ULONG)((DOMAIN_LOGOFF_INFORMATION *)Buffer1)->ForceLogoff.LowPart); + printf(" Value Retrieved is 0x%lx\n", + (ULONG)((DOMAIN_LOGOFF_INFORMATION *)Buffer2)->ForceLogoff.LowPart); + + TestStatus = FALSE; + + } + + SamFreeMemory( Buffer1 ); + SamFreeMemory( Buffer2 ); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + SamFreeMemory( Buffer1 ); + + } + + + + printf(" Set Modified . . . . . . . . . . . . . . . . . . . . "); + + + NtStatus = SamSetInformationDomain( + DomainHandle, + DomainModifiedInformation, + &LargeInteger1 + ); + + if (NtStatus != STATUS_INVALID_INFO_CLASS) { + + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } else { + printf("Succeeded\n"); + } + + + printf(" Set Lockout Information . . . . . . . . . . . . . . . "); + + // + // Get the current value... + // + + NtStatus = SamQueryInformationDomain( + DomainHandle, + DomainLockoutInformation, + &Buffer1 + ); + ASSERT( NT_SUCCESS(NtStatus) ); + + // + // Change the field to a new value and write it out. + // + + if ( ((DOMAIN_LOCKOUT_INFORMATION *)Buffer1)->LockoutDuration.LowPart == 0 ) { + ((DOMAIN_LOCKOUT_INFORMATION *)Buffer1)->LockoutDuration.LowPart = 9000000; + } else { + ((DOMAIN_LOCKOUT_INFORMATION *)Buffer1)->LockoutDuration.LowPart = 0; + } + if ( ((DOMAIN_LOCKOUT_INFORMATION *)Buffer1)->LockoutObservationWindow.LowPart == 0 ) { + ((DOMAIN_LOCKOUT_INFORMATION *)Buffer1)->LockoutObservationWindow.LowPart = 8000000; + } else { + ((DOMAIN_LOCKOUT_INFORMATION *)Buffer1)->LockoutObservationWindow.LowPart = 0; + } + if ( ((DOMAIN_LOCKOUT_INFORMATION *)Buffer1)->LockoutThreshold == 0 ) { + ((DOMAIN_LOCKOUT_INFORMATION *)Buffer1)->LockoutThreshold = 2; + } else { + ((DOMAIN_LOCKOUT_INFORMATION *)Buffer1)->LockoutThreshold = 0; + } + + NtStatus = SamSetInformationDomain( + DomainHandle, + DomainLockoutInformation, + Buffer1 + ); + if ( NT_SUCCESS(NtStatus) ) { + + // + // Now check that the change was really made... + // + + NtStatus = SamQueryInformationDomain( + DomainHandle, + DomainLockoutInformation, + &Buffer2 + ); + ASSERT(NT_SUCCESS( NtStatus ) ); + if ( (((DOMAIN_LOCKOUT_INFORMATION *)Buffer1)->LockoutDuration.LowPart == + ((DOMAIN_LOCKOUT_INFORMATION *)Buffer2)->LockoutDuration.LowPart ) && + (((DOMAIN_LOCKOUT_INFORMATION *)Buffer1)->LockoutObservationWindow.LowPart == + ((DOMAIN_LOCKOUT_INFORMATION *)Buffer2)->LockoutObservationWindow.LowPart ) && + (((DOMAIN_LOCKOUT_INFORMATION *)Buffer1)->LockoutThreshold == + ((DOMAIN_LOCKOUT_INFORMATION *)Buffer2)->LockoutThreshold ) ) { + + printf("Succeeded\n"); + + } else { + + printf("Failed\n"); + printf(" Value queried doesn't match value written\n"); + printf(" Duration Written is 0x%lx\n", + (ULONG)((DOMAIN_LOCKOUT_INFORMATION *)Buffer1)->LockoutDuration.LowPart); + printf(" Duration Retrieved is 0x%lx\n", + (ULONG)((DOMAIN_LOCKOUT_INFORMATION *)Buffer2)->LockoutDuration.LowPart); + printf(" Window Written is 0x%lx\n", + (ULONG)((DOMAIN_LOCKOUT_INFORMATION *)Buffer1)->LockoutObservationWindow.LowPart); + printf(" Window Retrieved is 0x%lx\n", + (ULONG)((DOMAIN_LOCKOUT_INFORMATION *)Buffer2)->LockoutObservationWindow.LowPart); + printf(" Duration Written is 0x%lx\n", + (ULONG)((DOMAIN_LOCKOUT_INFORMATION *)Buffer1)->LockoutThreshold); + printf(" Duration Retrieved is 0x%lx\n", + (ULONG)((DOMAIN_LOCKOUT_INFORMATION *)Buffer2)->LockoutThreshold); + + TestStatus = FALSE; + + } + + SamFreeMemory( Buffer1 ); + SamFreeMemory( Buffer2 ); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + SamFreeMemory( Buffer1 ); + + } + + + + + printf(" Set Domain Name . . . . . . . . . . . . . . . . . . . "); + + + NtStatus = SamSetInformationDomain( + DomainHandle, + DomainNameInformation, + &DummyName1 + ); + + if (NtStatus != STATUS_INVALID_INFO_CLASS) { + + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } else { + printf("Succeeded\n"); + } + + + printf(" Set OEM Information . . . . . . . . . . . . . . . . . "); + + // + // Get the current value... + // + + NtStatus = SamQueryInformationDomain( + DomainHandle, + DomainOemInformation, + &Buffer1 + ); + ASSERT( NT_SUCCESS(NtStatus) ); + + // + // Change the field to a new value and write it out. + // + + NameLength = ((DOMAIN_OEM_INFORMATION *)Buffer1)->OemInformation.Length; + if ( NameLength == DummyName1.Length ) { + ((DOMAIN_OEM_INFORMATION *)Buffer1)->OemInformation = DummyName2; + } else { + ((DOMAIN_OEM_INFORMATION *)Buffer1)->OemInformation = DummyName1; + } + + NtStatus = SamSetInformationDomain( + DomainHandle, + DomainOemInformation, + Buffer1 + ); + if ( NT_SUCCESS(NtStatus) ) { + + // + // Now check that the change was really made... + // + + NtStatus = SamQueryInformationDomain( + DomainHandle, + DomainOemInformation, + &Buffer2 + ); + ASSERT(NT_SUCCESS( NtStatus ) ); + if (((DOMAIN_OEM_INFORMATION *)Buffer1)->OemInformation.Length == + ((DOMAIN_OEM_INFORMATION *)Buffer2)->OemInformation.Length ) { + + printf("Succeeded\n"); + + } else { + + printf("Failed\n"); + printf(" Value queried doesn't match value written\n"); + printf(" Value Written is 0x%lx\n", + (ULONG)((DOMAIN_OEM_INFORMATION *)Buffer1)->OemInformation.Length); + printf(" Value Retrieved is 0x%lx\n", + (ULONG)((DOMAIN_OEM_INFORMATION *)Buffer2)->OemInformation.Length); + + TestStatus = FALSE; + + } + + SamFreeMemory( Buffer1 ); + SamFreeMemory( Buffer2 ); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + SamFreeMemory( Buffer1 ); + + } + + + + + printf(" Set Replication Information . . . . . . . . . . . . . "); + + // + // Get the current value... + // + + NtStatus = SamQueryInformationDomain( + DomainHandle, + DomainReplicationInformation, + &Buffer1 + ); + ASSERT( NT_SUCCESS(NtStatus) ); + + // + // Change the field to a new value and write it out. + // + + NameLength = ((DOMAIN_REPLICATION_INFORMATION *)Buffer1)->ReplicaSourceNodeName.Length; + if ( NameLength == DummyName1.Length ) { + ((DOMAIN_REPLICATION_INFORMATION *)Buffer1)->ReplicaSourceNodeName = DummyName2; + } else { + ((DOMAIN_REPLICATION_INFORMATION *)Buffer1)->ReplicaSourceNodeName = DummyName1; + } + + NtStatus = SamSetInformationDomain( + DomainHandle, + DomainReplicationInformation, + Buffer1 + ); + if ( NT_SUCCESS(NtStatus) ) { + + // + // Now check that the change was really made... + // + + NtStatus = SamQueryInformationDomain( + DomainHandle, + DomainReplicationInformation, + &Buffer2 + ); + ASSERT(NT_SUCCESS( NtStatus ) ); + if (((DOMAIN_REPLICATION_INFORMATION *)Buffer1)->ReplicaSourceNodeName.Length == + ((DOMAIN_REPLICATION_INFORMATION *)Buffer2)->ReplicaSourceNodeName.Length ) { + + printf("Succeeded\n"); + + } else { + + printf("Failed\n"); + printf(" Value queried doesn't match value written\n"); + printf(" Value Written is 0x%lx\n", + (ULONG)((DOMAIN_REPLICATION_INFORMATION *)Buffer1)->ReplicaSourceNodeName.Length); + printf(" Value Retrieved is 0x%lx\n", + (ULONG)((DOMAIN_REPLICATION_INFORMATION *)Buffer2)->ReplicaSourceNodeName.Length); + + TestStatus = FALSE; + + } + + SamFreeMemory( Buffer1 ); + SamFreeMemory( Buffer2 ); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + SamFreeMemory( Buffer1 ); + + } + + + + + /////////////////////////////////////////////////////////////////////////// + // // + // Create User/Group/Alias Suite // + // // + /////////////////////////////////////////////////////////////////////////// + + printf(" Create User/Group/Alias . . . . . . . . . . . . . . . . Suite\n"); + + + printf(" Create Group . . . . . . . . . . . . . . . . . . . . "); + + RtlInitString( &AccountNameAnsi, GROUP_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + + //InitializeObjectAttributes( &ObjectAttributes, &AccountName, 0, 0, NULL ); + + GroupRid = 0; + GroupHandle = NULL; + NtStatus = SamCreateGroupInDomain( + DomainHandle, + &AccountName, + GROUP_ALL_ACCESS, + &GroupHandle, + &GroupRid + ); + RtlFreeUnicodeString( &AccountName ); + + if (NT_SUCCESS(NtStatus)) { + if ( (GroupHandle == NULL) || (GroupRid == 0) ) { + + printf("Failed\n"); + printf(" Invalid GroupHandle or GroupRid returned.\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + printf(" GroupHandle value is: 0x%lx\n", (ULONG)GroupHandle); + printf(" GroupRid value is: 0x%lx\n", GroupRid); + TestStatus = FALSE; + } else { + + printf("Succeeded\n"); + SavedGroupRid = GroupRid; + NtStatus = SamCloseHandle( GroupHandle ); + if (!NT_SUCCESS(NtStatus)) { + printf(" SamCloseHandle() completion status is: 0x%lx\n", NtStatus); + } + ASSERT( NT_SUCCESS(NtStatus) ); + + } + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + + printf(" Create Duplicate Group . . . . . . . . . . . . . . . "); + RtlInitString( &AccountNameAnsi, GROUP_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + //InitializeObjectAttributes( &ObjectAttributes, &AccountName, 0, 0, NULL ); + + + GroupRid = 0; + GroupHandle = NULL; + NtStatus = SamCreateGroupInDomain( + DomainHandle, + &AccountName, + GROUP_ALL_ACCESS, + &GroupHandle, + &GroupRid + ); + RtlFreeUnicodeString( &AccountName ); + + if (NtStatus != STATUS_GROUP_EXISTS) { + + printf("Failed\n"); + printf(" Completion status should be STATUS_GROUP_EXISTS\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + + } else { + + printf("Succeeded\n"); + + } + + + + printf(" Create Alias . . . . . . . . . . . . . . . . . . . . "); + + RtlInitString( &AccountNameAnsi, ALIAS_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + + AliasRid = 0; + AliasHandle = NULL; + NtStatus = SamCreateAliasInDomain( + DomainHandle, + &AccountName, + ALIAS_ALL_ACCESS, + &AliasHandle, + &AliasRid + ); + RtlFreeUnicodeString( &AccountName ); + + if (NT_SUCCESS(NtStatus)) { + if ( (AliasHandle == NULL) || (AliasRid == 0) ) { + + printf("Failed\n"); + printf(" Invalid AliasHandle or AliasRid returned.\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + printf(" AliasHandle value is: 0x%lx\n", (ULONG)AliasHandle); + printf(" AliasRid value is: 0x%lx\n", AliasRid); + TestStatus = FALSE; + } else { + + printf("Succeeded\n"); + SavedAliasRid = AliasRid; + NtStatus = SamCloseHandle( AliasHandle ); + if (!NT_SUCCESS(NtStatus)) { + printf(" SamCloseHandle() completion status is: 0x%lx\n", NtStatus); + } + ASSERT( NT_SUCCESS(NtStatus) ); + + + if (AliasRid == SavedGroupRid) { + printf(" Create Group/Alias Comparison. . . . . . . . . . . . . Failed\n"); + + printf(" Same RID assigned to new alias and group.\n"); + TestStatus = FALSE; + } + } + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + + printf(" Create another Alias . . . . . . . . . . . . . . . . "); + + RtlInitString( &AccountNameAnsi, ALIAS_NAME2 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + + AliasRid = 0; + AliasHandle = NULL; + NtStatus = SamCreateAliasInDomain( + DomainHandle, + &AccountName, + ALIAS_ALL_ACCESS, + &AliasHandle, + &AliasRid + ); + RtlFreeUnicodeString( &AccountName ); + + if (NT_SUCCESS(NtStatus)) { + if ( (AliasHandle == NULL) || (AliasRid == 0) ) { + + printf("Failed\n"); + printf(" Invalid AliasHandle or AliasRid returned.\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + printf(" AliasHandle value is: 0x%lx\n", (ULONG)AliasHandle); + printf(" AliasRid value is: 0x%lx\n", AliasRid); + TestStatus = FALSE; + } else { + + printf("Succeeded\n"); + SavedAliasRid = AliasRid; + NtStatus = SamCloseHandle( AliasHandle ); + if (!NT_SUCCESS(NtStatus)) { + printf(" SamCloseHandle() completion status is: 0x%lx\n", NtStatus); + } + ASSERT( NT_SUCCESS(NtStatus) ); + + + if (AliasRid == SavedGroupRid) { + printf(" Create Group/Alias Comparison. . . . . . . . . . . . . Failed\n"); + + printf(" Same RID assigned to new alias and group.\n"); + TestStatus = FALSE; + } + } + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + + printf(" Create Duplicate Alias . . . . . . . . . . . . . . . "); + RtlInitString( &AccountNameAnsi, ALIAS_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + + AliasRid = 0; + AliasHandle = NULL; + NtStatus = SamCreateAliasInDomain( + DomainHandle, + &AccountName, + ALIAS_ALL_ACCESS, + &AliasHandle, + &AliasRid + ); + RtlFreeUnicodeString( &AccountName ); + + if (NtStatus != STATUS_ALIAS_EXISTS) { + + printf("Failed\n"); + printf(" Completion status should be STATUS_ALIAS_EXISTS\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + + } else { + + printf("Succeeded\n"); + + } + + + + + + printf(" Create User . . . . . . . . . . . . . . . . . . . . . "); + + RtlInitString( &AccountNameAnsi, USER_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + + UserRid = 0; + UserHandle = NULL; + NtStatus = SamCreateUserInDomain( + DomainHandle, + &AccountName, + USER_ALL_ACCESS, + &UserHandle, + &UserRid + ); + RtlFreeUnicodeString( &AccountName ); + + if (NT_SUCCESS(NtStatus)) { + if ( (UserHandle == NULL) || (UserRid == 0) ) { + + printf("Failed\n"); + printf(" Invalid UserHandle or UserRid returned.\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + printf(" UserHandle value is: 0x%lx\n", (ULONG)UserHandle); + printf(" UserRid value is: 0x%lx\n", UserRid); + TestStatus = FALSE; + } else { + + printf("Succeeded\n"); + ValidUserHandle = UserHandle; + + + if (UserRid == SavedGroupRid) { + printf(" Create Group/User Comparison. . . . . . . . . . . . . Failed\n"); + + printf(" Same RID assigned to new user and group.\n"); + TestStatus = FALSE; + } + + if (UserRid == SavedAliasRid) { + printf(" Create Alias/User Comparison. . . . . . . . . . . . . Failed\n"); + + printf(" Same RID assigned to new user and alias.\n"); + TestStatus = FALSE; + } + } + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + + + + + printf(" Create Duplicate User . . . . . . . . . . . . . . . . "); + + RtlInitString( &AccountNameAnsi, USER_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + + UserRid = 0; + UserHandle = NULL; + NtStatus = SamCreateUserInDomain( + DomainHandle, + &AccountName, + USER_ALL_ACCESS, + &UserHandle, + &UserRid + ); + RtlFreeUnicodeString( &AccountName ); + + if (NtStatus != STATUS_USER_EXISTS) { + + printf("Failed\n"); + printf(" Completion status should be STATUS_USER_EXISTS\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + + } else { + + printf("Succeeded\n"); + + } + + + + + printf(" Create Group With Same Name As User . . . . . . . . . "); + + RtlInitString( &AccountNameAnsi, USER_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + + GroupRid = 0; + GroupHandle = NULL; + NtStatus = SamCreateGroupInDomain( + DomainHandle, + &AccountName, + GROUP_ALL_ACCESS, + &GroupHandle, + &GroupRid + ); + RtlFreeUnicodeString( &AccountName ); + + if (NtStatus != STATUS_USER_EXISTS) { + + printf("Failed\n"); + printf(" Completion status should be STATUS_USER_EXISTS\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + + } else { + + printf("Succeeded\n"); + + } + + + + + printf(" Create Group With Same Name As Alias. . . . . . . . . "); + + RtlInitString( &AccountNameAnsi, ALIAS_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + + GroupRid = 0; + GroupHandle = NULL; + NtStatus = SamCreateGroupInDomain( + DomainHandle, + &AccountName, + GROUP_ALL_ACCESS, + &GroupHandle, + &GroupRid + ); + RtlFreeUnicodeString( &AccountName ); + + if (NtStatus != STATUS_ALIAS_EXISTS) { + + printf("Failed\n"); + printf(" Completion status should be STATUS_ALIAS_EXISTS\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + + } else { + + printf("Succeeded\n"); + + } + + + + printf(" Create Alias With Same Name As Group. . . . . . . . . "); + + RtlInitString( &AccountNameAnsi, GROUP_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + + AliasRid = 0; + AliasHandle = NULL; + NtStatus = SamCreateAliasInDomain( + DomainHandle, + &AccountName, + GROUP_ALL_ACCESS, + &AliasHandle, + &AliasRid + ); + RtlFreeUnicodeString( &AccountName ); + + if (NtStatus != STATUS_GROUP_EXISTS) { + + printf("Failed\n"); + printf(" Completion status should be STATUS_GROUP_EXISTS\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + + } else { + + printf("Succeeded\n"); + + } + + + + printf(" Create User With Same Name As Group . . . . . . . . . "); + + RtlInitString( &AccountNameAnsi, GROUP_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + //InitializeObjectAttributes( &ObjectAttributes, &AccountName, 0, 0, NULL ); + + + UserRid = 0; + UserHandle = NULL; + NtStatus = SamCreateUserInDomain( + DomainHandle, + &AccountName, + USER_ALL_ACCESS, + &UserHandle, + &UserRid + ); + RtlFreeUnicodeString( &AccountName ); + + if (NtStatus != STATUS_GROUP_EXISTS) { + + printf("Failed\n"); + printf(" Completion status should be STATUS_GROUP_EXISTS\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + + } else { + + printf("Succeeded\n"); + + } + + + + printf(" Create User With Same Name As Alias . . . . . . . . . "); + + RtlInitString( &AccountNameAnsi, ALIAS_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + + UserRid = 0; + UserHandle = NULL; + NtStatus = SamCreateUserInDomain( + DomainHandle, + &AccountName, + USER_ALL_ACCESS, + &UserHandle, + &UserRid + ); + RtlFreeUnicodeString( &AccountName ); + + if (NtStatus != STATUS_ALIAS_EXISTS) { + + printf("Failed\n"); + printf(" Completion status should be STATUS_ALIAS_EXISTS\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + + } else { + + printf("Succeeded\n"); + + } + + + + /////////////////////////////////////////////////////////////////////////// + // // + // Call server to test internal functions // + // // + /////////////////////////////////////////////////////////////////////////// + + printf("\n"); + printf(" Test internal functions . . . . . . . . . . . . . . . Suite\n"); + printf(" Test internal domain functions . . . . . . . . . . "); + + NtStatus = SamTestPrivateFunctionsDomain( DomainHandle ); + + if ( NT_SUCCESS( NtStatus ) ) { + + printf("Succeeded.\n"); + + } else { + + if ( NtStatus == STATUS_NOT_IMPLEMENTED ) { + + printf("Not Implemented\n"); + + } else { + + printf("Failed.\n"); + printf(" Status = %lx\n", NtStatus ); + TestStatus = FALSE; + } + } + + printf(" Test internal user functions . . . . . . . . . . . "); + + if (ValidUserHandle == NULL) { + + printf("Test omitted - Valid User handle not available\n"); + TestStatus = FALSE; + + } else { + + NtStatus = SamTestPrivateFunctionsUser( ValidUserHandle ); + IgnoreStatus = SamCloseHandle( ValidUserHandle ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + if ( NT_SUCCESS( NtStatus ) ) { + + printf("Succeeded.\n"); + + } else { + + if ( NtStatus == STATUS_NOT_IMPLEMENTED ) { + + printf("Not Implemented\n"); + + } else { + + printf("Failed.\n"); + printf(" Status = %lx\n", NtStatus ); + TestStatus = FALSE; + } + } + } + + + /////////////////////////////////////////////////////////////////////////// + // // + // Enumerate Users/Groups Suite // + // // + /////////////////////////////////////////////////////////////////////////// + + + printf(" Enumerate Users/Groups/Aliases. . . . . . . . . . . . Suite\n"); + + printf(" Enumerate Groups - large prefered length . . . . . . "); + + + EnumerationContext = 0; + NtStatus = SamEnumerateGroupsInDomain( + DomainHandle, + &EnumerationContext, + &Buffer, + 12000, // PreferedMaximumLength + &CountReturned + ); + AccountCount = CountReturned; // Save for future test + + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + if (NtStatus == STATUS_SUCCESS) { + + if (CountReturned > 1) { + printf("Succeeded\n"); + for (i=0; i<CountReturned; i++) { + printf(" Rid/Name(%ld): 0x%lx / %wZ\n",i, + ((PSAM_RID_ENUMERATION)(Buffer))[i].RelativeId, + &((PSAM_RID_ENUMERATION)(Buffer))[i].Name + ); + } + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + printf(" Expected several entries to be returned.\n"); + printf(" Received 0x%lx entries instead.\n", CountReturned); + TestStatus = FALSE; + } + + } else { + + printf("Failed\n"); + printf(" Expected STATUS_MORE_ENTRIES to be returned.\n"); + printf(" Received 0x%lx instead.\n", NtStatus); + printf(" Buffer = 0x%lx\n", (ULONG)Buffer); + printf(" CountReturned = 0x%lx\n", CountReturned); + TestStatus = FALSE; + } + + SamFreeMemory( Buffer ); + + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + + } + + + + + printf(" Enumerate Groups - small prefered length . . . . . . "); + + + for ( i=0; i<AccountCount; i++) { + EnumerationContext = i; + NtStatus = SamEnumerateGroupsInDomain( + DomainHandle, + &EnumerationContext, + &Buffer, + 0, // PreferedMaximumLength + &CountReturned + ); + + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + if ( ((i >= AccountCount -1) && (NtStatus == STATUS_SUCCESS)) || + ((i <= AccountCount -1) && (NtStatus == STATUS_MORE_ENTRIES)) ) { + + if (CountReturned != 1) { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + printf(" Expected one entry to be returned.\n"); + printf(" Received 0x%lx entries instead.\n", CountReturned); + TestStatus = FALSE; + i = AccountCount + 100; + } + + } else { + + printf("Failed\n"); + if (i < AccountCount -1 ) { + printf(" Expected STATUS_MORE_ENTRIES to be returned.\n"); + } else { + printf(" Expected STATUS_SUCCESS to be returned.\n"); + } + printf(" Received 0x%lx instead.\n", NtStatus); + printf(" Buffer = 0x%lx\n", (ULONG)Buffer); + printf(" CountReturned = 0x%lx\n", CountReturned); + TestStatus = FALSE; + i = AccountCount + 100; + } + + SamFreeMemory( Buffer ); + + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + i = AccountCount + 100; + } + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + i = AccountCount + 100; + + } + } + + if ( i == AccountCount) { + printf("Succeeded\n"); + } + + + + + printf(" Enumerate Aliases - large prefered length . . . . . . "); + + + EnumerationContext = 0; + NtStatus = SamEnumerateAliasesInDomain( + DomainHandle, + &EnumerationContext, + &Buffer, + 12000, // PreferedMaximumLength + &CountReturned + ); + AccountCount = CountReturned; // Save for future test + + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + if (NtStatus == STATUS_SUCCESS) { + + if (CountReturned > 1) { + printf("Succeeded\n"); + for (i=0; i<CountReturned; i++) { + printf(" Rid/Name(%ld): 0x%lx / %wZ\n",i, + ((PSAM_RID_ENUMERATION)(Buffer))[i].RelativeId, + &((PSAM_RID_ENUMERATION)(Buffer))[i].Name + ); + } + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + printf(" Expected several entries to be returned.\n"); + printf(" Received 0x%lx entries instead.\n", CountReturned); + TestStatus = FALSE; + } + + } else { + + printf("Failed\n"); + printf(" Expected STATUS_MORE_ENTRIES to be returned.\n"); + printf(" Received 0x%lx instead.\n", NtStatus); + printf(" Buffer = 0x%lx\n", (ULONG)Buffer); + printf(" CountReturned = 0x%lx\n", CountReturned); + TestStatus = FALSE; + } + + SamFreeMemory( Buffer ); + + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + + } + + + + + printf(" Enumerate Aliases - small prefered length . . . . . . "); + + + for ( i=0; i<AccountCount; i++) { + EnumerationContext = i; + NtStatus = SamEnumerateAliasesInDomain( + DomainHandle, + &EnumerationContext, + &Buffer, + 0, // PreferedMaximumLength + &CountReturned + ); + + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + if ( ((i >= AccountCount -1) && (NtStatus == STATUS_SUCCESS)) || + ((i <= AccountCount -1) && (NtStatus == STATUS_MORE_ENTRIES)) ) { + + if (CountReturned != 1) { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + printf(" Expected one entry to be returned.\n"); + printf(" Received 0x%lx entries instead.\n", CountReturned); + TestStatus = FALSE; + i = AccountCount + 100; + } + + } else { + + printf("Failed\n"); + if (i < AccountCount -1 ) { + printf(" Expected STATUS_MORE_ENTRIES to be returned.\n"); + } else { + printf(" Expected STATUS_SUCCESS to be returned.\n"); + } + printf(" Received 0x%lx instead.\n", NtStatus); + printf(" Buffer = 0x%lx\n", (ULONG)Buffer); + printf(" CountReturned = 0x%lx\n", CountReturned); + TestStatus = FALSE; + i = AccountCount + 100; + } + + SamFreeMemory( Buffer ); + + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + i = AccountCount + 100; + } + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + i = AccountCount + 100; + + } + } + + if ( i == AccountCount) { + printf("Succeeded\n"); + } + + + + + + printf(" Enumerate Users - large prefered length . . . . . . "); + + + EnumerationContext = 0; + NtStatus = SamEnumerateUsersInDomain( + DomainHandle, + &EnumerationContext, + 0, + &Buffer, + 12000, // PreferedMaximumLength + &CountReturned + ); + AccountCount = CountReturned; // Save for future test + + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + if (NtStatus == STATUS_SUCCESS) { + + if (CountReturned > 1) { + printf("Succeeded\n"); + for (i=0; i<CountReturned; i++) { + printf(" Rid/Name(%ld): 0x%lx / %wZ\n",i, + ((PSAM_RID_ENUMERATION)(Buffer))[i].RelativeId, + &((PSAM_RID_ENUMERATION)(Buffer))[i].Name + ); + } + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + printf(" Expected several entries to be returned.\n"); + printf(" Received 0x%lx entries instead.\n", CountReturned); + TestStatus = FALSE; + } + + } else { + + printf("Failed\n"); + printf(" Expected STATUS_MORE_ENTRIES to be returned.\n"); + printf(" Received 0x%lx instead.\n", NtStatus); + printf(" Buffer = 0x%lx\n", (ULONG)Buffer); + printf(" CountReturned = 0x%lx\n", CountReturned); + TestStatus = FALSE; + } + + SamFreeMemory( Buffer ); + + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + + } + + + + + printf(" Enumerate Users - small prefered length . . . . . . "); + + + for ( i=0; i<AccountCount; i++) { + EnumerationContext = i; + NtStatus = SamEnumerateUsersInDomain( + DomainHandle, + &EnumerationContext, + 0, + &Buffer, + 0, // PreferedMaximumLength + &CountReturned + ); + + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + if ( ((i >= AccountCount -1) && (NtStatus == STATUS_SUCCESS)) || + ((i <= AccountCount -1) && (NtStatus == STATUS_MORE_ENTRIES)) ) { + + if (CountReturned != 1) { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + printf(" Expected one entry to be returned.\n"); + printf(" Received 0x%lx entries instead.\n", CountReturned); + TestStatus = FALSE; + i = AccountCount + 100; + } + + } else { + + printf("Failed\n"); + if (i < AccountCount -1 ) { + printf(" Expected STATUS_MORE_ENTRIES to be returned.\n"); + } else { + printf(" Expected STATUS_SUCCESS to be returned.\n"); + } + printf(" Received 0x%lx instead.\n", NtStatus); + printf(" Buffer = 0x%lx\n", (ULONG)Buffer); + printf(" CountReturned = 0x%lx\n", CountReturned); + TestStatus = FALSE; + i = AccountCount + 100; + } + + SamFreeMemory( Buffer ); + + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + i = AccountCount + 100; + } + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + i = AccountCount + 100; + + } + } + + if ( i == AccountCount) { + printf("Succeeded\n"); + } + + + + + + + + + + + /////////////////////////////////////////////////////////////////////////// + // // + // Lookup Names/IDs Suite // + // // + /////////////////////////////////////////////////////////////////////////// + + + // LATER add alias search to lookup name suite..... + + + printf("\n"); + printf(" Lookup Names/IDs . . . . . . . . . . . . . . . . . . Suite\n"); + + + printf(" Lookup Names (all existing) . . . . . . . . . . . . . "); + + NtStatus = SamLookupNamesInDomain( + DomainHandle, + ALL_NAMES_COUNT, + &AllNames[0], + &LookedUpRids, + &LookedUpUses + ); + + + if (NT_SUCCESS(NtStatus)) { + ASSERT( LookedUpRids != NULL ); + ASSERT( LookedUpUses != NULL ); + + if ( + (LookedUpRids[0] == AllRids[0]) && (LookedUpUses[0] == AllUses[0]) + && + (LookedUpRids[1] == AllRids[1]) && (LookedUpUses[1] == AllUses[1]) + && + (LookedUpRids[2] == AllRids[2]) && (LookedUpUses[2] == AllUses[2]) + ) { + + + printf("Succeeded\n"); + + + } else { + printf("Failed\n"); + printf(" Rids or Uses dont match expected values.\n"); + printf(" Expected Rids: 0x%lx, 0x%lx, 0x%lx\n", + AllRids[0], AllRids[1], AllRids[2]); + printf(" Received Rids: 0x%lx, 0x%lx, 0x%lx\n", + LookedUpRids[0], LookedUpRids[1], LookedUpRids[2]); + printf(" Expected Uses: 0x%lx, 0x%lx, 0x%lx\n", + AllUses[0], AllUses[1], AllUses[2]); + printf(" Received Uses: 0x%lx, 0x%lx, 0x%lx\n", + LookedUpUses[0], LookedUpUses[1], LookedUpUses[2]); + TestStatus = FALSE; + } + + + SamFreeMemory( LookedUpRids ); + SamFreeMemory( LookedUpUses ); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + + printf(" Lookup Names (Some existing) . . . . . . . . . . . . "); + + NtStatus = SamLookupNamesInDomain( + DomainHandle, + SOME_NAMES_COUNT, + &SomeNames[0], + &LookedUpRids, + &LookedUpUses + ); + + + if (NtStatus == STATUS_SOME_NOT_MAPPED) { + ASSERT( LookedUpRids != NULL ); + ASSERT( LookedUpUses != NULL ); + + if ( + (LookedUpRids[0] == SomeRids[0]) && (LookedUpUses[0] == SomeUses[0]) + && + (LookedUpRids[1] == SomeRids[1]) && (LookedUpUses[1] == SomeUses[1]) + && + (LookedUpRids[2] == SomeRids[2]) && (LookedUpUses[2] == SomeUses[2]) + && + (LookedUpRids[3] == SomeRids[3]) && (LookedUpUses[3] == SomeUses[3]) + && + (LookedUpRids[4] == SomeRids[4]) && (LookedUpUses[4] == SomeUses[4]) + && + (LookedUpRids[5] == SomeRids[5]) && (LookedUpUses[5] == SomeUses[5]) + && + (LookedUpRids[6] == SomeRids[6]) && (LookedUpUses[6] == SomeUses[6]) + ) { + + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Rids or Uses dont match expected values.\n"); + printf(" Expected Rids: 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx\n", + SomeRids[0], SomeRids[1], SomeRids[2], SomeRids[3], SomeRids[4], SomeRids[5], SomeRids[6]); + printf(" Received Rids: 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx\n", + LookedUpRids[0], LookedUpRids[1], LookedUpRids[2], LookedUpRids[3], LookedUpRids[4], LookedUpRids[5], LookedUpRids[6]); + printf(" Expected Uses: 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx\n", + SomeUses[0], SomeUses[1], SomeUses[2], SomeUses[3], SomeUses[4], SomeUses[5], SomeUses[6]); + printf(" Received Uses: 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx\n", + LookedUpUses[0], LookedUpUses[1], LookedUpUses[2], LookedUpUses[3], LookedUpUses[4], LookedUpUses[5], LookedUpUses[2]); + TestStatus = FALSE; + } + + + SamFreeMemory( LookedUpRids ); + SamFreeMemory( LookedUpUses ); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + printf(" Lookup Names (None existing) . . . . . . . . . . . . "); + + NtStatus = SamLookupNamesInDomain( + DomainHandle, + NO_NAMES_COUNT, + &NoNames[0], + &LookedUpRids, + &LookedUpUses + ); + + + if (NtStatus == STATUS_NONE_MAPPED) { + ASSERT( LookedUpRids == NULL ); + ASSERT( LookedUpUses == NULL ); + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + printf(" Lookup SIDs (all existing) . . . . . . . . . . . . . "); + + NtStatus = SamLookupIdsInDomain( + DomainHandle, + ALL_NAMES_COUNT, + &AllRids[0], + &LookedUpNames, + &LookedUpUses + ); + + + if (NT_SUCCESS(NtStatus)) { + ASSERT( LookedUpUses != NULL ); + ASSERT( LookedUpNames != NULL ); + ASSERT( LookedUpNames[0].Buffer != NULL ); + ASSERT( LookedUpNames[1].Buffer != NULL ); + ASSERT( LookedUpNames[2].Buffer != NULL ); + + if ( + (LookedUpUses[0] == AllUses[0]) && + (LookedUpUses[1] == AllUses[1]) && + (LookedUpUses[2] == AllUses[2]) && + !RtlCompareString( (PSTRING)&LookedUpNames[0], (PSTRING)&AllNames[0], TRUE ) && + !RtlCompareString( (PSTRING)&LookedUpNames[1], (PSTRING)&AllNames[1], TRUE ) && + !RtlCompareString( (PSTRING)&LookedUpNames[2], (PSTRING)&AllNames[2], TRUE ) + ) { + + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Names or Uses dont match expected values.\n"); + printf(" Expected Name[0]: %wZ\n", &AllNames[0] ); + printf(" Received Name[0]: %wZ\n", &LookedUpNames[0] ); + printf(" Expected Name[1]: %wZ\n", &AllNames[1] ); + printf(" Received Name[1]: %wZ\n", &LookedUpNames[1] ); + printf(" Expected Name[2]: %wZ\n", &AllNames[2] ); + printf(" Received Name[2]: %wZ\n", &LookedUpNames[2] ); + + printf(" Expected Uses: 0x%lx, 0x%lx, 0x%lx\n", + AllUses[0], AllUses[1], AllUses[2]); + printf(" Received Uses: 0x%lx, 0x%lx, 0x%lx\n", + LookedUpUses[0], LookedUpUses[1], LookedUpUses[2]); + TestStatus = FALSE; + } + + + SamFreeMemory( LookedUpUses ); + SamFreeMemory( LookedUpNames ); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + + printf(" Lookup SIDs (Some existing) . . . . . . . . . . . . . "); + + NtStatus = SamLookupIdsInDomain( + DomainHandle, + SOME_NAMES_COUNT, + &SomeRids[0], + &LookedUpNames, + &LookedUpUses + ); + + + if (NtStatus == STATUS_SOME_NOT_MAPPED) { + ASSERT( LookedUpUses != NULL ); + ASSERT( LookedUpNames != NULL ); + ASSERT( LookedUpNames[0].Buffer != NULL ); + ASSERT( LookedUpNames[1].Buffer != NULL ); + ASSERT( LookedUpNames[2].Buffer == NULL ); // Unknown + ASSERT( LookedUpNames[3].Buffer == NULL ); // Unknown + ASSERT( LookedUpNames[4].Buffer == NULL ); // Unknown + ASSERT( LookedUpNames[5].Buffer != NULL ); + ASSERT( LookedUpNames[6].Buffer == NULL ); // Unknown + + if ( + (LookedUpUses[0] == SomeUses[0]) && + (LookedUpUses[1] == SomeUses[1]) && + (LookedUpUses[2] == SomeUses[2]) && + !RtlCompareString( (PSTRING)&LookedUpNames[0], (PSTRING)&SomeNames[0], TRUE ) && + !RtlCompareString( (PSTRING)&LookedUpNames[1], (PSTRING)&SomeNames[1], TRUE ) && + !RtlCompareString( (PSTRING)&LookedUpNames[5], (PSTRING)&SomeNames[5], TRUE ) + ) { + + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Names or Uses dont match expected values.\n"); + printf(" Expected Name[0]: %wZ\n", &SomeNames[0] ); + printf(" Received Name[0]: %wZ\n", &LookedUpNames[0] ); + printf(" Expected Name[1]: %wZ\n", &SomeNames[1] ); + printf(" Received Name[1]: %wZ\n", &LookedUpNames[1] ); + printf(" Name[2]: (Unknown)\n"); + printf(" Name[3]: (Unknown)\n"); + printf(" Name[4]: (Unknown)\n"); + printf(" Expected Name[5]: %wZ\n", &SomeNames[5] ); + printf(" Received Name[5]: %wZ\n", &LookedUpNames[5] ); + printf(" Name[6]: (Unknown)\n"); + + printf(" Expected Uses: 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx\n", + SomeUses[0], SomeUses[1], SomeUses[2], SomeUses[3], SomeUses[4], SomeUses[5], SomeUses[6]); + printf(" Received Uses: 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx\n", + LookedUpUses[0], LookedUpUses[1], LookedUpUses[2], LookedUpUses[3], LookedUpUses[4], LookedUpUses[5], LookedUpUses[2]); + TestStatus = FALSE; + } + + + SamFreeMemory( LookedUpUses ); + SamFreeMemory( LookedUpNames ); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + + printf(" Lookup SIDs (None existing) . . . . . . . . . . . . . "); + + NtStatus = SamLookupIdsInDomain( + DomainHandle, + NO_NAMES_COUNT, + &NoRids[0], + &LookedUpNames, + &LookedUpUses + ); + + + if (NtStatus == STATUS_NONE_MAPPED) { + ASSERT( LookedUpUses == NULL ); + ASSERT( LookedUpNames == NULL ); + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + + + + + return TestStatus; +} + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Group Object Test Suite // +// // +/////////////////////////////////////////////////////////////////////////////// + + +BOOLEAN +GroupTestSuite( + HANDLE DomainHandle, + ULONG Pass + ) + +{ + NTSTATUS NtStatus, IgnoreStatus; + HANDLE GroupHandle1, GroupHandle2, UserHandle1; + ULONG CountReturned, NameLength, i, MemberCount; + ULONG UserRid, GroupRid; + PVOID Buffer, Buffer1, Buffer2; + SAM_ENUMERATE_HANDLE EnumerationContext; + PULONG Members, Attributes; + PSID_NAME_USE LookedUpUses; + PULONG LookedUpRids; + UNICODE_STRING AccountNames[10], AccountName; + STRING AccountNameAnsi; + + BOOLEAN IndividualTestSucceeded, DeleteUser; + BOOLEAN TestStatus = TRUE; + + + if (Pass == 1) { + // + // This test suite assumes that lookup and enumeration API funciton + // properly. + // + + printf("\n"); + printf("\n"); + printf(" Group (Pass #1) . . . . . . . . . . . . . . . . . . . Test\n"); + + /////////////////////////////////////////////////////////////////////////// + // // + // Open Group Suite // + // // + /////////////////////////////////////////////////////////////////////////// + + printf(" Open Group . . . . . . . . . . . . . . . . . . . . . Suite\n"); + printf(" Open Groups . . . . . . . . . . . . . . . . . . . . . "); + IndividualTestSucceeded = TRUE; + EnumerationContext = 0; + NtStatus = SamEnumerateGroupsInDomain( + DomainHandle, + &EnumerationContext, + &Buffer, + 12000, // PreferedMaximumLength + &CountReturned + ); + + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(Buffer != NULL); + ASSERT(CountReturned > 0); + + for (i=0; i<CountReturned; i++) { + + NtStatus = SamOpenGroup( + DomainHandle, + GROUP_ALL_ACCESS, + ((PSAM_RID_ENUMERATION)(Buffer))[i].RelativeId, + &GroupHandle1 + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SamOpenGroup( + DomainHandle, + GENERIC_READ, + ((PSAM_RID_ENUMERATION)(Buffer))[i].RelativeId, + &GroupHandle2 + ); + + if (NT_SUCCESS(NtStatus)) { + IgnoreStatus = SamCloseHandle( GroupHandle2 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + printf(" Failed opening group second time.\n"); + printf(" Rid of account is: 0x%lx\n", + ((PSAM_RID_ENUMERATION)(Buffer))[i].RelativeId); + printf(" Name of account is: %wZ\n", + &((PSAM_RID_ENUMERATION)(Buffer))[i].Name ); + TestStatus = FALSE; + IndividualTestSucceeded = FALSE; + } + + IgnoreStatus = SamCloseHandle( GroupHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + } else { + + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + printf(" Failed opening group for first time.\n"); + printf(" Rid of account is: 0x%lx\n", + ((PSAM_RID_ENUMERATION)(Buffer))[i].RelativeId); + printf(" Name of account is: %wZ\n", + &((PSAM_RID_ENUMERATION)(Buffer))[i].Name ); + TestStatus = FALSE; + IndividualTestSucceeded = FALSE; + } + + if (!IndividualTestSucceeded) { + printf(" "); + } + } + + + SamFreeMemory( Buffer ); + if (IndividualTestSucceeded) { + printf("Succeeded\n"); + } + + + + /////////////////////////////////////////////////////////////////////////// + // // + // Query Group Suite // + // // + /////////////////////////////////////////////////////////////////////////// + + printf("\n"); + printf(" Query Group . . . . . . . . . . . . . . . . . . . . . Suite\n"); + + printf(" Query Group General Information . . . . . . . . . . . "); + + + NtStatus = SamOpenGroup( + DomainHandle, + GROUP_READ_INFORMATION, + DOMAIN_GROUP_RID_USERS, + &GroupHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + Buffer = NULL; + NtStatus = SamQueryInformationGroup( + GroupHandle1, + GroupGeneralInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + + if ( (((GROUP_GENERAL_INFORMATION *)Buffer)->Name.MaximumLength > 0) && + (((GROUP_GENERAL_INFORMATION *)Buffer)->Name.Buffer != NULL) ) { + + printf("Succeeded\n"); + + printf(" Member Count is: 0x%lx\n", + (((GROUP_GENERAL_INFORMATION *)Buffer)->MemberCount) ); + printf(" Attributes are: 0x%lx\n", + (((GROUP_GENERAL_INFORMATION *)Buffer)->Attributes) ); + printf(" Group Name is: %wZ\n", + &(((GROUP_GENERAL_INFORMATION *)Buffer)->Name) ); + + + + } else { + printf("Failed\n"); + printf(" Group Name not returned.\n"); + TestStatus = FALSE; + } + SamFreeMemory( Buffer ); + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + IgnoreStatus = SamCloseHandle( GroupHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + + + printf(" Query Group Name Information . . . . . . . . . . . . "); + + + NtStatus = SamOpenGroup( + DomainHandle, + GROUP_READ_INFORMATION, + DOMAIN_GROUP_RID_USERS, + &GroupHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + Buffer = NULL; + NtStatus = SamQueryInformationGroup( + GroupHandle1, + GroupNameInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + + if ( (((GROUP_NAME_INFORMATION *)Buffer)->Name.MaximumLength > 0) && + (((GROUP_NAME_INFORMATION *)Buffer)->Name.Buffer != NULL) ) { + + printf("Succeeded\n"); + + printf(" Group Name is: %wZ\n", + &(((GROUP_NAME_INFORMATION *)Buffer)->Name) ); + + + + } else { + printf("Failed\n"); + printf(" Group Name not returned.\n"); + TestStatus = FALSE; + } + SamFreeMemory( Buffer ); + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + IgnoreStatus = SamCloseHandle( GroupHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + + + printf(" Query Group Admin Comment Information . . . . . . . . "); + + + NtStatus = SamOpenGroup( + DomainHandle, + GROUP_READ_INFORMATION, + DOMAIN_GROUP_RID_USERS, + &GroupHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + Buffer = NULL; + NtStatus = SamQueryInformationGroup( + GroupHandle1, + GroupAdminCommentInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + + if ( (((GROUP_ADM_COMMENT_INFORMATION *)Buffer)->AdminComment.MaximumLength >= 0) ) { + + printf("Succeeded\n"); + + printf(" Group Admin Comment is: %wZ\n", + &(((GROUP_ADM_COMMENT_INFORMATION *)Buffer)->AdminComment) ); + + + + } else { + printf("Failed\n"); + printf(" Group Admin Comment not returned.\n"); + TestStatus = FALSE; + } + SamFreeMemory( Buffer ); + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + IgnoreStatus = SamCloseHandle( GroupHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + + + printf(" Query Group Attribute Information . . . . . . . . . . "); + + + NtStatus = SamOpenGroup( + DomainHandle, + GROUP_READ_INFORMATION, + DOMAIN_GROUP_RID_USERS, + &GroupHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + Buffer = NULL; + NtStatus = SamQueryInformationGroup( + GroupHandle1, + GroupAttributeInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + + + printf("Succeeded\n"); + + printf(" Attributes are: 0x%lx\n", + (((GROUP_ATTRIBUTE_INFORMATION *)Buffer)->Attributes) ); + + + SamFreeMemory( Buffer ); + + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + IgnoreStatus = SamCloseHandle( GroupHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + + + /////////////////////////////////////////////////////////////////////////// + // // + // Get Members Of Group Suite // + // // + /////////////////////////////////////////////////////////////////////////// + + printf("\n"); + printf(" Get Members . . . . . . . . . . . . . . . . . . . . . Suite\n"); + + printf(" Get Members of Well-Known Account . . . . . . . . . . "); + + NtStatus = SamOpenGroup( + DomainHandle, + GROUP_LIST_MEMBERS, + DOMAIN_GROUP_RID_USERS, + &GroupHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + Buffer = NULL; + NtStatus = SamGetMembersInGroup( + GroupHandle1, + &Members, + &Attributes, + &MemberCount + ); + if (NT_SUCCESS(NtStatus)) { + if (Members != NULL || Attributes != NULL) { + + printf("Succeeded\n"); + + + printf(" Member Count: %d Users\n", MemberCount); + for ( i=0; i<MemberCount; i++) { + + printf(" User[%d] Rid/Attributes: 0x%lx/0x%lx\n", + i, Members[i], Attributes[i]); + + + } + + SamFreeMemory( Members ); + SamFreeMemory( Attributes ); + + + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + IgnoreStatus = SamCloseHandle( GroupHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + + printf(" Get Members of Empty Group. . . . . . . . . . . . . . "); + + // + // This group was created earlier in the test + // + + RtlInitString( &AccountNameAnsi, GROUP_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountNames[0], &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + NtStatus = SamLookupNamesInDomain( + DomainHandle, + 1, + &AccountNames[0], + &LookedUpRids, + &LookedUpUses + ); + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(LookedUpUses[0] == SidTypeGroup); + RtlFreeUnicodeString( &AccountNames[0] ); + + + + GroupHandle1 = NULL; + + NtStatus = SamOpenGroup( DomainHandle, GROUP_LIST_MEMBERS, LookedUpRids[0], &GroupHandle1 ); + TST_SUCCESS_ASSERT(NtStatus); + SamFreeMemory( LookedUpUses ); SamFreeMemory( LookedUpRids ); + + NtStatus = SamGetMembersInGroup( + GroupHandle1, + &Members, + &Attributes, + &MemberCount + ); + if (NT_SUCCESS(NtStatus)) { + if (MemberCount == 0) { + + printf("Succeeded\n"); + + + + + } else { + printf("Failed\n"); + printf(" Buffer addresses set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + printf(" Member Count: %d\n", MemberCount); + for ( i=0; i<MemberCount; i++) { + + printf(" User[%d] Rid/Attributes: 0x%lx/0x%lx\n", + i, Members[i], Attributes[i]); + } + + SamFreeMemory( Members ); + SamFreeMemory( Attributes ); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + IgnoreStatus = SamCloseHandle( GroupHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + /////////////////////////////////////////////////////////////////////////// + // // + // Set Group Suite (pass 1) // + // // + /////////////////////////////////////////////////////////////////////////// + + printf("\n"); + printf(" Set Group . . . . . . . . . . . . . . . . . . . . . . Suite\n"); + + + printf(" Set Attribute . . . . . . . . . . . . . . . . . . . . "); + NtStatus = SamOpenGroup( + DomainHandle, + GROUP_WRITE_ACCOUNT | GROUP_READ_INFORMATION, + DOMAIN_GROUP_RID_USERS, + &GroupHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + Buffer1 = NULL; + NtStatus = SamQueryInformationGroup( + GroupHandle1, + GroupAttributeInformation, + &Buffer1 + ); + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(Buffer1 != NULL); + + // + // Change the value and write it back + // + + ((GROUP_ATTRIBUTE_INFORMATION *)Buffer1)->Attributes ^= + SE_GROUP_ENABLED_BY_DEFAULT; + + + NtStatus = SamSetInformationGroup( + GroupHandle1, + GroupAttributeInformation, + Buffer1 + ); + if (NT_SUCCESS(NtStatus)) { + + // + // Check the written value to make sure it stuck + // + + Buffer2 = NULL; + NtStatus = SamQueryInformationGroup( + GroupHandle1, + GroupAttributeInformation, + &Buffer2 + ); + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(Buffer2 != NULL); + + if (((GROUP_ATTRIBUTE_INFORMATION *)Buffer1)->Attributes == + ((GROUP_ATTRIBUTE_INFORMATION *)Buffer2)->Attributes ) { + + printf("Succeeded\n"); + + SamFreeMemory( Buffer2 ); + + } else { + printf("Failed\n"); + printf(" Returned Value Doesn't Match Set Value.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + SamFreeMemory( Buffer1 ); + IgnoreStatus = SamCloseHandle( GroupHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + + + + printf(" Set Admin Comment . . . . . . . . . . . . . . . . . . "); + + NtStatus = SamOpenGroup( + DomainHandle, + GROUP_WRITE_ACCOUNT | GROUP_READ_INFORMATION, + DOMAIN_GROUP_RID_USERS, + &GroupHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + // + // Get the current value... + // + + Buffer1 = NULL; + NtStatus = SamQueryInformationGroup( + GroupHandle1, + GroupAdminCommentInformation, + &Buffer1 + ); + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(Buffer1 != NULL); + + + // + // Change the field to a new value and write it out. + // + + NameLength = ((GROUP_ADM_COMMENT_INFORMATION *)Buffer1)->AdminComment.Length; + if ( NameLength == DummyString1.Length ) { + ((GROUP_ADM_COMMENT_INFORMATION *)Buffer1)->AdminComment = DummyString2; + } else { + ((GROUP_ADM_COMMENT_INFORMATION *)Buffer1)->AdminComment = DummyString1; + } + + NtStatus = SamSetInformationGroup( + GroupHandle1, + GroupAdminCommentInformation, + Buffer1 + ); + if ( NT_SUCCESS(NtStatus) ) { + + // + // Now check that the change was really made... + // + + Buffer2 = NULL; + NtStatus = SamQueryInformationGroup( + GroupHandle1, + GroupAdminCommentInformation, + &Buffer2 + ); + ASSERT(NT_SUCCESS( NtStatus ) ); + if ( + !RtlCompareString( + (PSTRING)&((GROUP_ADM_COMMENT_INFORMATION *)Buffer1)->AdminComment, + (PSTRING)&((GROUP_ADM_COMMENT_INFORMATION *)Buffer2)->AdminComment, + TRUE) + ) { + + printf("Succeeded\n"); + + } else { + + printf("Failed\n"); + printf(" Value queried doesn't match value written\n"); + printf(" Value Written is %wZ\n", + (PUNICODE_STRING)&((GROUP_ADM_COMMENT_INFORMATION *)Buffer1)->AdminComment); + printf(" Value Retrieved is %wZ\n", + (PUNICODE_STRING)&((GROUP_ADM_COMMENT_INFORMATION *)Buffer2)->AdminComment); + + TestStatus = FALSE; + + } + + SamFreeMemory( Buffer1 ); + SamFreeMemory( Buffer2 ); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + SamFreeMemory( Buffer1 ); + + } + + + + + + + } // END PASS #1 + if (Pass == 2) { + + printf("\n"); + printf("\n"); + printf(" Group (Pass #2) . . . . . . . . . . . . . . . . . . . Test\n"); + + /////////////////////////////////////////////////////////////////////////// + // // + // Delete Group Suite // + // // + /////////////////////////////////////////////////////////////////////////// + + printf("\n"); + printf(" Delete Group . . . . . . . . . . . . . . . . . . . . Suite\n"); + + printf(" Delete Normal Group . . . . . . . . . . . . . . . . . "); + + // + // This group was created in pass #1 + // + + RtlInitString( &AccountNameAnsi, GROUP_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountNames[0], &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + NtStatus = SamLookupNamesInDomain( + DomainHandle, + 1, + &AccountNames[0], + &LookedUpRids, + &LookedUpUses + ); + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(LookedUpUses[0] == SidTypeGroup); + RtlFreeUnicodeString( &AccountNames[0] ); + + + + GroupHandle1 = NULL; + + NtStatus = SamOpenGroup( DomainHandle, DELETE, LookedUpRids[0], &GroupHandle1 ); + TST_SUCCESS_ASSERT(NtStatus); + SamFreeMemory( LookedUpUses ); SamFreeMemory( LookedUpRids ); + + NtStatus = SamDeleteGroup( GroupHandle1 ); + if (NT_SUCCESS(NtStatus)) { + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + + printf(" Delete Well Known Group . . . . . . . . . . . . . . . "); + + GroupHandle1 = NULL; + + NtStatus = SamOpenGroup( DomainHandle, DELETE, DOMAIN_GROUP_RID_USERS, &GroupHandle1 ); + TST_SUCCESS_ASSERT(NtStatus); + + NtStatus = SamDeleteGroup( GroupHandle1 ); + if (NtStatus == STATUS_SPECIAL_ACCOUNT) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + NtStatus = SamCloseHandle( GroupHandle1 ); + ASSERT(NT_SUCCESS(NtStatus)); + + + + + + + + + printf(" Delete Primary Group Of A User. . . . . . . . . . . . "); + + // + // Make a user (might already exist) + // Make a group + // Make the group the user's primary group + // Attempt to delete the group + // Change the user so the group isn't the primary group + // delete the group + // If we created the user, delete it. + + // + // The following user might already exist (from earlier in the test) + // + + RtlInitString( &AccountNameAnsi, USER_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + //InitializeObjectAttributes( &ObjectAttributes, &AccountName, 0, 0, NULL ); + + + UserRid = 0; + UserHandle1 = NULL; + NtStatus = SamCreateUserInDomain( + DomainHandle, + &AccountName, + USER_ALL_ACCESS, + &UserHandle1, + &UserRid + ); + RtlFreeUnicodeString( &AccountName ); + DeleteUser = TRUE; + if (NtStatus == STATUS_USER_EXISTS) { + DeleteUser = FALSE; + RtlInitString( &AccountNameAnsi, USER_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountNames[0], &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + NtStatus = SamLookupNamesInDomain( + DomainHandle, + 1, + &AccountNames[0], + &LookedUpRids, + &LookedUpUses + ); + RtlFreeUnicodeString( &AccountNames[0] ); + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(LookedUpUses[0] == SidTypeUser); + UserRid = LookedUpRids[0]; + NtStatus = SamOpenUser( + DomainHandle, + USER_ALL_ACCESS, + UserRid, + &UserHandle1); + SamFreeMemory( LookedUpUses ); SamFreeMemory( LookedUpRids ); + } + + ASSERT(NT_SUCCESS(NtStatus)); + + // + // create the group + // + + RtlInitString( &AccountNameAnsi, GROUP_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + //InitializeObjectAttributes( &ObjectAttributes, &AccountName, 0, 0, NULL ); + + GroupRid = 0; + GroupHandle1 = NULL; + NtStatus = SamCreateGroupInDomain( + DomainHandle, + &AccountName, + GROUP_ALL_ACCESS, + &GroupHandle1, + &GroupRid + ); + RtlFreeUnicodeString( &AccountName ); + ASSERT(NT_SUCCESS(NtStatus)); + + // + // Make the user a member of this group + // + + NtStatus = SamAddMemberToGroup( + GroupHandle1, + UserRid, + SE_GROUP_MANDATORY | + SE_GROUP_ENABLED_BY_DEFAULT | + SE_GROUP_ENABLED + ); + ASSERT(NT_SUCCESS(NtStatus)); + + + + + // + // Now try to delete the group + // + + NtStatus = SamDeleteGroup( GroupHandle1 ); + if (NtStatus == STATUS_MEMBER_IN_GROUP) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + // + // Now get rid of the group and possibly the user account + // + + NtStatus = SamRemoveMemberFromGroup(GroupHandle1, UserRid); + ASSERT(NT_SUCCESS(NtStatus)); + + + NtStatus = SamDeleteGroup( GroupHandle1 ); + ASSERT(NT_SUCCESS(NtStatus)); + + if (DeleteUser == TRUE) { + NtStatus = SamDeleteUser( UserHandle1 ); + ASSERT(NT_SUCCESS(NtStatus)); + } else { + NtStatus = SamCloseHandle( UserHandle1 ); + ASSERT(NT_SUCCESS(NtStatus)); + } + + + + /////////////////////////////////////////////////////////////////////////// + // // + // Add/Remove Member Suite // + // // + /////////////////////////////////////////////////////////////////////////// + + printf("\n"); + printf(" Add/Remove Member Suite . . . . . . . . . . . . . . . Suite\n"); + + printf(" Add Member . . . . . . . . . . . . . . . . . . . . . "); + + // + // This test sets things up for the next test + // + + // + // The following user might already exist (from earlier in the test) + // + + RtlInitString( &AccountNameAnsi, USER_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + //InitializeObjectAttributes( &ObjectAttributes, &AccountName, 0, 0, NULL ); + + UserRid = 0; + UserHandle1 = NULL; + NtStatus = SamCreateUserInDomain( + DomainHandle, + &AccountName, + USER_ALL_ACCESS, + &UserHandle1, + &UserRid + ); + RtlFreeUnicodeString( &AccountName ); + DeleteUser = TRUE; + if (NtStatus == STATUS_USER_EXISTS) { + DeleteUser = FALSE; + RtlInitString( &AccountNameAnsi, USER_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountNames[0], &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + NtStatus = SamLookupNamesInDomain( + DomainHandle, + 1, + &AccountNames[0], + &LookedUpRids, + &LookedUpUses + ); + RtlFreeUnicodeString( &AccountNames[0] ); + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(LookedUpUses[0] == SidTypeUser); + UserRid = LookedUpRids[0]; + NtStatus = SamOpenUser( + DomainHandle, + USER_ALL_ACCESS, + UserRid, + &UserHandle1); + SamFreeMemory( LookedUpUses ); SamFreeMemory( LookedUpRids ); + } + + ASSERT(NT_SUCCESS(NtStatus)); + + + // + // create the group + // + + RtlInitString( &AccountNameAnsi, GROUP_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + //InitializeObjectAttributes( &ObjectAttributes, &AccountName, 0, 0, NULL ); + + GroupRid = 0; + GroupHandle1 = NULL; + NtStatus = SamCreateGroupInDomain( + DomainHandle, + &AccountName, + GROUP_ALL_ACCESS, + &GroupHandle1, + &GroupRid + ); + RtlFreeUnicodeString( &AccountName ); + ASSERT(NT_SUCCESS(NtStatus)); + + // + // Make the user a member of this group + // + + NtStatus = SamAddMemberToGroup( + GroupHandle1, + UserRid, + SE_GROUP_MANDATORY | + SE_GROUP_ENABLED_BY_DEFAULT | + SE_GROUP_ENABLED + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SamGetMembersInGroup( + GroupHandle1, + &Members, + &Attributes, + &MemberCount + ); + ASSERT(NT_SUCCESS(NtStatus)); + + NtStatus = STATUS_MEMBER_NOT_IN_GROUP; + for ( i=0; i<MemberCount; i++) { + if (Members[i] == UserRid) { + NtStatus = STATUS_SUCCESS; + break; + } + } + + if (NT_SUCCESS(NtStatus)) { + if (Attributes[i] == SE_GROUP_MANDATORY | + SE_GROUP_ENABLED_BY_DEFAULT | + SE_GROUP_ENABLED) { + printf("Succeeded\n"); + } else { + printf("Failed\n"); + printf("Member Added but attributes don't match expected value.\n"); + printf("Expected value: 0x%lx\n",(SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_ENABLED)); + printf("Retrieved value: 0x%lx\n",Attributes[i]); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf("Service returned SUCCESS, but user not in member list for group.\n"); + TestStatus = FALSE; + } + + + if (Members != NULL) { + SamFreeMemory( Members ); + SamFreeMemory( Attributes ); + } + + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + + printf(" Remove Member . . . . . . . . . . . . . . . . . . . . "); + + // + // The previous test sets this one up. + // + + // + // Now try to remove the user from the group + // + + NtStatus = SamRemoveMemberFromGroup(GroupHandle1, UserRid); + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SamGetMembersInGroup( + GroupHandle1, + &Members, + &Attributes, + &MemberCount + ); + ASSERT(NT_SUCCESS(NtStatus)); + + for ( i=0; i<MemberCount; i++) { + if (Members[i] == UserRid) { + NtStatus = STATUS_MEMBER_IN_GROUP; + break; + } + } + + if (NT_SUCCESS(NtStatus)) { + printf("Succeeded\n"); + } else { + printf("Failed\n"); + printf("Service returned SUCCESS, but user still in member list for group.\n"); + TestStatus = FALSE; + } + + + SamFreeMemory( Members ); + SamFreeMemory( Attributes ); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + // + // and clean up the user and group accounts + // + + NtStatus = SamDeleteGroup( GroupHandle1 ); + ASSERT(NT_SUCCESS(NtStatus)); + + if (DeleteUser == TRUE) { + NtStatus = SamDeleteUser( UserHandle1 ); + ASSERT(NT_SUCCESS(NtStatus)); + } else { + NtStatus = SamCloseHandle( UserHandle1 ); + ASSERT(NT_SUCCESS(NtStatus)); + } + + + + + + + printf(" Add Non-Existant Member . . . . . . . . . . . . . . . "); + + // + // create the group + // + + RtlInitString( &AccountNameAnsi, GROUP_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + //InitializeObjectAttributes( &ObjectAttributes, &AccountName, 0, 0, NULL ); + + GroupRid = 0; + GroupHandle1 = NULL; + NtStatus = SamCreateGroupInDomain( + DomainHandle, + &AccountName, + GROUP_ALL_ACCESS, + &GroupHandle1, + &GroupRid + ); + RtlFreeUnicodeString( &AccountName ); + ASSERT(NT_SUCCESS(NtStatus)); + + // + // Specify a non-existant user be added to this group + // + + UserRid = 30732579; // Pretty sure this user doesn't exist. + NtStatus = SamAddMemberToGroup( + GroupHandle1, + UserRid, + SE_GROUP_MANDATORY | + SE_GROUP_ENABLED_BY_DEFAULT | + SE_GROUP_ENABLED + ); + + if (NtStatus == STATUS_NO_SUCH_USER) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + NtStatus = SamDeleteGroup( GroupHandle1 ); + ASSERT(NT_SUCCESS(NtStatus)); + + + + + printf(" Remove Non-existant Member . . . . . . . . . . . . . "); + + // + // create the group + // + + RtlInitString( &AccountNameAnsi, GROUP_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + //InitializeObjectAttributes( &ObjectAttributes, &AccountName, 0, 0, NULL ); + + GroupRid = 0; + GroupHandle1 = NULL; + NtStatus = SamCreateGroupInDomain( + DomainHandle, + &AccountName, + GROUP_ALL_ACCESS, + &GroupHandle1, + &GroupRid + ); + RtlFreeUnicodeString( &AccountName ); + ASSERT(NT_SUCCESS(NtStatus)); + + // + // Specify a non-existant user be removed from this group + // + + UserRid = 30732579; // Pretty sure this user doesn't exist. + NtStatus = SamRemoveMemberFromGroup( GroupHandle1, UserRid ); + + if (NtStatus == STATUS_NO_SUCH_USER) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + NtStatus = SamDeleteGroup( GroupHandle1 ); + ASSERT(NT_SUCCESS(NtStatus)); + + + + + printf(" Remove Primary Group Of Member . . . . . . . . . . . "); + + + // + // Make a user (might already exist) + // Make a group + // Make the group the user's primary group + // Attempt to remove the group (should fail) + // Change the user so the group isn't the primary group + // remove the group + // delete the group + // If we created the user, delete it. + + // + // The following user might already exist (from earlier in the test) + // + + RtlInitString( &AccountNameAnsi, USER_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + //InitializeObjectAttributes( &ObjectAttributes, &AccountName, 0, 0, NULL ); + + UserRid = 0; + UserHandle1 = NULL; + NtStatus = SamCreateUserInDomain( + DomainHandle, + &AccountName, + USER_ALL_ACCESS, + &UserHandle1, + &UserRid + ); + RtlFreeUnicodeString( &AccountName ); + DeleteUser = TRUE; + if (NtStatus == STATUS_USER_EXISTS) { + DeleteUser = FALSE; + RtlInitString( &AccountNameAnsi, USER_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountNames[0], &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + NtStatus = SamLookupNamesInDomain( + DomainHandle, + 1, + &AccountNames[0], + &LookedUpRids, + &LookedUpUses + ); + RtlFreeUnicodeString( &AccountNames[0] ); + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(LookedUpUses[0] == SidTypeUser); + UserRid = LookedUpRids[0]; + NtStatus = SamOpenUser( + DomainHandle, + USER_ALL_ACCESS, + UserRid, + &UserHandle1); + SamFreeMemory( LookedUpUses ); SamFreeMemory( LookedUpRids ); + } + + ASSERT(NT_SUCCESS(NtStatus)); + + + // + // create the group + // + + RtlInitString( &AccountNameAnsi, GROUP_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + //InitializeObjectAttributes( &ObjectAttributes, &AccountName, 0, 0, NULL ); + + GroupRid = 0; + GroupHandle1 = NULL; + NtStatus = SamCreateGroupInDomain( + DomainHandle, + &AccountName, + GROUP_ALL_ACCESS, + &GroupHandle1, + &GroupRid + ); + RtlFreeUnicodeString( &AccountName ); + ASSERT(NT_SUCCESS(NtStatus)); + + // + // Make the user a member of this group + // + + NtStatus = SamAddMemberToGroup( + GroupHandle1, + UserRid, + SE_GROUP_MANDATORY | + SE_GROUP_ENABLED_BY_DEFAULT | + SE_GROUP_ENABLED + ); + ASSERT(NT_SUCCESS(NtStatus)); + + + // + // Set the user's primary group Id to be this group + // + + NtStatus = SamSetInformationUser( + UserHandle1, + UserPrimaryGroupInformation, + &GroupRid + ); + ASSERT(NT_SUCCESS(NtStatus)); + + + + // + // Now try to remove the user from the group + // + + NtStatus = SamRemoveMemberFromGroup(GroupHandle1, UserRid); + if (NtStatus == STATUS_MEMBERS_PRIMARY_GROUP) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + // + // Set the user's primary group Id back and remove the user + // from the group + // + + GroupRid = DOMAIN_GROUP_RID_USERS; + NtStatus = SamSetInformationUser( + UserHandle1, + UserPrimaryGroupInformation, + &GroupRid + ); + ASSERT(NT_SUCCESS(NtStatus)); + NtStatus = SamRemoveMemberFromGroup(GroupHandle1, UserRid); + ASSERT(NT_SUCCESS(NtStatus)); + + + + // + // Now get rid of the group and possibly the user account + // + + + NtStatus = SamDeleteGroup( GroupHandle1 ); + ASSERT(NT_SUCCESS(NtStatus)); + + if (DeleteUser == TRUE) { + NtStatus = SamDeleteUser( UserHandle1 ); + ASSERT(NT_SUCCESS(NtStatus)); + } else { + NtStatus = SamCloseHandle( UserHandle1 ); + ASSERT(NT_SUCCESS(NtStatus)); + } + + + + + + + /////////////////////////////////////////////////////////////////////////// + // // + // Set Group Suite (pass 2) // + // // + /////////////////////////////////////////////////////////////////////////// + + printf("\n"); + printf(" Set Group . . . . . . . . . . . . . . . . . . . . . . Suite\n"); + + + printf(" Set Name . . . . . . . . . . . . . . . . . . . . . . "); + printf("(Untested)\n"); + + + printf(" Set Name Of Well-Known Account . . . . . . . . . . . "); + printf("(Untested)\n"); + + } + + return(TestStatus); + +} + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Alias Object Test Suite // +// // +/////////////////////////////////////////////////////////////////////////////// + + +BOOLEAN +AliasTestSuite( + HANDLE DomainHandle, + HANDLE BuiltinDomainHandle, + PSID DomainSid, + ULONG Pass + ) + +{ + NTSTATUS NtStatus, IgnoreStatus; + HANDLE AdminAliasHandle, AliasHandle1, AliasHandle2, UserHandle1, UserHandle2, UserHandle3; + ULONG CountReturned, i, MemberCount; + ULONG UserRid, UserRid2, UserRid3, AliasRid, AliasRid2; + PVOID Buffer, Buffer1, Buffer2; + ULONG NameLength; + SAM_ENUMERATE_HANDLE EnumerationContext; + PULONG Members; + PSID *AliasMembers; + PSID_NAME_USE LookedUpUses; + PULONG LookedUpRids; + UNICODE_STRING AccountNames[10], AccountName; + STRING AccountNameAnsi; + PSID UserSid1, UserSid2, GroupSid; + + BOOLEAN IndividualTestSucceeded, DeleteUser; + BOOLEAN TestStatus = TRUE; + + + if (Pass == 1) { + // + // This test suite assumes that lookup and enumeration API funciton + // properly. + // + + printf("\n"); + printf("\n"); + printf(" Alias (Pass #1) . . . . . . . . . . . . . . . . . . . Test\n"); + + /////////////////////////////////////////////////////////////////////////// + // // + // Open Alias Suite // + // // + /////////////////////////////////////////////////////////////////////////// + + printf(" Open Alias . . . . . . . . . . . . . . . . . . . . . Suite\n"); + printf(" Open Aliases . . . . . . . . . . . . . . . . . . . . . "); + IndividualTestSucceeded = TRUE; + EnumerationContext = 0; + NtStatus = SamEnumerateAliasesInDomain( + DomainHandle, + &EnumerationContext, + &Buffer, + 12000, // PreferedMaximumLength + &CountReturned + ); + + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(Buffer != NULL); + ASSERT(CountReturned > 0); + + for (i=0; i<CountReturned; i++) { + + NtStatus = SamOpenAlias( + DomainHandle, + ALIAS_ALL_ACCESS, + ((PSAM_RID_ENUMERATION)(Buffer))[i].RelativeId, + &AliasHandle1 + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SamOpenAlias( + DomainHandle, + GENERIC_READ, + ((PSAM_RID_ENUMERATION)(Buffer))[i].RelativeId, + &AliasHandle2 + ); + + if (NT_SUCCESS(NtStatus)) { + IgnoreStatus = SamCloseHandle( AliasHandle2 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + printf(" Failed opening alias second time.\n"); + printf(" Rid of account is: 0x%lx\n", + ((PSAM_RID_ENUMERATION)(Buffer))[i].RelativeId); + printf(" Name of account is: %wZ\n", + &((PSAM_RID_ENUMERATION)(Buffer))[i].Name ); + TestStatus = FALSE; + IndividualTestSucceeded = FALSE; + } + + IgnoreStatus = SamCloseHandle( AliasHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + } else { + + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + printf(" Failed opening alias for first time.\n"); + printf(" Rid of account is: 0x%lx\n", + ((PSAM_RID_ENUMERATION)(Buffer))[i].RelativeId); + printf(" Name of account is: %wZ\n", + &((PSAM_RID_ENUMERATION)(Buffer))[i].Name ); + TestStatus = FALSE; + IndividualTestSucceeded = FALSE; + } + + if (!IndividualTestSucceeded) { + printf(" "); + } + } + + + SamFreeMemory( Buffer ); + if (IndividualTestSucceeded) { + printf("Succeeded\n"); + } + + + + /////////////////////////////////////////////////////////////////////////// + // // + // Query Alias Suite // + // // + /////////////////////////////////////////////////////////////////////////// + + + // + // Get the rid of an alias created earlier in the test + // + + RtlInitString( &AccountNameAnsi, ALIAS_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountNames[0], &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + NtStatus = SamLookupNamesInDomain( + DomainHandle, + 1, + &AccountNames[0], + &LookedUpRids, + &LookedUpUses + ); + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(LookedUpUses[0] == SidTypeAlias); + RtlFreeUnicodeString( &AccountNames[0] ); + + AliasRid = LookedUpRids[0]; + + SamFreeMemory( LookedUpUses ); SamFreeMemory( LookedUpRids ); + + + printf("\n"); + printf(" Query Alias . . . . . . . . . . . . . . . . . . . . . Suite\n"); + + printf(" Query Alias General Information . . . . . . . . . . . "); + + + NtStatus = SamOpenAlias( + DomainHandle, + ALIAS_READ_INFORMATION, + AliasRid, + &AliasHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + Buffer = NULL; + NtStatus = SamQueryInformationAlias( + AliasHandle1, + AliasGeneralInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + + if ( (((ALIAS_GENERAL_INFORMATION *)Buffer)->Name.MaximumLength > 0) && + (((ALIAS_GENERAL_INFORMATION *)Buffer)->Name.Buffer != NULL) ) { + + printf("Succeeded\n"); + + printf(" Member Count is: 0x%lx\n", + (((ALIAS_GENERAL_INFORMATION *)Buffer)->MemberCount) ); + printf(" Alias Name is: %wZ\n", + &(((ALIAS_GENERAL_INFORMATION *)Buffer)->Name) ); + + } else { + printf("Failed\n"); + printf(" Alias Name not returned.\n"); + TestStatus = FALSE; + } + SamFreeMemory( Buffer ); + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + IgnoreStatus = SamCloseHandle( AliasHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + + + printf(" Query Alias Name Information . . . . . . . . . . . . "); + + + NtStatus = SamOpenAlias( + DomainHandle, + ALIAS_READ_INFORMATION, + AliasRid, + &AliasHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + Buffer = NULL; + NtStatus = SamQueryInformationAlias( + AliasHandle1, + AliasNameInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + + if ( (((ALIAS_NAME_INFORMATION *)Buffer)->Name.MaximumLength > 0) && + (((ALIAS_NAME_INFORMATION *)Buffer)->Name.Buffer != NULL) ) { + + printf("Succeeded\n"); + + printf(" Alias Name is: %wZ\n", + &(((ALIAS_NAME_INFORMATION *)Buffer)->Name) ); + } else { + printf("Failed\n"); + printf(" Alias Name not returned.\n"); + TestStatus = FALSE; + } + SamFreeMemory( Buffer ); + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + IgnoreStatus = SamCloseHandle( AliasHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + + + printf(" Query Alias Admin Comment Information . . . . . . . . "); + + + NtStatus = SamOpenAlias( + DomainHandle, + ALIAS_READ_INFORMATION, + AliasRid, + &AliasHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + Buffer = NULL; + NtStatus = SamQueryInformationAlias( + AliasHandle1, + AliasAdminCommentInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + + if ( (((ALIAS_ADM_COMMENT_INFORMATION *)Buffer)->AdminComment.MaximumLength >= 0) ) { + + printf("Succeeded\n"); + + printf(" Alias Admin Comment is: %wZ\n", + &(((ALIAS_ADM_COMMENT_INFORMATION *)Buffer)->AdminComment) ); + } else { + printf("Failed\n"); + printf(" Alias Admin Comment not returned.\n"); + TestStatus = FALSE; + } + SamFreeMemory( Buffer ); + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + IgnoreStatus = SamCloseHandle( AliasHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + + + + /////////////////////////////////////////////////////////////////////////// + // // + // Get Members Of Alias Suite // + // // + /////////////////////////////////////////////////////////////////////////// + + printf("\n"); + printf(" Get Members . . . . . . . . . . . . . . . . . . . . . Suite\n"); + +#ifdef LATER // ALIAS_LATER - well-know aliases ? + + + davidc/chads - this needs to access the builtin domain. + + printf(" Get Members of Well-Known Account . . . . . . . . . . "); + + NtStatus = SamOpenAlias( + DomainHandle, + ALIAS_LIST_MEMBERS, + DOMAIN_ALIAS_RID_ADMINS, + &AliasHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + Buffer = NULL; + NtStatus = SamGetMembersInAlias( + AliasHandle1, + &AliasMembers, + &Attributes, + &MemberCount + ); + if (NT_SUCCESS(NtStatus)) { + if (Members != NULL || Attributes != NULL) { + + printf("Succeeded\n"); + + + printf(" Member Count: %d Users\n", MemberCount); + for ( i=0; i<MemberCount; i++) { + + // printf(" User[%d] Sid: 0x%lx\n", + // i, Members[i]); + + + } + + SamFreeMemory( AliasMembers ); + SamFreeMemory( Attributes ); + + + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + IgnoreStatus = SamCloseHandle( AliasHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); +#endif + + + printf(" Get Members of Empty Alias. . . . . . . . . . . . . . "); + + // + // This alias was created earlier in the test + // + + RtlInitString( &AccountNameAnsi, ALIAS_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountNames[0], &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + NtStatus = SamLookupNamesInDomain( + DomainHandle, + 1, + &AccountNames[0], + &LookedUpRids, + &LookedUpUses + ); + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(LookedUpUses[0] == SidTypeAlias); + RtlFreeUnicodeString( &AccountNames[0] ); + + + + AliasHandle1 = NULL; + + NtStatus = SamOpenAlias( DomainHandle, ALIAS_LIST_MEMBERS, LookedUpRids[0], &AliasHandle1 ); + TST_SUCCESS_ASSERT(NtStatus); + SamFreeMemory( LookedUpUses ); SamFreeMemory( LookedUpRids ); + + NtStatus = SamGetMembersInAlias( + AliasHandle1, + &AliasMembers, + &MemberCount + ); + if (NT_SUCCESS(NtStatus)) { + if (MemberCount == 0) { + + printf("Succeeded\n"); + + + + + } else { + printf("Failed\n"); + printf(" Member Count > 0 : %d\n", MemberCount); + for ( i=0; i<MemberCount; i++) { + + // printf(" User[%d] Rid/Attributes: 0x%lx/0x%lx\n", + // i, Members[i], Attributes[i]); + } + + SamFreeMemory( AliasMembers ); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + IgnoreStatus = SamCloseHandle( AliasHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + + /////////////////////////////////////////////////////////////////////////// + // // + // Set Alias Suite (pass 1) // + // // + /////////////////////////////////////////////////////////////////////////// + + printf("\n"); + printf(" Set Alias . . . . . . . . . . . . . . . . . . . . . . Suite\n"); + // + + + // Get the rid of an alias created earlier in the test + // + + RtlInitString( &AccountNameAnsi, ALIAS_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountNames[0], &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + NtStatus = SamLookupNamesInDomain( + DomainHandle, + 1, + &AccountNames[0], + &LookedUpRids, + &LookedUpUses + ); + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(LookedUpUses[0] == SidTypeAlias); + RtlFreeUnicodeString( &AccountNames[0] ); + + AliasRid = LookedUpRids[0]; + + SamFreeMemory( LookedUpUses ); SamFreeMemory( LookedUpRids ); + + + + printf(" Set Admin Comment . . . . . . . . . . . . . . . . . . "); + + NtStatus = SamOpenAlias( + DomainHandle, + ALIAS_WRITE_ACCOUNT | ALIAS_READ_INFORMATION, + AliasRid, + &AliasHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + // + // Get the current value... + // + + Buffer1 = NULL; + NtStatus = SamQueryInformationAlias( + AliasHandle1, + AliasAdminCommentInformation, + &Buffer1 + ); + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(Buffer1 != NULL); + + + // + // Change the field to a new value and write it out. + // + + NameLength = ((ALIAS_ADM_COMMENT_INFORMATION *)Buffer1)->AdminComment.Length; + if ( NameLength == DummyString1.Length ) { + ((ALIAS_ADM_COMMENT_INFORMATION *)Buffer1)->AdminComment = DummyString2; + } else { + ((ALIAS_ADM_COMMENT_INFORMATION *)Buffer1)->AdminComment = DummyString1; + } + + NtStatus = SamSetInformationAlias( + AliasHandle1, + AliasAdminCommentInformation, + Buffer1 + ); + if ( NT_SUCCESS(NtStatus) ) { + + // + // Now check that the change was really made... + // + + Buffer2 = NULL; + NtStatus = SamQueryInformationAlias( + AliasHandle1, + AliasAdminCommentInformation, + &Buffer2 + ); + ASSERT(NT_SUCCESS( NtStatus ) ); + if ( + !RtlCompareString( + (PSTRING)&((ALIAS_ADM_COMMENT_INFORMATION *)Buffer1)->AdminComment, + (PSTRING)&((ALIAS_ADM_COMMENT_INFORMATION *)Buffer2)->AdminComment, + TRUE) + ) { + + printf("Succeeded\n"); + + } else { + + printf("Failed\n"); + printf(" Value queried doesn't match value written\n"); + printf(" Value Written is %wZ\n", + (PUNICODE_STRING)&((ALIAS_ADM_COMMENT_INFORMATION *)Buffer1)->AdminComment); + printf(" Value Retrieved is %wZ\n", + (PUNICODE_STRING)&((ALIAS_ADM_COMMENT_INFORMATION *)Buffer2)->AdminComment); + + TestStatus = FALSE; + + } + + SamFreeMemory( Buffer1 ); + SamFreeMemory( Buffer2 ); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + SamFreeMemory( Buffer1 ); + + } + + + + + + } // END PASS #1 + if (Pass == 2) { + + printf("\n"); + printf("\n"); + printf(" Alias (Pass #2) . . . . . . . . . . . . . . . . . . . Test\n"); + + /////////////////////////////////////////////////////////////////////////// + // // + // Delete Alias Suite // + // // + /////////////////////////////////////////////////////////////////////////// + + printf("\n"); + printf(" Delete Alias . . . . . . . . . . . . . . . . . . . . Suite\n"); + + printf(" Delete Normal Alias . . . . . . . . . . . . . . . . . "); + + // + // This alias was created in pass #1 + // + + RtlInitString( &AccountNameAnsi, ALIAS_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountNames[0], &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + NtStatus = SamLookupNamesInDomain( + DomainHandle, + 1, + &AccountNames[0], + &LookedUpRids, + &LookedUpUses + ); + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(LookedUpUses[0] == SidTypeAlias); + RtlFreeUnicodeString( &AccountNames[0] ); + + + + AliasHandle1 = NULL; + + NtStatus = SamOpenAlias( DomainHandle, DELETE, LookedUpRids[0], &AliasHandle1 ); + TST_SUCCESS_ASSERT(NtStatus); + SamFreeMemory( LookedUpUses ); SamFreeMemory( LookedUpRids ); + + NtStatus = SamDeleteAlias( AliasHandle1 ); + if (NT_SUCCESS(NtStatus)) { + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + +#ifdef LATER // ALIAS_LATER - well know aliases ? + + + printf(" Delete Well Known Alias . . . . . . . . . . . . . . . "); + + AliasHandle1 = NULL; + + NtStatus = SamOpenAlias( DomainHandle, DELETE, DOMAIN_GROUP_RID_USERS, &AliasHandle1 ); + TST_SUCCESS_ASSERT(NtStatus); + + NtStatus = SamDeleteAlias( AliasHandle1 ); + if (NtStatus == STATUS_SPECIAL_ACCOUNT) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + NtStatus = SamCloseHandle( AliasHandle1 ); + ASSERT(NT_SUCCESS(NtStatus)); + + + + + printf(" Delete Admin Alias. . . . . . . . . . . . . . . . . . "); + AliasHandle1 = NULL; + + NtStatus = SamOpenAlias( DomainHandle, DELETE, DOMAIN_ALIAS_RID_ADMINS, &AliasHandle1 ); + TST_SUCCESS_ASSERT(NtStatus); + + NtStatus = SamDeleteAlias( AliasHandle1 ); + if (NtStatus == STATUS_SPECIAL_ACCOUNT) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + NtStatus = SamCloseHandle( AliasHandle1 ); + ASSERT(NT_SUCCESS(NtStatus)); + + +#endif + + + + /////////////////////////////////////////////////////////////////////////// + // // + // Add/Remove Member Suite // + // // + /////////////////////////////////////////////////////////////////////////// + + printf("\n"); + printf(" Add/Remove Member Suite . . . . . . . . . . . . . . . Suite\n"); + + printf(" Add Member . . . . . . . . . . . . . . . . . . . . . "); + + // + // This test sets things up for the next test + // + + // + // The following user might already exist (from earlier in the test) + // + + RtlInitString( &AccountNameAnsi, USER_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + UserRid = 0; + UserHandle1 = NULL; + NtStatus = SamCreateUserInDomain( + DomainHandle, + &AccountName, + USER_ALL_ACCESS, + &UserHandle1, + &UserRid + ); + RtlFreeUnicodeString( &AccountName ); + DeleteUser = TRUE; + if (NtStatus == STATUS_USER_EXISTS) { + DeleteUser = FALSE; + RtlInitString( &AccountNameAnsi, USER_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountNames[0], &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + NtStatus = SamLookupNamesInDomain( + DomainHandle, + 1, + &AccountNames[0], + &LookedUpRids, + &LookedUpUses + ); + RtlFreeUnicodeString( &AccountNames[0] ); + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(LookedUpUses[0] == SidTypeUser); + UserRid = LookedUpRids[0]; + NtStatus = SamOpenUser( + DomainHandle, + USER_ALL_ACCESS, + UserRid, + &UserHandle1); + SamFreeMemory( LookedUpUses ); SamFreeMemory( LookedUpRids ); + } + + ASSERT(NT_SUCCESS(NtStatus)); + + // + // This account won't exist yet + // + + RtlInitString( &AccountNameAnsi, USER_NAME2 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + UserRid2 = 0; + UserHandle2 = NULL; + NtStatus = SamCreateUserInDomain( + DomainHandle, + &AccountName, + USER_ALL_ACCESS, + &UserHandle2, + &UserRid2 + ); + RtlFreeUnicodeString( &AccountName ); + + ASSERT(NT_SUCCESS(NtStatus)); + + + // + // create the alias + // + + RtlInitString( &AccountNameAnsi, ALIAS_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + AliasRid = 0; + AliasHandle1 = NULL; + NtStatus = SamCreateAliasInDomain( + DomainHandle, + &AccountName, + ALIAS_ALL_ACCESS, + &AliasHandle1, + &AliasRid + ); + RtlFreeUnicodeString( &AccountName ); + ASSERT(NT_SUCCESS(NtStatus)); + + // + // Make user1 a member of this alias + // + + UserSid1 = CreateUserSid(DomainSid, UserRid); + ASSERT(UserSid1 != NULL); + + UserSid2 = CreateUserSid(DomainSid, UserRid2); + ASSERT(UserSid2 != NULL); + + + + NtStatus = SamAddMemberToAlias( + AliasHandle1, + UserSid1 + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SamGetMembersInAlias( + AliasHandle1, + &AliasMembers, + &MemberCount + ); + ASSERT(NT_SUCCESS(NtStatus)); + + NtStatus = STATUS_MEMBER_NOT_IN_ALIAS; + for ( i=0; i<MemberCount; i++) { + if (RtlEqualSid(AliasMembers[i], UserSid1)) { + NtStatus = STATUS_SUCCESS; + break; + } + } + + if (!NT_SUCCESS(NtStatus)) { + printf("Failed\n"); + printf("Service returned SUCCESS, but user not in member list for alias.\n"); + printf("Member list :\n"); + for (i=0; i<MemberCount; i++) { + printfSid(AliasMembers[i]); + printf("\n"); + } + DebugBreak(); + TestStatus = FALSE; + } + + + if (AliasMembers != NULL) { + SamFreeMemory( AliasMembers ); + } + + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SamGetAliasMembership( + DomainHandle, + 1, + &UserSid1, + &MemberCount, + &Members + ); + ASSERT(NT_SUCCESS(NtStatus)); + + NtStatus = STATUS_MEMBER_NOT_IN_ALIAS; + for ( i=0; i<MemberCount; i++) { + if (Members[i] == AliasRid) { + NtStatus = STATUS_SUCCESS; + break; + } + } + + if (!NT_SUCCESS(NtStatus)) { + printf("Failed\n"); + printf("Service returned SUCCESS, but alias not in account alias membership list.\n"); + printf("Alias Membership :\n"); + for (i=0; i<MemberCount; i++) { + printf("0x%lx\n", Members[i]); + } + DebugBreak(); + TestStatus = FALSE; + } + + if (Members != NULL) { + SamFreeMemory( Members ); + } + + + // + // Check for correct alias membership for multiple accounts + // User1 should be in alias1 + // User2 should be no aliases. + // + + if (NT_SUCCESS(NtStatus)) { + + PSID SidArray[2]; + SidArray[0] = UserSid1; + SidArray[1] = UserSid2; + + NtStatus = SamGetAliasMembership( + DomainHandle, + 2, + SidArray, + &MemberCount, + &Members + ); + ASSERT(NT_SUCCESS(NtStatus)); + + if (MemberCount != 1) { + + printf("Failed\n"); + printf("Service returned SUCCESS, but combined alias membership count for 2 accounts not correct.\n"); + printf("Combined Alias Membership :\n"); + for (i=0; i<MemberCount; i++) { + printf("0x%lx\n", Members[i]); + } + DebugBreak(); + TestStatus = FALSE; + + } else { + + if (Members[0] != AliasRid) { + printf("Failed\n"); + printf("Service returned SUCCESS, but combined alias membership for 2 accounts not correct.\n"); + printf("Combined Alias Membership :\n"); + for (i=0; i<MemberCount; i++) { + printf("0x%lx\n", Members[i]); + } + DebugBreak(); + TestStatus = FALSE; + + } else { + printf("Succeeded\n"); + } + } + + if (Members != NULL) { + SamFreeMemory( Members ); + } + } + } + + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + + + + + printf(" Add another member to another alias . . . . . . . . . "); + + + + + + + // + // Make user2 a member of alias2 + // + + // + // This alias was created in pass #1 + // + + RtlInitString( &AccountNameAnsi, ALIAS_NAME2 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountNames[0], &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + NtStatus = SamLookupNamesInDomain( + DomainHandle, + 1, + &AccountNames[0], + &LookedUpRids, + &LookedUpUses + ); + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(LookedUpUses[0] == SidTypeAlias); + RtlFreeUnicodeString( &AccountNames[0] ); + + AliasHandle2 = NULL; + AliasRid2 = LookedUpRids[0]; + + NtStatus = SamOpenAlias( DomainHandle, ALIAS_ALL_ACCESS, LookedUpRids[0], &AliasHandle2 ); + TST_SUCCESS_ASSERT(NtStatus); + SamFreeMemory( LookedUpUses ); SamFreeMemory( LookedUpRids ); + + + NtStatus = SamAddMemberToAlias( + AliasHandle2, + UserSid2 + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SamGetMembersInAlias( + AliasHandle2, + &AliasMembers, + &MemberCount + ); + ASSERT(NT_SUCCESS(NtStatus)); + + NtStatus = STATUS_MEMBER_NOT_IN_ALIAS; + for ( i=0; i<MemberCount; i++) { + if (RtlEqualSid(AliasMembers[i], UserSid2)) { + NtStatus = STATUS_SUCCESS; + break; + } + } + + if (!NT_SUCCESS(NtStatus)) { + printf("Failed\n"); + printf("Service returned SUCCESS, but user not in member list for alias.\n"); + printf("Member list :\n"); + for (i=0; i<MemberCount; i++) { + printfSid(AliasMembers[i]); + printf("\n"); + } + DebugBreak(); + TestStatus = FALSE; + } + + + if (AliasMembers != NULL) { + SamFreeMemory( AliasMembers ); + } + + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SamGetAliasMembership( + DomainHandle, + 1, + &UserSid2, + &MemberCount, + &Members + ); + ASSERT(NT_SUCCESS(NtStatus)); + + NtStatus = STATUS_MEMBER_NOT_IN_ALIAS; + for ( i=0; i<MemberCount; i++) { + if (Members[i] == AliasRid2) { + NtStatus = STATUS_SUCCESS; + break; + } + } + + if (!NT_SUCCESS(NtStatus)) { + printf("Failed\n"); + printf("Service returned SUCCESS, but alias not in account alias membership list.\n"); + printf("Alias Membership :\n"); + for (i=0; i<MemberCount; i++) { + printf("0x%lx\n", Members[i]); + } + DebugBreak(); + TestStatus = FALSE; + } + + if (Members != NULL) { + SamFreeMemory( Members ); + } + + // + // Check for correct alias membership for multiple accounts + // User1 should be in alias1 + // User2 should be in alias2. + // + + if (NT_SUCCESS(NtStatus)) { + + PSID SidArray[2]; + SidArray[0] = UserSid1; + SidArray[1] = UserSid2; + + NtStatus = SamGetAliasMembership( + DomainHandle, + 2, + SidArray, + &MemberCount, + &Members + ); + ASSERT(NT_SUCCESS(NtStatus)); + + if (MemberCount != 2) { + + printf("Failed\n"); + printf("Service returned SUCCESS, but combined alias membership count for 2 accounts not correct.\n"); + printf("Combined Alias Membership :\n"); + for (i=0; i<MemberCount; i++) { + printf("0x%lx\n", Members[i]); + } + DebugBreak(); + TestStatus = FALSE; + + } else { + + if (((Members[0] == AliasRid) && (Members[1] == AliasRid2)) || + ((Members[0] == AliasRid2) && (Members[1] == AliasRid)) ) { + printf("Succeeded\n"); + } else { + printf("Failed\n"); + printf("Service returned SUCCESS, but combined alias membership for 2 accounts not correct.\n"); + printf("Combined Alias Membership :\n"); + for (i=0; i<MemberCount; i++) { + printf("0x%lx\n", Members[i]); + } + DebugBreak(); + TestStatus = FALSE; + } + } + + if (Members != NULL) { + SamFreeMemory( Members ); + } + } + } + + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + // + // Remove user2 from alias2 again + // + + NtStatus = SamRemoveMemberFromAlias( + AliasHandle2, + UserSid2 + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SamGetMembersInAlias( + AliasHandle2, + &AliasMembers, + &MemberCount + ); + ASSERT(NT_SUCCESS(NtStatus)); + + NtStatus = STATUS_MEMBER_NOT_IN_ALIAS; + for ( i=0; i<MemberCount; i++) { + if (RtlEqualSid(AliasMembers[i], UserSid2)) { + NtStatus = STATUS_SUCCESS; + break; + } + } + + if (NtStatus != STATUS_MEMBER_NOT_IN_ALIAS) { + printf("Failed\n"); + printf("Service returned SUCCESS, but user still in member list for alias.\n"); + printf("Member list :\n"); + for (i=0; i<MemberCount; i++) { + printfSid(AliasMembers[i]); + printf("\n"); + } + DebugBreak(); + TestStatus = FALSE; + } + + + if (AliasMembers != NULL) { + SamFreeMemory( AliasMembers ); + } + + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SamGetAliasMembership( + DomainHandle, + 1, + &UserSid2, + &MemberCount, + &Members + ); + ASSERT(NT_SUCCESS(NtStatus)); + + for ( i=0; i<MemberCount; i++) { + if (Members[i] == AliasRid2) { + NtStatus = STATUS_MEMBER_IN_ALIAS; + break; + } + } + + if (!NT_SUCCESS(NtStatus)) { + printf("Failed\n"); + printf("Service returned SUCCESS, but alias still in account alias membership list.\n"); + printf("Alias Membership :\n"); + for (i=0; i<MemberCount; i++) { + printf("0x%lx\n", Members[i]); + } + DebugBreak(); + TestStatus = FALSE; + } + + if (Members != NULL) { + SamFreeMemory( Members ); + } + + // + // Check for correct alias membership for multiple accounts + // User1 should be in alias1 + // User2 should be in no aliases. + // + + if (NT_SUCCESS(NtStatus)) { + + PSID SidArray[2]; + SidArray[0] = UserSid1; + SidArray[1] = UserSid2; + + NtStatus = SamGetAliasMembership( + DomainHandle, + 2, + SidArray, + &MemberCount, + &Members + ); + ASSERT(NT_SUCCESS(NtStatus)); + + if (MemberCount != 1) { + + printf("Failed\n"); + printf("Service returned SUCCESS, but combined alias membership count for 2 accounts not correct.\n"); + printf("Combined Alias Membership :\n"); + for (i=0; i<MemberCount; i++) { + printf("0x%lx\n", Members[i]); + } + DebugBreak(); + TestStatus = FALSE; + + } else { + + if (Members[0] == AliasRid) { + printf("Succeeded\n"); + } else { + printf("Failed\n"); + printf("Service returned SUCCESS, but combined alias membership for 2 accounts not correct.\n"); + printf("Combined Alias Membership :\n"); + for (i=0; i<MemberCount; i++) { + printf("0x%lx\n", Members[i]); + } + DebugBreak(); + TestStatus = FALSE; + } + } + + if (Members != NULL) { + SamFreeMemory( Members ); + } + } + } + + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + NtStatus = SamCloseHandle( AliasHandle2 ); + ASSERT(NT_SUCCESS(NtStatus)); + + + + + + printf(" Add Another Member . . . . . . . . . . . . . . . . . "); + + + // + // Make user2 a member of this alias + // + + NtStatus = SamAddMemberToAlias( + AliasHandle1, + UserSid2 + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SamGetMembersInAlias( + AliasHandle1, + &AliasMembers, + &MemberCount + ); + ASSERT(NT_SUCCESS(NtStatus)); + + NtStatus = STATUS_MEMBER_NOT_IN_ALIAS; + for ( i=0; i<MemberCount; i++) { + if (RtlEqualSid(AliasMembers[i], UserSid2)) { + NtStatus = STATUS_SUCCESS; + break; + } + } + + if (!NT_SUCCESS(NtStatus)) { + printf("Failed\n"); + printf("Service returned SUCCESS, but user not in member list for alias.\n"); + printf("Member list :\n"); + for (i=0; i<MemberCount; i++) { + printfSid(AliasMembers[i]); + printf("\n"); + } + DebugBreak(); + TestStatus = FALSE; + } + + + if (AliasMembers != NULL) { + SamFreeMemory( AliasMembers ); + } + + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SamGetAliasMembership( + DomainHandle, + 1, + &UserSid2, + &MemberCount, + &Members + ); + ASSERT(NT_SUCCESS(NtStatus)); + + NtStatus = STATUS_MEMBER_NOT_IN_ALIAS; + for ( i=0; i<MemberCount; i++) { + if (Members[i] == AliasRid) { + NtStatus = STATUS_SUCCESS; + break; + } + } + + if (!NT_SUCCESS(NtStatus)) { + printf("Failed\n"); + printf("Service returned SUCCESS, but alias not in account alias membership list.\n"); + printf("Alias Membership :\n"); + for (i=0; i<MemberCount; i++) { + printf("0x%lx\n", Members[i]); + } + DebugBreak(); + TestStatus = FALSE; + } + + if (Members != NULL) { + SamFreeMemory( Members ); + } + + // + // Check for correct alias membership for multiple accounts + // User1 should be in alias1 + // User2 should be in alias1. + // + + if (NT_SUCCESS(NtStatus)) { + + PSID SidArray[2]; + SidArray[0] = UserSid1; + SidArray[1] = UserSid2; + + NtStatus = SamGetAliasMembership( + DomainHandle, + 2, + SidArray, + &MemberCount, + &Members + ); + ASSERT(NT_SUCCESS(NtStatus)); + + if (MemberCount != 1) { + + printf("Failed\n"); + printf("Service returned SUCCESS, but combined alias membership count for 2 accounts not correct.\n"); + printf("Combined Alias Membership :\n"); + for (i=0; i<MemberCount; i++) { + printf("0x%lx\n", Members[i]); + } + DebugBreak(); + TestStatus = FALSE; + + } else { + + if (Members[0] != AliasRid) { + printf("Failed\n"); + printf("Service returned SUCCESS, but combined alias membership for 2 accounts not correct.\n"); + printf("Combined Alias Membership :\n"); + for (i=0; i<MemberCount; i++) { + printf("0x%lx\n", Members[i]); + } + DebugBreak(); + TestStatus = FALSE; + + } else { + printf("Succeeded\n"); + } + } + + if (Members != NULL) { + SamFreeMemory( Members ); + } + } + } + + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + printf(" Remove Member . . . . . . . . . . . . . . . . . . . . "); + + // + // The previous test sets this one up. + // + + // + // Now try to remove the user from the alias + // + + NtStatus = SamRemoveMemberFromAlias(AliasHandle1, UserSid1); + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SamGetMembersInAlias( + AliasHandle1, + &AliasMembers, + &MemberCount + ); + ASSERT(NT_SUCCESS(NtStatus)); + + for ( i=0; i<MemberCount; i++) { + if (RtlEqualSid(AliasMembers[i], UserSid1)) { + NtStatus = STATUS_MEMBER_IN_ALIAS; + break; + } + } + + if (!NT_SUCCESS(NtStatus)) { + printf("Failed\n"); + printf("Service returned SUCCESS, but user still in member list for alias.\n"); + printf("Member list :\n"); + for (i=0; i<MemberCount; i++) { + printfSid(AliasMembers[i]); + printf("\n"); + } + DebugBreak(); + TestStatus = FALSE; + } + + SamFreeMemory( AliasMembers ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SamGetAliasMembership( + DomainHandle, + 1, + &UserSid1, + &MemberCount, + &Members + ); + ASSERT(NT_SUCCESS(NtStatus)); + + for ( i=0; i<MemberCount; i++) { + if (Members[i] == AliasRid) { + NtStatus = STATUS_MEMBER_IN_ALIAS; + break; + } + } + + if (!NT_SUCCESS(NtStatus)) { + printf("Failed\n"); + printf("Service returned SUCCESS, but alias still in account alias membership list.\n"); + printf("Alias Membership :\n"); + for (i=0; i<MemberCount; i++) { + printf("0x%lx\n", Members[i]); + } + DebugBreak(); + TestStatus = FALSE; + } + + if (Members != NULL) { + SamFreeMemory( Members ); + } + + // + // Check for correct alias membership for multiple accounts + // User1 should be in no aliases + // User2 should be in alias1. + // + + if (NT_SUCCESS(NtStatus)) { + + PSID SidArray[2]; + SidArray[0] = UserSid1; + SidArray[1] = UserSid2; + + NtStatus = SamGetAliasMembership( + DomainHandle, + 2, + SidArray, + &MemberCount, + &Members + ); + ASSERT(NT_SUCCESS(NtStatus)); + + if (MemberCount != 1) { + + printf("Failed\n"); + printf("Service returned SUCCESS, but combined alias membership count for 2 accounts not correct.\n"); + printf("Combined Alias Membership :\n"); + for (i=0; i<MemberCount; i++) { + printf("0x%lx\n", Members[i]); + } + DebugBreak(); + TestStatus = FALSE; + + } else { + + if (Members[0] != AliasRid) { + printf("Failed\n"); + printf("Service returned SUCCESS, but combined alias membership for 2 accounts not correct.\n"); + printf("Combined Alias Membership :\n"); + for (i=0; i<MemberCount; i++) { + printf("0x%lx\n", Members[i]); + } + DebugBreak(); + TestStatus = FALSE; + + } else { + printf("Succeeded\n"); + } + } + + if (Members != NULL) { + SamFreeMemory( Members ); + } + } + } + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + + printf(" Add A User to ADMIN Alias . . . . . . . . . . . . . . "); + + // + // Make user2 a member of the ADMIN alias + // + + NtStatus = SamOpenAlias( + BuiltinDomainHandle, + ALIAS_ALL_ACCESS, + DOMAIN_ALIAS_RID_ADMINS, + &AdminAliasHandle + ); + + ASSERT( NT_SUCCESS( NtStatus ) ); + + NtStatus = SamAddMemberToAlias( + AdminAliasHandle, + UserSid2 + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SamGetMembersInAlias( + AdminAliasHandle, + &AliasMembers, + &MemberCount + ); + ASSERT(NT_SUCCESS(NtStatus)); + + NtStatus = STATUS_MEMBER_NOT_IN_ALIAS; + for ( i=0; i<MemberCount; i++) { + if (RtlEqualSid(AliasMembers[i], UserSid2)) { + NtStatus = STATUS_SUCCESS; + break; + } + } + + if (!NT_SUCCESS(NtStatus)) { + printf("Failed\n"); + printf("Service returned SUCCESS, but user not in member list for alias.\n"); + printf("Member list :\n"); + for (i=0; i<MemberCount; i++) { + printfSid(AliasMembers[i]); + printf("\n"); + } + DebugBreak(); + TestStatus = FALSE; + } + + + if (AliasMembers != NULL) { + SamFreeMemory( AliasMembers ); + } + + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SamGetAliasMembership( + BuiltinDomainHandle, + 1, + &UserSid2, + &MemberCount, + &Members + ); + ASSERT(NT_SUCCESS(NtStatus)); + + NtStatus = STATUS_MEMBER_NOT_IN_ALIAS; + for ( i=0; i<MemberCount; i++) { + if (Members[i] == DOMAIN_ALIAS_RID_ADMINS) { + NtStatus = STATUS_SUCCESS; + break; + } + } + + if (!NT_SUCCESS(NtStatus)) { + printf("Failed\n"); + printf("Service returned SUCCESS, but alias not in account alias membership list.\n"); + printf("Alias Membership :\n"); + for (i=0; i<MemberCount; i++) { + printf("0x%lx\n", Members[i]); + } + DebugBreak(); + TestStatus = FALSE; + + } else { + + printf("Succeeded\n"); + } + + if (Members != NULL) { + SamFreeMemory( Members ); + } + } + + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + printf(" Add A Group to ADMIN Alias . . . . . . . . . . . . . . "); + + // + // Make a group a member of the ADMIN alias + // + + GroupSid = CreateUserSid(DomainSid, DOMAIN_GROUP_RID_USERS ); + ASSERT(GroupSid != NULL); + + NtStatus = SamAddMemberToAlias( + AdminAliasHandle, + GroupSid + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SamGetMembersInAlias( + AdminAliasHandle, + &AliasMembers, + &MemberCount + ); + ASSERT(NT_SUCCESS(NtStatus)); + + NtStatus = STATUS_MEMBER_NOT_IN_ALIAS; + for ( i=0; i<MemberCount; i++) { + if (RtlEqualSid(AliasMembers[i], GroupSid)) { + NtStatus = STATUS_SUCCESS; + break; + } + } + + if (!NT_SUCCESS(NtStatus)) { + printf("Failed\n"); + printf("Service returned SUCCESS, but user not in member list for alias.\n"); + printf("Member list :\n"); + for (i=0; i<MemberCount; i++) { + printfSid(AliasMembers[i]); + printf("\n"); + } + DebugBreak(); + TestStatus = FALSE; + } + + + if (AliasMembers != NULL) { + SamFreeMemory( AliasMembers ); + } + + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SamGetAliasMembership( + BuiltinDomainHandle, + 1, + &GroupSid, + &MemberCount, + &Members + ); + ASSERT(NT_SUCCESS(NtStatus)); + + NtStatus = STATUS_MEMBER_NOT_IN_ALIAS; + for ( i=0; i<MemberCount; i++) { + if (Members[i] == DOMAIN_ALIAS_RID_ADMINS) { + NtStatus = STATUS_SUCCESS; + break; + } + } + + if (!NT_SUCCESS(NtStatus)) { + printf("Failed\n"); + printf("Service returned SUCCESS, but alias not in account alias membership list.\n"); + printf("Alias Membership :\n"); + for (i=0; i<MemberCount; i++) { + printf("0x%lx\n", Members[i]); + } + DebugBreak(); + TestStatus = FALSE; + + } else { + + printf("Succeeded\n"); + } + + if (Members != NULL) { + SamFreeMemory( Members ); + } + } + + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + +// NOTE: user is already created in the group below. Should keep this +// test, AND add another with an all-new group that's been added to the ADMIN +// alias (then ADD user to group, rather than create in it). + printf(" Create user in ADMIN ALIAS'd Group. . . . . . . . . . . "); + + RtlInitString( &AccountNameAnsi, USER_NAME3 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + UserRid3 = 0; + UserHandle3 = NULL; + NtStatus = SamCreateUserInDomain( + DomainHandle, + &AccountName, + USER_ALL_ACCESS, + &UserHandle3, + &UserRid3 + ); + RtlFreeUnicodeString( &AccountName ); + + if ( NT_SUCCESS( NtStatus ) ) { + + printf("Succeeded\n"); + + } else { + + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + +//NOTE: doesn't work because this is primary group. +//put back in when all-new group is created, above +// printf(" Remove user from ADMIN ALIAS'd Group. . . . . . . . . . . "); +// +// NtStatus = SamOpenGroup( +// DomainHandle, +// GROUP_ALL_ACCESS, +// DOMAIN_GROUP_RID_USERS, +// &GroupHandle +// ); +// +// ASSERT(NT_SUCCESS(NtStatus)); +// +// NtStatus = SamRemoveMemberFromGroup( +// GroupHandle, +// UserRid3 +// ); +// +// if ( NT_SUCCESS( NtStatus ) ) { +// +// printf("Succeeded\n"); +// +// } else { +// +// printf("Failed\n"); +// printf(" Completion status is 0x%lx\n", NtStatus); +// TestStatus = FALSE; +// } +// +// IgnoreStatus = SamCloseHandle( GroupHandle ); +// ASSERT(NT_SUCCESS(IgnoreStatus)); + IgnoreStatus = SamCloseHandle( UserHandle3 ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + + + printf(" Remove User from ADMIN alias. . . . . . . . . . . "); + + // + // The previous test sets this one up. + // + // Now try to remove the user from the alias + // + + NtStatus = SamRemoveMemberFromAlias(AdminAliasHandle, UserSid2); + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SamGetMembersInAlias( + AdminAliasHandle, + &AliasMembers, + &MemberCount + ); + ASSERT(NT_SUCCESS(NtStatus)); + + for ( i=0; i<MemberCount; i++) { + if (RtlEqualSid(AliasMembers[i], UserSid2)) { + NtStatus = STATUS_MEMBER_IN_ALIAS; + break; + } + } + + if (!NT_SUCCESS(NtStatus)) { + printf("Failed\n"); + printf("Service returned SUCCESS, but user still in member list for alias.\n"); + printf("Member list :\n"); + for (i=0; i<MemberCount; i++) { + printfSid(AliasMembers[i]); + printf("\n"); + } + DebugBreak(); + TestStatus = FALSE; + } + + SamFreeMemory( AliasMembers ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SamGetAliasMembership( + BuiltinDomainHandle, + 1, + &UserSid2, + &MemberCount, + &Members + ); + ASSERT(NT_SUCCESS(NtStatus)); + + for ( i=0; i<MemberCount; i++) { + if (Members[i] == DOMAIN_ALIAS_RID_ADMINS) { + NtStatus = STATUS_MEMBER_IN_ALIAS; + break; + } + } + + if (!NT_SUCCESS(NtStatus)) { + printf("Failed\n"); + printf("Service returned SUCCESS, but alias still in account alias membership list.\n"); + printf("Alias Membership :\n"); + for (i=0; i<MemberCount; i++) { + printf("0x%lx\n", Members[i]); + } + DebugBreak(); + TestStatus = FALSE; + + } else { + + printf("Succeeded\n"); + } + + if (Members != NULL) { + SamFreeMemory( Members ); + } + } + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + + // + // Make user2 a member of the ADMIN alias again, so we can test + // the new function SamRemoveMemberFromForeignDomain(). + // NOTE: we should make this a real test item. + // + + NtStatus = SamAddMemberToAlias( + AdminAliasHandle, + UserSid2 + ); + + ASSERT (NT_SUCCESS(NtStatus)); + + NtStatus = SamRemoveMemberFromForeignDomain( + BuiltinDomainHandle, + UserSid2 ); + + ASSERT (NT_SUCCESS(NtStatus)); + + + + printf(" Remove Group from ADMIN alias. . . . . . . . . . . "); + + // + // The previous test sets this one up. + // + // Now try to remove the group from the alias + // + + NtStatus = SamRemoveMemberFromAlias(AdminAliasHandle, GroupSid); + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SamGetMembersInAlias( + AdminAliasHandle, + &AliasMembers, + &MemberCount + ); + ASSERT(NT_SUCCESS(NtStatus)); + + for ( i=0; i<MemberCount; i++) { + if (RtlEqualSid(AliasMembers[i], GroupSid)) { + NtStatus = STATUS_MEMBER_IN_ALIAS; + break; + } + } + + if (!NT_SUCCESS(NtStatus)) { + printf("Failed\n"); + printf("Service returned SUCCESS, but user still in member list for alias.\n"); + printf("Member list :\n"); + for (i=0; i<MemberCount; i++) { + printfSid(AliasMembers[i]); + printf("\n"); + } + DebugBreak(); + TestStatus = FALSE; + } + + SamFreeMemory( AliasMembers ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SamGetAliasMembership( + BuiltinDomainHandle, + 1, + &GroupSid, + &MemberCount, + &Members + ); + ASSERT(NT_SUCCESS(NtStatus)); + + for ( i=0; i<MemberCount; i++) { + if (Members[i] == DOMAIN_ALIAS_RID_ADMINS) { + NtStatus = STATUS_MEMBER_IN_ALIAS; + break; + } + } + + if (!NT_SUCCESS(NtStatus)) { + printf("Failed\n"); + printf("Service returned SUCCESS, but alias still in account alias membership list.\n"); + printf("Alias Membership :\n"); + for (i=0; i<MemberCount; i++) { + printf("0x%lx\n", Members[i]); + } + DebugBreak(); + TestStatus = FALSE; + + } else { + + printf("Succeeded\n"); + } + + if (Members != NULL) { + SamFreeMemory( Members ); + } + } + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + IgnoreStatus = SamCloseHandle( AdminAliasHandle ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + + printf(" Delete account while member of alias. . . . . . . . . "); + + + // + // Now delete user2 and check the alias member list is updated + // + + NtStatus = SamDeleteUser( UserHandle2 ); + ASSERT(NT_SUCCESS(NtStatus)); + + NtStatus = SamGetMembersInAlias( + AliasHandle1, + &AliasMembers, + &MemberCount + ); + ASSERT(NT_SUCCESS(NtStatus)); + + for ( i=0; i<MemberCount; i++) { + if (RtlEqualSid(AliasMembers[i], UserSid2)) { + NtStatus = STATUS_MEMBER_IN_ALIAS; + break; + } + } + + if (!NT_SUCCESS(NtStatus)) { + printf("Failed\n"); + printf("Service returned SUCCESS, but user still in member list for alias.\n"); + printf("Member list :\n"); + for (i=0; i<MemberCount; i++) { + printfSid(AliasMembers[i]); + printf("\n"); + } + DebugBreak(); + TestStatus = FALSE; + } + + SamFreeMemory( AliasMembers ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SamGetAliasMembership( + DomainHandle, + 1, + &UserSid2, + &MemberCount, + &Members + ); + ASSERT(NT_SUCCESS(NtStatus)); + + if (MemberCount != 0) { + printf("Failed\n"); + printf("Service returned SUCCESS, but alias still in alias membership list for account.\n"); + printf("Alias Membership :\n"); + for (i=0; i<MemberCount; i++) { + printf("0x%lx\n", Members[i]); + } + DebugBreak(); + TestStatus = FALSE; + } + + if (Members != NULL) { + SamFreeMemory( Members ); + } + + // + // Check for correct alias membership for multiple accounts + // User1 should be in no aliases + // User2 should be in no aliases. + // + + if (NT_SUCCESS(NtStatus)) { + + PSID SidArray[2]; + SidArray[0] = UserSid1; + SidArray[1] = UserSid2; + + NtStatus = SamGetAliasMembership( + DomainHandle, + 2, + SidArray, + &MemberCount, + &Members + ); + ASSERT(NT_SUCCESS(NtStatus)); + + if (MemberCount != 0) { + + printf("Failed\n"); + printf("Service returned SUCCESS, but combined alias membership count for 2 accounts not correct.\n"); + printf("Combined Alias Membership :\n"); + for (i=0; i<MemberCount; i++) { + printf("0x%lx\n", Members[i]); + } + DebugBreak(); + TestStatus = FALSE; + + } else { + printf("Succeeded\n"); + } + + if (Members != NULL) { + SamFreeMemory( Members ); + } + } + } + + + + + printf(" Delete alias with members . . . . . . . . . . . . . . "); + + // + // Make the user a member of this alias (again) + // + + NtStatus = SamAddMemberToAlias( + AliasHandle1, + UserSid1 + ); + ASSERT(NT_SUCCESS(NtStatus)); + + // + // Now delete the alias and check the membership list for user is updated + // + + NtStatus = SamDeleteAlias( AliasHandle1 ); + ASSERT(NT_SUCCESS(NtStatus)); + + NtStatus = SamGetAliasMembership( + DomainHandle, + 1, + &UserSid1, + &MemberCount, + &Members + ); + ASSERT(NT_SUCCESS(NtStatus)); + + for ( i=0; i<MemberCount; i++) { + if (Members[i] == AliasRid) { + NtStatus = STATUS_MEMBER_IN_ALIAS; + break; + } + } + + if (NT_SUCCESS(NtStatus)) { + printf("Succeeded\n"); + } else { + printf("Failed\n"); + printf("Service returned SUCCESS, but alias still in account alias membership list.\n"); + printf("Alias Membership :\n"); + for (i=0; i<MemberCount; i++) { + printf("0x%lx\n", Members[i]); + } + DebugBreak(); + TestStatus = FALSE; + } + + if (Members != NULL) { + SamFreeMemory( Members ); + } + + + + DeleteUserSid(UserSid1); + DeleteUserSid(UserSid2); + + // + // and clean up + // + + if (DeleteUser == TRUE) { + NtStatus = SamDeleteUser( UserHandle1 ); + ASSERT(NT_SUCCESS(NtStatus)); + } else { + NtStatus = SamCloseHandle( UserHandle1 ); + ASSERT(NT_SUCCESS(NtStatus)); + } + + + + + + + printf(" Add Foreign Domain Member . . . . . . . . . . . . . . "); + + // + // create the alias + // + + RtlInitString( &AccountNameAnsi, ALIAS_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + AliasRid = 0; + AliasHandle1 = NULL; + NtStatus = SamCreateAliasInDomain( + DomainHandle, + &AccountName, + ALIAS_ALL_ACCESS, + &AliasHandle1, + &AliasRid + ); + RtlFreeUnicodeString( &AccountName ); + ASSERT(NT_SUCCESS(NtStatus)); + + // + // Specify a non-existant user be added to this alias + // + + + { + PSID ForeignDomainSid; + + ForeignDomainSid = CreateUserSid(DomainSid, 307333); // random domain sub-authority + ASSERT(ForeignDomainSid != NULL); + + UserRid = 45728; // Random user rid + + UserSid1 = CreateUserSid(ForeignDomainSid, UserRid); + ASSERT(UserSid1 != NULL); + + DeleteUserSid(ForeignDomainSid); + } + + + NtStatus = SamAddMemberToAlias( + AliasHandle1, + UserSid1 + ); + + if (NtStatus == STATUS_SUCCESS) { + + NtStatus = SamGetMembersInAlias( + AliasHandle1, + &AliasMembers, + &MemberCount + ); + ASSERT(NT_SUCCESS(NtStatus)); + + NtStatus = STATUS_MEMBER_NOT_IN_ALIAS; + for ( i=0; i<MemberCount; i++) { + if (RtlEqualSid(AliasMembers[i], UserSid1)) { + NtStatus = STATUS_SUCCESS; + break; + } + } + + if (!NT_SUCCESS(NtStatus)) { + printf("Failed\n"); + printf("Service returned SUCCESS, but user not in member list for alias.\n"); + printf("Member list :\n"); + for (i=0; i<MemberCount; i++) { + printfSid(AliasMembers[i]); + printf("\n"); + } + DebugBreak(); + TestStatus = FALSE; + } + + + if (AliasMembers != NULL) { + SamFreeMemory( AliasMembers ); + } + + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SamGetAliasMembership( + DomainHandle, + 1, + &UserSid1, + &MemberCount, + &Members + ); + ASSERT(NT_SUCCESS(NtStatus)); + + NtStatus = STATUS_MEMBER_NOT_IN_ALIAS; + for ( i=0; i<MemberCount; i++) { + if (Members[i] == AliasRid) { + NtStatus = STATUS_SUCCESS; + break; + } + } + + if (NT_SUCCESS(NtStatus)) { + printf("Succeeded\n"); + } else { + printf("Failed\n"); + printf("Service returned SUCCESS, but alias not in account alias membership list.\n"); + printf("Alias Membership :\n"); + for (i=0; i<MemberCount; i++) { + printf("0x%lx\n", Members[i]); + } + DebugBreak(); + TestStatus = FALSE; + } + + if (Members != NULL) { + SamFreeMemory( Members ); + } + } + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + DeleteUserSid(UserSid1); + + + + + printf(" Add alias as member . . . . . . . . . . . . . . . . . "); + + // + // Specify an alias in the current domain be added to this alias + // + + + UserSid1 = CreateUserSid(DomainSid, AliasRid2); + ASSERT(UserSid1 != NULL); + + + NtStatus = SamAddMemberToAlias( + AliasHandle1, + UserSid1 + ); + + if (NtStatus != STATUS_INVALID_MEMBER) { + + printf("Failed\n"); + printf("Expected service to return STATUS_INVALID_MEMBER, actually returned 0x%lx.\n", NtStatus); + DebugBreak(); + TestStatus = FALSE; + } else { + printf("Succeeded\n"); + } + + DeleteUserSid(UserSid1); + + + + printf(" Add non-existant account in this domain as member . . "); + + // + // Specify a non-existant account in the current domain be added to this alias + // + + + UserSid1 = CreateUserSid(DomainSid, 32567); // Random rid + ASSERT(UserSid1 != NULL); + + + NtStatus = SamAddMemberToAlias( + AliasHandle1, + UserSid1 + ); + + if (NtStatus != STATUS_NO_SUCH_MEMBER) { + + printf("Failed\n"); + printf("Expected service to return STATUS_NO_SUCH_MEMBER, actually returned 0x%lx.\n", NtStatus); + DebugBreak(); + TestStatus = FALSE; + } else { + printf("Succeeded\n"); + } + + DeleteUserSid(UserSid1); + + + + printf(" Remove Non-member . . . . . . . . . . . . . . . . . . "); + + // + // Specify a non-existant user be removed from this alias + // + + { + PSID ForeignDomainSid; + + ForeignDomainSid = CreateUserSid(DomainSid, 35775); // random domain sub-authority + ASSERT(ForeignDomainSid != NULL); + + UserRid = 623545; // Random user rid + + UserSid1 = CreateUserSid(ForeignDomainSid, UserRid); + ASSERT(UserSid1 != NULL); + + DeleteUserSid(ForeignDomainSid); + } + + NtStatus = SamRemoveMemberFromAlias( AliasHandle1, UserSid1 ); + + if (NtStatus == STATUS_MEMBER_NOT_IN_ALIAS) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + DeleteUserSid(UserSid1); + + NtStatus = SamDeleteAlias( AliasHandle1 ); + ASSERT(NT_SUCCESS(NtStatus)); + + + + + } + + return(TestStatus); + +} + + +/////////////////////////////////////////////////////////////////////////////// +// // +// User Object Test Suite // +// // +/////////////////////////////////////////////////////////////////////////////// + + +BOOLEAN +UserTestSuite( + HANDLE DomainHandle, + ULONG Pass + ) + + +{ + + PUSER_ALL_INFORMATION All, All2; + NTSTATUS NtStatus, IgnoreStatus, TmpStatus; + HANDLE UserHandle1, UserHandle2, GroupHandle1; + ULONG CountReturned, NameLength, MembershipCount, i; + ULONG UserRid, GroupRid; + PVOID Buffer, Buffer1, Buffer2; + SAM_ENUMERATE_HANDLE EnumerationContext; + USER_GENERAL_INFORMATION GeneralInformation; + USER_LOGON_INFORMATION LogonInformation; + USER_ACCOUNT_INFORMATION AccountInformation; + PSID_NAME_USE LookedUpUses; + PULONG LookedUpRids; + UNICODE_STRING AccountNames[10], AccountName; + STRING AccountNameAnsi, TmpAnsiString; + + + + + BOOLEAN IndividualTestSucceeded, DeleteUser; + BOOLEAN TestStatus = TRUE; + + + + + if (Pass == 1) { + // This test suite assumes that lookup and enumeration API funciton + // properly. + // + + printf("\n"); + printf("\n"); + printf(" User (Pass #1) . . . . . . . . . . . . . . . . . . . Test\n"); + + /////////////////////////////////////////////////////////////////////////// + // // + // Open User Suite // + // // + /////////////////////////////////////////////////////////////////////////// + + printf(" Open User . . . . . . . . . . . . . . . . . . . . . Suite\n"); + printf(" Open Users . . . . . . . . . . . . . . . . . . . . . "); + IndividualTestSucceeded = TRUE; + EnumerationContext = 0; + NtStatus = SamEnumerateUsersInDomain( + DomainHandle, + &EnumerationContext, + 0, + &Buffer, + 12000, // PreferedMaximumLength + &CountReturned + ); + + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(Buffer != NULL); + ASSERT(CountReturned > 0); + + for (i=0; i<CountReturned; i++) { + + NtStatus = SamOpenUser( + DomainHandle, + USER_ALL_ACCESS, + ((PSAM_RID_ENUMERATION)(Buffer))[i].RelativeId, + &UserHandle1 + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SamOpenUser( + DomainHandle, + GENERIC_READ, + ((PSAM_RID_ENUMERATION)(Buffer))[i].RelativeId, + &UserHandle2 + ); + + if (NT_SUCCESS(NtStatus)) { + IgnoreStatus = SamCloseHandle( UserHandle2 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + printf(" Failed opening User second time.\n"); + printf(" Rid of account is: 0x%lx\n", + ((PSAM_RID_ENUMERATION)(Buffer))[i].RelativeId); + printf(" Name of account is: %wZ\n", + &((PSAM_RID_ENUMERATION)(Buffer))[i].Name ); + TestStatus = FALSE; + IndividualTestSucceeded = FALSE; + } + + IgnoreStatus = SamCloseHandle( UserHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + } else { + + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + printf(" Failed opening User for first time.\n"); + printf(" Rid of account is: 0x%lx\n", + ((PSAM_RID_ENUMERATION)(Buffer))[i].RelativeId); + printf(" Name of account is: %wZ\n", + &((PSAM_RID_ENUMERATION)(Buffer))[i].Name ); + TestStatus = FALSE; + IndividualTestSucceeded = FALSE; + } + + } + + + SamFreeMemory( Buffer ); + if (IndividualTestSucceeded) { + printf("Succeeded\n"); + } + + + + /////////////////////////////////////////////////////////////////////////// + // // + // Query User Suite // + // // + /////////////////////////////////////////////////////////////////////////// + + printf("\n"); + printf(" Query User . . . . . . . . . . . . . . . . . . . . . Suite\n"); + + printf(" Query User General Information . . . . . . . . . . . "); + + + NtStatus = SamOpenUser( + DomainHandle, + USER_READ_GENERAL, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + Buffer = NULL; + NtStatus = SamQueryInformationUser( + UserHandle1, + UserGeneralInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + + if ( (((USER_GENERAL_INFORMATION *)Buffer)->UserName.MaximumLength + >= 0) && + (((USER_GENERAL_INFORMATION *)Buffer)->UserName.Buffer != NULL) + ) { + + printf("Succeeded\n"); + + printf(" Primary Group is: 0x%lx\n", + (((USER_GENERAL_INFORMATION *)Buffer)->PrimaryGroupId) ); + printf(" User Name is: *%wZ*\n", + &(((USER_GENERAL_INFORMATION *)Buffer)->UserName) ); + printf(" Full Name is: *%wZ*\n", + &(((USER_GENERAL_INFORMATION *)Buffer)->FullName) ); + printf(" Admin Comment is: *%wZ*\n", + &(((USER_GENERAL_INFORMATION *)Buffer)->AdminComment) ); + printf(" User Comment is: *%wZ*\n", + &(((USER_GENERAL_INFORMATION *)Buffer)->UserComment) ); + + + } else { + printf("Failed\n"); + printf(" One of the UNICODE_STRINGs not returned.\n"); + TestStatus = FALSE; + } + SamFreeMemory( Buffer ); + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + IgnoreStatus = SamCloseHandle( UserHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + + + printf(" Query User Name Information . . . . . . . . . . . . . "); + + + NtStatus = SamOpenUser( + DomainHandle, + USER_READ_GENERAL, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + Buffer = NULL; + NtStatus = SamQueryInformationUser( + UserHandle1, + UserNameInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + + if ( (((USER_NAME_INFORMATION *)Buffer)->UserName.MaximumLength > 0) && + (((USER_NAME_INFORMATION *)Buffer)->UserName.Buffer != NULL) + ) { + + printf("Succeeded\n"); + + printf(" User Name is: *%wZ*\n", + &(((USER_NAME_INFORMATION *)Buffer)->UserName) ); + printf(" Full Name is: *%wZ*\n", + &(((USER_NAME_INFORMATION *)Buffer)->FullName) ); + + + + } else { + printf("Failed\n"); + printf(" One of the UNICODE_STRINGs not returned.\n"); + TestStatus = FALSE; + } + SamFreeMemory( Buffer ); + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + IgnoreStatus = SamCloseHandle( UserHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + + + printf(" Query User Account Name Information . . . . . . . . . "); + + + NtStatus = SamOpenUser( + DomainHandle, + USER_READ_GENERAL, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + Buffer = NULL; + NtStatus = SamQueryInformationUser( + UserHandle1, + UserAccountNameInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + + if ( (((USER_ACCOUNT_NAME_INFORMATION *)Buffer)->UserName.MaximumLength > 0) && + (((USER_ACCOUNT_NAME_INFORMATION *)Buffer)->UserName.Buffer != NULL) + ) { + + printf("Succeeded\n"); + + printf(" User Name is: *%wZ*\n", + &(((USER_ACCOUNT_NAME_INFORMATION *)Buffer)->UserName) ); + + + + } else { + printf("Failed\n"); + printf(" UNICODE_STRING not returned.\n"); + TestStatus = FALSE; + } + SamFreeMemory( Buffer ); + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + IgnoreStatus = SamCloseHandle( UserHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + + + printf(" Query User Full Name Information . . . . . . . . . . "); + + + NtStatus = SamOpenUser( + DomainHandle, + USER_READ_GENERAL, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + Buffer = NULL; + NtStatus = SamQueryInformationUser( + UserHandle1, + UserFullNameInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + + if ( (((USER_FULL_NAME_INFORMATION *)Buffer)->FullName.MaximumLength + >= 0) + ) { + + printf("Succeeded\n"); + + printf(" Full Name is: *%wZ*\n", + &(((USER_FULL_NAME_INFORMATION *)Buffer)->FullName) ); + + + + } else { + printf("Failed\n"); + printf(" UNICODE_STRING not returned.\n"); + TestStatus = FALSE; + } + SamFreeMemory( Buffer ); + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + IgnoreStatus = SamCloseHandle( UserHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + + + printf(" Query User Admin Comment Information . . . . . . . . "); + + + NtStatus = SamOpenUser( + DomainHandle, + USER_READ_GENERAL, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + Buffer = NULL; + NtStatus = SamQueryInformationUser( + UserHandle1, + UserAdminCommentInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + + if ( (((USER_ADMIN_COMMENT_INFORMATION *)Buffer)->AdminComment.MaximumLength + >= 0) + ) { + + printf("Succeeded\n"); + + printf(" Admin Comment is: *%wZ*\n", + &(((USER_ADMIN_COMMENT_INFORMATION *)Buffer)->AdminComment) ); + + } else { + printf("Failed\n"); + printf(" User Admin Comment not returned.\n"); + TestStatus = FALSE; + } + SamFreeMemory( Buffer ); + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + IgnoreStatus = SamCloseHandle( UserHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + + + printf(" Query User Primary Group Information . . . . . . . . "); + + + NtStatus = SamOpenUser( + DomainHandle, + USER_READ_GENERAL, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + Buffer = NULL; + NtStatus = SamQueryInformationUser( + UserHandle1, + UserPrimaryGroupInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + + + printf("Succeeded\n"); + + printf(" Primary Group is: 0x%lx\n", + (((USER_PRIMARY_GROUP_INFORMATION *)Buffer)->PrimaryGroupId) ); + + SamFreeMemory( Buffer ); + + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + IgnoreStatus = SamCloseHandle( UserHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + + + printf(" Query User Control Information . . . . . . . . . . . "); + + + NtStatus = SamOpenUser( + DomainHandle, + USER_READ_ACCOUNT, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + Buffer = NULL; + NtStatus = SamQueryInformationUser( + UserHandle1, + UserControlInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + + + printf("Succeeded\n"); + + printf(" Account Control is: 0x%lx\n", + (((USER_CONTROL_INFORMATION *)Buffer)->UserAccountControl) ); + + SamFreeMemory( Buffer ); + + + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + IgnoreStatus = SamCloseHandle( UserHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + + + printf(" Query User Expiration Information . . . . . . . . . . "); + + + NtStatus = SamOpenUser( + DomainHandle, + USER_READ_ACCOUNT, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + Buffer = NULL; + NtStatus = SamQueryInformationUser( + UserHandle1, + UserExpiresInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + + + printf("Succeeded\n"); + + printf(" Account Expires on: (0x%lx, 0x%lx)\n", + (((USER_EXPIRES_INFORMATION *)Buffer)->AccountExpires.HighPart), + (((USER_EXPIRES_INFORMATION *)Buffer)->AccountExpires.LowPart) ); + + SamFreeMemory( Buffer ); + + + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + IgnoreStatus = SamCloseHandle( UserHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + printf(" Query User Preferences Information . . . . . . . . . "); + + + NtStatus = SamOpenUser( + DomainHandle, + USER_READ_PREFERENCES | USER_READ_GENERAL, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + Buffer = NULL; + NtStatus = SamQueryInformationUser( + UserHandle1, + UserPreferencesInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + + if ( (((USER_PREFERENCES_INFORMATION *)Buffer)->UserComment.MaximumLength + >= 0) + ) { + + printf("Succeeded\n"); + + printf(" User Comment is: *%wZ*\n", + &(((USER_PREFERENCES_INFORMATION *)Buffer)->UserComment) ); + + } else { + printf("Failed\n"); + printf(" One of the UNICODE_STRINGs not returned.\n"); + TestStatus = FALSE; + } + SamFreeMemory( Buffer ); + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + IgnoreStatus = SamCloseHandle( UserHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + + + + printf(" Query User Home Directory Information . . . . . . . . "); + + + NtStatus = SamOpenUser( + DomainHandle, + USER_READ_LOGON, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + Buffer = NULL; + NtStatus = SamQueryInformationUser( + UserHandle1, + UserHomeInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + + if ( (((USER_HOME_INFORMATION *)Buffer)->HomeDirectory.MaximumLength + >= 0) && + (((USER_HOME_INFORMATION *)Buffer)->HomeDirectoryDrive.MaximumLength + >= 0) + ) { + + printf("Succeeded\n"); + + printf(" Home Directory is: *%wZ*\n", + &(((USER_HOME_INFORMATION *)Buffer)->HomeDirectory) ); + printf(" Home Directory Drive is: *%wZ*\n", + &(((USER_HOME_INFORMATION *)Buffer)->HomeDirectoryDrive) ); + + + } else { + printf("Failed\n"); + printf(" String not returned.\n"); + TestStatus = FALSE; + } + SamFreeMemory( Buffer ); + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + IgnoreStatus = SamCloseHandle( UserHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + + + printf(" Query User Script Path Information . . . . . . . . . "); + + + NtStatus = SamOpenUser( + DomainHandle, + USER_READ_LOGON, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + Buffer = NULL; + NtStatus = SamQueryInformationUser( + UserHandle1, + UserScriptInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + + if ( (((USER_SCRIPT_INFORMATION *)Buffer)->ScriptPath.MaximumLength + >= 0) + ) { + + printf("Succeeded\n"); + + printf(" Script Path is: *%wZ*\n", + &(((USER_SCRIPT_INFORMATION *)Buffer)->ScriptPath) ); + + + } else { + printf("Failed\n"); + printf(" String not returned.\n"); + TestStatus = FALSE; + } + SamFreeMemory( Buffer ); + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + IgnoreStatus = SamCloseHandle( UserHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + + printf(" Query User ProfilePath Information . . . . . . . . . "); + + + NtStatus = SamOpenUser( + DomainHandle, + USER_READ_LOGON, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + Buffer = NULL; + NtStatus = SamQueryInformationUser( + UserHandle1, + UserProfileInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + + if ( (((USER_PROFILE_INFORMATION *)Buffer)->ProfilePath.MaximumLength + >= 0) + ) { + + printf("Succeeded\n"); + + printf(" Profile Path is: *%wZ*\n", + &(((USER_PROFILE_INFORMATION *)Buffer)->ProfilePath) ); + + + } else { + printf("Failed\n"); + printf(" String not returned.\n"); + TestStatus = FALSE; + } + SamFreeMemory( Buffer ); + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + IgnoreStatus = SamCloseHandle( UserHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + + printf(" Query User Logon Information . . . . . . . . . . . . "); + + + NtStatus = SamOpenUser( + DomainHandle, + USER_READ_ACCOUNT | USER_READ_GENERAL | USER_READ_PREFERENCES | USER_READ_LOGON, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + Buffer = NULL; + NtStatus = SamQueryInformationUser( + UserHandle1, + UserLogonInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + + if ( (((USER_LOGON_INFORMATION *)Buffer)->UserName.MaximumLength > 0) && + (((USER_LOGON_INFORMATION *)Buffer)->UserName.Buffer != NULL) + ) { + + printf("Succeeded\n"); + + printf(" User RID is: 0x%lx\n", + (((USER_LOGON_INFORMATION *)Buffer)->UserId) ); + printf(" Primary Group is: 0x%lx\n", + (((USER_LOGON_INFORMATION *)Buffer)->PrimaryGroupId) ); + printf(" Logon Units are: 0x%lx\n", + (((USER_LOGON_INFORMATION *)Buffer)->LogonHours.UnitsPerWeek) ); + printf(" Bad PWD count is: 0x%lx\n", + (((USER_LOGON_INFORMATION *)Buffer)->BadPasswordCount) ); + printf(" Logon count is: 0x%lx\n", + (((USER_LOGON_INFORMATION *)Buffer)->LogonCount) ); + + printf(" last Logon is: (0x%lx, 0x%lx)\n", + (((USER_LOGON_INFORMATION *)Buffer)->LastLogon.HighPart), + (((USER_LOGON_INFORMATION *)Buffer)->LastLogon.LowPart) ); + printf(" last Logoff is: (0x%lx, 0x%lx)\n", + (((USER_LOGON_INFORMATION *)Buffer)->LastLogoff.HighPart), + (((USER_LOGON_INFORMATION *)Buffer)->LastLogoff.LowPart) ); + + + printf(" User Name is: *%wZ*\n", + &(((USER_LOGON_INFORMATION *)Buffer)->UserName) ); + printf(" Full Name is: *%wZ*\n", + &(((USER_LOGON_INFORMATION *)Buffer)->FullName) ); + printf(" Home Dir is: *%wZ*\n", + &(((USER_LOGON_INFORMATION *)Buffer)->HomeDirectory) ); + printf(" Home Dir Drive is: *%wZ*\n", + &(((USER_LOGON_INFORMATION *)Buffer)->HomeDirectoryDrive) ); + printf(" Script Path is: *%wZ*\n", + &(((USER_LOGON_INFORMATION *)Buffer)->ScriptPath) ); + printf(" Profile Path is: *%wZ*\n", + &(((USER_LOGON_INFORMATION *)Buffer)->ProfilePath) ); + printf(" WorkStations are: *%wZ*\n", + &(((USER_LOGON_INFORMATION *)Buffer)->WorkStations) ); + + + + + } else { + printf("Failed\n"); + printf(" One of the UNICODE_STRINGs not returned.\n"); + TestStatus = FALSE; + } + SamFreeMemory( Buffer ); + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + IgnoreStatus = SamCloseHandle( UserHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + + + printf(" Query User Logon Hours . . . . . . . . . . . . . . . "); + + + NtStatus = SamOpenUser( + DomainHandle, + USER_READ_LOGON, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + Buffer = NULL; + NtStatus = SamQueryInformationUser( + UserHandle1, + UserLogonHoursInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + + printf("Succeeded\n"); + + printf(" Logon Units are: 0x%lx\n", + (((USER_LOGON_HOURS_INFORMATION *)Buffer)->LogonHours.UnitsPerWeek) ); + + + SamFreeMemory( Buffer ); + + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + IgnoreStatus = SamCloseHandle( UserHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + + + printf(" Query Account Information . . . . . . . . . . . . . . "); + + NtStatus = SamOpenUser( + DomainHandle, + USER_READ_GENERAL | USER_READ_PREFERENCES | + USER_READ_LOGON | USER_READ_ACCOUNT, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + Buffer = NULL; + NtStatus = SamQueryInformationUser( + UserHandle1, + UserAccountInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + + if ( (((USER_ACCOUNT_INFORMATION *)Buffer)->UserName.MaximumLength > 0) && + (((USER_ACCOUNT_INFORMATION *)Buffer)->UserName.Buffer != NULL) + ) { + + printf("Succeeded\n"); + + printf(" User RID is: 0x%lx\n", + (((USER_ACCOUNT_INFORMATION *)Buffer)->UserId) ); + printf(" Primary Group is: 0x%lx\n", + (((USER_ACCOUNT_INFORMATION *)Buffer)->PrimaryGroupId) ); + printf(" Logon Units are: 0x%lx\n", + (((USER_ACCOUNT_INFORMATION *)Buffer)->LogonHours.UnitsPerWeek) ); + printf(" Bad PWD count is: 0x%lx\n", + (((USER_ACCOUNT_INFORMATION *)Buffer)->BadPasswordCount) ); + printf(" Logon count is: 0x%lx\n", + (((USER_ACCOUNT_INFORMATION *)Buffer)->LogonCount) ); + printf(" Account Ctrl is: 0x%lx\n", + (((USER_ACCOUNT_INFORMATION *)Buffer)->UserAccountControl) ); + + printf(" last Logon is: (0x%lx, 0x%lx)\n", + (((USER_ACCOUNT_INFORMATION *)Buffer)->LastLogon.HighPart), + (((USER_ACCOUNT_INFORMATION *)Buffer)->LastLogon.LowPart) ); + printf(" last Logoff is: (0x%lx, 0x%lx)\n", + (((USER_ACCOUNT_INFORMATION *)Buffer)->LastLogoff.HighPart), + (((USER_ACCOUNT_INFORMATION *)Buffer)->LastLogoff.LowPart) ); + printf(" Pwd Last Set is: (0x%lx, 0x%lx)\n", + (((USER_ACCOUNT_INFORMATION *)Buffer)->PasswordLastSet.HighPart), + (((USER_ACCOUNT_INFORMATION *)Buffer)->PasswordLastSet.LowPart) ); + printf(" Account Expires is: (0x%lx, 0x%lx)\n", + (((USER_ACCOUNT_INFORMATION *)Buffer)->AccountExpires.HighPart), + (((USER_ACCOUNT_INFORMATION *)Buffer)->AccountExpires.LowPart) ); + + + printf(" User Name is: *%wZ*\n", + &(((USER_ACCOUNT_INFORMATION *)Buffer)->UserName) ); + printf(" Full Name is: *%wZ*\n", + &(((USER_ACCOUNT_INFORMATION *)Buffer)->FullName) ); + printf(" Home Dir is: *%wZ*\n", + &(((USER_ACCOUNT_INFORMATION *)Buffer)->HomeDirectory) ); + printf(" Home Dir Drive is: *%wZ*\n", + &(((USER_ACCOUNT_INFORMATION *)Buffer)->HomeDirectoryDrive) ); + printf(" Script Path is: *%wZ*\n", + &(((USER_ACCOUNT_INFORMATION *)Buffer)->ScriptPath) ); + printf(" Profile Path is: *%wZ*\n", + &(((USER_ACCOUNT_INFORMATION *)Buffer)->ProfilePath) ); + printf(" Admin Comment is: *%wZ*\n", + &(((USER_ACCOUNT_INFORMATION *)Buffer)->AdminComment) ); + printf(" WorkStations are: *%wZ*\n", + &(((USER_ACCOUNT_INFORMATION *)Buffer)->WorkStations) ); + + + + } else { + printf("Failed\n"); + printf(" One of the UNICODE_STRINGs not returned.\n"); + TestStatus = FALSE; + } + SamFreeMemory( Buffer ); + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + IgnoreStatus = SamCloseHandle( UserHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + + + + + + + printf(" Query Workstations Information . . . . . . . . . . . "); + + NtStatus = SamOpenUser( + DomainHandle, + USER_READ_LOGON, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + Buffer = NULL; + NtStatus = SamQueryInformationUser( + UserHandle1, + UserWorkStationsInformation, + &Buffer + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + + if ( (((USER_WORKSTATIONS_INFORMATION *)Buffer)->WorkStations.MaximumLength + >= 0) + ) { + + printf("Succeeded\n"); + + printf(" Workstations is: *%wZ*\n", + &(((USER_WORKSTATIONS_INFORMATION *)Buffer)->WorkStations) ); + + + } else { + printf("Failed\n"); + printf(" String not returned.\n"); + TestStatus = FALSE; + } + SamFreeMemory( Buffer ); + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + IgnoreStatus = SamCloseHandle( UserHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + + + + printf(" Query Internal1 Information . . . . . . . . . . . "); + + NtStatus = SamOpenUser( + DomainHandle, + USER_READ_LOGON, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + Buffer = NULL; + NtStatus = SamQueryInformationUser( + UserHandle1, + UserInternal1Information, + &Buffer + ); + + if ( NtStatus == STATUS_INVALID_INFO_CLASS ) { + + // + // We're not a trusted client, so we expected this to fail. + // + + printf("Succeeded\n"); + + } else { + + printf("Failed\n"); + printf(" Status was %lx.\n", NtStatus ); + TestStatus = FALSE; + if ( NT_SUCCESS( NtStatus ) ) { + + SamFreeMemory( Buffer ); + } + } + +// This is the code that USED to test this function, when it was allowed +// for non-trusted clients. +// +// if (NT_SUCCESS(NtStatus)) { +// if (Buffer != NULL) { +// +// if ( (((USER_INTERNAL1_INFORMATION *)Buffer)->CaseInsensitiveDbcs.MaximumLength > 0) && +// (((USER_INTERNAL1_INFORMATION *)Buffer)->CaseInsensitiveDbcs.Buffer != NULL) && +// (((USER_INTERNAL1_INFORMATION *)Buffer)->CaseSensitiveUnicode.MaximumLength > 0) && +// (((USER_INTERNAL1_INFORMATION *)Buffer)->CaseSensitiveUnicode.Buffer != NULL) +// ) { +// +// printf("Succeeded\n"); +// +// // +// // Print them out as strings, even though they've been +// // through a OWF. +// // +// +// printf(" CaseInsensitiveDbcs is: *%s*\n", +// &(((USER_INTERNAL1_INFORMATION *)Buffer)->CaseInsensitiveDbcs) ); +// +// printf(" CaseSensitiveUnicode is: *%s*\n", +// &(((USER_INTERNAL1_INFORMATION *)Buffer)->CaseSensitiveUnicode) ); +// +// +// } else { +// printf("Failed\n"); +// printf(" One of the strings not returned.\n"); +// TestStatus = FALSE; +// } +// SamFreeMemory( Buffer ); +// } else { +// printf("Failed\n"); +// printf(" Buffer address not set on return.\n"); +// printf(" RPC should have allocated a buffer.\n"); +// TestStatus = FALSE; +// } +// } else { +// printf("Failed\n"); +// printf(" Completion status is 0x%lx\n", NtStatus); +// TestStatus = FALSE; +// } + + IgnoreStatus = SamCloseHandle( UserHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + + + + printf(" Query Internal2 Information . . . . . . . . . . . "); + + NtStatus = SamOpenUser( + DomainHandle, + USER_READ_LOGON, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + Buffer = NULL; + NtStatus = SamQueryInformationUser( + UserHandle1, + UserInternal2Information, + &Buffer + ); + + if ( NtStatus == STATUS_INVALID_INFO_CLASS ) { + + // + // We're not a trusted client, so we don't expect to be able + // to do this. + // + + printf("Succeeded.\n"); + + } else { + + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + SamFreeMemory( Buffer ); + } + +// This is the code that USED to test this function, when non-trusted +// clients were allowed to do this... +// +// if (NT_SUCCESS(NtStatus)) { +// if (Buffer != NULL) { +// +// printf("Succeeded\n"); +// +// printf(" last Logon is: (0x%lx, 0x%lx)\n", +// (((USER_INTERNAL2_INFORMATION *)Buffer)->LastLogon.HighPart), +// (((USER_INTERNAL2_INFORMATION *)Buffer)->LastLogon.LowPart) ); +// printf(" last Logoff is: (0x%lx, 0x%lx)\n", +// (((USER_INTERNAL2_INFORMATION *)Buffer)->LastLogoff.HighPart), +// (((USER_INTERNAL2_INFORMATION *)Buffer)->LastLogoff.LowPart) ); +// printf(" BadPwdCount is: (0x%x)\n", +// ((USER_INTERNAL2_INFORMATION *)Buffer)->BadPasswordCount ); +// printf(" LogonCount is: (0x%x)\n", +// ((USER_INTERNAL2_INFORMATION *)Buffer)->LogonCount ); +// +// SamFreeMemory( Buffer ); +// } else { +// printf("Failed\n"); +// printf(" Buffer address not set on return.\n"); +// printf(" RPC should have allocated a buffer.\n"); +// TestStatus = FALSE; +// } +// } else { +// printf("Failed\n"); +// printf(" Completion status is 0x%lx\n", NtStatus); +// TestStatus = FALSE; +// } + + IgnoreStatus = SamCloseHandle( UserHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + + + + printf(" Query Set Password Information . . . . . . . . . . . "); + + + + + NtStatus = SamOpenUser( + DomainHandle, + USER_READ_LOGON, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + Buffer = NULL; + NtStatus = SamQueryInformationUser( + UserHandle1, + UserSetPasswordInformation, + &Buffer + ); + if (NtStatus == STATUS_INVALID_INFO_CLASS ) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + printf(" Expected 0x%lx (INVALID_INFO_CLASS)\n", STATUS_INVALID_INFO_CLASS); + TestStatus = FALSE; + } + IgnoreStatus = SamCloseHandle( UserHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + + + /////////////////////////////////////////////////////////////////////////// + // // + // Get Groups For User Suite // + // // + /////////////////////////////////////////////////////////////////////////// + + printf("\n"); + printf(" Get Groups For User . . . . . . . . . . . . . . . . . Suite\n"); + + printf(" Get Groups For Well-Known Account . . . . . . . . . . "); + + NtStatus = SamOpenUser( + DomainHandle, + USER_LIST_GROUPS, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + Buffer = NULL; + NtStatus = SamGetGroupsForUser( + UserHandle1, + (PGROUP_MEMBERSHIP *)&Buffer, + &MembershipCount + ); + if (NT_SUCCESS(NtStatus)) { + if (Buffer != NULL) { + + printf("Succeeded\n"); + + + printf(" Member of: %d groups\n", MembershipCount); + for ( i=0; i<MembershipCount; i++) { + + printf(" Group[%d] Rid/Attributes: 0x%lx/0x%lx\n", + i, + (((PGROUP_MEMBERSHIP)Buffer)[i].RelativeId), + (((PGROUP_MEMBERSHIP)Buffer)[i].Attributes) + ); + + } + + SamFreeMemory( Buffer ); + + + } else { + printf("Failed\n"); + printf(" Buffer address not set on return.\n"); + printf(" RPC should have allocated a buffer.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + IgnoreStatus = SamCloseHandle( UserHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + + /////////////////////////////////////////////////////////////////////////// + // // + // Set User Suite // + // // + /////////////////////////////////////////////////////////////////////////// + + printf("\n"); + printf(" Set User . . . . . . . . . . . . . . . . . . . . . . Suite\n"); + + printf(" Set General Information . . . . . . . . . . . . . . . "); + NtStatus = SamOpenUser( + DomainHandle, + USER_ALL_ACCESS, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + // + // Make the parameter marshallable, but don't worry about values. + // + + GeneralInformation.UserName = DummyName1; + GeneralInformation.FullName = DummyName1; + GeneralInformation.AdminComment = DummyName1; + GeneralInformation.UserComment = DummyName1; + + Buffer = &GeneralInformation; + NtStatus = SamSetInformationUser( + UserHandle1, + UserGeneralInformation, + Buffer + ); + if (NtStatus == STATUS_INVALID_INFO_CLASS ) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + printf(" Expected 0x%lx (INVALID_INFO_CLASS)\n", STATUS_INVALID_INFO_CLASS); + TestStatus = FALSE; + } + IgnoreStatus = SamCloseHandle( UserHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + + printf(" Set Preferences Information . . . . . . . . . . . . . "); + NtStatus = SamOpenUser( + DomainHandle, + USER_READ_GENERAL | USER_WRITE_PREFERENCES | USER_READ_PREFERENCES, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + // + // Get the current value... + // + + Buffer1 = NULL; + NtStatus = SamQueryInformationUser( + UserHandle1, + UserPreferencesInformation, + &Buffer1 + ); + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(Buffer1 != NULL); + + + // + // Change the fields to new values and write them out. + // + + NameLength = ((USER_PREFERENCES_INFORMATION *)Buffer1)->UserComment.Length; + if ( NameLength == DummyString1.Length ) { + ((USER_PREFERENCES_INFORMATION *)Buffer1)->UserComment = DummyString2; + } else { + ((USER_PREFERENCES_INFORMATION *)Buffer1)->UserComment = DummyString1; + } + + ((USER_PREFERENCES_INFORMATION *)Buffer1)->CountryCode += 1; + ((USER_PREFERENCES_INFORMATION *)Buffer1)->CodePage += 1; + + NtStatus = SamSetInformationUser( + UserHandle1, + UserPreferencesInformation, + Buffer1 + ); + if ( NT_SUCCESS(NtStatus) ) { + + // + // Now check that the change was really made... + // + + NtStatus = SamQueryInformationUser( + UserHandle1, + UserPreferencesInformation, + &Buffer2 + ); + ASSERT(NT_SUCCESS( NtStatus ) ); + if ( + !RtlCompareString( + (PSTRING)&((USER_PREFERENCES_INFORMATION *)Buffer1)->UserComment, + (PSTRING)&((USER_PREFERENCES_INFORMATION *)Buffer2)->UserComment, + TRUE) + && + (((USER_PREFERENCES_INFORMATION *)Buffer1)->CountryCode == + ((USER_PREFERENCES_INFORMATION *)Buffer2)->CountryCode) + && + (((USER_PREFERENCES_INFORMATION *)Buffer1)->CodePage == + ((USER_PREFERENCES_INFORMATION *)Buffer2)->CodePage) + ) { + + printf("Succeeded\n"); + + // + // Change back some fields to keep from screwing up our database + // + + ((USER_PREFERENCES_INFORMATION *)Buffer1)->CountryCode -= 1; + ((USER_PREFERENCES_INFORMATION *)Buffer1)->CodePage -= 1; + + IgnoreStatus = SamSetInformationUser( + UserHandle1, + UserPreferencesInformation, + Buffer1 + ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + } else { + + printf("Failed\n"); + printf(" Values queried don't match values written\n"); + printf(" UserComment Written is %wZ\n", + (PUNICODE_STRING)&((USER_PREFERENCES_INFORMATION *)Buffer1)->UserComment); + printf(" UserComment Retrieved is %wZ\n", + (PUNICODE_STRING)&((USER_PREFERENCES_INFORMATION *)Buffer2)->UserComment); + printf(" CountryCode Written is 0x%lx\n", + (ULONG)((USER_PREFERENCES_INFORMATION *)Buffer1)->CountryCode); + printf(" CountryCode Retrieved is 0x%lx\n", + (ULONG)((USER_PREFERENCES_INFORMATION *)Buffer2)->CountryCode); + printf(" CodePage Written is 0x%lx\n", + (ULONG)((USER_PREFERENCES_INFORMATION *)Buffer1)->CodePage); + printf(" CodePage Retrieved is 0x%lx\n", + (ULONG)((USER_PREFERENCES_INFORMATION *)Buffer2)->CodePage); + + TestStatus = FALSE; + + } + + SamFreeMemory( Buffer1 ); + SamFreeMemory( Buffer2 ); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + SamFreeMemory( Buffer1 ); + + } + + + + + printf(" Set Logon Information . . . . . . . . . . . . . . . . "); + NtStatus = SamOpenUser( + DomainHandle, + USER_ALL_ACCESS, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + // + // Make the parameter marshallable, but don't worry about values. + // + + LogonInformation.UserName = DummyName1; + LogonInformation.FullName = DummyName1; + LogonInformation.HomeDirectory = DummyName1; + LogonInformation.HomeDirectoryDrive = DummyName1; + LogonInformation.ScriptPath = DummyName1; + LogonInformation.ProfilePath = DummyName1; + LogonInformation.WorkStations = DummyName1; + + LogonInformation.LogonHours = DummyLogonHours; + + Buffer = &LogonInformation; + NtStatus = SamSetInformationUser( + UserHandle1, + UserLogonInformation, + Buffer + ); + if (NtStatus == STATUS_INVALID_INFO_CLASS ) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + printf(" Expected 0x%lx (INVALID_INFO_CLASS)\n", STATUS_INVALID_INFO_CLASS); + TestStatus = FALSE; + } + IgnoreStatus = SamCloseHandle( UserHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + + printf(" Set Logon Hours Information . . . . . . . . . . . . . "); + NtStatus = SamOpenUser( + DomainHandle, + USER_WRITE_ACCOUNT | USER_READ_LOGON, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + // + // Get the current value... + // + + Buffer1 = NULL; + NtStatus = SamQueryInformationUser( + UserHandle1, + UserLogonHoursInformation, + &Buffer1 + ); + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(Buffer1 != NULL); + ASSERT( ((USER_LOGON_HOURS_INFORMATION *)Buffer1)->LogonHours.LogonHours + != NULL); //Don't support zero length bit masks in this test yet. + + + // + // Change the field to a new value and write it out. + // We have two choices for out test: + // NoLogonRestriction + // DummyLogonHours + // + // They are guaranteed to have different values in the + // LOGON_HOURS_DIFFERENT_OFFSET byte of their respective bit masks. + // + + if ( 0 == ((USER_LOGON_HOURS_INFORMATION *)Buffer1)->LogonHours.LogonHours[LOGON_HOURS_DIFFERENT_OFFSET]) { + ((USER_LOGON_HOURS_INFORMATION *)Buffer1)->LogonHours = DummyLogonHours; + } else { + ((USER_LOGON_HOURS_INFORMATION *)Buffer1)->LogonHours = NoLogonRestriction; + } + + NtStatus = SamSetInformationUser( + UserHandle1, + UserLogonHoursInformation, + Buffer1 + ); + if ( NT_SUCCESS(NtStatus) ) { + + // + // Now check that the change was really made... + // + + NtStatus = SamQueryInformationUser( + UserHandle1, + UserLogonHoursInformation, + &Buffer2 + ); + ASSERT(NT_SUCCESS( NtStatus ) ); + if ( + ((USER_LOGON_HOURS_INFORMATION *)Buffer1)->LogonHours.LogonHours[LOGON_HOURS_DIFFERENT_OFFSET] + == + ((USER_LOGON_HOURS_INFORMATION *)Buffer2)->LogonHours.LogonHours[LOGON_HOURS_DIFFERENT_OFFSET] + ) { + + printf("Succeeded\n"); + + } else { + + printf("Failed\n"); + printf(" Value queried doesn't match value written\n"); + printf(" Units Written are 0x%lx\n", + ((USER_LOGON_HOURS_INFORMATION *)Buffer1)->LogonHours.UnitsPerWeek); + printf(" Units Retrieved are 0x%lx\n", + ((USER_LOGON_HOURS_INFORMATION *)Buffer2)->LogonHours.UnitsPerWeek); + + printf(" Byte 0x%lx of the written bit mask is 0x%lx\n", + LOGON_HOURS_DIFFERENT_OFFSET, + (ULONG)((USER_LOGON_HOURS_INFORMATION *)Buffer1)->LogonHours.LogonHours[LOGON_HOURS_DIFFERENT_OFFSET] + ); + printf(" Byte 0x%lx of the retrieved bit mask is 0x%lx\n", + LOGON_HOURS_DIFFERENT_OFFSET, + (ULONG)((USER_LOGON_HOURS_INFORMATION *)Buffer2)->LogonHours.LogonHours[LOGON_HOURS_DIFFERENT_OFFSET] + ); + + TestStatus = FALSE; + + } + + SamFreeMemory( Buffer1 ); + SamFreeMemory( Buffer2 ); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + SamFreeMemory( Buffer1 ); + + } + + + + + + printf(" Set Account Information . . . . . . . . . . . . . . . "); + NtStatus = SamOpenUser( + DomainHandle, + USER_WRITE_ACCOUNT | + USER_READ_GENERAL | + USER_READ_PREFERENCES | + USER_READ_LOGON, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + // + // Make the parameter marshallable, but don't worry about values. + // + + AccountInformation.UserName = DummyName1; + AccountInformation.FullName = DummyName1; + AccountInformation.HomeDirectory = DummyName1; + AccountInformation.HomeDirectoryDrive = DummyName1; + AccountInformation.ScriptPath = DummyName1; + AccountInformation.ProfilePath = DummyName1; + AccountInformation.AdminComment = DummyName1; + AccountInformation.WorkStations = DummyName1; + + AccountInformation.LogonHours = DummyLogonHours; + + Buffer = &AccountInformation; + NtStatus = SamSetInformationUser( + UserHandle1, + UserAccountInformation, + Buffer + ); + if (NtStatus == STATUS_INVALID_INFO_CLASS ) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + printf(" Expected 0x%lx (INVALID_INFO_CLASS)\n", STATUS_INVALID_INFO_CLASS); + TestStatus = FALSE; + } + IgnoreStatus = SamCloseHandle( UserHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + printf(" Set Home . . . . . . . . . . . . . . . . . . . . . . "); + NtStatus = SamOpenUser( + DomainHandle, + USER_WRITE_ACCOUNT | USER_READ_LOGON, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + // + // Get the current value... + // + + Buffer1 = NULL; + NtStatus = SamQueryInformationUser( + UserHandle1, + UserHomeInformation, + &Buffer1 + ); + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(Buffer1 != NULL); + + + // + // Change the field to a new value and write it out. + // + + NameLength = ((USER_HOME_INFORMATION *)Buffer1)->HomeDirectory.Length; + if ( NameLength == DummyString1.Length ) { + ((USER_HOME_INFORMATION *)Buffer1)->HomeDirectory = DummyString2; + } else { + ((USER_HOME_INFORMATION *)Buffer1)->HomeDirectory = DummyString1; + } + + NameLength = ((USER_HOME_INFORMATION *)Buffer1)->HomeDirectoryDrive.Length; + if ( NameLength == DummyString1.Length ) { + ((USER_HOME_INFORMATION *)Buffer1)->HomeDirectoryDrive = DummyString2; + } else { + ((USER_HOME_INFORMATION *)Buffer1)->HomeDirectoryDrive = DummyString1; + } + + NtStatus = SamSetInformationUser( + UserHandle1, + UserHomeInformation, + Buffer1 + ); + if ( NT_SUCCESS(NtStatus) ) { + + // + // Now check that the change was really made... + // + + NtStatus = SamQueryInformationUser( + UserHandle1, + UserHomeInformation, + &Buffer2 + ); + ASSERT(NT_SUCCESS( NtStatus ) ); + + if (!RtlCompareString( + (PSTRING)&((USER_HOME_INFORMATION *)Buffer1)->HomeDirectory, + (PSTRING)&((USER_HOME_INFORMATION *)Buffer2)->HomeDirectory, + TRUE) ) { + + if (!RtlCompareString( + (PSTRING)&((USER_HOME_INFORMATION *)Buffer1)->HomeDirectoryDrive, + (PSTRING)&((USER_HOME_INFORMATION *)Buffer2)->HomeDirectoryDrive, + TRUE) + ) { + printf("Succeeded\n"); + } else { + + printf("Failed\n"); + printf(" Drive Value queried doesn't match value written\n"); + printf(" Value Written is %wZ\n", + (PUNICODE_STRING)&((USER_HOME_INFORMATION *)Buffer1)->HomeDirectoryDrive); + printf(" Value Retrieved is %wZ\n", + (PUNICODE_STRING)&((USER_HOME_INFORMATION *)Buffer2)->HomeDirectoryDrive); + + TestStatus = FALSE; + } + + } else { + + printf("Failed\n"); + printf(" Directory Value queried doesn't match value written\n"); + printf(" Value Written is %wZ\n", + (PUNICODE_STRING)&((USER_HOME_INFORMATION *)Buffer1)->HomeDirectory); + printf(" Value Retrieved is %wZ\n", + (PUNICODE_STRING)&((USER_HOME_INFORMATION *)Buffer2)->HomeDirectory); + + TestStatus = FALSE; + + } + + SamFreeMemory( Buffer1 ); + SamFreeMemory( Buffer2 ); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + SamFreeMemory( Buffer1 ); + + } + + + + + printf(" Set Script . . . . . . . . . . . . . . . . . . . . . "); + NtStatus = SamOpenUser( + DomainHandle, + USER_WRITE_ACCOUNT | USER_READ_LOGON, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + // + // Get the current value... + // + + Buffer1 = NULL; + NtStatus = SamQueryInformationUser( + UserHandle1, + UserScriptInformation, + &Buffer1 + ); + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(Buffer1 != NULL); + + + // + // Change the field to a new value and write it out. + // + + NameLength = ((USER_SCRIPT_INFORMATION *)Buffer1)->ScriptPath.Length; + if ( NameLength == DummyString1.Length ) { + ((USER_SCRIPT_INFORMATION *)Buffer1)->ScriptPath = DummyString2; + } else { + ((USER_SCRIPT_INFORMATION *)Buffer1)->ScriptPath = DummyString1; + } + + NtStatus = SamSetInformationUser( + UserHandle1, + UserScriptInformation, + Buffer1 + ); + if ( NT_SUCCESS(NtStatus) ) { + + // + // Now check that the change was really made... + // + + NtStatus = SamQueryInformationUser( + UserHandle1, + UserScriptInformation, + &Buffer2 + ); + ASSERT(NT_SUCCESS( NtStatus ) ); + if ( + !RtlCompareString( + (PSTRING)&((USER_SCRIPT_INFORMATION *)Buffer1)->ScriptPath, + (PSTRING)&((USER_SCRIPT_INFORMATION *)Buffer2)->ScriptPath, + TRUE) + ) { + + printf("Succeeded\n"); + + } else { + + printf("Failed\n"); + printf(" Value queried doesn't match value written\n"); + printf(" Value Written is %wZ\n", + (PUNICODE_STRING)&((USER_SCRIPT_INFORMATION *)Buffer1)->ScriptPath); + printf(" Value Retrieved is %wZ\n", + (PUNICODE_STRING)&((USER_SCRIPT_INFORMATION *)Buffer2)->ScriptPath); + + TestStatus = FALSE; + + } + + SamFreeMemory( Buffer1 ); + SamFreeMemory( Buffer2 ); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + SamFreeMemory( Buffer1 ); + + } + + + + + printf(" Set Profile . . . . . . . . . . . . . . . . . . . . . "); + NtStatus = SamOpenUser( + DomainHandle, + USER_WRITE_ACCOUNT | USER_READ_LOGON, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + // + // Get the current value... + // + + Buffer1 = NULL; + NtStatus = SamQueryInformationUser( + UserHandle1, + UserProfileInformation, + &Buffer1 + ); + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(Buffer1 != NULL); + + + // + // Change the field to a new value and write it out. + // + + NameLength = ((USER_PROFILE_INFORMATION *)Buffer1)->ProfilePath.Length; + if ( NameLength == DummyString1.Length ) { + ((USER_PROFILE_INFORMATION *)Buffer1)->ProfilePath = DummyString2; + } else { + ((USER_PROFILE_INFORMATION *)Buffer1)->ProfilePath = DummyString1; + } + + NtStatus = SamSetInformationUser( + UserHandle1, + UserProfileInformation, + Buffer1 + ); + if ( NT_SUCCESS(NtStatus) ) { + + // + // Now check that the change was really made... + // + + NtStatus = SamQueryInformationUser( + UserHandle1, + UserProfileInformation, + &Buffer2 + ); + ASSERT(NT_SUCCESS( NtStatus ) ); + if ( + !RtlCompareString( + (PSTRING)&((USER_PROFILE_INFORMATION *)Buffer1)->ProfilePath, + (PSTRING)&((USER_PROFILE_INFORMATION *)Buffer2)->ProfilePath, + TRUE) + ) { + + printf("Succeeded\n"); + + } else { + + printf("Failed\n"); + printf(" Value queried doesn't match value written\n"); + printf(" Value Written is %wZ\n", + (PUNICODE_STRING)&((USER_PROFILE_INFORMATION *)Buffer1)->ProfilePath); + printf(" Value Retrieved is %wZ\n", + (PUNICODE_STRING)&((USER_PROFILE_INFORMATION *)Buffer2)->ProfilePath); + + TestStatus = FALSE; + + } + + SamFreeMemory( Buffer1 ); + SamFreeMemory( Buffer2 ); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + SamFreeMemory( Buffer1 ); + + } + + + + + printf(" Set Admin Comment . . . . . . . . . . . . . . . . . . "); + + NtStatus = SamOpenUser( + DomainHandle, + USER_WRITE_ACCOUNT | USER_READ_GENERAL, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + // + // Get the current value... + // + + Buffer1 = NULL; + NtStatus = SamQueryInformationUser( + UserHandle1, + UserAdminCommentInformation, + &Buffer1 + ); + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(Buffer1 != NULL); + + + // + // Change the field to a new value and write it out. + // + + NameLength = ((USER_ADMIN_COMMENT_INFORMATION *)Buffer1)->AdminComment.Length; + if ( NameLength == DummyString1.Length ) { + ((USER_ADMIN_COMMENT_INFORMATION *)Buffer1)->AdminComment = DummyString2; + } else { + ((USER_ADMIN_COMMENT_INFORMATION *)Buffer1)->AdminComment = DummyString1; + } + + NtStatus = SamSetInformationUser( + UserHandle1, + UserAdminCommentInformation, + Buffer1 + ); + if ( NT_SUCCESS(NtStatus) ) { + + // + // Now check that the change was really made... + // + + NtStatus = SamQueryInformationUser( + UserHandle1, + UserAdminCommentInformation, + &Buffer2 + ); + ASSERT(NT_SUCCESS( NtStatus ) ); + if ( + !RtlCompareString( + (PSTRING)&((USER_ADMIN_COMMENT_INFORMATION *)Buffer1)->AdminComment, + (PSTRING)&((USER_ADMIN_COMMENT_INFORMATION *)Buffer2)->AdminComment, + TRUE) + ) { + + printf("Succeeded\n"); + + } else { + + printf("Failed\n"); + printf(" Value queried doesn't match value written\n"); + printf(" Value Written is %wZ\n", + (PUNICODE_STRING)&((USER_ADMIN_COMMENT_INFORMATION *)Buffer1)->AdminComment); + printf(" Value Retrieved is %wZ\n", + (PUNICODE_STRING)&((USER_ADMIN_COMMENT_INFORMATION *)Buffer2)->AdminComment); + + TestStatus = FALSE; + + } + + SamFreeMemory( Buffer1 ); + SamFreeMemory( Buffer2 ); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + SamFreeMemory( Buffer1 ); + + } + + + printf(" Set Workstations . . . . . . . . . . . . . . . . . . "); + printf("BROKEN TEST - NOT TESTED\n"); +#ifdef BROKEN + NtStatus = SamOpenUser( + DomainHandle, + USER_WRITE_ACCOUNT | USER_READ_LOGON, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + // + // Get the current value... + // + + Buffer1 = NULL; + NtStatus = SamQueryInformationUser( + UserHandle1, + UserWorkStationsInformation, + &Buffer1 + ); + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(Buffer1 != NULL); + + + // + // Change the field to a new value and write it out. + // + + NameLength = ((USER_WORKSTATIONS_INFORMATION *)Buffer1)->WorkStations.Length; + if ( NameLength == DummyString1.Length ) { + ((USER_WORKSTATIONS_INFORMATION *)Buffer1)->WorkStations = DummyString2; + } else { + ((USER_WORKSTATIONS_INFORMATION *)Buffer1)->WorkStations = DummyString1; + } + + NtStatus = SamSetInformationUser( + UserHandle1, + UserWorkStationsInformation, + Buffer1 + ); + if ( NT_SUCCESS(NtStatus) ) { + + // + // Now check that the change was really made... + // + + NtStatus = SamQueryInformationUser( + UserHandle1, + UserWorkStationsInformation, + &Buffer2 + ); + ASSERT(NT_SUCCESS( NtStatus ) ); + if ( + !RtlCompareString( + (PSTRING)&((USER_WORKSTATIONS_INFORMATION *)Buffer1)->WorkStations, + (PSTRING)&((USER_WORKSTATIONS_INFORMATION *)Buffer2)->WorkStations, + TRUE) + ) { + + printf("Succeeded\n"); + + } else { + + printf("Failed\n"); + printf(" Value queried doesn't match value written\n"); + printf(" Value Written is %wZ\n", + (PUNICODE_STRING)&((USER_WORKSTATIONS_INFORMATION *)Buffer1)->WorkStations); + printf(" Value Retrieved is %wZ\n", + (PUNICODE_STRING)&((USER_WORKSTATIONS_INFORMATION *)Buffer2)->WorkStations); + + TestStatus = FALSE; + + } + + SamFreeMemory( Buffer1 ); + SamFreeMemory( Buffer2 ); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + SamFreeMemory( Buffer1 ); + + } +#endif //BROKEN + + + printf(" Set Internal1 . . . . . . . . . . . . . . . . . . . "); + + NtStatus = SamOpenUser( + DomainHandle, + USER_WRITE_ACCOUNT | USER_READ_LOGON | USER_FORCE_PASSWORD_CHANGE, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + // + // We can't get the current values, since this level is only + // queryable by trusted clients. So just try setting a couple + // of values and make sure that we don't get an error. + // + + Buffer1 = RtlAllocateHeap( RtlProcessHeap(), 0, sizeof(USER_INTERNAL1_INFORMATION) ); + ASSERT( Buffer1 != NULL ); + + ((PUSER_INTERNAL1_INFORMATION)Buffer1)->NtPasswordPresent = FALSE; + ((PUSER_INTERNAL1_INFORMATION)Buffer1)->LmPasswordPresent = FALSE; + + NtStatus = SamSetInformationUser( + UserHandle1, + UserInternal1Information, + Buffer1 + ); + + if (NtStatus != STATUS_PASSWORD_RESTRICTION) { + + printf("Failed\n"); + printf(" Expected Status = 0x%lx\n", STATUS_PASSWORD_RESTRICTION); + printf(" Received Status = 0x%lx\n", NtStatus ); + TestStatus = FALSE; + + } else { + + // + // The NULL password worked, so let's try a real password. + // + + NtStatus = RtlCalculateNtOwfPassword( + &DummyName1, + &((PUSER_INTERNAL1_INFORMATION)Buffer1)->NtOwfPassword + ); + ASSERT(NT_SUCCESS(NtStatus)); + + ((PUSER_INTERNAL1_INFORMATION)Buffer1)->NtPasswordPresent = TRUE; + + NtStatus = RtlCalculateLmOwfPassword( + DUMMY_STRING1, + &((PUSER_INTERNAL1_INFORMATION)Buffer1)->LmOwfPassword + ); + ASSERT(NT_SUCCESS(NtStatus)); + + ((PUSER_INTERNAL1_INFORMATION)Buffer1)->LmPasswordPresent = TRUE; + + NtStatus = SamSetInformationUser( + UserHandle1, + UserInternal1Information, + Buffer1 + ); + + if ( NT_SUCCESS(NtStatus) ) { + + printf("Succeeded\n"); + + } else { + + printf("Failed\n"); + printf(" Return status was %lx\n", NtStatus ); + TestStatus = FALSE; + } + } + + RtlFreeHeap( RtlProcessHeap(), 0, Buffer1 ); + + +// This is the code that used to be here, when UserInternal1Information was +// queryable by non-trusted clients... +// +// Buffer1 = NULL; +// NtStatus = SamQueryInformationUser( +// UserHandle1, +// UserInternal1Information, +// &Buffer1 +// ); +// TST_SUCCESS_ASSERT(NtStatus); +// ASSERT(Buffer1 != NULL); +// +// // +// // The passwords were initially empty. Put in some random +// // OWF passwords, and have them written out. +// // +// +// NtStatus = RtlCalculateNtOwfPassword( +// (PNT_PASSWORD)&DummyName1, +// &EncryptedPasswordBuffer +// ); +// +// ((USER_INTERNAL1_INFORMATION *)Buffer1)->CaseSensitiveUnicode.Buffer = (PCHAR)&EncryptedPasswordBuffer; +// ((USER_INTERNAL1_INFORMATION *)Buffer1)->CaseSensitiveUnicode.Length = 16; +// ((USER_INTERNAL1_INFORMATION *)Buffer1)->CaseSensitiveUnicode.MaximumLength = 16; +// +// NtStatus = RtlCalculateNtOwfPassword( +// (PNT_PASSWORD)&DummyName2, +// &EncryptedPasswordBuffer2 +// ); +// +// ((USER_INTERNAL1_INFORMATION *)Buffer1)->CaseInsensitiveDbcs.Buffer = (PCHAR)&EncryptedPasswordBuffer2; +// ((USER_INTERNAL1_INFORMATION *)Buffer1)->CaseInsensitiveDbcs.Length = 16; +// ((USER_INTERNAL1_INFORMATION *)Buffer1)->CaseInsensitiveDbcs.MaximumLength = 16; +// +// NtStatus = SamSetInformationUser( +// UserHandle1, +// UserInternal1Information, +// Buffer1 +// ); +// if ( NT_SUCCESS(NtStatus) ) { +// +// // +// // Now check that the change was really made... +// // +// +// NtStatus = SamQueryInformationUser( +// UserHandle1, +// UserInternal1Information, +// &Buffer2 +// ); +// ASSERT(NT_SUCCESS( NtStatus ) ); +// +// if ( ( +// !RtlCompareString( +// (PSTRING)&((USER_INTERNAL1_INFORMATION *)Buffer1)->CaseSensitiveUnicode, +// (PSTRING)&((USER_INTERNAL1_INFORMATION *)Buffer2)->CaseSensitiveUnicode, +// TRUE) +// ) || ( +// !RtlCompareString( +// (PSTRING)&((USER_INTERNAL1_INFORMATION *)Buffer1)->CaseInsensitiveDbcs, +// (PSTRING)&((USER_INTERNAL1_INFORMATION *)Buffer2)->CaseInsensitiveDbcs, +// TRUE) +// ) ) { +// +// printf("Succeeded\n"); +// +// } else { +// +// printf("Failed\n"); +// printf(" Value queried doesn't match value written\n"); +// printf(" CaseInsensitiveDbcs Written is %wZ\n", +// (PUNICODE_STRING)&((USER_INTERNAL1_INFORMATION *)Buffer1)->CaseInsensitiveDbcs); +// printf(" CaseInsensitiveDbcs Retrieved is %wZ\n", +// (PUNICODE_STRING)&((USER_INTERNAL1_INFORMATION *)Buffer2)->CaseInsensitiveDbcs); +// printf(" CaseSensitiveUnicode Written is %wZ\n", +// (PUNICODE_STRING)&((USER_INTERNAL1_INFORMATION *)Buffer1)->CaseSensitiveUnicode); +// printf(" CaseSensitiveUnicode Retrieved is %wZ\n", +// (PUNICODE_STRING)&((USER_INTERNAL1_INFORMATION *)Buffer2)->CaseSensitiveUnicode); +// +// TestStatus = FALSE; +// +// } +// +// SamFreeMemory( Buffer1 ); +// SamFreeMemory( Buffer2 ); +// +// } else { +// printf("Failed\n"); +// printf(" Completion status is 0x%lx\n", NtStatus); +// TestStatus = FALSE; +// SamFreeMemory( Buffer1 ); +// +// } + + + + printf(" Set Internal2 . . . . . . . . . . . . . . . . . . . "); + + NtStatus = SamOpenUser( + DomainHandle, + USER_WRITE_ACCOUNT | USER_READ_LOGON, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + // + // We can't get the current values, since this level is only + // queryable by trusted clients. We can't set either, but + // try it and make sure we get the correct error. + // + + Buffer1 = RtlAllocateHeap( RtlProcessHeap(), 0, sizeof(USER_INTERNAL2_INFORMATION) ); + ASSERT( Buffer1 != NULL ); + + ((USER_INTERNAL2_INFORMATION *)Buffer1)->LastLogon.HighPart = 1; + ((USER_INTERNAL2_INFORMATION *)Buffer1)->LastLogoff.HighPart = 2; + ((USER_INTERNAL2_INFORMATION *)Buffer1)->LastLogon.LowPart = 3; + ((USER_INTERNAL2_INFORMATION *)Buffer1)->LastLogoff.LowPart = 4; + ((USER_INTERNAL2_INFORMATION *)Buffer1)->BadPasswordCount = 5; + ((USER_INTERNAL2_INFORMATION *)Buffer1)->LogonCount = 6; + + NtStatus = SamSetInformationUser( + UserHandle1, + UserInternal2Information, + Buffer1 + ); + + RtlFreeHeap( RtlProcessHeap(), 0, Buffer1 ); + + if ( NtStatus == STATUS_INVALID_INFO_CLASS ) { + + printf("Succeeded\n"); + + } else { + + printf("Failed\n"); + printf(" Expected Status = 0x%lx\n", STATUS_INVALID_INFO_CLASS); + printf(" Received Status = 0x%lx\n", NtStatus ); + TestStatus = FALSE; + } + +// This is the code that was here when UserInternal2Information could be +// queried and set by non-trusted clients... +// +// // +// // Get the current values... +// // +// +// Buffer1 = NULL; +// NtStatus = SamQueryInformationUser( +// UserHandle1, +// UserInternal2Information, +// &Buffer1 +// ); +// TST_SUCCESS_ASSERT(NtStatus); +// ASSERT(Buffer1 != NULL); +// +// // +// // Now change the fields and write them out. +// // +// +// ((USER_INTERNAL2_INFORMATION *)Buffer1)->LastLogon.HighPart += 1; +// ((USER_INTERNAL2_INFORMATION *)Buffer1)->LastLogoff.HighPart += 1; +// ((USER_INTERNAL2_INFORMATION *)Buffer1)->LastLogon.LowPart += 2; +// ((USER_INTERNAL2_INFORMATION *)Buffer1)->LastLogoff.LowPart += 2; +// ((USER_INTERNAL2_INFORMATION *)Buffer1)->BadPasswordCount += 1; +// ((USER_INTERNAL2_INFORMATION *)Buffer1)->LogonCount += 1; +// +// NtStatus = SamSetInformationUser( +// UserHandle1, +// UserInternal2Information, +// Buffer1 +// ); +// if ( NT_SUCCESS(NtStatus) ) { +// +// // +// // Now check that the change was really made... +// // +// +// NtStatus = SamQueryInformationUser( +// UserHandle1, +// UserInternal2Information, +// &Buffer2 +// ); +// ASSERT(NT_SUCCESS( NtStatus ) ); +// if ( +// (((USER_INTERNAL2_INFORMATION *)Buffer1)->LastLogon.HighPart == +// ((USER_INTERNAL2_INFORMATION *)Buffer2)->LastLogon.HighPart) && +// (((USER_INTERNAL2_INFORMATION *)Buffer1)->LastLogon.LowPart == +// ((USER_INTERNAL2_INFORMATION *)Buffer2)->LastLogon.LowPart) && +// (((USER_INTERNAL2_INFORMATION *)Buffer1)->LastLogoff.HighPart == +// ((USER_INTERNAL2_INFORMATION *)Buffer2)->LastLogoff.HighPart) && +// (((USER_INTERNAL2_INFORMATION *)Buffer1)->LastLogoff.LowPart == +// ((USER_INTERNAL2_INFORMATION *)Buffer2)->LastLogoff.LowPart) && +// (((USER_INTERNAL2_INFORMATION *)Buffer1)->BadPasswordCount == +// ((USER_INTERNAL2_INFORMATION *)Buffer2)->BadPasswordCount) && +// (((USER_INTERNAL2_INFORMATION *)Buffer1)->LogonCount == +// ((USER_INTERNAL2_INFORMATION *)Buffer2)->LogonCount) +// ) { +// +// printf("Succeeded\n"); +// +// } else { +// +// printf("Failed\n"); +// printf(" Value queried doesn't match value written\n"); +// +// TestStatus = FALSE; +// +// } +// +// SamFreeMemory( Buffer1 ); +// SamFreeMemory( Buffer2 ); +// +// } else { +// printf("Failed\n"); +// printf(" Completion status is 0x%lx\n", NtStatus); +// TestStatus = FALSE; +// SamFreeMemory( Buffer1 ); +// +// } + + + + printf(" Set Password . . . . . . . . . . . . . . . . . . . . "); + + NtStatus = SamOpenUser( + DomainHandle, + USER_FORCE_PASSWORD_CHANGE, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + // + // Create a fake cleartext UNICODE password and write it out. + // + + NtStatus = SamSetInformationUser( + UserHandle1, + UserSetPasswordInformation, + &DummyName2 + ); + if ( NT_SUCCESS(NtStatus) ) { + + // + // We can't verify that it really worked, so we just have + // to trust the return code. + // + + printf("Succeeded\n"); + + } else { + + printf("Failed\n"); + printf(" Return code was %lx\n", NtStatus ); + TestStatus = FALSE; + } + + + + printf(" Set Control . . . . . . . . . . . . . . . . . . . . . "); + NtStatus = SamOpenUser( + DomainHandle, + USER_WRITE_ACCOUNT | USER_READ_ACCOUNT, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + Buffer1 = NULL; + NtStatus = SamQueryInformationUser( + UserHandle1, + UserControlInformation, + &Buffer1 + ); + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(Buffer1 != NULL); + + // + // Change the value and write it back + // + + ((USER_CONTROL_INFORMATION *)Buffer1)->UserAccountControl ^= USER_HOME_DIRECTORY_REQUIRED; + + + NtStatus = SamSetInformationUser( + UserHandle1, + UserControlInformation, + Buffer1 + ); + if (NT_SUCCESS(NtStatus)) { + + // + // Check the written value to make sure it stuck + // + + Buffer2 = NULL; + NtStatus = SamQueryInformationUser( + UserHandle1, + UserControlInformation, + &Buffer2 + ); + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(Buffer2 != NULL); + + if ( ((USER_CONTROL_INFORMATION *)Buffer1)->UserAccountControl == + ((USER_CONTROL_INFORMATION *)Buffer2)->UserAccountControl ) { + + printf("Succeeded\n"); + + SamFreeMemory( Buffer2 ); + + // + // Make sure the account is left enabled to prevent problems. + // + + ((USER_CONTROL_INFORMATION *)Buffer1)->UserAccountControl &= ~USER_ACCOUNT_DISABLED; + + IgnoreStatus = SamSetInformationUser( + UserHandle1, + UserControlInformation, + Buffer1 + ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + } else { + printf("Failed\n"); + printf(" Returned Value Doesn't Match Set Value.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + SamFreeMemory( Buffer1 ); + IgnoreStatus = SamCloseHandle( UserHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + + printf(" Set Expires . . . . . . . . . . . . . . . . . . . . . "); + printf("BROKEN TEST - NOT TESTED\n"); +#ifdef BROKEN + NtStatus = SamOpenUser( + DomainHandle, + USER_WRITE_ACCOUNT | USER_READ_ACCOUNT, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + Buffer1 = NULL; + NtStatus = SamQueryInformationUser( + UserHandle1, + UserExpiresInformation, + &Buffer1 + ); + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(Buffer1 != NULL); + + // + // Change the value and write it back + // + + ((USER_EXPIRES_INFORMATION *)Buffer1)->AccountExpires.LowPart += 1234; + ((USER_EXPIRES_INFORMATION *)Buffer1)->AccountExpires.HighPart += 1234; + + + NtStatus = SamSetInformationUser( + UserHandle1, + UserExpiresInformation, + Buffer1 + ); + if (NT_SUCCESS(NtStatus)) { + + // + // Check the written value to make sure it stuck + // + + Buffer2 = NULL; + NtStatus = SamQueryInformationUser( + UserHandle1, + UserExpiresInformation, + &Buffer2 + ); + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(Buffer2 != NULL); + + if ( ( ((USER_EXPIRES_INFORMATION *)Buffer1)->AccountExpires.LowPart == + ((USER_EXPIRES_INFORMATION *)Buffer2)->AccountExpires.LowPart ) && + ( ((USER_EXPIRES_INFORMATION *)Buffer1)->AccountExpires.HighPart == + ((USER_EXPIRES_INFORMATION *)Buffer2)->AccountExpires.HighPart ) ) { + + printf("Succeeded\n"); + + SamFreeMemory( Buffer2 ); + + // + // Change the values back + // + + ((USER_EXPIRES_INFORMATION *)Buffer1)->AccountExpires.LowPart += 1234; + ((USER_EXPIRES_INFORMATION *)Buffer1)->AccountExpires.HighPart += 1234; + + IgnoreStatus = SamSetInformationUser( + UserHandle1, + UserExpiresInformation, + Buffer1 + ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + } else { + printf("Failed\n"); + printf(" Returned Value Doesn't Match Set Value.\n"); + TestStatus = FALSE; + } + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + SamFreeMemory( Buffer1 ); + IgnoreStatus = SamCloseHandle( UserHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); +#endif //BROKEN + + + + /////////////////////////////////////////////////////////////////////////// + // // + // Change Password For User Suite // + // // + /////////////////////////////////////////////////////////////////////////// + + printf("\n"); + printf(" Change Password For User . . . . . . . . . . . . . . Suite\n"); + + printf(" Change Password For Well-Known User . . . . . . . . . "); + + NtStatus = SamOpenUser( + DomainHandle, + USER_CHANGE_PASSWORD, + DOMAIN_USER_RID_ADMIN, + &UserHandle1 + ); + ASSERT(NT_SUCCESS(NtStatus) ); + + Buffer = NULL; + + // + // The current password is DummyName2. Using DummyName2 as the + // old password, change it to DummyName1 and make sure we get + // STATUS_SUCCESS. + // + + NtStatus = SamChangePasswordUser( + UserHandle1, + &DummyName2, + &DummyName1 + ); + + // + // The current password is DummyName1. Using something WRONG for + // the old password, try to change it to DummyName2 and make sure + // it doesn't succeed. + // + + if ( NtStatus == STATUS_SUCCESS ) { + + NtStatus = SamChangePasswordUser( + UserHandle1, + &DummyName2, + &DummyName2 + ); + + if ( NtStatus == STATUS_SUCCESS ) { + + NtStatus = STATUS_UNSUCCESSFUL; + + } else { + + NtStatus = STATUS_SUCCESS; + } + } + + // + // The current password is DummyName1. Using DummyName1 as the + // old password, change it to DummyName2 and make sure it works + // since by default there is no password history. + // + + if ( NtStatus == STATUS_SUCCESS ) { + + NtStatus = SamChangePasswordUser( + UserHandle1, + &DummyName1, + &DummyName2 + ); + } + + if ( NT_SUCCESS( NtStatus ) ) { + + printf("Succeeded\n"); + + } else { + + printf("Failed\n"); + printf(" Status is %lx\n", NtStatus); + + TestStatus = FALSE; + } + + IgnoreStatus = SamCloseHandle( UserHandle1 ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + } + +// END PASS #1 + + if (Pass == 2) { + + printf("\n"); + printf("\n"); + printf(" User (Pass #2) . . . . . . . . . . . . . . . . . . . Test\n"); + + + /////////////////////////////////////////////////////////////////////////// + // // + // Delete User Suite // + // // + /////////////////////////////////////////////////////////////////////////// + + printf("\n"); + printf(" Delete User . . . . . . . . . . . . . . . . . . . . Suite\n"); + + + + printf(" Delete Normal User . . . . . . . . . . . . . . . . . "); + + // + // This User was created in pass #1 + // + + RtlInitString( &AccountNameAnsi, USER_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountNames[0], &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + NtStatus = SamLookupNamesInDomain( + DomainHandle, + 1, + &AccountNames[0], + &LookedUpRids, + &LookedUpUses + ); + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(LookedUpUses[0] == SidTypeUser); + RtlFreeUnicodeString( &AccountNames[0] ); + + + + UserHandle1 = NULL; + + NtStatus = SamOpenUser( DomainHandle, DELETE, LookedUpRids[0], &UserHandle1 ); + TST_SUCCESS_ASSERT(NtStatus); + SamFreeMemory( LookedUpUses ); SamFreeMemory( LookedUpRids ); + + NtStatus = SamDeleteUser( UserHandle1 ); + if (NT_SUCCESS(NtStatus)) { + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + + + printf(" Delete Admin Group Member . . . . . . . . . . . . . . "); + printf("(Unimplemented)\n"); + + printf(" Delete Last Admin Group Member . . . . . . . . . . . "); + printf("(Unimplemented)\n"); + + + + + + /////////////////////////////////////////////////////////////////////////// + // // + // Set User Suite // + // // + /////////////////////////////////////////////////////////////////////////// + + printf("\n"); + printf(" Set User (Pass 2) . . . . . . . . . . . . . . . . . . Suite\n"); + + printf(" Set ALL information. . . . . . . . . . . . "); + printf("BROKEN TEST - NOT TESTED\n"); +#ifdef BROKEN + + RtlInitString( &AccountNameAnsi, "AllUser" ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + UserRid = 0; + UserHandle1 = NULL; + NtStatus = SamCreateUserInDomain( + DomainHandle, + &AccountName, + USER_ALL_ACCESS, + &UserHandle1, + &UserRid + ); + RtlFreeUnicodeString( &AccountName ); + + ASSERT(NT_SUCCESS(NtStatus)); + + All = NULL; + + NtStatus = SamQueryInformationUser( + UserHandle1, + UserAllInformation, + &All + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // Now change some of the data, and set it + // + + RtlInitString( &TmpAnsiString, "FullName" ); + TmpStatus = RtlAnsiStringToUnicodeString( + (PUNICODE_STRING)(&All->FullName), + &TmpAnsiString, + TRUE ); + ASSERT( NT_SUCCESS( TmpStatus ) ); + + RtlInitString( &TmpAnsiString, "HomeDirectory" ); + TmpStatus = RtlAnsiStringToUnicodeString( + (PUNICODE_STRING)(&All->HomeDirectory), + &TmpAnsiString, + TRUE ); + ASSERT( NT_SUCCESS( TmpStatus ) ); + + RtlInitString( &TmpAnsiString, "HomeDirectoryDrive" ); + TmpStatus = RtlAnsiStringToUnicodeString( + (PUNICODE_STRING)(&All->HomeDirectoryDrive), + &TmpAnsiString, + TRUE ); + ASSERT( NT_SUCCESS( TmpStatus ) ); + + RtlInitString( &TmpAnsiString, "ScriptPath" ); + TmpStatus = RtlAnsiStringToUnicodeString( + (PUNICODE_STRING)(&All->ScriptPath), + &TmpAnsiString, + TRUE ); + ASSERT( NT_SUCCESS( TmpStatus ) ); + + RtlInitString( &TmpAnsiString, "ProfilePath" ); + TmpStatus = RtlAnsiStringToUnicodeString( + (PUNICODE_STRING)(&All->ProfilePath), + &TmpAnsiString, + TRUE ); + ASSERT( NT_SUCCESS( TmpStatus ) ); + + RtlInitString( &TmpAnsiString, "AdminComment" ); + TmpStatus = RtlAnsiStringToUnicodeString( + (PUNICODE_STRING)(&All->AdminComment), + &TmpAnsiString, + TRUE ); + ASSERT( NT_SUCCESS( TmpStatus ) ); + + RtlInitString( &TmpAnsiString, "WorkStations" ); + TmpStatus = RtlAnsiStringToUnicodeString( + (PUNICODE_STRING)(&All->WorkStations), + &TmpAnsiString, + TRUE ); + ASSERT( NT_SUCCESS( TmpStatus ) ); + + RtlInitString( &TmpAnsiString, "UserComment" ); + TmpStatus = RtlAnsiStringToUnicodeString( + (PUNICODE_STRING)(&All->UserComment), + &TmpAnsiString, + TRUE ); + ASSERT( NT_SUCCESS( TmpStatus ) ); + + RtlInitString( &TmpAnsiString, "Parameters" ); + TmpStatus = RtlAnsiStringToUnicodeString( + (PUNICODE_STRING)(&All->Parameters), + &TmpAnsiString, + TRUE ); + ASSERT( NT_SUCCESS( TmpStatus ) ); + + All->CountryCode = 7; + All->CodePage = 8; + + All->PasswordExpired = TRUE; + All->NtPasswordPresent = TRUE; + All->LmPasswordPresent = FALSE; + + RtlInitString( &TmpAnsiString, "NtPassword" ); + TmpStatus = RtlAnsiStringToUnicodeString( + (PUNICODE_STRING)(&All->NtPassword), + &TmpAnsiString, + TRUE ); + ASSERT( NT_SUCCESS( TmpStatus ) ); + + All->LogonHours.UnitsPerWeek = 7; + + All->WhichFields = ( USER_ALL_FULLNAME | + USER_ALL_HOMEDIRECTORY | + USER_ALL_HOMEDIRECTORYDRIVE | + USER_ALL_SCRIPTPATH | + USER_ALL_PROFILEPATH | + USER_ALL_ADMINCOMMENT | + USER_ALL_WORKSTATIONS | + USER_ALL_USERCOMMENT | + USER_ALL_PARAMETERS | + USER_ALL_COUNTRYCODE | + USER_ALL_CODEPAGE | + USER_ALL_PASSWORDEXPIRED | + USER_ALL_NTPASSWORDPRESENT | + USER_ALL_LOGONHOURS ); + + NtStatus = SamSetInformationUser( + UserHandle1, + UserAllInformation, + All + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + NtStatus = SamQueryInformationUser( + UserHandle1, + UserAllInformation, + &All2 + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // Verify that queried info is as we set it + // + + if ( + + // + // Fields that we didn't touch. Note that + // PasswordMustChange changed anyway, since we + // changed from a null to a non-null password. + // + + ( All2->WhichFields != (USER_ALL_READ_GENERAL_MASK | + USER_ALL_READ_PREFERENCES_MASK | + USER_ALL_READ_ACCOUNT_MASK | + USER_ALL_READ_LOGON_MASK) ) || + ( All->LastLogon.QuadPart != All2->LastLogon.QuadPart) || + ( All->LastLogoff.QuadPart != All2->LastLogoff.QuadPart) || + ( All->PasswordLastSet.QuadPart != All2->PasswordLastSet.QuadPart) || + ( All->AccountExpires.QuadPart != All2->AccountExpires.QuadPart) || + ( All->PasswordCanChange.QuadPart != All2->PasswordCanChange.QuadPart) || + ( All->PasswordMustChange.QuadPart == All2->PasswordMustChange.QuadPart) || + (RtlCompareUnicodeString( + &(All->UserName), + &(All2->UserName), + FALSE) != 0) || + ( All->UserId != All2->UserId ) || + ( All->PrimaryGroupId != All2->PrimaryGroupId ) || + ( All->UserAccountControl != All2->UserAccountControl ) || + ( All->PrivateDataSensitive != + All2->PrivateDataSensitive ) || + + // Fields that we changed. Note that we set + // NtPasswordSet, but it shouldn't be set on return. + + (RtlCompareUnicodeString( + &(All->FullName), + &(All2->FullName), + FALSE) != 0) || + (RtlCompareUnicodeString( + &(All->HomeDirectory), + &(All2->HomeDirectory), + FALSE) != 0) || + (RtlCompareUnicodeString( + &(All->HomeDirectoryDrive), + &(All2->HomeDirectoryDrive), + FALSE) != 0) || + (RtlCompareUnicodeString( + &(All->ScriptPath), + &(All2->ScriptPath), + FALSE) != 0) || + (RtlCompareUnicodeString( + &(All->ProfilePath), + &(All2->ProfilePath), + FALSE) != 0) || + (RtlCompareUnicodeString( + &(All->AdminComment), + &(All2->AdminComment), + FALSE) != 0) || + (RtlCompareUnicodeString( + &(All->WorkStations), + &(All2->WorkStations), + FALSE) != 0) || + (RtlCompareUnicodeString( + &(All->UserComment), + &(All2->UserComment), + FALSE) != 0) || + (RtlCompareUnicodeString( + &(All->Parameters), + &(All2->Parameters), + FALSE) != 0) || + ( All->CountryCode != All2->CountryCode ) || + ( All->CodePage != All2->CodePage ) || + ( All->LmPasswordPresent != All2->LmPasswordPresent ) || + ( All->NtPasswordPresent == All2->NtPasswordPresent ) || + ( All->LogonHours.UnitsPerWeek != + All2->LogonHours.UnitsPerWeek ) + ) { + + NtStatus = STATUS_DATA_ERROR; + } + + SamFreeMemory( All2 ); + } + } + + SamFreeMemory( All ); + } + + if (NtStatus == STATUS_SUCCESS) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + // + // Now get rid of the user account if necessary + // + + NtStatus = SamDeleteUser( UserHandle1 ); + ASSERT(NT_SUCCESS(NtStatus)); +#endif //BROKEN + + + printf(" Set Primary Group (non member). . . . . . . . . . . . "); + // + // The following user might already exist (from earlier in the test) + // + + RtlInitString( &AccountNameAnsi, USER_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + //InitializeObjectAttributes( &ObjectAttributes, &AccountName, 0, 0, NULL ); + + + UserRid = 0; + UserHandle1 = NULL; + NtStatus = SamCreateUserInDomain( + DomainHandle, + &AccountName, + USER_ALL_ACCESS, + &UserHandle1, + &UserRid + ); + RtlFreeUnicodeString( &AccountName ); + DeleteUser = TRUE; + if (NtStatus == STATUS_USER_EXISTS) { + DeleteUser = FALSE; + RtlInitString( &AccountNameAnsi, USER_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountNames[0], &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + NtStatus = SamLookupNamesInDomain( + DomainHandle, + 1, + &AccountNames[0], + &LookedUpRids, + &LookedUpUses + ); + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(LookedUpUses[0] == SidTypeUser); + RtlFreeUnicodeString( &AccountNames[0] ); + NtStatus = SamOpenUser( + DomainHandle, + USER_ALL_ACCESS, + LookedUpRids[0], + &UserHandle1); + SamFreeMemory( LookedUpUses ); SamFreeMemory( LookedUpRids ); + } + + ASSERT(NT_SUCCESS(NtStatus)); + + + // + // The user is not a member of DOMAIN_GROUP_RID_ADMINS. + // See if we can make this group the user's primary group + // + + ASSERT(sizeof(GroupRid) == sizeof(USER_PRIMARY_GROUP_INFORMATION)); + GroupRid = DOMAIN_GROUP_RID_ADMINS; + NtStatus = SamSetInformationUser( + UserHandle1, + UserPrimaryGroupInformation, + &GroupRid + ); + + if (NtStatus == STATUS_MEMBER_NOT_IN_GROUP) { + + printf("Succeeded\n"); + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + // + // Now get rid of the user account if necessary + // + + if (DeleteUser == TRUE) { + NtStatus = SamDeleteUser( UserHandle1 ); + ASSERT(NT_SUCCESS(NtStatus)); + } else { + NtStatus = SamCloseHandle( UserHandle1 ); + ASSERT(NT_SUCCESS(NtStatus)); + } + + + + printf(" Set Primary Group (member). . . . . . . . . . . . . . "); + + // + // Make a user (might already exist) + // Make a group + // Make the group the user's primary group + // Change the user so the group isn't the primary group + // remove the group + // delete the group + // If we created the user, delete it. + + // + // The following user might already exist (from earlier in the test) + // + + RtlInitString( &AccountNameAnsi, USER_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + //InitializeObjectAttributes( &ObjectAttributes, &AccountName, 0, 0, NULL ); + + UserRid = 0; + UserHandle1 = NULL; + NtStatus = SamCreateUserInDomain( + DomainHandle, + &AccountName, + USER_ALL_ACCESS, + &UserHandle1, + &UserRid + ); + RtlFreeUnicodeString( &AccountName ); + DeleteUser = TRUE; + if (NtStatus == STATUS_USER_EXISTS) { + DeleteUser = FALSE; + RtlInitString( &AccountNameAnsi, USER_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountNames[0], &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + NtStatus = SamLookupNamesInDomain( + DomainHandle, + 1, + &AccountNames[0], + &LookedUpRids, + &LookedUpUses + ); + RtlFreeUnicodeString( &AccountNames[0] ); + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(LookedUpUses[0] == SidTypeUser); + UserRid = LookedUpRids[0]; + NtStatus = SamOpenUser( + DomainHandle, + USER_ALL_ACCESS, + UserRid, + &UserHandle1); + SamFreeMemory( LookedUpUses ); SamFreeMemory( LookedUpRids ); + } + + ASSERT(NT_SUCCESS(NtStatus)); + + + // + // create the group + // + + RtlInitString( &AccountNameAnsi, GROUP_NAME1 ); + NtStatus = RtlAnsiStringToUnicodeString( &AccountName, &AccountNameAnsi, TRUE ); + TST_SUCCESS_ASSERT(NtStatus); + + //InitializeObjectAttributes( &ObjectAttributes, &AccountName, 0, 0, NULL ); + + GroupRid = 0; + GroupHandle1 = NULL; + NtStatus = SamCreateGroupInDomain( + DomainHandle, + &AccountName, + GROUP_ALL_ACCESS, + &GroupHandle1, + &GroupRid + ); + RtlFreeUnicodeString( &AccountName ); + ASSERT(NT_SUCCESS(NtStatus)); + + // + // Make the user a member of this group + // + + NtStatus = SamAddMemberToGroup( + GroupHandle1, + UserRid, + SE_GROUP_MANDATORY | + SE_GROUP_ENABLED_BY_DEFAULT | + SE_GROUP_ENABLED + ); + ASSERT(NT_SUCCESS(NtStatus)); + + + // + // Set the user's primary group Id to be this group + // + + NtStatus = SamSetInformationUser( + UserHandle1, + UserPrimaryGroupInformation, + &GroupRid + ); + if (NT_SUCCESS(NtStatus)) { + + Buffer1 = NULL; + NtStatus = SamQueryInformationUser( + UserHandle1, + UserPrimaryGroupInformation, + &Buffer1 + ); + TST_SUCCESS_ASSERT(NtStatus); + ASSERT(Buffer1 != NULL); + + if ( ((USER_PRIMARY_GROUP_INFORMATION *)Buffer1)->PrimaryGroupId == + GroupRid ) { + + printf("Succeeded\n"); + + SamFreeMemory( Buffer1 ); + } else { + + printf("Failed\n"); + printf(" Returned Value Doesn't Match Set Value.\n"); + printf(" Value written is: 0x%lx\n", GroupRid); + printf(" Value retrieved is: 0x%lx\n", + ((USER_PRIMARY_GROUP_INFORMATION *)Buffer1)->PrimaryGroupId); + TestStatus = FALSE; + + } + + } else { + printf("Failed\n"); + printf(" Completion status is 0x%lx\n", NtStatus); + TestStatus = FALSE; + } + + + // + // Set the user's primary group Id back and remove the user + // from the group + // + + GroupRid = DOMAIN_GROUP_RID_USERS; + NtStatus = SamSetInformationUser( + UserHandle1, + UserPrimaryGroupInformation, + &GroupRid + ); + ASSERT(NT_SUCCESS(NtStatus)); + NtStatus = SamRemoveMemberFromGroup(GroupHandle1, UserRid); + ASSERT(NT_SUCCESS(NtStatus)); + + + + // + // Now get rid of the group and possibly the user account + // + + + NtStatus = SamDeleteGroup( GroupHandle1 ); + ASSERT(NT_SUCCESS(NtStatus)); + + if (DeleteUser == TRUE) { + NtStatus = SamDeleteUser( UserHandle1 ); + ASSERT(NT_SUCCESS(NtStatus)); + } else { + NtStatus = SamCloseHandle( UserHandle1 ); + ASSERT(NT_SUCCESS(NtStatus)); + } + + + + + + + + + + printf(" Set Name Information . . . . . . . . . . . . . . . . "); + printf("(Untested)\n"); + + + } + + return(TestStatus); + + + +} diff --git a/private/newsam2/client/tshut.c b/private/newsam2/client/tshut.c new file mode 100644 index 000000000..2def0d2d1 --- /dev/null +++ b/private/newsam2/client/tshut.c @@ -0,0 +1,119 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + tshut.c + +Abstract: + + This test is used to shut-down a SAM server. This might be useful + for killing SAM without rebooting during development. + +Author: + + Jim Kelly (JimK) 12-July-1991 + +Environment: + + User Mode - Win32 + +Revision History: + + +--*/ + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include <nt.h> +#include <ntsam.h> +#include <ntrtl.h> // DbgPrint() + + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// private service prototypes // +// // +/////////////////////////////////////////////////////////////////////////////// + + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Routines // +// // +/////////////////////////////////////////////////////////////////////////////// + +VOID +main ( + VOID + ) + +/*++ + +Routine Description: + + This is the main entry routine for this test. + +Arguments: + + None. + +Return Value: + + + Note: + + +--*/ +{ + NTSTATUS NtStatus; + SAM_HANDLE ServerHandle; + OBJECT_ATTRIBUTES ObjectAttributes; + + + InitializeObjectAttributes( &ObjectAttributes, NULL, 0, 0, NULL ); + + + NtStatus = SamConnect( + NULL, // ServerName (Local machine) + &ServerHandle, + SAM_SERVER_ALL_ACCESS, + &ObjectAttributes + ); + + DbgPrint("SAM TEST (Tshut): Status of SamConnect() is: 0x%lx\n", NtStatus); + if (!NT_SUCCESS(NtStatus)) { return; } + + + NtStatus = SamShutdownSamServer( ServerHandle ); + DbgPrint("SAM TEST (Tshut): Status of SamShutdownSamServer() is: 0x%lx\n", NtStatus); + if (!NT_SUCCESS(NtStatus)) { return; } + + + // + // I'm not sure why, but it seems to take another awakening of the + // server to make it die. + // + + NtStatus = SamConnect( + NULL, // ServerName (Local machine) + &ServerHandle, + SAM_SERVER_ALL_ACCESS, + &ObjectAttributes + ); + + + + return; +} diff --git a/private/newsam2/client/um/makefile b/private/newsam2/client/um/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/newsam2/client/um/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/newsam2/client/um/makefile.inc b/private/newsam2/client/um/makefile.inc new file mode 100644 index 000000000..0286a5543 --- /dev/null +++ b/private/newsam2/client/um/makefile.inc @@ -0,0 +1,6 @@ +# +# MIDL Compile +# +# +samrpc_c.c : ..\..\samrpc.idl + midl -oldnames -error allocation -error ref -ms_ext -c_ext $(CLIENT_CPP) $(CLIENT_FLAGS) $(INCS) /client stub /server none ..\..\samrpc.idl diff --git a/private/newsam2/client/um/samlib.def b/private/newsam2/client/um/samlib.def new file mode 100644 index 000000000..572b141b4 --- /dev/null +++ b/private/newsam2/client/um/samlib.def @@ -0,0 +1,70 @@ +LIBRARY SAMLIB + +DESCRIPTION 'Security Accounts Manager User-Mode Client Library' + +EXPORTS + +; +; Dave Hart (DaveHart) 28-Mar-92 +; +; Exported Public Sam APIs +; + SamFreeMemory + SamSetSecurityObject + SamQuerySecurityObject + SamCloseHandle + SamConnect + SamShutdownSamServer + SamLookupDomainInSamServer + SamEnumerateDomainsInSamServer + SamOpenDomain + SamQueryInformationDomain + SamSetInformationDomain + SamCreateGroupInDomain + SamEnumerateGroupsInDomain + SamCreateUserInDomain + SamCreateUser2InDomain + SamEnumerateUsersInDomain + SamCreateAliasInDomain + SamEnumerateAliasesInDomain + SamRemoveMemberFromForeignDomain + SamGetAliasMembership + SamLookupNamesInDomain + SamLookupIdsInDomain + SamOpenGroup + SamQueryInformationGroup + SamSetInformationGroup + SamAddMemberToGroup + SamDeleteGroup + SamRemoveMemberFromGroup + SamGetMembersInGroup + SamSetMemberAttributesOfGroup + SamOpenAlias + SamQueryInformationAlias + SamSetInformationAlias + SamDeleteAlias + SamAddMemberToAlias + SamAddMultipleMembersToAlias + SamRemoveMemberFromAlias + SamRemoveMultipleMembersFromAlias + SamGetMembersInAlias + SamOpenUser + SamDeleteUser + SamQueryInformationUser + SamSetInformationUser + SamChangePasswordUser + SamChangePasswordUser2 + SamGetGroupsForUser + SamQueryDisplayInformation + SamGetDisplayEnumerationIndex + + SamTestPrivateFunctionsDomain + SamTestPrivateFunctionsUser + + SamiChangePasswordUser + SamiLmChangePasswordUser + SamiChangePasswordUser2 + SamiOemChangePasswordUser2 + SamiEncryptPasswords + + diff --git a/private/newsam2/client/um/sources b/private/newsam2/client/um/sources new file mode 100644 index 000000000..9e74403ca --- /dev/null +++ b/private/newsam2/client/um/sources @@ -0,0 +1,94 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Jim Kelly (JimK) 4-July-1991 + + +Revision History: + +!ENDIF + + +MAJORCOMP = SAM +MINORCOMP = client + +TARGETNAME= samlib +TARGETPATH= obj +TARGETTYPE=DYNLINK + +TARGETLIBS= \ + $(BASEDIR)\public\sdk\lib\*\advapi32.lib \ + $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \ + $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \ + $(BASEDIR)\public\sdk\lib\*\rpcndr.lib \ + $(BASEDIR)\Public\Sdk\Lib\*\kernel32.lib \ + ..\..\..\lsa\crypt\engine\obj\*\rc4c.obj + + +INCLUDES=.;..;..\..\inc;..\..\..\inc;..\..\..\lsa\crypt\engine + + + +SOURCES=\ + bind.c \ + sam_rev.rc \ + wrappers.c \ + samrpc_c.c + + +UMTYPE=console +UMTEST=tmp*tconnect*tshut*tsamobj*tchgpwd*tdisplay +C_DEFINES=-DRPC_NO_WINDOWS_H +UMLIBS= \ + $(BASEDIR)\public\sdk\lib\*\samlib.lib \ + $(BASEDIR)\public\sdk\lib\*\advapi32.lib \ + $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \ + $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \ + $(BASEDIR)\public\sdk\lib\*\rpcndr.lib \ + $(BASEDIR)\public\sdk\lib\*\advapi32.lib \ + $(BASEDIR)\public\sdk\lib\*\ntdll.lib \ + $(BASEDIR)\Public\Sdk\Lib\*\kernel32.lib + +# +# FOR MIDL compile +# +IDL_NAME = samrpc +CLIENT_ACF = ..\..\samsrv.acf +CLIENT_INC_FILE = samrpc.h +SDKINC = $(BASEDIR)\public\sdk\inc +SDKCRTINC = $(BASEDIR)\public\sdk\inc\crt +PRIVATEINC = ..\..\..\inc +CLIENT_FLAGS = -acf $(CLIENT_ACF) -header $(CLIENT_INC_FILE) -DUSER_MODE_SAM +INCS = -I$(SDKINC) -I$(SDKCRTINC) -I..\.. -I$(PRIVATEINC) +CLIENT_CPP = -cpp_cmd "$(MIDL_CPP)" $(MIDL_FLAGS) + + + + +# +# Defining the NTTARGETFILES variable causes MAKEFILE.DEF to attempt to +# include .\makefile.inc immediately after it specifies the top +# level targets (all, clean and loc) and their dependencies. MAKEFILE.DEF +# also expands the value of the NTTARGETFILES variable at the end of the +# list of dependencies for the all target. Useful for specifying additional +# targets and dependencies that don't fit the general case covered by +# MAKEFILE.DEF +# + +UMRES=obj\*\sam_rev.res +NTTARGETFILE0=samrpc_c.c diff --git a/private/newsam2/client/wrappers.c b/private/newsam2/client/wrappers.c new file mode 100644 index 000000000..d0b0a6085 --- /dev/null +++ b/private/newsam2/client/wrappers.c @@ -0,0 +1,8953 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + wrappers.c + +Abstract: + + This file contains all SAM rpc wrapper routines. + +Author: + + Jim Kelly (JimK) 4-July-1991 + +Environment: + + User Mode - Win32 + +Revision History: + + +--*/ + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include "samclip.h" + +/////////////////////////////////////////////////////////////////////////////// +// // +// Private defines // +// // +/////////////////////////////////////////////////////////////////////////////// + +#define SAMP_MAXIMUM_SUB_LOOKUP_COUNT ((ULONG) 0x00000200) + +// Simple tracing routine for client-side API -- this should be removed once +// we understand how the DS-based SAM works. This macro is only called from +// within wrappers.c. + +#define SAMP_TRACE_CLIENT_API 0 + +#if SAMP_TRACE_CLIENT_API == 1 + +#define SampOutputDebugString(Message) \ + OutputDebugStringA("SAM API = "); \ + OutputDebugStringA(Message); \ + OutputDebugStringA("\n"); \ + +#else + +#define SampOutputDebugString(Message) + +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Local data types // +// // +/////////////////////////////////////////////////////////////////////////////// + +// +// This structure is used to pass a (potentially) very large +// user requested name lookup into (possibly many) smaller +// remote lookup requests. This is necessary to prevent the +// server from being asked to allocate huge chunks of memory, +// potentially running out of memory. +// +// There will be one of these structures for each server call +// that is necessary. +// + +typedef struct _SAMP_NAME_LOOKUP_CALL { + + // + // Each call is represented by one of these structures. + // The structures are chained together to show the order + // the calls were made (allowing an easy way to build the + // buffer that is to be returned to the user). + // + + LIST_ENTRY Link; + + // + // These fields define the beginning and ending indexes into + // the user passed Names buffer that are being represented by + // this call. + // + + ULONG StartIndex; + ULONG Count; + + + // + // These fields will receive the looked up RIDs and USE buffers. + // Notice that the .Element fields of these fields will receive + // pointers to the bulk of the returned information. + // + + SAMPR_ULONG_ARRAY RidBuffer; + SAMPR_ULONG_ARRAY UseBuffer; + +} SAMP_NAME_LOOKUP_CALL, *PSAMP_NAME_LOOKUP_CALL; + + +/////////////////////////////////////////////////////////////////////////////// +// // +// private service prototypes // +// // +/////////////////////////////////////////////////////////////////////////////// + +NTSTATUS +SampLookupIdsInDomain( + IN SAM_HANDLE DomainHandle, + IN ULONG Count, + IN PULONG RelativeIds, + OUT PUNICODE_STRING *Names, + OUT PSID_NAME_USE *Use OPTIONAL + ); + +NTSTATUS +SampMapCompletionStatus( + IN NTSTATUS Status + ); + +NTSTATUS +SampCalculateLmPassword( + IN PUNICODE_STRING NtPassword, + OUT PCHAR *LmPasswordBuffer + ); + +NTSTATUS +SampCheckPasswordRestrictions( + IN SAMPR_HANDLE UserHandle, + IN PUNICODE_STRING NewNtPassword, + OUT PBOOLEAN UseOwfPasswords + ); + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Routines // +// // +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// // +// General services // +// // +/////////////////////////////////////////////////////////////////////////////// + + + +NTSTATUS +SamFreeMemory( + IN PVOID Buffer + ) + +/*++ +Routine Description: + + + Some SAM services that return a potentially large amount of memory, + such as an enumeration might, allocate the buffer in which the data + is returned. This function is used to free those buffers when they + are no longer needed. + +Parameters: + + Buffer - Pointer to the buffer to be freed. This buffer must + have been allocated by a previous SAM service call. + +Return Values: + + STATUS_SUCCESS - normal, successful completion. + + +--*/ +{ + MIDL_user_free( Buffer ); + return(STATUS_SUCCESS); +} + + + +NTSTATUS +SamSetSecurityObject( + IN SAM_HANDLE ObjectHandle, + IN SECURITY_INFORMATION SecurityInformation, + IN PSECURITY_DESCRIPTOR SecurityDescriptor + ) + +/*++ +Routine Description: + + + This function (SamSetSecurityObject) takes a well formed Security + Descriptor provided by the caller and assigns specified portions of + it to an object. Based on the flags set in the SecurityInformation + parameter and the caller's access rights, this procedure will + replace any or all of the security information associated with an + object. + + This is the only function available to users and applications for + changing security information, including the owner ID, group ID, and + the discretionary and system ACLs of an object. The caller must + have WRITE_OWNER access to the object to change the owner or primary + group of the object. The caller must have WRITE_DAC access to the + object to change the discretionary ACL. The caller must have + ACCESS_SYSTEM_SECURITY access to an object to assign a system ACL + to the object. + + This API is modelled after the NtSetSecurityObject() system service. + + +Parameters: + + ObjectHandle - A handle to an existing object. + + SecurityInformation - Indicates which security information is to + be applied to the object. The value(s) to be assigned are + passed in the SecurityDescriptor parameter. + + SecurityDescriptor - A pointer to a well formed Security + Descriptor. + + +Return Values: + + STATUS_SUCCESS - normal, successful completion. + + STATUS_ACCESS_DENIED - The specified handle was not opened for + either WRITE_OWNER, WRITE_DAC, or ACCESS_SYSTEM_SECURITY + access. + + STATUS_INVALID_HANDLE - The specified handle is not that of an + opened SAM object. + + STATUS_BAD_DESCRIPTOR_FORMAT - Indicates something about security descriptor + is not valid. This may indicate that the structure of the descriptor is + not valid or that a component of the descriptor specified via the + SecurityInformation parameter is not present in the security descriptor. + + STATUS_INVALID_PARAMETER - Indicates no security information was specified. + + + +--*/ +{ + NTSTATUS NtStatus; + + ULONG SDLength; + SAMPR_SR_SECURITY_DESCRIPTOR DescriptorToPass; + + SampOutputDebugString("SamSetSecurityObject"); + + + + // + // Make a self relative security descriptor for use in the RPC call.. + // + + + SDLength = 0; + NtStatus = RtlMakeSelfRelativeSD( + SecurityDescriptor, + NULL, + &SDLength + ); + + if (NtStatus != STATUS_BUFFER_TOO_SMALL) { + + return(STATUS_INVALID_PARAMETER); + + } else { + + + DescriptorToPass.SecurityDescriptor = MIDL_user_allocate( SDLength ); + DescriptorToPass.Length = SDLength; + if (DescriptorToPass.SecurityDescriptor == NULL) { + + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + + } else { + + + // + // make an appropriate self-relative security descriptor + // + + NtStatus = RtlMakeSelfRelativeSD( + SecurityDescriptor, + (PSECURITY_DESCRIPTOR)DescriptorToPass.SecurityDescriptor, + &SDLength + ); + } + + } + + + + + + + + // + // Call the server ... + // + + if (NT_SUCCESS(NtStatus)) { + RpcTryExcept{ + + NtStatus = + SamrSetSecurityObject( + (SAMPR_HANDLE)ObjectHandle, + SecurityInformation, + &DescriptorToPass + ); + + + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + } + + MIDL_user_free( DescriptorToPass.SecurityDescriptor ); + + return(SampMapCompletionStatus(NtStatus)); +} + + + +NTSTATUS +SamQuerySecurityObject( + IN SAM_HANDLE ObjectHandle, + IN SECURITY_INFORMATION SecurityInformation, + OUT PSECURITY_DESCRIPTOR * SecurityDescriptor + ) +/*++ + +Routine Description: + + + This function (SamQuerySecurityObject) returns to the caller requested + security information currently assigned to an object. + + Based on the caller's access rights this procedure + will return a security descriptor containing any or all of the + object's owner ID, group ID, discretionary ACL or system ACL. To + read the owner ID, group ID, or the discretionary ACL the caller + must be granted READ_CONTROL access to the object. To read the + system ACL the caller must be granted ACCESS_SYSTEM_SECURITY + access. + + This API is modelled after the NtQuerySecurityObject() system + service. + + +Parameters: + + ObjectHandle - A handle to an existing object. + + SecurityInformation - Supplies a value describing which pieces of + security information are being queried. + + SecurityDescriptor - Receives a pointer to the buffer containing + the requested security information. This information is + returned in the form of a self-relative security descriptor. + The caller is responsible for freeing the returned buffer + (using SamFreeMemory()) when the security descriptor + is no longer needed. + +Return Values: + + STATUS_SUCCESS - normal, successful completion. + + STATUS_ACCESS_DENIED - The specified handle was not opened for + either READ_CONTROL or ACCESS_SYSTEM_SECURITY + access. + + STATUS_INVALID_HANDLE - The specified handle is not that of an + opened SAM object. + + + +--*/ +{ + NTSTATUS NtStatus; + SAMPR_SR_SECURITY_DESCRIPTOR ReturnedSD; + PSAMPR_SR_SECURITY_DESCRIPTOR PReturnedSD; + + SampOutputDebugString("SamQuerySecurityObject"); + + // + // The retrieved security descriptor is returned via a data structure that + // looks like: + // + // +-----------------------+ + // | Length (bytes) | + // |-----------------------| +--------------+ + // | SecurityDescriptor ---|--------->| Self-Relative| + // +-----------------------+ | Security | + // | Descriptor | + // +--------------+ + // + // The first of these buffers is a local stack variable. The buffer containing + // the self-relative security descriptor is allocated by the RPC runtime. The + // pointer to the self-relative security descriptor is what is passed back to our + // caller. + // + // + + // + // To prevent RPC from trying to marshal a self-relative security descriptor, + // make sure its field values are appropriately initialized to zero and null. + // + + ReturnedSD.Length = 0; + ReturnedSD.SecurityDescriptor = NULL; + + + + // + // Call the server ... + // + + + RpcTryExcept{ + + PReturnedSD = &ReturnedSD; + NtStatus = + SamrQuerySecurityObject( + (SAMPR_HANDLE)ObjectHandle, + SecurityInformation, + &PReturnedSD + ); + + + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + (*SecurityDescriptor) = ReturnedSD.SecurityDescriptor; + return(SampMapCompletionStatus(NtStatus)); +} + + + +NTSTATUS +SamCloseHandle( + OUT SAM_HANDLE SamHandle + ) + +/*++ +Routine Description: + + This API closes a currently opened SAM object. + +Arguments: + + SamHandle - Specifies the handle of a previously opened SAM object to + close. + + +Return Value: + + + STATUS_SUCCESS - The object was successfully closed. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + +--*/ +{ + NTSTATUS NtStatus; + SAMPR_HANDLE TmpHandle; + + SampOutputDebugString("SamCloseHandle"); + + + if (SamHandle == NULL) { + return(STATUS_INVALID_HANDLE); + } + + // + // Call the server ... + // + + TmpHandle = (SAMPR_HANDLE)SamHandle; + + RpcTryExcept{ + + NtStatus = SamrCloseHandle( + (SAMPR_HANDLE *)(&TmpHandle) + ); + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + if ((NtStatus != RPC_NT_SS_CONTEXT_MISMATCH) && + (NtStatus != RPC_NT_INVALID_BINDING)) { + (void) RpcSsDestroyClientContext( &TmpHandle); + } + + } RpcEndExcept; + + + return(SampMapCompletionStatus(NtStatus)); +} + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Server object related services // +// // +/////////////////////////////////////////////////////////////////////////////// + + +NTSTATUS +SamConnect( + IN PUNICODE_STRING ServerName, + OUT PSAM_HANDLE ServerHandle, + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes + ) + +/*++ +Routine Description: + + Establish a session with a SAM subsystem and subsequently open the + SamServer object of that subsystem. The caller must have + SAM_SERVER_CONNECT access to the SamServer object of the subsystem + being connected to. + + The handle returned is for use in future calls. + + +Arguments: + + ServerName - Name of the server to use, or NULL if local. + + ServerHandle - A handle to be used in future requests. This handle + represents both the handle to the SamServer object and the RPC + context handle for the connection to the SAM subsystem. + + DesiredAccess - Is an access mask indicating which access types are + desired to the SamServer. These access types are reconciled + with the Discretionary Access Control list of the SamServer to + determine whether the accesses will be granted or denied. The + access type of SAM_SERVER_CONNECT is always implicitly included + in this access mask. + + ObjectAttributes - Pointer to the set of object attributes to use for + this connection. Only the security Quality Of Service + information is used and should provide SecurityIdentification + level of impersonation. + +Return Value: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Access was denied. + + +--*/ +{ + NTSTATUS NtStatus; + + PSAMPR_SERVER_NAME RServerName; + PSAMPR_SERVER_NAME RServerNameWithNull; + USHORT RServerNameWithNullLength; + + + SampOutputDebugString("SamConnect"); + + // + // Hmmm - what to do with security QOS??? + // + + + // + // Call the server, passing either a NULL Server Name pointer, or + // a pointer to a Unicode Buffer with a Wide Character NULL terminator. + // Since the input name is contained in a counted Unicode String, there + // is no NULL terminator necessarily provided, so we must append one. + // + + RServerNameWithNull = NULL; + + if (ARGUMENT_PRESENT(ServerName)) { + + RServerName = (PSAMPR_SERVER_NAME)(ServerName->Buffer); + RServerNameWithNullLength = ServerName->Length + (USHORT) sizeof(WCHAR); + RServerNameWithNull = MIDL_user_allocate( RServerNameWithNullLength ); + + if (RServerNameWithNull == NULL) { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + RtlCopyMemory( RServerNameWithNull, RServerName, ServerName->Length); + RServerNameWithNull[ServerName->Length/sizeof(WCHAR)] = L'\0'; + } + + RpcTryExcept { + + NtStatus = SamrConnect2( + RServerNameWithNull, + (SAMPR_HANDLE *)ServerHandle, + DesiredAccess + ); + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + // + // If the new connect call failed because it didn't exist, + // try the old one. + // + + if ((NtStatus == RPC_NT_UNKNOWN_IF) || + (NtStatus == RPC_NT_PROCNUM_OUT_OF_RANGE)) { + + RpcTryExcept { + + NtStatus = SamrConnect( + RServerNameWithNull, + (SAMPR_HANDLE *)ServerHandle, + DesiredAccess + ); + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + } + + if (RServerNameWithNull != NULL) { + MIDL_user_free( RServerNameWithNull ); + } + + return(SampMapCompletionStatus(NtStatus)); + + DBG_UNREFERENCED_PARAMETER(ObjectAttributes); + +} + + +NTSTATUS +SamShutdownSamServer( + IN SAM_HANDLE ServerHandle + ) + +/*++ +Routine Description: + + This is the wrapper routine for SamShutdownSamServer(). + +Arguments: + + ServerHandle - Handle from a previous SamConnect() call. + +Return Value: + + + STATUS_SUCCESS The service completed successfully or the server + has already shutdown. + + + STATUS_ACCESS_DENIED - Caller does not have the appropriate access + to complete the operation. + +--*/ +{ + NTSTATUS NtStatus; + + + // + // Call the server ... + // + + + RpcTryExcept{ + + NtStatus = SamrShutdownSamServer(ServerHandle); + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + + // + // If the error status is one that would result from a server + // not being there, then replace it with success. + // + + if (NtStatus == RPC_NT_CALL_FAILED) { + NtStatus = STATUS_SUCCESS; + } + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); +} + + +NTSTATUS +SamLookupDomainInSamServer( + IN SAM_HANDLE ServerHandle, + IN PUNICODE_STRING Name, + OUT PSID *DomainId + ) + +/*++ + +Routine Description: + + This service returns the SID corresponding to the specified domain. + The domain is specified by name. + + +Arguments: + + ServerHandle - Handle from a previous SamConnect() call. + + Name - The name of the domain whose ID is to be looked up. A + case-insensitive comparison of this name will be performed for + the lookup operation. + + DomainId - Receives a pointer to a buffer containing the SID of the + looked up domain. This buffer must be freed using + SamFreeMemory() when no longer needed. + + +Return Value: + + + STATUS_SUCCESS - The service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate access + to complete the operation. SAM_SERVER_LOOKUP_DOMAIN access is + needed. + + STATUS_NO_SUCH_DOMAIN - The specified domain does not exist at this + server. + + STATUS_INVALID_SERVER_STATE - Indicates the SAM server is currently + disabled. + +--*/ +{ + NTSTATUS NtStatus; + + SampOutputDebugString("SamLookupDomainInSamServer"); + + // + // Call the server ... + // + + + RpcTryExcept{ + + (*DomainId) = 0; + + NtStatus = + SamrLookupDomainInSamServer( + (SAMPR_HANDLE)ServerHandle, + (PRPC_UNICODE_STRING)Name, + (PRPC_SID *)DomainId + ); + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); +} + + +NTSTATUS +SamEnumerateDomainsInSamServer( + IN SAM_HANDLE ServerHandle, + IN OUT PSAM_ENUMERATE_HANDLE EnumerationContext, + OUT PVOID * Buffer, + IN ULONG PreferedMaximumLength, + OUT PULONG CountReturned +) + +/*++ + +Routine Description: + + This API lists all the domains defined in the account database. + Since there may be more domains than can fit into a buffer, the + caller is provided with a handle that can be used across calls to + the API. On the initial call, EnumerationContext should point to a + SAM_ENUMERATE_HANDLE variable that is set to 0. + + If the API returns STATUS_MORE_ENTRIES, then the API should be + called again with EnumerationContext. When the API returns + STATUS_SUCCESS or any error return, the handle becomes invalid for + future use. + + This API requires SAM_SERVER_ENUMERATE_DOMAINS access to the + SamServer object. + + +Parameters: + + ServerHandle - Handle obtained from a previous SamConnect call. + + EnumerationContext - API specific handle to allow multiple calls + (see routine description). This is a zero based index. + + Buffer - Receives a pointer to the buffer where the information + is placed. The information returned is contiguous + SAM_RID_ENUMERATION data structures. However, the + RelativeId field of each of these structures is not valid. + This buffer must be freed when no longer needed using + SamFreeMemory(). + + PreferedMaximumLength - Prefered maximum length of returned data + (in 8-bit bytes). This is not a hard upper limit, but serves + as a guide to the server. Due to data conversion between + systems with different natural data sizes, the actual amount + of data returned may be greater than this value. + + CountReturned - Number of entries returned. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully, and there + are no additional entries. + + STATUS_MORE_ENTRIES - There are more entries, so call again. + This is a successful return. + + STATUS_ACCESS_DENIED - Caller does not have the access required + to enumerate the domains. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_INVALID_SERVER_STATE - Indicates the SAM server is + currently disabled. + +--*/ +{ + + NTSTATUS NtStatus; + PSAMPR_ENUMERATION_BUFFER LocalBuffer; + + SampOutputDebugString("SamEnumerateDomainsInSamServer"); + + // + // Make sure we aren't trying to have RPC allocate the EnumerationContext. + // + + if ( !ARGUMENT_PRESENT(EnumerationContext) ) { + return(STATUS_INVALID_PARAMETER); + } + if ( !ARGUMENT_PRESENT(Buffer) ) { + return(STATUS_INVALID_PARAMETER); + } + if ( !ARGUMENT_PRESENT(CountReturned) ) { + return(STATUS_INVALID_PARAMETER); + } + + + // + // Call the server ... + // + + (*Buffer) = NULL; + LocalBuffer = NULL; + + RpcTryExcept{ + + NtStatus = SamrEnumerateDomainsInSamServer( + (SAMPR_HANDLE)ServerHandle, + EnumerationContext, + (PSAMPR_ENUMERATION_BUFFER *)&LocalBuffer, + PreferedMaximumLength, + CountReturned + ); + + if (LocalBuffer != NULL) { + + // + // What comes back is a three level structure: + // + // Local +-------------+ + // Buffer ---> | EntriesRead | + // |-------------| +-------+ + // | Enumeration |--->| Name0 | --- > (NameBuffer0) + // | Return | |-------| o + // | Buffer | | ... | o + // +-------------+ |-------| o + // | NameN | --- > (NameBufferN) + // +-------+ + // + // The buffer containing the EntriesRead field is not returned + // to our caller. Only the buffers containing name information + // are returned. + // + + if (LocalBuffer->Buffer != NULL) { + (*Buffer) = LocalBuffer->Buffer; + } + + MIDL_user_free( LocalBuffer); + + + } + + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); + +} + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Domain object related services // +// // +/////////////////////////////////////////////////////////////////////////////// + + + +NTSTATUS +SamOpenDomain( + IN SAM_HANDLE ServerHandle, + IN ACCESS_MASK DesiredAccess, + IN PSID DomainId, + OUT PSAM_HANDLE DomainHandle + ) + +/*++ +Routine Description: + + This API opens a domain object. It returns a handle to the newly + opened domain that must be used for successive operations on the + domain. This handle may be closed with the SamCloseHandle API. + + +Arguments: + + ServerHandle - Handle from a previous SamConnect() call. + + DesiredAccess - Is an access mask indicating which access types are + desired to the domain. These access types are reconciled with + the Discretionary Access Control list of the domain to determine + whether the accesses will be granted or denied. + + DomainId - The SID assigned to the domain to open. + + DomainHandle - Receives a handle referencing the newly opened domain. + This handle will be required in successive calls to operate on + the domain. + +Return Value: + + + STATUS_SUCCESS - The domain was successfully opened. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate access + to complete the operation. + + STATUS_INVALID_SERVER_STATE - Indicates the SAM server is currently + disabled. + +--*/ +{ + NTSTATUS NtStatus; + + SampOutputDebugString("SamOpenDomain"); + + + // + // Call the server ... + // + + + RpcTryExcept{ + + (*DomainHandle) = 0; + + NtStatus = + SamrOpenDomain( + (SAMPR_HANDLE)ServerHandle, + DesiredAccess, + (PRPC_SID)DomainId, + (SAMPR_HANDLE *)DomainHandle + ); + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); +} + + +NTSTATUS +SamQueryInformationDomain( + IN SAM_HANDLE DomainHandle, + IN DOMAIN_INFORMATION_CLASS DomainInformationClass, + OUT PVOID *Buffer + ) + +/*++ +Routine Description: + + This API retrieves the domain information. This API requires either + DOMAIN_READ_PASSWORD_PARAMETERS or DOMAIN_READ_OTHER_PARAMETERS. + + +Arguments: + + DomainHandle - Handle from a previous SamOpenDomain() call. + + DomainInformationClass - Class of information desired. The accesses + required for each class is shown below: + + Info Level Required Access Type + --------------------------- ------------------------------- + DomainGeneralInformation DOMAIN_READ_OTHER_PARAMETERS + DomainPasswordInformation DOMAIN_READ_PASSWORD_PARAMS + DomainLogoffInformation DOMAIN_READ_OTHER_PARAMETERS + DomainOemInformation DOMAIN_READ_OTHER_PARAMETERS + DomainNameInformation DOMAIN_READ_OTHER_PARAMETERS + DomainServerRoleInformation DOMAIN_READ_OTHER_PARAMETERS + DomainReplicationInformation DOMAIN_READ_OTHER_PARAMETERS + DomainModifiedInformation DOMAIN_READ_OTHER_PARAMETERS + DomainStateInformation DOMAIN_READ_OTHER_PARAMETERS + DomainUasInformation DOMAIN_READ_OTHER_PARAMETERS + Added for NT1.0A... + DomainGeneralInformation2 DOMAIN_READ_OTHER_PARAMETERS + DomainLockoutInformation DOMAIN_READ_OTHER_PARAMETERS + + + Buffer - Receives a pointer to a buffer containing the requested + information. When this information is no longer needed, this buffer + must be freed using SamFreeMemory(). + +Return Value: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate access + to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_INVALID_INFO_CLASS - The class provided was invalid. + +--*/ +{ + NTSTATUS NtStatus; + + SampOutputDebugString("SamQueryInformationDomain"); + + + // + // Call the server ... + // + + + (*Buffer) = NULL; + + RpcTryExcept{ + + if (DomainInformationClass <= DomainUasInformation) { + NtStatus = SamrQueryInformationDomain( + (SAMPR_HANDLE)DomainHandle, + DomainInformationClass, + (PSAMPR_DOMAIN_INFO_BUFFER *)Buffer + ); + } else { + NtStatus = SamrQueryInformationDomain2( + (SAMPR_HANDLE)DomainHandle, + DomainInformationClass, + (PSAMPR_DOMAIN_INFO_BUFFER *)Buffer + ); + + } + + + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + // + // If the exception indicates the server doesn't have + // the selected api, that means the server doesn't know + // about the info level we passed. Set our completion + // status appropriately. + // + + if (RpcExceptionCode() == RPC_S_INVALID_LEVEL || + RpcExceptionCode() == RPC_S_PROCNUM_OUT_OF_RANGE || + RpcExceptionCode() == RPC_NT_PROCNUM_OUT_OF_RANGE ) { + NtStatus = STATUS_INVALID_INFO_CLASS; + } else { + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + } + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); +} + + +NTSTATUS +SamSetInformationDomain( + IN SAM_HANDLE DomainHandle, + IN DOMAIN_INFORMATION_CLASS DomainInformationClass, + IN PVOID DomainInformation +) + +/*++ + +Routine Description: + + This API sets the domain information to the values passed in the + buffer. + + +Parameters: + + DomainHandle - A domain handle returned from a previous call to + SamOpenDomain. + + DomainInformationClass - Class of information desired. The + accesses required for each class is shown below: + + Info Level Required Access Type + ------------------------- ---------------------------- + + DomainPasswordInformation DOMAIN_WRITE_PASSWORD_PARAMS + + DomainLogoffInformation DOMAIN_WRITE_OTHER_PARAMETERS + + DomainOemInformation DOMAIN_WRITE_OTHER_PARAMETERS + + DomainNameInformation (not valid for set operations.) + + DomainServerRoleInformation DOMAIN_ADMINISTER_SERVER + + DomainReplicationInformation DOMAIN_ADMINISTER_SERVER + + DomainModifiedInformation (not valid for set operations.) + + DomainStateInformation DOMAIN_ADMINISTER_SERVER + + DomainUasInformation DOMAIN_WRITE_OTHER_PARAMETERS + + DomainInformation - Buffer where the domain information can be + found. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_INVALID_INFO_CLASS - The class provided was invalid. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be disabled before role + changes can be made. + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. + +--*/ +{ + + NTSTATUS NtStatus; + + SampOutputDebugString("SamSetInformationDomain"); + + + // + // Call the server ... + // + + + RpcTryExcept{ + + NtStatus = + SamrSetInformationDomain( + (SAMPR_HANDLE)DomainHandle, + DomainInformationClass, + (PSAMPR_DOMAIN_INFO_BUFFER)DomainInformation + ); + + + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); + +} + + + +NTSTATUS +SamCreateGroupInDomain( + IN SAM_HANDLE DomainHandle, + IN PUNICODE_STRING AccountName, + IN ACCESS_MASK DesiredAccess, + OUT PSAM_HANDLE GroupHandle, + OUT PULONG RelativeId + ) +/*++ + +Routine Description: + + This API creates a new group in the account database. Initially, + this group does not contain any users. Note that creating a group + is a protected operation, and requires the DOMAIN_CREATE_GROUP + access type. + + This call returns a handle to the newly created group that may be + used for successive operations on the group. This handle may be + closed with the SamCloseHandle API. + + A newly created group will have the following initial field value + settings. If another value is desired, it must be explicitly + changed using the group object manipulation services. + + Name - The name of the group will be as specified in the + creation API. + + Attributes - The following attributes will be set: + + Mandatory + EnabledByDefault + + MemberCount - Zero. Initially the group has no members. + + RelativeId - will be a uniquelly allocated ID. + + +Parameters: + + DomainHandle - A domain handle returned from a previous call to + SamOpenDomain. + + AccountName - Points to the name of the new account. A + case-insensitive comparison must not find a group, alias or user + with this name already defined. + + DesiredAccess - Is an access mask indicating which access types + are desired to the group. + + GroupHandle - Receives a handle referencing the newly created + group. This handle will be required in successive calls to + operate on the group. + + RelativeId - Receives the relative ID of the newly created group + account. The SID of the new group account is this relative + ID value prefixed with the domain's SID value. + +Return Values: + + STATUS_SUCCESS - The group was added successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_INVALID_ACCOUNT_NAME - The name was poorly formed, e.g. + contains non-printable characters. + + STATUS_GROUP_EXISTS - The name is already in use as a group. + + STATUS_USER_EXISTS - The name is already in use as a user. + + STATUS_ALIAS_EXISTS - The name is already in use as an alias. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled before groups + can be created in it. + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. The domain server must be a primary server to + create group accounts. + + +--*/ +{ + + NTSTATUS NtStatus; + + SampOutputDebugString("SamCreateGroupInDomain"); + + + // + // Call the server ... + // + + + (*GroupHandle) = NULL; + (*RelativeId) = 0; + + RpcTryExcept{ + + NtStatus = + SamrCreateGroupInDomain( + (SAMPR_HANDLE)DomainHandle, + (PRPC_UNICODE_STRING)AccountName, + DesiredAccess, + (SAMPR_HANDLE *)GroupHandle, + RelativeId + ); + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); + +} + + + +NTSTATUS +SamEnumerateGroupsInDomain( + IN SAM_HANDLE DomainHandle, + IN OUT PSAM_ENUMERATE_HANDLE EnumerationContext, + IN PVOID * Buffer, + IN ULONG PreferedMaximumLength, + OUT PULONG CountReturned +) + +/*++ + +Routine Description: + + This API lists all the groups defined in the account database. + Since there may be more groups than can fit into a buffer, the + caller is provided with a handle that can be used across calls to + the API. On the initial call, EnumerationContext should point to a + SAM_ENUMERATE_HANDLE variable that is set to 0. + + If the API returns STATUS_MORE_ENTRIES, then the API should be + called again with EnumerationContext. When the API returns + STATUS_SUCCESS or any error return, the handle becomes invalid for + future use. + + This API requires DOMAIN_LIST_ACCOUNTS access to the Domain object. + + +Parameters: + + DomainHandle - A domain handle returned from a previous call to + SamOpenDomain. + + EnumerationContext - API specific handle to allow multiple calls + (see routine description). This is a zero based index. + + Buffer - Receives a pointer to the buffer containing the + requested information. The information returned is + structured as an array of SAM_RID_ENUMERATION data + structures. When this information is no longer needed, the + buffer must be freed using SamFreeMemory(). + + PreferedMaximumLength - Prefered maximum length of returned data + (in 8-bit bytes). This is not a hard upper limit, but serves + as a guide to the server. Due to data conversion between + systems with different natural data sizes, the actual amount + of data returned may be greater than this value. + + CountReturned - Number of entries returned. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully, and there + are no additional entries. + + STATUS_MORE_ENTRIES - There are more entries, so call again. + This is a successful return. + + STATUS_ACCESS_DENIED - Caller does not have privilege required to + request that data. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + +--*/ +{ + + NTSTATUS NtStatus; + PSAMPR_ENUMERATION_BUFFER LocalBuffer; + + SampOutputDebugString("SamEnumerateGroupsInDomain"); + + // + // Make sure we aren't trying to have RPC allocate the EnumerationContext. + // + + if ( !ARGUMENT_PRESENT(EnumerationContext) ) { + return(STATUS_INVALID_PARAMETER); + } + if ( !ARGUMENT_PRESENT(Buffer) ) { + return(STATUS_INVALID_PARAMETER); + } + if ( !ARGUMENT_PRESENT(CountReturned) ) { + return(STATUS_INVALID_PARAMETER); + } + + + // + // Call the server ... + // + + (*Buffer) = NULL; + LocalBuffer = NULL; + + + RpcTryExcept{ + + NtStatus = SamrEnumerateGroupsInDomain( + (SAMPR_HANDLE)DomainHandle, + EnumerationContext, + (PSAMPR_ENUMERATION_BUFFER *)&LocalBuffer, + PreferedMaximumLength, + CountReturned + ); + + + if (LocalBuffer != NULL) { + + // + // What comes back is a three level structure: + // + // Local +-------------+ + // Buffer ---> | EntriesRead | + // |-------------| +-------+ + // | Enumeration |--->| Name0 | --- > (NameBuffer0) + // | Return | |-------| o + // | Buffer | | ... | o + // +-------------+ |-------| o + // | NameN | --- > (NameBufferN) + // +-------+ + // + // The buffer containing the EntriesRead field is not returned + // to our caller. Only the buffers containing name information + // are returned. + // + + if (LocalBuffer->Buffer != NULL) { + (*Buffer) = LocalBuffer->Buffer; + } + + MIDL_user_free( LocalBuffer); + + + } + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); + +} + + +NTSTATUS +SamCreateUser2InDomain( + IN SAM_HANDLE DomainHandle, + IN PUNICODE_STRING AccountName, + IN ULONG AccountType, + IN ACCESS_MASK DesiredAccess, + OUT PSAM_HANDLE UserHandle, + OUT PULONG GrantedAccess, + OUT PULONG RelativeId +) + +/*++ + +Routine Description: + + This API adds a new user to the account database. The account is + created in a disabled state. Default information is assigned to all + fields except the account name. A password must be provided before + the account may be enabled, unless the PasswordNotRequired control + field is set. + + This api may be used in either of two ways: + + 1) An administrative utility may use this api to create + any type of user account. In this case, the DomainHandle + is expected to be open for DOMAIN_CREATE_USER access. + + 2) A non-administrative user may use this api to create + a machine account. In this case, the caller is expected + to have the SE_CREATE_MACHINE_ACCOUNT_PRIV privilege + and the DomainHandle is expected to be open for DOMAIN_LOOKUP + access. + + + For the normal administrative model ( #1 above), the creator will + be assigned as the owner of the created user account. Furthermore, + the new account will be give USER_WRITE access to itself. + + For the special machine-account creation model (#2 above), the + "Administrators" will be assigned as the owner of the account. + Furthermore, the new account will be given NO access to itself. + Instead, the creator of the account will be give USER_WRITE and + DELETE access to the account. + + + This call returns a handle to the newly created user that may be + used for successive operations on the user. This handle may be + closed with the SamCloseHandle() API. If a machine account is + being created using model #2 above, then this handle will have + only USER_WRITE and DELETE access. Otherwise, it will be open + for USER_ALL_ACCESS. + + + A newly created user will automatically be made a member of the + DOMAIN_USERS group. + + A newly created user will have the following initial field value + settings. If another value is desired, it must be explicitly + changed using the user object manipulation services. + + UserName - the name of the account will be as specified in the + creation API. + + FullName - will be null. + + UserComment - will be null. + + Parameters - will be null. + + CountryCode - will be zero. + + UserId - will be a uniquelly allocated ID. + + PrimaryGroupId - Will be DOMAIN_USERS. + + PasswordLastSet - will be the time the account was created. + + HomeDirectory - will be null. + + HomeDirectoryDrive - will be null. + + UserAccountControl - will have the following flags set: + + UserAccountDisable, + UserPasswordNotRequired, + and the passed account type. + + + ScriptPath - will be null. + + WorkStations - will be null. + + CaseInsensitiveDbcs - will be null. + + CaseSensitiveUnicode - will be null. + + LastLogon - will be zero delta time. + + LastLogoff - will be zero delta time + + AccountExpires - will be very far into the future. + + BadPasswordCount - will be negative 1 (-1). + + LastBadPasswordTime - will be SampHasNeverTime ( [High,Low] = [0,0] ). + + LogonCount - will be negative 1 (-1). + + AdminCount - will be zero. + + AdminComment - will be null. + + Password - will be "". + + +Parameters: + + DomainHandle - A domain handle returned from a previous call to + SamOpenDomain. + + AccountName - Points to the name of the new account. A case-insensitive + comparison must not find a group or user with this name already defined. + + AccountType - Indicates what type of account is being created. + Exactly one account type must be provided: + + USER_INTERDOMAIN_TRUST_ACCOUNT + USER_WORKSTATION_TRUST_ACCOUNT + USER_SERVER_TRUST_ACCOUNT + USER_TEMP_DUPLICATE_ACCOUNT + USER_NORMAL_ACCOUNT + USER_MACHINE_ACCOUNT_MASK + + + DesiredAccess - Is an access mask indicating which access types + are desired to the user. + + UserHandle - Receives a handle referencing the newly created + user. This handle will be required in successive calls to + operate on the user. + + GrantedAccess - Receives the accesses actually granted to via + the UserHandle. When creating an account on a down-level + server, this value may be unattainable. In this case, it + will be returned as zero (0). + + RelativeId - Receives the relative ID of the newly created user + account. The SID of the new user account is this relative ID + value prefixed with the domain's SID value. + + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_GROUP_EXISTS - The name is already in use as a group. + + STATUS_USER_EXISTS - The name is already in use as a user. + + STATUS_ALIAS_EXISTS - The name is already in use as an alias. + + STATUS_INVALID_ACCOUNT_NAME - The name was poorly formed, e.g. + contains non-printable characters. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled before users + can be created in it. + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. The domain server must be a primary server to + create user accounts. + + +--*/ +{ + NTSTATUS + NtStatus, + IgnoreStatus; + + + USER_CONTROL_INFORMATION + UserControlInfoBuffer; + + SampOutputDebugString("SamCreateUser2InDomain"); + + + // + // Call the server ... + // + + + (*UserHandle) = NULL; + (*RelativeId) = 0; + + RpcTryExcept{ + + NtStatus = + SamrCreateUser2InDomain( + (SAMPR_HANDLE)DomainHandle, + (PRPC_UNICODE_STRING)AccountName, + AccountType, + DesiredAccess, + (SAMPR_HANDLE *)UserHandle, + GrantedAccess, + RelativeId + ); + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + if (RpcExceptionCode() == RPC_S_PROCNUM_OUT_OF_RANGE || + RpcExceptionCode() == RPC_NT_PROCNUM_OUT_OF_RANGE ) { + NtStatus = RPC_NT_PROCNUM_OUT_OF_RANGE; + } else { + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + } + + } RpcEndExcept; + + + + // + // If the server doesn't support the new api, then + // do the equivalent work with the old apis. + // + + if (NtStatus == RPC_NT_PROCNUM_OUT_OF_RANGE) { + + DesiredAccess = DesiredAccess | USER_WRITE_ACCOUNT; + NtStatus = + SamCreateUserInDomain( + DomainHandle, + AccountName, + DesiredAccess, + UserHandle, + RelativeId ); + + if (NT_SUCCESS(NtStatus)) { + + + + // + // Set the AccountType (unless it is normal) + // + + if (~(AccountType & USER_NORMAL_ACCOUNT)) { + + UserControlInfoBuffer.UserAccountControl = + AccountType | + USER_ACCOUNT_DISABLED | + USER_PASSWORD_NOT_REQUIRED; + + NtStatus = SamSetInformationUser( + (*UserHandle), + UserControlInformation, + &UserControlInfoBuffer + ); + if (!NT_SUCCESS(NtStatus)) { + IgnoreStatus = SamDeleteUser( UserHandle ); + } + + // + // We can't be positive what accesses have been + // granted, so don't try lying. + // + + (*GrantedAccess) = 0; + + } + } + } + + + return(SampMapCompletionStatus(NtStatus)); +} + + +NTSTATUS +SamCreateUserInDomain( + IN SAM_HANDLE DomainHandle, + IN PUNICODE_STRING AccountName, + IN ACCESS_MASK DesiredAccess, + OUT PSAM_HANDLE UserHandle, + OUT PULONG RelativeId +) + +/*++ + +Routine Description: + + This API adds a new user to the account database. The account is + created in a disabled state. Default information is assigned to all + fields except the account name. A password must be provided before + the account may be enabled, unless the PasswordNotRequired control + field is set. + + Note that DOMAIN_CREATE_USER access type is needed by this API. + Also, the caller of this API becomes the owner of the user object + upon creation. + + This call returns a handle to the newly created user that may be + used for successive operations on the user. This handle may be + closed with the SamCloseHandle() API. + + A newly created user will automatically be made a member of the + DOMAIN_USERS group. + + A newly created user will have the following initial field value + settings. If another value is desired, it must be explicitly + changed using the user object manipulation services. + + UserName - the name of the account will be as specified in the + creation API. + + FullName - will be null. + + UserComment - will be null. + + Parameters - will be null. + + CountryCode - will be zero. + + UserId - will be a uniquelly allocated ID. + + PrimaryGroupId - Will be DOMAIN_USERS. + + PasswordLastSet - will be the time the account was created. + + HomeDirectory - will be null. + + HomeDirectoryDrive - will be null. + + UserAccountControl - will have the following flags set: + + USER_ACCOUNT_DISABLED, + USER_NORMAL_ACCOUNT, + USER_PASSWORD_NOT_REQUIRED + + ScriptPath - will be null. + + WorkStations - will be null. + + CaseInsensitiveDbcs - will be null. + + CaseSensitiveUnicode - will be null. + + LastLogon - will be zero. + + LastLogoff - will be zero. + + AccountExpires - will be very far into the future. + + BadPasswordCount - will be negative 1 (-1). + + LogonCount - will be negative 1 (-1). + + AdminComment - will be null. + + Password - will contain any value, but is not used because the + USER_PASSWORD_NOT_REQUIRED control flag is set. If a password + is to be required, then this field must be set to a + specific value and the USER_PASSWORD_NOT_REQUIRED flag must be + cleared. + + +Parameters: + + DomainHandle - A domain handle returned from a previous call to + SamOpenDomain. + + AccountName - The name to be assigned to the new account. + + DesiredAccess - Is an access mask indicating which access types + are desired to the user. + + UserHandle - Receives a handle referencing the newly created + user. This handle will be required in successive calls to + operate on the user. + + RelativeId - Receives the relative ID of the newly created user + account. The SID of the new user account is this relative ID + value prefixed with the domain's SID value. + + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_GROUP_EXISTS - The name is already in use as a group. + + STATUS_USER_EXISTS - The name is already in use as a user. + + STATUS_ALIAS_EXISTS - The name is already in use as an alias. + + STATUS_INVALID_ACCOUNT_NAME - The name was poorly formed, e.g. + contains non-printable characters. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled before users + can be created in it. + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. The domain server must be a primary server to + create user accounts. + + +--*/ +{ + NTSTATUS NtStatus; + + SampOutputDebugString("SamCreateUserInDomain"); + + + // + // Call the server ... + // + + + (*UserHandle) = NULL; + (*RelativeId) = 0; + + RpcTryExcept{ + + NtStatus = + SamrCreateUserInDomain( + (SAMPR_HANDLE)DomainHandle, + (PRPC_UNICODE_STRING)AccountName, + DesiredAccess, + (SAMPR_HANDLE *)UserHandle, + RelativeId + ); + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); +} + + + + +NTSTATUS +SamEnumerateUsersInDomain( + IN SAM_HANDLE DomainHandle, + IN OUT PSAM_ENUMERATE_HANDLE EnumerationContext, + IN ULONG UserAccountControl, + OUT PVOID * Buffer, + IN ULONG PreferedMaximumLength, + OUT PULONG CountReturned +) + +/*++ + +Routine Description: + + This API lists all the users defined in the account database. Since + there may be more users than can fit into a buffer, the caller is + provided with a handle that can be used across calls to the API. On + the initial call, EnumerationContext should point to a + SAM_ENUMERATE_HANDLE variable that is set to 0. + + If the API returns STATUS_MORE_ENTRIES, then the API should be + called again with EnumerationContext. When the API returns + STATUS_SUCCESS or any error return, the handle becomes invalid for + future use. + + This API requires DOMAIN_LIST_ACCOUNTS access to the Domain object. + + +Parameters: + + DomainHandle - A domain handle returned from a previous call to + SamOpenDomain. + + EnumerationContext - API specific handle to allow multiple calls + (see routine description). This is a zero based index. + + UserAccountControl - Provides enumeration filtering information. Any + characteristics specified here will cause that type of User account + to be included in the enumeration process. + + Buffer - Receives a pointer to the buffer containing the + requested information. The information returned is + structured as an array of SAM_RID_ENUMERATION data + structures. When this information is no longer needed, the + buffer must be freed using SamFreeMemory(). + + PreferedMaximumLength - Prefered maximum length of returned data + (in 8-bit bytes). This is not a hard upper limit, but serves + as a guide to the server. Due to data conversion between + systems with different natural data sizes, the actual amount + of data returned may be greater than this value. + + CountReturned - Number of entries returned. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully, and there + are no additional entries. + + STATUS_MORE_ENTRIES - There are more entries, so call again. + This is a successful return. + + STATUS_ACCESS_DENIED - Caller does not have privilege required to + request that data. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + +--*/ +{ + + NTSTATUS NtStatus; + PSAMPR_ENUMERATION_BUFFER LocalBuffer; + + SampOutputDebugString("SamEnumerateUsersInDomain"); + + // + // Make sure we aren't trying to have RPC allocate the EnumerationContext. + // + + if ( !ARGUMENT_PRESENT(EnumerationContext) ) { + return(STATUS_INVALID_PARAMETER); + } + if ( !ARGUMENT_PRESENT(Buffer) ) { + return(STATUS_INVALID_PARAMETER); + } + if ( !ARGUMENT_PRESENT(CountReturned) ) { + return(STATUS_INVALID_PARAMETER); + } + + + // + // Call the server ... + // + + (*Buffer) = NULL; + LocalBuffer = NULL; + + + RpcTryExcept{ + + NtStatus = SamrEnumerateUsersInDomain( + (SAMPR_HANDLE)DomainHandle, + EnumerationContext, + UserAccountControl, + (PSAMPR_ENUMERATION_BUFFER *)&LocalBuffer, + PreferedMaximumLength, + CountReturned + ); + + + if (LocalBuffer != NULL) { + + // + // What comes back is a three level structure: + // + // Local +-------------+ + // Buffer ---> | EntriesRead | + // |-------------| +-------+ + // | Enumeration |--->| Name0 | --- > (NameBuffer0) + // | Return | |-------| o + // | Buffer | | ... | o + // +-------------+ |-------| o + // | NameN | --- > (NameBufferN) + // +-------+ + // + // The buffer containing the EntriesRead field is not returned + // to our caller. Only the buffers containing name information + // are returned. + // + + if (LocalBuffer->Buffer != NULL) { + (*Buffer) = LocalBuffer->Buffer; + } + + MIDL_user_free( LocalBuffer); + + + } + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); + +} + + + +NTSTATUS +SamCreateAliasInDomain( + IN SAM_HANDLE DomainHandle, + IN PUNICODE_STRING AccountName, + IN ACCESS_MASK DesiredAccess, + OUT PSAM_HANDLE AliasHandle, + OUT PULONG RelativeId +) + +/*++ + +Routine Description: + + This API adds a new alias to the account database. Initially, this + alias does not contain any members. + + This call returns a handle to the newly created account that may be + used for successive operations on the object. This handle may be + closed with the SamCloseHandle API. + + A newly created group will have the following initial field value + settings. If another value is desired, it must be explicitly changed + using the Alias object manipulation services. + + Name - the name of the account will be as specified in the creation + API. + + MemberCount - Zero. Initially the alias has no members. + + RelativeId - will be a uniquelly allocated ID. + + +Parameters: + + DomainHandle - A domain handle returned from a previous call to + SamOpenDomain. The handle must be open for DOMAIN_CREATE_ALIAS + access. + + AccountName - The name of the alias to be added. + + DesiredAccess - Is an access mask indicating which access types + are desired to the alias. + + AliasHandle - Receives a handle referencing the newly created + alias. This handle will be required in successive calls to + operate on the alias. + + RelativeId - Receives the relative ID of the newly created alias. + The SID of the new alias is this relative ID value prefixed with + the domain's SID value. + + +Return Values: + + STATUS_SUCCESS - The account was added successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_INVALID_ACCOUNT_NAME - The name was poorly formed, e.g. + contains non-printable characters. + + STATUS_GROUP_EXISTS - The name is already in use as a group. + + STATUS_USER_EXISTS - The name is already in use as a user. + + STATUS_ALIAS_EXISTS - The name is already in use as an alias. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled before aliases + can be created in it. + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. The domain server must be a primary server to + create aliases. + + +--*/ +{ + NTSTATUS NtStatus; + + SampOutputDebugString("SamCreateAliasInDomain"); + + + // + // Call the server ... + // + + + (*AliasHandle) = NULL; + (*RelativeId) = 0; + + RpcTryExcept{ + + NtStatus = + SamrCreateAliasInDomain( + (SAMPR_HANDLE)DomainHandle, + (PRPC_UNICODE_STRING)AccountName, + DesiredAccess, + (SAMPR_HANDLE *)AliasHandle, + RelativeId + ); + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); +} + + + +NTSTATUS +SamEnumerateAliasesInDomain( + IN SAM_HANDLE DomainHandle, + IN OUT PSAM_ENUMERATE_HANDLE EnumerationContext, + IN PVOID *Buffer, + IN ULONG PreferedMaximumLength, + OUT PULONG CountReturned +) + +/*++ + +Routine Description: + + This API lists all the aliases defined in the account database. Since + there may be more aliases than can fit into a buffer, the caller is + provided with a handle that can be used across calls to the API. On + the initial call, EnumerationContext should point to a + SAM_ENUMERATE_HANDLE variable that is set to 0. + + If the API returns STATUS_MORE_ENTRIES, then the API should be + called again with EnumerationContext. When the API returns + STATUS_SUCCESS or any error return, the handle becomes invalid for + future use. + + This API requires DOMAIN_LIST_ACCOUNTS access to the Domain object. + + +Parameters: + + DomainHandle - A domain handle returned from a previous call to + SamOpenDomain. + + EnumerationContext - API specific handle to allow multiple calls + (see routine description). This is a zero based index. + + Buffer - Receives a pointer to the buffer containing the + requested information. The information returned is + structured as an array of SAM_RID_ENUMERATION data + structures. When this information is no longer needed, the + buffer must be freed using SamFreeMemory(). + + PreferedMaximumLength - Prefered maximum length of returned data + (in 8-bit bytes). This is not a hard upper limit, but serves + as a guide to the server. Due to data conversion between + systems with different natural data sizes, the actual amount + of data returned may be greater than this value. + + CountReturned - Number of entries returned. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully, and there + are no additional entries. + + STATUS_MORE_ENTRIES - There are more entries, so call again. + This is a successful return. + + STATUS_ACCESS_DENIED - Caller does not have privilege required to + request that data. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + +--*/ +{ + + NTSTATUS NtStatus; + PSAMPR_ENUMERATION_BUFFER LocalBuffer; + + SampOutputDebugString("SamEnumerateAliasesInDomain"); + + // + // Make sure we aren't trying to have RPC allocate the EnumerationContext. + // + + if ( !ARGUMENT_PRESENT(EnumerationContext) ) { + return(STATUS_INVALID_PARAMETER); + } + if ( !ARGUMENT_PRESENT(Buffer) ) { + return(STATUS_INVALID_PARAMETER); + } + if ( !ARGUMENT_PRESENT(CountReturned) ) { + return(STATUS_INVALID_PARAMETER); + } + + + // + // Call the server ... + // + + (*Buffer) = NULL; + LocalBuffer = NULL; + + + RpcTryExcept{ + + NtStatus = SamrEnumerateAliasesInDomain( + (SAMPR_HANDLE)DomainHandle, + EnumerationContext, + (PSAMPR_ENUMERATION_BUFFER *)&LocalBuffer, + PreferedMaximumLength, + CountReturned + ); + + + if (LocalBuffer != NULL) { + + // + // What comes back is a three level structure: + // + // Local +-------------+ + // Buffer ---> | EntriesRead | + // |-------------| +-------+ + // | Enumeration |--->| Name0 | --- > (NameBuffer0) + // | Return | |-------| o + // | Buffer | | ... | o + // +-------------+ |-------| o + // | NameN | --- > (NameBufferN) + // +-------+ + // + // The buffer containing the EntriesRead field is not returned + // to our caller. Only the buffers containing name information + // are returned. + // + + if (LocalBuffer->Buffer != NULL) { + (*Buffer) = LocalBuffer->Buffer; + } + + MIDL_user_free( LocalBuffer); + + + } + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); + +} + + + + +NTSTATUS +SamGetAliasMembership( + IN SAM_HANDLE DomainHandle, + IN ULONG PassedCount, + IN PSID *Sids, + OUT PULONG MembershipCount, + OUT PULONG *Aliases +) + +/*++ + +Routine Description: + + This API searches the set of aliases in the specified domain to see + which aliases, if any, the passed SIDs are members of. Any aliases + that any of the SIDs are found to be members of are returned. + + +Parameters: + + DomainHandle - A domain handle returned from a previous call to + SamOpenDomain. + + PassedCount - Specifies the number of Sids being passed. + + Sids - Pointer to an array of Count pointers to Sids whose alias + memberships are to be looked up. + + MembershipCount - Receives the number of aliases that are being + returned via the Aliases parameter. + + Aliases - Receives a pointer to an array of SIDs. This is the set + of aliases the passed SIDs were found to be members of. If + MembershipCount is returned as zero, then a null value will be + returned here. + + When this information is no longer needed, it must be released + by passing the returned pointer to SamFreeMemory(). + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have privilege required to + request that data. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + +--*/ +{ + + NTSTATUS NtStatus; + SAMPR_PSID_ARRAY Accounts; + SAMPR_ULONG_ARRAY Membership; + + SampOutputDebugString("SamAliasMembership"); + + // + // Make sure we aren't trying to have RPC allocate the EnumerationContext. + // + + if ( !ARGUMENT_PRESENT(Sids) ) { + return(STATUS_INVALID_PARAMETER); + } + if ( !ARGUMENT_PRESENT(MembershipCount) ) { + return(STATUS_INVALID_PARAMETER); + } + if ( !ARGUMENT_PRESENT(Aliases) ) { + return(STATUS_INVALID_PARAMETER); + } + + + // + // Call the server ... + // + + Membership.Element = NULL; + + RpcTryExcept{ + + Accounts.Count = PassedCount; + Accounts.Sids = (PSAMPR_SID_INFORMATION)Sids; + + NtStatus = SamrGetAliasMembership( + (SAMPR_HANDLE)DomainHandle, + &Accounts, + &Membership + ); + + if (NT_SUCCESS(NtStatus)) { + (*MembershipCount) = Membership.Count; + (*Aliases) = Membership.Element; + } else { + + // + // Deallocate any returned buffers on error + // + + if (Membership.Element != NULL) { + MIDL_user_free(Membership.Element); + } + } + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); + +} + + + + + +NTSTATUS +SamLookupNamesInDomain( + IN SAM_HANDLE DomainHandle, + IN ULONG Count, + IN PUNICODE_STRING Names, + OUT PULONG *RelativeIds, + OUT PSID_NAME_USE *Use +) + +/*++ + +Routine Description: + + This API attempts to find relative IDs corresponding to name + strings. If a name can not be mapped to a relative ID, a zero is + placed in the corresponding relative ID array entry, and translation + continues. + + DOMAIN_LOOKUP access to the domain is needed to use this service. + + +Parameters: + + DomainHandle - A domain handle returned from a previous call to + SamOpenDomain. + + Count - Number of names to translate. + + Names - Pointer to an array of Count UNICODE_STRINGs that contain + the names to map to relative IDs. Case-insensitive + comparisons of these names will be performed for the lookup + operation. + + RelativeIds - Receives a pointer to an array of Count Relative IDs + that have been filled in. The relative ID of the nth name will + be the nth entry in this array. Any names that could not be + translated will have a zero relative ID. This buffer must be + freed when no longer needed using SamFreeMemory(). + + Use - Recieves a pointer to an array of Count SID_NAME_USE + entries that have been filled in with what significance each + name has. The nth entry in this array indicates the meaning + of the nth name passed. This buffer must be freed when no longer + needed using SamFreeMemory(). + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The domain handle passed is invalid. + + STATUS_SOME_NOT_MAPPED - Some of the names provided could not be + mapped. This is a successful return. + + STATUS_NONE_MAPPED - No names could be mapped. This is an error + return. + + +--*/ +{ + + + NTSTATUS + ReturnStatus, + NtStatus; + + LIST_ENTRY + CallHead; + + PSAMP_NAME_LOOKUP_CALL + Next; + + PSID_NAME_USE + UseBuffer; + + PULONG + RidBuffer; + + ULONG + Calls, + CallLength, + i; + + BOOLEAN + NoneMapped = TRUE, + SomeNotMapped = FALSE; + + SampOutputDebugString("SamLookupNamesInDomain"); + + + if ( (Count == 0) || (Names == NULL) ) { + return(STATUS_INVALID_PARAMETER); + } + + + // + // Default error return + // + + (*Use) = UseBuffer = NULL; + (*RelativeIds) = RidBuffer = NULL; + + + // + // Set up the call structures list + // + + InitializeListHead( &CallHead ); + Calls = 0; + + + // + // By default we will return NONE_MAPPED. + // This will get superseded by either STATUS_SUCCESS + // or STATUS_SOME_NOT_MAPPED. + // + + // + // Now build up and make each call + // + + i = 0; + while ( i < Count ) { + + // + // Make sure the next entry isn't too long. + // That would put us in an infinite loop. + // + + if (Names[i].Length > SAM_MAXIMUM_LOOKUP_LENGTH) { + ReturnStatus = STATUS_INVALID_PARAMETER; + goto SampNameLookupFreeAndReturn; + } + + // + // Get the next call structure + // + + Next = (PSAMP_NAME_LOOKUP_CALL)MIDL_user_allocate( sizeof(SAMP_NAME_LOOKUP_CALL) ); + + if (Next == NULL) { + ReturnStatus = STATUS_INSUFFICIENT_RESOURCES; + goto SampNameLookupFreeAndReturn; + } + + // + // Fill in the call structure. + // It takes a little to figure out how many entries to send in + // this call. It is limited by both Count (sam_MAXIMUM_LOOKUP_COUNT) + // and by size (SAM_MAXIMUM_LOOKUP_LENGTH). + // + + Next->Count = 0; + Next->StartIndex = i; + Next->RidBuffer.Element = NULL; + Next->UseBuffer.Element = NULL; + + CallLength = 0; + for ( i=i; + ( (i < Count) && + (CallLength+Names[i].Length < SAM_MAXIMUM_LOOKUP_LENGTH) && + (Next->Count < SAM_MAXIMUM_LOOKUP_COUNT) + ); + i++ ) { + + // + // Add in the next length and increment the number of entries + // being processed by this call. + // + + CallLength += Names[i].Length; + Next->Count ++; + + } + + + + // + // Add this call structure to the list of call structures + // + + Calls ++; + InsertTailList( &CallHead, &Next->Link ); + + + // + // Now make the call + // + + RpcTryExcept{ + + NtStatus = SamrLookupNamesInDomain( + (SAMPR_HANDLE)DomainHandle, + Next->Count, + (PRPC_UNICODE_STRING)(&Names[Next->StartIndex]), + &Next->RidBuffer, + &Next->UseBuffer + ); + + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + + // + // Keep track of what our completion status should be. + // + + if (!NT_SUCCESS(NtStatus) && + NtStatus != STATUS_NONE_MAPPED) { + ReturnStatus = NtStatus; // Unexpected error + goto SampNameLookupFreeAndReturn; + } + + if (NT_SUCCESS(NtStatus)) { + NoneMapped = FALSE; + if (NtStatus == STATUS_SOME_NOT_MAPPED) { + SomeNotMapped = TRUE; + } + } + } + + + // + // Set our return status... + // + + if (NoneMapped) { + ASSERT(SomeNotMapped == FALSE); + ReturnStatus = STATUS_NONE_MAPPED; + } else if (SomeNotMapped) { + ReturnStatus = STATUS_SOME_NOT_MAPPED; + } else { + ReturnStatus = STATUS_SUCCESS; + } + + + + + // + // At this point we have (potentially) a lot of call structures. + // The RidBuffer and UseBuffer elements of each call structure + // is allocated and returned by the RPC call and looks + // like: + // + // RidBuffer + // +-------------+ + // | Count | + // |-------------| +-------+ * + // | Element ---|--->| Rid-0 | | / + // +-------------+ |-------| | / Only this part + // | ... | > < is allocated by + // |-------| | \ the rpc call. + // | Rid-N | | \ + // +-------+ * + // + // If only one RPC call was made, we can return this information + // directly. Otherwise, we need to copy the information from + // all the calls into a single large buffer and return that buffer + // (freeing all the individual call buffers). + // + // The user is responsible for freeing whichever buffer we do + // return. + // + + ASSERT(Calls != 0); // Error go around this path, success always has calls + + + // + // Optimize for a single call + // + + if (Calls == 1) { + (*Use) = (PSID_NAME_USE) + (((PSAMP_NAME_LOOKUP_CALL)(CallHead.Flink))-> + UseBuffer.Element); + (*RelativeIds) = ((PSAMP_NAME_LOOKUP_CALL)(CallHead.Flink))-> + RidBuffer.Element; + MIDL_user_free( CallHead.Flink ); // Free the call structure + return(ReturnStatus); + } + + + // + // More than one call. + // Allocate return buffers large enough to copy all the information into. + // + + RidBuffer = MIDL_user_allocate( sizeof(ULONG) * Count ); + if (RidBuffer == NULL) { + ReturnStatus = STATUS_INSUFFICIENT_RESOURCES; + goto SampNameLookupFreeAndReturn; + } + + UseBuffer = MIDL_user_allocate( sizeof(SID_NAME_USE) * Count ); + if (UseBuffer == NULL) { + MIDL_user_free( RidBuffer ); + RidBuffer = NULL; + ReturnStatus = STATUS_INSUFFICIENT_RESOURCES; + goto SampNameLookupFreeAndReturn; + } + + + + +SampNameLookupFreeAndReturn: + + // + // Walk the list of calls. + // For each call: + // + // If we have a return buffer, copy the results into it. + // Free the call buffers. + // Free the call structure itself. + // + // Completion status has already been set appropriatly in ReturnStatus. + // + + Next = (PSAMP_NAME_LOOKUP_CALL)RemoveHeadList( &CallHead ); + while (Next != (PSAMP_NAME_LOOKUP_CALL)&CallHead) { + + // + // Copy RID information and then free the call buffer + // + + if (RidBuffer != NULL) { + RtlMoveMemory( + &RidBuffer[ Next->StartIndex ], // Destination + &Next->RidBuffer.Element[0], // Source + Next->Count * sizeof(ULONG) // Length + ); + } + + if (Next->RidBuffer.Element != NULL) { + MIDL_user_free( Next->RidBuffer.Element ); + } + + + // + // Copy USE information and then free the call buffer + // + + if (UseBuffer != NULL) { + RtlMoveMemory( + &UseBuffer[ Next->StartIndex ], // Destination + &Next->UseBuffer.Element[0], // Source + Next->Count * sizeof(SID_NAME_USE) // Length + ); + } + + if (Next->UseBuffer.Element != NULL) { + MIDL_user_free( Next->UseBuffer.Element ); + } + + // + // Free the call structure itself + // + + MIDL_user_free( Next ); + + Next = (PSAMP_NAME_LOOKUP_CALL)RemoveHeadList( &CallHead ); + } // end-while + + + + // + // For better or worse, we're all done + // + + (*Use) = UseBuffer; + (*RelativeIds) = RidBuffer; + + + return(SampMapCompletionStatus(NtStatus)); +} + + +NTSTATUS +SamLookupIdsInDomain( + IN SAM_HANDLE DomainHandle, + IN ULONG Count, + IN PULONG RelativeIds, + OUT PUNICODE_STRING *Names, + OUT PSID_NAME_USE *Use OPTIONAL + ) + +/*++ + +Routine Description: + + This API maps a number of relative IDs to their corresponding names. + The use of the name (domain, group, alias, user, or unknown) is also + returned. + + The API stores the actual names in Buffer, then creates an array of + UNICODE_STRINGs in the Names OUT parameter. If a relative ID can + not be mapped, a NULL value is placed in the slot for the + UNICODE_STRING, and STATUS_SOME_NOT_MAPPED is returned. + + DOMAIN_LOOKUP access to the domain is needed to use this service. + + +Parameters: + + DomainHandle - A domain handle returned from a previous call to + SamOpenDomain. + + Count - Provides the number of relative IDs to translate. + + RelativeIds - Array of Count relative IDs to be mapped. + + Names - Receives a pointer to an array of Count UNICODE_STRINGs that + have been filled in. The nth pointer within this array will + correspond the nth relative id passed . Each name string buffer + will be in a separately allocated block of memory. Any entry is + not successfully translated will have a NULL name buffer pointer + returned. This Names buffer must be freed using SamFreeMemory() + when no longer needed. + + Use - Optionally, receives a pointer to an array of Count SID_NAME_USE + entries that have been filled in with what significance each + name has. The nth entry in this array indicates the meaning + of the nth name passed. This buffer must be freed when no longer + needed using SamFreeMemory(). + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The domain handle passed is invalid. + + STATUS_SOME_NOT_MAPPED - Some of the names provided could not be + mapped. This is a successful return. + + STATUS_NONE_MAPPED - No names could be mapped. This is an error + return. +--*/ + +{ + NTSTATUS NtStatus; + ULONG SubRequest, SubRequests; + ULONG TotalCountToDate; + ULONG Index, UsedLength, Length, NamesLength; + ULONG UsesLength, LastSubRequestCount; + PULONG UstringStructDisps; + PULONG Counts = NULL; + PULONG RidIndices = NULL; + PUNICODE_STRING *SubRequestNames = NULL; + PSID_NAME_USE *SubRequestUses = NULL; + PUNICODE_STRING OutputNames = NULL; + PSID_NAME_USE OutputUses = NULL; + PUCHAR Destination = NULL, Source = NULL; + PUNICODE_STRING DestUstring = NULL; + ULONG SomeNotMappedStatusCount = 0; + ULONG NoneMappedStatusCount = 0; + + SampOutputDebugString("SamLookupIdsInDomain"); + + // + // If the Count for this request does not exceed the maximum limit that + // can be looked up in a single call, just call the Sub Request version + // of the routine. + // + + if (Count <= SAM_MAXIMUM_LOOKUP_COUNT) { + + NtStatus = SampLookupIdsInDomain( + DomainHandle, + Count, + RelativeIds, + Names, + Use + ); + + return(NtStatus); + } + + // + // Break down larger requests into smaller chunks + // + + SubRequests = Count / SAMP_MAXIMUM_SUB_LOOKUP_COUNT; + LastSubRequestCount = Count % SAMP_MAXIMUM_SUB_LOOKUP_COUNT; + + if (LastSubRequestCount > 0) { + + SubRequests++; + } + + // + // Allocate memory for array of starting Rid Indices, Rid Counts and + // Unicode String block offsets for each SubRequest. + // + + NtStatus = STATUS_NO_MEMORY; + + RidIndices = MIDL_user_allocate( SubRequests * sizeof(ULONG) ); + + if (RidIndices == NULL) { + + goto LookupIdsInDomainError; + } + + Counts = MIDL_user_allocate( SubRequests * sizeof(ULONG) ); + + if (Counts == NULL) { + + goto LookupIdsInDomainError; + } + + SubRequestNames = MIDL_user_allocate( SubRequests * sizeof(PUNICODE_STRING) ); + + if (SubRequestNames == NULL) { + + goto LookupIdsInDomainError; + } + + SubRequestUses = MIDL_user_allocate( SubRequests * sizeof(SID_NAME_USE) ); + + if (SubRequestUses == NULL) { + + goto LookupIdsInDomainError; + } + + UstringStructDisps = MIDL_user_allocate( SubRequests * sizeof(ULONG) ); + + if (UstringStructDisps == NULL) { + + goto LookupIdsInDomainError; + } + + NtStatus = STATUS_SUCCESS; + + TotalCountToDate = 0; + + for (SubRequest = 0; SubRequest < SubRequests; SubRequest++) { + + RidIndices[SubRequest] = TotalCountToDate; + + if ((Count - TotalCountToDate) > SAMP_MAXIMUM_SUB_LOOKUP_COUNT) { + + Counts[SubRequest] = SAMP_MAXIMUM_SUB_LOOKUP_COUNT; + + } else { + + Counts[SubRequest] = Count - TotalCountToDate; + } + + TotalCountToDate += Counts[SubRequest]; + + NtStatus = SampLookupIdsInDomain( + DomainHandle, + Counts[SubRequest], + &RelativeIds[RidIndices[SubRequest]], + &SubRequestNames[SubRequest], + &SubRequestUses[SubRequest] + ); + + // + // We keep a tally of the number of times STATUS_SOME_NOT_MAPPED + // and STATUS_NONE_MAPPED were returned. This is so that we + // can return the appropriate status at the end based on the + // global picture. We continue lookups after either status code + // is encountered. + // + + if (NtStatus == STATUS_SOME_NOT_MAPPED) { + + SomeNotMappedStatusCount++; + + } else if (NtStatus == STATUS_NONE_MAPPED) { + + NoneMappedStatusCount++; + NtStatus = STATUS_SUCCESS; + + } + + if (!NT_SUCCESS(NtStatus)) { + + break; + } + } + + if (!NT_SUCCESS(NtStatus)) { + + goto LookupIdsInDomainError; + } + + // + // Now allocate a single buffer for the Names + // + + NamesLength = Count * sizeof(UNICODE_STRING); + + for (SubRequest = 0; SubRequest < SubRequests; SubRequest++) { + + for (Index = 0; Index < Counts[SubRequest]; Index++) { + + NamesLength += (SubRequestNames[SubRequest] + Index)->MaximumLength; + } + } + + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + + OutputNames = MIDL_user_allocate( NamesLength ); + + if (OutputNames == NULL) { + + goto LookupIdsInDomainError; + } + + NtStatus = STATUS_SUCCESS; + + // + // Now copy in the Unicode String Structures for the Names returned from + // each subrequest. We will later overwrite the Buffer fields in them + // when we assign space and move in each Unicode String. + // + + Destination = (PUCHAR) OutputNames; + UsedLength = 0; + + for (SubRequest = 0; SubRequest < SubRequests; SubRequest++) { + + Source = (PUCHAR) SubRequestNames[SubRequest]; + Length = Counts[SubRequest] * sizeof(UNICODE_STRING); + UstringStructDisps[SubRequest] = (Destination - Source); + RtlMoveMemory( Destination, Source, Length ); + Destination += Length; + UsedLength += Length; + } + + // + // Now copy in the Unicode Strings themselves. These are appended to + // the array of Unicode String structures. As we go, update the + // Unicode string buffer pointers to point to the copied version + // of each string. + // + + for (SubRequest = 0; SubRequest < SubRequests; SubRequest++) { + + for (Index = 0; Index < Counts[SubRequest]; Index++) { + + Source = (PUCHAR)(SubRequestNames[SubRequest] + Index)->Buffer; + Length = (ULONG)(SubRequestNames[SubRequest] + Index)->MaximumLength; + + // + // It is possible that a returned Unicode String has 0 length + // because an Id was not mapped. In this case, skip to the next + // one. + // + + if (Length == 0) { + + continue; + } + + DestUstring = (PUNICODE_STRING) + (((PUCHAR)(SubRequestNames[SubRequest] + Index)) + + UstringStructDisps[SubRequest]); + + DestUstring->Buffer = (PWSTR) Destination; + + ASSERT(UsedLength + Length <= NamesLength); + + RtlMoveMemory( Destination, Source, Length ); + Destination += Length; + UsedLength += Length; + continue; + } + } + + if (!NT_SUCCESS(NtStatus)) { + + goto LookupIdsInDomainError; + } + + // + // Now allocate a single buffer for the Uses + // + + UsesLength = Count * sizeof(SID_NAME_USE); + + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + + OutputUses = MIDL_user_allocate( UsesLength ); + + if (OutputUses == NULL) { + + goto LookupIdsInDomainError; + } + + NtStatus = STATUS_SUCCESS; + + // + // Now copy in the SID_NAME_USE Structures for the Uses returned from + // each subrequest. + // + + Destination = (PUCHAR) OutputUses; + UsedLength = 0; + + for (SubRequest = 0; SubRequest < SubRequests; SubRequest++) { + + Source = (PUCHAR) SubRequestUses[SubRequest]; + Length = Counts[SubRequest] * sizeof(SID_NAME_USE); + RtlMoveMemory( Destination, Source, Length ); + Destination += Length; + UsedLength += Length; + } + + if (!NT_SUCCESS(NtStatus)) { + + goto LookupIdsInDomainError; + } + + *Names = OutputNames; + *Use = OutputUses; + + // + // Determine the final status to return. This is the normal NtStatus code + // except that if NtStatus is a success status and none/not all Ids were + // mapped, NtStatus will be set to either STATUS_SOME_NOT_MAPPED or + // STATUS_NONE_MAPPED as appropriate. If STATUS_SOME_NOT_MAPPED was + // returned on at least one call, return that status. If STATUS_NONE_MAPPED + // was returned on all calls, return that status. + // + + if (NT_SUCCESS(NtStatus)) { + + if (SomeNotMappedStatusCount > 0) { + + NtStatus = STATUS_SOME_NOT_MAPPED; + + } else if (NoneMappedStatusCount == SubRequests) { + + NtStatus = STATUS_NONE_MAPPED; + + } else if (NoneMappedStatusCount > 0) { + + // + // One or more calls returned STATUS_NONE_MAPPED but not all. + // So at least one Id was mapped, but not all ids. + // + + NtStatus = STATUS_SOME_NOT_MAPPED; + } + } + +LookupIdsInDomainFinish: + + // + // If necessary, free the buffer containing the starting Rid Indices for + // each Sub Request. + // + + if (RidIndices != NULL) { + + MIDL_user_free(RidIndices); + RidIndices = NULL; + } + + // + // If necessary, free the buffer containing the Rid Counts for + // each Sub Request. + // + + if (Counts != NULL) { + + MIDL_user_free(Counts); + Counts = NULL; + } + + // + // If necessary, free the buffer containing the offsets from the + // source and destination Unicode String structures. + // + + if (UstringStructDisps != NULL) { + + MIDL_user_free(UstringStructDisps); + UstringStructDisps = NULL; + } + + // + // If necessary, free the SubRequestNames output returned for each + // Sub Request. + // + + if (SubRequestNames != NULL) { + + for (SubRequest = 0; SubRequest < SubRequests; SubRequest++) { + + if (SubRequestNames[SubRequest] != NULL) { + + MIDL_user_free(SubRequestNames[SubRequest]); + SubRequestNames[SubRequest] = NULL; + } + } + + MIDL_user_free(SubRequestNames); + SubRequestNames = NULL; + } + + // + // If necessary, free the SubRequestUses output returned for each + // Sub Request. + // + + if (SubRequestUses != NULL) { + + for (SubRequest = 0; SubRequest < SubRequests; SubRequest++) { + + if (SubRequestUses[SubRequest] != NULL) { + + MIDL_user_free(SubRequestUses[SubRequest]); + SubRequestUses[SubRequest] = NULL; + } + } + + MIDL_user_free(SubRequestUses); + SubRequestUses = NULL; + } + + return(NtStatus); + +LookupIdsInDomainError: + + // + // If necessary, free the buffers we would hande returned for + // Names and Use. + // + + if (OutputNames != NULL) { + + MIDL_user_free(OutputNames); + OutputNames = NULL; + } + + if (OutputUses != NULL) { + + MIDL_user_free(OutputUses); + OutputUses = NULL; + } + + *Names = NULL; + *Use = NULL; + + goto LookupIdsInDomainFinish; +} + + +NTSTATUS +SampLookupIdsInDomain( + IN SAM_HANDLE DomainHandle, + IN ULONG Count, + IN PULONG RelativeIds, + OUT PUNICODE_STRING *Names, + OUT PSID_NAME_USE *Use OPTIONAL + ) + +/*++ + +Routine Description: + + This API maps a number of relative IDs to their corresponding names. + The use of the name (domain, group, alias, user, or unknown) is also + returned. + + The API stores the actual names in Buffer, then creates an array of + UNICODE_STRINGs in the Names OUT parameter. If a relative ID can + not be mapped, a NULL value is placed in the slot for the + UNICODE_STRING, and STATUS_SOME_NOT_MAPPED is returned. + + DOMAIN_LOOKUP access to the domain is needed to use this service. + + +Parameters: + + DomainHandle - A domain handle returned from a previous call to + SamOpenDomain. + + Count - Provides the number of relative IDs to translate. + + RelativeIds - Array of Count relative IDs to be mapped. + + Names - Receives a pointer to an array of Count UNICODE_STRINGs that + have been filled in. The nth pointer within this array will + correspond the nth relative id passed . Each name string buffer + will be in a separately allocated block of memory. Any entry is + not successfully translated will have a NULL name buffer pointer + returned. This Names buffer must be freed using SamFreeMemory() + when no longer needed. + + Use - Optionally, receives a pointer to an array of Count SID_NAME_USE + entries that have been filled in with what significance each + name has. The nth entry in this array indicates the meaning + of the nth name passed. This buffer must be freed when no longer + needed using SamFreeMemory(). + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The domain handle passed is invalid. + + STATUS_SOME_NOT_MAPPED - Some of the names provided could not be + mapped. This is a successful return. + + STATUS_NONE_MAPPED - No names could be mapped. This is an error + return. + + +--*/ + +{ + + NTSTATUS NtStatus; + SAMPR_RETURNED_USTRING_ARRAY NameBuffer; + SAMPR_ULONG_ARRAY UseBuffer; + + SampOutputDebugString("SamLookupIdsInDomain"); + + // + // Make sure our parameters are within bounds + // + + if (RelativeIds == NULL) { + return(STATUS_INVALID_PARAMETER); + } + + if (Count > SAM_MAXIMUM_LOOKUP_COUNT) { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + + + // + // Call the server ... + // + + + NameBuffer.Element = NULL; + UseBuffer.Element = NULL; + + + RpcTryExcept{ + + NtStatus = SamrLookupIdsInDomain( + (SAMPR_HANDLE)DomainHandle, + Count, + RelativeIds, + &NameBuffer, + &UseBuffer + ); + + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + + // + // What comes back for the "Names" OUT parameter is a two level + // structure, the first level of which is on our stack: + // + // NameBuffer + // +-------------+ + // | Count | + // |-------------| +-------+ + // | Element ---|--->| Name0 | --- > (NameBuffer0) + // +-------------+ |-------| o + // | ... | o + // |-------| o + // | NameN | --- > (NameBufferN) + // +-------+ + // + // The buffer containing the EntriesRead field is not returned + // to our caller. Only the buffers containing name information + // are returned. + // + // NOTE: The buffers containing name information are allocated + // by the RPC runtime in a single buffer. This is caused + // by a line the the client side .acf file. + // + + // + // What comes back for the "Use" OUT parameter is a two level + // structure, the first level of which is on our stack: + // + // UseBuffer + // +-------------+ + // | Count | + // |-------------| +-------+ + // | Element ---|--->| Use-0 | + // +-------------+ |-------| + // | ... | + // |-------| + // | Use-N | + // +-------+ + // + // The Pointer in the Element field is what gets returned + // to our caller. The caller is responsible for deallocating + // this when no longer needed. + // + + (*Names) = (PUNICODE_STRING)NameBuffer.Element; + if ( ARGUMENT_PRESENT(Use) ) { + (*Use) = (PSID_NAME_USE) UseBuffer.Element; + } else { + if (UseBuffer.Element != NULL) { + MIDL_user_free(UseBuffer.Element); + UseBuffer.Element = NULL; + } + } + + + // + // Don't force our caller to deallocate things on unexpected + // return value. + // + + if (NtStatus != STATUS_SUCCESS && + NtStatus != STATUS_SOME_NOT_MAPPED + ) { + if ( ARGUMENT_PRESENT(Use) ) { + (*Use) = NULL; + } + if (UseBuffer.Element != NULL) { + MIDL_user_free(UseBuffer.Element); + UseBuffer.Element = NULL; + } + + (*Names) = NULL; + if (NameBuffer.Element != NULL) { + MIDL_user_free(NameBuffer.Element); + NameBuffer.Element = NULL; + } + + } + + + return(SampMapCompletionStatus(NtStatus)); + + + + +} + + + +NTSTATUS +SamQueryDisplayInformation ( + IN SAM_HANDLE DomainHandle, + IN DOMAIN_DISPLAY_INFORMATION DisplayInformation, + IN ULONG Index, + IN ULONG EntryCount, + IN ULONG PreferredMaximumLength, + OUT PULONG TotalAvailable, + OUT PULONG TotalReturned, + OUT PULONG ReturnedEntryCount, + OUT PVOID *SortedBuffer + ) +/*++ + +Routine Description: + + This routine provides fast return of information commonly + needed to be displayed in user interfaces. + + NT User Interface has a requirement for quick enumeration of SAM + accounts for display in list boxes. (Replication has similar but + broader requirements.) + + +Parameters: + + DomainHandle - A handle to an open domain for DOMAIN_LIST_ACCOUNTS. + + DisplayInformation - Indicates which information is to be enumerated. + + Index - The index of the first entry to be retrieved. + + PreferedMaximumLength - A recommended upper limit to the number of + bytes to be returned. The returned information is allocated by + this routine. + + TotalAvailable - Total number of bytes availabe in the specified info + class. This parameter is optional (and is not returned) for + the following info levels: + + DomainDisplayOemUser + DomainDisplayOemGroup + + + TotalReturned - Number of bytes actually returned for this call. Zero + indicates there are no entries with an index as large as that + specified. + + ReturnedEntryCount - Number of entries returned by this call. Zero + indicates there are no entries with an index as large as that + specified. + + + SortedBuffer - Receives a pointer to a buffer containing a sorted + list of the requested information. This buffer is allocated + by this routine and contains the following structure: + + + DomainDisplayUser --> An array of ReturnedEntryCount elements + of type DOMAIN_DISPLAY_USER. This is + followed by the bodies of the various + strings pointed to from within the + DOMAIN_DISPLAY_USER structures. + + DomainDisplayMachine --> An array of ReturnedEntryCount elements + of type DOMAIN_DISPLAY_MACHINE. This is + followed by the bodies of the various + strings pointed to from within the + DOMAIN_DISPLAY_MACHINE structures. + + DomainDisplayGroup --> An array of ReturnedEntryCount elements + of type DOMAIN_DISPLAY_GROUP. This is + followed by the bodies of the various + strings pointed to from within the + DOMAIN_DISPLAY_GROUP structures. + + DomainDisplayOemUser --> An array of ReturnedEntryCount elements + of type DOMAIN_DISPLAY_OEM_USER. This is + followed by the bodies of the various + strings pointed to from within the + DOMAIN_DISPLAY_OEM_user structures. + + DomainDisplayOemGroup --> An array of ReturnedEntryCount elements + of type DOMAIN_DISPLAY_OEM_GROUP. This is + followed by the bodies of the various + strings pointed to from within the + DOMAIN_DISPLAY_OEM_GROUP structures. + +Return Values: + + STATUS_SUCCESS - normal, successful completion. + + STATUS_ACCESS_DENIED - The specified handle was not opened for + the necessary access. + + STATUS_INVALID_HANDLE - The specified handle is not that of an + opened Domain object. + + STATUS_INVALID_INFO_CLASS - The requested class of information + is not legitimate for this service. + + +--*/ +{ + NTSTATUS + NtStatus; + + SAMPR_DISPLAY_INFO_BUFFER + DisplayInformationBuffer; + + ULONG + LocalTotalAvailable; + + SampOutputDebugString("SamQueryDisplayInformation"); + + // + // Check parameters + // + + if ( !ARGUMENT_PRESENT(TotalAvailable) && + (DisplayInformation != DomainDisplayOemUser) && + (DisplayInformation != DomainDisplayOemGroup) ) { + return(STATUS_INVALID_PARAMETER); + } + if ( !ARGUMENT_PRESENT(TotalReturned) ) { + return(STATUS_INVALID_PARAMETER); + } + if ( !ARGUMENT_PRESENT(ReturnedEntryCount) ) { + return(STATUS_INVALID_PARAMETER); + } + if ( !ARGUMENT_PRESENT(SortedBuffer) ) { + return(STATUS_INVALID_PARAMETER); + } + + // + // Call the server ... + // + + RpcTryExcept{ + + if (DisplayInformation <= DomainDisplayMachine) { + NtStatus = SamrQueryDisplayInformation( + (SAMPR_HANDLE)DomainHandle, + DisplayInformation, + Index, + EntryCount, + PreferredMaximumLength, + &LocalTotalAvailable, + TotalReturned, + &DisplayInformationBuffer); + + } else if (DisplayInformation <= DomainDisplayGroup) { + NtStatus = SamrQueryDisplayInformation2( + (SAMPR_HANDLE)DomainHandle, + DisplayInformation, + Index, + EntryCount, + PreferredMaximumLength, + &LocalTotalAvailable, + TotalReturned, + &DisplayInformationBuffer); + } else { + NtStatus = SamrQueryDisplayInformation3( + (SAMPR_HANDLE)DomainHandle, + DisplayInformation, + Index, + EntryCount, + PreferredMaximumLength, + &LocalTotalAvailable, + TotalReturned, + &DisplayInformationBuffer); + } + + if (NT_SUCCESS(NtStatus)) { + + if (ARGUMENT_PRESENT(TotalAvailable)) { + (*TotalAvailable) = LocalTotalAvailable; + } + + switch (DisplayInformation) { + + case DomainDisplayUser: + *ReturnedEntryCount = DisplayInformationBuffer.UserInformation.EntriesRead; + *SortedBuffer = DisplayInformationBuffer.UserInformation.Buffer; + break; + + case DomainDisplayMachine: + *ReturnedEntryCount = DisplayInformationBuffer.MachineInformation.EntriesRead; + *SortedBuffer = DisplayInformationBuffer.MachineInformation.Buffer; + break; + + case DomainDisplayGroup: + *ReturnedEntryCount = DisplayInformationBuffer.GroupInformation.EntriesRead; + *SortedBuffer = DisplayInformationBuffer.GroupInformation.Buffer; + break; + + case DomainDisplayOemUser: + *ReturnedEntryCount = DisplayInformationBuffer.OemUserInformation.EntriesRead; + *SortedBuffer = DisplayInformationBuffer.OemUserInformation.Buffer; + break; + + case DomainDisplayOemGroup: + *ReturnedEntryCount = DisplayInformationBuffer.OemGroupInformation.EntriesRead; + *SortedBuffer = DisplayInformationBuffer.OemGroupInformation.Buffer; + break; + + } + + } else { + *ReturnedEntryCount = 0; + *SortedBuffer = NULL; + } + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + // + // If the exception indicates the server doesn't have + // the selected api, that means the server doesn't know + // about the info level we passed. Set our completion + // status appropriately. + // + + if (RpcExceptionCode() == RPC_S_INVALID_LEVEL || + RpcExceptionCode() == RPC_S_PROCNUM_OUT_OF_RANGE || + RpcExceptionCode() == RPC_NT_PROCNUM_OUT_OF_RANGE ) { + NtStatus = STATUS_INVALID_INFO_CLASS; + } else { + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + } + + } RpcEndExcept; + + + return(SampMapCompletionStatus(NtStatus)); + +} + + + +NTSTATUS +SamGetDisplayEnumerationIndex ( + IN SAM_HANDLE DomainHandle, + IN DOMAIN_DISPLAY_INFORMATION DisplayInformation, + IN PUNICODE_STRING Prefix, + OUT PULONG Index + ) +/*++ + +Routine Description: + + This routine returns the index of the entry which alphabetically + immediatly preceeds a specified prefix. If no such entry exists, + then zero is returned as the index. + +Parameters: + + DomainHandle - A handle to an open domain for DOMAIN_LIST_ACCOUNTS. + + DisplayInformation - Indicates which sorted information class is + to be searched. + + Prefix - The prefix to compare. + + Index - Receives the index of the entry of the information class + with a LogonName (or MachineName) which immediatly preceeds the + provided prefix string. If there are no elements which preceed + the prefix, then zero is returned. + + +Return Values: + + STATUS_SUCCESS - normal, successful completion. + + STATUS_ACCESS_DENIED - The specified handle was not opened for + the necessary access. + + STATUS_INVALID_HANDLE - The specified handle is not that of an + opened Domain object. + + STATUS_NO_MORE_ENTRIES - There are no entries for this information class. + The returned index is invalid. + +--*/ +{ + NTSTATUS NtStatus; + + // + // Check parameters + // + + if ( !ARGUMENT_PRESENT(Prefix) ) { + return(STATUS_INVALID_PARAMETER); + } + if ( !ARGUMENT_PRESENT(Index) ) { + return(STATUS_INVALID_PARAMETER); + } + + + // + // Call the server ... + // + + RpcTryExcept{ + if (DisplayInformation <= DomainDisplayMachine) { + // + // Info levels supported via original API in NT1.0 + // + + NtStatus = SamrGetDisplayEnumerationIndex ( + (SAMPR_HANDLE)DomainHandle, + DisplayInformation, + (PRPC_UNICODE_STRING)Prefix, + Index + ); + } else { + + // + // Info levels added in NT1.0A via new API + // + + NtStatus = SamrGetDisplayEnumerationIndex2 ( + (SAMPR_HANDLE)DomainHandle, + DisplayInformation, + (PRPC_UNICODE_STRING)Prefix, + Index + ); + } + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); + + +} + + + + +NTSTATUS +SamOpenGroup( + IN SAM_HANDLE DomainHandle, + IN ACCESS_MASK DesiredAccess, + IN ULONG GroupId, + OUT PSAM_HANDLE GroupHandle + ) + +/*++ + +Routine Description: + + This API opens an existing group in the account database. The group + is specified by a ID value that is relative to the SID of the + domain. The operations that will be performed on the group must be + declared at this time. + + This call returns a handle to the newly opened group that may be + used for successive operations on the group. This handle may be + closed with the SamCloseHandle API. + + +Parameters: + + DomainHandle - A domain handle returned from a previous call to + SamOpenDomain. + + DesiredAccess - Is an access mask indicating which access types + are desired to the group. These access types are reconciled + with the Discretionary Access Control list of the group to + determine whether the accesses will be granted or denied. + + GroupId - Specifies the relative ID value of the group to be + opened. + + GroupHandle - Receives a handle referencing the newly opened + group. This handle will be required in successive calls to + operate on the group. + +Return Values: + + STATUS_SUCCESS - The group was successfully opened. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_NO_SUCH_GROUP - The specified group does not exist. + + STATUS_INVALID_HANDLE - The domain handle passed is invalid. + +--*/ +{ + NTSTATUS NtStatus; + + SampOutputDebugString("SamOpenGroup"); + + + // + // Call the server ... + // + + + RpcTryExcept{ + + (*GroupHandle) = 0; + + NtStatus = + SamrOpenGroup( + (SAMPR_HANDLE)DomainHandle, + DesiredAccess, + GroupId, + (SAMPR_HANDLE *)GroupHandle + ); + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); +} + + + +NTSTATUS +SamQueryInformationGroup( + IN SAM_HANDLE GroupHandle, + IN GROUP_INFORMATION_CLASS GroupInformationClass, + OUT PVOID * Buffer +) + +/*++ + +Routine Description: + + This API retrieves information on the group specified. + + +Parameters: + + GroupHandle - The handle of an opened group to operate on. + + GroupInformationClass - Class of information to retrieve. The + accesses required for each class is shown below: + + Info Level Required Access Type + ---------------------- ---------------------- + GroupGeneralInformation GROUP_READ_INFORMATION + GroupNameInformation GROUP_READ_INFORMATION + GroupAttributeInformation GROUP_READ_INFORMATION + GroupAdminInformation GROUP_READ_INFORMATION + + Buffer - Receives a pointer to a buffer containing the requested + information. When this information is no longer needed, this + buffer must be freed using SamFreeMemory(). + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_INVALID_INFO_CLASS - The class provided was invalid. + + +--*/ +{ + + NTSTATUS NtStatus; + + SampOutputDebugString("SamQueryInformationGroup"); + + + // + // Call the server ... + // + + + (*Buffer) = NULL; + + RpcTryExcept{ + + NtStatus = + SamrQueryInformationGroup( + (SAMPR_HANDLE)GroupHandle, + GroupInformationClass, + (PSAMPR_GROUP_INFO_BUFFER *)Buffer + ); + + + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); +} + + + +NTSTATUS +SamSetInformationGroup( + IN SAM_HANDLE GroupHandle, + IN GROUP_INFORMATION_CLASS GroupInformationClass, + IN PVOID Buffer +) +/*++ + + +Routine Description: + + This API allows the caller to modify group information. + + +Parameters: + + GroupHandle - The handle of an opened group to operate on. + + GroupInformationClass - Class of information to retrieve. The + accesses required for each class is shown below: + + Info Level Required Access Type + ------------------------ ------------------------- + + GroupGeneralInformation (can't write) + + GroupNameInformation GROUP_WRITE_ACCOUNT + GroupAttributeInformation GROUP_WRITE_ACCOUNT + GroupAdminInformation GROUP_WRITE_ACCOUNT + + Buffer - Buffer where information retrieved is placed. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_INFO_CLASS - The class provided was invalid. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_NO_SUCH_GROUP - The group specified is unknown. + + STATUS_SPECIAL_GROUP - The group specified is a special group and + cannot be operated on in the requested fashion. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled for this + operation + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. + + +--*/ +{ + + NTSTATUS NtStatus; + + SampOutputDebugString("SamSetInformationGroup"); + + + // + // Call the server ... + // + + RpcTryExcept{ + + NtStatus = + SamrSetInformationGroup( + (SAMPR_HANDLE)GroupHandle, + GroupInformationClass, + Buffer + ); + + + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); +} + + + +NTSTATUS +SamAddMemberToGroup( + IN SAM_HANDLE GroupHandle, + IN ULONG MemberId, + IN ULONG Attributes +) + +/*++ + +Routine Description: + + This API adds a member to a group. Note that this API requires the + GROUP_ADD_MEMBER access type for the group. + + +Parameters: + + GroupHandle - The handle of an opened group to operate on. + + MemberId - Relative ID of the member to add. + + Attributes - The attributes of the group assigned to the user. The + attributes assigned here must be compatible with the attributes + assigned to the group as a whole. Compatible attribute + assignments are: + + Mandatory - If the Mandatory attribute is assigned to the + group as a whole, then it must be assigned to the + group for each member of the group. + + EnabledByDefault - This attribute may be set to any value + for each member of the group. It does not matter + what the attribute value for the group as a whole + is. + + Enabled - This attribute may be set to any value for each + member of the group. It does not matter what the + attribute value for the group as a whole is. + + Owner - The Owner attribute may be assigned any value. + However, if the Owner attribute of the group as a + whole is not set, then the value assigned to + members is ignored. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_NO_SUCH_MEMBER - The member specified is unknown. + + STATUS_MEMBER_IN_GROUP - The member already belongs to the group. + + STATUS_INVALID_GROUP_ATTRIBUTES - Indicates the group attribute + values being assigned to the member are not compatible with + the attribute values of the group as a whole. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled for this + operation + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. + + +--*/ +{ + NTSTATUS NtStatus; + + + SampOutputDebugString("SamAddMemberToGroup"); + + // + // Call the server ... + // + + RpcTryExcept{ + + NtStatus = + SamrAddMemberToGroup( + (SAMPR_HANDLE)GroupHandle, + MemberId, + Attributes + ); + + + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); + +} + + + +NTSTATUS +SamDeleteGroup( + IN SAM_HANDLE GroupHandle +) + +/*++ + +Routine Description: + + This API removes a group from the account database. There may be no + members in the group or the deletion request will be rejected. Note + that this API requires DELETE access to the specific group being + deleted. + + +Parameters: + + GroupHandle - The handle of an opened group to operate on. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_SPECIAL_GROUP - The group specified is a special group and + cannot be operated on in the requested fashion. + + STATUS_MEMBER_IN_GROUP - The group still has members. A group may + not be deleted unless it has no members. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled for this + operation + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. + + +--*/ +{ + + NTSTATUS NtStatus; + SAMPR_HANDLE LocalHandle; + + SampOutputDebugString("SamDeleteGroup"); + + LocalHandle = (SAMPR_HANDLE)GroupHandle; + + if (LocalHandle == 0) { + return(STATUS_INVALID_HANDLE); + } + + + // + // Call the server ... + // + + RpcTryExcept{ + + NtStatus = SamrDeleteGroup( &LocalHandle ); + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); + +} + + + +NTSTATUS +SamRemoveMemberFromGroup( + IN SAM_HANDLE GroupHandle, + IN ULONG MemberId +) + +/*++ + +Routine Description: + + This API removes a single member from a group. Note that this API + requires the GROUP_REMOVE_MEMBER access type. + + +Parameters: + + GroupHandle - The handle of an opened group to operate on. + + MemberId - Relative ID of the member to remove. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_SPECIAL_GROUP - The group specified is a special group and + cannot be operated on in the requested fashion. + + STATUS_MEMBER_NOT_IN_GROUP - The specified user does not belong + to the group. + + STATUS_MEMBERS_PRIMARY_GROUP - A user may not be removed from its + primary group. The primary group of the user account must first + be changed. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled for this + operation + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. + + +--*/ +{ + NTSTATUS NtStatus; + + SampOutputDebugString("SamRemoveMemberFromGroup"); + + + // + // Call the server ... + // + + RpcTryExcept{ + + NtStatus = + SamrRemoveMemberFromGroup( + (SAMPR_HANDLE)GroupHandle, + MemberId + ); + + + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); + +} + + + +NTSTATUS +SamGetMembersInGroup( + IN SAM_HANDLE GroupHandle, + OUT PULONG * MemberIds, + OUT PULONG * Attributes, + OUT PULONG MemberCount +) + +/*++ + +Routine Description: + + This API lists all the members in a group. This API requires + GROUP_LIST_MEMBERS access to the group. + + +Parameters: + + GroupHandle - The handle of an opened group to operate on. + + MemberIds - Receives a pointer to a buffer containing An array of + ULONGs. These ULONGs contain the relative IDs of the members + of the group. When this information is no longer needed, + this buffer must be freed using SamFreeMemory(). + + Attributes - Receives a pointer to a buffer containing an array of + ULONGs. These ULONGs contain the attributes of the + corresponding member ID returned via the MemberId paramter. + + MemberCount - number of members in the group (and, thus, the + number relative IDs returned). + +Return Values: + + STATUS_SUCCESS - The Service completed successfully, and there + are no additional entries. + + STATUS_ACCESS_DENIED - Caller does not have privilege required to + request that data. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + +--*/ +{ + + NTSTATUS NtStatus; + PSAMPR_GET_MEMBERS_BUFFER GetMembersBuffer; + + + SampOutputDebugString("SamGetMembersInGroup"); + + + // + // Call the server ... + // + + + GetMembersBuffer = NULL; + + RpcTryExcept{ + + NtStatus = + SamrGetMembersInGroup( + (SAMPR_HANDLE)GroupHandle, + &GetMembersBuffer + ); + + // + // What we get back is the following: + // + // +-------------+ + // --------->| MemberCount | + // |-------------+ +-------+ + // | Members --|------------------->| Rid-0 | + // |-------------| +------------+ | ... | + // | Attributes-|-->| Attribute0 | | | + // +-------------+ | ... | | Rid-N | + // | AttributeN | +-------+ + // +------------+ + // + // Each block allocated with MIDL_user_allocate. We return the + // RID and ATTRIBUTE blocks, and free the block containing + // the MemberCount ourselves. + // + + + if (NT_SUCCESS(NtStatus)) { + (*MemberCount) = GetMembersBuffer->MemberCount; + (*MemberIds) = GetMembersBuffer->Members; + (*Attributes) = GetMembersBuffer->Attributes; + MIDL_user_free( GetMembersBuffer ); + } else { + + // + // Deallocate any returned buffers on error + // + + if (GetMembersBuffer != NULL) { + if (GetMembersBuffer->Members != NULL) { + MIDL_user_free(GetMembersBuffer->Members); + } + if (GetMembersBuffer->Attributes != NULL) { + MIDL_user_free(GetMembersBuffer->Attributes); + } + MIDL_user_free(GetMembersBuffer); + } + } + + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); + + +} + + + +NTSTATUS +SamSetMemberAttributesOfGroup( + IN SAM_HANDLE GroupHandle, + IN ULONG MemberId, + IN ULONG Attributes +) + +/*++ + +Routine Description: + + This routine modifies the group attributes of a member of the group. + + +Parameters: + + GroupHandle - The handle of an opened group to operate on. This + handle must be open for GROUP_ADD_MEMBER access to the group. + + MemberId - Contains the relative ID of member whose attributes + are to be modified. + + Attributes - The group attributes to set for the member. These + attributes must not conflict with the attributes of the group + as a whole. See SamAddMemberToGroup() for more information + on compatible attribute settings. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully, and there + are no additional entries. + + STATUS_INVALID_INFO_CLASS - The class provided was invalid. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_NO_SUCH_USER - The user specified does not exist. + + STATUS_MEMBER_NOT_IN_GROUP - Indicates the specified relative ID + is not a member of the group. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled for this + operation + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. + + +--*/ +{ + NTSTATUS NtStatus; + + SampOutputDebugString("SamSetMemberAttributesOfGroup"); + + + // + // Call the server ... + // + + RpcTryExcept{ + + NtStatus = + SamrSetMemberAttributesOfGroup( + (SAMPR_HANDLE)GroupHandle, + MemberId, + Attributes + ); + + + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); + +} + + + +NTSTATUS +SamOpenAlias( + IN SAM_HANDLE DomainHandle, + IN ACCESS_MASK DesiredAccess, + IN ULONG AliasId, + OUT PSAM_HANDLE AliasHandle + ) + +/*++ + +Routine Description: + + This API opens an existing Alias object. The Alias is specified by + a ID value that is relative to the SID of the domain. The operations + that will be performed on the Alias must be declared at this time. + + This call returns a handle to the newly opened Alias that may be used + for successive operations on the Alias. This handle may be closed + with the SamCloseHandle API. + + +Parameters: + + DomainHandle - A domain handle returned from a previous call to + SamOpenDomain. + + DesiredAccess - Is an access mask indicating which access types are + desired to the alias. + + AliasId - Specifies the relative ID value of the Alias to be opened. + + AliasHandle - Receives a handle referencing the newly opened Alias. + This handle will be required in successive calls to operate on + the Alias. + +Return Values: + + STATUS_SUCCESS - The Alias was successfully opened. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate access + to complete the operation. + + STATUS_NO_SUCH_ALIAS - The specified Alias does not exist. + + STATUS_INVALID_HANDLE - The domain handle passed is invalid. + + +--*/ + +{ + NTSTATUS NtStatus; + + SampOutputDebugString("SamOpenAlias"); + + + // + // Call the server ... + // + + + RpcTryExcept{ + + (*AliasHandle) = 0; + + NtStatus = + SamrOpenAlias( + (SAMPR_HANDLE)DomainHandle, + DesiredAccess, + AliasId, + (SAMPR_HANDLE *)AliasHandle + ); + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); +} + + + +NTSTATUS +SamQueryInformationAlias( + IN SAM_HANDLE AliasHandle, + IN ALIAS_INFORMATION_CLASS AliasInformationClass, + OUT PVOID * Buffer +) + +/*++ + +Routine Description: + + This API retrieves information on the alias specified. + + +Parameters: + + AliasHandle - The handle of an opened alias to operate on. + + AliasInformationClass - Class of information to retrieve. The + accesses required for each class is shown below: + + Info Level Required Access Type + ---------------------- ---------------------- + AliasGeneralInformation ALIAS_READ_INFORMATION + AliasNameInformation ALIAS_READ_INFORMATION + AliasAdminInformation ALIAS_READ_INFORMATION + + Buffer - Receives a pointer to a buffer containing the requested + information. When this information is no longer needed, this + buffer and any memory pointed to through this buffer must be + freed using SamFreeMemory(). + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_INVALID_INFO_CLASS - The class provided was invalid. + + +--*/ +{ + + NTSTATUS NtStatus; + + SampOutputDebugString("SamQueryInformationAlias"); + + + // + // Call the server ... + // + + + (*Buffer) = NULL; + + RpcTryExcept{ + + NtStatus = + SamrQueryInformationAlias( + (SAMPR_HANDLE)AliasHandle, + AliasInformationClass, + (PSAMPR_ALIAS_INFO_BUFFER *)Buffer + ); + + + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); +} + + + +NTSTATUS +SamSetInformationAlias( + IN SAM_HANDLE AliasHandle, + IN ALIAS_INFORMATION_CLASS AliasInformationClass, + IN PVOID Buffer +) +/*++ + + +Routine Description: + + This API allows the caller to modify alias information. + + +Parameters: + + AliasHandle - The handle of an opened alias to operate on. + + AliasInformationClass - Class of information to retrieve. The + accesses required for each class is shown below: + + Info Level Required Access Type + ------------------------ ------------------------- + + AliasGeneralInformation (can't write) + + AliasNameInformation ALIAS_WRITE_ACCOUNT + AliasAdminInformation ALIAS_WRITE_ACCOUNT + + Buffer - Buffer where information retrieved is placed. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_INFO_CLASS - The class provided was invalid. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_NO_SUCH_ALIAS - The alias specified is unknown. + + STATUS_SPECIAL_ALIAS - The alias specified is a special alias and + cannot be operated on in the requested fashion. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled for this + operation + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. + + +--*/ +{ + + NTSTATUS NtStatus; + + SampOutputDebugString("SamSetInformationAlias"); + + + // + // Call the server ... + // + + RpcTryExcept{ + + NtStatus = + SamrSetInformationAlias( + (SAMPR_HANDLE)AliasHandle, + AliasInformationClass, + Buffer + ); + + + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); +} + + + +NTSTATUS +SamDeleteAlias( + IN SAM_HANDLE AliasHandle +) + +/*++ + +Routine Description: + + This API deletes an Alias from the account database. The Alias does + not have to be empty. + + Note that following this call, the AliasHandle is no longer valid. + + +Parameters: + + AliasHandle - The handle of an opened Alias to operate on. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled for this + operation + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. + + +--*/ +{ + NTSTATUS NtStatus; + SAMPR_HANDLE LocalHandle; + + SampOutputDebugString("SamDeleteAlias"); + + LocalHandle = (SAMPR_HANDLE)AliasHandle; + + if (LocalHandle == 0) { + return(STATUS_INVALID_HANDLE); + } + + + // + // Call the server ... + // + + RpcTryExcept{ + + NtStatus = SamrDeleteAlias( &LocalHandle ); + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); + +} + + + +NTSTATUS +SamAddMemberToAlias( + IN SAM_HANDLE AliasHandle, + IN PSID MemberId + ) + +/*++ + +Routine Description: + + This API adds a member to an Alias. + + +Parameters: + + AliasHandle - The handle of an opened Alias object to operate on. + The handle must be open for ALIAS_ADD_MEMBER. + + MemberId - The SID of the member to add. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate access + to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_MEMBER_IN_ALIAS - The member already belongs to the Alias. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the correct + state (disabled or enabled) to perform the requested operation. + The domain server must be enabled for this operation. + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the incorrect + role (primary or backup) to perform the requested operation. + + +--*/ + +{ + NTSTATUS NtStatus; + + SampOutputDebugString("SamAddMemberToAlias"); + + // + // Call the server ... + // + + RpcTryExcept{ + + NtStatus = + SamrAddMemberToAlias( + (SAMPR_HANDLE)AliasHandle, + MemberId + ); + + + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); + +} + + + + +NTSTATUS +SamRemoveMemberFromAlias( + IN SAM_HANDLE AliasHandle, + IN PSID MemberId + ) + +/*++ + +Routine Description: + + This API removes a member from an Alias. + + +Parameters: + + AliasHandle - The handle of an opened Alias object to operate on. + The handle must be open for ALIAS_REMOVE_MEMBER. + + MemberId - The SID of the member to remove. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate access + to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_SPECIAL_ALIAS - The group specified is a special alias and + cannot be operated on in the requested fashion. + + STATUS_MEMBER_NOT_IN_ALIAS - The specified member does not belong to + the Alias. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the correct + state (disabled or enabled) to perform the requested operation. + The domain server must be enabled for this operation. + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the incorrect + role (primary or backup) to perform the requested operation. + + +--*/ + +{ + NTSTATUS NtStatus; + + SampOutputDebugString("SamRemoveMemberFromAlias"); + + + // + // Call the server ... + // + + RpcTryExcept{ + + NtStatus = + SamrRemoveMemberFromAlias( + (SAMPR_HANDLE)AliasHandle, + MemberId + ); + + + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); + +} + + + + +NTSTATUS +SamRemoveMemberFromForeignDomain( + IN SAM_HANDLE DomainHandle, + IN PSID MemberId + ) + +/*++ + +Routine Description: + + This API removes a member from an all Aliases in the domain specified. + + +Parameters: + + DomainHandle - The handle of an opened domain to operate in. All + aliases in the domain that the member is a part of must be + accessible by the caller with ALIAS_REMOVE_MEMBER. + + MemberId - The SID of the member to remove. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate access + to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the correct + state (disabled or enabled) to perform the requested operation. + The domain server must be enabled for this operation. + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the incorrect + role (primary or backup) to perform the requested operation. + + +--*/ + +{ + NTSTATUS NtStatus; + + SampOutputDebugString("SamRemoveMemberFromForeignDomain"); + + + // + // Call the server ... + // + + RpcTryExcept{ + + NtStatus = + SamrRemoveMemberFromForeignDomain( + (SAMPR_HANDLE)DomainHandle, + MemberId + ); + + + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); + +} + + + + +NTSTATUS +SamGetMembersInAlias( + IN SAM_HANDLE AliasHandle, + OUT PSID **MemberIds, + OUT PULONG MemberCount + ) + +/*++ + +Routine Description: + + This API lists all members in an Alias. This API requires + ALIAS_LIST_MEMBERS access to the Alias. + + +Parameters: + + AliasHandle - The handle of an opened Alias to operate on. + + MemberIds - Receives a pointer to a buffer containing an array of + pointers to SIDs. These SIDs are the SIDs of the members of the + Alias. When this information is no longer needed, this buffer + must be freed using SamFreeMemory(). + + MemberCount - number of members in the Alias (and, thus, the number + of relative IDs returned). + +Return Values: + + STATUS_SUCCESS - The Service completed successfully, and there are + no additional entries. + + STATUS_ACCESS_DENIED - Caller does not have privilege required to + request that data. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + +--*/ + +{ + NTSTATUS NtStatus; + SAMPR_PSID_ARRAY MembersBuffer; + + SampOutputDebugString("SamGetMembersInAlias"); + + // + // Validate parameters + // + + if ( !ARGUMENT_PRESENT(MemberIds) ) { + return(STATUS_INVALID_PARAMETER_2); + } + if ( !ARGUMENT_PRESENT(MemberCount) ) { + return(STATUS_INVALID_PARAMETER_3); + } + + + RpcTryExcept{ + + // + // Prepare for failure + // + + *MemberIds = NULL; + *MemberCount = 0; + + // + // Call the server ... + // + + MembersBuffer.Sids = NULL; + + NtStatus = SamrGetMembersInAlias( + (SAMPR_HANDLE)AliasHandle, + &MembersBuffer + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Return the member list + // + + *MemberCount = MembersBuffer.Count; + *MemberIds = (PSID *)(MembersBuffer.Sids); + + } + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + + + return(SampMapCompletionStatus(NtStatus)); + +} + + + +NTSTATUS +SamAddMultipleMembersToAlias( + IN SAM_HANDLE AliasHandle, + IN PSID *MemberIds, + IN ULONG MemberCount + ) + +/*++ + +Routine Description: + + This API adds the SIDs listed in MemberIds to the specified Alias. + + +Parameters: + + AliasHandle - The handle of an opened Alias to operate on. + + MemberIds - Points to an array of SID pointers containing MemberCount + entries. + + MemberCount - number of members in the array. + + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. All of the + listed members are now members of the alias. However, some of + the members may already have been members of the alias (this is + NOT an error or warning condition). + + STATUS_ACCESS_DENIED - Caller does not have the object open for + the required access. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_INVALID_MEMBER - The member has the wrong account type. + + STATUS_INVALID_SID - The member sid is corrupted. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled for this + operation + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. +--*/ + +{ + NTSTATUS NtStatus; + SAMPR_PSID_ARRAY MembersBuffer; + + SampOutputDebugString("SamAddMultipleMembersToAlias"); + + // + // Validate parameters + // + + if ( !ARGUMENT_PRESENT(MemberIds) ) { + return(STATUS_INVALID_PARAMETER_2); + } + + + RpcTryExcept{ + + // + // Call the server ... + // + + MembersBuffer.Count = MemberCount; + MembersBuffer.Sids = (PSAMPR_SID_INFORMATION)MemberIds; + + NtStatus = SamrAddMultipleMembersToAlias( + (SAMPR_HANDLE)AliasHandle, + &MembersBuffer + ); + + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + + return(SampMapCompletionStatus(NtStatus)); + +} + + + +NTSTATUS +SamRemoveMultipleMembersFromAlias( + IN SAM_HANDLE AliasHandle, + IN PSID *MemberIds, + IN ULONG MemberCount + ) + +/*++ + +Routine Description: + + This API Removes the SIDs listed in MemberIds from the specified Alias. + + +Parameters: + + AliasHandle - The handle of an opened Alias to operate on. + + MemberIds - Points to an array of SID pointers containing MemberCount + entries. + + MemberCount - number of members in the array. + + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. All of the + listed members are now NOT members of the alias. However, some of + the members may already have not been members of the alias (this + is NOT an error or warning condition). + + STATUS_ACCESS_DENIED - Caller does not have the object open for + the required access. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + +--*/ + +{ + NTSTATUS NtStatus; + SAMPR_PSID_ARRAY MembersBuffer; + + SampOutputDebugString("SamRemoveMultipleMembersFromAlias"); + + // + // Validate parameters + // + + if ( !ARGUMENT_PRESENT(MemberIds) ) { + return(STATUS_INVALID_PARAMETER_2); + } + + + RpcTryExcept{ + + // + // Call the server ... + // + + MembersBuffer.Count = MemberCount; + MembersBuffer.Sids = (PSAMPR_SID_INFORMATION)MemberIds; + + NtStatus = SamrRemoveMultipleMembersFromAlias( + (SAMPR_HANDLE)AliasHandle, + &MembersBuffer + ); + + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + + return(SampMapCompletionStatus(NtStatus)); + +} + + + +NTSTATUS +SamOpenUser( + IN SAM_HANDLE DomainHandle, + IN ACCESS_MASK DesiredAccess, + IN ULONG UserId, + OUT PSAM_HANDLE UserHandle + ) +/*++ + +Routine Description: + + This API opens an existing user in the account database. The user + is specified by SID value. The operations that will be performed on + the user must be declared at this time. + + This call returns a handle to the newly opened user that may be used + for successive operations on the user. This handle may be closed + with the SamCloseHandle API. + + +Parameters: + + DomainHandle - A domain handle returned from a previous call to + SamOpenDomain. + + DesiredAccess - Is an access mask indicating which access types + are desired to the user. These access types are reconciled + with the Discretionary Access Control list of the user to + determine whether the accesses will be granted or denied. + + UserId - Specifies the relative ID value of the user account to + be opened. + + UserHandle - Receives a handle referencing the newly opened User. + This handle will be required in successive calls to operate + on the user. + +Return Values: + + STATUS_SUCCESS - The group was successfully opened. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_NO_SUCH_USER - The specified user does not exist. + + STATUS_INVALID_HANDLE - The domain handle passed is invalid. + + + +--*/ +{ + + NTSTATUS NtStatus; + + SampOutputDebugString("SamOpenUser"); + + + // + // Call the server ... + // + + + RpcTryExcept{ + + (*UserHandle) = 0; + + NtStatus = + SamrOpenUser( + (SAMPR_HANDLE)DomainHandle, + DesiredAccess, + UserId, + (SAMPR_HANDLE *)UserHandle + ); + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); + +} + + + +NTSTATUS +SamDeleteUser( + IN SAM_HANDLE UserHandle +) + +/*++ + +Routine Description: + + This API deletes a user from the account database. If the account + being deleted is the last account in the database in the ADMIN + group, then STATUS_LAST_ADMIN is returned, and the Delete fails. Note + that this API required DOMAIN_DELETE_USER access. + + Note that following this call, the UserHandle is no longer valid. + + +Parameters: + + UserHandle - The handle of an opened user to operate on. The handle + must be opened for DELETE access. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_LAST_ADMIN - Cannot delete the last administrator. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled for this + operation + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. + + +--*/ +{ + NTSTATUS NtStatus; + SAMPR_HANDLE LocalHandle; + + SampOutputDebugString("SamDeleteUser"); + + LocalHandle = (SAMPR_HANDLE)UserHandle; + + if (LocalHandle == 0) { + return(STATUS_INVALID_HANDLE); + } + + + // + // Call the server ... + // + + RpcTryExcept{ + + NtStatus = SamrDeleteUser( &LocalHandle ); + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); + +} + + + +NTSTATUS +SamQueryInformationUser( + IN SAM_HANDLE UserHandle, + IN USER_INFORMATION_CLASS UserInformationClass, + OUT PVOID * Buffer +) + +/*++ + + +Routine Description: + + This API looks up some level of information about a particular user. + + +Parameters: + + UserHandle - The handle of an opened user to operate on. + + UserInformationClass - Class of information desired about this + user. The accesses required for each class is shown below: + + Info Level Required Access Type + ---------------------- -------------------------- + + UserGeneralInformation USER_READ_GENERAL + UserPreferencesInformation USER_READ_PREFERENCES + UserLogonInformation USER_READ_GENERAL and + USER_READ_PREFERENCES and + USER_READ_LOGON + + UserLogonHoursInformation USER_READ_LOGON + + UserAccountInformation USER_READ_GENERAL and + USER_READ_PREFERENCES and + USER_READ_LOGON and + USER_READ_ACCOUNT + + UserParametersInformation USER_READ_ACCOUNT + + UserNameInformation USER_READ_GENERAL + UserAccountNameInformation USER_READ_GENERAL + UserFullNameInformation USER_READ_GENERAL + UserPrimaryGroupInformation USER_READ_GENERAL + UserHomeInformation USER_READ_LOGON + UserScriptInformation USER_READ_LOGON + UserProfileInformation USER_READ_LOGON + UserAdminCommentInformation USER_READ_GENERAL + UserWorkStationsInformation USER_READ_LOGON + + UserSetPasswordInformation (Can't query) + + UserControlInformation USER_READ_ACCOUNT + UserExpiresInformation USER_READ_ACCOUNT + + UserInternal1Information (trusted client use only) + UserInternal2Information (trusted client use only) + + UserAllInformation Will return fields that user + has access to. + + Buffer - Receives a pointer to a buffer containing the requested + information. When this information is no longer needed, this + buffer must be freed using SamFreeMemory(). + + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_INVALID_INFO_CLASS - The class provided was invalid. + + +--*/ +{ + NTSTATUS NtStatus; + + SampOutputDebugString("SamQueryInformationUser"); + + + // + // Call the server ... + // + + + (*Buffer) = NULL; + + RpcTryExcept{ + + NtStatus = + SamrQueryInformationUser( + (SAMPR_HANDLE)UserHandle, + UserInformationClass, + (PSAMPR_USER_INFO_BUFFER *)Buffer + ); + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); + +} + + + +NTSTATUS +SampOwfPassword( + IN SAM_HANDLE UserHandle, + IN PUNICODE_STRING UnicodePassword, + IN BOOLEAN IgnorePasswordRestrictions, + OUT PBOOLEAN NtPasswordPresent, + OUT PNT_OWF_PASSWORD NtOwfPassword, + OUT PBOOLEAN LmPasswordPresent, + OUT PLM_OWF_PASSWORD LmOwfPassword +) + +/*++ + +Routine Description: + + This routine takes a cleartext unicode NT password from the user, + makes sure it meets our high standards for password quality, + converts it to an LM password if possible, and runs both passwords + through a one-way function (OWF). + +Parameters: + + UserHandle - The handle of an opened user to operate on. + + UnicodePassword - the cleartext unicode NT password. + + IgnorePasswordRestrictions - When TRUE, indicates that the password + should be accepted as legitimate regardless of what the domain's + password restrictions indicate (e.g., can be less than + required password length). This is expected to be used when + setting up a new machine account. + + NtPasswordPresent - receives a boolean that says whether the NT + password is present or not. + + NtOwfPassword - receives the OWF'd version of the NT password. + + LmPasswordPresent - receives a boolean that says whether the LM + password is present or not. + + LmOwfPassword - receives the OWF'd version of the LM password. + + +Return Values: + + STATUS_SUCCESS - the service has completed. The booleans say which + of the OWFs are valid. + + Errors are returned by SampCheckPasswordRestrictions(), + RtlCalculateNtOwfPassword(), SampCalculateLmPassword(), and + RtlCalculateLmOwfPassword(). + +--*/ +{ + NTSTATUS NtStatus; + PCHAR LmPasswordBuffer; + BOOLEAN UseOwfPasswords; + + // + // We ignore the UseOwfPasswords flag since we already are. + // + + if (IgnorePasswordRestrictions) { + NtStatus = STATUS_SUCCESS; + } else { + NtStatus = SampCheckPasswordRestrictions( + UserHandle, + UnicodePassword, + &UseOwfPasswords + ); + } + + // + // Compute the NT One-Way-Function of the password + // + + if ( NT_SUCCESS( NtStatus ) ) { + + *NtPasswordPresent = TRUE; + + NtStatus = RtlCalculateNtOwfPassword( + UnicodePassword, + NtOwfPassword + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // Calculate the LM version of the password + // + + NtStatus = SampCalculateLmPassword( + UnicodePassword, + &LmPasswordBuffer); + + if (NT_SUCCESS(NtStatus)) { + + // + // Compute the One-Way-Function of the LM password + // + + *LmPasswordPresent = TRUE; + + NtStatus = RtlCalculateLmOwfPassword( + LmPasswordBuffer, + LmOwfPassword); + + // + // We're finished with the LM password + // + + MIDL_user_free(LmPasswordBuffer); + } + } + } + + return( NtStatus ); +} + + +NTSTATUS +SampRandomFill( + IN ULONG BufferSize, + IN OUT PUCHAR Buffer +) +/*++ + +Routine Description: + + This routine fills a buffer with random data. + +Parameters: + + BufferSize - Length of the input buffer, in bytes. + + Buffer - Input buffer to be filled with random data. + +Return Values: + + Errors from NtQuerySystemTime() + + +--*/ +{ + ULONG Index; + LARGE_INTEGER Time; + ULONG Seed; + NTSTATUS NtStatus; + + + NtStatus = NtQuerySystemTime(&Time); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + Seed = Time.LowPart ^ Time.HighPart; + + for (Index = 0 ; Index < BufferSize ; Index++ ) + { + *Buffer++ = (UCHAR) (RtlRandom(&Seed) % 256); + } + return(STATUS_SUCCESS); + +} + + + +NTSTATUS +SampEncryptClearPassword( + IN SAM_HANDLE UserHandle, + IN PUNICODE_STRING UnicodePassword, + OUT PSAMPR_ENCRYPTED_USER_PASSWORD EncryptedUserPassword +) + +/*++ + +Routine Description: + + This routine takes a cleartext unicode NT password from the user, + and encrypts it with the session key. + +Parameters: + + UserHandle - SAM_HANDLE used to acquiring a session key. + + UnicodePassword - the cleartext unicode NT password. + + EncryptedUserPassword - receives the encrypted cleartext password. + +Return Values: + + STATUS_SUCCESS - the service has completed. The booleans say which + of the OWFs are valid. + + +--*/ +{ + NTSTATUS NtStatus; + USER_SESSION_KEY UserSessionKey; + struct RC4_KEYSTRUCT Rc4Key; + PSAMPR_USER_PASSWORD UserPassword = (PSAMPR_USER_PASSWORD) EncryptedUserPassword; + + if (UnicodePassword->Length > SAM_MAX_PASSWORD_LENGTH * sizeof(WCHAR)) { + return(STATUS_PASSWORD_RESTRICTION); + } + + NtStatus = RtlGetUserSessionKeyClient( + (RPC_BINDING_HANDLE)UserHandle, + &UserSessionKey + ); + + // + // Convert the session key into an RC4 key + // + + if (NT_SUCCESS(NtStatus)) { + + rc4_key( + &Rc4Key, + sizeof(USER_SESSION_KEY), + (PUCHAR) &UserSessionKey + ); + + RtlCopyMemory( + ((PCHAR) UserPassword->Buffer) + + (SAM_MAX_PASSWORD_LENGTH * sizeof(WCHAR)) - + UnicodePassword->Length, + UnicodePassword->Buffer, + UnicodePassword->Length + ); + UserPassword->Length = UnicodePassword->Length; + + NtStatus = SampRandomFill( + (SAM_MAX_PASSWORD_LENGTH * sizeof(WCHAR)) - + UnicodePassword->Length, + (PUCHAR) UserPassword->Buffer + ); + + if (NT_SUCCESS(NtStatus)) { + rc4( + &Rc4Key, + sizeof(SAMPR_ENCRYPTED_USER_PASSWORD), + (PUCHAR) UserPassword + ); + + } + + + } + + + return( NtStatus ); +} + + + + +NTSTATUS +SampEncryptOwfs( + IN SAM_HANDLE UserHandle, + IN BOOLEAN NtPasswordPresent, + IN PNT_OWF_PASSWORD NtOwfPassword, + OUT PENCRYPTED_NT_OWF_PASSWORD EncryptedNtOwfPassword, + IN BOOLEAN LmPasswordPresent, + IN PLM_OWF_PASSWORD LmOwfPassword, + OUT PENCRYPTED_LM_OWF_PASSWORD EncryptedLmOwfPassword +) + +/*++ + +Routine Description: + + This routine takes NT and LM passwords that have already been OWF'd, + and encrypts them so that they can be safely sent to the server. + + +Parameters: + + UserHandle - The handle of an opened user to operate on. + + NtPasswordPresent - indicates whether NtOwfPassword is valid or not. + + NtOwfPassword - an OWF'd NT password, if NtPasswordPresent is true. + + EncryptedNtOwfPassword - an encrypted version of the OWF'd NT password + that can be safely sent to the server. + + LmPasswordPresent - indicates whether LmOwfPassword is valid or not. + + LmOwfPassword - an OWF'd LM password, if LmPasswordPresent is true. + + EncryptedLmOwfPassword - an encrypted version of the OWF'd LM password + that can be safely sent to the server. + +Return Values: + + STATUS_SUCCESS - the passwords were encrypted and may be sent to the + server. + + Errors may be returned by RtlGetUserSessionKeyClient(), + RtlEncryptNtOwfPwdWithUserKey(), and RtlEncryptLmOwfPwdWithUserKey(). + +--*/ +{ + NTSTATUS NtStatus; + USER_SESSION_KEY UserSessionKey; + + NtStatus = RtlGetUserSessionKeyClient( + (RPC_BINDING_HANDLE)UserHandle, + &UserSessionKey + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + if (NtPasswordPresent) { + + // + // Encrypt the Nt OWF Password with the user session key + // and store it the buffer to send + // + + NtStatus = RtlEncryptNtOwfPwdWithUserKey( + NtOwfPassword, + &UserSessionKey, + EncryptedNtOwfPassword + ); + } + + + if ( NT_SUCCESS( NtStatus ) ) { + + if (LmPasswordPresent) { + + // + // Encrypt the Lm OWF Password with the user session key + // and store it the buffer to send + // + + NtStatus = RtlEncryptLmOwfPwdWithUserKey( + LmOwfPassword, + &UserSessionKey, + EncryptedLmOwfPassword + ); + } + } + } + + return( NtStatus ); +} + + + +NTSTATUS +SamSetInformationUser( + IN SAM_HANDLE UserHandle, + IN USER_INFORMATION_CLASS UserInformationClass, + IN PVOID Buffer +) + +/*++ + + +Routine Description: + + This API modifies information in a user record. The data modified + is determined by the UserInformationClass parameter. To change + information here requires access to the user object defined above. + Each structure has both a read and write access type associated with + it. In general, a user may call GetInformation with class + UserLogonInformation, but may only call SetInformation with class + UserPreferencesInformation. Access type USER_WRITE_ACCOUNT allows + changes to be made to all fields. + + NOTE: If the password is set to a new password then the password- + set timestamp is reset as well. + + +Parameters: + + UserHandle - The handle of an opened user to operate on. + + UserInformationClass - Class of information provided. The + accesses required for each class is shown below: + + Info Level Required Access Type + ----------------------- ------------------------ + UserGeneralInformation (Can't set) + + UserPreferencesInformation USER_WRITE_PREFERENCES + + UserParametersInformation USER_WRITE_ACCOUNT + + UserLogonInformation (Can't set) + + UserLogonHoursInformation USER_WRITE_ACCOUNT + + UserAccountInformation (Can't set) + + UserNameInformation USER_WRITE_ACCOUNT + UserAccountNameInformation USER_WRITE_ACCOUNT + UserFullNameInformation USER_WRITE_ACCOUNT + UserPrimaryGroupInformation USER_WRITE_ACCOUNT + UserHomeInformation USER_WRITE_ACCOUNT + UserScriptInformation USER_WRITE_ACCOUNT + UserProfileInformation USER_WRITE_ACCOUNT + UserAdminCommentInformation USER_WRITE_ACCOUNT + UserWorkStationsInformation USER_WRITE_ACCOUNT + UserSetPasswordInformation USER_FORCE_PASSWORD_CHANGE (also see note below) + UserControlInformation USER_WRITE_ACCOUNT + UserExpiresInformation USER_WRITE_ACCOUNT + UserInternal1Information USER_FORCE_PASSWORD_CHANGE (also see note below) + UserInternal2Information (trusted client use only) + UserAllInformation Will set fields that user + specifies, if accesses are + held as described above. + + + NOTE: When setting a password (with either + UserSetPasswordInformation or UserInternal1Information), + you MUST open the user account via a DomainHandle that + was opened for DOMAIN_READ_PASSWORD_PARAMETERS. + + Buffer - Buffer containing a user info struct. + + + + + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_INVALID_INFO_CLASS - The class provided was invalid. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled for this + operation + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. + + +--*/ +{ + SAMPR_USER_INTERNAL1_INFORMATION Internal1RpcBuffer; + USER_INTERNAL1_INFORMATION Internal1Buffer; + SAMPR_USER_INTERNAL4_INFORMATION Internal4RpcBuffer; + SAMPR_USER_INTERNAL5_INFORMATION Internal5RpcBuffer; + PVOID BufferToPass; + PUSER_ALL_INFORMATION UserAll; + USER_ALL_INFORMATION LocalAll; + NTSTATUS NtStatus = STATUS_SUCCESS; + BOOLEAN IgnorePasswordRestrictions; + ULONG Pass = 0; + USER_INFORMATION_CLASS ClassToUse = UserInformationClass; + BOOLEAN SendOwfs = FALSE; + + SampOutputDebugString("SamSetInformationUser"); + + do + { + + RpcTryExcept{ + + // + // Normally just pass the info buffer through to rpc + // + + BufferToPass = Buffer; + + + // + // Deal with special cases + // + + switch (UserInformationClass) { + + + case UserPreferencesInformation: { + + // + // Field is unused, but make sure RPC doesn't choke on it. + // + + ((PUSER_PREFERENCES_INFORMATION)(Buffer))->Reserved1.Length = 0; + ((PUSER_PREFERENCES_INFORMATION)(Buffer))->Reserved1.MaximumLength = 0; + ((PUSER_PREFERENCES_INFORMATION)(Buffer))->Reserved1.Buffer = NULL; + + break; + } + + case UserSetPasswordInformation: + + if (Pass == 0) { + + // + // On the zeroth pass try sending a UserInternal5 structure. + // This is only available on 3.51 and above releases. + // + + // + // Check password restrictions. + // + + NtStatus = SampCheckPasswordRestrictions( + UserHandle, + &((PUSER_SET_PASSWORD_INFORMATION)(Buffer))->Password, + &SendOwfs + ); + + // + // If password restrictions told us we could send reversibly + // encrypted passwords, compute them. Otherwise drop through + // to the OWF case. + // + + if (!SendOwfs) { + + // + // Encrypt the cleatext password - we don't need to + // restrictions because that can be done on the server. + // + + NtStatus = SampEncryptClearPassword( + UserHandle, + &((PUSER_SET_PASSWORD_INFORMATION)(Buffer))->Password, + &Internal5RpcBuffer.UserPassword + ); + + if (!NT_SUCCESS(NtStatus)) { + break; + } + + Internal5RpcBuffer.PasswordExpired = + ((PUSER_SET_PASSWORD_INFORMATION)(Buffer))->PasswordExpired; + + + // + // Set the class and buffer to send over. + // + + ClassToUse = UserInternal5Information; + BufferToPass = &Internal5RpcBuffer; + break; + + } + + } else { + + // + // Set the pass counter to one since we aren't trying a new + // interface and don't want to retry. + // + + Pass = 1; + SendOwfs = TRUE; + } + + ASSERT(SendOwfs); + + // + // We're going to calculate the OWFs for the password and + // turn this into an INTERNAL1 set info request by dropping + // through to the INTERNAL1 code with Buffer pointing at our + // local INTERNAL1 buffer. First, make sure that the password + // meets our quality requirements. + // + + NtStatus = SampOwfPassword( + UserHandle, + &((PUSER_SET_PASSWORD_INFORMATION)(Buffer))->Password, + FALSE, // Don't ignore password restrictions + &Internal1Buffer.NtPasswordPresent, + &Internal1Buffer.NtOwfPassword, + &Internal1Buffer.LmPasswordPresent, + &Internal1Buffer.LmOwfPassword + ); + + if (!NT_SUCCESS(NtStatus)) { + break; + } + + + // + // Copy the PasswordExpired flag + // + + Internal1Buffer.PasswordExpired = + ((PUSER_SET_PASSWORD_INFORMATION)(Buffer))->PasswordExpired; + + + // + // We now have a USER_INTERNAL1_INFO buffer in Internal1Buffer. + // Point Buffer at Internal1buffer and drop through to the code + // that handles INTERNAL1 requests + + Buffer = &Internal1Buffer; + ClassToUse = UserInternal1Information; + + // + // drop through..... + // + + + case UserInternal1Information: + + + // + // We're going to pass a different data structure to rpc + // + + BufferToPass = &Internal1RpcBuffer; + + + // + // Copy the password present flags + // + + Internal1RpcBuffer.NtPasswordPresent = + ((PUSER_INTERNAL1_INFORMATION)Buffer)->NtPasswordPresent; + + Internal1RpcBuffer.LmPasswordPresent = + ((PUSER_INTERNAL1_INFORMATION)Buffer)->LmPasswordPresent; + + + // + // Copy the PasswordExpired flag + // + + Internal1RpcBuffer.PasswordExpired = + ((PUSER_INTERNAL1_INFORMATION)Buffer)->PasswordExpired; + + + // + // Encrypt the OWFs with the user session key before we send + // them over the Rpc link + // + + NtStatus = SampEncryptOwfs( + UserHandle, + Internal1RpcBuffer.NtPasswordPresent, + &((PUSER_INTERNAL1_INFORMATION)Buffer)->NtOwfPassword, + &Internal1RpcBuffer.EncryptedNtOwfPassword, + Internal1RpcBuffer.LmPasswordPresent, + &((PUSER_INTERNAL1_INFORMATION)Buffer)->LmOwfPassword, + &Internal1RpcBuffer.EncryptedLmOwfPassword + ); + + break; + + + + case UserAllInformation: + + UserAll = (PUSER_ALL_INFORMATION)Buffer; + + // + // If the caller is passing passwords we need to convert them + // into OWFs and encrypt them. + // + + if (UserAll->WhichFields & (USER_ALL_LMPASSWORDPRESENT | + USER_ALL_NTPASSWORDPRESENT) ) { + + // + // We'll need a private copy of the buffer which we can edit + // and then send over RPC. + // + + + + + if (UserAll->WhichFields & USER_ALL_OWFPASSWORD) { + + LocalAll = *UserAll; + BufferToPass = &LocalAll; + SendOwfs = TRUE; + + // + // The caller is passing OWFS directly + // Check they're valid and copy them into the + // Internal1Buffer in preparation for encryption. + // + + if (LocalAll.WhichFields & USER_ALL_NTPASSWORDPRESENT) { + + if (LocalAll.NtPasswordPresent) { + + if (LocalAll.NtPassword.Length != NT_OWF_PASSWORD_LENGTH) { + NtStatus = STATUS_INVALID_PARAMETER; + } else { + Internal1Buffer.NtOwfPassword = + *((PNT_OWF_PASSWORD)LocalAll.NtPassword.Buffer); + } + + } else { + LocalAll.NtPasswordPresent = FALSE; + } + } + + if (LocalAll.WhichFields & USER_ALL_LMPASSWORDPRESENT) { + + if (LocalAll.LmPasswordPresent) { + + if (LocalAll.LmPassword.Length != LM_OWF_PASSWORD_LENGTH) { + NtStatus = STATUS_INVALID_PARAMETER; + } else { + Internal1Buffer.LmOwfPassword = + *((PNT_OWF_PASSWORD)LocalAll.LmPassword.Buffer); + } + + } else { + LocalAll.LmPasswordPresent = FALSE; + } + } + + + // + // Always remove the OWF_PASSWORDS flag. This is used + // only on the client side to determine the mode + // of password input and will be rejected by the server + // + + LocalAll.WhichFields &= ~USER_ALL_OWFPASSWORD; + + + + } else { + + + + // + // The caller is passing text passwords. + // Check for validity and convert to OWFs. + // + + if (UserAll->WhichFields & USER_ALL_LMPASSWORDPRESENT) { + + // + // User clients are only allowed to put a unicode string + // in the NT password. We always calculate the LM password + // + + NtStatus = STATUS_INVALID_PARAMETER; + + } else { + + // + // The caller might be simultaneously setting + // the password and changing the account to be + // a machine or trust account. In this case, + // we don't validate the password (e.g., length). + // + + IgnorePasswordRestrictions = FALSE; + if (UserAll->WhichFields & + USER_ALL_USERACCOUNTCONTROL) { + if (UserAll->UserAccountControl & + (USER_WORKSTATION_TRUST_ACCOUNT | USER_SERVER_TRUST_ACCOUNT) + ) { + IgnorePasswordRestrictions = TRUE; + } + } + + + SendOwfs = TRUE; + if (Pass == 0) { + + // + // On the first pass, try sending the cleatext + // password. + // + + Internal4RpcBuffer.I1 = *(PSAMPR_USER_ALL_INFORMATION) + UserAll; + + BufferToPass = &Internal4RpcBuffer; + ClassToUse = UserInternal4Information; + SendOwfs = FALSE; + + // + // Check the password restrictions. We also + // want to get the information on whether + // we can send reversibly encrypted passwords. + // + + NtStatus = SampCheckPasswordRestrictions( + UserHandle, + &UserAll->NtPassword, + &SendOwfs + ); + + if (IgnorePasswordRestrictions) { + NtStatus = STATUS_SUCCESS; + } + + if (!SendOwfs) { + // + // Encrypt the clear password + // + + NtStatus = SampEncryptClearPassword( + UserHandle, + &UserAll->NtPassword, + &Internal4RpcBuffer.UserPassword + ); + if (!NT_SUCCESS(NtStatus)) { + break; + } + + // + // Zero the password NT password + // + + RtlZeroMemory( + &Internal4RpcBuffer.I1.NtOwfPassword, + sizeof(UNICODE_STRING) + ); + + } + } + + if (SendOwfs) { + + + // + // On the second pass, do the normal thing. + // + + LocalAll = *UserAll; + BufferToPass = &LocalAll; + SendOwfs = TRUE; + + ClassToUse = UserAllInformation; + if ( LocalAll.WhichFields & USER_ALL_NTPASSWORDPRESENT ) { + + // + // The user specified a password. We must validate + // it, convert it to LM, and calculate the OWFs + // + + LocalAll.WhichFields |= USER_ALL_LMPASSWORDPRESENT; + + + // + // Stick the OWFs in the Internal1Buffer - just + // until we use them in the SampEncryptOwfs(). + // + + NtStatus = SampOwfPassword( + UserHandle, + &LocalAll.NtPassword, + IgnorePasswordRestrictions, + &LocalAll.NtPasswordPresent, + &(Internal1Buffer.NtOwfPassword), + &LocalAll.LmPasswordPresent, + &(Internal1Buffer.LmOwfPassword) + ); + } + } + + } + } + + + + + // + // We now have one or more OWFs in Internal1 buffer. + // We got these either directly or we calculated them + // from the text strings. + // Encrypt these OWFs with the session key and + // store the result in Internal1RpcBuffer. + // + // Note the Password present flags are in LocalAll. + // (The ones in Internal1Buffer are not used.) + // + + if ( NT_SUCCESS( NtStatus ) && SendOwfs ) { + + // + // Make all LocalAll's password strings point to + // the buffers in Internal1RpcBuffer + // + + LocalAll.NtPassword.Length = + sizeof( ENCRYPTED_NT_OWF_PASSWORD ); + LocalAll.NtPassword.MaximumLength = + sizeof( ENCRYPTED_NT_OWF_PASSWORD ); + LocalAll.NtPassword.Buffer = (PWSTR) + &Internal1RpcBuffer.EncryptedNtOwfPassword; + + LocalAll.LmPassword.Length = + sizeof( ENCRYPTED_LM_OWF_PASSWORD ); + LocalAll.LmPassword.MaximumLength = + sizeof( ENCRYPTED_LM_OWF_PASSWORD ); + LocalAll.LmPassword.Buffer = (PWSTR) + &Internal1RpcBuffer.EncryptedLmOwfPassword; + + // + // Encrypt the Owfs + // + + NtStatus = SampEncryptOwfs( + UserHandle, + LocalAll.NtPasswordPresent, + &Internal1Buffer.NtOwfPassword, + &Internal1RpcBuffer.EncryptedNtOwfPassword, + LocalAll.LmPasswordPresent, + &Internal1Buffer.LmOwfPassword, + &Internal1RpcBuffer.EncryptedLmOwfPassword + ); + } + } + + break; + + default: + + break; + + } // switch + + + + + // + // Call the server ... + // + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // If we are trying one of the new info levels, use the new + // api. + // + + if ((ClassToUse == UserInternal4Information) || + (ClassToUse == UserInternal5Information)) { + + NtStatus = + SamrSetInformationUser2( + (SAMPR_HANDLE)UserHandle, + ClassToUse, + BufferToPass + ); + + } else { + NtStatus = + SamrSetInformationUser( + (SAMPR_HANDLE)UserHandle, + ClassToUse, + BufferToPass + ); + } + } + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + Pass++; + + // + // If this is the first pass and the status indicated that the + // server did not support the info class or the api + // and we were trying one of the new info levels, try again. + // + + } while ( (Pass < 2) && + ((NtStatus == RPC_NT_INVALID_TAG) || + (NtStatus == RPC_NT_UNKNOWN_IF) || + (NtStatus == RPC_NT_PROCNUM_OUT_OF_RANGE))); + + return(SampMapCompletionStatus(NtStatus)); + +} + + + + + +NTSTATUS +SamiLmChangePasswordUser( + IN SAM_HANDLE UserHandle, + IN PENCRYPTED_LM_OWF_PASSWORD LmOldEncryptedWithLmNew, + IN PENCRYPTED_LM_OWF_PASSWORD LmNewEncryptedWithLmOld +) + +/*++ + + +Routine Description: + + Changes the password of a user account. This routine is intended to be + called by down-level system clients who have only the cross-encrypted + LM passwords available to them. + Password will be set to NewPassword only if OldPassword matches the + current user password for this user and the NewPassword is not the + same as the domain password parameter PasswordHistoryLength + passwords. This call allows users to change their own password if + they have access USER_CHANGE_PASSWORD. Password update restrictions + apply. + + This api will fail unless UAS Compatibility is enabled for the domain. + + +Parameters: + + UserHandle - The handle of an opened user to operate on. + + LmOldEncryptedWithLmNew - the OWF of the old LM password encrypted using + the OWF of the new LM password as a key. + + LmNewEncryptedWithLmOld - the OWF of the new LM password encrypted using + the OWF of the old LM password as a key. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_PASSWORD_RESTRICTION - A restriction prevents the password + from being changed. This may be for a number of reasons, + including time restrictions on how often a password may be + changed or length restrictions on the provided password. + + This error might also be returned if the new password matched + a password in the recent history log for the account. + Security administrators indicate how many of the most + recently used passwords may not be re-used. These are kept + in the password recent history log. + + STATUS_WRONG_PASSWORD - The old password is incorrect. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled for this + operation + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. + + +--*/ +{ + NTSTATUS NtStatus; + + // + // Check parameter validity + // + + if (LmOldEncryptedWithLmNew == NULL) { + return(STATUS_INVALID_PARAMETER_1); + } + if (LmNewEncryptedWithLmOld == NULL) { + return(STATUS_INVALID_PARAMETER_2); + } + + // + // Call the server ... + // + + RpcTryExcept{ + + NtStatus = SamrChangePasswordUser( + (SAMPR_HANDLE)UserHandle, + + TRUE, // LmOldPresent + LmOldEncryptedWithLmNew, + LmNewEncryptedWithLmOld, + + FALSE, // NtPresent + NULL, // NtOldEncryptedWithNtNew + NULL, // NtNewEncryptedWithNtOld + + FALSE, // NtCrossEncryptionPresent + NULL, + + FALSE, // LmCrossEncryptionPresent + NULL + + ); + + // + // We should never get asked for cross-encrypted data + // since the server knows we don't have any NT data. + // + + ASSERT (NtStatus != STATUS_NT_CROSS_ENCRYPTION_REQUIRED); + ASSERT (NtStatus != STATUS_LM_CROSS_ENCRYPTION_REQUIRED); + + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); + +} + + + + + +NTSTATUS +SamiChangePasswordUser( + IN SAM_HANDLE UserHandle, + IN BOOLEAN LmOldPresent, + IN PLM_OWF_PASSWORD LmOldOwfPassword, + IN PLM_OWF_PASSWORD LmNewOwfPassword, + IN BOOLEAN NtPresent, + IN PNT_OWF_PASSWORD NtOldOwfPassword, + IN PNT_OWF_PASSWORD NtNewOwfPassword +) + +/*++ + + +Routine Description: + + Changes the password of a user account. This is the worker routine for + SamChangePasswordUser and can be called by OWF-aware clients. + Password will be set to NewPassword only if OldPassword matches the + current user password for this user and the NewPassword is not the + same as the domain password parameter PasswordHistoryLength + passwords. This call allows users to change their own password if + they have access USER_CHANGE_PASSWORD. Password update restrictions + apply. + + +Parameters: + + UserHandle - The handle of an opened user to operate on. + + LMOldPresent - TRUE if the LmOldOwfPassword is valid. This should only + be FALSE if the old password is too long to be represented + by a LM password. (Complex NT password). + Note the LMNewOwfPassword must always be valid. + If the new password is complex, the LMNewOwfPassword should + be the well-known LM OWF of a NULL password. + + LmOldOwfPassword - One-way-function of the current LM password for the user. + - Ignored if LmOldPresent == FALSE + + LmNewOwfPassword - One-way-function of the new LM password for the user. + + NtPresent - TRUE if the NT one-way-functions are valid. + - i.e. This will be FALSE if we're called from a down-level client. + + NtOldOwfPassword - One-way-function of the current NT password for the user. + + NtNewOwfPassword - One-way-function of the new NT password for the user. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_ILL_FORMED_PASSWORD - The new password is poorly formed, + e.g. contains characters that can't be entered from the + keyboard, etc. + + STATUS_PASSWORD_RESTRICTION - A restriction prevents the password + from being changed. This may be for a number of reasons, + including time restrictions on how often a password may be + changed or length restrictions on the provided password. + + This error might also be returned if the new password matched + a password in the recent history log for the account. + Security administrators indicate how many of the most + recently used passwords may not be re-used. These are kept + in the password recent history log. + + STATUS_WRONG_PASSWORD - OldPassword does not contain the user's + current password. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled for this + operation + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. + + STATUS_INVALID_PARAMETER_MIX - LmOldPresent or NtPresent or both + must be TRUE. + +--*/ +{ + NTSTATUS NtStatus; + ENCRYPTED_NT_OWF_PASSWORD NtNewEncryptedWithNtOld; + ENCRYPTED_NT_OWF_PASSWORD NtOldEncryptedWithNtNew; + ENCRYPTED_NT_OWF_PASSWORD NtNewEncryptedWithLmNew; + ENCRYPTED_LM_OWF_PASSWORD LmNewEncryptedWithLmOld; + ENCRYPTED_LM_OWF_PASSWORD LmOldEncryptedWithLmNew; + ENCRYPTED_LM_OWF_PASSWORD LmNewEncryptedWithNtNew; + + PENCRYPTED_NT_OWF_PASSWORD pNtNewEncryptedWithNtOld; + PENCRYPTED_NT_OWF_PASSWORD pNtOldEncryptedWithNtNew; + PENCRYPTED_LM_OWF_PASSWORD pLmNewEncryptedWithLmOld; + PENCRYPTED_LM_OWF_PASSWORD pLmOldEncryptedWithLmNew; + + // + // Check parameter validity + // + + if (!LmOldPresent && !NtPresent) { + return(STATUS_INVALID_PARAMETER_MIX); + } + + // + // Call the server ... + // + + RpcTryExcept{ + + // + // We're going to encrypt the oldLM with the newLM and vice-versa. + // We're going to encrypt the oldNT with the newNT and vice-versa. + // We're going to send these 4 encryptions and see if we're successful. + // + // If we get a return code of STATUS_LM_CROSS_ENCRYPTION_REQUIRED, + // we'll also encrypt the newLM with the newNT and send it all again. + // + // If we get a return code of STATUS_NT_CROSS_ENCRYPTION_REQUIRED, + // we'll also encrypt the newNT with the newLM and send it all again. + // + // We don't always send the cross-encryption otherwise we would be + // compromising security on pure NT systems with long passwords. + // + + // + // Do the LM Encryption + // + + if (!LmOldPresent) { + + pLmOldEncryptedWithLmNew = NULL; + pLmNewEncryptedWithLmOld = NULL; + + } else { + + pLmOldEncryptedWithLmNew = &LmOldEncryptedWithLmNew; + pLmNewEncryptedWithLmOld = &LmNewEncryptedWithLmOld; + + NtStatus = RtlEncryptLmOwfPwdWithLmOwfPwd( + LmOldOwfPassword, + LmNewOwfPassword, + &LmOldEncryptedWithLmNew); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = RtlEncryptLmOwfPwdWithLmOwfPwd( + LmNewOwfPassword, + LmOldOwfPassword, + &LmNewEncryptedWithLmOld); + } + } + + // + // Do the NT Encryption + // + + if (NT_SUCCESS(NtStatus)) { + + if (!NtPresent) { + + pNtOldEncryptedWithNtNew = NULL; + pNtNewEncryptedWithNtOld = NULL; + + } else { + + pNtOldEncryptedWithNtNew = &NtOldEncryptedWithNtNew; + pNtNewEncryptedWithNtOld = &NtNewEncryptedWithNtOld; + + NtStatus = RtlEncryptNtOwfPwdWithNtOwfPwd( + NtOldOwfPassword, + NtNewOwfPassword, + &NtOldEncryptedWithNtNew); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = RtlEncryptNtOwfPwdWithNtOwfPwd( + NtNewOwfPassword, + NtOldOwfPassword, + &NtNewEncryptedWithNtOld); + } + } + } + + + // + // Call the server (with no cross-encryption) + // + + if ( NT_SUCCESS( NtStatus ) ) { + + NtStatus = SamrChangePasswordUser( + (SAMPR_HANDLE)UserHandle, + + LmOldPresent, + pLmOldEncryptedWithLmNew, + pLmNewEncryptedWithLmOld, + + NtPresent, + pNtOldEncryptedWithNtNew, + pNtNewEncryptedWithNtOld, + + FALSE, // NtCrossEncryptionPresent + NULL, + + FALSE, // LmCrossEncryptionPresent + NULL + + ); + + if (NtStatus == STATUS_NT_CROSS_ENCRYPTION_REQUIRED) { + + // + // We should only get this if we have both LM and NT data + // (This is not obvious - it results from the server-side logic) + // + + ASSERT(NtPresent && LmOldPresent); + + // + // Compute the cross-encryption of the new Nt password + // + + ASSERT(LM_OWF_PASSWORD_LENGTH == NT_OWF_PASSWORD_LENGTH); + + NtStatus = RtlEncryptNtOwfPwdWithNtOwfPwd( + NtNewOwfPassword, + (PNT_OWF_PASSWORD)LmNewOwfPassword, + &NtNewEncryptedWithLmNew); + + + // + // Call the server (with NT cross-encryption) + // + + if ( NT_SUCCESS( NtStatus ) ) { + + NtStatus = SamrChangePasswordUser( + (SAMPR_HANDLE)UserHandle, + + LmOldPresent, + pLmOldEncryptedWithLmNew, + pLmNewEncryptedWithLmOld, + + NtPresent, + pNtOldEncryptedWithNtNew, + pNtNewEncryptedWithNtOld, + + TRUE, + &NtNewEncryptedWithLmNew, + + FALSE, + NULL + ); + } + + } else { + + if (NtStatus == STATUS_LM_CROSS_ENCRYPTION_REQUIRED) { + + // + // We should only get this if we have NT but no old LM data + // (This is not obvious - it results from the server-side logic) + // + + ASSERT(NtPresent && !LmOldPresent); + + // + // Compute the cross-encryption of the new Nt password + // + + ASSERT(LM_OWF_PASSWORD_LENGTH == NT_OWF_PASSWORD_LENGTH); + + NtStatus = RtlEncryptLmOwfPwdWithLmOwfPwd( + LmNewOwfPassword, + (PLM_OWF_PASSWORD)NtNewOwfPassword, + &LmNewEncryptedWithNtNew); + + + // + // Call the server (with LM cross-encryption) + // + + if ( NT_SUCCESS( NtStatus ) ) { + + NtStatus = SamrChangePasswordUser( + (SAMPR_HANDLE)UserHandle, + + LmOldPresent, + pLmOldEncryptedWithLmNew, + pLmNewEncryptedWithLmOld, + + NtPresent, + pNtOldEncryptedWithNtNew, + pNtNewEncryptedWithNtOld, + + FALSE, + NULL, + + TRUE, + &LmNewEncryptedWithNtNew + ); + } + } + } + + } + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); + +} + + + +NTSTATUS +SamChangePasswordUser( + IN SAM_HANDLE UserHandle, + IN PUNICODE_STRING OldNtPassword, + IN PUNICODE_STRING NewNtPassword +) + +/*++ + + +Routine Description: + + Password will be set to NewPassword only if OldPassword matches the + current user password for this user and the NewPassword is not the + same as the domain password parameter PasswordHistoryLength + passwords. This call allows users to change their own password if + they have access USER_CHANGE_PASSWORD. Password update restrictions + apply. + + +Parameters: + + UserHandle - The handle of an opened user to operate on. + + OldPassword - Current password for the user. + + NewPassword - Desired new password for the user. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_ILL_FORMED_PASSWORD - The new password is poorly formed, + e.g. contains characters that can't be entered from the + keyboard, etc. + + STATUS_PASSWORD_RESTRICTION - A restriction prevents the password + from being changed. This may be for a number of reasons, + including time restrictions on how often a password may be + changed or length restrictions on the provided password. + + This error might also be returned if the new password matched + a password in the recent history log for the account. + Security administrators indicate how many of the most + recently used passwords may not be re-used. These are kept + in the password recent history log. + + STATUS_WRONG_PASSWORD - OldPassword does not contain the user's + current password. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled for this + operation + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. + + +--*/ +{ + LM_OWF_PASSWORD NewLmOwfPassword, OldLmOwfPassword; + NT_OWF_PASSWORD NewNtOwfPassword, OldNtOwfPassword; + BOOLEAN LmOldPresent; + PCHAR LmPassword; + NTSTATUS NtStatus; + BOOLEAN UseOwfPasswords; + + // + // Call the server ... + // + + RpcTryExcept{ + + NtStatus = SampCheckPasswordRestrictions( + UserHandle, + NewNtPassword, + &UseOwfPasswords + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // Calculate the one-way-functions of the NT passwords + // + + NtStatus = RtlCalculateNtOwfPassword( + OldNtPassword, + &OldNtOwfPassword + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + NtStatus = RtlCalculateNtOwfPassword( + NewNtPassword, + &NewNtOwfPassword + ); + } + + + // + // Calculate the one-way-functions of the LM passwords + // + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // Calculate the LM version of the old password + // + + NtStatus = SampCalculateLmPassword( + OldNtPassword, + &LmPassword); + + if (NT_SUCCESS(NtStatus)) { + + if (NtStatus == STATUS_NULL_LM_PASSWORD) { + LmOldPresent = FALSE; + } else { + LmOldPresent = TRUE; + + // + // Compute the One-Way-Function of the old LM password + // + + NtStatus = RtlCalculateLmOwfPassword( + LmPassword, + &OldLmOwfPassword); + } + + // + // We're finished with the LM password + // + + MIDL_user_free(LmPassword); + } + + // + // Calculate the LM version of the new password + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampCalculateLmPassword( + NewNtPassword, + &LmPassword); + + if (NT_SUCCESS(NtStatus)) { + + // + // Compute the One-Way-Function of the new LM password + // + + NtStatus = RtlCalculateLmOwfPassword( + LmPassword, + &NewLmOwfPassword); + + // + // We're finished with the LM password + // + + MIDL_user_free(LmPassword); + } + } + } + + + // + // Call our worker routine with the one-way-functions + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SamiChangePasswordUser( + UserHandle, + LmOldPresent, + &OldLmOwfPassword, + &NewLmOwfPassword, + TRUE, // NT present + &OldNtOwfPassword, + &NewNtOwfPassword + ); + } + } + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); + +} + + + +NTSTATUS +SamGetGroupsForUser( + IN SAM_HANDLE UserHandle, + OUT PGROUP_MEMBERSHIP * Groups, + OUT PULONG MembershipCount +) + +/*++ + + +Routine Description: + + This service returns the list of groups that a user is a member of. + It returns a structure for each group that includes the relative ID + of the group, and the attributes of the group that are assigned to + the user. + + This service requires USER_LIST_GROUPS access to the user account + object. + + +Parameters: + + UserHandle - The handle of an opened user to operate on. + + Groups - Receives a pointer to a buffer containing an array of + GROUP_MEMBERSHIPs data structures. When this information is + no longer needed, this buffer must be freed using + SamFreeMemory(). + + MembershipCount - Receives the number of groups the user is a + member of, and, thus, the number elements returned in the + Groups array. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + +--*/ +{ + NTSTATUS NtStatus; + PSAMPR_GET_GROUPS_BUFFER GetGroupsBuffer; + + + SampOutputDebugString("SamGetGroupsForUser"); + + + // + // Call the server ... + // + + + GetGroupsBuffer = NULL; + + RpcTryExcept{ + + NtStatus = + SamrGetGroupsForUser( + (SAMPR_HANDLE)UserHandle, + &GetGroupsBuffer + ); + + if (NT_SUCCESS(NtStatus)) { + (*MembershipCount) = GetGroupsBuffer->MembershipCount; + (*Groups) = GetGroupsBuffer->Groups; + MIDL_user_free( GetGroupsBuffer ); + } else { + + // + // Deallocate any returned buffers on error + // + + if (GetGroupsBuffer != NULL) { + if (GetGroupsBuffer->Groups != NULL) { + MIDL_user_free(GetGroupsBuffer->Groups); + } + MIDL_user_free(GetGroupsBuffer); + } + } + + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + + } RpcEndExcept; + + return(SampMapCompletionStatus(NtStatus)); + +} + + + +NTSTATUS +SamTestPrivateFunctionsDomain( + IN SAMPR_HANDLE DomainHandle + ) + +/*++ + +Routine Description: + + This service is called to test functions that are normally only + accessible inside the security process. + + +Arguments: + + DomainHandle - Handle to a domain to be tested. + +Return Value: + + STATUS_SUCCESS - The tests completed successfully. + + Any errors are as propogated from the tests. + + +--*/ +{ +#ifdef SAM_SERVER_TESTS + return( SamrTestPrivateFunctionsDomain( DomainHandle ) ); +#else + return( STATUS_NOT_IMPLEMENTED ); + UNREFERENCED_PARAMETER(DomainHandle); +#endif +} + + + +NTSTATUS +SamTestPrivateFunctionsUser( + IN SAMPR_HANDLE UserHandle + ) + +/*++ + +Routine Description: + + This service is called to test functions that are normally only + accessible inside the security process. + + +Arguments: + + UserHandle - Handle to a user to be tested. + +Return Value: + + STATUS_SUCCESS - The tests completed successfully. + + Any errors are as propogated from the tests. + + +--*/ +{ +#ifdef SAM_SERVER_TESTS + return( SamrTestPrivateFunctionsUser( UserHandle ) ); +#else + return( STATUS_NOT_IMPLEMENTED ); + UNREFERENCED_PARAMETER(UserHandle); +#endif +} + + +/////////////////////////////////////////////////////////////////////////////// +// // +// private services // +// // +/////////////////////////////////////////////////////////////////////////////// + + +NTSTATUS +SampMapCompletionStatus( + IN NTSTATUS Status + ) + +/*++ + +Routine Description: + + This service maps completion status received back from an RPC call + into a completion status to be returned from SAM api. + + +Parameters: + + Status - Status value to be mapped. + +Return Values: + + The mapped SAM status value. + + +--*/ +{ + + if (Status == RPC_NT_INVALID_BINDING) { + Status = STATUS_INVALID_HANDLE; + } +// if (Status == RPC_ACCESS_DENIED) { +// Status = STATUS_ACCESS_DENIED; +// } + + + + return( Status ); + +} + + + +NTSTATUS +SampCalculateLmPassword( + IN PUNICODE_STRING NtPassword, + OUT PCHAR *LmPasswordBuffer + ) + +/*++ + +Routine Description: + + This service converts an NT password into a LM password. + +Parameters: + + NtPassword - The Nt password to be converted. + + LmPasswordBuffer - On successful return, points at the LM password + The buffer should be freed using MIDL_user_free + +Return Values: + + STATUS_SUCCESS - LMPassword contains the LM version of the password. + + STATUS_NULL_LM_PASSWORD - The password is too complex to be represented + by a LM password. The LM password returned is a NULL string. + + +--*/ +{ + +#define LM_BUFFER_LENGTH (LM20_PWLEN + 1) + + NTSTATUS NtStatus; + ANSI_STRING LmPassword; + + // + // Prepare for failure + // + + *LmPasswordBuffer = NULL; + + + // + // Compute the Ansi version to the Unicode password. + // + // The Ansi version of the Cleartext password is at most 14 bytes long, + // exists in a trailing zero filled 15 byte buffer, + // is uppercased. + // + + LmPassword.Buffer = MIDL_user_allocate(LM_BUFFER_LENGTH); + if (LmPassword.Buffer == NULL) { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + LmPassword.MaximumLength = LmPassword.Length = LM_BUFFER_LENGTH; + RtlZeroMemory( LmPassword.Buffer, LM_BUFFER_LENGTH ); + + NtStatus = RtlUpcaseUnicodeStringToOemString( &LmPassword, NtPassword, FALSE ); + + + if ( !NT_SUCCESS(NtStatus) ) { + + // + // The password is longer than the max LM password length + // + + NtStatus = STATUS_NULL_LM_PASSWORD; // Informational return code + RtlZeroMemory( LmPassword.Buffer, LM_BUFFER_LENGTH ); + + } + + + + + // + // Return a pointer to the allocated LM password + // + + if (NT_SUCCESS(NtStatus)) { + + *LmPasswordBuffer = LmPassword.Buffer; + + } else { + + MIDL_user_free(LmPassword.Buffer); + } + + return(NtStatus); +} + + + +NTSTATUS +SampCheckPasswordRestrictions( + IN SAMPR_HANDLE UserHandle, + IN PUNICODE_STRING NewNtPassword, + OUT PBOOLEAN UseOwfPasswords + ) + +/*++ + +Routine Description: + + This service is called to make sure that the password presented meets + our quality requirements. + + +Arguments: + + UserHandle - Handle to a user. + + NewNtPassword - Pointer to the UNICODE_STRING containing the new + password. + + UseOwfPasswords - Indicates that reversibly encrypted passwords should + not be sent over the network. + + +Return Value: + + STATUS_SUCCESS - The password is acceptable. + + STATUS_PASSWORD_RESTRICTION - The password is too short, or is not + complex enough, etc. + + STATUS_INVALID_RESOURCES - There was not enough memory to do the + password checking. + + +--*/ +{ + USER_DOMAIN_PASSWORD_INFORMATION DomainPasswordInformationBuffer; + NTSTATUS NtStatus; + PWORD CharInfoBuffer = NULL; + ULONG i; + + // + // If the new password is zero length the server side will do + // the necessary checking. + // + + if (NewNtPassword->Length == 0) { + return(STATUS_SUCCESS); + } + + *UseOwfPasswords = FALSE; + + + // + // Query information domain to get password length and + // complexity requirements. + // + + NtStatus = SamrGetUserDomainPasswordInformation( + UserHandle, + &DomainPasswordInformationBuffer + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + if ( (USHORT)( NewNtPassword->Length / sizeof(WCHAR) ) < DomainPasswordInformationBuffer.MinPasswordLength ) { + + NtStatus = STATUS_PASSWORD_RESTRICTION; + + } else { + + // + // Check whether policy allows us to send reversibly encrypted + // passwords. + // + + if ( DomainPasswordInformationBuffer.PasswordProperties & + DOMAIN_PASSWORD_NO_CLEAR_CHANGE ) { + *UseOwfPasswords = TRUE; + } + + // + // Check password complexity. + // + + if ( DomainPasswordInformationBuffer.PasswordProperties & DOMAIN_PASSWORD_COMPLEX ) { + + // + // Make sure that the password meets our requirements for + // complexity. If it's got an odd byte count, it's + // obviously not a hand-entered UNICODE string so we'll + // consider it complex by default. + // + + if ( !( NewNtPassword->Length & 1 ) ) { + + USHORT NumsInPassword = 0; + USHORT UppersInPassword = 0; + USHORT LowersInPassword = 0; + USHORT OthersInPassword = 0; + + CharInfoBuffer = MIDL_user_allocate( NewNtPassword->Length ); + + if ( CharInfoBuffer == NULL ) { + + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + + } else { + + if ( GetStringTypeW( + CT_CTYPE1, + NewNtPassword->Buffer, + NewNtPassword->Length / 2, + CharInfoBuffer ) ) { + + for ( i = 0; i < (ULONG)( NewNtPassword->Length / sizeof(WCHAR) ); i++ ) { + + if ( CharInfoBuffer[i] & C1_DIGIT ) { + + NumsInPassword = 1; + } + + if ( CharInfoBuffer[i] & C1_UPPER ) { + + UppersInPassword = 1; + } + + if ( CharInfoBuffer[i] & C1_LOWER ) { + + LowersInPassword = 1; + } + + if ( !( CharInfoBuffer[i] & ( C1_ALPHA | C1_DIGIT ) ) ) { + + // + // Having any "other" characters is + // sufficient to make the password + // complex. + // + + OthersInPassword = 2; + } + } + + if ( ( NumsInPassword + UppersInPassword + + LowersInPassword + OthersInPassword ) < 2 ) { + + // + // It didn't have at least two of the four + // types of characters, so it's not complex + // enough. + // + + NtStatus = STATUS_PASSWORD_RESTRICTION; + } + + } else { + + // + // GetStringTypeW failed; dunno why. Perhaps the + // password is binary. Consider it complex by + // default. + // + + NtStatus = STATUS_SUCCESS; + } + + MIDL_user_free( CharInfoBuffer ); + } + } + } + } + } + + return( NtStatus ); +} + + + +NTSTATUS +SamiEncryptPasswords( + IN PUNICODE_STRING OldPassword, + IN PUNICODE_STRING NewPassword, + OUT PSAMPR_ENCRYPTED_USER_PASSWORD NewEncryptedWithOldNt, + OUT PENCRYPTED_NT_OWF_PASSWORD OldNtOwfEncryptedWithNewNt, + OUT PBOOLEAN LmPresent, + OUT PSAMPR_ENCRYPTED_USER_PASSWORD NewEncryptedWithOldLm, + OUT PENCRYPTED_NT_OWF_PASSWORD OldLmOwfEncryptedWithNewNt +) +/*++ + +Routine Description: + + This routine takes old and new cleartext passwords, converts them to + LM passwords, generates OWF passwords, and produces reversibly + encrypted cleartext and OWF passwords. + +Arguments: + + OldPassword - The current cleartext password for the user. + + NewPassword - The new cleartext password for the user. + + NewEncryptedWithOldNt - The new password, in an SAMPR_USER_PASSWORD + structure, reversibly encrypted with the old NT OWF password. + + OldNtOwfEncryptedWithNewNt - The old NT OWF password reversibly + encrypted with the new NT OWF password. + + LmPresent - Indicates whether or not LM versions of the passwords could + be calculated. + + NewEncryptedWithOldLm - The new password, in an SAMPR_USER_PASSWORD + structure, reversibly encrypted with the old LM OWF password. + + OldLmOwfEncryptedWithNewNt - The old LM OWF password reversibly + encrypted with the new NT OWF password. + + +Return Value: + + Errors from RtlEncryptXXX functions + +--*/ +{ + PCHAR OldLmPassword = NULL; + PCHAR NewLmPassword = NULL; + LM_OWF_PASSWORD OldLmOwfPassword; + NT_OWF_PASSWORD OldNtOwfPassword; + NT_OWF_PASSWORD NewNtOwfPassword; + PSAMPR_USER_PASSWORD NewNt = (PSAMPR_USER_PASSWORD) NewEncryptedWithOldNt; + PSAMPR_USER_PASSWORD NewLm = (PSAMPR_USER_PASSWORD) NewEncryptedWithOldLm; + struct RC4_KEYSTRUCT Rc4Key; + NTSTATUS NtStatus; + BOOLEAN OldLmPresent = TRUE; + BOOLEAN NewLmPresent = TRUE; + + + // + // Initialization + // + + *LmPresent = TRUE; + + // + // Make sure the password isn't too long. + // + + if (NewPassword->Length > SAM_MAX_PASSWORD_LENGTH * sizeof(WCHAR)) { + return(STATUS_PASSWORD_RESTRICTION); + } + + // + // Calculate the LM passwords. This may fail because the passwords are + // too complex, but we can deal with that, so just remember what failed. + // + + NtStatus = SampCalculateLmPassword( + OldPassword, + &OldLmPassword + ); + + if (NtStatus != STATUS_SUCCESS) { + OldLmPresent = FALSE; + *LmPresent = FALSE; + + // + // If the error was that it couldn't calculate the password, that + // is o.k. + // + + if (NtStatus == STATUS_NULL_LM_PASSWORD) { + NtStatus = STATUS_SUCCESS; + } + + } + + + + // + // Calculate the LM OWF passwords + // + + if (NT_SUCCESS(NtStatus) && OldLmPresent) { + NtStatus = RtlCalculateLmOwfPassword( + OldLmPassword, + &OldLmOwfPassword + ); + } + + + // + // Calculate the NT OWF passwords + // + + if (NT_SUCCESS(NtStatus)) { + NtStatus = RtlCalculateNtOwfPassword( + OldPassword, + &OldNtOwfPassword + ); + } + + if (NT_SUCCESS(NtStatus)) { + NtStatus = RtlCalculateNtOwfPassword( + NewPassword, + &NewNtOwfPassword + ); + } + + // + // Calculate the encrypted old passwords + // + + if (NT_SUCCESS(NtStatus)) { + NtStatus = RtlEncryptNtOwfPwdWithNtOwfPwd( + &OldNtOwfPassword, + &NewNtOwfPassword, + OldNtOwfEncryptedWithNewNt + ); + } + + // + // Compute the encrypted old LM password. Always use the new NT OWF + // to encrypt it, since we may not have a new LM OWF password. + // + + + if (NT_SUCCESS(NtStatus) && OldLmPresent) { + ASSERT(LM_OWF_PASSWORD_LENGTH == NT_OWF_PASSWORD_LENGTH); + + NtStatus = RtlEncryptLmOwfPwdWithLmOwfPwd( + &OldLmOwfPassword, + (PLM_OWF_PASSWORD) &NewNtOwfPassword, + OldLmOwfEncryptedWithNewNt + ); + } + + // + // Calculate the encrypted new passwords + // + + if (NT_SUCCESS(NtStatus)) { + + ASSERT(sizeof(SAMPR_ENCRYPTED_USER_PASSWORD) == sizeof(SAMPR_USER_PASSWORD)); + + // + // Compute the encrypted new password with NT key. + // + + rc4_key( + &Rc4Key, + NT_OWF_PASSWORD_LENGTH, + (PUCHAR) &OldNtOwfPassword + ); + + RtlCopyMemory( + ((PUCHAR) NewNt->Buffer) + + SAM_MAX_PASSWORD_LENGTH * sizeof(WCHAR) - + NewPassword->Length, + NewPassword->Buffer, + NewPassword->Length + ); + + *(ULONG UNALIGNED *) &NewNt->Length = NewPassword->Length; + + // + // Fill the rest of the buffer with random numbers + // + + NtStatus = SampRandomFill( + (SAM_MAX_PASSWORD_LENGTH * sizeof(WCHAR)) - + NewPassword->Length, + (PUCHAR) NewNt->Buffer + ); + } + + if (NT_SUCCESS(NtStatus)) + { + rc4(&Rc4Key, + sizeof(SAMPR_USER_PASSWORD), + (PUCHAR) NewEncryptedWithOldNt + ); + + } + + // + // Compute the encrypted new password with LM key if it exists. + // + + + if (NT_SUCCESS(NtStatus) && OldLmPresent) { + + rc4_key( + &Rc4Key, + LM_OWF_PASSWORD_LENGTH, + (PUCHAR) &OldLmOwfPassword + ); + + RtlCopyMemory( + ((PUCHAR) NewLm->Buffer) + + (SAM_MAX_PASSWORD_LENGTH * sizeof(WCHAR)) - + NewPassword->Length, + NewPassword->Buffer, + NewPassword->Length + ); + + *(ULONG UNALIGNED *) &NewLm->Length = NewPassword->Length; + + NtStatus = SampRandomFill( + (SAM_MAX_PASSWORD_LENGTH * sizeof(WCHAR)) - + NewPassword->Length, + (PUCHAR) NewLm->Buffer + ); + + + } + + // + // Encrypt the password (or, if the old LM OWF password does not exist, + // zero it). + + if (NT_SUCCESS(NtStatus) && OldLmPresent) { + + rc4(&Rc4Key, + sizeof(SAMPR_USER_PASSWORD), + (PUCHAR) NewEncryptedWithOldLm + ); + + } else { + RtlZeroMemory( + NewLm, + sizeof(SAMPR_ENCRYPTED_USER_PASSWORD) + ); + } + + + + // + // Make sure to zero the passwords before freeing so we don't have + // passwords floating around in the page file. + // + + if (OldLmPassword != NULL) { + + RtlZeroMemory( + OldLmPassword, + lstrlenA(OldLmPassword) + ); + + MIDL_user_free(OldLmPassword); + } + + + return(NtStatus); + +} + + + + + +NTSTATUS +SampChangePasswordUser2( + IN PUNICODE_STRING ServerName, + IN PUNICODE_STRING UserName, + IN PUNICODE_STRING OldPassword, + IN PUNICODE_STRING NewPassword +) + +/*++ + + +Routine Description: + + Password will be set to NewPassword only if OldPassword matches the + current user password for this user and the NewPassword is not the + same as the domain password parameter PasswordHistoryLength + passwords. This call allows users to change their own password if + they have access USER_CHANGE_PASSWORD. Password update restrictions + apply. + + +Parameters: + + UserHandle - The handle of an opened user to operate on. + + OldPassword - Current password for the user. + + NewPassword - Desired new password for the user. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_ILL_FORMED_PASSWORD - The new password is poorly formed, + e.g. contains characters that can't be entered from the + keyboard, etc. + + STATUS_PASSWORD_RESTRICTION - A restriction prevents the password + from being changed. This may be for a number of reasons, + including time restrictions on how often a password may be + changed or length restrictions on the provided password. + + This error might also be returned if the new password matched + a password in the recent history log for the account. + Security administrators indicate how many of the most + recently used passwords may not be re-used. These are kept + in the password recent history log. + + STATUS_WRONG_PASSWORD - OldPassword does not contain the user's + current password. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled for this + operation + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. + + +--*/ +{ + NTSTATUS NtStatus; + SAM_HANDLE SamServerHandle = NULL; + SAM_HANDLE DomainHandle = NULL; + SAM_HANDLE UserHandle = NULL; + LSA_HANDLE PolicyHandle = NULL; + OBJECT_ATTRIBUTES ObjectAttributes; + SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService; + PPOLICY_ACCOUNT_DOMAIN_INFO AccountDomainInfo = NULL; + PULONG UserId = NULL; + PSID_NAME_USE NameUse = NULL; + + InitializeObjectAttributes( + &ObjectAttributes, + NULL, + 0, + NULL, + NULL + ); + + // + // The InitializeObjectAttributes call doesn't initialize the + // quality of serivce, so do that separately. + // + + SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); + SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation; + SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; + SecurityQualityOfService.EffectiveOnly = FALSE; + + ObjectAttributes.SecurityQualityOfService = &SecurityQualityOfService; + + + + NtStatus = LsaOpenPolicy( + ServerName, + &ObjectAttributes, + POLICY_VIEW_LOCAL_INFORMATION, + &PolicyHandle + ); + + if (!NT_SUCCESS(NtStatus)) { + goto Cleanup; + } + + NtStatus = LsaQueryInformationPolicy( + PolicyHandle, + PolicyAccountDomainInformation, + &AccountDomainInfo + ); + if (!NT_SUCCESS(NtStatus)) { + goto Cleanup; + } + + NtStatus = SamConnect( + ServerName, + &SamServerHandle, + SAM_SERVER_LOOKUP_DOMAIN, + &ObjectAttributes + ); + if (!NT_SUCCESS(NtStatus)) { + goto Cleanup; + } + + NtStatus = SamOpenDomain( + SamServerHandle, + GENERIC_EXECUTE, + AccountDomainInfo->DomainSid, + &DomainHandle + ); + if (!NT_SUCCESS(NtStatus)) { + goto Cleanup; + } + + NtStatus = SamLookupNamesInDomain( + DomainHandle, + 1, + UserName, + &UserId, + &NameUse + ); + + if (!NT_SUCCESS(NtStatus)) { + if (NtStatus == STATUS_NONE_MAPPED) { + NtStatus = STATUS_NO_SUCH_USER; + } + goto Cleanup; + } + + NtStatus = SamOpenUser( + DomainHandle, + USER_CHANGE_PASSWORD, + *UserId, + &UserHandle + ); + + if (!NT_SUCCESS(NtStatus)) { + goto Cleanup; + } + + NtStatus = SamChangePasswordUser( + UserHandle, + OldPassword, + NewPassword + ); +Cleanup: + if (UserHandle != NULL) { + SamCloseHandle(UserHandle); + } + if (DomainHandle != NULL) { + SamCloseHandle(DomainHandle); + } + if (SamServerHandle != NULL) { + SamCloseHandle(SamServerHandle); + } + if (PolicyHandle != NULL){ + LsaClose(PolicyHandle); + } + if (AccountDomainInfo != NULL) { + LsaFreeMemory(AccountDomainInfo); + } + if (UserId != NULL) { + SamFreeMemory(UserId); + } + if (NameUse != NULL) { + SamFreeMemory(NameUse); + } + + return(NtStatus); + +} + +NTSTATUS +SamiChangePasswordUser2( + PUNICODE_STRING ServerName, + PUNICODE_STRING UserName, + PSAMPR_ENCRYPTED_USER_PASSWORD NewPasswordEncryptedWithOldNt, + PENCRYPTED_NT_OWF_PASSWORD OldNtOwfPasswordEncryptedWithNewNt, + BOOLEAN LmPresent, + PSAMPR_ENCRYPTED_USER_PASSWORD NewPasswordEncryptedWithOldLm, + PENCRYPTED_LM_OWF_PASSWORD OldLmOwfPasswordEncryptedWithNewLmOrNt + ) +/*++ + + +Routine Description: + + Changes the password of a user account. This is the worker routine for + SamChangePasswordUser2 and can be called by OWF-aware clients. + Password will be set to NewPassword only if OldPassword matches the + current user password for this user and the NewPassword is not the + same as the domain password parameter PasswordHistoryLength + passwords. This call allows users to change their own password if + they have access USER_CHANGE_PASSWORD. Password update restrictions + apply. + + +Parameters: + + ServerName - The server to operate on, or NULL for this machine. + + UserName - Name of user whose password is to be changed + + NewPasswordEncryptedWithOldNt - The new cleartext password encrypted + with the old NT OWF password. + + OldNtOwfPasswordEncryptedWithNewNt - The old NT OWF password encrypted + with the new NT OWF password. + + LmPresent - If TRUE, indicates that the following two last parameter + was encrypted with the LM OWF password not the NT OWF password. + + NewPasswordEncryptedWithOldLm - The new cleartext password encrypted + with the old LM OWF password. + + OldLmOwfPasswordEncryptedWithNewLmOrNt - The old LM OWF password encrypted + with the new LM OWF password. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_ILL_FORMED_PASSWORD - The new password is poorly formed, + e.g. contains characters that can't be entered from the + keyboard, etc. + + STATUS_PASSWORD_RESTRICTION - A restriction prevents the password + from being changed. This may be for a number of reasons, + including time restrictions on how often a password may be + changed or length restrictions on the provided password. + + This error might also be returned if the new password matched + a password in the recent history log for the account. + Security administrators indicate how many of the most + recently used passwords may not be re-used. These are kept + in the password recent history log. + + STATUS_WRONG_PASSWORD - OldPassword does not contain the user's + current password. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled for this + operation + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. + +--*/ + +{ + handle_t BindingHandle; + PSAMPR_SERVER_NAME RServerNameWithNull; + USHORT RServerNameWithNullLength; + PSAMPR_SERVER_NAME RServerName; + ULONG Tries = 2; + NTSTATUS NtStatus; + USER_DOMAIN_PASSWORD_INFORMATION PasswordInformation; + + RServerNameWithNull = NULL; + + if (ARGUMENT_PRESENT(ServerName)) { + + RServerName = (PSAMPR_SERVER_NAME)(ServerName->Buffer); + RServerNameWithNullLength = ServerName->Length + (USHORT) sizeof(WCHAR); + RServerNameWithNull = MIDL_user_allocate( RServerNameWithNullLength ); + + if (RServerNameWithNull == NULL) { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + RtlCopyMemory( RServerNameWithNull, RServerName, ServerName->Length); + RServerNameWithNull[ServerName->Length/sizeof(WCHAR)] = L'\0'; + + } + + + do + { + // + // Try privacy level first, and if that failed with unknown authn + // level or invalid binding try with a lower level (none). + // + + if (Tries == 2) { + BindingHandle = SampSecureBind( + RServerNameWithNull, + RPC_C_AUTHN_LEVEL_PKT_PRIVACY + ); + + + } else if ((NtStatus == RPC_NT_UNKNOWN_AUTHN_LEVEL) || + (NtStatus == RPC_NT_UNKNOWN_AUTHN_TYPE) || + (NtStatus == RPC_NT_UNKNOWN_AUTHN_SERVICE) || + (NtStatus == RPC_NT_INVALID_BINDING) || + (NtStatus == STATUS_ACCESS_DENIED) ) { + SampSecureUnbind(BindingHandle); + + BindingHandle = SampSecureBind( + RServerNameWithNull, + RPC_C_AUTHN_LEVEL_NONE + ); + + } else { + break; + } + + if (BindingHandle != NULL) { + + RpcTryExcept{ + + // + // Get password information to make sure this operation + // is allowed. We do it now because we wanted to bind + // before trying it. + // + + NtStatus = SamrGetDomainPasswordInformation( + BindingHandle, + (PRPC_UNICODE_STRING) ServerName, + &PasswordInformation + ); + + if (NtStatus == STATUS_SUCCESS) { + + if (!( PasswordInformation.PasswordProperties & + DOMAIN_PASSWORD_NO_CLEAR_CHANGE) ) { + + NtStatus = SamrUnicodeChangePasswordUser2( + BindingHandle, + (PRPC_UNICODE_STRING) ServerName, + (PRPC_UNICODE_STRING) UserName, + NewPasswordEncryptedWithOldNt, + OldNtOwfPasswordEncryptedWithNewNt, + LmPresent, + NewPasswordEncryptedWithOldLm, + OldLmOwfPasswordEncryptedWithNewLmOrNt + ); + + } else { + + // + // Set the error to indicate that we should try the + // downlevel way to change passwords. + // + + NtStatus = STATUS_NOT_SUPPORTED; + } + } + + + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + + // + // The mapping function doesn't handle this error so + // special case it by hand. + // + NtStatus = RpcExceptionCode(); + + if (NtStatus == RPC_S_SEC_PKG_ERROR) { + NtStatus = STATUS_ACCESS_DENIED; + } else { + NtStatus = I_RpcMapWin32Status(NtStatus); + } + + + } RpcEndExcept; + + } else { + NtStatus = RPC_NT_INVALID_BINDING; + } + + Tries--; + } while ( (Tries > 0) && (!NT_SUCCESS(NtStatus)) ); + if (RServerNameWithNull != NULL) { + MIDL_user_free( RServerNameWithNull ); + } + + if (BindingHandle != NULL) { + SampSecureUnbind(BindingHandle); + } + + // + // Map these errors to STATUS_NOT_SUPPORTED + // + + if ((NtStatus == RPC_NT_UNKNOWN_IF) || + (NtStatus == RPC_NT_PROCNUM_OUT_OF_RANGE)) { + + NtStatus = STATUS_NOT_SUPPORTED; + } + return(SampMapCompletionStatus(NtStatus)); + + +} + +NTSTATUS +SamiOemChangePasswordUser2( + PSTRING ServerName, + PSTRING UserName, + PSAMPR_ENCRYPTED_USER_PASSWORD NewPasswordEncryptedWithOldLm, + PENCRYPTED_LM_OWF_PASSWORD OldLmOwfPasswordEncryptedWithNewLm + ) +/*++ + + +Routine Description: + + Changes the password of a user account. This can be called by OWF-aware + clients. Password will be set to NewPassword only if OldPassword matches + the current user password for this user and the NewPassword is not the + same as the domain password parameter PasswordHistoryLength + passwords. This call allows users to change their own password if + they have access USER_CHANGE_PASSWORD. Password update restrictions + apply. + + +Parameters: + + ServerName - The server to operate on, or NULL for this machine. + + UserName - Name of user whose password is to be changed + + + NewPasswordEncryptedWithOldLm - The new cleartext password encrypted + with the old LM OWF password. + + OldLmOwfPasswordEncryptedWithNewLm - The old LM OWF password encrypted + with the new LM OWF password. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_ILL_FORMED_PASSWORD - The new password is poorly formed, + e.g. contains characters that can't be entered from the + keyboard, etc. + + STATUS_PASSWORD_RESTRICTION - A restriction prevents the password + from being changed. This may be for a number of reasons, + including time restrictions on how often a password may be + changed or length restrictions on the provided password. + + This error might also be returned if the new password matched + a password in the recent history log for the account. + Security administrators indicate how many of the most + recently used passwords may not be re-used. These are kept + in the password recent history log. + + STATUS_WRONG_PASSWORD - OldPassword does not contain the user's + current password. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled for this + operation + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. + +--*/ + +{ + handle_t BindingHandle; + UNICODE_STRING RemoteServerName; + ULONG Tries = 2; + NTSTATUS NtStatus; + USER_DOMAIN_PASSWORD_INFORMATION PasswordInformation; + + RemoteServerName.Buffer = NULL; + RemoteServerName.Length = 0; + + if (ARGUMENT_PRESENT(ServerName)) { + + NtStatus = RtlAnsiStringToUnicodeString( + &RemoteServerName, + ServerName, + TRUE // allocate destination + ); + + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + ASSERT(RemoteServerName.Buffer[RemoteServerName.Length/sizeof(WCHAR)] == L'\0'); + } + + + do + { + // + // Try privacy level first, and if that failed with unknown authn + // level or invalid binding try with a lower level (none). + // + + if (Tries == 2) { + BindingHandle = SampSecureBind( + RemoteServerName.Buffer, + RPC_C_AUTHN_LEVEL_PKT_PRIVACY + ); + + + } else if ((NtStatus == RPC_NT_UNKNOWN_AUTHN_LEVEL) || + (NtStatus == RPC_NT_UNKNOWN_AUTHN_TYPE) || + (NtStatus == RPC_NT_INVALID_BINDING) || + (NtStatus == STATUS_ACCESS_DENIED) ) { + SampSecureUnbind(BindingHandle); + + BindingHandle = SampSecureBind( + RemoteServerName.Buffer, + RPC_C_AUTHN_LEVEL_NONE + ); + + } else { + break; + } + + if (BindingHandle != NULL) { + + RpcTryExcept{ + + // + // Get password information to make sure this operation + // is allowed. We do it now because we wanted to bind + // before trying it. + // + + NtStatus = SamrGetDomainPasswordInformation( + BindingHandle, + (PRPC_UNICODE_STRING) ServerName, + &PasswordInformation + ); + + if (NtStatus == STATUS_SUCCESS) { + + if (!( PasswordInformation.PasswordProperties & + DOMAIN_PASSWORD_NO_CLEAR_CHANGE) ) { + + NtStatus = SamrOemChangePasswordUser2( + BindingHandle, + (PRPC_STRING) ServerName, + (PRPC_STRING) UserName, + NewPasswordEncryptedWithOldLm, + OldLmOwfPasswordEncryptedWithNewLm + ); + + } else { + + // + // Set the error to indicate that we should try the + // downlevel way to change passwords. + // + + NtStatus = STATUS_NOT_SUPPORTED; + } + } + + + + } RpcExcept( EXCEPTION_EXECUTE_HANDLER ) { + + + // + // The mappin function doesn't handle this error so + // special case it by hand. + // + + if (NtStatus == RPC_S_SEC_PKG_ERROR) { + NtStatus = STATUS_ACCESS_DENIED; + } else { + NtStatus = I_RpcMapWin32Status(RpcExceptionCode()); + } + + + } RpcEndExcept; + + } else { + NtStatus = RPC_NT_INVALID_BINDING; + } + + Tries--; + } while ( (Tries > 0) && (!NT_SUCCESS(NtStatus)) ); + + RtlFreeUnicodeString( &RemoteServerName ); + + if (BindingHandle != NULL) { + SampSecureUnbind(BindingHandle); + } + + // + // Map these errors to STATUS_NOT_SUPPORTED + // + + if ((NtStatus == RPC_NT_UNKNOWN_IF) || + (NtStatus == RPC_NT_PROCNUM_OUT_OF_RANGE)) { + + NtStatus = STATUS_NOT_SUPPORTED; + } + + return(SampMapCompletionStatus(NtStatus)); + +} + + +NTSTATUS +SamChangePasswordUser2( + IN PUNICODE_STRING ServerName, + IN PUNICODE_STRING UserName, + IN PUNICODE_STRING OldPassword, + IN PUNICODE_STRING NewPassword +) + +/*++ + + +Routine Description: + + Password will be set to NewPassword only if OldPassword matches the + current user password for this user and the NewPassword is not the + same as the domain password parameter PasswordHistoryLength + passwords. This call allows users to change their own password if + they have access USER_CHANGE_PASSWORD. Password update restrictions + apply. + + +Parameters: + + ServerName - The server to operate on, or NULL for this machine. + + UserName - Name of user whose password is to be changed + + OldPassword - Current password for the user. + + NewPassword - Desired new password for the user. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_ILL_FORMED_PASSWORD - The new password is poorly formed, + e.g. contains characters that can't be entered from the + keyboard, etc. + + STATUS_PASSWORD_RESTRICTION - A restriction prevents the password + from being changed. This may be for a number of reasons, + including time restrictions on how often a password may be + changed or length restrictions on the provided password. + + This error might also be returned if the new password matched + a password in the recent history log for the account. + Security administrators indicate how many of the most + recently used passwords may not be re-used. These are kept + in the password recent history log. + + STATUS_WRONG_PASSWORD - OldPassword does not contain the user's + current password. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled for this + operation + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. + + +--*/ +{ + SAMPR_ENCRYPTED_USER_PASSWORD NewNtEncryptedWithOldNt; + SAMPR_ENCRYPTED_USER_PASSWORD NewNtEncryptedWithOldLm; + ENCRYPTED_NT_OWF_PASSWORD OldNtOwfEncryptedWithNewNt; + ENCRYPTED_NT_OWF_PASSWORD OldLmOwfEncryptedWithNewNt; + NTSTATUS NtStatus; + BOOLEAN LmPresent = TRUE; + ULONG AuthnLevel; + ULONG Tries = 2; + USER_DOMAIN_PASSWORD_INFORMATION PasswordInformation; + + + // + // Call the server, passing either a NULL Server Name pointer, or + // a pointer to a Unicode Buffer with a Wide Character NULL terminator. + // Since the input name is contained in a counted Unicode String, there + // is no NULL terminator necessarily provided, so we must append one. + // + + // + // Encrypted the passwords + // + + NtStatus = SamiEncryptPasswords( + OldPassword, + NewPassword, + &NewNtEncryptedWithOldNt, + &OldNtOwfEncryptedWithNewNt, + &LmPresent, + &NewNtEncryptedWithOldLm, + &OldLmOwfEncryptedWithNewNt + ); + + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + // + // Try the remote call... + // + + + NtStatus = SamiChangePasswordUser2( + ServerName, + UserName, + &NewNtEncryptedWithOldNt, + &OldNtOwfEncryptedWithNewNt, + LmPresent, + &NewNtEncryptedWithOldLm, + &OldLmOwfEncryptedWithNewNt + ); + + + // + // If the new API failed, try calling the old API. + // + + if (NtStatus == STATUS_NOT_SUPPORTED) { + + NtStatus = SampChangePasswordUser2( + ServerName, + UserName, + OldPassword, + NewPassword + ); + } + + return(SampMapCompletionStatus(NtStatus)); + +} diff --git a/private/newsam2/dirs b/private/newsam2/dirs new file mode 100644 index 000000000..89fc46543 --- /dev/null +++ b/private/newsam2/dirs @@ -0,0 +1,27 @@ +!IF 0 + +Copyright (c) 1989 - 1996 Microsoft Corporation + +Abstract: + + This file specifies the sub-directories of the current directory that + contain component makefiles. + +Author: + + Steve Wood (stevewo) 17-Apr-1990 + +History: + + Chris Mayhall (ChrisMay) 03-Jul-1996 + Reorganized makefiles for NTDS builds and ntds.exe. + +NOTE: Commented description of this file is in \nt\oak\bin\dirs.tpl + +!ENDIF + +DIRS= \ + client \ + server + +OPTIONAL_DIRS= diff --git a/private/newsam2/makefil0 b/private/newsam2/makefil0 new file mode 100644 index 000000000..35960958c --- /dev/null +++ b/private/newsam2/makefil0 @@ -0,0 +1,78 @@ +# +# This is the MIDL compile phase of the build process. +# +# The following symbols should be defined in your environment: +# NOTE: This file is designed to provide separate generation +# of client and server stubs. Right now, it uses an +# .acf for only the client stub generation. However, +# lines to cause a server .acf file to be used are present +# but commented out. +# The following is where you put the name of your .idl file without +# the .idl extension: + +!INCLUDE $(NTMAKEENV)\makefile.plt + +IDL_NAME = samrpc +CLIENT_ACF = samcli.acf +SERVER_ACF = samsrv.acf + +CLIENT_INC_FILE = samrpc_c.h +SERVER_INC_FILE = samrpc.h + +SDKINC = $(BASEDIR)\public\sdk\inc +SDKCRTINC = $(BASEDIR)\public\sdk\inc\crt +PRIVATEINC = ..\inc + +CLIENT_FLAGS = -acf $(CLIENT_ACF) -header $(CLIENT_INC_FILE) +SERVER_FLAGS = -acf $(SERVER_ACF) -header $(SERVER_INC_FILE) +INCS = -I$(SDKINC) -I$(SDKCRTINC) -I$(PRIVATEINC) + +CLIENT_CPP = -cpp_cmd "$(MIDL_CPP)" $(MIDL_FLAGS) +SERVER_CPP = -cpp_cmd "$(MIDL_CPP)" $(MIDL_FLAGS) + +# seperate client and server targets. The .h file is built by both. + +CLIENT_TARGETS = client\$(IDL_NAME)_c.c \ + .\client\$(CLIENT_INC_FILE) + +SERVER_TARGETS = server\$(IDL_NAME)_s.c \ + $(PRIVATEINC)\$(SERVER_INC_FILE) + +TARGETS = $(CLIENT_TARGETS) \ + $(SERVER_TARGETS) + +CLIENT_EXTRN_DEPENDS = $(CLIENT_ACF) +EXTRN_DEPENDS = $(CLIENT_EXTRN_DEPENDS) + +# Define Products and Dependencies + +all: $(CLIENT_TARGETS) $(SERVER_TARGETS) $(EXTRN_DEPENDS) +!IF "$(BUILDMSG)" != "" + @ech ; $(BUILDMSG) ; +!ENDIF + +clean: delete_source all + +delete_source: + -erase $(TARGETS) + +# +# MIDL COMPILE +# + +$(CLIENT_TARGETS) : $(IDL_NAME).idl $(CLIENT_EXTRN_DEPENDS) + copy $(PRIVATEINC)\samimp.h . + midl -Oi -oldnames -error allocation -error ref -ms_ext -c_ext $(CLIENT_CPP) $(CLIENT_FLAGS) $(IDL_NAME).idl $(INCS) + IF EXIST $(IDL_NAME)_c.c copy $(IDL_NAME)_c.c .\client & del $(IDL_NAME)_c.c + IF EXIST $(IDL_NAME)_s.c del $(IDL_NAME)_s.c + IF EXIST $(CLIENT_INC_FILE) copy $(CLIENT_INC_FILE) .\client & del $(CLIENT_INC_FILE) + +#$(SERVER_TARGETS) : $(IDL_NAME).idl $(SERVER_EXTRN_DEPENDS) +$(SERVER_TARGETS) : $(IDL_NAME).idl + IF EXIST .\inc\$(IDL_NAME).h del .\inc\$(IDL_NAME).h + IF EXIST .\server\$(IDL_NAME).h del .\server\$(IDL_NAME).h + copy $(PRIVATEINC)\samimp.h . + midl -oldnames -error allocation -error ref -ms_ext -c_ext $(SERVER_CPP) $(SERVER_FLAGS) $(IDL_NAME).idl $(INCS) + IF EXIST $(IDL_NAME)_c.c del $(IDL_NAME)_c.c + IF EXIST $(IDL_NAME)_s.c copy $(IDL_NAME)_s.c .\server & del $(IDL_NAME)_s.c + IF EXIST $(SERVER_INC_FILE) copy $(SERVER_INC_FILE) $(PRIVATEINC) & del $(SERVER_INC_FILE) diff --git a/private/newsam2/samcli.acf b/private/newsam2/samcli.acf new file mode 100644 index 000000000..2349f072d --- /dev/null +++ b/private/newsam2/samcli.acf @@ -0,0 +1,96 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + samcli.acf + +Abstract: + + Security Account Manager CLIENT rpc stub attribute configuration file. + + This file contains the attribute configuration information necessary + for generating the client stubs for remotable SAM functions. The + definitions in this file qualify the information in samrpc.idl. + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! !! + !! This .acf file is USED ONLY WHEN GENERATING SAM CLIENT STUBS. !! + !! !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + + Use samsrv.acf when generating server stubs. + + + + + The client likes to have all returned data in a single block of + allocated memory. This allows them to free the returned information + with a single call, rather than walking down some random tree of + allocated blocks. + + + +Author: + + Jim Kelly (JimK) July 2, 1991 + +Environment: + + User Mode + +Revision History: + +--*/ + + +[ implicit_handle( handle_t samcli_handle) ] +interface samr + +{ + + +//typedef [explicit_handle] PSAMPR_SERVER_NAME; + +//typedef [context_handle] SAMPR_HANDLE; + + + +// +// define complex [out] parameters to be [allocate(all_nodes)]... +// + + +typedef [allocate(all_nodes)] PSAMPR_RID_ENUMERATION; + +typedef [allocate(all_nodes)] PSAMPR_SID_ENUMERATION; + +typedef [allocate(all_nodes)] PSAMPR_RETURNED_STRING; + +typedef [allocate(all_nodes)] PSAMPR_RETURNED_NORMAL_STRING; + +typedef [allocate(all_nodes)] PSAMPR_SID_INFORMATION; + +typedef [allocate(all_nodes)] PSAMPR_DOMAIN_DISPLAY_USER; + +typedef [allocate(all_nodes)] PSAMPR_DOMAIN_DISPLAY_MACHINE; + +typedef [allocate(all_nodes)] PSAMPR_DOMAIN_DISPLAY_GROUP; + +typedef [allocate(all_nodes)] PSAMPR_DOMAIN_DISPLAY_OEM_USER; + +typedef [allocate(all_nodes)] PSAMPR_DOMAIN_DISPLAY_OEM_GROUP; + +typedef [allocate(all_nodes)] PSAMPR_DOMAIN_INFO_BUFFER; + +typedef [allocate(all_nodes)] PSAMPR_USER_INFO_BUFFER; + +typedef [allocate(all_nodes)] PSAMPR_GROUP_INFO_BUFFER; + +typedef [allocate(all_nodes)] PSAMPR_ALIAS_INFO_BUFFER; + + + +} diff --git a/private/newsam2/samimp.idl b/private/newsam2/samimp.idl new file mode 100644 index 000000000..200606c31 --- /dev/null +++ b/private/newsam2/samimp.idl @@ -0,0 +1,56 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + samimp.idl + +Abstract: + + This file is necessary to create RPC interfaces that require the use + of ntos2 types. The .idl file for the RPC product should contain a + line in the interface body that imports this file. For example: + + import "samimp.h"; + + Doing this causes the MIDL generated header file to contain the + following line: + + #include "samimp.h" + + If this technique is not used, and instead the .idl file for the RPC + product simply contains #include <samimp.h>, then the contents of + samimp.h will be expanded in the MIDL generated header file. This + can lead to duplicate definition problems later when the RPC client + or RPC server code needs to include both the MIDL generated header file + and a file that is included in samimp.h. + +Author: + Jim Kelly (JimK) May 23, 1991 + +Environment: + + User Mode + +Revision History: + +--*/ +[ + uuid(12345678-1234-ABCD-EF00-0123476589AB), //FIX, FIX Need real uuid +#ifdef __midl + ms_union, +#endif // __midl + version(0.0), + endpoint("mscn_np:[\pipe\samimp]") +] + +interface samimp + +{ + +#define SAM_MIDL_PASS +#define MIDL_PASS +#include "samimp.h" + +} diff --git a/private/newsam2/samrpc.idl b/private/newsam2/samrpc.idl new file mode 100644 index 000000000..909ab44e9 --- /dev/null +++ b/private/newsam2/samrpc.idl @@ -0,0 +1,1450 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + samrpc.idl + +Abstract: + + Security Account Manager RPC Interface Definition File + + This file contains the RPC Interface Definition Language file for + remotable SAM functions. + + These functions are internal versions of API and are NOT visible to + SAM clients. SAM clients call the SAM API defined in file ntsam.h. + Those routines, in turn, call corresponding RPC routines defined by + this file. + +Author: + + Jim Kelly (JimK) May 23, 1991 + +Environment: + + User Mode + +Revision History: + +--*/ + +[ +#ifdef USER_MODE_SAM + uuid(d1f01720-a214-11cf-a73f-00aa006e0529), //FIX,FIX - Need real uuid +#else + uuid(12345778-1234-ABCD-EF00-0123456789AC), +#endif + version(1.0), +#ifdef __midl + ms_union, +#endif // __midl + pointer_default(unique) +] + +interface samr + +{ + +// +// Import a dummy interface containing #includes for public .h files. This +// trick is necessary so that midl will only generate marshalling routines +// for subtypes that are relevant to the parameters specified on the RPC +// interface. midl also ingores function prototypes contained therein. +// + +import "samimp.idl"; + + +/////////////////////////// TEMPORARY ////////////////////////////////////// +// // +// I'm tired of fighting MIDL trying to figure out how to import common // +// definitions for the following data structures. SOOO. I'm just going // +// to hard-code them in here for a while. // +// // +/////////////////////////// TEMPORARY ////////////////////////////////////// + + +// +// Unicode strings are counted 16-bit character strings. +// The Length field and MaximumLength fields specify number of bytes, +// (not wide-characters) in the string. So, this definition differs +// a bit from the real unicode string type. +// +// The Length field does not include a null terminating character +// if present. +// +// NOTE: A null termination character (two bytes of zero), if present, +// will not be copied with the RPC. It must be explicitly added +// on the client or server side. +// +// + +typedef struct _RPC_UNICODE_STRING { + USHORT Length; + USHORT MaximumLength; +// [size_is(MaximumLength/sizeof(WCHAR)), length_is(Length/sizeof(WCHAR))] PWCH Buffer; + [size_is(MaximumLength/2), length_is(Length/2)] PWCH Buffer; +} RPC_UNICODE_STRING, *PRPC_UNICODE_STRING; + + +typedef struct _RPC_CYPHER_DATA { + ULONG Length; + ULONG MaximumLength; + [size_is(MaximumLength), length_is(Length)] PCHAR Buffer; +} RPC_CYPHER_DATA, *PRPC_CYPHER_DATA; + + +// +// ANSI counted string +// + +typedef struct _RPC_STRING { + USHORT Length; + USHORT MaximumLength; + [size_is(MaximumLength), length_is(Length)] PCHAR Buffer; +} RPC_STRING, *PRPC_STRING, RPC_ANSI_STRING, *PRPC_ANSI_STRING; + + + + + +// +// RPC definition of the SID structure. Note the use of the [size_is()] +// qualifier to specify the number of elements in the variable size +// imbedded SubAuthorityCount array at runtime. +// +// + +typedef struct _RPC_SID { + UCHAR Revision; + UCHAR SubAuthorityCount; + SID_IDENTIFIER_AUTHORITY IdentifierAuthority; + [size_is(SubAuthorityCount)] ULONG SubAuthority[*]; +} RPC_SID, *PRPC_SID, **PPRPC_SID; + + + + + +////////////////////// END TEMPORARY ////////////////////////////////////// +////////////////////// END TEMPORARY ////////////////////////////////////// +////////////////////// END TEMPORARY ////////////////////////////////////// + + +// +// SAM Generic Handle used to bind from client to server. +// This handle is used for both LOCAL and REMOTE services. +// + +typedef [handle] LPWSTR PSAMPR_SERVER_NAME; + +// +// SAM RPC Context Handle (Internal definition of SAM_HANDLE) +// + +typedef [context_handle] PVOID SAMPR_HANDLE; + + +// +// Sam enumeration return buffer format +// + +typedef struct _SAMPR_RID_ENUMERATION { + ULONG RelativeId; + RPC_UNICODE_STRING Name; +} SAMPR_RID_ENUMERATION, *PSAMPR_RID_ENUMERATION; + +typedef struct _SAMPR_SID_ENUMERATION { + PSID Sid; + RPC_UNICODE_STRING Name; +} SAMPR_SID_ENUMERATION, *PSAMPR_SID_ENUMERATION; + +typedef struct _SAMPR_ENUMERATION_BUFFER { + ULONG EntriesRead; + [size_is(EntriesRead)] PSAMPR_RID_ENUMERATION Buffer; +} SAMPR_ENUMERATION_BUFFER, *PSAMPR_ENUMERATION_BUFFER; + + + +// +// Used for passing and/or returning self-relative security descriptors +// + +typedef struct _SAMPR_SR_SECURITY_DESCRIPTOR { + ULONG Length; + [size_is(Length)] PUCHAR SecurityDescriptor; +} SAMPR_SR_SECURITY_DESCRIPTOR, *PSAMPR_SR_SECURITY_DESCRIPTOR; + + + +// +// Sam get groups return buffer format +// + +typedef struct _SAMPR_GET_GROUPS_BUFFER { + ULONG MembershipCount; + [size_is(MembershipCount)] PGROUP_MEMBERSHIP Groups; +} SAMPR_GET_GROUPS_BUFFER, *PSAMPR_GET_GROUPS_BUFFER; + + +// +// Sam get members in group return buffer format +// + +typedef struct _SAMPR_GET_MEMBERS_BUFFER { + ULONG MemberCount; + [size_is(MemberCount)] PULONG Members; + [size_is(MemberCount)] PULONG Attributes; +} SAMPR_GET_MEMBERS_BUFFER, *PSAMPR_GET_MEMBERS_BUFFER; + + +// +// Logon hours points to an array of bytes that varies in length +// depending upon how many units per-week are specified. +// + +typedef struct _SAMPR_LOGON_HOURS { + + USHORT UnitsPerWeek; + + // + // Points to an array of bitmask. + // The bits represent either days, hours or minutes in the week + // depending upon the value of UnitsPerWeek. (Technically, they + // could represent any division of time not finer than minute + // granularity). + // Day granularity is specified by specifying SAM_DAYS_PER_WEEK. + // Hours granularity is specified by specifying SAM_HOURS_PER_WEEK. + // Minute granularity is specified by specifying SAM_MINUTES_PER_WEEK. + // The number of bytes pointed to by this field is + // ((UnitsPerWeek + 7) / 8) and may not exceed + // ((SAM_MINUTES_PER_WEEK+7)/8 == 1260). + // + + [size_is(1260), length_is((UnitsPerWeek+7)/8)] PUCHAR LogonHours; + +} SAMPR_LOGON_HOURS, *PSAMPR_LOGON_HOURS; + + + + +typedef struct _SAMPR_ULONG_ARRAY { + + // + // Indicates the number of Elements in the array. + // + + ULONG Count; + + // + // Normally, the client wrapper wants to set this to NULL + // before calling the stub. This causes the client stub + // to allocate a buffe for the returned information which + // can be passed back to the caller of the wrapper routine. + // + + [size_is(Count)] ULONG * Element; + +} SAMPR_ULONG_ARRAY, *PSAMPR_ULONG_ARRAY; + + +// +// We must hide the PSID in a structure to avoid too many *'s in a +// field that uses size_is - otherwise MIDL has a fit. +// + +typedef struct _SAMPR_SID_INFORMATION { + + PRPC_SID SidPointer; + +} SAMPR_SID_INFORMATION, *PSAMPR_SID_INFORMATION; + + +// +// Define an array of pointers to SIDs +// + +typedef struct _SAMPR_PSID_ARRAY { + + // + // Indicates the number of Elements in the array. + // + + ULONG Count; + + // + // Points to the array of sid-pointers + // + + [size_is(Count)] PSAMPR_SID_INFORMATION Sids; + +} SAMPR_PSID_ARRAY, *PSAMPR_PSID_ARRAY; + + +// +// The following structure is used to receive (as an out parameter) a list +// of relative IDs (or other ULONGS). This structure is necessary because +// RPC needs the count to be in the same structure as the returned ULONGs. +// A wrapper routine is expected to initialize this structure on its stack +// and pass its address. +// +// WARNING: before passing this structure to an RPC stub, the UlongArrayBuffer +// field must be set to NULL. This causes the stub to allocate +// the return buffer. +// + +//typedef struct _SAMPR_RETURNED_ULONG_ARRAY { +// ULONG Count; +// [size_is(Count)] ULONG * UlongArrayBuffer; // Set to NULL before call !!! +//} SAMPR_RETURNED_ULONG_ARRAY, *PSAMPR_RETURNED_ULONG_ARRAY; + + + + + + + +typedef struct _SAMPR_UNICODE_STRING_ARRAY { + + // + // Indicates the number of Elements in the array. + // + + ULONG Count; + + // + // Normally, the client wrapper wants to set this to NULL + // before calling the stub. This causes the client stub + // to allocate a buffer for the returned information which + // can be passed back to the caller of the wrapper routine. + // + + [size_is(Count)] RPC_UNICODE_STRING * Element; + +} SAMPR_UNICODE_STRING_ARRAY, *PSAMPR_UNICODE_STRING_ARRAY; + + + + +// +// The allocation scheme used on the client side and server side is +// different when a list of unicode string names is to be returned. +// The server wants to be able to return multiple allocation +// blocks and the client only wants to have to worry about freeing a single +// allocation block. +// +// To accomplish this, a notion of unicode name arrays are introduced. +// one representing an array passed by a client and another representing +// an array returned by a server. Then, a client .acf file is used to get +// these to look correct when generating client stubs and a server .acf +// file is used to get them to be correct for the server when generating +// the server stubs. +// + + +typedef RPC_UNICODE_STRING SAMPR_RETURNED_STRING; +typedef RPC_UNICODE_STRING *PSAMPR_RETURNED_STRING; + +typedef STRING SAMPR_RETURNED_NORMAL_STRING; +typedef STRING *PSAMPR_RETURNED_NORMAL_STRING; + + + +// +// The following structure is used to receive (as an out parameter) a list +// of unicode string names. This structure is necessary because +// RPC needs the count to be in the same structure as the returned names. +// A wrapper routine is expected to initialize this structure on its stack +// and pass its address. +// +// WARNING: before passing this structure to an RPC stub, the UnameArrayBuffer +// field must be set to NULL. This causes the stub to allocate +// the return buffer. +// + +typedef struct _SAMPR_RETURNED_USTRING_ARRAY { + ULONG Count; + [size_is(Count)] PSAMPR_RETURNED_STRING Element; // Set to NULL before call !!! +} SAMPR_RETURNED_USTRING_ARRAY, *PSAMPR_RETURNED_USTRING_ARRAY; + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Server Object Related RPC Definitions // +// // +/////////////////////////////////////////////////////////////////////////////// + + +// (None) + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Domain Object Related RPC Definitions // +// // +/////////////////////////////////////////////////////////////////////////////// + +#pragma pack(4) +typedef struct _SAMPR_DOMAIN_GENERAL_INFORMATION { + OLD_LARGE_INTEGER ForceLogoff; + RPC_UNICODE_STRING OemInformation; + RPC_UNICODE_STRING DomainName; + RPC_UNICODE_STRING ReplicaSourceNodeName; + OLD_LARGE_INTEGER DomainModifiedCount; + ULONG DomainServerState; + ULONG DomainServerRole; + BOOLEAN UasCompatibilityRequired; + ULONG UserCount; + ULONG GroupCount; + ULONG AliasCount; +} SAMPR_DOMAIN_GENERAL_INFORMATION, *PSAMPR_DOMAIN_GENERAL_INFORMATION; +#pragma pack() + +#pragma pack(4) +typedef struct _SAMPR_DOMAIN_GENERAL_INFORMATION2 { + SAMPR_DOMAIN_GENERAL_INFORMATION I1; + + // + // New fields added for this structure (NT1.0A). + // + +#if defined(MIDL_PASS) + OLD_LARGE_INTEGER LockoutDuration; //Must be a Delta time + OLD_LARGE_INTEGER LockoutObservationWindow; //Must be a Delta time +#else + LARGE_INTEGER LockoutDuration; //Must be a Delta time + LARGE_INTEGER LockoutObservationWindow; //Must be a Delta time +#endif + USHORT LockoutThreshold; + +} SAMPR_DOMAIN_GENERAL_INFORMATION2, *PSAMPR_DOMAIN_GENERAL_INFORMATION2; +#pragma pack() + +typedef struct _SAMPR_DOMAIN_OEM_INFORMATION { + RPC_UNICODE_STRING OemInformation; +} SAMPR_DOMAIN_OEM_INFORMATION, *PSAMPR_DOMAIN_OEM_INFORMATION; + +typedef struct _SAMPR_DOMAIN_NAME_INFORMATION { + RPC_UNICODE_STRING DomainName; +} SAMPR_DOMAIN_NAME_INFORMATION, *PSAMPR_DOMAIN_NAME_INFORMATION; + + +typedef struct SAMPR_DOMAIN_REPLICATION_INFORMATION { + RPC_UNICODE_STRING ReplicaSourceNodeName; +} SAMPR_DOMAIN_REPLICATION_INFORMATION, *PSAMPR_DOMAIN_REPLICATION_INFORMATION; + +typedef struct _SAMPR_DOMAIN_LOCKOUT_INFORMATION { +#if defined(MIDL_PASS) + OLD_LARGE_INTEGER LockoutDuration; //Must be a Delta time + OLD_LARGE_INTEGER LockoutObservationWindow; //Must be a Delta time +#else + LARGE_INTEGER LockoutDuration; //Must be a Delta time + LARGE_INTEGER LockoutObservationWindow; //Must be a Delta time +#endif + USHORT LockoutThreshold; //Zero means no lockout +} SAMPR_DOMAIN_LOCKOUT_INFORMATION, *PSAMPR_DOMAIN_LOCKOUT_INFORMATION; + + +typedef [switch_type(DOMAIN_INFORMATION_CLASS)] union +_SAMPR_DOMAIN_INFO_BUFFER { + [case(DomainPasswordInformation)] DOMAIN_PASSWORD_INFORMATION Password; + [case(DomainGeneralInformation)] SAMPR_DOMAIN_GENERAL_INFORMATION General; + [case(DomainLogoffInformation)] DOMAIN_LOGOFF_INFORMATION Logoff; + [case(DomainOemInformation)] SAMPR_DOMAIN_OEM_INFORMATION Oem; + [case(DomainNameInformation)] SAMPR_DOMAIN_NAME_INFORMATION Name; + [case(DomainServerRoleInformation)] DOMAIN_SERVER_ROLE_INFORMATION Role; + [case(DomainReplicationInformation)] SAMPR_DOMAIN_REPLICATION_INFORMATION Replication; + [case(DomainModifiedInformation)] DOMAIN_MODIFIED_INFORMATION Modified; + [case(DomainStateInformation)] DOMAIN_STATE_INFORMATION State; + [case(DomainGeneralInformation2)] SAMPR_DOMAIN_GENERAL_INFORMATION2 General2; + [case(DomainLockoutInformation)] SAMPR_DOMAIN_LOCKOUT_INFORMATION Lockout; + [case(DomainModifiedInformation2)] DOMAIN_MODIFIED_INFORMATION2 Modified2; +} SAMPR_DOMAIN_INFO_BUFFER, *PSAMPR_DOMAIN_INFO_BUFFER; + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Group Object Related Definitions // +// // +/////////////////////////////////////////////////////////////////////////////// + + +typedef struct _SAMPR_GROUP_GENERAL_INFORMATION { + RPC_UNICODE_STRING Name; + ULONG Attributes; + ULONG MemberCount; + RPC_UNICODE_STRING AdminComment; +} SAMPR_GROUP_GENERAL_INFORMATION, *PSAMPR_GROUP_GENERAL_INFORMATION; + +typedef struct _SAMPR_GROUP_NAME_INFORMATION { + RPC_UNICODE_STRING Name; +} SAMPR_GROUP_NAME_INFORMATION, *PSAMPR_GROUP_NAME_INFORMATION; + + +typedef struct _SAMPR_GROUP_ADM_COMMENT_INFORMATION { + RPC_UNICODE_STRING AdminComment; +} SAMPR_GROUP_ADM_COMMENT_INFORMATION, *PSAMPR_GROUP_ADM_COMMENT_INFORMATION; + + + +typedef [switch_type(GROUP_INFORMATION_CLASS)] union +_SAMPR_GROUP_INFO_BUFFER { + [case(GroupGeneralInformation)] SAMPR_GROUP_GENERAL_INFORMATION General; + [case(GroupNameInformation)] SAMPR_GROUP_NAME_INFORMATION Name; + [case(GroupAttributeInformation)] GROUP_ATTRIBUTE_INFORMATION Attribute; + [case(GroupAdminCommentInformation)] SAMPR_GROUP_ADM_COMMENT_INFORMATION AdminComment; +} SAMPR_GROUP_INFO_BUFFER, *PSAMPR_GROUP_INFO_BUFFER; + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Alias Object Related Definitions // +// // +/////////////////////////////////////////////////////////////////////////////// + + +typedef struct _SAMPR_ALIAS_GENERAL_INFORMATION { + RPC_UNICODE_STRING Name; + ULONG MemberCount; + RPC_UNICODE_STRING AdminComment; +} SAMPR_ALIAS_GENERAL_INFORMATION, *PSAMPR_ALIAS_GENERAL_INFORMATION; + +typedef struct _SAMPR_ALIAS_NAME_INFORMATION { + RPC_UNICODE_STRING Name; +} SAMPR_ALIAS_NAME_INFORMATION, *PSAMPR_ALIAS_NAME_INFORMATION; + + +typedef struct _SAMPR_ALIAS_ADM_COMMENT_INFORMATION { + RPC_UNICODE_STRING AdminComment; +} SAMPR_ALIAS_ADM_COMMENT_INFORMATION, *PSAMPR_ALIAS_ADM_COMMENT_INFORMATION; + + + +typedef [switch_type(ALIAS_INFORMATION_CLASS)] union +_SAMPR_ALIAS_INFO_BUFFER { + [case(AliasGeneralInformation)] SAMPR_ALIAS_GENERAL_INFORMATION General; + [case(AliasNameInformation)] SAMPR_ALIAS_NAME_INFORMATION Name; + [case(AliasAdminCommentInformation)] SAMPR_ALIAS_ADM_COMMENT_INFORMATION AdminComment; +} SAMPR_ALIAS_INFO_BUFFER, *PSAMPR_ALIAS_INFO_BUFFER; + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// User Object Related Definitions // +// // +/////////////////////////////////////////////////////////////////////////////// + + +#pragma pack(4) +typedef struct _SAMPR_USER_ALL_INFORMATION { + OLD_LARGE_INTEGER LastLogon; + OLD_LARGE_INTEGER LastLogoff; + OLD_LARGE_INTEGER PasswordLastSet; + OLD_LARGE_INTEGER AccountExpires; + OLD_LARGE_INTEGER PasswordCanChange; + OLD_LARGE_INTEGER PasswordMustChange; + RPC_UNICODE_STRING UserName; + RPC_UNICODE_STRING FullName; + RPC_UNICODE_STRING HomeDirectory; + RPC_UNICODE_STRING HomeDirectoryDrive; + RPC_UNICODE_STRING ScriptPath; + RPC_UNICODE_STRING ProfilePath; + RPC_UNICODE_STRING AdminComment; + RPC_UNICODE_STRING WorkStations; + RPC_UNICODE_STRING UserComment; + RPC_UNICODE_STRING Parameters; + RPC_UNICODE_STRING LmOwfPassword; + RPC_UNICODE_STRING NtOwfPassword; + RPC_UNICODE_STRING PrivateData; + SAMPR_SR_SECURITY_DESCRIPTOR SecurityDescriptor; + ULONG UserId; + ULONG PrimaryGroupId; + ULONG UserAccountControl; + ULONG WhichFields; + SAMPR_LOGON_HOURS LogonHours; + USHORT BadPasswordCount; + USHORT LogonCount; + USHORT CountryCode; + USHORT CodePage; + BOOLEAN LmPasswordPresent; + BOOLEAN NtPasswordPresent; + BOOLEAN PasswordExpired; + BOOLEAN PrivateDataSensitive; +} SAMPR_USER_ALL_INFORMATION, *PSAMPR_USER_ALL_INFORMATION; +#pragma pack() + +#pragma pack(4) +typedef struct _SAMPR_USER_INTERNAL3_INFORMATION { + SAMPR_USER_ALL_INFORMATION I1; +#if defined(MIDL_PASS) + OLD_LARGE_INTEGER LastBadPasswordTime; +#else + LARGE_INTEGER LastBadPasswordTime; +#endif +} SAMPR_USER_INTERNAL3_INFORMATION, *PSAMPR_USER_INTERNAL3_INFORMATION; +#pragma pack() + + +typedef struct _SAMPR_USER_GENERAL_INFORMATION { + RPC_UNICODE_STRING UserName; + RPC_UNICODE_STRING FullName; + ULONG PrimaryGroupId; + RPC_UNICODE_STRING AdminComment; + RPC_UNICODE_STRING UserComment; +} SAMPR_USER_GENERAL_INFORMATION, *PSAMPR_USER_GENERAL_INFORMATION; + +typedef struct _SAMPR_USER_PREFERENCES_INFORMATION { + RPC_UNICODE_STRING UserComment; + RPC_UNICODE_STRING Reserved1; + USHORT CountryCode; + USHORT CodePage; +} SAMPR_USER_PREFERENCES_INFORMATION, *PSAMPR_USER_PREFERENCES_INFORMATION; + +typedef struct _SAMPR_USER_PARAMETERS_INFORMATION { + RPC_UNICODE_STRING Parameters; +} SAMPR_USER_PARAMETERS_INFORMATION, *PSAMPR_USER_PARAMETERS_INFORMATION; + +#pragma pack(4) +typedef struct _SAMPR_USER_LOGON_INFORMATION { + RPC_UNICODE_STRING UserName; + RPC_UNICODE_STRING FullName; + ULONG UserId; + ULONG PrimaryGroupId; + RPC_UNICODE_STRING HomeDirectory; + RPC_UNICODE_STRING HomeDirectoryDrive; + RPC_UNICODE_STRING ScriptPath; + RPC_UNICODE_STRING ProfilePath; + RPC_UNICODE_STRING WorkStations; + OLD_LARGE_INTEGER LastLogon; + OLD_LARGE_INTEGER LastLogoff; + OLD_LARGE_INTEGER PasswordLastSet; + OLD_LARGE_INTEGER PasswordCanChange; + OLD_LARGE_INTEGER PasswordMustChange; + SAMPR_LOGON_HOURS LogonHours; + USHORT BadPasswordCount; + USHORT LogonCount; + ULONG UserAccountControl; +} SAMPR_USER_LOGON_INFORMATION, *PSAMPR_USER_LOGON_INFORMATION; +#pragma pack() + +#pragma pack(4) +typedef struct _SAMPR_USER_ACCOUNT_INFORMATION { + RPC_UNICODE_STRING UserName; + RPC_UNICODE_STRING FullName; + ULONG UserId; + ULONG PrimaryGroupId; + RPC_UNICODE_STRING HomeDirectory; + RPC_UNICODE_STRING HomeDirectoryDrive; + RPC_UNICODE_STRING ScriptPath; + RPC_UNICODE_STRING ProfilePath; + RPC_UNICODE_STRING AdminComment; + RPC_UNICODE_STRING WorkStations; + OLD_LARGE_INTEGER LastLogon; + OLD_LARGE_INTEGER LastLogoff; + SAMPR_LOGON_HOURS LogonHours; + USHORT BadPasswordCount; + USHORT LogonCount; + OLD_LARGE_INTEGER PasswordLastSet; + OLD_LARGE_INTEGER AccountExpires; + ULONG UserAccountControl; +} SAMPR_USER_ACCOUNT_INFORMATION, *PSAMPR_USER_ACCOUNT_INFORMATION; +#pragma pack() + +typedef struct _SAMPR_USER_A_NAME_INFORMATION { + RPC_UNICODE_STRING UserName; +} SAMPR_USER_A_NAME_INFORMATION, *PSAMPR_USER_A_NAME_INFORMATION; + +typedef struct _SAMPR_USER_F_NAME_INFORMATION { + RPC_UNICODE_STRING FullName; +} SAMPR_USER_F_NAME_INFORMATION, *PSAMPR_USER_F_NAME_INFORMATION; + +typedef struct _SAMPR_USER_NAME_INFORMATION { + RPC_UNICODE_STRING UserName; + RPC_UNICODE_STRING FullName; +} SAMPR_USER_NAME_INFORMATION, *PSAMPR_USER_NAME_INFORMATION; + +typedef struct _SAMPR_USER_HOME_INFORMATION { + RPC_UNICODE_STRING HomeDirectory; + RPC_UNICODE_STRING HomeDirectoryDrive; +} SAMPR_USER_HOME_INFORMATION, *PSAMPR_USER_HOME_INFORMATION; + +typedef struct _SAMPR_USER_SCRIPT_INFORMATION { + RPC_UNICODE_STRING ScriptPath; +} SAMPR_USER_SCRIPT_INFORMATION, *PSAMPR_USER_SCRIPT_INFORMATION; + +typedef struct _SAMPR_USER_PROFILE_INFORMATION { + RPC_UNICODE_STRING ProfilePath; +} SAMPR_USER_PROFILE_INFORMATION, *PSAMPR_USER_PROFILE_INFORMATION; + +typedef struct _SAMPR_USER_ADMIN_COMMENT_INFORMATION { + RPC_UNICODE_STRING AdminComment; +} SAMPR_USER_ADMIN_COMMENT_INFORMATION, *PSAMPR_USER_ADMIN_COMMENT_INFORMATION; + +typedef struct _SAMPR_USER_WORKSTATIONS_INFORMATION { + RPC_UNICODE_STRING WorkStations; +} SAMPR_USER_WORKSTATIONS_INFORMATION, *PSAMPR_USER_WORKSTATIONS_INFORMATION; + +typedef struct _SAMPR_USER_LOGON_HOURS_INFORMATION { + SAMPR_LOGON_HOURS LogonHours; +} SAMPR_USER_LOGON_HOURS_INFORMATION, *PSAMPR_USER_LOGON_HOURS_INFORMATION; + +typedef struct _SAMPR_USER_INTERNAL1_INFORMATION { + ENCRYPTED_NT_OWF_PASSWORD EncryptedNtOwfPassword; + ENCRYPTED_LM_OWF_PASSWORD EncryptedLmOwfPassword; + BOOLEAN NtPasswordPresent; + BOOLEAN LmPasswordPresent; + BOOLEAN PasswordExpired; +} SAMPR_USER_INTERNAL1_INFORMATION, *PSAMPR_USER_INTERNAL1_INFORMATION; + + +typedef struct _SAMPR_USER_INTERNAL4_INFORMATION { + SAMPR_USER_ALL_INFORMATION I1; + SAMPR_ENCRYPTED_USER_PASSWORD UserPassword; +} SAMPR_USER_INTERNAL4_INFORMATION, *PSAMPR_USER_INTERNAL4_INFORMATION; + +typedef struct _SAMPR_USER_INTERNAL5_INFORMATION { + SAMPR_ENCRYPTED_USER_PASSWORD UserPassword; + BOOLEAN PasswordExpired; +} SAMPR_USER_INTERNAL5_INFORMATION, *PSAMPR_USER_INTERNAL5_INFORMATION; + + +typedef [switch_type(USER_INFORMATION_CLASS)] union +_SAMPR_USER_INFO_BUFFER { + [case(UserGeneralInformation)] SAMPR_USER_GENERAL_INFORMATION General; + [case(UserPreferencesInformation)] SAMPR_USER_PREFERENCES_INFORMATION Preferences; + [case(UserLogonInformation)] SAMPR_USER_LOGON_INFORMATION Logon; + [case(UserLogonHoursInformation)] SAMPR_USER_LOGON_HOURS_INFORMATION LogonHours; + [case(UserAccountInformation)] SAMPR_USER_ACCOUNT_INFORMATION Account; + [case(UserNameInformation)] SAMPR_USER_NAME_INFORMATION Name; + [case(UserAccountNameInformation)] SAMPR_USER_A_NAME_INFORMATION AccountName; + [case(UserFullNameInformation)] SAMPR_USER_F_NAME_INFORMATION FullName; + [case(UserPrimaryGroupInformation)] USER_PRIMARY_GROUP_INFORMATION PrimaryGroup; + [case(UserHomeInformation)] SAMPR_USER_HOME_INFORMATION Home; + [case(UserScriptInformation)] SAMPR_USER_SCRIPT_INFORMATION Script; + [case(UserProfileInformation)] SAMPR_USER_PROFILE_INFORMATION Profile; + [case(UserAdminCommentInformation)] SAMPR_USER_ADMIN_COMMENT_INFORMATION AdminComment; + [case(UserWorkStationsInformation)] SAMPR_USER_WORKSTATIONS_INFORMATION WorkStations; + [case(UserControlInformation)] USER_CONTROL_INFORMATION Control; + [case(UserExpiresInformation)] USER_EXPIRES_INFORMATION Expires; + [case(UserInternal1Information)] SAMPR_USER_INTERNAL1_INFORMATION Internal1; + [case(UserInternal2Information)] USER_INTERNAL2_INFORMATION Internal2; + [case(UserParametersInformation)] SAMPR_USER_PARAMETERS_INFORMATION Parameters; + [case(UserAllInformation)] SAMPR_USER_ALL_INFORMATION All; + [case(UserInternal3Information)] SAMPR_USER_INTERNAL3_INFORMATION Internal3; + [case(UserInternal4Information)] SAMPR_USER_INTERNAL4_INFORMATION Internal4; + [case(UserInternal5Information)] SAMPR_USER_INTERNAL5_INFORMATION Internal5; +} SAMPR_USER_INFO_BUFFER, *PSAMPR_USER_INFO_BUFFER; + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Domain display information Related Definitions // +// // +/////////////////////////////////////////////////////////////////////////////// + + +typedef struct _SAMPR_DOMAIN_DISPLAY_USER { + ULONG Index; + ULONG Rid; + ULONG AccountControl; + SAMPR_RETURNED_STRING LogonName; + SAMPR_RETURNED_STRING AdminComment; + SAMPR_RETURNED_STRING FullName; +} SAMPR_DOMAIN_DISPLAY_USER, *PSAMPR_DOMAIN_DISPLAY_USER; + +typedef struct _SAMPR_DOMAIN_DISPLAY_MACHINE { + ULONG Index; + ULONG Rid; + ULONG AccountControl; + SAMPR_RETURNED_STRING Machine; + SAMPR_RETURNED_STRING Comment; +} SAMPR_DOMAIN_DISPLAY_MACHINE, *PSAMPR_DOMAIN_DISPLAY_MACHINE; + +typedef struct _SAMPR_DOMAIN_DISPLAY_GROUP { // Added for NT1.0A + ULONG Index; + ULONG Rid; + ULONG Attributes; + SAMPR_RETURNED_STRING Group; + SAMPR_RETURNED_STRING Comment; +} SAMPR_DOMAIN_DISPLAY_GROUP, *PSAMPR_DOMAIN_DISPLAY_GROUP; + +typedef struct _SAMPR_DOMAIN_DISPLAY_OEM_USER { // Added for NT1.0A + ULONG Index; + SAMPR_RETURNED_NORMAL_STRING OemUser; +} SAMPR_DOMAIN_DISPLAY_OEM_USER, *PSAMPR_DOMAIN_DISPLAY_OEM_USER; + +typedef struct _SAMPR_DOMAIN_DISPLAY_OEM_GROUP { // Added for NT1.0A + ULONG Index; + SAMPR_RETURNED_NORMAL_STRING OemGroup; +} SAMPR_DOMAIN_DISPLAY_OEM_GROUP, *PSAMPR_DOMAIN_DISPLAY_OEM_GROUP; + + +typedef struct _SAMPR_DOMAIN_DISPLAY_USER_BUFFER { + ULONG EntriesRead; + [size_is(EntriesRead)] PSAMPR_DOMAIN_DISPLAY_USER Buffer; +} SAMPR_DOMAIN_DISPLAY_USER_BUFFER, *PSAMPR_DOMAIN_DISPLAY_USER_BUFFER; + + +typedef struct _SAMPR_DOMAIN_DISPLAY_MACHINE_BUFFER { + ULONG EntriesRead; + [size_is(EntriesRead)] PSAMPR_DOMAIN_DISPLAY_MACHINE Buffer; +} SAMPR_DOMAIN_DISPLAY_MACHINE_BUFFER, *PSAMPR_DOMAIN_DISPLAY_MACHINE_BUFFER; + +typedef struct _SAMPR_DOMAIN_DISPLAY_GROUP_BUFFER { // Added for NT1.0A + ULONG EntriesRead; + [size_is(EntriesRead)] PSAMPR_DOMAIN_DISPLAY_GROUP Buffer; +} SAMPR_DOMAIN_DISPLAY_GROUP_BUFFER, *PSAMPR_DOMAIN_DISPLAY_GROUP_BUFFER; + +typedef struct _SAMPR_DOMAIN_DISPLAY_OEM_USER_BUFFER { // Added for NT1.0A + ULONG EntriesRead; + [size_is(EntriesRead)] PSAMPR_DOMAIN_DISPLAY_OEM_USER Buffer; +} SAMPR_DOMAIN_DISPLAY_OEM_USER_BUFFER, *PSAMPR_DOMAIN_DISPLAY_OEM_USER_BUFFER; + + +typedef struct _SAMPR_DOMAIN_DISPLAY_OEM_GROUP_BUFFER { // Added for NT1.0A + ULONG EntriesRead; + [size_is(EntriesRead)] PSAMPR_DOMAIN_DISPLAY_OEM_GROUP Buffer; +} SAMPR_DOMAIN_DISPLAY_OEM_GROUP_BUFFER, *PSAMPR_DOMAIN_DISPLAY_OEM_GROUP_BUFFER; + + + +typedef [switch_type(DOMAIN_DISPLAY_INFORMATION)] union +_SAMPR_DISPLAY_INFO_BUFFER { + [case(DomainDisplayUser)] SAMPR_DOMAIN_DISPLAY_USER_BUFFER UserInformation; + [case(DomainDisplayMachine)] SAMPR_DOMAIN_DISPLAY_MACHINE_BUFFER MachineInformation; + [case(DomainDisplayGroup)] SAMPR_DOMAIN_DISPLAY_GROUP_BUFFER GroupInformation; + [case(DomainDisplayOemUser)] SAMPR_DOMAIN_DISPLAY_OEM_USER_BUFFER OemUserInformation; + [case(DomainDisplayOemGroup)] SAMPR_DOMAIN_DISPLAY_OEM_GROUP_BUFFER OemGroupInformation; +} SAMPR_DISPLAY_INFO_BUFFER, *PSAMPR_DISPLAY_INFO_BUFFER; + + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// APIs Exported By SAM // +// // +// Note that MIDL will prefix each of these routine names with the // +// interface name. So, we will end up with names like Samr_Connect(). // +// // +/////////////////////////////////////////////////////////////////////////////// + + +NTSTATUS +SamrConnect( + [in,unique] PSAMPR_SERVER_NAME ServerName, + [out] SAMPR_HANDLE * ServerHandle, + [in] ACCESS_MASK DesiredAccess + ); + +NTSTATUS +SamrCloseHandle( + [in,out] SAMPR_HANDLE * SamHandle + ); + + +NTSTATUS +SamrSetSecurityObject( + [in] SAMPR_HANDLE ObjectHandle, + [in] SECURITY_INFORMATION SecurityInformation, + [in] PSAMPR_SR_SECURITY_DESCRIPTOR SecurityDescriptor + ); + +NTSTATUS +SamrQuerySecurityObject( + [in] SAMPR_HANDLE ObjectHandle, + [in] SECURITY_INFORMATION SecurityInformation, + [out] PSAMPR_SR_SECURITY_DESCRIPTOR *SecurityDescriptor + ); + + + + + +// +// Server related API +// + +NTSTATUS +SamrShutdownSamServer( + [in] SAMPR_HANDLE ServerHandle + ); + + +NTSTATUS +SamrLookupDomainInSamServer( + [in] SAMPR_HANDLE ServerHandle, + [in] PRPC_UNICODE_STRING Name, + [out] PRPC_SID * DomainId + ); + +NTSTATUS +SamrEnumerateDomainsInSamServer( + [in] SAMPR_HANDLE ServerHandle, + [in,out] PSAM_ENUMERATE_HANDLE EnumerationContext, + [out] PSAMPR_ENUMERATION_BUFFER *Buffer, + [in] ULONG PreferedMaximumLength, + [out] PULONG CountReturned + ); + + + +// +// Domain related API +// + +NTSTATUS +SamrOpenDomain( + [in] SAMPR_HANDLE ServerHandle, + [in] ACCESS_MASK DesiredAccess, + [in] PRPC_SID DomainId, + [out] SAMPR_HANDLE * DomainHandle + ); + +// +// Don't call the following api with an InfoClass level beyond +// DomainUasInformation. This is the highest info level supported +// in NT1.0, and RPC didn't raise an exception when an invalid +// info level was passed, so the server side stub tries to unmarshal +// the [out] parameter (which isn't there) and can access violate. +// +// CALL SamrQueryInformationDomain2() instead (see end of this file) +// + +NTSTATUS +SamrQueryInformationDomain( + [in] SAMPR_HANDLE DomainHandle, + [in] DOMAIN_INFORMATION_CLASS DomainInformationClass, + [out, switch_is(DomainInformationClass)] + PSAMPR_DOMAIN_INFO_BUFFER *Buffer + ); + +NTSTATUS +SamrSetInformationDomain( + [in] SAMPR_HANDLE DomainHandle, + [in] DOMAIN_INFORMATION_CLASS DomainInformationClass, + [in, switch_is(DomainInformationClass)] + PSAMPR_DOMAIN_INFO_BUFFER DomainInformation + ); + +NTSTATUS +SamrCreateGroupInDomain( + [in] SAMPR_HANDLE DomainHandle, + [in] PRPC_UNICODE_STRING Name, + [in] ACCESS_MASK DesiredAccess, + [out] SAMPR_HANDLE * GroupHandle, + [out] PULONG RelativeId + ); + + +NTSTATUS +SamrEnumerateGroupsInDomain( + [in] SAMPR_HANDLE DomainHandle, + [in,out] PSAM_ENUMERATE_HANDLE EnumerationContext, + [out] PSAMPR_ENUMERATION_BUFFER *Buffer, + [in] ULONG PreferedMaximumLength, + [out] PULONG CountReturned + ); + +NTSTATUS +SamrCreateUserInDomain( + [in] SAMPR_HANDLE DomainHandle, + [in] PRPC_UNICODE_STRING Name, + [in] ACCESS_MASK DesiredAccess, + [out] SAMPR_HANDLE * UserHandle, + [out] PULONG RelativeId + ); + +NTSTATUS +SamrEnumerateUsersInDomain( + [in] SAMPR_HANDLE DomainHandle, + [in,out] PSAM_ENUMERATE_HANDLE EnumerationContext, + [in] ULONG UserAccountControl, + [out] PSAMPR_ENUMERATION_BUFFER *Buffer, + [in] ULONG PreferedMaximumLength, + [out] PULONG CountReturned + ); + +NTSTATUS +SamrCreateAliasInDomain( + [in] SAMPR_HANDLE DomainHandle, + [in] PRPC_UNICODE_STRING AccountName, + [in] ACCESS_MASK DesiredAccess, + [out] SAMPR_HANDLE * AliasHandle, + [out] PULONG RelativeId + ); + +NTSTATUS +SamrEnumerateAliasesInDomain( + [in] SAMPR_HANDLE DomainHandle, + [in,out] PSAM_ENUMERATE_HANDLE EnumerationContext, + [out] PSAMPR_ENUMERATION_BUFFER *Buffer, + [in] ULONG PreferedMaximumLength, + [out] PULONG CountReturned + ); + +NTSTATUS +SamrGetAliasMembership( + [in] SAMPR_HANDLE DomainHandle, + [in] PSAMPR_PSID_ARRAY SidArray, + [out] PSAMPR_ULONG_ARRAY Membership + ); + +// +// The format of parameters in LookupNamesInDomain() differs between +// client and server side. This is accomplished using multiple .acf files. +// Please see samclient.acf and samsrvr.acf for further descriptions of +// parameter formats. +// + + +NTSTATUS +SamrLookupNamesInDomain( + [in] SAMPR_HANDLE DomainHandle, + [in] ULONG Count, + // + // The following count must match SAM_MAXIMUM_LOOKUP_COUNT, + // defined in ntsam.h + // + [in,size_is(1000), length_is(Count)] + RPC_UNICODE_STRING Names[*], + [out] PSAMPR_ULONG_ARRAY RelativeIds, + [out] PSAMPR_ULONG_ARRAY Use + ); + +// +// +// The format of parameters in LookupIdsInDomain() differs between +// client and server side. This is accomplished using multiple .acf files. +// Please see samclient.acf and samsrvr.acf for further descriptions of +// parameter formats. +// + +NTSTATUS +SamrLookupIdsInDomain( + [in] SAMPR_HANDLE DomainHandle, + [in] ULONG Count, + // + // The following count must match SAM_MAXIMUM_LOOKUP_COUNT, + // defined in ntsam.h + // + [in,size_is(1000), length_is(Count)] + PULONG RelativeIds, + [out] PSAMPR_RETURNED_USTRING_ARRAY Names, + [out] PSAMPR_ULONG_ARRAY Use + ); + + + + + + + +// +// Group related API +// + +NTSTATUS +SamrOpenGroup( + [in] SAMPR_HANDLE DomainHandle, + [in] ACCESS_MASK DesiredAccess, + [in] ULONG GroupId, + [out] SAMPR_HANDLE * GroupHandle + ); + +NTSTATUS +SamrQueryInformationGroup( + [in] SAMPR_HANDLE GroupHandle, + [in] GROUP_INFORMATION_CLASS GroupInformationClass, + [out, switch_is(GroupInformationClass)] + PSAMPR_GROUP_INFO_BUFFER *Buffer + ); + +NTSTATUS +SamrSetInformationGroup( + [in] SAMPR_HANDLE GroupHandle, + [in] GROUP_INFORMATION_CLASS GroupInformationClass, + [in, switch_is(GroupInformationClass)] + PSAMPR_GROUP_INFO_BUFFER Buffer + ); + +NTSTATUS +SamrAddMemberToGroup( + [in] SAMPR_HANDLE GroupHandle, + [in] ULONG MemberId, + [in] ULONG Attributes + ); + +NTSTATUS +SamrDeleteGroup( + [in,out] SAMPR_HANDLE * GroupHandle + ); + +NTSTATUS +SamrRemoveMemberFromGroup( + [in] SAMPR_HANDLE GroupHandle, + [in] ULONG MemberId + ); + +NTSTATUS +SamrGetMembersInGroup( + [in] SAMPR_HANDLE GroupHandle, + [out] PSAMPR_GET_MEMBERS_BUFFER *Members + ); + +NTSTATUS +SamrSetMemberAttributesOfGroup( + [in] SAMPR_HANDLE GroupHandle, + [in] ULONG MemberId, + [in] ULONG Attributes + ); + + + + + + +// +// Alias related API +// + +NTSTATUS +SamrOpenAlias( + [in] SAMPR_HANDLE DomainHandle, + [in] ACCESS_MASK DesiredAccess, + [in] ULONG AliasId, + [out] SAMPR_HANDLE * AliasHandle + ); + +NTSTATUS +SamrQueryInformationAlias( + [in] SAMPR_HANDLE AliasHandle, + [in] ALIAS_INFORMATION_CLASS AliasInformationClass, + [out, switch_is(AliasInformationClass)] + PSAMPR_ALIAS_INFO_BUFFER *Buffer + ); + +NTSTATUS +SamrSetInformationAlias( + [in] SAMPR_HANDLE AliasHandle, + [in] ALIAS_INFORMATION_CLASS AliasInformationClass, + [in, switch_is(AliasInformationClass)] + PSAMPR_ALIAS_INFO_BUFFER Buffer + ); + +NTSTATUS +SamrDeleteAlias( + [in, out] SAMPR_HANDLE * AliasHandle + ); + +NTSTATUS +SamrAddMemberToAlias( + [in] SAMPR_HANDLE AliasHandle, + [in] PRPC_SID MemberId + ); + +NTSTATUS +SamrRemoveMemberFromAlias( + [in] SAMPR_HANDLE AliasHandle, + [in] PRPC_SID MemberId + ); + +NTSTATUS +SamrGetMembersInAlias( + [in] SAMPR_HANDLE AliasHandle, + [out] PSAMPR_PSID_ARRAY Members + ); + + + + + + +// +// User related API +// + +NTSTATUS +SamrOpenUser( + [in] SAMPR_HANDLE DomainHandle, + [in] ACCESS_MASK DesiredAccess, + [in] ULONG UserId, + [out] SAMPR_HANDLE * UserHandle + ); + +NTSTATUS +SamrDeleteUser( + [in,out] SAMPR_HANDLE * UserHandle + ); + +// +// Don't call the following api with an InfoClass level beyond +// UserAllInformation. This is the highest info level supported +// in NT1.0, and RPC didn't raise an exception when an invalid +// info level was passed, so the server side stub tries to unmarshal +// the [out] parameter (which isn't there) and can access violate. +// +// CALL SamrQueryInformationUser2() instead (see end of this file) +// + +NTSTATUS +SamrQueryInformationUser( + [in] SAMPR_HANDLE UserHandle, + [in] USER_INFORMATION_CLASS UserInformationClass, + [out, switch_is(UserInformationClass)] + PSAMPR_USER_INFO_BUFFER *Buffer + ); + +NTSTATUS +SamrSetInformationUser( + [in] SAMPR_HANDLE UserHandle, + [in] USER_INFORMATION_CLASS UserInformationClass, + [in, switch_is(UserInformationClass)] + PSAMPR_USER_INFO_BUFFER Buffer + ); + + +NTSTATUS +SamrChangePasswordUser( + [in] SAMPR_HANDLE UserHandle, + + [in] BOOLEAN LmPresent, + [in, unique] PENCRYPTED_LM_OWF_PASSWORD LmOldEncryptedWithLmNew, + [in, unique] PENCRYPTED_LM_OWF_PASSWORD LmNewEncryptedWithLmOld, + + [in] BOOLEAN NtPresent, + [in, unique] PENCRYPTED_NT_OWF_PASSWORD NtOldEncryptedWithNtNew, + [in, unique] PENCRYPTED_NT_OWF_PASSWORD NtNewEncryptedWithNtOld, + + [in] BOOLEAN NtCrossEncryptionPresent, + [in, unique] PENCRYPTED_NT_OWF_PASSWORD NtNewEncryptedWithLmNew, + + [in] BOOLEAN LmCrossEncryptionPresent, + [in, unique] PENCRYPTED_LM_OWF_PASSWORD LmNtNewEncryptedWithNtNew + ); + +NTSTATUS +SamrGetGroupsForUser( + [in] SAMPR_HANDLE UserHandle, + [out] PSAMPR_GET_GROUPS_BUFFER *Groups + ); + +// +// Don't call the following api with an InfoClass level beyond +// DomainDisplayMachine. This is the highest info level supported +// in NT1.0, and RPC didn't raise an exception when an invalid +// info level was passed, so the server side stub tries to unmarshal +// the [out] parameter (which isn't there) and can access violate. +// +// CALL SamrQueryDisplayInformation2() instead (see end of this file) +// + +NTSTATUS +SamrQueryDisplayInformation ( + [in] SAMPR_HANDLE DomainHandle, + [in] DOMAIN_DISPLAY_INFORMATION DisplayInformationClass, + [in] ULONG Index, + [in] ULONG EntryCount, + [in] ULONG PreferredMaximumLength, + [out] PULONG TotalAvailable, + [out] PULONG TotalReturned, + [out, switch_is(DisplayInformationClass)] + PSAMPR_DISPLAY_INFO_BUFFER Buffer + ); + +// +// Don't call the following api with an InfoClass level beyond +// DomainDisplayMachine. This is the highest info level supported +// in NT1.0, and RPC didn't raise an exception when an invalid +// info level was passed, so the server side stub tries to unmarshal +// the [out] parameter (which isn't there) and can access violate. +// +// CALL SamrGetDisplayEnumerationIndex2() instead (see end of this file) +// + +NTSTATUS +SamrGetDisplayEnumerationIndex ( + [in] SAMPR_HANDLE DomainHandle, + [in] DOMAIN_DISPLAY_INFORMATION DisplayInformationClass, + [in] PRPC_UNICODE_STRING Prefix, + [out] PULONG Index + ); + + +// +// Test APIs. +// These only do anything on special builds. +// + +NTSTATUS +SamrTestPrivateFunctionsDomain ( + [in] SAMPR_HANDLE DomainHandle + ); + +NTSTATUS +SamrTestPrivateFunctionsUser ( + [in] SAMPR_HANDLE UserHandle + ); + + +// +// The following is only for use by WRAPPERS.C, although there's no +// harm done if an application calls it. +// + +NTSTATUS +SamrGetUserDomainPasswordInformation ( + [in] SAMPR_HANDLE UserHandle, + [out] PUSER_DOMAIN_PASSWORD_INFORMATION PasswordInformation + ); + +// +// API added late, so we want it at the end. +// + +NTSTATUS +SamrRemoveMemberFromForeignDomain ( + [in] SAMPR_HANDLE DomainHandle, + [in] PRPC_SID MemberSid + ); + +// +// API added for NT1.0A. +// These must be added at the end of the file to ensure operability +// with down-level systems. That is, the API number of all existing +// API must be kept the same as it was. +// + +NTSTATUS +SamrQueryInformationDomain2( // Added for NT1.0A + [in] SAMPR_HANDLE DomainHandle, + [in] DOMAIN_INFORMATION_CLASS DomainInformationClass, + [out, switch_is(DomainInformationClass)] + PSAMPR_DOMAIN_INFO_BUFFER *Buffer + ); + +NTSTATUS +SamrQueryInformationUser2( + [in] SAMPR_HANDLE UserHandle, + [in] USER_INFORMATION_CLASS UserInformationClass, + [out, switch_is(UserInformationClass)] + PSAMPR_USER_INFO_BUFFER *Buffer + ); + +NTSTATUS +SamrQueryDisplayInformation2 ( + [in] SAMPR_HANDLE DomainHandle, + [in] DOMAIN_DISPLAY_INFORMATION DisplayInformationClass, + [in] ULONG Index, + [in] ULONG EntryCount, + [in] ULONG PreferredMaximumLength, + [out] PULONG TotalAvailable, + [out] PULONG TotalReturned, + [out, switch_is(DisplayInformationClass)] + PSAMPR_DISPLAY_INFO_BUFFER Buffer + ); + +NTSTATUS +SamrGetDisplayEnumerationIndex2 ( + [in] SAMPR_HANDLE DomainHandle, + [in] DOMAIN_DISPLAY_INFORMATION DisplayInformationClass, + [in] PRPC_UNICODE_STRING Prefix, + [out] PULONG Index + ); + + +NTSTATUS +SamrCreateUser2InDomain( + [in] SAMPR_HANDLE DomainHandle, + [in] PRPC_UNICODE_STRING Name, + [in] ULONG AccountType, + [in] ACCESS_MASK DesiredAccess, + [out] SAMPR_HANDLE * UserHandle, + [out] PULONG GrantedAccess, + [out] PULONG RelativeId + ); + +NTSTATUS +SamrQueryDisplayInformation3 ( + [in] SAMPR_HANDLE DomainHandle, + [in] DOMAIN_DISPLAY_INFORMATION DisplayInformationClass, + [in] ULONG Index, + [in] ULONG EntryCount, + [in] ULONG PreferredMaximumLength, + [out] PULONG TotalAvailable, + [out] PULONG TotalReturned, + [out, switch_is(DisplayInformationClass)] + PSAMPR_DISPLAY_INFO_BUFFER Buffer + ); + +NTSTATUS +SamrAddMultipleMembersToAlias( + [in] SAMPR_HANDLE AliasHandle, + [in] PSAMPR_PSID_ARRAY MembersBuffer + ); + +NTSTATUS +SamrRemoveMultipleMembersFromAlias( + [in] SAMPR_HANDLE AliasHandle, + [in] PSAMPR_PSID_ARRAY MembersBuffer + ); + + +NTSTATUS +SamrOemChangePasswordUser2( + [in] handle_t BindingHandle, + [in,unique] PRPC_STRING ServerName, + [in] PRPC_STRING UserName, + [in,unique] PSAMPR_ENCRYPTED_USER_PASSWORD NewPasswordEncryptedWithOldLm, + [in,unique] PENCRYPTED_LM_OWF_PASSWORD OldLmOwfPassswordEncryptedWithNewLm + ); + +NTSTATUS +SamrUnicodeChangePasswordUser2( + [in] handle_t BindingHandle, + [in,unique] PRPC_UNICODE_STRING ServerName, + [in] PRPC_UNICODE_STRING UserName, + [in,unique] PSAMPR_ENCRYPTED_USER_PASSWORD NewPasswordEncryptedWithOldNt, + [in,unique] PENCRYPTED_NT_OWF_PASSWORD OldNtOwfPasswordEncryptedWithNewNt, + [in] BOOLEAN LmPresent, + [in,unique] PSAMPR_ENCRYPTED_USER_PASSWORD NewPasswordEncryptedWithOldLm, + [in,unique] PENCRYPTED_LM_OWF_PASSWORD OldLmOwfPassswordEncryptedWithNewLmOrNt + ); + +NTSTATUS +SamrGetDomainPasswordInformation ( + [in] handle_t BindingHandle, + [in,unique] PRPC_UNICODE_STRING ServerName, + [out] PUSER_DOMAIN_PASSWORD_INFORMATION PasswordInformation + ); + +NTSTATUS +SamrConnect2( + [in,unique,string] PSAMPR_SERVER_NAME ServerName, + [out] SAMPR_HANDLE * ServerHandle, + [in] ACCESS_MASK DesiredAccess + ); + + +NTSTATUS +SamrSetInformationUser2( + [in] SAMPR_HANDLE UserHandle, + [in] USER_INFORMATION_CLASS UserInformationClass, + [in, switch_is(UserInformationClass)] + PSAMPR_USER_INFO_BUFFER Buffer + ); + +} + + diff --git a/private/newsam2/samsrv.acf b/private/newsam2/samsrv.acf new file mode 100644 index 000000000..2da84a2ec --- /dev/null +++ b/private/newsam2/samsrv.acf @@ -0,0 +1,64 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + samsrv.acf + +Abstract: + + Security Account Manager SERVER rpc stub attribute configuration file. + + This file contains the attribute configuration information necessary + for generating the server stubs for remotable SAM functions. The + definitions in this file qualify the information in samrpc.idl. + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! !! + !! This .acf file is USED ONLY WHEN GENERATING SAM SERVER STUBS. !! + !! !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + + Use samcli.acf when generating client stubs. + + + + + The server likes to have all passed data in a single block of + allocated memory. This allows it to free the returned information + with a single call, rather than walking down some random tree of + allocated blocks. + + + +Author: + + Jim Kelly (JimK) July 3, 1991 + +Environment: + + User Mode + +Revision History: + +--*/ + + + +[ implicit_handle( handle_t samsrv_handle) ] +interface samr + +{ + + +// +// define complex [in] parameters to be [allocate(all_nodes)]... +// + +//typedef [allocate(all_nodes)] PSAMPR_PASSED_NAMES; + +typedef [allocate(all_nodes)] PSAMPR_SID_INFORMATION; + +} diff --git a/private/newsam2/server/alias.c b/private/newsam2/server/alias.c new file mode 100644 index 000000000..45f2c7480 --- /dev/null +++ b/private/newsam2/server/alias.c @@ -0,0 +1,4284 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + alias.c + +Abstract: + + This file contains services related to the SAM "alias" object. + + +Author: + + Chad Schwitters (chads) 15-Jan-1992 + +Environment: + + User Mode - Win32 + +Revision History: + + 7-1-96 - MURLIS - Modified to Use DS. + + +--*/ + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + + + +#include <samsrvp.h> +#include <msaudite.h> +#include <dslayer.h> +#include <dsmember.h> + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// private service prototypes // +// // +/////////////////////////////////////////////////////////////////////////////// + +NTSTATUS +SampAddAccountToAlias( + IN PSAMP_OBJECT AccountContext, + IN PSID AccountSid + ); + +NTSTATUS +SampRemoveAccountFromAlias( + IN PSAMP_OBJECT AccountContext, + IN PSID AccountSid + ); + +NTSTATUS +SampAddAliasToAccountMembership( + IN ULONG AliasRid, + IN PSID AccountSid + ); + +NTSTATUS +SampRemoveAliasFromAccountMembership( + IN ULONG AliasRid, + IN PSID AccountSid + ); + +NTSTATUS +SampRemoveAliasFromAllAccounts( + IN PSAMP_OBJECT AliasContext + ); + +NTSTATUS +SampDeleteAliasKeys( + IN PSAMP_OBJECT Context + ); + +NTSTATUS +SampRetrieveAliasMembers( + IN PSAMP_OBJECT AliasContext, + IN PULONG MemberCount, + IN PSID **Members OPTIONAL + ); + +NTSTATUS +SampDeleteAliasMembershipKeysForAccount( + IN PSID AccountSid + ); + +NTSTATUS +SampAdjustAliasDomainsCount( + IN BOOLEAN Increment + ); + +NTSTATUS +SampValidateNewAliasMember( + IN PSID MemberId + ); + +NTSTATUS +SampChangeAliasAccountName( + IN PSAMP_OBJECT Context, + IN PUNICODE_STRING NewAccountName, + OUT PUNICODE_STRING OldAccountName + ); + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Exposed RPC'able Services // +// // +/////////////////////////////////////////////////////////////////////////////// + + + + +NTSTATUS +SamrOpenAlias( + IN SAM_HANDLE DomainHandle, + IN ACCESS_MASK DesiredAccess, + IN ULONG AliasId, + OUT PSAM_HANDLE AliasHandle + ) + +/*++ + +Routine Description: + + This API opens an existing Alias object. The Alias is specified by + a ID value that is relative to the SID of the domain. The operations + that will be performed on the Alias must be declared at this time. + + This call returns a handle to the newly opened Alias that may be used + for successive operations on the Alias. This handle may be closed + with the SamCloseHandle API. + + +Parameters: + + DomainHandle - A domain handle returned from a previous call to + SamOpenDomain. + + DesiredAccess - Is an access mask indicating which access types are + desired to the alias. + + AliasId - Specifies the relative ID value of the Alias to be opened. + + AliasHandle - Receives a handle referencing the newly opened Alias. + This handle will be required in successive calls to operate on + the Alias. + +Return Values: + + STATUS_SUCCESS - The Alias was successfully opened. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate access + to complete the operation. + + STATUS_NO_SUCH_ALIAS - The specified Alias does not exist. + + STATUS_INVALID_HANDLE - The domain handle passed is invalid. + + +--*/ +{ + NTSTATUS NtStatus; + + SAMTRACE("SamrOpenAlias"); + + NtStatus = SampOpenAccount( + SampAliasObjectType, + DomainHandle, + DesiredAccess, + AliasId, + FALSE, + AliasHandle + ); + + return(NtStatus); +} + + + +NTSTATUS +SamrQueryInformationAlias( + IN SAMPR_HANDLE AliasHandle, + IN ALIAS_INFORMATION_CLASS AliasInformationClass, + OUT PSAMPR_ALIAS_INFO_BUFFER *Buffer + ) + +/*++ + +Routine Description: + + This API retrieves information on the alias specified. + + + +Parameters: + + AliasHandle - The handle of an opened alias to operate on. + + AliasInformationClass - Class of information to retrieve. The + accesses required for each class is shown below: + + Info Level Required Access Type + ----------------------- ---------------------- + + AliasGeneralInformation ALIAS_READ_INFORMATION + AliasNameInformation ALIAS_READ_INFORMATION + AliasAdminInformation ALIAS_READ_INFORMATION + + Buffer - Receives a pointer to a buffer containing the requested + information. When this information is no longer needed, this + buffer and any memory pointed to through this buffer must be + freed using SamFreeMemory(). + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_INVALID_INFO_CLASS - The class provided was invalid. + +--*/ +{ + + NTSTATUS NtStatus; + NTSTATUS IgnoreStatus; + PSAMP_OBJECT AccountContext; + SAMP_OBJECT_TYPE FoundType; + ACCESS_MASK DesiredAccess; + ULONG i; + + // + // Used for tracking allocated blocks of memory - so we can deallocate + // them in case of error. Don't exceed this number of allocated buffers. + // || + // vv + PVOID AllocatedBuffer[10]; + ULONG AllocatedBufferCount = 0; + + SAMTRACE("SamrQueryInformationAlias"); + + #define RegisterBuffer(Buffer) \ + { \ + if ((Buffer) != NULL) { \ + \ + ASSERT(AllocatedBufferCount < \ + sizeof(AllocatedBuffer) / sizeof(*AllocatedBuffer)); \ + \ + AllocatedBuffer[AllocatedBufferCount++] = (Buffer); \ + } \ + } + + #define AllocateBuffer(NewBuffer, Size) \ + { \ + (NewBuffer) = MIDL_user_allocate(Size); \ + RegisterBuffer(NewBuffer); \ + } \ + + + + // + // Make sure we understand what RPC is doing for (to) us. + // + + ASSERT (Buffer != NULL); + ASSERT ((*Buffer) == NULL); + + + + // + // Set the desired access based upon the Info class + // + + switch (AliasInformationClass) { + + case AliasGeneralInformation: + case AliasNameInformation: + case AliasAdminCommentInformation: + + DesiredAccess = ALIAS_READ_INFORMATION; + break; + + default: + (*Buffer) = NULL; + return(STATUS_INVALID_INFO_CLASS); + } // end_switch + + + + // + // Allocate the info structure + // + + AllocateBuffer( *Buffer, sizeof(SAMPR_ALIAS_INFO_BUFFER) ); + if ((*Buffer) == NULL) { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + + SampAcquireReadLock(); + + + // + // Validate type of, and access to object. + // + + AccountContext = (PSAMP_OBJECT)AliasHandle; + NtStatus = SampLookupContext( + AccountContext, + DesiredAccess, + SampAliasObjectType, // ExpectedType + &FoundType + ); + + + if (NT_SUCCESS(NtStatus)) { + + // + // case on the type information requested + // + + switch (AliasInformationClass) { + + case AliasGeneralInformation: + + // + // Get the member count + // + + NtStatus = SampRetrieveAliasMembers( + AccountContext, + &(*Buffer)->General.MemberCount, + NULL // Only need members + ); + if (NT_SUCCESS(NtStatus)) { + + // + // Get copies of the strings we must retrieve from + // the registry. + // + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_ALIAS_NAME, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->General.Name) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->General.Name.Buffer); + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_ALIAS_ADMIN_COMMENT, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->General.AdminComment) + ); + + if (NT_SUCCESS(NtStatus)) { + RegisterBuffer((*Buffer)->General.AdminComment.Buffer); + } + } + } + + + break; + + + case AliasNameInformation: + + // + // Get copies of the strings we must retrieve from + // the registry. + // + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_ALIAS_NAME, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->Name.Name) + ); + + if (NT_SUCCESS(NtStatus)) { + RegisterBuffer((*Buffer)->Name.Name.Buffer); + } + + break; + + + case AliasAdminCommentInformation: + + // + // Get copies of the strings we must retrieve from + // the registry. + // + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_ALIAS_ADMIN_COMMENT, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->AdminComment.AdminComment) + ); + + if (NT_SUCCESS(NtStatus)) { + RegisterBuffer((*Buffer)->AdminComment.AdminComment.Buffer); + } + + + break; + + } // end_switch + + + // + // De-reference the object, discard any changes + // + + IgnoreStatus = SampDeReferenceContext( AccountContext, FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + // + // Free the read lock + // + + SampReleaseReadLock(); + + + + // + // If we didn't succeed, free any allocated memory + // + + if (!NT_SUCCESS(NtStatus)) { + for ( i=0; i<AllocatedBufferCount ; i++ ) { + MIDL_user_free( AllocatedBuffer[i] ); + } + + (*Buffer) = NULL; + } + + return(NtStatus); +} + + + +NTSTATUS +SamrSetInformationAlias( + IN SAMPR_HANDLE AliasHandle, + IN ALIAS_INFORMATION_CLASS AliasInformationClass, + IN PSAMPR_ALIAS_INFO_BUFFER Buffer + ) + +/*++ + +Routine Description: + + This API allows the caller to modify alias information. + + +Parameters: + + AliasHandle - The handle of an opened alias to operate on. + + AliasInformationClass - Class of information to retrieve. The + accesses required for each class is shown below: + + Info Level Required Access Type + ------------------------ ------------------------- + + AliasGeneralInformation (can't write) + + AliasNameInformation ALIAS_WRITE_ACCOUNT + AliasAdminCommentInformation ALIAS_WRITE_ACCOUNT + + Buffer - Buffer where information retrieved is placed. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_INFO_CLASS - The class provided was invalid. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_NO_SUCH_ALIAS - The alias specified is unknown. + + STATUS_SPECIAL_ALIAS - The alias specified is a special alias and + cannot be operated on in the requested fashion. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled for this + operation + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. + +--*/ +{ + + NTSTATUS NtStatus; + NTSTATUS TmpStatus; + NTSTATUS IgnoreStatus; + + PSAMP_OBJECT AccountContext; + + SAMP_OBJECT_TYPE FoundType; + + PSAMP_DEFINED_DOMAINS Domain; + + ACCESS_MASK DesiredAccess; + + UNICODE_STRING OldAccountName; + + ULONG AliasRid, + DomainIndex; + + BOOLEAN Modified = FALSE; + + SAMTRACE("SamrSetInformationAlias"); + + + OldAccountName.Buffer = NULL; + + + // + // Make sure we understand what RPC is doing for (to) us. + // + + if (Buffer == NULL) { + return(STATUS_INVALID_PARAMETER); + } + + + + // + // Set the desired access based upon the Info class + // + + switch (AliasInformationClass) { + + case AliasNameInformation: + case AliasAdminCommentInformation: + + DesiredAccess = ALIAS_WRITE_ACCOUNT; + break; + + + case AliasGeneralInformation: + default: + + return(STATUS_INVALID_INFO_CLASS); + + } // end_switch + + + + // + // Grab the lock + // + + NtStatus = SampAcquireWriteLock(); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + + // + // Validate type of, and access to object. + // + + AccountContext = (PSAMP_OBJECT)AliasHandle; + NtStatus = SampLookupContext( + AccountContext, + DesiredAccess, + SampAliasObjectType, // ExpectedType + &FoundType + ); + + + + if (NT_SUCCESS(NtStatus)) { + + // + // Get a pointer to the domain this object is in. + // This is used for auditing. + // + + DomainIndex = AccountContext->DomainIndex; + Domain = &SampDefinedDomains[ DomainIndex ]; + + // + // case on the type information requested + // + + switch (AliasInformationClass) { + + case AliasNameInformation: + + NtStatus = SampChangeAliasAccountName( + AccountContext, + (PUNICODE_STRING)&(Buffer->Name.Name), + &OldAccountName + ); + + if (!NT_SUCCESS(NtStatus)) { + OldAccountName.Buffer = NULL; + } + + // + // Don't delete the old account name yet; we'll still need + // to pass it to Netlogon below. + // + + break; + + + case AliasAdminCommentInformation: + + NtStatus = SampSetUnicodeStringAttribute( + AccountContext, + SAMP_ALIAS_ADMIN_COMMENT, + (PUNICODE_STRING)&(Buffer->AdminComment.AdminComment) + ); + + break; + + + } // end_switch + + + // + // Generate an audit if necessary + // + + if ((NT_SUCCESS(NtStatus) && + SampDoAccountAuditing(DomainIndex))) { + + UNICODE_STRING + AccountName; + + IgnoreStatus = SampGetUnicodeStringAttribute( + AccountContext, // Context + SAMP_ALIAS_NAME, // AttributeIndex + FALSE, // MakeCopy + &AccountName // UnicodeAttribute + ); + if (NT_SUCCESS(IgnoreStatus)) { + LsaIAuditSamEvent( + STATUS_SUCCESS, + SE_AUDITID_LOCAL_GROUP_CHANGE, // AuditId + Domain->Sid, // Domain SID + NULL, // Member Rid (not used) + NULL, // Member Sid (not used) + &AccountName, // Account Name + &Domain->ExternalName, // Domain + &AccountContext->TypeBody.Alias.Rid, // Account Rid + NULL // Privileges used + ); + } + + } + + + // + // Dereference the account context + // + + if (NT_SUCCESS(NtStatus)) { + + // + // Save object RID before dereferencing context. + // RID is used in SampNotifyNetlogonOfDelta() call. + // + + AliasRid = AccountContext->TypeBody.Alias.Rid; + + // + // De-reference the object, write out any change to current xaction. + // + + NtStatus = SampDeReferenceContext( AccountContext, TRUE ); + + } else { + + // + // De-reference the object, ignore changes + // + + TmpStatus = SampDeReferenceContext( AccountContext, FALSE ); + ASSERT(NT_SUCCESS(TmpStatus)); + } + + } //end_if + + // + // Commit the transaction and notify netlogon of any changes + // + + if ( NT_SUCCESS(NtStatus) ) { + + NtStatus = SampCommitAndRetainWriteLock(); + + if ( NT_SUCCESS(NtStatus) ) { + + if ( AliasInformationClass == AliasNameInformation ) { + + SampNotifyNetlogonOfDelta( + SecurityDbRename, + SecurityDbObjectSamAlias, + AliasRid, + &OldAccountName, + (DWORD) FALSE, // Replicate immediately + NULL // Delta data + ); + + } else { + + SampNotifyNetlogonOfDelta( + SecurityDbChange, + SecurityDbObjectSamAlias, + AliasRid, + NULL, + (DWORD) FALSE, // Replicate immediately + NULL // Delta data + ); + } + } + } + + + // + // Free up our old account name if we have one + // + + SampFreeUnicodeString( &OldAccountName ); + + + // + // Now release the write lock and return, propogating any errors. + // + + TmpStatus = SampReleaseWriteLock( FALSE ); + ASSERT(NT_SUCCESS(TmpStatus)); + + + if (NT_SUCCESS(NtStatus)) { + NtStatus = TmpStatus; + } + + return(NtStatus); + +} + + + +NTSTATUS +SamrDeleteAlias( + IN SAM_HANDLE *AliasHandle + ) + +/*++ + +Routine Description: + + This API deletes an Alias from the account database. The Alias does + not have to be empty. + + Note that following this call, the AliasHandle is no longer valid. + + + +Parameters: + + AliasHandle - The handle of an opened Alias to operate on. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled for this + operation + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. + + + +--*/ +{ + UNICODE_STRING AliasName; + NTSTATUS NtStatus, TmpStatus, IgnoreStatus; + PSAMP_OBJECT AccountContext; + PSAMP_DEFINED_DOMAINS Domain; + SAMP_OBJECT_TYPE FoundType; + ULONG AliasRid, + DomainIndex; + + + SAMTRACE("SamrDeleteAlias"); + + + + // + // Grab the lock + // + + NtStatus = SampAcquireWriteLock(); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + + // + // Validate type of, and access to object. + // + + AccountContext = (PSAMP_OBJECT)(*AliasHandle); + + NtStatus = SampLookupContext( + AccountContext, + DELETE, + SampAliasObjectType, // ExpectedType + &FoundType + ); + + if (NT_SUCCESS(NtStatus)) { + + AliasRid = AccountContext->TypeBody.Alias.Rid; + + // + // Get a pointer to the domain this object is in. + // This is used for auditing. + // + + DomainIndex = AccountContext->DomainIndex; + Domain = &SampDefinedDomains[ DomainIndex ]; + + // + // Make sure the account is one that can be deleted. + // Can't be a built-in account, unless caller is trusted. + // + + if ( !AccountContext->TrustedClient ) { + + NtStatus = SampIsAccountBuiltIn( AliasRid ); + } + + if (NT_SUCCESS(NtStatus)) { + + // + // Remove this alias from every account's alias-membership list + // + + NtStatus = SampRemoveAliasFromAllAccounts(AccountContext); + + + if (NT_SUCCESS(NtStatus)) { + + // + // First get and save the account name for + // I_NetNotifyLogonOfDelta. + // + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_ALIAS_NAME, + TRUE, // Make copy + &AliasName + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // This must be done before we invalidate contexts, because our + // own handle to the alias gets closed as well. + // + + NtStatus = SampDeleteAliasKeys( AccountContext ); + + if (NT_SUCCESS(NtStatus)) { + + // + // We must invalidate any open contexts to this alias + // This will close all handles to the alias's keys. + // THIS IS AN IRREVERSIBLE PROCESS. + // + + SampInvalidateAliasContexts( AliasRid ); + + // + // Commit the whole mess + // + + NtStatus = SampCommitAndRetainWriteLock(); + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // Update the Alias Information Cache + // + + IgnoreStatus = SampAlDeleteAlias( AliasHandle ); + + // + // Audit the deletion before we free the write lock + // so that we have access to the context block. + // + + if (SampDoAccountAuditing(DomainIndex) && + NT_SUCCESS(NtStatus) ) { + + LsaIAuditSamEvent( + STATUS_SUCCESS, + SE_AUDITID_LOCAL_GROUP_DELETED, // AuditId + Domain->Sid, // Domain SID + NULL, // Member Rid (not used) + NULL, // Member sid (not used) + &AliasName, // Account Name + &Domain->ExternalName, // Domain + &AliasRid, // Account Rid + NULL // Privileges used + ); + + } + + // + // Notify netlogon of the change + // + + SampNotifyNetlogonOfDelta( + SecurityDbDelete, + SecurityDbObjectSamAlias, + AliasRid, + &AliasName, + (DWORD) FALSE, // Replicate immediately + NULL // Delta data + ); + + // + // Do delete auditing + // + + if (NT_SUCCESS(NtStatus)) { + (VOID) NtDeleteObjectAuditAlarm( + &SampSamSubsystem, + *AliasHandle, + AccountContext->AuditOnClose + ); + } + + + } + } + + SampFreeUnicodeString( &AliasName ); + } + } + } + + + + // + // De-reference the object, discard any changes + // + + TmpStatus = SampDeReferenceContext( AccountContext, FALSE ); + ASSERT(NT_SUCCESS(TmpStatus)); + + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // If we actually deleted the alias, then delete the context + // and let RPC know that the handle is invalid. + // + + SampDeleteContext( AccountContext ); + + (*AliasHandle) = NULL; + } + + } //end_if + + // + // Free the lock - + // + // Everything has already been committed above, so we must indicate + // no additional changes have taken place. + // + + TmpStatus = SampReleaseWriteLock( FALSE ); + + if (NtStatus == STATUS_SUCCESS) { + NtStatus = TmpStatus; + } + + return(NtStatus); + +} + + +NTSTATUS +SamrAddMemberToAlias( + IN SAMPR_HANDLE AliasHandle, + IN PRPC_SID MemberId + ) + +/*++ + +Routine Description: + + This API adds a member to an alias. Note that this API requires the + ALIAS_ADD_MEMBER access type for the alias. + + +Parameters: + + AliasHandle - The handle of an opened alias to operate on. + + MemberId - SID of the member to add. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_NO_SUCH_MEMBER - The member specified is unknown. + + STATUS_MEMBER_IN_ALIAS - The member already belongs to the alias. + + STATUS_INVALID_MEMBER - The member has the wrong account type. + + STATUS_INVALID_SID - The member sid is corrupted. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled for this + operation + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. + +--*/ +{ + + NTSTATUS NtStatus, TmpStatus, IgnoreStatus; + PSAMP_OBJECT AccountContext; + SAMP_OBJECT_TYPE FoundType; + ULONG ObjectRid; + SAMP_MEMBERSHIP_DELTA AdminChange = NoChange; + SAMP_MEMBERSHIP_DELTA OperatorChange = NoChange; + + SAMTRACE("SamrAddMemberToAlias"); + + + // + // Grab the lock + // + + NtStatus = SampAcquireWriteLock(); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + + + // + // Validate type of, and access to object. + // + + AccountContext = (PSAMP_OBJECT)(AliasHandle); + NtStatus = SampLookupContext( + AccountContext, + ALIAS_ADD_MEMBER, + SampAliasObjectType, // ExpectedType + &FoundType + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Check the potential new member is OK + // + + NtStatus = SampValidateNewAliasMember(MemberId); + + // + // If the member is being added to an ADMIN alias, we must make + // sure the member ACL(s) don't allow access by account operators. + // + + if ( NT_SUCCESS( NtStatus ) ) { + if ( AccountContext->TypeBody.Alias.Rid == DOMAIN_ALIAS_RID_ADMINS ) { + + AdminChange = AddToAdmin; + + } else if ( ( AccountContext->TypeBody.Alias.Rid == DOMAIN_ALIAS_RID_SYSTEM_OPS ) || + ( AccountContext->TypeBody.Alias.Rid == DOMAIN_ALIAS_RID_PRINT_OPS ) || + ( AccountContext->TypeBody.Alias.Rid == DOMAIN_ALIAS_RID_BACKUP_OPS ) || + ( AccountContext->TypeBody.Alias.Rid == DOMAIN_ALIAS_RID_ACCOUNT_OPS ) ) { + + OperatorChange = AddToAdmin; + } + + // + // If either of these are changing, change account operator + // access to this member + // + + if ( ( OperatorChange != NoChange ) || + ( AdminChange != NoChange ) ) { + + NtStatus = SampChangeAccountOperatorAccessToMember( + MemberId, + AdminChange, + OperatorChange + ); + } + + } + + if (NT_SUCCESS(NtStatus)) { + + // + // Perform the user object side of things + // + + NtStatus = SampAddAliasToAccountMembership( + AccountContext->TypeBody.Alias.Rid, + MemberId + ); + + + // + // Now perform the alias side of things + // + + if (NT_SUCCESS(NtStatus)) { + + // + // Add the user to the alias (should not fail) + // + + NtStatus = SampAddAccountToAlias( + AccountContext, + MemberId + ); + } + } + + + + // + // Dereference the account context + // + + if (NT_SUCCESS(NtStatus)) { + + // + // Save object RID before dereferencing context. + // RID is used in SampNotifyNetlogonOfDelta() call. + // + + ObjectRid = AccountContext->TypeBody.Alias.Rid; + + // + // De-reference the object, write out any change to current xaction. + // + + NtStatus = SampDeReferenceContext( AccountContext, TRUE ); + + } else { + + // + // De-reference the object, ignore changes + // + + TmpStatus = SampDeReferenceContext( AccountContext, FALSE ); + ASSERT(NT_SUCCESS(TmpStatus)); + } + + + + + } + + if (NT_SUCCESS(NtStatus)) { + + // + // Commit the whole mess + // + + NtStatus = SampCommitAndRetainWriteLock(); + + if ( NT_SUCCESS( NtStatus ) ) { + + SAM_DELTA_DATA DeltaData; + + // + // Update the Alias Information Cache + // + + SAMPR_PSID_ARRAY MemberSids; + MemberSids.Count = 1; + MemberSids.Sids = (PSAMPR_SID_INFORMATION) &MemberId; + + IgnoreStatus = SampAlAddMembersToAlias( + AliasHandle, + 0, + &MemberSids + ); + + + // + // Fill in id of member being added + // + + DeltaData.AliasMemberId.MemberSid = MemberId; + + SampNotifyNetlogonOfDelta( + SecurityDbChangeMemberAdd, + SecurityDbObjectSamAlias, + ObjectRid, + (PUNICODE_STRING) NULL, + (DWORD) FALSE, // Replicate immediately + &DeltaData + ); + } + } + + TmpStatus = SampReleaseWriteLock( FALSE ); + ASSERT(NT_SUCCESS(TmpStatus)); + + return(NtStatus); +} + + + +NTSTATUS +SamrAddMultipleMembersToAlias( + IN SAMPR_HANDLE AliasHandle, + IN PSAMPR_PSID_ARRAY MembersBuffer + ) + +/*++ + +Routine Description: + + This api adds multiple members to an alias. + + NOTE: For now, this routine takes a brute force approach. + I tried to do it in a better (more efficient) manner, + but kept running into problems. Finally, when I ran + into problems in the way SAM uses RXACT, I gave up + and did this brute force approach. + +Parameters: + + AliasHandle - The handle of an opened Alias to operate on. + + MembersBuffer - Contains a count of SIDs to be added to the + alias and a pointer to a buffer containing an array of + pointers to SIDs. These SIDs are the SIDs of the members to + be added to the Alias. + + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. All of the + listed members are now members of the alias. However, some of + the members may already have been members of the alias (this is + NOT an error or warning condition). + + STATUS_ACCESS_DENIED - Caller does not have the object open for + the required access. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_INVALID_MEMBER - The member has the wrong account type. + + STATUS_INVALID_SID - The member sid is corrupted. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled for this + operation + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. + +--*/ +{ + + NTSTATUS + NtStatus; + + LONG + MemberCount, + i; + + PSID + *MemberId; + + SAMTRACE("SamrAddMultipleMembersToAlias"); + + MemberCount = (LONG)MembersBuffer->Count; + MemberId = (PSID *)MembersBuffer->Sids; + + + // + // Set completion status in case there are no members + // + + NtStatus = STATUS_SUCCESS; + + + // + // Loop through the SIDs, adding them to the alias. + // Ignore any status value indicating the member is already + // a member. Other errors, however, will cause us to abort. + // + + for (i=0; i<MemberCount; i++) { + + NtStatus = SamrAddMemberToAlias( AliasHandle, MemberId[i] ); + + if (NtStatus == STATUS_MEMBER_IN_ALIAS) { + NtStatus = STATUS_SUCCESS; + } + + if (!NT_SUCCESS(NtStatus)) { + break; //for loop + } + + } //end_for + + return(NtStatus); +} + + +NTSTATUS +SamrRemoveMemberFromAlias( + IN SAMPR_HANDLE AliasHandle, + IN PRPC_SID MemberId + ) + +/*++ + +Routine Description: + + This API removes a member from an alias. Note that this API requires the + ALIAS_REMOVE_MEMBER access type for the alias. + + +Parameters: + + AliasHandle - The handle of an opened alias to operate on. + + MemberId - SID of the member to remove. + +Return Value: + + + ???? + + +--*/ +{ + NTSTATUS NtStatus, TmpStatus, IgnoreStatus; + PSAMP_OBJECT AccountContext; + SAMP_OBJECT_TYPE FoundType; + ULONG ObjectRid; + SAMP_MEMBERSHIP_DELTA AdminChange = NoChange; + SAMP_MEMBERSHIP_DELTA OperatorChange = NoChange; + + SAMTRACE("SamrRemoveMemberFromAlias"); + + // + // Grab the lock + // + + NtStatus = SampAcquireWriteLock(); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + // + // Validate type of, and access to object. + // + + AccountContext = (PSAMP_OBJECT)(AliasHandle); + NtStatus = SampLookupContext( + AccountContext, + ALIAS_REMOVE_MEMBER, + SampAliasObjectType, // ExpectedType + &FoundType + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Validate the sid of the member. + // + + if ((MemberId == NULL) || !RtlValidSid(MemberId)) { + NtStatus = STATUS_INVALID_SID; + } + + if (NT_SUCCESS(NtStatus)) { + + // + // Perform the user object side of things + // + + NtStatus = SampRemoveAliasFromAccountMembership( + AccountContext->TypeBody.Alias.Rid, + (PSID)MemberId + ); + + + + // + // Now perform the alias side of things + // + + if (NT_SUCCESS(NtStatus)) { + + // + // Remove the user from the alias (should not fail) + // + + NtStatus = SampRemoveAccountFromAlias( + AccountContext, + (PSID)MemberId + ); + + // + // If the member is being removed from an ADMIN alias, we must make + // sure the member ACL(s) allow access by account operators. + // + + if ( NT_SUCCESS( NtStatus ) ) { + if ( AccountContext->TypeBody.Alias.Rid == DOMAIN_ALIAS_RID_ADMINS ) { + + AdminChange = RemoveFromAdmin; + + } else if ( ( AccountContext->TypeBody.Alias.Rid == DOMAIN_ALIAS_RID_SYSTEM_OPS ) || + ( AccountContext->TypeBody.Alias.Rid == DOMAIN_ALIAS_RID_PRINT_OPS ) || + ( AccountContext->TypeBody.Alias.Rid == DOMAIN_ALIAS_RID_BACKUP_OPS ) || + ( AccountContext->TypeBody.Alias.Rid == DOMAIN_ALIAS_RID_ACCOUNT_OPS ) ) { + + OperatorChange = RemoveFromAdmin; + } + + // + // If either of these are changing, change account operator + // access to this member + // + + if ( ( OperatorChange != NoChange ) || + ( AdminChange != NoChange ) ) { + + NtStatus = SampChangeAccountOperatorAccessToMember( + MemberId, + AdminChange, + OperatorChange + ); + } + + } + } + } + + // + // Dereference the account context + // + + if (NT_SUCCESS(NtStatus)) { + + // + // Save object RID before dereferencing context. + // RID is used in SampNotifyNetlogonOfDelta() call. + // + + ObjectRid = AccountContext->TypeBody.Alias.Rid; + + // + // De-reference the object, write out any change to current xaction. + // + + NtStatus = SampDeReferenceContext( AccountContext, TRUE ); + + } else { + + // + // De-reference the object, ignore changes + // + + TmpStatus = SampDeReferenceContext( AccountContext, FALSE ); + ASSERT(NT_SUCCESS(TmpStatus)); + } + + } + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampCommitAndRetainWriteLock(); + + if ( NT_SUCCESS( NtStatus ) ) { + + SAM_DELTA_DATA DeltaData; + + // + // Update the Alias Information Cache + // + + SAMPR_PSID_ARRAY MemberSids; + MemberSids.Count = 1; + MemberSids.Sids = (PSAMPR_SID_INFORMATION) &MemberId; + + IgnoreStatus = SampAlRemoveMembersFromAlias( + AliasHandle, + 0, + &MemberSids + ); + + + // + // Fill in id of member being deleted + // + + DeltaData.AliasMemberId.MemberSid = MemberId; + + SampNotifyNetlogonOfDelta( + SecurityDbChangeMemberDel, + SecurityDbObjectSamAlias, + ObjectRid, + (PUNICODE_STRING) NULL, + (DWORD) FALSE, // Replicate immediately + &DeltaData + ); + + } + } + + TmpStatus = SampReleaseWriteLock( FALSE ); + ASSERT(NT_SUCCESS(TmpStatus)); + + return(NtStatus); + +} + + +NTSTATUS +SamrRemoveMultipleMembersFromAlias( + IN SAMPR_HANDLE AliasHandle, + IN PSAMPR_PSID_ARRAY MembersBuffer + ) + +/*++ + +Routine Description: + + This API removes members from an alias. Note that this API requires + the ALIAS_REMOVE_MEMBER access type for the alias. + + NOTE: This api currently uses a brute-force approach to adding + members to the alias. This is because of problems + encountered when trying to do "the right thing". + + +Parameters: + + AliasHandle - The handle of an opened alias to operate on. + + MembersBuffer - Contains a count of SIDs to be added to the + alias and a pointer to a buffer containing an array of + pointers to SIDs. These SIDs are the SIDs of the members to + be added to the Alias. + + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. All of the + listed members are now members of the alias. However, some of + the members may already have been members of the alias (this is + NOT an error or warning condition). + + STATUS_ACCESS_DENIED - Caller does not have the object open for + the required access. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_INVALID_SID - The member sid is corrupted. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled for this + operation + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. + + +--*/ +{ + + NTSTATUS + NtStatus; + + LONG + MemberCount, + i; + + PSID + *MemberId; + + SAMTRACE("SamrRemoveMultipleMembersFromAlias"); + + MemberCount = (LONG)MembersBuffer->Count; + MemberId = (PSID *)MembersBuffer->Sids; + + + // + // Set completion status in case there are no members + // + + NtStatus = STATUS_SUCCESS; + + + // + // Loop through the SIDs, adding them to the alias. + // Ignore any status value indicating the member is already + // a member. Other errors, however, will cause us to abort. + // + + for (i=0; i<MemberCount; i++) { + + NtStatus = SamrAddMemberToAlias( AliasHandle, MemberId[i] ); + + if (NtStatus == STATUS_MEMBER_NOT_IN_ALIAS) { + NtStatus = STATUS_SUCCESS; + } + + if (!NT_SUCCESS(NtStatus)) { + break; //for loop + } + + } //end_for + + return(NtStatus); + +} + + +NTSTATUS +SamrGetMembersInAlias( + IN SAM_HANDLE AliasHandle, + OUT PSAMPR_PSID_ARRAY GetMembersBuffer + ) + +/*++ + +Routine Description: + + This API lists all members in an Alias. This API requires + ALIAS_LIST_MEMBERS access to the Alias. + + NOTE: This function does not use the Alias cache. + + +Parameters: + + AliasHandle - The handle of an opened Alias to operate on. + + MemberIds - Receives a pointer to a buffer containing an array of + pointers to SIDs. These SIDs are the SIDs of the members of the + Alias. When this information is no longer needed, this buffer + must be freed using SamFreeMemory(). + + MemberCount - number of members in the Alias (and, thus, the number + of relative IDs returned). + +Return Values: + + STATUS_SUCCESS - The Service completed successfully, and there are + no additional entries. + + STATUS_ACCESS_DENIED - Caller does not have privilege required to + request that data. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + +--*/ +{ + NTSTATUS NtStatus, IgnoreStatus; + PSAMP_OBJECT AccountContext; + SAMP_OBJECT_TYPE FoundType; + + SAMTRACE("SamrGetMembersInAlias"); + + // + // Make sure we understand what RPC is doing for (to) us. + // + + ASSERT (GetMembersBuffer != NULL); + + // + // Grab the lock + // + + SampAcquireReadLock(); + + + // + // Validate type of, and access to object. + // + + AccountContext = (PSAMP_OBJECT)AliasHandle; + NtStatus = SampLookupContext( + AccountContext, + ALIAS_LIST_MEMBERS, + SampAliasObjectType, // ExpectedType + &FoundType + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampRetrieveAliasMembers( + AccountContext, + &(GetMembersBuffer->Count), + (PSID **)&(GetMembersBuffer->Sids) + ); + + // + // De-reference the object, discarding changes + // + + IgnoreStatus = SampDeReferenceContext( AccountContext, FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + // + // Free the read lock + // + + SampReleaseReadLock(); + + + // + // Tidy up on failure + // + + if (!NT_SUCCESS(NtStatus)){ + + GetMembersBuffer->Count = 0; + GetMembersBuffer->Sids = NULL; + } + + return(NtStatus); +} + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Internal Services Available For Use in Other SAM Modules // +// // +/////////////////////////////////////////////////////////////////////////////// + + + +NTSTATUS +SampRemoveAccountFromAllAliases( + IN PSID AccountSid, + IN BOOLEAN CheckAccess, + IN SAMPR_HANDLE DomainHandle OPTIONAL, + IN PULONG MembershipCount OPTIONAL, + IN PULONG *Membership OPTIONAL + ) + +/*++ + +Routine Description: + + This routine removes the specified account from the member list of all + aliases in this domain. + + + The caller of this service is expected to be in the middle of a + RXACT transaction. This service simply adds some actions to that + RXACT transaction. + + This routine is used while deleting a user or a group and hence should + never be called on a DS domain. ( The DS will maintain the cross consi- + stency when a user or group is deleted ). + + +Arguments: + + AccountSid - The SID of the account being Removed. + + CheckAccess - if TRUE, this routine will make sure that the caller + is allowed REMOVE_ALIAS_MEMBER access to this alias. If FALSE, + the caller is already known to have proper access. + + DomainHandle - if CheckAccess is TRUE, this handle must be provided + to allow access to be checked. + + MembershipCount - if CheckAccess is TRUE, this pointer must be + provided to receive the number of aliases the account was + deleted from. + + Membership - if CheckAccess is TRUE, this pointer must be provided + to point to a list of aliases the account was removed from. The + caller must free this list with MIDL_user_free(). + +Return Value: + + + STATUS_SUCCESS - The user has been Removed from all aliases. + +--*/ +{ + NTSTATUS NtStatus, IgnoreStatus; + OBJECT_ATTRIBUTES ObjectAttributes; + UNICODE_STRING DomainKeyName, AccountKeyName; + HANDLE TempHandle, AliasHandle; + ULONG LocalMembershipCount; + PULONG LocalMembership; + ULONG KeyValueLength; + ULONG i; + PSAMP_OBJECT AliasContext; + + SAMTRACE("SampRemoveAccountFromAllAliases"); + + + // + // We should never get this call for a DS domain + // + + ASSERT(IsDsObject( + SampDefinedDomains[SampTransactionDomainIndex].Context + )== FALSE + ); + + // + // Get the alias membership for this account + // + + NtStatus = SampBuildAliasMembersKeyName( + AccountSid, + &DomainKeyName, + &AccountKeyName + ); + if (NT_SUCCESS(NtStatus)) { + + InitializeObjectAttributes( + &ObjectAttributes, + &AccountKeyName, + OBJ_CASE_INSENSITIVE, + SampKey, + NULL + ); + + SampDumpNtOpenKey((KEY_READ), &ObjectAttributes, 0); + + NtStatus = RtlpNtOpenKey( + &TempHandle, + (KEY_READ), + &ObjectAttributes, + 0 + ); + + if ((NtStatus == STATUS_OBJECT_PATH_NOT_FOUND) || + (NtStatus == STATUS_OBJECT_NAME_NOT_FOUND) ) { + + // + // This account is not a member of any of our aliases + // + + NtStatus = STATUS_SUCCESS; + + if ( CheckAccess ) { + + // + // Return the list of aliases the account was + // removed from; in this case, none. + // + + ( *MembershipCount ) = 0; + ( *Membership ) = NULL; + } + + } else { + + // + // Load in the alias membership list + // + + if (NT_SUCCESS(NtStatus)) { + + KeyValueLength = 0; + + NtStatus = RtlpNtQueryValueKey( TempHandle, + &LocalMembershipCount, + NULL, + &KeyValueLength, + NULL); + + SampDumpRtlpNtQueryValueKey(&LocalMembershipCount, + NULL, + &KeyValueLength, + NULL); + + if (NT_SUCCESS(NtStatus)) { + ASSERT(LocalMembershipCount == 0); + } + + if (NtStatus == STATUS_BUFFER_OVERFLOW) { + + LocalMembership = MIDL_user_allocate( KeyValueLength ); + + if (LocalMembership == NULL) { + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + } else { + + NtStatus = RtlpNtQueryValueKey( + TempHandle, + NULL, + LocalMembership, + &KeyValueLength, + NULL); + + SampDumpRtlpNtQueryValueKey(NULL, + LocalMembership, + &KeyValueLength, + NULL); + + if (NT_SUCCESS(NtStatus)) { + + // + // Remove the account from each alias + // + + for (i=0; i < LocalMembershipCount; i++) { + + if ( CheckAccess ) { + + // + // If account is being removed from + // the ADMIN alias, change ACL to + // allow account operators to access + // the account (unless account is an + // admin some other way). Kind of + // useless since the account is about + // to be deleted, but do it anyway + // in case something bad happens and + // it doesn't get deleted. + // + + // + // BUGBUG: this may not do it - we may + // need to check the admin count on + // the group. MMS 9/5/95 + // + + if ( LocalMembership[i] == + DOMAIN_ALIAS_RID_ADMINS ) { + + NtStatus = SampChangeAccountOperatorAccessToMember( + AccountSid, + RemoveFromAdmin, + NoChange ); + } + + // + // Just open and close the alias + // to make sure we are allowed + // the necessary access. + // + + SampTransactionWithinDomain = FALSE; + + NtStatus = SampOpenAccount( + SampAliasObjectType, + DomainHandle, + ALIAS_REMOVE_MEMBER, + LocalMembership[i], + TRUE, + (SAMPR_HANDLE *)&AliasHandle + ); + + if (NT_SUCCESS(NtStatus)) { + + SampDeleteContext( + (PSAMP_OBJECT)( AliasHandle ) ); + } + } + + if (!NT_SUCCESS(NtStatus)) { + break; + } + + NtStatus = SampCreateAccountContext( + SampAliasObjectType, + LocalMembership[i], + TRUE, // Trusted client + TRUE, // Account exists + &AliasContext + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampRemoveAccountFromAlias( + AliasContext, + AccountSid ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Save the alias changes we just + // made. We'll delete the context, + // so don't let RXACT use the open + // key handle in the context. + // + + NtStatus = SampStoreObjectAttributes( + AliasContext, + FALSE + ); + } + + SampDeleteContext(AliasContext); + } + + if (!NT_SUCCESS(NtStatus)) { + break; + } + } + + // + // Delete the account membership keys + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampDeleteAliasMembershipKeysForAccount( + AccountSid); + } + + } + + if ( CheckAccess ) { + + // + // Return the list of aliases the account was + // removed from. + // + + ( *MembershipCount ) = LocalMembershipCount; + ( *Membership ) = LocalMembership; + + } else { + + MIDL_user_free(LocalMembership); + } + } + } + + IgnoreStatus = NtClose( TempHandle ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + } + } + + SampFreeUnicodeString( &DomainKeyName ); + SampFreeUnicodeString( &AccountKeyName ); + + } + + return( NtStatus ); +} + + + + +NTSTATUS +SampRetrieveAliasMembership( + IN PSID Account, + OUT PULONG MemberCount OPTIONAL, + IN OUT PULONG BufferSize OPTIONAL, + OUT PULONG Buffer OPTIONAL + ) + +/*++ +Routine Description: + + This service retrieves the number of aliases in the current domain + that the specified account is a member of. If desired it will also fill + in a buffer with the alias rids. + + + Note: THIS ROUTINE REFERENCES THE CURRENT TRANSACTION DOMAIN + (ESTABLISHED USING SampSetTransactioDomain()). THIS + SERVICE MAY ONLY BE CALLED AFTER SampSetTransactionDomain() + AND BEFORE SampReleaseWriteLock(). + + +Arguments: + + Account - the account whose membership we are interested in. + + MemberCount - Receives the number of current-domain-aliases the + account is a member of. + + BufferSize - (Optional) Specified the size of memory pointer to by buffer. + + Buffer - (Otional) Is filled in with the list of alias membership rids. + If this value is NULL, then this information + is not returned. The returned buffer is allocated using + MIDL_user_allocate() and must be freed using MIDL_user_free() when + no longer needed. + +Return Value: + + + STATUS_SUCCESS - The information has been retrieved. + + STATUS_INSUFFICIENT_RESOURCES - Memory could not be allocated for the + string to be returned in. + + Other status values that may be returned are those returned + by: + + NtOpenKey() + NtQueryValueKey() + + + +--*/ +{ + + NTSTATUS NtStatus, IgnoreStatus; + OBJECT_ATTRIBUTES ObjectAttributes; + UNICODE_STRING DomainKeyName, AccountKeyName; + HANDLE TempHandle; + PSAMP_OBJECT DomainContext; + + SAMTRACE("SampRetrieveAliasMembership"); + + + DomainContext = SampDefinedDomains[SampTransactionDomainIndex].Context; + if (IsDsObject(DomainContext)) + { + // + // DS Case + // + + NtStatus = SampDsGetAliasMembershipOfAccount( + DomainContext->ObjectNameInDs, + Account, + MemberCount, + BufferSize, + Buffer + ); + } + else + { + // + // Get the membership count for this account + // + + NtStatus = SampBuildAliasMembersKeyName( + Account, + &DomainKeyName, + &AccountKeyName + ); + if (NT_SUCCESS(NtStatus)) { + + InitializeObjectAttributes( + &ObjectAttributes, + &AccountKeyName, + OBJ_CASE_INSENSITIVE, + SampKey, + NULL + ); + + SampDumpNtOpenKey((KEY_READ), &ObjectAttributes, 0); + + NtStatus = RtlpNtOpenKey( + &TempHandle, + (KEY_READ), + &ObjectAttributes, + 0 + ); + + if ((NtStatus == STATUS_OBJECT_PATH_NOT_FOUND) || + (NtStatus == STATUS_OBJECT_NAME_NOT_FOUND) ) { + + // + // This account is not a member of any of our aliases + // + + NtStatus = STATUS_SUCCESS; + + if (ARGUMENT_PRESENT(MemberCount)) { + *MemberCount = 0; + } + if (ARGUMENT_PRESENT(BufferSize)) { + *BufferSize = 0; + } + + } else { + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = RtlpNtQueryValueKey( TempHandle, + MemberCount, + Buffer, + BufferSize, + NULL); + + SampDumpRtlpNtQueryValueKey(MemberCount, + Buffer, + BufferSize, + NULL); + + IgnoreStatus = NtClose( TempHandle ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + } + } + + SampFreeUnicodeString( &DomainKeyName ); + SampFreeUnicodeString( &AccountKeyName ); + + } + } + + return( NtStatus ); + +} + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Services Private to this file // +// // +/////////////////////////////////////////////////////////////////////////////// + + + + +NTSTATUS +SampAddAccountToAlias( + IN PSAMP_OBJECT AccountContext, + IN PSID AccountSid + ) + +/*++ + +Routine Description: + + This service is used to add an account as a member of a specified alias + This is done by simply adding the account SID to the list of SIDs + in the MEMBERS attribute of the the specified alias + + + The caller of this service is expected to be in the middle of a + RXACT transaction. This service simply edits the in-memory copy of + the alias information. + + +Arguments: + + AccountContext - Context block Describing the Alias + + AccountSid - The Sid of the account being added as a new member. + +Return Value: + + STATUS_SUCCESS - The account was added. + +--*/ + +{ + NTSTATUS NtStatus; + ULONG MemberCount, i; + ULONG MemberArraySize; + PSID MemberArray; + + SAMTRACE("SampAddAccountToAlias"); + + + // + // Need to do different things for DS and Registry + // + + if (IsDsObject(AccountContext)) + { + DSNAME * DsNameOfAccount = NULL; + // + // DS based Domain + // + + // + // Get the DSNAME corresponding to the given SID. + // This may result in a call to the GC server. + // + + NtStatus = SampDsObjectFromSid(AccountSid,&DsNameOfAccount); + if NT_SUCCESS(NtStatus) + { + // + // Add this entry to the Ds + // + + NtStatus = SampDsAddMembershipAttribute( + AccountContext->ObjectNameInDs, + SampAliasObjectType, + DsNameOfAccount + ); + + MIDL_user_free(DsNameOfAccount); + } + + + } + else + { + + // + // Regisry based Domain + // + + NtStatus = SampGetSidArrayAttribute( + AccountContext, + SAMP_ALIAS_MEMBERS, + FALSE, // Reference directly + &MemberArray, + &MemberArraySize, + &MemberCount + ); + + if (NT_SUCCESS(NtStatus)) { + + PSID MemberPointer = MemberArray; + + // + // Check the member is really new + // + + for (i = 0; i<MemberCount ; i++ ) { + + if (RtlEqualSid(MemberPointer, AccountSid)) { + + NtStatus = STATUS_MEMBER_IN_ALIAS; + break; + } + + ((PCHAR)MemberPointer) += RtlLengthSid(MemberPointer); + } + + + + if (NT_SUCCESS(NtStatus)) { + + // + // MemberPointer now points at the byte beyond the end of the + // old member array + // + + // + // Allocate a new membership buffer large enough for the existing + // member list and the new one. + // + + ULONG OldTotalSize = ((PCHAR)MemberPointer) - ((PCHAR)MemberArray); + ULONG NewMemberSize = RtlLengthSid(AccountSid); + ULONG NewTotalSize = OldTotalSize + NewMemberSize; + PSID NewMemberArray; + + + NewMemberArray = MIDL_user_allocate( NewTotalSize ); + + if (NewMemberArray == NULL) { + + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + + } else { + + // + // Copy the member list into the new array + // + + RtlCopyMemory(NewMemberArray, MemberArray, OldTotalSize); + + // + // Add the new member to the end + // + + MemberCount += 1; + + NtStatus = RtlCopySid( + NewMemberSize, + ((PCHAR)NewMemberArray) + OldTotalSize, + AccountSid); + + if (NT_SUCCESS(NtStatus)) { + + // + // Update the alias with it's new member list + // + + NtStatus = SampSetSidArrayAttribute( + AccountContext, + SAMP_ALIAS_MEMBERS, + NewMemberArray, + NewTotalSize, + MemberCount + ); + } + + // + // Free up the membership array we allocated + // + + MIDL_user_free( NewMemberArray ); + } + + } + } + // + // End of Registry Part + // + + } + + // + // Account has been added to alias membership + // audit this, if necessary. + // + + if (NT_SUCCESS(NtStatus) && + SampDoAccountAuditing(AccountContext->DomainIndex)) { + + UNICODE_STRING NameString; + PSAMP_DEFINED_DOMAINS Domain; + SAMP_OBJECT_TYPE ObjectType; + NTSTATUS Status; + + Domain = &SampDefinedDomains[ AccountContext->DomainIndex ]; + + Status = SampLookupAccountName( + AccountContext->TypeBody.Alias.Rid, + &NameString, + &ObjectType + ); + + if ( !NT_SUCCESS( Status )) { + RtlInitUnicodeString( &NameString, L"-" ); + } + + + LsaIAuditSamEvent( + STATUS_SUCCESS, + SE_AUDITID_LOCAL_GROUP_ADD, // AuditId + Domain->Sid, // Domain SID + NULL, // Member Rid + AccountSid, // Member sid + &NameString, // Account Name + &Domain->ExternalName, // Domain + &AccountContext->TypeBody.Alias.Rid, // Account Rid + NULL // Privileges used + ); + + if ( NT_SUCCESS( Status )) { + MIDL_user_free( NameString.Buffer ); + } + } + + return(NtStatus); +} + + + +NTSTATUS +SampRemoveAccountFromAlias( + IN PSAMP_OBJECT AccountContext, + IN PSID AccountSid + ) + +/*++ + +Routine Description: + + This routine is used to Remove an account from a specified alias. + This is done by simply Removing the user's Sid From the list of Sids + in the MEMBERS sub-key of the the specified alias. + + It is the caller's responsibility to know that the user is, in fact, + currently a member of the alias. + + + The caller of this service is expected to be in the middle of a + RXACT transaction. This service simply adds some actions to that + RXACT transaction. + + +Arguments: + + AliasRid - The RID of the alias the account is to be removed from. + + AccountSid - The SID of the account being Removed. + +Return Value: + + + STATUS_SUCCESS - The user has been Removed. + + STATUS_MEMBER_NOT_IN_ALIAS - The account was not a member of the alias. + +--*/ +{ + NTSTATUS NtStatus; + ULONG MemberCount, i; + ULONG MemberArraySize; + PSID MemberArray, Member, NextMember; + + ULONG RemovedMemberSize = RtlLengthSid(AccountSid); + + SAMTRACE("SampRemoveAccountFromAlias"); + + // + // Test wether we are DS based + // + + if (IsDsObject(AccountContext)) + { + DSNAME *DsNameOfAccount = NULL; + + // + // Get the DS Name corresponding to the account + // + + NtStatus = SampDsObjectFromSid(AccountSid, &DsNameOfAccount); + if (NT_SUCCESS(NtStatus)) + { + // + // Remove the account from the membership list + // + NtStatus = SampDsRemoveMembershipAttribute( + AccountContext->ObjectNameInDs, + SampAliasObjectType, + DsNameOfAccount + ); + } + } + else + { + + // + // We are registry based + // + + // + // Get a copy of the current member array. + // + + NtStatus = SampGetSidArrayAttribute( + AccountContext, + SAMP_ALIAS_MEMBERS, + TRUE, // Make copy + &MemberArray, + &MemberArraySize, + &MemberCount + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // For each member sid, copy it from old to new member + // arrays if it is not the sid we're trying to delete + // + + Member = MemberArray; + + for (i = 0; i < MemberCount ; i++ ) { + + NextMember = (PSID)(((PCHAR)Member) + RtlLengthSid(Member)); + + if (RtlEqualSid(Member, AccountSid)) { + + // + // Found the member to delete. Shift subsequent members + // + + while ((PCHAR)NextMember < + (((PCHAR)MemberArray) + MemberArraySize)) { + + *((PCHAR)Member)++ = *((PCHAR)NextMember)++; + } + + break; + } + + // + // Advance the old pointer + // + + Member = NextMember; + + ASSERT((PCHAR)Member <= (((PCHAR)MemberArray) + MemberArraySize)); + } + + + // + // If nothing was removed, we didn't find the account + // + + if (i == MemberCount) { + + NtStatus = STATUS_MEMBER_NOT_IN_ALIAS; + + } else { + + // + // The member has been removed, write out the new member list + // + + ASSERT((PCHAR)Member == + (((PCHAR)MemberArray)) + MemberArraySize - RemovedMemberSize); + + NtStatus = SampSetSidArrayAttribute( + AccountContext, + SAMP_ALIAS_MEMBERS, + MemberArray, + MemberArraySize - RemovedMemberSize, + MemberCount - 1 + ); + + // + // audit this, if necessary. + // + + if (NT_SUCCESS(NtStatus) && + SampDoAccountAuditing(AccountContext->DomainIndex)) { + + UNICODE_STRING NameString; + SAMP_OBJECT_TYPE ObjectType; + NTSTATUS Status; + PSAMP_DEFINED_DOMAINS Domain; + + Status = SampLookupAccountName( + AccountContext->TypeBody.Alias.Rid, + &NameString, + &ObjectType + ); + + if ( !NT_SUCCESS( Status )) { + RtlInitUnicodeString( &NameString, L"-" ); + } + + Domain = &SampDefinedDomains[ AccountContext->DomainIndex ]; + + LsaIAuditSamEvent( + STATUS_SUCCESS, + SE_AUDITID_LOCAL_GROUP_REM, // AuditId + Domain->Sid, // Domain SID + NULL, // Member Rid + AccountSid, // Member sid + &NameString, // Account Name + &Domain->ExternalName, // Domain + &AccountContext->TypeBody.Alias.Rid, // Account Rid + NULL // Privileges used + ); + + if ( NT_SUCCESS( Status )) { + MIDL_user_free( NameString.Buffer ); + } + } + } + + // + // Free up the member array + // + + MIDL_user_free(MemberArray); + } + } + + return(NtStatus); +} + + + +NTSTATUS +SampAddAliasToAccountMembership( + IN ULONG AliasRid, + IN PSID AccountSid + ) + +/*++ + +Routine Description: + + This service adds the specified alias to the account's membership + list. It is not assumed that the caller knows anything about + the target account. In particular, the caller doesn't know whether + the account exists or not, nor whether the account is already a member + of the alias. + + THIS SERVICE MUST BE CALLED WITH THE TRANSACTION DOMAIN SET. + + It is a No-Op for DS based accounts, as no reverse membership list + is being maintained. + +Arguments: + + AliasRid - The relative ID of the alias. + + AccountSid - The SID of the account. + + +Return Value: + + + STATUS_SUCCESS - The information has been updated and added to the + RXACT. + + STATUS_MEMBER_IN_ALIAS - The account is already a member of the + specified alias. + + Other status values that may be returned are those returned + by: + + NtOpenKey() + NtQueryValueKey() + RtlAddActionToRXact() + + +--*/ +{ + + NTSTATUS NtStatus, IgnoreStatus; + UNICODE_STRING DomainKeyName; + UNICODE_STRING AccountKeyName; + HANDLE TempHandle; + ULONG MembershipCount, KeyValueLength; + ULONG DomainRidCount; + ULONG i; + PULONG MembershipArray; + OBJECT_ATTRIBUTES ObjectAttributes; + BOOLEAN NewAccount; + + SAMTRACE("SampAddAliasToAccountMembership"); + + // + // There are 4 cases + // + // 1. If the Alias exists in the DS , Member Id corresponds to a SID + // in a registry based domain. Only builtin domains will be registry + // based and we do not support adding a security prinicpal from a + // built in domain into the membership list of an alias in a DS domain. + // This case should never happen. If it happens do nothing + // + // 2. Alias is in DS, Member Id is a SID in a DS based domain + // Operation is a No Op + // + // 3. Alias exists in Registry ( Builtin Domain Alias ), Member Id is + // a SID in a DS based domain. In this case do whatever the original + // code did + // + // 4. Alias exists in Registry ( builtin Domain Alias ). Member Id is a + // SID in a registry based domain ( Builtin Domain ). Do whatever + // the previous code did + // + + + if (!IsDsObject(SampDefinedDomains[SampTransactionDomainIndex].Context)) + { + // + // Cases 3, 4 + // + + // + // Assume the account is a member of at least one of our aliases + // + + NewAccount = FALSE; + + NtStatus = SampBuildAliasMembersKeyName( + AccountSid, + &DomainKeyName, + &AccountKeyName + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Try to open the domain alias/members/(domain) key for this account + // + + InitializeObjectAttributes( + &ObjectAttributes, + &DomainKeyName, + OBJ_CASE_INSENSITIVE, + SampKey, + NULL + ); + + SampDumpNtOpenKey((KEY_READ), &ObjectAttributes, 0); + + NtStatus = RtlpNtOpenKey( + &TempHandle, + (KEY_READ), + &ObjectAttributes, + 0 + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Get the current domain rid count + // + + NtStatus = RtlpNtQueryValueKey( + TempHandle, + &DomainRidCount, + NULL, + NULL, + NULL); + + SampDumpRtlpNtQueryValueKey(&DomainRidCount, + NULL, + NULL, + NULL); + + IgnoreStatus = NtClose(TempHandle); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + } else { + + if (NtStatus == STATUS_OBJECT_NAME_NOT_FOUND) { + + // + // No other accounts in this domain are members of any of our + // aliases. + // + // Create a new key for this domain with no accounts (rids). + // + + NewAccount = TRUE; + + DomainRidCount = 0; // No accounts yet + + NtStatus = RtlAddActionToRXact( + SampRXactContext, + RtlRXactOperationSetValue, + &DomainKeyName, + DomainRidCount, + NULL, + 0 + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Keep our domain count uptodate + // + + NtStatus = SampAdjustAliasDomainsCount(TRUE); + } + } + } + + + + if (NT_SUCCESS(NtStatus)) { + + if (!NewAccount) { + + // + // Try to open the domain alias/members/(domain)/(account) key + // + + InitializeObjectAttributes( + &ObjectAttributes, + &AccountKeyName, + OBJ_CASE_INSENSITIVE, + SampKey, + NULL + ); + + SampDumpNtOpenKey((KEY_READ), &ObjectAttributes, 0); + + NtStatus = RtlpNtOpenKey( + &TempHandle, + (KEY_READ), + &ObjectAttributes, + 0 + ); + } + + + if (NewAccount || (NtStatus == STATUS_OBJECT_NAME_NOT_FOUND)) { + + // + // This account is not a member of any of our aliases yet. + // + + NewAccount = TRUE; + + // + // Set up it's initial membership + // + + MembershipCount = 1; + MembershipArray = &AliasRid; + + NtStatus = STATUS_SUCCESS; // We're doing fine + } + + + if (NT_SUCCESS(NtStatus) && !NewAccount) { + + // + // This account already exists + // + // Get the current membership buffer and add the new alias + // + + KeyValueLength = 0; + + NtStatus = RtlpNtQueryValueKey( + TempHandle, + &MembershipCount, + NULL, + &KeyValueLength, + NULL); + + SampDumpRtlpNtQueryValueKey(&MembershipCount, + NULL, + &KeyValueLength, + NULL); + + if (NT_SUCCESS(NtStatus) || (NtStatus == STATUS_BUFFER_OVERFLOW)) { + + ASSERT(KeyValueLength == (MembershipCount * sizeof(ULONG))); + + // + // Allocate a membership buffer large enough for an + // additional member. + // + + KeyValueLength += sizeof(ULONG); + MembershipArray = MIDL_user_allocate( KeyValueLength ); + + if (MembershipArray == NULL) { + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + } else { + + NtStatus = RtlpNtQueryValueKey( + TempHandle, + NULL, + MembershipArray, + &KeyValueLength, + NULL); + + SampDumpRtlpNtQueryValueKey(NULL, + MembershipArray, + &KeyValueLength, + NULL); + + if (NT_SUCCESS(NtStatus)) { + + // + // See if the account is already a member ... + // + + for (i = 0; i<MembershipCount ; i++ ) { + if ( MembershipArray[i] == AliasRid ) { + NtStatus = STATUS_MEMBER_IN_ALIAS; + break; + } + } + + if (NT_SUCCESS(NtStatus)) { + + // + // Add the new alias's RID to the end + // + + MembershipCount += 1; + MembershipArray[MembershipCount-1] = AliasRid; + } + } + } + } + + // + // Close the account key handle + // + + IgnoreStatus = NtClose( TempHandle ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + } + + // + // We now have a new membership list desribed by : + // MembershipArray, MembershipCount + // + // Write it out and free it up + // + + if (NT_SUCCESS(NtStatus)) { + + KeyValueLength = MembershipCount * sizeof(ULONG); + + NtStatus = RtlAddActionToRXact( + SampRXactContext, + RtlRXactOperationSetValue, + &AccountKeyName, + MembershipCount, + MembershipArray, + KeyValueLength + ); + + if (MembershipArray != &AliasRid) { + MIDL_user_free( MembershipArray ); + } + } + + // + // If this is a new account, we need to increment the rid count + // in the account domain. + // + + if (NewAccount) { + + // + // Increment the domain rid count + // + + NtStatus = RtlAddActionToRXact( + SampRXactContext, + RtlRXactOperationSetValue, + &DomainKeyName, + DomainRidCount + 1, + NULL, + 0 + ); + } + + } + + SampFreeUnicodeString( &DomainKeyName ); + SampFreeUnicodeString( &AccountKeyName ); + + } + } + + return( NtStatus ); + +} + + + +NTSTATUS +SampRemoveAliasFromAccountMembership( + IN ULONG AliasRid, + IN PSID AccountSid + ) + +/*++ + +Routine Description: + + This service removes the specified alias from the account's membership + list. It is not assumed that the caller knows anything about + the target account. In particular, the caller doesn't know whether + the account exists or not, nor whether the account is really a member + of the alias. + + This routine removes the reference to the alias from the account's + membership list, removes the account key if there are no more aliases, + and removes the domain-sid key if this is the last account in the + domain. + + THIS SERVICE MUST BE CALLED WITH THE TRANSACTION DOMAIN SET. + + This routine is a No Op for the case where the Alias is in a domain + defined in the DS. + +Arguments: + + AliasRid - The relative ID of the alias. + + AccountSid - The SID of the account. + + +Return Value: + + + STATUS_SUCCESS - The information has been updated and added to the + RXACT. + + STATUS_NO_SUCH_USER - The account does not exist. + + STATUS_MEMBER_NOT_IN_ALIAS - The account is not a member of the + specified alias. + + Other status values that may be returned are those returned + by: + + NtOpenKey() + NtQueryValueKey() + RtlAddActionToRXact() + + + +--*/ +{ + + NTSTATUS NtStatus, IgnoreStatus; + UNICODE_STRING DomainKeyName; + UNICODE_STRING AccountKeyName; + HANDLE TempHandle; + ULONG MembershipCount, KeyValueLength, i; + PULONG MembershipArray; + OBJECT_ATTRIBUTES ObjectAttributes; + + SAMTRACE("SampRemoveAliasFromAccountMembership"); + + // + // We have 4 cases + // 1 Account and Alias both in DS - No Op because no reverse membership + // list is being maintained + // + // 2 Account in Registry, Alias in DS. This case should never occur as + // built in domain security principals should not be members of aliases + // in DS domain + // + // 3. Account in DS, Alias in Registry - No reverse membership + // + // 4. Account and ALias in Registry - DO what NT4 did + // + + if (!IsDsObject(SampDefinedDomains[SampTransactionDomainIndex].Context)) + { + + // + // Get the account membership + // + + NtStatus = SampBuildAliasMembersKeyName( + AccountSid, + &DomainKeyName, + &AccountKeyName + ); + if (NT_SUCCESS(NtStatus)) { + + InitializeObjectAttributes( + &ObjectAttributes, + &AccountKeyName, + OBJ_CASE_INSENSITIVE, + SampKey, + NULL + ); + + + SampDumpNtOpenKey((KEY_READ), &ObjectAttributes, 0); + + NtStatus = RtlpNtOpenKey( + &TempHandle, + (KEY_READ), + &ObjectAttributes, + 0 + ); + if (NtStatus == STATUS_OBJECT_NAME_NOT_FOUND || + NtStatus == STATUS_OBJECT_PATH_NOT_FOUND) { + + NtStatus = STATUS_MEMBER_NOT_IN_ALIAS; + } + + if (NT_SUCCESS(NtStatus)) { + + // + // Retrieve the length of the membership buffer + // + + KeyValueLength = 0; + + NtStatus = RtlpNtQueryValueKey( + TempHandle, + &MembershipCount, + NULL, + &KeyValueLength, + NULL); + + SampDumpRtlpNtQueryValueKey(&MembershipCount, + NULL, + &KeyValueLength, + NULL); + + if (NT_SUCCESS(NtStatus)) { + ASSERT(MembershipCount == 0); + + NtStatus = STATUS_MEMBER_NOT_IN_ALIAS; + } + + if (NtStatus == STATUS_BUFFER_OVERFLOW) { + + ASSERT(MembershipCount != 0); + ASSERT(KeyValueLength == (MembershipCount * sizeof(ULONG))); + + MembershipArray = MIDL_user_allocate( KeyValueLength ); + + if (MembershipArray == NULL) { + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + } else { + + NtStatus = RtlpNtQueryValueKey( + TempHandle, + NULL, + MembershipArray, + &KeyValueLength, + NULL); + + SampDumpRtlpNtQueryValueKey(NULL, + MembershipArray, + &KeyValueLength, + NULL); + + if (NT_SUCCESS(NtStatus)) { + + // + // See if the account is a member ... + // + + NtStatus = STATUS_MEMBER_NOT_IN_ALIAS; + + for (i = 0; i<MembershipCount ; i++ ) { + if ( MembershipArray[i] == AliasRid ) { + NtStatus = STATUS_SUCCESS; + break; + } + } + + if (NT_SUCCESS(NtStatus)) { + + // + // Replace the removed alias information + // with the last entry's information. + // Then add it to the RXACT transaction + // to be written out. + // + + MembershipCount -= 1; + KeyValueLength -= sizeof(ULONG); + + if (MembershipCount > 0) { + + MembershipArray[i] = MembershipArray[MembershipCount]; + + ASSERT(KeyValueLength == (MembershipCount * sizeof(ULONG))); + NtStatus = RtlAddActionToRXact( + SampRXactContext, + RtlRXactOperationSetValue, + &AccountKeyName, + MembershipCount, + MembershipArray, + KeyValueLength + ); + } else { + + // + // This is the last alias membership for + // this account. Delete the keys. + // + + NtStatus = SampDeleteAliasMembershipKeysForAccount( + AccountSid); + } + } + } + + MIDL_user_free( MembershipArray ); + } + + } + + IgnoreStatus = NtClose( TempHandle ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + } + + + SampFreeUnicodeString( &DomainKeyName ); + SampFreeUnicodeString( &AccountKeyName ); + + } + } + + return( NtStatus ); + +} + + + +NTSTATUS +SampRemoveAliasFromAllAccounts( + IN PSAMP_OBJECT AliasContext + ) + +/*++ + +Routine Description: + + This service removes the specified alias from all account memberships + + THIS SERVICE MUST BE CALLED WITH THE TRANSACTION DOMAIN SET. + + This service leaves the alias membership list intact. It is assumed + that the caller will delete the alias member list as part of the + current transaction. + + This routine is a No OP for the DS case + +Arguments: + + AliasRid - The relative ID of the alias. + +Return Value: + + + STATUS_SUCCESS - The information has been updated and added to the + RXACT. + + STATUS_NO_SUCH_ALIAS - The alias does not exist. + + + Other status values that may be returned are those returned + by: + + NtOpenKey() + NtQueryValueKey() + RtlAddActionToRXact() + + + +--*/ +{ + NTSTATUS NtStatus; + ULONG MemberCount, i; + PSID *MemberArray; + + + + + SAMTRACE("SampRemoveAliasFromAllAccounts"); + + if (!IsDsObject(AliasContext)) + { + + // + // Get the list of members in this alias + // + + MemberArray = NULL; + + NtStatus = SampRetrieveAliasMembers( + AliasContext, + &MemberCount, + &MemberArray); + + if (NT_SUCCESS(NtStatus)) { + + ASSERT((MemberCount != 0) == (MemberArray != NULL)); + + // + // Remove this alias from each of our members in turn + // + + for (i = 0; i < MemberCount ; i++ ) { + + ULONG AliasRid = AliasContext->TypeBody.Alias.Rid; + + NtStatus = SampRemoveAliasFromAccountMembership(AliasRid, MemberArray[i]); + + if (!NT_SUCCESS(NtStatus)) { + break; + } + } + + if (MemberArray != NULL) { + MIDL_user_free( MemberArray ); + } + } + } + + return(NtStatus); +} + + + +NTSTATUS +SampRetrieveAliasMembers( + IN PSAMP_OBJECT AliasContext, + OUT PULONG MemberCount, + OUT PSID **Members OPTIONAL + ) + +/*++ +Routine Description: + + This service retrieves the number of members in a alias. If desired, + it will also retrieve an array of SIDs of the members of the alias. + + THIS SERVICE MUST BE CALLED WITH THE TRANSACTION DOMAIN SET. + +Arguments: + + Context - Points to the account context whose alias members are to + to be retrieved. + + MemberCount - Receives the number of members currently in the alias. + + Members - (Otional) Receives a pointer to a buffer containing an array + of member PSIDs. If this value is NULL, then this information + is not returned. The returned buffer is allocated using + MIDL_user_allocate() and must be freed using MIDL_user_free() when + no longer needed. + +Return Value: + + + STATUS_SUCCESS - The information has been retrieved. + + STATUS_INSUFFICIENT_RESOURCES - Memory could not be allocated for the + string to be returned in. + + Other status values that may be returned are those returned + by: + + NtOpenKey() + NtQueryValueKey() + + If this routine returns failure, *MemberCount will be zero and + *Members will be NULL. + + +--*/ +{ + + NTSTATUS NtStatus; + PSID MemberArray; + ULONG MemberArraySize; + ULONG i; + + SAMTRACE("SampRetieveAliasMembers"); + + if (IsDsObject(AliasContext)) + { + // + // DS case, this routine in DS layer does all the + // work + // + NtStatus = SampDsGetAliasMembershipList( + AliasContext->ObjectNameInDs, + MemberCount, + Members + ); + } + else + { + + // + // Registry based case + // + // + + NtStatus = SampGetSidArrayAttribute( + AliasContext, + SAMP_ALIAS_MEMBERS, + FALSE, // Reference directly + &MemberArray, + &MemberArraySize, + MemberCount + ); + + if (NT_SUCCESS(NtStatus)) { + + if (ARGUMENT_PRESENT(Members)) { + + // + // Allocate memory for the sid array and sid data + // + + ULONG SidArraySize = *MemberCount * sizeof(PSID); + ULONG SidDataSize = MemberArraySize; + + if ( *MemberCount == 0 ) { + + // + // Nothing to copy, just return success. + // + + *Members = NULL; + return( NtStatus ); + } + + (*Members) = (PSID *)MIDL_user_allocate(SidArraySize + SidDataSize); + + if ((*Members) == NULL) { + + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + + } else { + + // + // Copy the sid data into the last part of the block + // + + PSID SidData = (PSID)(&((*Members)[*MemberCount])); + + RtlCopyMemory(SidData, MemberArray, MemberArraySize); + + // + // Fill in the sid pointer array + // + + for (i = 0; i < *MemberCount ; i++) { + + (*Members)[i] = SidData; + + ((PCHAR)SidData) += RtlLengthSid(SidData); + } + + ASSERT(SidData == ((PCHAR)(*Members)) + SidArraySize + SidDataSize); + + } + } + } + } + + return( NtStatus ); + +} + + + +NTSTATUS +SampDeleteAliasKeys( + IN PSAMP_OBJECT Context + ) + +/*++ +Routine Description: + + This service deletes all registry keys related to a alias object. + + +Arguments: + + Context - Points to the alias context whose registry keys are + being deleted. + + +Return Value: + + + STATUS_SUCCESS - The information has been retrieved. + + + Other status values that may be returned by: + + RtlAddActionToRXact() + + + +--*/ +{ + + NTSTATUS NtStatus; + ULONG Rid; + UNICODE_STRING AccountName, KeyName; + + + SAMTRACE("SampDeleteAliasKeys"); + + Rid = Context->TypeBody.Alias.Rid; + + + // + // Aliases are arranged as follows: + // + // +-- Aliases [Count] + // ---+-- + // +-- Names + // | --+-- + // | +-- (AliasName) [AliasRid,] + // | + // +-- (AliasRid) [Revision,SecurityDescriptor] + // ---+----- + // +-- V1_Fixed [,SAM_V1_FIXED_LENGTH_ALIAS] + // +-- Name [,Name] + // +-- AdminComment [,unicode string] + // +-- Members [Count,(Member0Sid, (...), MemberX-1Sid)] + // + // This all needs to be deleted from the bottom up. + // + + + // + // Decrement the alias count + // + + NtStatus = SampAdjustAccountCount(SampAliasObjectType, FALSE ); + + + + + // + // Delete the registry key that has the alias's name to RID mapping. + // + + if (NT_SUCCESS(NtStatus)) { + + // + // Get the name + // + + NtStatus = SampGetUnicodeStringAttribute( + Context, + SAMP_ALIAS_NAME, + TRUE, // Make copy + &AccountName + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampBuildAccountKeyName( + SampAliasObjectType, + &KeyName, + &AccountName + ); + + SampFreeUnicodeString( &AccountName ); + + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = RtlAddActionToRXact( + SampRXactContext, + RtlRXactOperationDelete, + &KeyName, + 0, + NULL, + 0 + ); + + SampFreeUnicodeString( &KeyName ); + } + } + } + + + + // + // Delete the attribute keys + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampDeleteAttributeKeys( + Context + ); + } + + + // + // Delete the RID key + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampBuildAccountSubKeyName( + SampAliasObjectType, + &KeyName, + Rid, + NULL + ); + + if (NT_SUCCESS(NtStatus)) { + + + NtStatus = RtlAddActionToRXact( + SampRXactContext, + RtlRXactOperationDelete, + &KeyName, + 0, + NULL, + 0 + ); + + SampFreeUnicodeString( &KeyName ); + } + + + } + + + + return( NtStatus ); + +} + + + +NTSTATUS +SampDeleteAliasMembershipKeysForAccount( + IN PSID AccountSid + ) + +/*++ + +Routine Description: + + This service deletes the alias membership keys for the specified account. + + This account rid key is deleted. If this was the last account-rid for + the domain then the domain keys is deleted also. + + THIS SERVICE MUST BE CALLED WITH THE TRANSACTION DOMAIN SET. + + It is assumed we are in the middle of a registry transaction. + +Arguments: + + AccountSid - The SID of the account. + + +Return Value: + + + STATUS_SUCCESS - The transactions have been added. + + Other status values that may be returned are those returned + by: + + NtOpenKey() + NtQueryValueKey() + RtlAddActionToRXact() + +--*/ +{ + + NTSTATUS NtStatus, IgnoreStatus; + UNICODE_STRING DomainKeyName; + UNICODE_STRING AccountKeyName; + HANDLE TempHandle; + ULONG MembershipCount; + OBJECT_ATTRIBUTES ObjectAttributes; + + SAMTRACE("SampDeleteAliasMembershipKeysForAccount"); + + // + // Get the account membership key names + // + + NtStatus = SampBuildAliasMembersKeyName( + AccountSid, + &DomainKeyName, + &AccountKeyName + ); + if (NT_SUCCESS(NtStatus)) { + + + // + // Delete the account rid key + // + + NtStatus = RtlAddActionToRXact( + SampRXactContext, + RtlRXactOperationDelete, + &AccountKeyName, + 0, + NULL, + 0 + ); + + // + // Adjust the rid count for the domain + // + + if (NT_SUCCESS(NtStatus)) { + + InitializeObjectAttributes( + &ObjectAttributes, + &DomainKeyName, + OBJ_CASE_INSENSITIVE, + SampKey, + NULL + ); + + + SampDumpNtOpenKey((KEY_READ), &ObjectAttributes, 0); + + NtStatus = RtlpNtOpenKey( + &TempHandle, + (KEY_READ), + &ObjectAttributes, + 0 + ); + ASSERT(NT_SUCCESS(NtStatus)); // We just opened a sub-key successfully ! + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = RtlpNtQueryValueKey( + TempHandle, + &MembershipCount, + NULL, + NULL, + NULL); + + SampDumpRtlpNtQueryValueKey(&MembershipCount, + NULL, + NULL, + NULL); + + if (NT_SUCCESS(NtStatus)) { + + // + // Decrement the rid count, write out or delete key if 0 + // + + MembershipCount -= 1; + if (MembershipCount > 0) { + + // + // Decrement the domain rid count + // + + NtStatus = RtlAddActionToRXact( + SampRXactContext, + RtlRXactOperationSetValue, + &DomainKeyName, + MembershipCount, + NULL, + 0 + ); + } else { + + // + // Delete the domain key + // + + NtStatus = RtlAddActionToRXact( + SampRXactContext, + RtlRXactOperationDelete, + &DomainKeyName, + 0, + NULL, + 0 + ); + + // + // Adjust the count of domain keys + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampAdjustAliasDomainsCount(FALSE); + } + } + + } + + // + // Close the domain key handle + // + + IgnoreStatus = NtClose( TempHandle ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + } + } + + + SampFreeUnicodeString( &DomainKeyName ); + SampFreeUnicodeString( &AccountKeyName ); + + } + + + + return( NtStatus ); + +} + + + +NTSTATUS +SampAdjustAliasDomainsCount( + IN BOOLEAN Increment + ) + +/*++ +Routine Description: + + This service increments or decrements the number of domains that have + at least one account that is a member of one of our aliases. + + This value is contained in the type of \(domain)\ALIASES\MEMBERS + + + + Note: THIS ROUTINE REFERENCES THE CURRENT TRANSACTION DOMAIN + (ESTABLISHED USING SampSetTransactioDomain()). THIS + SERVICE MAY ONLY BE CALLED AFTER SampSetTransactionDomain() + AND BEFORE SampReleaseWriteLock(). + +Arguments: + + Increment - TRUE to increment, FALSE to decrement + +Return Value: + + STATUS_SUCCESS - The value has been adjusted and the new value added + to the current RXACT transaction. + + STATUS_INSUFFICIENT_RESOURCES - Not enough memory could be allocated + to perform the requested operation. + + Other values are unexpected errors. These may originate from + internal calls to: + + NtOpenKey() + NtQueryInformationKey() + RtlAddActionToRXact() + + + +--*/ +{ + + // + // Don't maintain a count of domains for now + // + + + SAMTRACE("SampAdjustAliasDomainsCount"); + + return(STATUS_SUCCESS); + + DBG_UNREFERENCED_PARAMETER(Increment); +} + + + +NTSTATUS +SampValidateNewAliasMember( + IN PSID MemberId + ) + +/*++ + +Routine Description: + + This service checks the passed Sid is acceptable as a potential new + member of one of the aliases in the current domain. + + Note: THIS ROUTINE REFERENCES THE CURRENT TRANSACTION DOMAIN + (ESTABLISHED USING SampSetTransactioDomain()). THIS + SERVICE MAY ONLY BE CALLED AFTER SampSetTransactionDomain() + AND BEFORE SampReleaseWriteLock(). + +Arguments: + + MemberId - the full Sid of the member to validate + +Return Value: + + STATUS_SUCCESS - MemberId is a valid potential alias member + + STATUS_INVALID_MEMBER - MemberId has the wrong account type. + + STATUS_NO_SUCH_MEMBER - MemberId is not a valid account. + + STATUS_INVALID_SID - MemberId is not a valid sid. + +--*/ +{ + NTSTATUS NtStatus; + PSID MemberDomainSid = NULL, CurrentDomainSid = NULL; + ULONG MemberRid; + SAMP_OBJECT_TYPE MemberType; + + SAMTRACE("SampValidateNewAliasMember"); + + // + // Check the new member sid for structural soundness + // + + if ((MemberId == NULL) || !RtlValidSid(MemberId)) { + return(STATUS_INVALID_SID); + } + + + // + // Get the current domain sid + // + + ASSERT(SampTransactionWithinDomain); + CurrentDomainSid = SampDefinedDomains[SampTransactionDomainIndex].Sid; + + // + // Break up the new member into domain and rid + // + + NtStatus = SampSplitSid(MemberId, &MemberDomainSid, &MemberRid); + + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + // + // If the member isn't from this domain, then they're OK. + // + + if (!RtlEqualSid(CurrentDomainSid, MemberDomainSid)) { + + NtStatus = STATUS_SUCCESS; + + } else { + + // + // The member is in our domain - check that the type of + // account is acceptable. + // + + NtStatus = SampLookupAccountName( + MemberRid, + NULL, + &MemberType + ); + + if (NT_SUCCESS(NtStatus)) { + + switch (MemberType) { + case SampUserObjectType: + case SampGroupObjectType: + NtStatus = STATUS_SUCCESS; + break; + + case SampUnknownObjectType: + NtStatus = STATUS_NO_SUCH_MEMBER; + break; + + default: + NtStatus = STATUS_INVALID_MEMBER; + break; + } + } + + } + + + MIDL_user_free(MemberDomainSid); + + return(NtStatus); +} + + + + +NTSTATUS +SampChangeAliasAccountName( + IN PSAMP_OBJECT Context, + IN PUNICODE_STRING NewAccountName, + OUT PUNICODE_STRING OldAccountName + ) + +/*++ +Routine Description: + + This routine changes the account name of an alias account. + + THIS SERVICE MUST BE CALLED WITH THE TRANSACTION DOMAIN SET. + +Arguments: + + Context - Points to the account context whose name is to be changed. + + NewAccountName - New name to give this account + + OldAccountName - old name is returned here. The buffer should be freed + by calling MIDL_user_free. + +Return Value: + + + STATUS_SUCCESS - The information has been retrieved. + + + Other status values that may be returned by: + + SampGetUnicodeStringAttribute() + SampSetUnicodeStringAttribute() + SampValidateAccountNameChange() + RtlAddActionToRXact() + + + +--*/ +{ + + NTSTATUS NtStatus; + UNICODE_STRING KeyName; + + SAMTRACE("SampChangeAliasAccountName"); + + + + + // + // Get the current name so we can delete the old Name->Rid + // mapping key. + // + + NtStatus = SampGetUnicodeStringAttribute( + Context, + SAMP_ALIAS_NAME, + TRUE, // Make copy + OldAccountName + ); + + // + // Make sure the name is valid and not already in use + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampValidateAccountNameChange( + NewAccountName, + OldAccountName + ); + + if (!(IsDsObject(Context))) { + + // + // For registry based Aliases re-create the + // Name to Rid mapping keys + // + + ///////////////////////////////////////////////////////////// + // There are two copies of the name of each account. // + // one is under the DOMAIN\(domainName)\ALIAS\NAMES key, // + // one is the value of the // + // DOMAIN\(DomainName)\ALIAS\(rid)\NAME key // + ///////////////////////////////////////////////////////////// + + + // + // Delete the old name key + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampBuildAccountKeyName( + SampAliasObjectType, + &KeyName, + OldAccountName + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = RtlAddActionToRXact( + SampRXactContext, + RtlRXactOperationDelete, + &KeyName, + 0, + NULL, + 0 + ); + SampFreeUnicodeString( &KeyName ); + } + + } + + // + // + // Create the new Name->Rid mapping key + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampBuildAccountKeyName( + SampAliasObjectType, + &KeyName, + NewAccountName + ); + + if (NT_SUCCESS(NtStatus)) { + + ULONG AliasRid = Context->TypeBody.Alias.Rid; + + NtStatus = RtlAddActionToRXact( + SampRXactContext, + RtlRXactOperationSetValue, + &KeyName, + AliasRid, + (PVOID)NULL, + 0 + ); + SampFreeUnicodeString( &KeyName ); + } + } + } + + // + // replace the account's name + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampSetUnicodeStringAttribute( + Context, + SAMP_ALIAS_NAME, + NewAccountName + ); + } + + // + // Free up the old account name if we failed + // + + if (!NT_SUCCESS(NtStatus)) { + SampFreeUnicodeString(OldAccountName); + } + + } + + + return(NtStatus); +} diff --git a/private/newsam2/server/almember.c b/private/newsam2/server/almember.c new file mode 100644 index 000000000..a624f5e45 --- /dev/null +++ b/private/newsam2/server/almember.c @@ -0,0 +1,3666 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + almember.c + +Abstract: + + This file contains utilities related to membership of aliases. + Alternative design + + +Author: + + Scott Birrell 01-Apr-1993 + +Environment: + + User Mode - Win32 + +Revision History: + + +--*/ + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include <samsrvp.h> + +#define SAMP_AL_FREE_OLD_LIST ((ULONG) 0x00000001L) +#define SAMP_AL_ERROR_IF_MEMBER ((ULONG) 0x00000002L) +#define SAMP_AL_ERROR_IF_NOT_MEMBER ((ULONG) 0x00000004L) +#define SAMP_AL_ASSIGN_NEW_REFERENCES ((ULONG) 0x00000008L) +#define SAMP_AL_LOOKUP_BY_SID ((ULONG) 0x00000010L) +#define SAMP_AL_LOOKUP_BY_REFERENCE ((ULONG) 0x00000020L) +#define SAMP_AL_VERIFY_NO_ALIASES_IN_ACCOUNT ((ULONG) 0x00000040L) +#define SAMP_AL_VERIFY_ALL_ALIASES_IN_ACCOUNT ((ULONG) 0x00000080L) +#define SAMP_AL_VERIFY_NO_MEMBERS_IN_ALIAS ((ULONG) 0x00000100L) +#define SAMP_AL_VERIFY_ALL_MEMBERS_IN_ALIAS ((ULONG) 0x00000200L) + +#define SAMP_UNKNOWN_INDEX ((ULONG) 0xffffffffL) +#define SAMP_AL_ALIAS_LIST_DELTA ((ULONG) 0x00000100L) +#define SAMP_AL_ALIAS_DELTA ((ULONG) 0x00000040L) +#define SAMP_AL_REFERENCED_DOMAIN_LIST_DELTA ((ULONG) 0x00000100L) +#define SAMP_AL_INITIAL_MEMBER_ALIAS_LIST_LENGTH ((ULONG) 0x00001000L) +#define SAMP_AL_MEMBER_ALIAS_LIST_DELTA ((ULONG) 0x00001000L) +#define SAMP_AL_INITIAL_REFERENCED_DOMAIN_LIST_LENGTH ((ULONG) 0x00000400L) +#define SAMP_AL_INITIAL_MEMBER_DOMAIN_LENGTH ((ULONG) 0x00000040L) +#define SAMP_AL_INITIAL_MEMBER_ACCOUNT_ALIAS_CAPACITY ((ULONG) 0x00000004L) +#define SAMP_AL_ENUM_PREFERRED_LENGTH ((ULONG) 0x00001000L) +#define SAMP_AL_INITIAL_MEMBERSHIP_COUNT ((ULONG) 0x0000000aL) +#define SAMP_AL_MEMBERSHIP_COUNT_DELTA ((ULONG) 0x0000000aL) +#define SAMP_AL_MEMBER_ALIAS_LIST_SIGNATURE ((ULONG) 0x53494c41) +#define SAMP_AL_MEMBER_DOMAIN_SIGNATURE ((ULONG) 0x4d4f444d) +#define SAMP_AL_MEMBER_ACCOUNT_SIGNATURE ((ULONG) 0x4343414d) + +#define SAMP_AL_DR_ALIAS_LIST_KEY_NAME L"Aliases\\Members\\AliasList" +#define SAMP_AL_DR_REFERENCED_DOMAIN_LIST_KEY_NAME \ + L"Aliases\\Members\\ReferencedDomainList" + + +///////////////////////////////////////////////////////////////////////////// +// // +// Private macro functions // +// // +///////////////////////////////////////////////////////////////////////////// + +#define SampAlFirstMemberDomain( MemberAliasList ) \ + (MemberAliasList->MemberDomains) + +#define SampAlOffsetFirstMemberDomain( MemberAliasList ) \ + (((PUCHAR) SampAlFirstMemberDomain(MemberAliasList)) - ((PUCHAR) MemberAliasList)) + +#define SampAlFirstMemberAccount( MemberDomain ) \ + ((PSAMP_AL_MEMBER_ACCOUNT) \ + (((PUCHAR) &((MemberDomain)->DomainSid)) + RtlLengthSid(&((MemberDomain)->DomainSid)))) + +#define SampAlOffsetFirstMemberAccount( MemberDomain ) \ + (((PUCHAR) SampAlFirstMemberAccount(MemberDomain)) - ((PUCHAR) MemberDomain)) + +#define SampAlNextMemberAccount( MemberAccount ) \ + ((PSAMP_AL_MEMBER_ACCOUNT)(((PUCHAR) MemberAccount) + (MemberAccount)->MaximumLength)) + +#define SampAlOffsetFirstAlias( OutputMemberAccount ) \ + ((ULONG) FIELD_OFFSET(SAMP_AL_MEMBER_ACCOUNT, AliasRids)) + +#define SampAlNextMemberDomain( MemberDomain ) \ + ((PSAMP_AL_MEMBER_DOMAIN)(((PUCHAR) MemberDomain) + (MemberDomain)->MaximumLength)) + +#define SampAlNextNewAliasInMemberAccount( MemberAccount ) \ + ((PULONG)(((PUCHAR) MemberAccount) + (MemberAccount)->UsedLength)) + +#define SampAlNextNewMemberAccount( MemberDomain ) \ + ((PSAMP_AL_MEMBER_ACCOUNT)(((PUCHAR) MemberDomain) + (MemberDomain)->UsedLength)) + +#define SampAlNextNewMemberDomain( MemberAliasList ) \ + ((PSAMP_AL_MEMBER_DOMAIN)(((PUCHAR) MemberAliasList) + (MemberAliasList)->UsedLength)) + +#define SampAlInfoIsValid(DomainIndex) \ + ((SampDefinedDomains[DomainIndex].AliasInformation.Valid) || \ + (SampServiceState == SampServiceInitializing )) + +#define SampAlInfoMakeValid(DomainIndex) \ + (SampDefinedDomains[DomainIndex].AliasInformation.Valid = TRUE) + +#define SampAlInfoMakeInvalid(DomainIndex) \ + (SampDefinedDomains[DomainIndex].AliasInformation.Valid = FALSE) + +#define SampAlDomainIndexToMemberAliasList( DomainIndex ) \ + ((PSAMP_AL_MEMBER_ALIAS_LIST) \ + SampDefinedDomains[ DomainIndex].AliasInformation.MemberAliasList) + +#define SampAlDomainHandleToMemberAliasList( DomainHandle ) \ + (SampAlDomainIndexToMemberAliasList(((PSAMP_OBJECT) DomainHandle)->DomainIndex)) + +#define SampAlAliasHandleToMemberAliasList( AliasHandle ) \ + (SampAlDomainIndexToMemberAliasList(((PSAMP_OBJECT) AliasHandle)->DomainIndex)) + +#define SampAlMemberDomainToOffset( MemberAliasList, MemberDomain) \ + (((PUCHAR) MemberDomain) - ((PUCHAR) MemberAliasList)) + +#define SampAlMemberDomainFromOffset( MemberDomain, MemberDomainOffset) \ + ((PSAMP_AL_MEMBER_DOMAIN)(((PUCHAR) MemberDomain) + MemberDomainOffset)) + +#define SampAlMemberAccountToOffset( MemberDomain, MemberAccount) \ + (((PUCHAR) MemberAccount) - ((PUCHAR) MemberDomain)) + +#define SampAlMemberAccountFromOffset( MemberDomain, MemberAccountOffset) \ + ((PSAMP_AL_MEMBER_ACCOUNT)(((PUCHAR) MemberDomain) + MemberAccountOffset)) + +#define SampAlLengthRequiredMemberAccount( AliasCapacity ) \ + (sizeof(SAMP_AL_MEMBER_ACCOUNT) + ((AliasCapacity - 1) * sizeof(ULONG))) + +#define SampAlUpdateMemberAliasList( AliasHandle, MemberAliasList ) \ + { \ + PSAMP_OBJECT InternalAliasHandle = (PSAMP_OBJECT) AliasHandle; \ + SampDefinedDomains[InternalAliasHandle->DomainIndex].AliasInformation.MemberAliasList \ + = MemberAliasList; \ + } + +///////////////////////////////////////////////////////////////////////////// +// // +// Private Datatypes // +// // +///////////////////////////////////////////////////////////////////////////// + +// This datatype is not currently used. It may be used if Alias information +// is every stored to Registry Keys. +// + +typedef enum _SAMP_AL_LIST_TYPE { + + SampAlMemberAliasList = 1 + +} SAMP_AL_LIST_TYPE, *PSAMP_AL_LIST_TYPE; + +///////////////////////////////////////////////////////////////////////////// +// // +// Private Static Data // +// // +///////////////////////////////////////////////////////////////////////////// + +UNICODE_STRING SampAlDrMemberAliasListKeyName; +BOOLEAN SampAlEnableBuildingOfList[SAMP_DEFINED_DOMAINS_COUNT] = { TRUE, TRUE }; + +///////////////////////////////////////////////////////////////////////////// +// // +// Prototypes of functions private to this module // +// // +///////////////////////////////////////////////////////////////////////////// + +NTSTATUS +SampAlCreateMemberAliasList( + IN LONG DomainIndex, + IN ULONG InitialMemberAliasListLength, + OUT OPTIONAL PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList + ); + +NTSTATUS +SampAlGrowMemberAliasList( + IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, + IN ULONG ExtraSpaceRequired + ); + +NTSTATUS +SampAlBuildMemberAliasList( + IN LONG DomainIndex + ); + +NTSTATUS +SampAlCreateMemberDomain( + IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, + IN OUT PSID DomainSid, + OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain + ); + +NTSTATUS +SampAlAllocateMemberDomain( + IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, + IN ULONG MaximumLengthMemberDomain, + OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain + ); + +NTSTATUS +SampAlGrowMemberDomain( + IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, + IN OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain, + IN ULONG ExtraSpaceRequired + ); + +NTSTATUS +SampAlDeleteMemberDomain( + IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, + IN OUT PSAMP_AL_MEMBER_DOMAIN MemberDomain + ); + +NTSTATUS +SampAlLookupMemberDomain( + IN PSAMP_AL_MEMBER_ALIAS_LIST MemberAliasList, + IN PSID DomainSid, + OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain + ); + +NTSTATUS +SampAlCreateMemberAccount( + IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, + IN OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain, + IN ULONG Rid, + IN ULONG AliasCapacity, + OUT PSAMP_AL_MEMBER_ACCOUNT *MemberAccount + ); + +NTSTATUS +SampAlAllocateMemberAccount( + IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, + IN OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain, + IN ULONG MaximumLengthMemberAccount, + OUT PSAMP_AL_MEMBER_ACCOUNT *MemberAccount + ); + +NTSTATUS +SampAlGrowMemberAccount( + IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, + IN OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain, + IN OUT PSAMP_AL_MEMBER_ACCOUNT *MemberAccount, + IN ULONG ExtraSpaceRequired + ); + +NTSTATUS +SampAlDeleteMemberAccount( + IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, + IN OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain, + IN OUT PSAMP_AL_MEMBER_ACCOUNT MemberAccount, + OUT PBOOLEAN MemberDomainDeleted + ); + +NTSTATUS +SampAlLookupMemberAccount( + IN PSAMP_AL_MEMBER_DOMAIN MemberDomain, + IN ULONG MemberRid, + OUT PSAMP_AL_MEMBER_ACCOUNT *MemberAccount + ); + +NTSTATUS +SampAlAddAliasesToMemberAccount( + IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, + IN OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain, + IN OUT PSAMP_AL_MEMBER_ACCOUNT *MemberAccount, + IN ULONG Options, + IN PSAMPR_ULONG_ARRAY AliasRids + ); + +NTSTATUS +SampAlRemoveAliasesFromMemberAccount( + IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, + IN OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain, + IN OUT PSAMP_AL_MEMBER_ACCOUNT *MemberAccount, + IN ULONG Options, + IN PSAMPR_ULONG_ARRAY AliasRids, + OUT PBOOLEAN MemberDomainDeleted, + OUT PBOOLEAN MemberAccountDeleted + ); + +NTSTATUS +SampAlLookupAliasesInMemberAccount( + IN PSAMP_AL_MEMBER_ACCOUNT MemberAccount, + IN PSAMPR_ULONG_ARRAY AliasRids, + OUT PULONG ExistingAliasCount + ); + +NTSTATUS +SampAlSplitMemberSids( + IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, + IN ULONG Options, + IN PSAMPR_PSID_ARRAY MemberSids, + OUT PSAMP_AL_SPLIT_MEMBER_SID_LIST SplitMemberSids + ); + +BOOLEAN +SampAlInfoIsValidForDomain( + IN SAMPR_HANDLE DomainHandle + ); + +BOOLEAN +SampAlInfoIsValidForAlias( + IN SAMPR_HANDLE AliasHandle + ); + +////////////////////////////////////////////////////////////////////////////// +// // +// Code of Exported Routines // +// // +////////////////////////////////////////////////////////////////////////////// + +NTSTATUS +SamrGetAliasMembership( + IN SAMPR_HANDLE DomainHandle, + IN PSAMPR_PSID_ARRAY SidArray, + OUT PSAMPR_ULONG_ARRAY Membership + ) + +/*++ + +Routine Description: + + This API searches the set of aliases in the specified domain to see + which aliases, if any, the passed SIDs are members of. Any aliases + that any of the SIDs are found to be members of are returned. + + Note that any particular alias will appear only once in the returned list. + +Parameters: + + DomainHandle - Handle from a SamOpenDomain call. + + PassedCount - Specifies the number of Sids being passed. + + Sids - Pointer to an arrray of Count pointers to Sids whose alias + memberships are to be looked up. + + Membership - receives the array of rids rerpresenting the aliases + in this domain that any of the sid(s) are members of. + +Return Values: + + STATUS_SUCCESS - The combined alias membership is in Membership + + STATUS_INVALID_SID - One of the passed sids was invalid + +--*/ + +{ + NTSTATUS NtStatus, IgnoreStatus; + PSAMP_OBJECT DomainContext; + SAMP_OBJECT_TYPE FoundType; + ULONG i; + ULONG SidCount; + PSID *Sids; + BOOLEAN ObjectReferenced = FALSE; + + SAMTRACE("SamrGetAliasMembership"); + + ASSERT(Membership != NULL); + ASSERT(Membership->Element == NULL); + + SidCount = SidArray->Count; + Sids = (PSID *)(SidArray->Sids); + + // + // Grab the lock + // + + SampAcquireReadLock(); + + // + // Validate type of, and access to object. + // + + DomainContext = (PSAMP_OBJECT)DomainHandle; + + NtStatus = SampLookupContext( + DomainContext, + DOMAIN_LOOKUP, + SampDomainObjectType, + &FoundType + ); + + if (!NT_SUCCESS(NtStatus)) { + + goto GetAliasMembershipError; + } + + ObjectReferenced = TRUE; + + // + // Validate the Sids. if any are invalid, return an error. + // + + for (i=0; i < SidCount; i++) { + + // + // Check for valid sid + // + + if ( (Sids[i] == NULL) || !RtlValidSid(Sids[i]) ) { + + NtStatus = STATUS_INVALID_SID; + break; + } + } + + if (!NT_SUCCESS(NtStatus)) { + + goto GetAliasMembershipError; + } + + // + // If the in-memory Alias Membership information for this domain is valid, + // use it to retrieve the Alias members. + // + + if (SampAlInfoIsValidForDomain(DomainHandle)) { + + NtStatus = SampAlQueryAliasMembership( + DomainHandle, + SidArray, + Membership + ); + } else { + + NtStatus = SampAlSlowQueryAliasMembership( + DomainHandle, + SidArray, + Membership + ); + } + +GetAliasMembershipFinish: + + // + // If necessary, dereference the SAM server object. + // + + if (ObjectReferenced) { + + IgnoreStatus = SampDeReferenceContext( DomainContext, FALSE ); + } + + // + // Free the read lock + // + + SampReleaseReadLock(); + + return(NtStatus); + +GetAliasMembershipError: + + goto GetAliasMembershipFinish; +} + + +NTSTATUS +SampAlQueryAliasMembership( + IN SAMPR_HANDLE DomainHandle, + IN PSAMPR_PSID_ARRAY SidArray, + OUT PSAMPR_ULONG_ARRAY Membership + ) + +/*++ + +Routine Description: + + This function is one of two worker routines for the SamrGetAliasMembership + API. This worker uses the Member Alias List to determine which aliases, + if any, the passed SIDs are members of. Any aliases that any of the SIDs + are found to be members of are returned. + + Note that any particular alias will appear only once in the returned list. + + See also SampAlSlowQueryAliasMembership() + + WARNING: The SAM Read Lock must be held while this function executes. + +Parameters: + + DomainHandle - Handle from a SamrOpenDomain call. + + SidArray - Pointer to a counted array of pointers to Sids whose alias + memberships are to be looked up. + + Membership - Receives the array of rids rerpresenting the aliases + in this domain that any of the sid(s) are members of. + +Return Values: + + STATUS_SUCCESS - The combined alias membership is in Membership + + STATUS_INVALID_SID - One of the passed sids was invalid + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PSAMP_AL_MEMBER_ALIAS_LIST MemberAliasList = NULL; + PSAMP_AL_MEMBER_DOMAIN MemberDomain = NULL; + ULONG Rid, AliasRid; + ULONG AliasIndex, SidIndex; + PSAMP_AL_MEMBER_ACCOUNT MemberAccount = NULL; + BOOLEAN AliasAlreadyFound; + ULONG AliasFoundIndex, MembershipMaximumCount; + PSID DomainSid = NULL; + PSID Sid = NULL; + PULONG NewMembership = NULL; + + SAMTRACE("SampAlQueryAliasMembership"); + + Membership->Count = 0; + Membership->Element = NULL; + + // + // Obtain pointer to Alias Member List. + // + + MemberAliasList = SampAlDomainHandleToMemberAliasList( DomainHandle ); + + // + // If there are no Member Domains in this Member Alias List, then just + // finish. + // + + if (MemberAliasList->DomainCount == 0) { + + goto QueryAliasMembershipFinish; + } + + // + // Allocate Scratch Sid buffer. We will use this same buffer for splitting + // each Sid. + // + + DomainSid = MIDL_user_allocate( RtlLengthRequiredSid( 256 )); + + Status = STATUS_NO_MEMORY; + + if (DomainSid == NULL) { + + goto QueryAliasMembershipError; + } + + Status = STATUS_SUCCESS; + + // + // Allocate output array with a nominal initial size. Reallocate it + // as necessary + // + + MembershipMaximumCount = SAMP_AL_INITIAL_MEMBERSHIP_COUNT; + + Membership->Element = MIDL_user_allocate( MembershipMaximumCount * sizeof(ULONG)); + + Status = STATUS_NO_MEMORY; + + if (Membership->Element == NULL) { + + goto QueryAliasMembershipError; + } + + Status = STATUS_SUCCESS; + + // + // Now query the membership of the array of split Sids. For each + // Sid, we skip the Sid if it has an unknown MemberDomain, because + // it does not belong to any aliases. For each surviving Sid, we scan the + // Alias List, skipping entries for aliases we've already entered in the + // output list. We search for the Rid only in the section of the + // Alias List pertinent to the Sid's domain. + // + + for (SidIndex = 0; SidIndex < SidArray->Count; SidIndex++) { + + Sid = SidArray->Sids[ SidIndex ].SidPointer; + + // + // Split this Sid into a DomainSid and a Rid. Note that we re-use + // the buffer containing the Domain Sid for the next Sid. + // + + Status = SampSplitSid( Sid, &DomainSid, &Rid); + + if (!NT_SUCCESS(Status)) { + + break; + } + + // + // Search the Member Alias List for the Sid's Member Domain + // (if any). + // + + Status = SampAlLookupMemberDomain( + MemberAliasList, + DomainSid, + &MemberDomain + ); + + if (!NT_SUCCESS(Status)) { + + // + // The only expected error is STATUS_NO_SUCH_DOMAIN. If we + // don't get this error, fail the request. Otherwise, the + // Sid is not a member of any aliases in the SAM local domain, so + // just skip to the next Sid. + // + + if (Status != STATUS_NO_SUCH_DOMAIN) { + + break; + } + + Status = STATUS_SUCCESS; + continue; + } + + // + // We've found the Member Domain. Now find the Member Account. + // + + Status = SampAlLookupMemberAccount( + MemberDomain, + Rid, + &MemberAccount + ); + + if (!NT_SUCCESS(Status)) { + + // + // The only expected error is STATUS_NO_SUCH_MEMBER. If we + // don't get this error, fail the request. Otherwise, the + // Sid is not a member of any aliases in the domain, so just + // skip to the next Sid. + // + + if (Status != STATUS_NO_SUCH_MEMBER) { + + break; + } + + Status = STATUS_SUCCESS; + continue; + } + + // + // We've found the Member Account. For each of the aliases our Sid + // belongs to, add the alias to the output list if not already there. + // + + for (AliasIndex = 0; AliasIndex < MemberAccount->AliasCount; AliasIndex++) { + + AliasRid = MemberAccount->AliasRids[AliasIndex]; + + AliasAlreadyFound = FALSE; + + for (AliasFoundIndex = 0; + AliasFoundIndex < Membership->Count; + AliasFoundIndex++) { + + if (AliasRid == Membership->Element[AliasFoundIndex]) { + + AliasAlreadyFound = TRUE; + break; + } + } + + if (!AliasAlreadyFound) { + + // + // If there isn't enough room in the output Membership + // array, reallocate it. + // + + if (Membership->Count == MembershipMaximumCount) { + + MembershipMaximumCount += SAMP_AL_MEMBERSHIP_COUNT_DELTA; + + NewMembership = MIDL_user_allocate( + MembershipMaximumCount * sizeof(ULONG) + ); + + Status = STATUS_NO_MEMORY; + + if (NewMembership == NULL) { + + break; + } + + Status = STATUS_SUCCESS; + + RtlMoveMemory( + NewMembership, + Membership->Element, + Membership->Count * sizeof(ULONG) + ); + + MIDL_user_free( Membership->Element); + Membership->Element = NewMembership; + } + + Membership->Element[Membership->Count] = AliasRid; + Membership->Count++; + } + } + } + + // + // If the buffer we've allocated turns out to be way overboard, allocate + // a smaller one for the output. + // + + // TBS + +QueryAliasMembershipFinish: + + // + // If we got as far as allocating a buffer for the DomainSids, free it. + // + + if (DomainSid != NULL) { + + MIDL_user_free(DomainSid); + DomainSid = NULL; + } + + return(Status); + +QueryAliasMembershipError: + + // + // If necessary, free the output membership array. + // + + if (Membership->Element != NULL) { + + MIDL_user_free( Membership->Element); + Membership->Element = NULL; + } + + goto QueryAliasMembershipFinish; +} + + +NTSTATUS +SampAlSlowQueryAliasMembership( + IN SAMPR_HANDLE DomainHandle, + IN PSAMPR_PSID_ARRAY SidArray, + OUT PSAMPR_ULONG_ARRAY Membership + ) + +/*++ + +Routine Description: + + This function is the slow version of the worker routine for the + SamrGetAliasMembership API searches. It is called when the in-memory + Alias Information is no longer valid. + + WARNING! The caller of this function must hold the SAM Database Read + Lock. + +Parameters: + + DomainHandle - Handle from a SamOpenDomain call. + + SidArray - Pointer to a counted array of pointers to Sids whose alias + memberships are to be looked up. + + Membership - Receives the array of rids rerpresenting the aliases + in this domain that any of the sid(s) are members of. + +Return Values: + + STATUS_SUCCESS - The combined alias membership is in Membership + + STATUS_INVALID_SID - One of the passed sids was invalid + +--*/ + +{ + NTSTATUS NtStatus; + ULONG i; + ULONG MembershipCount; + ULONG TotalMembershipCount; + ULONG MembershipIndex; + ULONG BufferSize; + ULONG TotalBufferSize; + ULONG SidCount = SidArray->Count; + PSID *Sids = (PSID *) &SidArray->Sids->SidPointer; + + SAMTRACE("SampAlSlowQueryAliasMembership"); + + // + // For each Sid, retrieve its membership and size up how many membership + // entries we'll have in total. + // + + TotalMembershipCount = 0; + TotalBufferSize = 0; + + for (i=0; i < SidCount; i++) { + + // + // Get the membership count for this account + // + + BufferSize = 0; + + NtStatus = SampRetrieveAliasMembership( + Sids[i], + &MembershipCount, + &BufferSize, + NULL + ); + + if (NT_SUCCESS(NtStatus) || (NtStatus == STATUS_BUFFER_OVERFLOW)) { + + ASSERT(BufferSize == (MembershipCount * sizeof(ULONG))); + + TotalMembershipCount += MembershipCount; + TotalBufferSize += BufferSize; + + NtStatus = STATUS_SUCCESS; + + } else { + + break; + } + } + + // + // Allocate and fill in the membership array + // + + if (NT_SUCCESS(NtStatus)) { + + Membership->Element = MIDL_user_allocate(TotalBufferSize); + + if (Membership->Element == NULL) { + + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + + } else { + + // + // Fill in the allocated membership list + // + + MembershipIndex = 0; + + for (i=0; i < SidCount; i++) { + + // + // Get the membership list for this account + // + + BufferSize = TotalBufferSize; + + NtStatus = SampRetrieveAliasMembership( + Sids[i], + &MembershipCount, + &BufferSize, + &(Membership->Element[MembershipIndex]) + ); + + if (!NT_SUCCESS(NtStatus)) { + break; + } + + ASSERT(BufferSize == (MembershipCount * sizeof(*(Membership->Element)))); + + // + // Remove duplicate aliases + // + + if (MembershipCount > 0) { + + ULONG ExistingIndex, NewIndex; + + for (ExistingIndex = 0; ExistingIndex < MembershipIndex; ExistingIndex ++) { + + for (NewIndex = MembershipIndex; NewIndex < MembershipIndex + MembershipCount; NewIndex ++) { + + if (Membership->Element[ExistingIndex] == + Membership->Element[NewIndex]) { + + // + // This alias is already in the list - forget it + // + + if (NewIndex < MembershipIndex + MembershipCount - 1) { + + // + // Remove the duplicate alias + // + + Membership->Element[NewIndex] = + Membership->Element[MembershipIndex + MembershipCount - 1]; + + NewIndex --; // So we come back to this alias again + } + + MembershipCount --; + TotalMembershipCount --; + } + } + } + } + + MembershipIndex += MembershipCount; + + ASSERT(MembershipIndex <= TotalMembershipCount); + ASSERT(TotalBufferSize >= BufferSize); + + TotalBufferSize -= BufferSize; + } + + if (!NT_SUCCESS(NtStatus)) { + MIDL_user_free(Membership->Element); + Membership->Element = NULL; + } else { + Membership->Count = TotalMembershipCount; + } + } + } + + return NtStatus; +} + + +NTSTATUS +SampAlQueryMembersOfAlias( + IN SAMPR_HANDLE AliasHandle, + OUT PSAMPR_PSID_ARRAY MemberSids + ) + +/*++ + +Routine Description: + + This function returns an array of Sids of accounts that are members of + a specified alias. + +Arguments: + + AliasHandle - Handle to an Alias object + + MemberSids - Receives an array of Sids that belong to the Alias + +Return Value: + +--*/ + +{ + NTSTATUS Status; + PSAMP_AL_ALIAS_MEMBER_LIST AliasMemberList = NULL; + PSID *Members = NULL; + ULONG AliasMemberCount; + + SAMTRACE("SampAlQueryMembersOfAlias"); + + Status = SampRetrieveAliasMembers( + AliasHandle, + &AliasMemberCount, + &Members + ); + + if (!NT_SUCCESS(Status)) { + + goto QueryMembersOfAliasError; + } + +QueryMembersOfAliasFinish: + + MemberSids->Count = AliasMemberCount; + MemberSids->Sids = (PSAMPR_SID_INFORMATION) Members; + return(Status); + +QueryMembersOfAliasError: + + AliasMemberCount = 0; + Members = NULL; + goto QueryMembersOfAliasFinish; +} + + +NTSTATUS +SampAlAddMembersToAlias( + IN SAMPR_HANDLE AliasHandle, + IN ULONG Options, + IN PSAMPR_PSID_ARRAY MemberSids + ) + +/*++ + +Routine Description: + + This function adds one or more member to an alias. Any failure results + in the in-memory Alias Information being discarded. + + WARNING: The calling function must perform all parameter validation and + the SAM Database Write Lock must be held. + +Parameters: + + AliasHandle - The handle of an opened alias to operate on. + + Options - Specifies optional actions to be taken + + SAMP_AL_VERIFY_NO_MEMBERS_IN_ALIAS - Verify that none of the + Members are already present in the Alias. + + MemberSids - Array of member Sids to be added. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_MEMBER_IN_ALIAS - The member already belongs to the alias. + +--*/ + +{ + NTSTATUS Status; + PSAMP_AL_MEMBER_ALIAS_LIST MemberAliasList = NULL; + PSAMP_AL_MEMBER_ALIAS_LIST OldMemberAliasList = NULL; + PSAMP_AL_MEMBER_DOMAIN MemberDomain = NULL; + PSAMP_AL_MEMBER_ACCOUNT MemberAccount = NULL; + ULONG AliasRid = ((PSAMP_OBJECT) AliasHandle)->TypeBody.Alias.Rid; + ULONG MemberRid, SidIndex, MembershipCount; + PSID DomainSid = NULL; + PSID MemberSid = NULL; + SAMPR_ULONG_ARRAY AliasRids; + + SAMTRACE("SampAlAddMembersToAlias"); + + AliasRids.Count = 0; + AliasRids.Element = NULL; + + // + // Verify that the cached Alias Membership information is valid. + // + + if (!SampAlInfoIsValidForAlias(AliasHandle)) { + + goto AddMembersToAliasFinish; + } + + // + // If requested, verify that none of members already belong to the alias + // + + if (Options & SAMP_AL_VERIFY_NO_MEMBERS_IN_ALIAS) { + + Status = SampAlLookupMembersInAlias( + AliasHandle, + AliasRid, + MemberSids, + &MembershipCount + ); + + if (!NT_SUCCESS(Status)) { + + goto AddMembersToAliasError; + } + + Status = STATUS_MEMBER_NOT_IN_ALIAS; + + if (MembershipCount > 0) { + + goto AddMembersToAliasError; + } + + Status = STATUS_SUCCESS; + } + + // + // Allocate Scratch Sid buffer. We will use this same buffer for splitting + // each Sid. + // + + DomainSid = MIDL_user_allocate( RtlLengthRequiredSid( 256 )); + + Status = STATUS_NO_MEMORY; + + if (DomainSid == NULL) { + + goto AddMembersToAliasError; + } + + Status = STATUS_SUCCESS; + + // + // Obtain pointer to Member Alias List. + // + + MemberAliasList = SampAlAliasHandleToMemberAliasList( AliasHandle ); + OldMemberAliasList = MemberAliasList; + + // + // For each Sid, obtain its DomainSid and Rid. Then lookup its + // DomainSid to obtain the MemberDomain, creating one if necessary. + // Then lookup its Rid to obtain its MemberAccount, creating one + // if necessary. Then add the Alias to the MemebrAccount. + // + + for (SidIndex = 0; SidIndex < MemberSids->Count; SidIndex++ ) { + + MemberSid = MemberSids->Sids[ SidIndex ].SidPointer; + + Status = SampSplitSid( MemberSid, &DomainSid, &MemberRid ); + + if (!NT_SUCCESS(Status)) { + + break; + } + + // + // Lookup the Member Domain for this DomainSid in the Member Alias + // List. + // + + Status = SampAlLookupMemberDomain( + MemberAliasList, + DomainSid, + &MemberDomain + ); + + if (!NT_SUCCESS(Status)) { + + if (Status != STATUS_NO_SUCH_DOMAIN) { + + break; + } + + Status = STATUS_SUCCESS; + + // + // The Member Domain was not found. Create a new Member Domain + // + + Status = SampAlCreateMemberDomain( + &MemberAliasList, + DomainSid, + &MemberDomain + ); + + if (!NT_SUCCESS(Status)) { + + break; + } + + // + // Create a Member Account entry. + // + + Status = SampAlCreateMemberAccount( + &MemberAliasList, + &MemberDomain, + MemberRid, + SAMP_AL_INITIAL_MEMBER_ACCOUNT_ALIAS_CAPACITY, + &MemberAccount + ); + + if (!NT_SUCCESS(Status)) { + + break; + } + + } else { + + // + // We found the domain. This means that we have to lookup + // each Member Account. If a Member Account does not exist, + // we'll create one. Note that we may already have one due + // to this account being a member of another Alias. + // + + Status = SampAlLookupMemberAccount( + MemberDomain, + MemberRid, + &MemberAccount + ); + + if (!NT_SUCCESS(Status)) { + + if (Status != STATUS_NO_SUCH_MEMBER) { + + break; + } + + // + // Create a Member Account for this Rid, + // + + Status = SampAlCreateMemberAccount( + &MemberAliasList, + &MemberDomain, + MemberRid, + SAMP_AL_INITIAL_MEMBER_ACCOUNT_ALIAS_CAPACITY, + &MemberAccount + ); + + if (!NT_SUCCESS(Status)) { + + break; + } + } + } + + // + // We now have a MemberAccount. Now add the Alias to it. + // + + AliasRids.Count = 1; + AliasRids.Element = &AliasRid; + + Status = SampAlAddAliasesToMemberAccount( + &MemberAliasList, + &MemberDomain, + &MemberAccount, + 0, + &AliasRids + ); + + if (!NT_SUCCESS(Status)) { + + break; + } + + // + // Deal with next Member Sid for the Alias. + // + } + + if (!NT_SUCCESS(Status)) { + + goto AddMembersToAliasError; + } + + // + // If the Member Alias List has been reallocated, store its new address. + // + + if (MemberAliasList != OldMemberAliasList) { + + SampAlUpdateMemberAliasList( AliasHandle, MemberAliasList ); + } + +AddMembersToAliasFinish: + + // + // If necessary, free the DomainSid. + // + + if (DomainSid != NULL) { + + MIDL_user_free( DomainSid ); + DomainSid = NULL; + } + + return(Status); + +AddMembersToAliasError: + + goto AddMembersToAliasFinish; +} + + +NTSTATUS +SampAlRemoveMembersFromAlias( + IN SAMPR_HANDLE AliasHandle, + IN ULONG Options, + IN PSAMPR_PSID_ARRAY MemberSids + ) + +/*++ + +Routine Description: + + This function removes a list of members from an Alias. + +Arguments: + + AliasHandle - The handle of an opened alias to operate on. + + Options - Specifies optional actions to be taken + + SAMP_AL_VERIFY_ALL_MEMBERS_IN_ALIAS - Verify that all of the + Members belong to the Alias. + + MemberSids - Array of member Sids to be removed. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + +--*/ + +{ + NTSTATUS Status; + PSAMP_AL_MEMBER_ALIAS_LIST MemberAliasList = NULL; + PSAMP_AL_MEMBER_ALIAS_LIST OldMemberAliasList = NULL; + PSAMP_AL_MEMBER_DOMAIN MemberDomain = NULL; + PSAMP_AL_MEMBER_ACCOUNT MemberAccount = NULL; + BOOLEAN MemberDomainDeleted; + BOOLEAN MemberAccountDeleted; + ULONG AliasRid = ((PSAMP_OBJECT) AliasHandle)->TypeBody.Alias.Rid; + ULONG MemberRid, SidIndex, MembershipCount; + PSID DomainSid = NULL; + PSID MemberSid = NULL; + SAMPR_ULONG_ARRAY AliasRids; + + SAMTRACE("SampAlRemoveMembersFromAlias"); + + AliasRids.Count = 0; + AliasRids.Element = NULL; + + // + // If requested, verify that all of members already belong to the alias + // + + if (Options & SAMP_AL_VERIFY_ALL_MEMBERS_IN_ALIAS) { + + Status = SampAlLookupMembersInAlias( + AliasHandle, + AliasRid, + MemberSids, + &MembershipCount + ); + + if (!NT_SUCCESS(Status)) { + + goto RemoveMembersFromAliasError; + } + + Status = STATUS_MEMBER_NOT_IN_ALIAS; + + if (MembershipCount < MemberSids->Count) { + + goto RemoveMembersFromAliasError; + } + + Status = STATUS_SUCCESS; + } + + // + // Obtain pointer to Member Alias List. + // + + MemberAliasList = SampAlAliasHandleToMemberAliasList( AliasHandle ); + OldMemberAliasList = MemberAliasList; + + if (!NT_SUCCESS(Status)) { + + goto RemoveMembersFromAliasError; + } + + // + // For each Sid, obtain its DomainSid and Rid. Then lookup its + // DomainSid to obtain the MemberDomain. Then lookup its Rid to obtain + // its MemberAccount. Then remove the Alias from the MemberAccount. + // + + for (SidIndex = 0; SidIndex < MemberSids->Count; SidIndex++ ) { + + MemberSid = MemberSids->Sids[ SidIndex ].SidPointer; + + SampSplitSid( MemberSid, &DomainSid, &MemberRid ); + + // + // Lookup the Member Domain for this DomainSid in the Member Alias + // List. + // + + Status = SampAlLookupMemberDomain( + MemberAliasList, + DomainSid, + &MemberDomain + ); + + if (!NT_SUCCESS(Status)) { + + break; + } + + if (!NT_SUCCESS(Status)) { + + if (Status != STATUS_MEMBER_NOT_IN_ALIAS) { + + break; + } + + if (Options & SAMP_AL_VERIFY_ALL_MEMBERS_IN_ALIAS) { + + ASSERT( FALSE ); + } + + Status = STATUS_SUCCESS; + continue; + } + + // + // We found the domain. This means that we have to lookup + // each Member Account. If a Member Account does not exist, + // we'll just skip this account unless we already checked existence. + // If we checked existence and we can't find it now, its an + // internal error. + // + + Status = SampAlLookupMemberAccount( + MemberDomain, + MemberRid, + &MemberAccount + ); + + if (!NT_SUCCESS(Status)) { + + if (Status != STATUS_MEMBER_NOT_IN_ALIAS) { + + break; + } + + if (Options & SAMP_AL_VERIFY_ALL_MEMBERS_IN_ALIAS) { + + ASSERT( FALSE); + } + + Status = STATUS_SUCCESS; + continue; + } + + // + // We now have the MemberAccount. Now remove the Alias from it. + // + + AliasRids.Count = 1; + AliasRids.Element = &AliasRid; + + Status = SampAlRemoveAliasesFromMemberAccount( + &MemberAliasList, + &MemberDomain, + &MemberAccount, + 0, + &AliasRids, + &MemberDomainDeleted, + &MemberAccountDeleted + ); + + if (!NT_SUCCESS(Status)) { + + break; + } + + // + // Deal with next Member Sid for the Alias. + // + + MIDL_user_free( DomainSid ); + DomainSid = NULL; + } + + if (!NT_SUCCESS(Status)) { + + goto RemoveMembersFromAliasError; + } + + // + // If the Member Alias List has been reallocated, store its new address. + // + + if (MemberAliasList != OldMemberAliasList) { + + SampAlUpdateMemberAliasList( AliasHandle, MemberAliasList ); + } + +RemoveMembersFromAliasFinish: + + return(Status); + +RemoveMembersFromAliasError: + + goto RemoveMembersFromAliasFinish; +} + + +NTSTATUS +SampAlLookupMembersInAlias( + IN SAMPR_HANDLE AliasHandle, + IN ULONG AliasRid, + IN PSAMPR_PSID_ARRAY MemberSids, + OUT PULONG MembershipCount + ) + +/*++ + +Routine Description: + + This function checks how many of a given list of Member Sids belong + to an Alias. It is called prior to updating Alias Memberships. + +Arguments: + + AliasHandle - Handle to Alias Object + + AliasRid - Specifies the Rid of the Alias + + MemberSids - Pointer to counted array of pointers to Member Sids + + MembershipCount - Receives count of member Sids in the given set + that belong to the alias. + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + SAMPR_PSID_ARRAY AliasMemberSids; + ULONG OutputMembershipCount = 0; + ULONG SidIndex, AliasMemberSidIndex; + PSID Sid = NULL; + PSID AliasMemberSid = NULL; + + SAMTRACE("SampAlLookupMembersInAlias"); + + // + // First, query the members of the Alias. + // + + Status = SampAlQueryMembersOfAlias( + AliasHandle, + &AliasMemberSids + ); + + if (!NT_SUCCESS(Status)) { + + goto LookupMembersInAliasError; + } + + // + // Now scan each of the given Member Sids and count it if it is a member + // of the Alias. + // + + for (SidIndex = 0; SidIndex < MemberSids->Count; SidIndex++) { + + Sid = MemberSids->Sids[ SidIndex].SidPointer; + + for (AliasMemberSidIndex = 0; + AliasMemberSidIndex = AliasMemberSids.Count; + AliasMemberSidIndex++) { + + AliasMemberSid = AliasMemberSids.Sids[ AliasMemberSidIndex].SidPointer; + + if (RtlEqualSid( Sid, AliasMemberSid)) { + + OutputMembershipCount++; + } + } + } + + *MembershipCount = OutputMembershipCount; + +LookupMembersInAliasFinish: + + return(Status); + +LookupMembersInAliasError: + + *MembershipCount =0; + goto LookupMembersInAliasFinish; +} + + +NTSTATUS +SampAlDeleteAlias( + IN SAMPR_HANDLE *AliasHandle + ) + +/*++ + +Routine Description: + + This function deletes an alias. + +Arguments: + + AliasHandle - Pointer to Handle to Alias + +Return Values: + + NTSTATUS - Standard Nt Result Code + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PSAMP_AL_MEMBER_ALIAS_LIST MemberAliasList = NULL; + PSAMP_AL_MEMBER_DOMAIN MemberDomain = NULL; + PSAMP_AL_MEMBER_ACCOUNT MemberAccount = NULL; + BOOLEAN MemberDomainDeleted; + BOOLEAN MemberAccountDeleted; + ULONG AliasRid = ((PSAMP_OBJECT) *AliasHandle)->TypeBody.Alias.Rid; + LONG DomainIndex; + ULONG RidCount; + LONG DomainCount; + ULONG AccountIndex; + SAMPR_ULONG_ARRAY AliasRids; + AliasRids.Count = 1; + AliasRids.Element = &AliasRid; + + SAMTRACE("SampAlDeleteAlias"); + + // + // Obtain pointer to Member Alias List. + // + + MemberAliasList = SampAlAliasHandleToMemberAliasList( *AliasHandle ); + + if (!NT_SUCCESS(Status)) { + + goto DeleteAliasError; + } + + // + // Traverse the Member Alias List. Look in every Member Account for the + // Alias and remove it if present. This is rather slow if there is a + // large number of alias relationships for diverse domains. + // + DomainCount = (LONG) MemberAliasList->DomainCount; + for (DomainIndex = 0, + MemberDomain = SampAlFirstMemberDomain( MemberAliasList ); + DomainIndex < DomainCount; + DomainIndex++ ) { + + RidCount = MemberDomain->RidCount; + for (AccountIndex = 0, + MemberAccount = SampAlFirstMemberAccount( MemberDomain ); + AccountIndex < RidCount; + AccountIndex++ ) { + + ASSERT(MemberAccount->Signature == SAMP_AL_MEMBER_ACCOUNT_SIGNATURE); + // + // We now have the MemberAccount. Now remove the Alias from it. + // + + Status = SampAlRemoveAliasesFromMemberAccount( + &MemberAliasList, + &MemberDomain, + &MemberAccount, + 0, + &AliasRids, + &MemberDomainDeleted, + &MemberAccountDeleted + ); + + if (!NT_SUCCESS(Status)) { + + if (Status == STATUS_MEMBER_NOT_IN_ALIAS) { + + Status = STATUS_SUCCESS; + continue; + } + + break; + } + + // + // Move the the next member account unless the one we were pointing + // to was deleted (in which case the next one moved to us). + // + + if (!MemberAccountDeleted) { + MemberAccount = SampAlNextMemberAccount( MemberAccount ); + } + + // + // If the member domain was deleted, then the count of members + // is off as is the member account pointer. + // + + if (MemberDomainDeleted) { + break; + } + } + + if (!NT_SUCCESS(Status)) { + + break; + } + + // + // Move the the next member domain unless the one we were pointing + // to was deleted (in which case the next one moved to us). + // + + if (!MemberDomainDeleted) { + MemberDomain = SampAlNextMemberDomain( MemberDomain ); + } + } + + if (!NT_SUCCESS(Status)) { + + goto DeleteAliasError; + } + +DeleteAliasFinish: + + return(Status); + +DeleteAliasError: + + goto DeleteAliasFinish; + +} + + +NTSTATUS +SampAlRemoveAccountFromAllAliases( + IN PSID AccountSid, + IN BOOLEAN CheckAccess, + IN SAMPR_HANDLE DomainHandle OPTIONAL, + IN PULONG MembershipCount OPTIONAL, + IN PULONG *Membership OPTIONAL + ) + +/*++ + +Routine Description: + + This routine removes the specified account from the member list of all + aliases in this domain. + +Arguments: + + AccountSid - The SID of the account being Removed. + + CheckAccess - if TRUE, this routine will make sure that the caller + is allowed REMOVE_ALIAS_MEMBER access to this alias. If FALSE, + the caller is already known to have proper access. + + DomainHandle - if CheckAccess is TRUE, this handle must be provided + to allow access to be checked. + + MembershipCount - if CheckAccess is TRUE, this pointer must be + provided to receive the number of aliases the account was + deleted from. + + Membership - if CheckAccess is TRUE, this pointer must be provided + to point to a list of aliases the account was removed from. The + caller must free this list with MIDL_user_free(). + +Return Value: + + + STATUS_SUCCESS - The user has been Removed from all aliases. + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PSAMP_AL_MEMBER_ALIAS_LIST MemberAliasList = NULL; + PSAMP_AL_MEMBER_DOMAIN MemberDomain = NULL; + PSAMP_AL_MEMBER_ACCOUNT MemberAccount = NULL; + BOOLEAN MemberDomainDeleted; + PSID DomainSid = NULL; + LONG DomainIndex; + ULONG MemberRid, AliasRid; + SAMPR_ULONG_ARRAY AliasRids; + AliasRids.Count = 1; + AliasRids.Element = &AliasRid; + + SAMTRACE("SampAlRemoveAccountFromAllAliases"); + + // + // Obtain pointer to Member Alias List for the Current Transaction Domain. + // + + DomainIndex = SampTransactionDomainIndex; + MemberAliasList = SampAlDomainIndexToMemberAliasList( DomainIndex ); + + // + // We remove the Account from all aliases by locating its Member Account + // structure and deleting it. First, find the Member Domain. + // + + SampSplitSid( AccountSid, &DomainSid, &MemberRid ); + + // + // Lookup the Member Domain for this DomainSid in the Member Alias + // List. + // + + Status = SampAlLookupMemberDomain( + MemberAliasList, + DomainSid, + &MemberDomain + ); + + if (!NT_SUCCESS(Status)) { + + if (Status != STATUS_NO_SUCH_DOMAIN) { + + goto RemoveAccountFromAllAliasesError; + } + + // + // There is no member Domain object for this account. This means + // the account does not belong to any aliases. + // + + Status = STATUS_SUCCESS; + + goto RemoveAccountFromAllAliasesFinish; + } + + // + // We found the Member Domain. Now find the Member Account. + // + + Status = SampAlLookupMemberAccount( + MemberDomain, + MemberRid, + &MemberAccount + ); + + if (!NT_SUCCESS(Status)) { + + if (Status != STATUS_NO_SUCH_MEMBER) { + + goto RemoveAccountFromAllAliasesError; + } + + Status = STATUS_SUCCESS; + + goto RemoveAccountFromAllAliasesFinish; + } + + // + // If CheckAccess = TRUE, return a list of Aliases that the account was + // a member of. + // + + if (CheckAccess) { + + *Membership = MIDL_user_allocate( MemberAccount->AliasCount * sizeof(ULONG)); + *MembershipCount = MemberAccount->AliasCount; + + Status = STATUS_NO_MEMORY; + + if (*Membership == NULL) { + + goto RemoveAccountFromAllAliasesError; + } + + Status = STATUS_SUCCESS; + } + + // + // We now have the MemberAccount. Now delete it, thereby removing the + // account from all Aliases. + // + + Status = SampAlDeleteMemberAccount( + &MemberAliasList, + &MemberDomain, + MemberAccount, + &MemberDomainDeleted + ); + + if (!NT_SUCCESS(Status)) { + + goto RemoveAccountFromAllAliasesError; + } + +RemoveAccountFromAllAliasesFinish: + + // + // Free the Domain Sid buffer (if any) + // + + if (DomainSid != NULL) { + + MIDL_user_free( DomainSid ); + DomainSid = NULL; + } + + return(Status); + +RemoveAccountFromAllAliasesError: + + if (CheckAccess) { + + *Membership = NULL; + *MembershipCount = 0; + } + + goto RemoveAccountFromAllAliasesFinish; +} + + +NTSTATUS +SampAlBuildAliasInformation( + ) + +/*++ + +Routine Description: + + This function builds the Alias Information for each of the SAM Local + Domains. For each Domain, this information consists of the Member Alias + List. + +Arguments: + + None. + +Return Values: + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + LONG DomainIndex; + + SAMTRACE("SampAlBuildAliasInformation"); + + for (DomainIndex = 0; DomainIndex < (LONG) SampDefinedDomainsCount; DomainIndex++) { + + if (SampAlEnableBuildingOfList[ DomainIndex]) { + + Status = SampAlBuildMemberAliasList( DomainIndex); + + if (!NT_SUCCESS(Status)) { + + break; + } + } + } + + if (!NT_SUCCESS(Status)) { + + goto BuildAliasInformationError; + } + +BuildAliasInformationFinish: + + return(Status); + +BuildAliasInformationError: + + goto BuildAliasInformationFinish; +} + + +//////////////////////////////////////////////////////////////////////////// +// // +// Private functions // +// // +//////////////////////////////////////////////////////////////////////////// + +NTSTATUS +SampAlCreateMemberAccount( + IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, + IN OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain, + IN ULONG Rid, + IN ULONG AliasCapacity, + OUT PSAMP_AL_MEMBER_ACCOUNT *MemberAccount + ) + +/*++ + +Routine Description: + + This function creates an empty Member Account in the specified Member Domain + for the specified Member Rid. There must not already be al account for this + Rid. The Member Account is appended to the end of any existing ones in the + Member Domain. + +Arguments: + + MemberAliasList - Pointer to pointer to Member Alias List. + + MemberDomain - Pointer to Member Domain in which the Member Account is + to be created. The Member Domain must already exist. + + Rid - Specifies the Account Rid. + + AliasCapacity - Specifies the initial number of Alias Rids that the + MemberAccount can hold. + + MemberAccount - Receives pointer to the newly created Member Account. + +--*/ + +{ + NTSTATUS Status; + ULONG MaximumLengthMemberAccount; + PSAMP_AL_MEMBER_ACCOUNT OutputMemberAccount = NULL; + + SAMTRACE("SampAlCreateMemberAccount"); + + // + // Calculate the length of data needed for the new member Account entry. + // + + MaximumLengthMemberAccount = SampAlLengthRequiredMemberAccount( AliasCapacity ); + + // + // Allocate space for the Member Account. + // + + Status = SampAlAllocateMemberAccount( + MemberAliasList, + MemberDomain, + MaximumLengthMemberAccount, + &OutputMemberAccount + ); + + if (!NT_SUCCESS(Status)) { + + goto CreateMemberAccountError; + } + + // + // Scratch the new Member Account + // + + OutputMemberAccount->Signature = SAMP_AL_MEMBER_ACCOUNT_SIGNATURE; + OutputMemberAccount->MaximumLength = MaximumLengthMemberAccount; + OutputMemberAccount->UsedLength = + SampAlOffsetFirstAlias( OutputMemberAccount ); + ASSERT(OutputMemberAccount->MaximumLength >= + OutputMemberAccount->UsedLength); + OutputMemberAccount->Rid = Rid; + OutputMemberAccount->AliasCount = 0; + + ((*MemberDomain)->RidCount)++; + *MemberAccount = OutputMemberAccount; + +CreateMemberAccountFinish: + + return(Status); + +CreateMemberAccountError: + + *MemberAccount = NULL; + SampAlInfoMakeInvalid( (*MemberAliasList)->DomainIndex ); + + goto CreateMemberAccountFinish; +} + + +NTSTATUS +SampAlAllocateMemberAccount( + IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, + IN OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain, + IN ULONG MaximumLengthMemberAccount, + OUT PSAMP_AL_MEMBER_ACCOUNT *MemberAccount + ) + +/*++ + +Routine Description: + + This function allocates the space for a new Member Account in a Member + Domain. If necessary, the Mmeber Domain and its associated Member Alias + List will be grown. + +Arguments: + + MemberAliasList - Pointer to pointer to the Member Alias List. + + MemberDomain - Pointer to pointer to the Member Domain + + MaximumLengthMemberAccount - Initial Maximum Length required for the + Member Account + + MemberAccount - receives pointer to the newly allocated Member Account + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + ULONG SpaceAvailable; + + SAMTRACE("SampAlAllocateMemberAccount"); + + // + // Calculate the space available in the Member Domain + // + + SpaceAvailable = (*MemberDomain)->MaximumLength - (*MemberDomain)->UsedLength; + + if (MaximumLengthMemberAccount > SpaceAvailable) { + + Status = SampAlGrowMemberDomain( + MemberAliasList, + MemberDomain, + MaximumLengthMemberAccount - SpaceAvailable + ); + + if (!NT_SUCCESS(Status)) { + + goto AllocateMemberAccountError; + } + } + + // + // The Member Domain is now guaranteed to be large enough. Reserve the + // space for the new Member Account. + // + + *MemberAccount = SampAlNextNewMemberAccount(*MemberDomain); + (*MemberDomain)->UsedLength += MaximumLengthMemberAccount; + ASSERT((*MemberDomain)->MaximumLength >= + (*MemberDomain)->UsedLength); + +AllocateMemberAccountFinish: + + return(Status); + +AllocateMemberAccountError: + + SampAlInfoMakeInvalid( (*MemberAliasList)->DomainIndex ); + *MemberAccount = NULL; + + goto AllocateMemberAccountFinish; +} + + +NTSTATUS +SampAlGrowMemberAccount( + IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, + IN OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain, + IN OUT PSAMP_AL_MEMBER_ACCOUNT *MemberAccount, + IN ULONG ExtraSpaceRequired + ) + +/*++ + +Routine Description: + + This function grows a Member Account by at least the requested amount. If + necessary, the containing Member Domain and Member Alias List will also be + grown. + +Arguments: + + MemberAliasList - Pointer to pointer to the Member Alias List. + + MemberDomain - Pointer to pointer to the Member Domain + + MemberAccount - Pointer to Pointer to the Member Account. + + ExtraSpaceRequired - Extra space needed in the Member Account. + +Return Values: + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + ULONG SpaceAvailable, MemberAccountOffset, CopyLength; + PUCHAR Destination = NULL; + PUCHAR Source = NULL; + + SAMTRACE("SampAlGrowMemberAccount"); + + // + // Calculate the space available in the Member Domain + // + + SpaceAvailable = (*MemberDomain)->MaximumLength - (*MemberDomain)->UsedLength; + + if (ExtraSpaceRequired > SpaceAvailable) { + + // + // We need to grow the Member Domain. Calculate the offset of the + // Member Account in the old Member Domain, grow the Member Domain + // and then calculate the new address of the Member Account. + // + + MemberAccountOffset = SampAlMemberAccountToOffset( + *MemberDomain, + *MemberAccount + ); + + Status = SampAlGrowMemberDomain( + MemberAliasList, + MemberDomain, + ExtraSpaceRequired - SpaceAvailable + ); + + if (!NT_SUCCESS(Status)) { + + goto GrowMemberAccountError; + } + + *MemberAccount = SampAlMemberAccountFromOffset( + *MemberDomain, + MemberAccountOffset + ); + + } + + // + // The Member Domain is now guaranteed to be large enough. + // Now shift any Member Accounts that follow the one being grown + // up to make room for the expanded Member Account. The source address + // for the move is the address of the next Member Account (if any) based + // on the existing size of the Member Account. The destination address + // of the move is the address of the next Member Account (if any) based + // on the new size of the Member Account. + // + + Source = (PUCHAR) SampAlNextMemberAccount( *MemberAccount ); + (*MemberAccount)->MaximumLength += ExtraSpaceRequired; + Destination = (PUCHAR) SampAlNextMemberAccount( *MemberAccount ); + CopyLength = + (((PUCHAR)(SampAlNextNewMemberAccount(*MemberDomain))) - Source); + + // + // Reserve the space in the Member Domain. If all's well, the + // end of the destination buffer should match the updated end of the + // used area of the Member Domain. + // + + (*MemberDomain)->UsedLength += ExtraSpaceRequired; + ASSERT((*MemberDomain)->MaximumLength >= + (*MemberDomain)->UsedLength); + + ASSERT( Destination + CopyLength == + (PUCHAR) SampAlNextNewMemberAccount( *MemberDomain )); + ASSERT( Destination + CopyLength <= + (PUCHAR)(*MemberAliasList) + (*MemberAliasList)->MaximumLength ); + ASSERT( Destination + CopyLength <= + (PUCHAR)(*MemberDomain) + (*MemberDomain)->MaximumLength ); + + if (CopyLength > 0) { + + RtlMoveMemory( Destination, Source, CopyLength ); + } + +GrowMemberAccountFinish: + + return(Status); + +GrowMemberAccountError: + + SampAlInfoMakeInvalid( (*MemberAliasList)->DomainIndex ); + + goto GrowMemberAccountFinish; +} + + +NTSTATUS +SampAlLookupMemberAccount( + IN PSAMP_AL_MEMBER_DOMAIN MemberDomain, + IN ULONG MemberRid, + OUT PSAMP_AL_MEMBER_ACCOUNT *MemberAccount + ) + +/*++ + +Routine Description: + + This function looks up an Account Rid in a Member Domain to see if there + is a Member Account structure for it. + +Arguments: + + MemberDomain - Pointer to Member Domain + + MemberRid - Specifies the Account Rid + + MemberAccount - Receives pointer to Member Account if found. + +--*/ + +{ + NTSTATUS Status; + PSAMP_AL_MEMBER_ACCOUNT NextMemberAccount = NULL; + ULONG RidIndex; + BOOLEAN AccountFound = FALSE; + + SAMTRACE("SampAlLookupMemberAccount"); + + + for (RidIndex = 0, + NextMemberAccount = SampAlFirstMemberAccount( MemberDomain ); + RidIndex < MemberDomain->RidCount; + RidIndex++, NextMemberAccount = SampAlNextMemberAccount( NextMemberAccount)) { + + if (MemberRid == NextMemberAccount->Rid) { + + AccountFound = TRUE; + + break; + } + } + + Status = STATUS_NO_SUCH_MEMBER; + + if (!AccountFound) { + + goto LookupMemberAccountError; + } + + *MemberAccount = NextMemberAccount; + Status = STATUS_SUCCESS; + +LookupMemberAccountFinish: + + return(Status); + +LookupMemberAccountError: + + goto LookupMemberAccountFinish; +} + + +NTSTATUS +SampAlAddAliasesToMemberAccount( + IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, + IN OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain, + IN OUT PSAMP_AL_MEMBER_ACCOUNT *MemberAccount, + IN ULONG Options, + IN PSAMPR_ULONG_ARRAY AliasRids + ) + +/*++ + +Routine Description: + + This function adds an array of aliases to a Member Account. An error + will be returned if any of the aliases exist in the Member Account. + If necessary, the containing Member Account, Member Domain and Member + Alias List will also be grown. + +Arguments: + + MemberAliasList - Pointer to pointer to the Member Alias List. + + MemberDomain - Pointer to pointer to the Member Domain + + MemberAccount - Pointer to Pointer to the Member Account. + + Options - Specifies optional actions to be taken + + SAMP_AL_VERIFY_NO_ALIASES_IN_ACCOUNT - Verify that none of the + Aliases presented belong to the various Member Accounts. + + AliasRids - Pointer to counted array of Alias Rids. + +Return Values: + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + ULONG SpaceRequired, SpaceAvailable, CopyLength; + PUCHAR Source = NULL; + PUCHAR Destination = NULL; + ULONG ExistingAliasCount; + + SAMTRACE("SampAlAddAliasesToMemberAccount"); + + // + // If requested, verify that none of the Aliases are already + // in the Member Account + // + + if (Options & SAMP_AL_VERIFY_NO_ALIASES_IN_ACCOUNT) { + + Status = SampAlLookupAliasesInMemberAccount( + *MemberAccount, + AliasRids, + &ExistingAliasCount + ); + + if (!NT_SUCCESS(Status)) { + + goto AddAliasesToMemberAccountError; + } + + Status = STATUS_MEMBER_IN_ALIAS; + + if (ExistingAliasCount > 0) { + + goto AddAliasesToMemberAccountError; + } + + Status = STATUS_SUCCESS; + } + + // + // Calculate the space required for the new Aliases. + // + + SpaceRequired = AliasRids->Count * sizeof( ULONG ); + + // + // If there is not enough space available in the Member Account, + // grow it. + // + + SpaceAvailable = (*MemberAccount)->MaximumLength - (*MemberAccount)->UsedLength; + + if (SpaceRequired > SpaceAvailable) { + + Status = SampAlGrowMemberAccount( + MemberAliasList, + MemberDomain, + MemberAccount, + SpaceRequired - SpaceAvailable + ); + + if (!NT_SUCCESS(Status)) { + + goto AddAliasesToMemberAccountError; + } + } + + // + // The Member Account is now large enough. Copy in the aliases. + // + + Destination = (PUCHAR) SampAlNextNewAliasInMemberAccount( *MemberAccount ); + Source = (PUCHAR) AliasRids->Element; + CopyLength = SpaceRequired; + (*MemberAccount)->UsedLength += SpaceRequired; + ASSERT((*MemberAccount)->MaximumLength >= + (*MemberAccount)->UsedLength); + RtlMoveMemory( Destination, Source, CopyLength ); + + // + // Update the count of Aliases both in this Member Account and in the + // Member Alias List. + // + + (*MemberAccount)->AliasCount += AliasRids->Count; + +AddAliasesToMemberAccountFinish: + + return(Status); + +AddAliasesToMemberAccountError: + + goto AddAliasesToMemberAccountFinish; +} + + +NTSTATUS +SampAlLookupAliasesInMemberAccount( + IN PSAMP_AL_MEMBER_ACCOUNT MemberAccount, + IN PSAMPR_ULONG_ARRAY AliasRids, + OUT PULONG ExistingAliasCount + ) + +/*++ + +Routine Description: + + This function checks a set of Alias Rids to see if any are present in a + Member Account. + +Arguments: + + MemberAccount - Pointer to Member Account + + AliasRids - Specifies counted array of Alias Rids. + + ExistingAliasCount - Receives a count of the Alias Rids presented that + are already in the Member Account. + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + ULONG AliasIndex, AliasInMemberAccountIndex; + + SAMTRACE("SampAlLookupAliasesInMemberAccount"); + + // + // Scan the Alias Rids, looking each one up. + // + + for (AliasIndex = 0; AliasIndex < AliasRids->Count; AliasRids++ ) { + + for (AliasInMemberAccountIndex = 0; + AliasInMemberAccountIndex < MemberAccount->AliasCount; + AliasInMemberAccountIndex++) { + + if (AliasRids->Element[ AliasIndex ] == + MemberAccount->AliasRids[ AliasInMemberAccountIndex ] ) { + + (*ExistingAliasCount)++; + } + } + } + + return(Status); +} + + +NTSTATUS +SampAlRemoveAliasesFromMemberAccount( + IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, + IN OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain, + IN OUT PSAMP_AL_MEMBER_ACCOUNT *MemberAccount, + IN ULONG Options, + IN PSAMPR_ULONG_ARRAY AliasRids, + OUT PBOOLEAN MemberDomainDeleted, + OUT PBOOLEAN MemberAccountDeleted + ) + +/*++ + +Routine Description: + + This function removes aliases from a Member Account. The Aliases need + not already exist unless an option to check that they do exist is + specified. No down sizing of the Member Account occurs, but an + empty one will be deleted. + + NOTE: I don't know why ScottBi made MemberAliasList, MemberDomain, and + MemberAccount parameters pointers to pointers. He never updates + the pointers so he could have passed them in directly. JK + +Arguments: + + MemberAliasList - Pointer to pointer to the Member Alias List. + + MemberDomain - Pointer to pointer to the Member Domain + + MemberAccount - Pointer to Pointer to the Member Account. + + Options - Specifies optional actions to be taken + + SAMP_AL_VERIFY_ALL_ALIASES_IN_ACCOUNT - Verify that none of the + Aliases presented belong to the Member Account. + + MemberDomainDeleted - Will be set to TRUE if the member domain + pointed to by MemberDomain was deleted. Otherwise FALSE is returned. + + MemberAccountDeleted - Will be set to TRUE if the member account + pointed to by MemberAccount was deleted. Otherwise FALSE is returned. + + AliasRids - Pointer to counted array of Alias Rids. + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + ULONG ExistingAliasIndex, LastAliasIndex, RemoveAliasIndex, ExistingAlias; + ULONG ExistingAliasCount; + + SAMTRACE("SampAlRemoveAliasesFromMemberAccount"); + + (*MemberDomainDeleted) = FALSE; + (*MemberAccountDeleted) = FALSE; + + // + // If requested, verify that all of the Aliases are already + // in the Member Account + // + + if (Options & SAMP_AL_VERIFY_ALL_ALIASES_IN_ACCOUNT) { + + Status = SampAlLookupAliasesInMemberAccount( + *MemberAccount, + AliasRids, + &ExistingAliasCount + ); + + if (!NT_SUCCESS(Status)) { + + goto RemoveAliasesFromMemberAccountError; + } + + Status = STATUS_MEMBER_IN_ALIAS; + + if (ExistingAliasCount < AliasRids->Count) { + + goto RemoveAliasesFromMemberAccountError; + } + + Status = STATUS_SUCCESS; + } + + // + // If the Member Account is empty, then somebody forgot to delete it + // + + ASSERT((*MemberAccount)->AliasCount != 0); + + + LastAliasIndex = (*MemberAccount)->AliasCount - 1; + + for (ExistingAliasIndex = 0; + ExistingAliasIndex < (*MemberAccount)->AliasCount; + ExistingAliasIndex++) { + + ExistingAlias = (*MemberAccount)->AliasRids[ ExistingAliasIndex ]; + + for (RemoveAliasIndex = 0; + RemoveAliasIndex < AliasRids->Count; + RemoveAliasIndex++) { + + if (ExistingAlias == AliasRids->Element[ RemoveAliasIndex ]) { + + // + // We're to delete this Alias. If this Alias Rid is not at the + // end of the list contained in the Member Account, overwrite + // it with the one at the end of the list. + // + + if (ExistingAliasIndex < LastAliasIndex) { + + (*MemberAccount)->AliasRids[ ExistingAliasIndex] = + (*MemberAccount)->AliasRids[ LastAliasIndex]; + } + + (*MemberAccount)->AliasCount--; + (*MemberAccount)->UsedLength -= sizeof(ULONG); + ASSERT((*MemberAccount)->MaximumLength >= + (*MemberAccount)->UsedLength); + + // + // If the Member Account is now empty, quit. + // + + if ((*MemberAccount)->AliasCount == 0) { + + break; + } + + LastAliasIndex--; + } + } + + // + // If the Member Account is now empty, quit. + // + + if ((*MemberAccount)->AliasCount == 0) { + + break; + } + } + + // + // If the Member Account is now empty, delete it. + // + + if ((*MemberAccount)->AliasCount == 0) { + + Status = SampAlDeleteMemberAccount( + MemberAliasList, + MemberDomain, + *MemberAccount, + MemberDomainDeleted + ); + if (NT_SUCCESS(Status)) { + (*MemberAccountDeleted) = TRUE; + } + } + +RemoveAliasesFromMemberAccountFinish: + + return(Status); + +RemoveAliasesFromMemberAccountError: + + goto RemoveAliasesFromMemberAccountFinish; +} + + +NTSTATUS +SampAlDeleteMemberAccount( + IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, + IN OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain, + IN OUT PSAMP_AL_MEMBER_ACCOUNT MemberAccount, + OUT PBOOLEAN MemberDomainDeleted + ) + +/*++ + +Routine Description: + + This function deletes a Member Account. Currently, the containing + Member Domain and Member Alias List are not shrunk, but the containing + Member Domain will be deleted if empty. + +Arguments: + + MemberAliasList - Pointer to pointer to the Member Alias List. + + MemberDomain - Pointer to pointer to the Member Domain + + MemberAccount - Pointer to the Member Account. + + MemberDomainDeleted - Will be set to TRUE if the member domain + pointed to by MemberDomain was deleted. Otherwise FALSE is returned. + + +Return Values: + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PUCHAR Source = NULL; + PUCHAR Destination = NULL; + ULONG CopyLength; + + SAMTRACE("SampAlDeleteMemberAccount"); + + (*MemberDomainDeleted) = FALSE; + + // + // Calculate pointers for moving the residual portion of the Member + // Domain down to close the gap left by the extant Member Account. + // unused space. The start of the residual portion is the end of the + // Member Account being deleted. The length of the residual portion is + // the distance from the start to the end of the used portion of the + // Member Domain. + // + + Source = (PUCHAR) SampAlNextMemberAccount( MemberAccount ); + Destination = (PUCHAR) MemberAccount; + CopyLength = (PUCHAR) SampAlNextNewMemberAccount( *MemberDomain ) - Source; + + (*MemberDomain)->UsedLength -= MemberAccount->MaximumLength; + ASSERT((*MemberDomain)->MaximumLength >= + (*MemberDomain)->UsedLength); + (*MemberDomain)->RidCount--; + + if (CopyLength > 0) { + + RtlMoveMemory( Destination, Source, CopyLength ); +#if DBG + { + PSAMP_AL_MEMBER_ACCOUNT Member = (PSAMP_AL_MEMBER_ACCOUNT) Destination; + ASSERT(Member->Signature == SAMP_AL_MEMBER_ACCOUNT_SIGNATURE); + } + +#endif + } + + // + // If the Member Domain now has no Member Accounts, delete it. + // + + if ((*MemberDomain)->RidCount == 0) { + + Status = SampAlDeleteMemberDomain( + MemberAliasList, + *MemberDomain + ); + + if (!NT_SUCCESS(Status)) { + goto DeleteMemberAccountError; + } + (*MemberDomainDeleted) = TRUE; + } + +DeleteMemberAccountFinish: + + return(Status); + +DeleteMemberAccountError: + + goto DeleteMemberAccountFinish; +} + + +NTSTATUS +SampAlCreateMemberDomain( + IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, + IN PSID DomainSid, + OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain + ) + +/*++ + +Routine Description: + + This function creates a new Member Domain in the specified Alias Member + List. + +Arguments: + + MemberAliasList - Pointer to pointer to Alias Member List. + + DomainSid - Pointer to Sid of Domain to which this MemberDomain + relates. + + MemberDomain - Receives pointer to the newly created Member Domain. + +Return Values: + +--*/ + +{ + NTSTATUS Status; + PSAMP_AL_MEMBER_DOMAIN OutputMemberDomain = NULL; + PSAMP_AL_MEMBER_ACCOUNT OutputMemberAccount = NULL; + ULONG MaximumLengthMemberDomain; + ULONG DomainSidLength = RtlLengthSid(DomainSid); + ULONG AlternativeLength; + + SAMTRACE("SampAlCreateMemberDomain"); + + + // + // Allocate the Member Domain. + // + + MaximumLengthMemberDomain = SAMP_AL_INITIAL_MEMBER_DOMAIN_LENGTH; + AlternativeLength = FIELD_OFFSET(SAMP_AL_MEMBER_DOMAIN, DomainSid) + + DomainSidLength; + if (MaximumLengthMemberDomain < AlternativeLength) { + MaximumLengthMemberDomain = AlternativeLength; + } + + Status = SampAlAllocateMemberDomain( + MemberAliasList, + MaximumLengthMemberDomain, + &OutputMemberDomain + ); + + if (!NT_SUCCESS(Status)) { + + goto CreateMemberDomainError; + } + + // + // Setup the new Member Domain entry. + // + + OutputMemberDomain->MaximumLength = MaximumLengthMemberDomain; + OutputMemberDomain->RidCount = 0; + OutputMemberDomain->Signature = SAMP_AL_MEMBER_DOMAIN_SIGNATURE; + + RtlCopySid( + DomainSidLength, + &OutputMemberDomain->DomainSid, + DomainSid + ); + + OutputMemberDomain->UsedLength = SampAlOffsetFirstMemberAccount( + OutputMemberDomain + ); + ASSERT(OutputMemberDomain->MaximumLength >= + OutputMemberDomain->UsedLength); + + ((*MemberAliasList)->DomainCount)++; + *MemberDomain = OutputMemberDomain; + +CreateMemberDomainFinish: + + return(Status); + +CreateMemberDomainError: + + *MemberDomain = NULL; + goto CreateMemberDomainFinish; +} + + +NTSTATUS +SampAlAllocateMemberDomain( + IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, + IN ULONG MaximumLengthMemberDomain, + OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain + ) + +/*++ + +Routine Description: + + This function allocates the space for a new Member Domain in a Member + Alias List. If necessary, the Member Alias List will be grown. + +Arguments: + + MemberAliasList - Pointer to pointer to the Member Alias List. + + MaximumLengthMemberDomain - Initial Maximum Length required for the + Member Domain + + MemberDomain - Receives pointer to the Member Domain + +Return Values: + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + ULONG SpaceAvailable; + + SAMTRACE("SampAlAllocateMemberDomain"); + + // + // Calculate the space available in the Member Alias List + // + + SpaceAvailable = (*MemberAliasList)->MaximumLength - (*MemberAliasList)->UsedLength; + + if (MaximumLengthMemberDomain > SpaceAvailable) { + + Status = SampAlGrowMemberAliasList( + MemberAliasList, + MaximumLengthMemberDomain - SpaceAvailable + ); + + if (!NT_SUCCESS(Status)) { + + goto AllocateMemberDomainError; + } + } + + // + // The Member Alias List is now guaranteed to be large enough. Reserve the + // space for the new Member Domain. + // + + *MemberDomain = SampAlNextNewMemberDomain(*MemberAliasList); + (*MemberAliasList)->UsedLength += MaximumLengthMemberDomain; + ASSERT((*MemberAliasList)->MaximumLength >= + (*MemberAliasList)->UsedLength); + +AllocateMemberDomainFinish: + + return(Status); + +AllocateMemberDomainError: + + SampAlInfoMakeInvalid( (*MemberAliasList)->DomainIndex ); + *MemberDomain = NULL; + goto AllocateMemberDomainFinish; +} + + +NTSTATUS +SampAlGrowMemberDomain( + IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, + IN OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain, + IN ULONG ExtraSpaceRequired + ) + +/*++ + +Routine Description: + + This function grows a Member Domain by at least the requested amount. If + necessary, the Member Alias List will also be grown. + +Arguments: + + MemberAliasList - Pointer to pointer to the Member Alias List. + + MemberDomain - Pointer to pointer to the Member Domain + + ExtraSpaceRequired - Extra space needed in the Member Domain. + +Return Values: + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + ULONG SpaceAvailable, MemberDomainOffset, CopyLength; + PUCHAR Destination = NULL; + PUCHAR Source = NULL; + + SAMTRACE("SampAlGrowMemberDomain"); + + // + // Calculate the space available in the Member Alias List + // + + SpaceAvailable = (*MemberAliasList)->MaximumLength - (*MemberAliasList)->UsedLength; + + if (ExtraSpaceRequired > SpaceAvailable) { + + // + // We need to grow the Member Alias List. Calculate the offset of the + // Member Domain in the old Member Alias List, grow the Member Alias + // List and then calculate the new address of the Member Domain. + // + + MemberDomainOffset = SampAlMemberDomainToOffset( + *MemberAliasList, + *MemberDomain + ); + + Status = SampAlGrowMemberAliasList( + MemberAliasList, + ExtraSpaceRequired - SpaceAvailable + ); + + if (!NT_SUCCESS(Status)) { + + goto GrowMemberDomainError; + } + + // + // Calculate the new address of the Member Domain + // + + *MemberDomain = SampAlMemberDomainFromOffset( + *MemberAliasList, + MemberDomainOffset + ); + } + + // + // The Member Alias List is now guaranteed to be large enough. + // Now shift any Member Domains that follow the one being grown + // up to make room for the expanded Member Domain. The source address + // for the move is the address of the next Member Domain (if any) based + // on the existing size of the Member Domain. The destination address + // of the move is the address of the next Member Domain (if any) based + // on the new size of the Member Domain. + // + + Source = (PUCHAR) SampAlNextMemberDomain( *MemberDomain ); + (*MemberDomain)->MaximumLength += ExtraSpaceRequired; + Destination = (PUCHAR) SampAlNextMemberDomain( *MemberDomain ); + CopyLength = + (((PUCHAR)(SampAlNextNewMemberDomain(*MemberAliasList))) - Source); + + // + // Reserve the space in the Member Alias List. If all's well, the + // end of the destination buffer should match the updated end of the + // used area of the member Alias List. + // + + (*MemberAliasList)->UsedLength += ExtraSpaceRequired; + ASSERT((*MemberAliasList)->MaximumLength >= + (*MemberAliasList)->UsedLength); + + ASSERT( Destination + CopyLength == + (PUCHAR) SampAlNextNewMemberDomain( *MemberAliasList )); + ASSERT( Destination + CopyLength <= + (PUCHAR)(*MemberAliasList) + (*MemberAliasList)->MaximumLength ); + + if (CopyLength > 0) { + + RtlMoveMemory( Destination, Source, CopyLength ); + } + +GrowMemberDomainFinish: + + return(Status); + +GrowMemberDomainError: + + SampAlInfoMakeInvalid( (*MemberAliasList)->DomainIndex ); + + goto GrowMemberDomainFinish; +} + + +NTSTATUS +SampAlLookupMemberDomain( + IN PSAMP_AL_MEMBER_ALIAS_LIST MemberAliasList, + IN PSID DomainSid, + OUT PSAMP_AL_MEMBER_DOMAIN *MemberDomain + ) + +/*++ + +Routine Description: + +This function looks up a Domain Sid in a Member Alias List to find its +Member Domain structure (if any). + +Arguments: + + MemberAliasList - Pointer to pointer to Member Alias List + + DomainSid - Domain Sid whose Member Domain is to be found. + +--*/ + +{ + NTSTATUS Status; + PSAMP_AL_MEMBER_DOMAIN NextMemberDomain = NULL; + LONG DomainIndex; + BOOLEAN DomainFound = FALSE; + + SAMTRACE("SampAlLookupMemberDomain"); + + + for (DomainIndex = 0, + NextMemberDomain = SampAlFirstMemberDomain( MemberAliasList ); + DomainIndex < (LONG) MemberAliasList->DomainCount; + DomainIndex++, NextMemberDomain = SampAlNextMemberDomain( NextMemberDomain ) + ) { + + if (RtlEqualSid( DomainSid, &NextMemberDomain->DomainSid)) { + + DomainFound = TRUE; + + break; + } + } + + Status = STATUS_NO_SUCH_DOMAIN; + + if (!DomainFound) { + + goto LookupMemberDomainError; + } + + *MemberDomain = NextMemberDomain; + Status = STATUS_SUCCESS; + +LookupMemberDomainFinish: + + return(Status); + +LookupMemberDomainError: + + *MemberDomain = NULL; + goto LookupMemberDomainFinish; +} + + +NTSTATUS +SampAlDeleteMemberDomain( + IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, + IN OUT PSAMP_AL_MEMBER_DOMAIN MemberDomain + ) + +/*++ + +Routine Description: + + This function deletes a Member Domain. The Member Domain may contain + zero or more Member Accounts. The containing Member Alias List is shrunk. + +Arguments: + + MemberAliasList - Pointer to pointer to the Member Alias List. + + MemberDomain - Pointer to the Member Domain + +Return Values: + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PUCHAR Source = NULL; + PUCHAR Destination = NULL; + ULONG CopyLength; + + SAMTRACE("SampAlDeleteMemberDomain"); + + // + // Calculate pointers for moving the residual portion of the + // Member Alias List down to close the gap left by the extant Member + // Domain. The start of the residual portion is the next Member Domain. + // The size of the portion is the distance between the start and the + // used portion of the Member Alias List. + // + + Source = (PUCHAR) SampAlNextMemberDomain( MemberDomain ); + Destination = (PUCHAR) MemberDomain; + CopyLength = ((PUCHAR) SampAlNextNewMemberDomain( *MemberAliasList )) - Source; + + (*MemberAliasList)->UsedLength -= MemberDomain->MaximumLength; + ASSERT((*MemberAliasList)->MaximumLength >= + (*MemberAliasList)->UsedLength); + (*MemberAliasList)->DomainCount--; + + if (CopyLength > 0) { + + RtlMoveMemory( Destination, Source, CopyLength ); + } + + return(Status); +} + + +NTSTATUS +SampAlCreateMemberAliasList( + IN LONG DomainIndex, + IN ULONG InitialMemberAliasListLength, + OUT OPTIONAL PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList + ) + +/*++ + +Routine Description: + + This function creates an empty Member Alias List for the specified SAM Local + Domain. The Member Alias List will be marked invalid. + +Arguments: + + DomainIndex - Specifies the Local SAM Domain + + InitialMemberAliasListLength - Specifies the initial maximum length of the + Member Alias List in bytes + + MemberAliasList - Optional pointer to location in which a pointer to the + Member Alias List will be returned. Note that the pointer can always + be retrieved given the DomainIndex. + +Return Values: + +--*/ + +{ + NTSTATUS Status; + PSAMP_AL_MEMBER_ALIAS_LIST OutputMemberAliasList = NULL; + PSAMP_AL_ALIAS_INFORMATION AliasInformation = NULL; + + SAMTRACE("SampAlCreateAliasMemberList"); + + // + // Allocate memory for the list. + // + + OutputMemberAliasList = MIDL_user_allocate( InitialMemberAliasListLength ); + + Status = STATUS_NO_MEMORY; + + if (OutputMemberAliasList == NULL) { + + goto CreateMemberAliasListError; + } + + Status = STATUS_SUCCESS; + + // + // Scratch the List header + // + + OutputMemberAliasList->Signature = SAMP_AL_MEMBER_ALIAS_LIST_SIGNATURE; + OutputMemberAliasList->MaximumLength = InitialMemberAliasListLength; + OutputMemberAliasList->UsedLength = SampAlOffsetFirstMemberDomain( + OutputMemberAliasList + ); + ASSERT(OutputMemberAliasList->MaximumLength >= + OutputMemberAliasList->UsedLength); + + OutputMemberAliasList->DomainIndex = DomainIndex; + OutputMemberAliasList->DomainCount = 0; + + // + // Link the Member Alias List to the SAM Local Domain info + // + + AliasInformation = &(SampDefinedDomains[ DomainIndex].AliasInformation); + AliasInformation->MemberAliasList = OutputMemberAliasList; + + *MemberAliasList = OutputMemberAliasList; + +CreateMemberAliasListFinish: + + return(Status); + +CreateMemberAliasListError: + + *MemberAliasList = NULL; + goto CreateMemberAliasListFinish; +} + + +NTSTATUS +SampAlGrowMemberAliasList( + IN OUT PSAMP_AL_MEMBER_ALIAS_LIST *MemberAliasList, + IN ULONG ExtraSpaceRequired + ) + +/*++ + +Routine Description: + + This function grows a Member Alias List by at least the requested amount. + +Arguments: + + MemberAliasList - Pointer to pointer to the Member Alias List. + + ExtraSpaceRequired - Extra space needed in the Member Alias List. + +Return Values: + +--*/ + +{ + NTSTATUS Status; + ULONG NewMaximumLengthMemberAliasList; + PSAMP_AL_MEMBER_ALIAS_LIST OutputMemberAliasList = NULL; + + SAMTRACE("SampAlGrowMemberAliasList"); + + // + // Calculate the new size of the Member Alias List needed. Round up to + // a multiple of the granularity. + // + + NewMaximumLengthMemberAliasList = (*MemberAliasList)->MaximumLength + + ExtraSpaceRequired; + + NewMaximumLengthMemberAliasList += + (SAMP_AL_MEMBER_ALIAS_LIST_DELTA - (ULONG) 1); + + NewMaximumLengthMemberAliasList &= + ((ULONG)(~(SAMP_AL_MEMBER_ALIAS_LIST_DELTA - (ULONG) 1))); + + // + // Allocate memory for the grown Member Alias List. + // + + OutputMemberAliasList = MIDL_user_allocate( + NewMaximumLengthMemberAliasList + ); + + Status = STATUS_NO_MEMORY; + + if (OutputMemberAliasList == NULL) { + + goto GrowMemberAliasListError; + } + + Status = STATUS_SUCCESS; + + // + // Copy the old list to the new list and the the new maximum length. + // Return pointer to new list. + // + + RtlMoveMemory( + OutputMemberAliasList, + *MemberAliasList, + (*MemberAliasList)->UsedLength + ); + + OutputMemberAliasList->MaximumLength = NewMaximumLengthMemberAliasList; + ASSERT(OutputMemberAliasList->MaximumLength >= + OutputMemberAliasList->UsedLength); + *MemberAliasList = OutputMemberAliasList; + +GrowMemberAliasListFinish: + + return(Status); + +GrowMemberAliasListError: + + SampAlInfoMakeInvalid( (*MemberAliasList)->DomainIndex ); + + goto GrowMemberAliasListFinish; +} + + +NTSTATUS +SampAlBuildMemberAliasList( + IN LONG DomainIndex + ) + +/*++ + +Routine Description: + + This function builds the Member Alias List for the specified SAM Local + Domain. For each Alias, its list of member Sids is read from backing + storage and MemberDomain and MemberAccount blocks are created. + +Arguments: + + DomainIndex - Specifies the SAM Local Domain + +--*/ + +{ + NTSTATUS Status, EnumerationStatus; + PSAMP_AL_MEMBER_ALIAS_LIST OutputMemberAliasList = NULL; + PSAMP_AL_MEMBER_DOMAIN MemberDomain = NULL; + PSAMP_AL_MEMBER_ACCOUNT MemberAccount = NULL; + SAMPR_ULONG_ARRAY AliasRids; + ULONG Rids[1], EnumerationContext, AliasCount, AliasRid; + ULONG AliasIndex; + PSAMP_OBJECT AccountContext = NULL; + PSAMP_OBJECT AliasContext = NULL; + SAMPR_PSID_ARRAY MemberSids; + ULONG DomainSidMaximumLength = RtlLengthRequiredSid( 256 ); + PSAMPR_ENUMERATION_BUFFER EnumerationBuffer = NULL; + PSID DomainSid = NULL; + PSID MemberSid = NULL; + + SAMTRACE("SampAlBuildMemberAliasList"); + + AliasRids.Element = Rids; + + // + // Mark the Member Alias List invalid + // + + SampAlInfoMakeInvalid( DomainIndex ); + + + // + // Allocate a scratch Domain Sid for splitting Sids. This has length + // equal to maximum possible Sid length. + // + + Status = STATUS_NO_MEMORY; + + DomainSid = MIDL_user_allocate( DomainSidMaximumLength ); + + if (DomainSid == NULL) { + + goto BuildMemberAliasListError; + } + + Status = STATUS_SUCCESS; + + // + // Create an empty Member Alias List and connect it to the + // local SAM Domain. + // + + Status = SampAlCreateMemberAliasList( + DomainIndex, + SAMP_AL_INITIAL_MEMBER_ALIAS_LIST_LENGTH, + &OutputMemberAliasList + ); + + if (!NT_SUCCESS(Status)) { + + goto BuildMemberAliasListError; + } + + // + // For each Alias in the SAM local domain, add its members to the + // Alias List + // + + EnumerationContext = 0; + EnumerationStatus = STATUS_MORE_ENTRIES; + + // + // It is currently necessary to set the Transaction Domain before + // calling SampEnumerateAccountNames even though we're not modifying + // anything. The is because called routine SampBuildAccountKeyName() + // uses this information. + // + + SampTransactionWithinDomain = FALSE; + SampSetTransactionDomain( DomainIndex ); + + while (EnumerationStatus == STATUS_MORE_ENTRIES) { + + Status = SampEnumerateAccountNames( + SampAliasObjectType, + &EnumerationContext, + &EnumerationBuffer, + SAMP_AL_ENUM_PREFERRED_LENGTH, + 0, + &AliasCount, + TRUE + ); + + if (!NT_SUCCESS(Status)) { + + break; + } + + EnumerationStatus = Status; + + for (AliasIndex = 0; AliasIndex < AliasCount; AliasIndex++) { + + AliasRid = EnumerationBuffer->Buffer[ AliasIndex ].RelativeId; + + // + // Create a context for the account. + // + + Status = SampCreateAccountContext( + SampAliasObjectType, + AliasRid, + TRUE, + TRUE, + &AliasContext + ); + + if (NT_SUCCESS(Status)) { + + // + // There is a rather ugly feature of the way the DomainIndex + // field is used in context handles while initializing. This + // value is set to the count of SAM Local Domains! So, I am + // setting it to the DomainIndex for the SAM Local Domain we're + // initializing, since this AliasContext is used only by me. + // + + AliasContext->DomainIndex = DomainIndex; + + Status = SampAlQueryMembersOfAlias( + AliasContext, + &MemberSids + ); + + if (NT_SUCCESS(Status)) { + + // + // Add these members to the Alias. No need to verify that + // they are already present since we're loading the Member Alias + // List from scratch. + // + + Status = SampAlAddMembersToAlias( + AliasContext, + 0, + &MemberSids + ); + } + + + SampDeleteContext( AliasContext ); + } + + if (!NT_SUCCESS(Status)) { + + break; + } + } + + // + // Enumerate next set of Aliases + // + + if (!NT_SUCCESS(Status)) { + + break; + } + + // + // Dispose of the Enumeration Buffer returned by SampEnumerateAccountNames + // + + SamIFree_SAMPR_ENUMERATION_BUFFER( EnumerationBuffer ); + EnumerationBuffer = NULL; + } + + if (!NT_SUCCESS(Status)) { + + goto BuildMemberAliasListError; + } + + // + // Mark the Member Alias List valid + // + + SampAlInfoMakeValid( DomainIndex ); + +BuildMemberAliasListFinish: + + SampTransactionWithinDomain = FALSE; + return(Status); + +BuildMemberAliasListError: + + goto BuildMemberAliasListFinish; +} + + +BOOLEAN +SampAlInfoIsValidForDomain( + IN SAMPR_HANDLE DomainHandle + ) + +/*++ + +Routine Description: + + This function checks whether Alias Information is valid for a specific + SAM Local Domain + +Arguments: + + DomainHandle - Handle to SAM Local Domain + +Return Values: + + BOOLEAN - TRUE if Alias Information is valid. The Alias Information may + be used in place of the backing storage to determine Alias membership + FALSE if the Alias Information is not valid. The Alias Information + does not exist, or is not reliable. + +--*/ + +{ + LONG DomainIndex; + + SAMTRACE("SampAlInfoIsValidForDomain"); + + // + // Get the Domain Index for the SAM Local Domain specified by DomainHandle. + + DomainIndex = ((PSAMP_OBJECT) DomainHandle)->DomainIndex; + + return(SampAlInfoIsValid( DomainIndex )); +} + + +BOOLEAN +SampAlInfoIsValidForAlias( + IN SAMPR_HANDLE AliasHandle + ) + +/*++ + +Routine Description: + + This function checks whether Alias Information is valid for a specific + Alias. The information is valid if it is valid for the SAM Local Domain + containing the Alias. + +Arguments: + + AliasHandle - Handle to SAM Alias + +Return Values: + + BOOLEAN - TRUE if Alias Information is valid. The Alias Information may + be used in place of the backing storage to determine Alias membership + FALSE if the Alias Information is not valid. The Alias Information + does not exist, or is not reliable. + +--*/ + +{ + LONG DomainIndex; + + SAMTRACE("SampAlInfoIsValidForAlias"); + + // + // Get the Domain Index for the SAM Local Domain specified by DomainHandle. + + DomainIndex = ((PSAMP_OBJECT) AliasHandle)->DomainIndex; + + return(SampAlInfoIsValid( DomainIndex )); +} diff --git a/private/newsam2/server/attr.c b/private/newsam2/server/attr.c new file mode 100644 index 000000000..25660be37 --- /dev/null +++ b/private/newsam2/server/attr.c @@ -0,0 +1,6020 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + attr.c + +Abstract: + + This file contains services that manipulate SAM object attributes. + + + WARNING: Terminology can sometimes be confusing. SAM objects have + attributes (e.g., users have LogonHours, FullName, AcctName, + et cetera). These attributes are stored in the registry + in registry-key-attributes. There is NOT a one-to-one + correllation between object-attributes and registry-key- + attributes. For example, all the fixed-length attributes + of an object are stored in a single registry-key-attribute + (whose name is pointed to by SampFixedAttributeName). + + +Author: + + Jim Kelly (JimK) 26-June-1992 + +Environment: + + User Mode - Win32 + +Revision History: + + ChrisMay 04-Jun-96 + Added routines for DS data manipulation. + ChrisMay 10-Jun-96 + Rewrote SampStoreObjectAttributes to branch to either the registry + or DS backing store, based on the value of Context->ObjectFlags. Note + that when a context object is created, this member is set to indicate + registry storage by default. + ChrisMay 18-Jun-96 + Set FlushVariable flag correctly in SampStoreDsObjectAttributes. Add- + ed routines to validate DS data by making SampValidateAttributes a + wrapper for SampValidateRegAttributes and SampValidateDsAttributes. + Moved SAMP_FIXED/VARIABLE_ATTRIBUTES into dsutilp.h. + ChrisMay 25-Jun-96 + Added code to SampValidateDsAttributes to update the SAM context + OnDisk member if the attributes are invalid. Added code to handle + initial case when OnDisk is NULL (new context). + ChrisMay 26-Jun-96 + Added code to update the buffer lengths and offsets in the SAMP_- + OBJECT and SAMP_OBJECT_INFORMATION structures after the attribute + buffer (Context.OnDisk) has been updated during SampDsValidateAttri- + butes. + ChrisMay 28-Jun-96 + Finished separating the attribute accessor macros to handle both + the registry and DS versions of the attribute buffers. + ChrisMay 02-Jul-96 + Corrected attribute-address computation in SampObjectAttributeAddress + for DS attributes. Corrected attribute-offset computation in Samp- + VariableAttributeOffset for DS attributes. + ChrisMay 19-Jul-96 + Corrected buffer-length computation in SampDsUpdateContextFixed- + Attributes. + +--*/ + + + +/* + + Each SAM object-type has an Object-type descriptor. This is in a + data structure called SAMP_OBJECT_INFORMATION. This structure + contains information that applies to all instances of that object + type. This includes things like a mask of write operations for + the object type, and a name for the object type to be used in + auditing. + + Each instance of an open SAM object has another data structure + used to identify it (called SAMP_OBJECT). The header of this + structure contains information that is common to all object-types + and is there to allow unified object manipulation. This includes + things like the handle to the object's registry key. + + There are fields in each of these structures that are there to + allow generic object-attribute support routines to operate. In + SAMP_OBJECT, there is a pointer to a block of allocated memory + housing a copy of the object's attributes as they are stored on-disk. + These attributes are arbitrarily divided into two groups: fixed-length + and variable-length. + + One of the fields in SAMP_OBJECT_INFORMATION indicates whether the + fixed-length and variable-length attributes for that object-type + are stored together in a single registry-key-attribute or separately + in two registry-key-attributes. + + + The registry api for querying and setting registry-key attributes are + rather peculiar in that they require the I/O buffer to include a + description of the data. Even the simplest data structure for reading + attribute values (KEY_VALUE_PARTIAL_INFORMATION) includes 3 ULONGs + before the actual data (TitleIndex, value Type, data length, + and then, finally, the data). To efficiently perform registry i/o, + the in-memory copy of the on-disk object attributes includes room + for this information preceeding the fixed and variable-length attribute + sections of the data. + + + NOTE: For object classes that store fixed and variable-length + data together, only the KEY_VALUE_PARTIAL_INFORMATION + structure preceeding the fixed-length attributes is used. + The one preceeding the variable-length attributes is + #ifdef'd out. + + + The structures related to object-attributes look like: + + + On-Disk Image + +-------------+ SAMP_OBJECT_INFORMATION + +-->|KEY_VALUE_ | +-----------------------+ + SAMP_OBJECT | |PARTIAL_ | | | + +-----------+ | |INFORMATION | | (header) | + | | | |-------------| | | + | (header) | | | Fixed-Length|<-----+ | | + | | | | Attributes | | |-----------------------| + |-----------| | | | +-------|-< FixedAttrsOffset | + | OnDisk >-|--+ |-------------+ |-----------------------| + |-----------| |KEY_VALUE_ |<-------------|-< VariableBuffOffset | + | OnDisk | |PARTIAL_ | |-----------------------| + | Control | |INFORMATION | +-------|-< VariableArrayOffset | + | Flags | |(Optional) | | |-----------------------| + |-----------| |-------------| | +----|-< VariableDataOffset | + | | | Variable- |<-----+ | |-----------------------| + | type- | | Length | | |VariableAttributeCount | + | specific | | Attributes | | |-----------------------| + | body | | Array | | |FixedStoredSeparately | + | | |-------------| | |-----------------------| + +-----------+ | Variable- |<--------+ | | + | Length | | | + | Attributes | | o | + | Data | | o | + | | +-----------------------+ + | | + +-------------+ + + + + + The KEY_VALUE_PARTIAL_INFORMATION preceeding the VariableLengthAttributes + array is marked optional because it is only present if fixed-length and + variable-length attribute information is stored separately. In this case, + the VariableBufferOffset field in the SAMP_OBJECT_INFORMATION structure + is set to be zero. + +*/ + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include <samsrvp.h> +#include <lmcons.h> +#include <nturtl.h> +#include <dsutilp.h> +#include <dslayer.h> + +// +// This value indicates the minumum size block of memory to allocate +// when retrieving object attributes from disk. + +#define SAMP_MINIMUM_ATTRIBUTE_ALLOC (1000) + +// +// This value is used when growing the size of the buffer containing +// object attributes. It represents the amount of free space that +// should be left (approximately) for future growth in the buffer. +// + +#define SAMP_MINIMUM_ATTRIBUTE_PAD (200) + +// +// The following line enables attribute debugging code +// + +//#define SAM_DEBUG_ATTRIBUTES +//#ifdef SAM_DEBUG_ATTRIBUTES +//Boolean that allows us to turn off debugging output +//BOOLEAN SampDebugAttributes = FALSE; +//#endif + +// Private debugging display routine is enabled when ATTR_DBG_PRINTF = 1. + +#define ATTR_DBG_PRINTF 0 + +#if (ATTR_DBG_PRINTF == 1) +#define DebugPrint printf +#else +#define DebugPrint +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// private macros // +// // +/////////////////////////////////////////////////////////////////////////////// + +// +// Macro to round up a ULONG value to be dword aligned +// (i.e., be a multipleof 4). +// Note, this does not handle the case where the Ulong is greater +// than 0xfffffffc. +// + +#define SampDwordAlignUlong( v ) (((v)+3) & 0xfffffffc) + +// +// Make sure an object type and corresponding variable-length attribute +// index are legitimate. +// + +#define SampValidateAttributeIndex( c, i ) { \ + ASSERT( ((c)->ObjectType < SampUnknownObjectType) ); \ + ASSERT(((i) < SampObjectInformation[(c)->ObjectType].VariableAttributeCount) ); \ +} + +// +// Test to see if an object's fixed or variable-length attributes +// are in memory. +// + +#define SampFixedAttributesValid( c ) ((c)->FixedValid) + +#define SampVariableAttributesValid( c ) ((c)->VariableValid) + +// +// Get the number of variable-length attributes defined for the +// specified object +// + +#define SampVariableAttributeCount( c ) \ + (SampObjectInformation[(c)->ObjectType].VariableAttributeCount) + +// +// Get the offset of the beginning of the attribute buffers +// + +#define SampRegFixedBufferOffset( c ) \ + ( \ + SampObjectInformation[(c)->ObjectType].FixedAttributesOffset \ + ) + +#define SampDsFixedBufferOffset( c ) \ + ( \ + SampObjectInformation[(c)->ObjectType].FixedDsAttributesOffset \ + ) + +#define SampFixedBufferOffset( c ) \ + ( \ + (IsDsObject(c)) ? \ + SampDsFixedBufferOffset(c) : SampRegFixedBufferOffset(c) \ + ) + +#define SampRegVariableBufferOffset( c ) \ + ( \ + SampObjectInformation[(c)->ObjectType].VariableBufferOffset \ + ) + +#define SampDsVariableBufferOffset( c ) \ + ( \ + SampObjectInformation[(c)->ObjectType].VariableDsBufferOffset \ + ) + +#define SampVariableBufferOffset( c ) \ + ( \ + (IsDsObject(c)) ? \ + SampDsVariableBufferOffset(c) : SampRegVariableBufferOffset(c) \ + ) + +// +// Get the offset of the beginning of the variable data i/o buffer. +// If the fixed and variable-length attributes are stored separately, +// then this will be the lower half of the buffer. +// Otherwise, there is only one buffer, so it is the entire allocated buffer. +// + +#define SampRegFixedBufferAddress( c ) \ + ( \ + ((PUCHAR)((c)->OnDisk)) + SampFixedBufferOffset( c ) \ + ) + +#define SampDsFixedBufferAddress( c ) \ + ( \ + ((PUCHAR)((c)->OnDisk)) + SampDsFixedBufferOffset( c ) \ + ) + +#define SampFixedBufferAddress( c ) \ + ( \ + (IsDsObject(c)) ? \ + SampDsFixedBufferAddress(c): SampRegFixedBufferAddress(c) \ + ) + +#define SampRegVariableBufferAddress( c ) \ + ( \ + ((PUCHAR)((c)->OnDisk)) + SampVariableBufferOffset( c ) \ + ) + +#define SampDsVariableBufferAddress( c ) \ + ( \ + ((PUCHAR)((c)->OnDisk)) + SampDsVariableBufferOffset( c ) \ + ) + +#define SampVariableBufferAddress( c ) \ + ( \ + (IsDsObject(c)) ? \ + SampDsVariableBufferAddress(c) : SampRegVariableBufferAddress(c)\ + ) + +// +// Get the offset of the beginning of the variable-length +// attributes discriptors array. This address is dword-aligned. +// + +#define SampRegVariableArrayOffset( c ) \ + ( \ + SampObjectInformation[(c)->ObjectType].VariableArrayOffset \ + ) + +#define SampDsVariableArrayOffset( c ) \ + ( \ + SampObjectInformation[(c)->ObjectType].VariableDsArrayOffset \ + ) + +#define SampVariableArrayOffset( c ) \ + ( \ + (IsDsObject(c)) ? \ + SampDsVariableArrayOffset(c) : SampRegVariableArrayOffset(c) \ + ) + +// +// Calculate the address of the beginning of the variable-length +// attributes array. +// + +#define SampRegVariableArrayAddress( c ) \ + ( \ + (PSAMP_VARIABLE_LENGTH_ATTRIBUTE)((PUCHAR)((c)->OnDisk) + \ + SampVariableArrayOffset( c ) ) \ + ) + +#define SampDsVariableArrayAddress( c ) \ + ( \ + (PSAMP_VARIABLE_LENGTH_ATTRIBUTE)((PUCHAR)((c)->OnDisk) + \ + SampDsVariableArrayOffset( c ) ) \ + ) + +#define SampVariableArrayAddress( c ) \ + ( \ + (IsDsObject(c)) ? \ + SampDsVariableArrayAddress(c) : SampRegVariableArrayAddress(c) \ + ) + +// +// Get the offset of the beginning of the variable-length +// attributes data. +// + +#define SampRegVariableDataOffset( c ) \ + ( \ + SampObjectInformation[(c)->ObjectType].VariableDataOffset \ + ) + +#define SampDsVariableDataOffset( c ) \ + ( \ + SampObjectInformation[(c)->ObjectType].VariableDsDataOffset \ + ) + +#define SampVariableDataOffset( c ) \ + ( \ + (IsDsObject(c)) ? \ + SampDsVariableDataOffset(c) : SampRegVariableDataOffset(c) \ + ) + +// +// Get the length of the on-disk buffer for holding the variable-length +// attribute array and data. If the fixed and variable-length attributes +// are stored separately, then this will be the lower half of the buffer. +// Otherwise, there is only one buffer, so it is the entire allocated buffer. +// + +#define SampRegFixedBufferLength( c ) \ + ( \ + SampObjectInformation[(c)->ObjectType].FixedLengthSize \ + ) + +#define SampDsFixedBufferLength( c ) \ + ( \ + SampObjectInformation[(c)->ObjectType].FixedDsLengthSize \ + ) + +#define SampFixedBufferLength( c ) \ + ( \ + (IsDsObject(c)) ? \ + SampDsFixedBufferLength(c) : SampRegFixedBufferLength(c) \ + ) + +#define SampRegVariableBufferLength( c ) \ + ( \ + (c)->OnDiskAllocated - SampVariableBufferOffset( c ) \ + ) + +#define SampDsVariableBufferLength( c ) \ + ( \ + (c)->OnDiskAllocated - SampDsVariableBufferOffset( c ) \ + ) + +#define SampVariableBufferLength( c ) \ + ( \ + (IsDsObject(c)) ? \ + SampDsVariableBufferLength(c) : SampRegVariableBufferLength(c) \ + ) + +// +// Return the address of a Qualifier field within the variable-length +// attribute descriptor array. +// + +#define SampRegVariableQualifier( c, i ) \ + ( \ + SampVariableArrayAddress( c ) + \ + (sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE) * i) \ + + FIELD_OFFSET(SAMP_VARIABLE_LENGTH_ATTRIBUTE, Qualifier) \ + ) + +#define SampDsVariableQualifier( c, i ) \ + ( \ + SampDsVariableArrayAddress( c ) + \ + (sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE) * i) \ + + FIELD_OFFSET(SAMP_VARIABLE_LENGTH_ATTRIBUTE, Qualifier) \ + ) + +#define SampVariableQualifier( c, i ) \ + ( \ + (IsDsObject(c)) ? \ + SampDsVariableQualifier(c, i) : SampRegVariableQualifier(c, i) \ + ) + +// +// Return the address of the first byte of free space +// in an object's attribute data buffer. +// This will be dword aligned. +// + +#define SampRegFirstFreeVariableAddress( c ) \ + (PUCHAR)(((PUCHAR)((c)->OnDisk)) + (c)->OnDiskUsed) + +#define SampDsFirstFreeVariableAddress( c ) \ + (PUCHAR)(((PUCHAR)((c)->OnDisk)) + (c)->OnDiskUsed) + +#define SampFirstFreeVariableAddress( c ) \ + ( \ + (IsDsObject(c)) ? \ + SampDsFirstFreeVariableAddress(c) : \ + SampRegFirstFreeVariableAddress(c) \ + ) + +// +// Get the number of bytes needed to store the entire variable-length +// attribute information on disk. +// + +#define SampRegVariableBufferUsedLength( c ) \ + ( \ + (PUCHAR)SampFirstFreeVariableAddress(c) - \ + (PUCHAR)SampVariableArrayAddress(c) \ + ) + +#define SampDsVariableBufferUsedLength( c ) \ + ( \ + (PUCHAR)SampDsFirstFreeVariableAddress(c) - \ + (PUCHAR)SampDsVariableArrayAddress(c) \ + ) + +#define SampVariableBufferUsedLength( c ) \ + ( \ + (IsDsObject(c)) ? \ + SampDsVariableBufferUsedLength(c): \ + SampRegVariableBufferUsedLength(c) \ + ) + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// private service prototypes // +// // +/////////////////////////////////////////////////////////////////////////////// + +NTSTATUS +SampValidateAttributes( + IN PSAMP_OBJECT Context, + IN ULONG AttributeGroup + ); + +PUCHAR +SampObjectAttributeAddress( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex + ); + +ULONG +SampObjectAttributeLength( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex + ); + +PULONG +SampObjectAttributeQualifier( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex + ); + +NTSTATUS +SampGetAttributeBufferReadInfo( + IN PSAMP_OBJECT Context, + IN ULONG AttributeGroup, + OUT PUCHAR *Buffer, + OUT PULONG BufferLength, + OUT PUNICODE_STRING *KeyAttributeName + ); + +NTSTATUS +SampExtendAttributeBuffer( + IN PSAMP_OBJECT Context, + IN ULONG NewSize + ); + +NTSTATUS +SampReadRegistryAttribute( + IN HANDLE Key, + IN PUCHAR Buffer, + IN ULONG BufferLength, + IN PUNICODE_STRING AttributeName, + OUT PULONG RequiredLength + ); + +NTSTATUS +SampSetVariableAttribute( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex, + IN ULONG Qualifier, + IN PUCHAR Buffer, + IN ULONG Length + ); + +NTSTATUS +SampUpgradeToCurrentRevision( + IN PSAMP_OBJECT Context, + IN ULONG AttributeGroup, + IN PUCHAR Buffer, + IN ULONG LengthOfDataRead, + IN PULONG TotalRequiredLength + ); + + + +#ifdef SAM_DEBUG_ATTRIBUTES +VOID +SampDumpAttributes( + IN PSAMP_OBJECT Context + ); + +VOID +SampDumpData( + IN PVOID Buffer, + IN ULONG Length + ); +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Public Routines // +// // +/////////////////////////////////////////////////////////////////////////////// + +VOID +SampInitDsObjectInfoAttributes( + ) + +/*++ + +Routine Description: + + This routine initializes the offset and length information fields of the + SAM_OBJECT_INFORMATION structure for DS attributes. This structure con- + tains offset and length information for two sets of attributes: + + -Those attributes stored in the registry (workstation account info) + + -Those attributes stored in the DS (domain or DC account info) + + The former set are initialized by SampInitObjectInfoAttriubtes, while this + routine inializes the latter set of information. Regardless of whether the + attributes are persistently stored in the registry or the DS, their in- + memory representation always uses the SAM fixed-length and variable-length + data buffers. + + Note that DS data buffers do not contain the KEY_VALUE_PARTIAL_INFORMATION + data because this is registry-specific, hence unnecessary for the DS-based + attributes. + +Parameters: + + None. + +Return Values: + + None. + +--*/ + +{ + PSAMP_OBJECT_INFORMATION Object; + + SAMTRACE("SampInitDsObjectInfoAttributes"); + + // + // SERVER object attribute information + // + + Object = &SampObjectInformation[SampServerObjectType]; + + // Object->FixedStoredSeparately = SAMP_SERVER_STORED_SEPARATELY; + Object->FixedDsAttributesOffset = 0; + Object->FixedDsLengthSize = sizeof(SAMP_V1_FIXED_LENGTH_SERVER); + +#if SAMP_SERVER_STORED_SEPARATELY + + Object->VariableDsBufferOffset = + Object->FixedDsAttributesOffset + + SampDwordAlignUlong(Object->FixedDsLengthSize); + + Object->VariableDsArrayOffset = + Object->VariableDsBufferOffset + 0; +#else + + Object->VariableDsBufferOffset = 0; + + Object->VariableDsArrayOffset = + Object->FixedDsAttributesOffset + + SampDwordAlignUlong(Object->FixedDsLengthSize); + +#endif //SAMP_SERVER_STORED_SEPARATELY + + // Object->VariableAttributeCount = SAMP_SERVER_VARIABLE_ATTRIBUTES; + + Object->VariableDsDataOffset = + SampDwordAlignUlong( Object->VariableDsArrayOffset + + (SAMP_SERVER_VARIABLE_ATTRIBUTES * + sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE)) + ); + + // + // DOMAIN object attribute information + // + + Object = &SampObjectInformation[SampDomainObjectType]; + + // Object->FixedStoredSeparately = SAMP_DOMAIN_STORED_SEPARATELY; + Object->FixedDsAttributesOffset = 0; + Object->FixedDsLengthSize = sizeof(SAMP_V1_0A_FIXED_LENGTH_DOMAIN); + +#if SAMP_DOMAIN_STORED_SEPARATELY + + Object->VariableDsBufferOffset = + Object->FixedDsAttributesOffset + + SampDwordAlignUlong(Object->FixedDsLengthSize); + + Object->VariableDsArrayOffset = + Object->VariableDsBufferOffset + 0; +#else + + Object->VariableDsBufferOffset = 0; + + Object->VariableDsArrayOffset = + Object->FixedDsAttributesOffset + + SampDwordAlignUlong(Object->FixedDsLengthSize); + +#endif //SAMP_DOMAIN_STORED_SEPARATELY + + // Object->VariableAttributeCount = SAMP_DOMAIN_VARIABLE_ATTRIBUTES; + + Object->VariableDsDataOffset = + SampDwordAlignUlong( Object->VariableDsArrayOffset + + (SAMP_DOMAIN_VARIABLE_ATTRIBUTES * + sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE)) + ); + + // + // USER object attribute information + // + + Object = &SampObjectInformation[SampUserObjectType]; + + // Object->FixedStoredSeparately = SAMP_USER_STORED_SEPARATELY; + Object->FixedDsAttributesOffset = 0; + Object->FixedDsLengthSize = sizeof(SAMP_V1_0A_FIXED_LENGTH_USER); + +#if SAMP_USER_STORED_SEPARATELY + + Object->VariableDsBufferOffset = + Object->FixedDsAttributesOffset + + SampDwordAlignUlong(Object->FixedDsLengthSize); + + Object->VariableDsArrayOffset = + Object->VariableDsBufferOffset + 0; +#else + + Object->VariableDsBufferOffset = 0; + + Object->VariableDsArrayOffset = + Object->FixedDsAttributesOffset + + SampDwordAlignUlong(Object->FixedDsLengthSize); + +#endif //SAMP_USER_STORED_SEPARATELY + + // Object->VariableAttributeCount = SAMP_USER_VARIABLE_ATTRIBUTES; + + Object->VariableDsDataOffset = + SampDwordAlignUlong( Object->VariableDsArrayOffset + + (SAMP_USER_VARIABLE_ATTRIBUTES * + sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE)) + ); + + // + // GROUP object attribute information + // + + Object = &SampObjectInformation[SampGroupObjectType]; + + // Object->FixedStoredSeparately = SAMP_GROUP_STORED_SEPARATELY; + Object->FixedDsAttributesOffset = 0; + Object->FixedDsLengthSize = sizeof(SAMP_V1_0A_FIXED_LENGTH_GROUP); + +#if SAMP_GROUP_STORED_SEPARATELY + + Object->VariableDsBufferOffset = + Object->FixedDsAttributesOffset + + SampDwordAlignUlong(Object->FixedDsLengthSize); + + Object->VariableDsArrayOffset = + Object->VariableDsBufferOffset + 0; +#else + + Object->VariableDsBufferOffset = 0; + + Object->VariableDsArrayOffset = + Object->FixedDsAttributesOffset + + SampDwordAlignUlong(Object->FixedDsLengthSize); + +#endif //SAMP_GROUP_STORED_SEPARATELY + + // Object->VariableAttributeCount = SAMP_GROUP_VARIABLE_ATTRIBUTES; + + Object->VariableDsDataOffset = + SampDwordAlignUlong( Object->VariableDsArrayOffset + + (SAMP_GROUP_VARIABLE_ATTRIBUTES * + sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE)) + ); + + // + // ALIAS object attribute information + // + + Object = &SampObjectInformation[SampAliasObjectType]; + + // Object->FixedStoredSeparately = SAMP_ALIAS_STORED_SEPARATELY; + Object->FixedDsAttributesOffset = 0; + Object->FixedDsLengthSize = sizeof(SAMP_V1_FIXED_LENGTH_ALIAS); + +#if SAMP_ALIAS_STORED_SEPARATELY + + Object->VariableDsBufferOffset = + Object->FixedDsAttributesOffset + + SampDwordAlignUlong(Object->FixedDsLengthSize); + + Object->VariableDsArrayOffset = + Object->VariableDsBufferOffset + 0; +#else + + Object->VariableDsBufferOffset = 0; + + Object->VariableDsArrayOffset = + Object->FixedDsAttributesOffset + + SampDwordAlignUlong(Object->FixedDsLengthSize); + +#endif //SAMP_ALIAS_STORED_SEPARATELY + + // Object->VariableAttributeCount = SAMP_ALIAS_VARIABLE_ATTRIBUTES; + + Object->VariableDsDataOffset = + SampDwordAlignUlong( Object->VariableDsArrayOffset + + (SAMP_ALIAS_VARIABLE_ATTRIBUTES * + sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE)) + ); + + return; +} + + + +VOID +SampInitObjectInfoAttributes( + ) + + +/*++ + + This API initializes the attribute field information + of the various object information structures. + + Attribute information includes: + + FixedStoredSeparately (BOOLEAN) + + FixedAttributeOffset (ULONG) + VariableBufferOffset (ULONG) + VariableArrayOffset (ULONG) + VariableDataOffset (ULONG) + + FixedLengthSize (ULONG) + VariableAttributeCount (ULONG) + + +Parameters: + + None. + + + +Return Values: + + None. + + +--*/ +{ + + + // + // Define the size of the header that is in front of our data when + // we read it back out of the registry. + // + +#define KEY_VALUE_HEADER_SIZE (SampDwordAlignUlong( \ + FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data))) + + + PSAMP_OBJECT_INFORMATION Object; + + SAMTRACE("SampInitObjectInfoAttributes"); + + // + // SERVER object attribute information + // + + Object = &SampObjectInformation[SampServerObjectType]; + + Object->FixedStoredSeparately = SAMP_SERVER_STORED_SEPARATELY; + + Object->FixedAttributesOffset = KEY_VALUE_HEADER_SIZE; + + Object->FixedLengthSize = sizeof(SAMP_V1_FIXED_LENGTH_SERVER); + +#if SAMP_SERVER_STORED_SEPARATELY + + Object->VariableBufferOffset = + Object->FixedAttributesOffset + + SampDwordAlignUlong(Object->FixedLengthSize); + + Object->VariableArrayOffset = + Object->VariableBufferOffset + KEY_VALUE_HEADER_SIZE; +#else + + Object->VariableBufferOffset = 0; + + Object->VariableArrayOffset = + Object->FixedAttributesOffset + + SampDwordAlignUlong(Object->FixedLengthSize); + +#endif //SAMP_SERVER_STORED_SEPARATELY + + + Object->VariableAttributeCount = SAMP_SERVER_VARIABLE_ATTRIBUTES; + + Object->VariableDataOffset = + SampDwordAlignUlong( Object->VariableArrayOffset + + (SAMP_SERVER_VARIABLE_ATTRIBUTES * + sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE)) + ); + + + + + + // + // DOMAIN object attribute information + // + + Object = &SampObjectInformation[SampDomainObjectType]; + + Object->FixedStoredSeparately = SAMP_DOMAIN_STORED_SEPARATELY; + + Object->FixedAttributesOffset = KEY_VALUE_HEADER_SIZE; + + Object->FixedLengthSize = sizeof(SAMP_V1_0A_FIXED_LENGTH_DOMAIN); + +#if SAMP_DOMAIN_STORED_SEPARATELY + + Object->VariableBufferOffset = + Object->FixedAttributesOffset + + SampDwordAlignUlong(Object->FixedLengthSize); + + Object->VariableArrayOffset = + Object->VariableBufferOffset + KEY_VALUE_HEADER_SIZE; +#else + + Object->VariableBufferOffset = 0; + + Object->VariableArrayOffset = + Object->FixedAttributesOffset + + SampDwordAlignUlong(Object->FixedLengthSize); + +#endif //SAMP_DOMAIN_STORED_SEPARATELY + + + Object->VariableAttributeCount = SAMP_DOMAIN_VARIABLE_ATTRIBUTES; + + Object->VariableDataOffset = + SampDwordAlignUlong( Object->VariableArrayOffset + + (SAMP_DOMAIN_VARIABLE_ATTRIBUTES * + sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE)) + ); + + + + + + // + // USER object attribute information + // + + Object = &SampObjectInformation[SampUserObjectType]; + + Object->FixedStoredSeparately = SAMP_USER_STORED_SEPARATELY; + + Object->FixedAttributesOffset = KEY_VALUE_HEADER_SIZE; + + Object->FixedLengthSize = sizeof(SAMP_V1_0A_FIXED_LENGTH_USER); + +#if SAMP_USER_STORED_SEPARATELY + + Object->VariableBufferOffset = + Object->FixedAttributesOffset + + SampDwordAlignUlong(Object->FixedLengthSize); + + Object->VariableArrayOffset = + Object->VariableBufferOffset + KEY_VALUE_HEADER_SIZE; +#else + + Object->VariableBufferOffset = 0; + + Object->VariableArrayOffset = + Object->FixedAttributesOffset + + SampDwordAlignUlong(Object->FixedLengthSize); + +#endif //SAMP_USER_STORED_SEPARATELY + + + Object->VariableAttributeCount = SAMP_USER_VARIABLE_ATTRIBUTES; + + Object->VariableDataOffset = + SampDwordAlignUlong( Object->VariableArrayOffset + + (SAMP_USER_VARIABLE_ATTRIBUTES * + sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE)) + ); + + + + + + // + // GROUP object attribute information + // + + Object = &SampObjectInformation[SampGroupObjectType]; + + Object->FixedStoredSeparately = SAMP_GROUP_STORED_SEPARATELY; + + Object->FixedAttributesOffset = KEY_VALUE_HEADER_SIZE; + + Object->FixedLengthSize = sizeof(SAMP_V1_0A_FIXED_LENGTH_GROUP); + +#if SAMP_GROUP_STORED_SEPARATELY + + Object->VariableBufferOffset = + Object->FixedAttributesOffset + + SampDwordAlignUlong(Object->FixedLengthSize); + + Object->VariableArrayOffset = + Object->VariableBufferOffset + KEY_VALUE_HEADER_SIZE; +#else + + Object->VariableBufferOffset = 0; + + Object->VariableArrayOffset = + Object->FixedAttributesOffset + + SampDwordAlignUlong(Object->FixedLengthSize); + +#endif //SAMP_GROUP_STORED_SEPARATELY + + + Object->VariableAttributeCount = SAMP_GROUP_VARIABLE_ATTRIBUTES; + + Object->VariableDataOffset = + SampDwordAlignUlong( Object->VariableArrayOffset + + (SAMP_GROUP_VARIABLE_ATTRIBUTES * + sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE)) + ); + + + + + + // + // ALIAS object attribute information + // + + Object = &SampObjectInformation[SampAliasObjectType]; + + Object->FixedStoredSeparately = SAMP_ALIAS_STORED_SEPARATELY; + + Object->FixedAttributesOffset = KEY_VALUE_HEADER_SIZE; + + Object->FixedLengthSize = sizeof(SAMP_V1_FIXED_LENGTH_ALIAS); + +#if SAMP_ALIAS_STORED_SEPARATELY + + Object->VariableBufferOffset = + Object->FixedAttributesOffset + + SampDwordAlignUlong(Object->FixedLengthSize); + + Object->VariableArrayOffset = + Object->VariableBufferOffset + KEY_VALUE_HEADER_SIZE; +#else + + Object->VariableBufferOffset = 0; + + Object->VariableArrayOffset = + Object->FixedAttributesOffset + + SampDwordAlignUlong(Object->FixedLengthSize); + +#endif //SAMP_ALIAS_STORED_SEPARATELY + + + Object->VariableAttributeCount = SAMP_ALIAS_VARIABLE_ATTRIBUTES; + + Object->VariableDataOffset = + SampDwordAlignUlong( Object->VariableArrayOffset + + (SAMP_ALIAS_VARIABLE_ATTRIBUTES * + sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE)) + ); + + // Initialize the DS-specific buffer offsets and lengths. + + SampInitDsObjectInfoAttributes(); + + return; +} + + + +NTSTATUS +SampStoreDsObjectAttributes( + IN PSAMP_OBJECT Context + ) + +/*++ + +Routine Description: + + This routine does the work of writing the SAM attributes out to the DS + backing store. Determination is made as to whether the fixed, or vari- + able, or both sets of attributes are dirty and valid. If so, then they + are updated in the backing store. The SAM attributes are first converted + into a DSATTRBLOCK so that they can be written to storage. The dirty + flags are updated accordingly. + +Arguments: + + Context - Pointer, the object's SAM context. + +Return Value: + + STATUS_SUCCESS - storage was updated without a problem, otherwise an + error code is returned. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_SUCCESS; + BOOLEAN FlushFixed = FALSE; + BOOLEAN FlushVariable = FALSE; + INT ObjectType = Context->ObjectType; + ULONG Flags = 0; + PDSNAME DsObjectName = Context->ObjectNameInDs; + PDSATTRBLOCK AttributeBlock = NULL; + + SAMTRACE("SampStoreDsObjectAttributes"); + + // Determine which attributes (fixed or variable) need to be written to + // storage. + + if (Context->FixedValid && Context->FixedDirty) + { + FlushFixed = TRUE; + } + + if (Context->VariableValid && Context->VariableDirty) + { + FlushVariable = TRUE; + } + + if (NULL != (Context->OnDisk)) + { + // Determine whether the attributes are stored separately or combined. + + if (TRUE == SampObjectInformation[ObjectType].FixedStoredSeparately) + { + if (TRUE == FlushFixed) + { + // Get a pointer to the start of the fixed-length attributes + // (note that this should always be the same address as the + // Context->OnDisk address for DS-based attributes) and con- + // them into a DSATTRBLOCK. + + NtStatus = SampConvertFixedLengthAttributesToAttrBlock( + ObjectType, + SampDsFixedBufferAddress(Context), + &AttributeBlock); + + if (NT_SUCCESS(NtStatus)) + { + if (NULL != AttributeBlock) + { + // Write the fixed-length attributes to storage. + + NtStatus = SampDsSetAttributes(DsObjectName, + Flags, + ObjectType, + AttributeBlock); + + if (NT_SUCCESS(NtStatus)) + { + Context->FixedDirty = FALSE; + } + } + else + { + NtStatus = STATUS_INTERNAL_ERROR; + } + } + } + + if (NT_SUCCESS(NtStatus) && (TRUE == FlushVariable)) + { + // Get a pointer to the variable-length attributes and convert + // them into a DSATTRBLOCK. + + NtStatus = SampConvertVarLengthAttributesToAttrBlock( + ObjectType, + SampDsVariableArrayAddress(Context), + &AttributeBlock); + + if (NT_SUCCESS(NtStatus)) + { + if (NULL != AttributeBlock) + { + NtStatus = SampDsSetAttributes(DsObjectName, + Flags, + ObjectType, + AttributeBlock); + + if (NT_SUCCESS(NtStatus)) + { + Context->VariableDirty = FALSE; + } + } + else + { + NtStatus = STATUS_INTERNAL_ERROR; + } + } + } + } + else + { + // The attributes are combined, check the flush status. + + if ((TRUE == FlushFixed) || (TRUE == FlushVariable)) + { + // Get a pointer to the combined (i.e. fixed and variable- + // length) attributes and convert them into a DSATTRBLOCK. + + // BUG: Where is OnDiskAllocated, OnDiskUsed, etc. set? + + NtStatus = SampConvertCombinedAttributesToAttrBlock( + ObjectType, + Context->OnDisk, + SampDsFixedBufferLength(Context), + SampDsVariableBufferLength(Context), + &AttributeBlock); + + if (NT_SUCCESS(NtStatus)) + { + if (NULL != AttributeBlock) + { + NtStatus = SampDsSetAttributes(DsObjectName, + Flags, + ObjectType, + AttributeBlock); + + if (NT_SUCCESS(NtStatus)) + { + Context->FixedDirty = FALSE; + Context->VariableDirty = FALSE; + } + } + else + { + NtStatus = STATUS_INTERNAL_ERROR; + } + } + } + } + } + + return(NtStatus); +} + + + +NTSTATUS +SampStoreRegObjectAttributes( + IN PSAMP_OBJECT Context, + IN BOOLEAN UseKeyHandle + ) + +/*++ + + This API is used to store an object's attributes onto + backing store. + + The object attributes are not flushed to disk with this + routine. They are just added to the RXACT. + + + + +Parameters: + + Context - Pointer to an object context block. + + UseKeyHandle - If TRUE, the RootKey in the context block is passed + to the transaction code - this assumes that the key + will still be open when the transaction is committed. + If FALSE, the RootKey will be ignored and the transaction code will + open a key for itself. + + +Return Values: + + STATUS_SUCCESS - The service completed successfully. + + + Other status values as may be returned by the RXACT services. + + +--*/ +{ + NTSTATUS + NtStatus; + + BOOLEAN + FlushFixed = FALSE, + FlushVariable = FALSE; + + HANDLE + RootKey; + + SAMTRACE("SampStoreRegObjectAttributes"); + + // + // See if anything is dirty and needs to be stored + // + + if (Context->FixedValid && Context->FixedDirty) { + + FlushFixed = TRUE; + } + + if (Context->VariableValid && Context->VariableDirty) { + + FlushVariable = TRUE; + } + + + if (!(FlushFixed || FlushVariable)) { + + return(STATUS_SUCCESS); + } + + + // + // Calculate the RootKey to pass to the transaction code + // + + if (UseKeyHandle) { + RootKey = Context->RootKey; + } else { + RootKey = INVALID_HANDLE_VALUE; + } + + // + // We keep an open domain context that is used to modify the change + // count whenever a change is made. But if this is a domain change + // here, then that change will overwrite this one. Check for that + // case, and copy this fixed data to the open domain context. Note + // that the open domain's variable data never gets changed. + // + + if ( ( Context->ObjectType == SampDomainObjectType ) && + ( Context != SampDefinedDomains[Context->DomainIndex].Context ) ) { + + PSAMP_OBJECT DefinedContext; + + // + // Get a pointer to the corresponding open defined domain. + // No changes should have been made to its data. + // + + DefinedContext = SampDefinedDomains[Context->DomainIndex].Context; + + ASSERT( DefinedContext->FixedValid == TRUE ); + ASSERT( DefinedContext->FixedDirty == FALSE ); + +#if DBG + if ( DefinedContext->VariableValid ) { + ASSERT( DefinedContext->VariableDirty == FALSE ); + } +#endif + DefinedContext->VariableDirty = FALSE; + + // + // Copy our fixed data over the defined domain's fixed data. + // Note that we're assuming that the fixed and variable data are + // stored separately. + // + + ASSERT(SampObjectInformation[SampDomainObjectType].FixedStoredSeparately); + + RtlCopyMemory( + SampFixedBufferAddress( DefinedContext ), + SampFixedBufferAddress( Context ), + SampFixedBufferLength( Context ) + ); + + // + // No need to flush this context's fixed data, since the commit + // code will flush the same stuff (plus an altered modified count). + // + + FlushFixed = FALSE; + Context->FixedDirty = FALSE; + } + + // + // One or more of the attributes needs to be stored. + // + + if (!SampObjectInformation[Context->ObjectType].FixedStoredSeparately) { + + // + // fixed and variable-length attributes stored together. + // Note - strip off the partial key info struct from the start + // + + SampDumpRXact(SampRXactContext, + RtlRXactOperationSetValue, + &(Context->RootName), + RootKey, + &SampCombinedAttributeName, + REG_BINARY, + SampFixedBufferAddress(Context), + Context->OnDiskUsed - SampFixedBufferOffset(Context), + FixedBufferAddressFlag); + + NtStatus = RtlAddAttributeActionToRXact( + SampRXactContext, + RtlRXactOperationSetValue, + &(Context->RootName), + RootKey, + &SampCombinedAttributeName, + REG_BINARY, + SampFixedBufferAddress(Context), + Context->OnDiskUsed - SampFixedBufferOffset(Context) + ); +#if SAMP_DIAGNOSTICS + if (!NT_SUCCESS(NtStatus)) { + SampDiagPrint( DISPLAY_STORAGE_FAIL, + ("SAM: Failed to add action to RXact (0x%lx)\n", + NtStatus) ); + IF_SAMP_GLOBAL( BREAK_ON_STORAGE_FAIL ) { + ASSERT(NT_SUCCESS(NtStatus)); + } + } +#endif //SAMP_DIAGNOSTICS + + + if ( NT_SUCCESS( NtStatus ) ) { + + Context->FixedDirty = FALSE; + Context->VariableDirty = FALSE; + } + + } else { + + // + // fixed and variable-length attributes stored separately. + // Only update the one(s) we need to. + // + + NtStatus = STATUS_SUCCESS; + if (FlushFixed) { + + SampDumpRXact(SampRXactContext, + RtlRXactOperationSetValue, + &(Context->RootName), + RootKey, + &SampFixedAttributeName, + REG_BINARY, + SampFixedBufferAddress(Context), + SampVariableBufferOffset(Context) - SampFixedBufferOffset(Context), + FixedBufferAddressFlag); + + NtStatus = RtlAddAttributeActionToRXact( + SampRXactContext, + RtlRXactOperationSetValue, + &(Context->RootName), + RootKey, + &SampFixedAttributeName, + REG_BINARY, + SampFixedBufferAddress(Context), + SampVariableBufferOffset(Context) - SampFixedBufferOffset(Context) + ); + +#if SAMP_DIAGNOSTICS + if (!NT_SUCCESS(NtStatus)) { + SampDiagPrint( DISPLAY_STORAGE_FAIL, + ("SAM: Failed to add action to RXact (0x%lx)\n", + NtStatus) ); + IF_SAMP_GLOBAL( BREAK_ON_STORAGE_FAIL ) { + ASSERT(NT_SUCCESS(NtStatus)); + } + } +#endif //SAMP_DIAGNOSTICS + + if ( NT_SUCCESS( NtStatus ) ) { + + Context->FixedDirty = FALSE; + } + } + + if (NT_SUCCESS(NtStatus) && FlushVariable) { + + SampDumpRXact(SampRXactContext, + RtlRXactOperationSetValue, + &(Context->RootName), + RootKey, + &SampVariableAttributeName, + REG_BINARY, + (PUCHAR)SampVariableArrayAddress(Context), + SampVariableBufferUsedLength(Context), + VARIABLE_LENGTH_ATTRIBUTE_FLAG); + + NtStatus = RtlAddAttributeActionToRXact( + SampRXactContext, + RtlRXactOperationSetValue, + &(Context->RootName), + RootKey, + &SampVariableAttributeName, + REG_BINARY, + SampVariableArrayAddress( Context ), + SampVariableBufferUsedLength(Context) + ); + +#if SAMP_DIAGNOSTICS + if (!NT_SUCCESS(NtStatus)) { + SampDiagPrint( DISPLAY_STORAGE_FAIL, + ("SAM: Failed to add action to RXact (0x%lx)\n", + NtStatus) ); + IF_SAMP_GLOBAL( BREAK_ON_STORAGE_FAIL ) { + ASSERT(NT_SUCCESS(NtStatus)); + } + } +#endif //SAMP_DIAGNOSTICS + + + if ( NT_SUCCESS( NtStatus ) ) { + Context->VariableDirty = FALSE; + } + } + } + + return(NtStatus); +} + + + +NTSTATUS +SampStoreObjectAttributes( + IN PSAMP_OBJECT Context, + IN BOOLEAN UseKeyHandle + ) + +/*++ + +Routine Description: + + This routine determines from the object context whether to update object + attributes residing in the registry or in the DS backing store, and then + calls the appropriate routine to do the work. + +Arguments: + + Context - Pointer, the object's SAM context. + + UseKeyHandle - Flag indicating that the registry key handle should be + used (if this is a registry update--it is not used in DS updates). + +Return Value: + + STATUS_SUCCESS - storage was updated without a problem, otherwise an + error code is returned. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + + SAMTRACE("SampStoreObjectAttributes"); + + if (NULL != Context) + { + if (IsDsObject(Context)) + { + NtStatus = SampStoreDsObjectAttributes(Context); + } + else + { + NtStatus = SampStoreRegObjectAttributes(Context, UseKeyHandle); + } + } + else + { + NtStatus = STATUS_INVALID_PARAMETER; + } + + return(NtStatus); +} + + + +NTSTATUS +SampDeleteAttributeKeys( + IN PSAMP_OBJECT Context + ) + +/*++ + + This API is used to delete the attribute keys that are created in the + registry underneath a SAM object. + + + +Parameters: + + Context - Pointer to an object context block. + + + +Return Values: + + STATUS_SUCCESS - The service completed successfully. + + Error status may be returned by registry calls. + +--*/ +{ + UNICODE_STRING + KeyName; + + NTSTATUS + NtStatus; + + SAMTRACE("SampDeleteAttributeKeys"); + + if (SampObjectInformation[Context->ObjectType].FixedStoredSeparately) { + + // + // Must delete both fixed and variable attribute keys. + // + + NtStatus = SampBuildAccountSubKeyName( + SampUserObjectType, + &KeyName, + Context->TypeBody.User.Rid, + &SampFixedAttributeName + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = RtlAddActionToRXact( + SampRXactContext, + RtlRXactOperationDelete, + &KeyName, + 0, + NULL, + 0 + ); + SampFreeUnicodeString( &KeyName ); + + NtStatus = SampBuildAccountSubKeyName( + SampUserObjectType, + &KeyName, + Context->TypeBody.User.Rid, + &SampVariableAttributeName + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = RtlAddActionToRXact( + SampRXactContext, + RtlRXactOperationDelete, + &KeyName, + 0, + NULL, + 0 + ); + SampFreeUnicodeString( &KeyName ); + } + } + + } else { + + // + // Must delete the combined attribute key. + // + + NtStatus = SampBuildAccountSubKeyName( + SampUserObjectType, + &KeyName, + Context->TypeBody.User.Rid, + &SampCombinedAttributeName + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = RtlAddActionToRXact( + SampRXactContext, + RtlRXactOperationDelete, + &KeyName, + 0, + NULL, + 0 + ); + + SampFreeUnicodeString( &KeyName ); + } + } + + return( NtStatus ); +} + + + + +NTSTATUS +SampGetFixedAttributes( + IN PSAMP_OBJECT Context, + IN BOOLEAN MakeCopy, + OUT PVOID *FixedData + ) + +/*++ + + This API is used to get a pointer to the fixed-length attributes. + + + + +Parameters: + + Context - Pointer to an object context block. + + FixedData - Receives a pointer to the fixed-length data. + + + +Return Values: + + STATUS_SUCCESS - The service completed successfully. + + STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not + be allocated. + +--*/ +{ + NTSTATUS + NtStatus; + + SAMTRACE("SampGetFixedAttributes"); + + // + // Make the data valid + // + + NtStatus = SampValidateAttributes( Context, SAMP_FIXED_ATTRIBUTES ); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + // + // Return a pointer to the fixed-length attributes. + // + + if (MakeCopy == FALSE) { + *FixedData = (PVOID)SampFixedBufferAddress( Context ); + return(STATUS_SUCCESS); + } + + // + // Need to make a copy of the fixed data + // + + *FixedData = (PVOID)MIDL_user_allocate( SampFixedBufferLength( Context ) ); + if ((*FixedData) == NULL) { + return(STATUS_NO_MEMORY); + } + + RtlCopyMemory( *FixedData, + SampFixedBufferAddress( Context ), + SampFixedBufferLength( Context ) ); + + return(STATUS_SUCCESS); +} + + + + +NTSTATUS +SampSetFixedAttributes( + IN PSAMP_OBJECT Context, + IN PVOID FixedData + ) + +/*++ + + This API is used to replace the fixed-length data attribute. + + + +Parameters: + + Context - Pointer to an object context block. + + + + +Return Values: + + STATUS_SUCCESS - The service completed successfully. + +--*/ +{ + NTSTATUS + NtStatus; + + SAMTRACE("SampSetFixedAttributes"); + + // + // Make the fixed-length data valid + // + + NtStatus = SampValidateAttributes( Context, SAMP_FIXED_ATTRIBUTES ); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + if ( FixedData != SampFixedBufferAddress( Context ) ) { + + // + // The caller had a copy of the data, so we must copy the changes + // over our data buffer. + // + + RtlCopyMemory( SampFixedBufferAddress( Context ), + FixedData, + SampFixedBufferLength( Context ) ); + } + + // + // Mark the buffer dirty now and it will get flushed when the + // changes are committed. + // + + Context->FixedDirty = TRUE; + + return( NtStatus ); +} + + + + +NTSTATUS +SampGetUnicodeStringAttribute( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex, + IN BOOLEAN MakeCopy, + OUT PUNICODE_STRING UnicodeAttribute + ) + + +/*++ + + This API is used to get a copy of a UNICODE_STRING attribute or a + pointer to the attribute. If a pointer to the attribute is sought, + care must be taken to ensure the pointer is not used after it becomes + invalid. Actions that may cause an attribute pointer to become invalid + include setting an attribute value or dereferencing and then referencing the + object again. + + If MakeCopy is FALSE, indicating the string is to be referenced rather + than copied, then only the body of the string is referenced. The lengths + and pointer are set in the provided argument. + + + + +Parameters: + + Context - Pointer to an object context block. + + AttributeIndex - Indicates the index (into the variable length attribute + array) of the attribute to be retrieved as a UNICODE_STRING. + + MakeCopy - If TRUE, indicates that a copy of the attribute is to be made. + If FALSE, indicates a pointer to the attribute is desired without + making a copy. WARNING, if this is FALSE, the pointer is only + valid while the in-memory copy of the attribute remains in place. + Addition or replacement of any variable length attribute may + cause the attribute to be moved, and previously returned pointers + invalidated. + + UnicodeAttribute - Receives a pointer to the UNICODE_STRING. If + MakeCopy was specified as TRUE, then this pointer points to a block + of memory allocated with MIDL_user_allocate() which the caller is + responsible for freeing (using MIDL_user_free()). + + + +Return Values: + + STATUS_SUCCESS - The service completed successfully. + + STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not + be allocated. + +--*/ + +{ + NTSTATUS NtStatus; + ULONG Length; + + SAMTRACE("SampGetUnicodeStringAttribute"); + + // + // Make sure the requested attribute exists for the specified + // object type. + // + + SampValidateAttributeIndex( Context, AttributeIndex ); + + // + // Make the data valid + // + + NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES ); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + // + // Get the length of the attribute + // + + Length = SampObjectAttributeLength( Context, AttributeIndex ); + ASSERT(Length <= 0xFFFF); + + UnicodeAttribute->MaximumLength = (USHORT)Length; + UnicodeAttribute->Length = (USHORT)Length; + + // + // If we are not to allocate memory, then just return a pointer + // to the attribute. + // + + if (MakeCopy == FALSE) { + UnicodeAttribute->Buffer = + (PWSTR)SampObjectAttributeAddress( Context, AttributeIndex ); + return(STATUS_SUCCESS); + } + + // + // Need to make a copy of the attribute + // + // NOTE: We should test for zero length here and return a NULL pointer + // in that case, but this change would require verification of all of the + // callers of this routine, so I'm leaving it as is. + // + + UnicodeAttribute->Buffer = (PSID)MIDL_user_allocate( Length ); + if ((UnicodeAttribute->Buffer) == NULL) { + return(STATUS_NO_MEMORY); + } + + RtlCopyMemory( + UnicodeAttribute->Buffer, + SampObjectAttributeAddress( Context, AttributeIndex ), + Length + ); + + return(STATUS_SUCCESS); + +} + + +NTSTATUS +SampSetUnicodeStringAttribute( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex, + IN PUNICODE_STRING Attribute + ) + + +/*++ + + This API is used to replace a UNICODE_STRING attribute in an + object's variable length attributes. + + + + +Parameters: + + Context - Pointer to an object context block. + + AttributeIndex - Indicates the index (into the variable length attribute + array) of the attribute to be set as a UNICODE_STRING. + + + Attribute - Points to the new UNICODE_STRING value. + + + + +Return Values: + + STATUS_SUCCESS - The service completed successfully. + + STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not + be allocated. + +--*/ +{ + NTSTATUS + NtStatus; + + SAMTRACE("SampSetUnicodeStringAttribute"); + + + // + // Make sure the requested attribute exists for the specified + // object type. + // + + SampValidateAttributeIndex( Context, AttributeIndex ); + + // + // Make the variable-length data valid + // + + NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES ); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + // + // Set the new attribute value... + // + + NtStatus = SampSetVariableAttribute( + Context, + AttributeIndex, + 0, // Qualifier not used + (PUCHAR)Attribute->Buffer, + Attribute->Length + ); + + return(NtStatus); + +} + + +NTSTATUS +SampGetSidAttribute( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex, + IN BOOLEAN MakeCopy, + OUT PSID *Sid + ) + + +/*++ + + This API is used to get a copy of a SID attribute or a pointer to + the attribute. If a pointer to the attribute is sought, care must + be taken to ensure the pointer is not used after it becomes invalid. + Actions that may cause an attribute pointer to become invalid include + setting an attribute value or dereferencing and then referencing the + object again. + + + + +Parameters: + + Context - Pointer to an object context block. + + AttributeIndex - Indicates the index (into the variable length attribute + array) of the attribute to be retrieved as a SID. + + MakeCopy - If TRUE, indicates that a copy of the SID is to be made. + If FALSE, indicates a pointer to the SID is desired without + making a copy. WARNING, if this is FALSE, the pointer is only + valid while the in-memory copy of the SID remains in place. + Addition or replacement of any variable length attribute may + cause the SID to be moved, and previously returned pointers + invalidated. + + Sid - Receives a pointer to the SID. If MakeCopy was specified, then + this pointer points to a block of memory allocated with + MIDL_user_allocate() which the caller is responsible for freeing + (using MIDL_user_free()). + + + +Return Values: + + STATUS_SUCCESS - The service completed successfully. + + STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not + be allocated. + +--*/ +{ + NTSTATUS + NtStatus; + + ULONG + Length; + + PSID + SidAttribute; + + + SAMTRACE("SampGetSidAttribute"); + + + + // + // Make sure the requested attribute exists for the specified + // object type. + // + + SampValidateAttributeIndex( Context, AttributeIndex ); + + // + // Make the variable-length data valid + // + + NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES ); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + + // + // Get the address of the attribute in question + // + + SidAttribute = (PSID)SampObjectAttributeAddress( Context, AttributeIndex ); + + // + // If we are not to allocate memory, then just return a pointer + // to the attribute. + // + + if (MakeCopy == FALSE) { + (*Sid) = SidAttribute; + return(STATUS_SUCCESS); + } + + + // + // Need to make a copy of the SID + // + + Length = SampObjectAttributeLength( Context, AttributeIndex ); + ASSERT(Length == RtlLengthSid( SidAttribute ) ); + + (*Sid) = (PSID)MIDL_user_allocate( Length ); + if ((*Sid) == NULL) { + return(STATUS_NO_MEMORY); + } + + NtStatus = RtlCopySid( Length, (*Sid), SidAttribute ); + ASSERT(NT_SUCCESS(NtStatus)); + + return(NtStatus); + +} + + +NTSTATUS +SampSetSidAttribute( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex, + IN PSID Attribute + ) + + +/*++ + + This API is used to replace a SID attribute in an object's variable + length attributes. + + + + +Parameters: + + Context - Pointer to an object context block. + + AttributeIndex - Indicates the index (into the variable length attribute + array) of the attribute to be set as a SID. + + + Attribute - Points to the new SID value. + + Length - The length of the new attribute value (in bytes). + + + +Return Values: + + STATUS_SUCCESS - The service completed successfully. + + STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not + be allocated. + +--*/ +{ + NTSTATUS + NtStatus; + + SAMTRACE("SampSetSidAttribute"); + + // + // Validate the passed SID + // + + ASSERT(RtlValidSid(Attribute)); + + + // + // Make sure the requested attribute exists for the specified + // object type. + // + + SampValidateAttributeIndex( Context, AttributeIndex ); + + // + // Make the variable-length data valid + // + + NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES ); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + // + // Set the new attribute value... + // + + NtStatus = SampSetVariableAttribute( + Context, + AttributeIndex, + 0, // Qualifier not used + (PUCHAR)Attribute, + RtlLengthSid(Attribute) + ); + + return(NtStatus); + +} + + +NTSTATUS +SampGetAccessAttribute( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex, + IN BOOLEAN MakeCopy, + OUT PULONG Revision, + OUT PSECURITY_DESCRIPTOR *SecurityDescriptor + ) + + +/*++ + + This API is used to get a copy of the object access information. + This includes the security descriptor and revision level of the + object. + + + + +Parameters: + + Context - Pointer to an object context block. + + AttributeIndex - Indicates the index (into the variable length attribute + array) of the attribute to be retrieved. + + MakeCopy - If TRUE, indicates that a copy of the attribute is to be made. + If FALSE, indicates a pointer to the attribute is desired without + making a copy. WARNING, if this is FALSE, the pointer is only + valid while the in-memory copy of the attribute remains in place. + Addition or replacement of any variable length attribute may + cause the attribute to be moved, and previously returned pointers + invalidated. + + Revision - Receives the revision level from the access information. + + SecurityDescriptor - Receives a pointer to the attribute. If MakeCopy + was specified as TRUE, then this pointer points to a block of memory + allocated with MIDL_user_allocate() which the caller is responsible + for freeing (using MIDL_user_free()). + + + +Return Values: + + STATUS_SUCCESS - The service completed successfully. + + STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not + be allocated. + +--*/ +{ + NTSTATUS + NtStatus; + + ULONG + Length; + + PVOID + RawAttribute; + + SAMTRACE("SampGetAccessAttribute"); + + + // + // Make sure the requested attribute exists for the specified + // object type. + // + + SampValidateAttributeIndex( Context, AttributeIndex ); + + + + // + // Make the data valid + // + + NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES ); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + + // + // Get the revision level from the qualifier field of the variable + // array entry. + // + + (*Revision) = *(SampObjectAttributeQualifier( Context, AttributeIndex )); + + + // + // Get the address of the attribute in question + // + + RawAttribute = (PVOID)SampObjectAttributeAddress( Context, AttributeIndex ); + + + // + // If we are not to allocate memory, then just return a pointer + // to the attribute. + // + + if (MakeCopy == FALSE) { + (*SecurityDescriptor) = (PSECURITY_DESCRIPTOR)RawAttribute; + return(STATUS_SUCCESS); + } + + + // + // Need to make a copy of the attribute + // + + Length = SampObjectAttributeLength( Context, AttributeIndex ); + + (*SecurityDescriptor) = (PSECURITY_DESCRIPTOR)MIDL_user_allocate( Length ); + if ((*SecurityDescriptor) == NULL) { + return(STATUS_NO_MEMORY); + } + + RtlCopyMemory( (*SecurityDescriptor), RawAttribute, Length ); + + return(STATUS_SUCCESS); + +} + + + +NTSTATUS +SampSetAccessAttribute( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex, + IN PSECURITY_DESCRIPTOR Attribute, + IN ULONG Length + ) + + +/*++ + + This API is used to replace a SECURITY_DESCRIPTOR attribute in + an object's variable length attributes. + + + + +Parameters: + + Context - Pointer to an object context block. + + AttributeIndex - Indicates the index (into the variable length attribute + array) of the attribute to be set as a SECURITY_DESCRIPTOR. + + + Attribute - Points to the new SECURITY_DESCRIPTOR value. + + Length - The length of the new attribute value (in bytes). + + + +Return Values: + + STATUS_SUCCESS - The service completed successfully. + + STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not + be allocated. + +--*/ +{ + NTSTATUS + NtStatus; + + SAMTRACE("SampSetAccessAttribute"); + + + // + // Make sure the requested attribute exists for the specified + // object type. + // + + SampValidateAttributeIndex( Context, AttributeIndex ); + + // + // Make the variable-length data valid + // + + NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES ); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + // + // Set the new attribute value... + // + + NtStatus = SampSetVariableAttribute( + Context, + AttributeIndex, + SAMP_REVISION, + (PUCHAR)Attribute, + Length + ); + + return(NtStatus); + +} + + +NTSTATUS +SampGetUlongArrayAttribute( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex, + IN BOOLEAN MakeCopy, + OUT PULONG *UlongArray, + OUT PULONG UsedCount, + OUT PULONG LengthCount + ) + + +/*++ + + This API is used to get a copy of an array of ULONGs attribute or + a pointer to the attribute. If a pointer to the attribute is sought, + care must be taken to ensure the pointer is not used after it becomes + invalid. Actions that may cause an attribute pointer to become invalid + include setting an attribute value or dereferencing and then referencing + the object again. + + + + +Parameters: + + Context - Pointer to an object context block. + + AttributeIndex - Indicates the index (into the variable length attribute + array) of the attribute to be retrieved as a ULONG array. + + MakeCopy - If TRUE, indicates that a copy of the attribute is to be made. + If FALSE, indicates a pointer to the attribute is desired without + making a copy. WARNING, if this is FALSE, the pointer is only + valid while the in-memory copy of the attribute remains in place. + Addition or replacement of any variable length attribute may + cause the attribute to be moved, and previously returned pointers + invalidated. + + UlongArray - Receives a pointer to the array of ULONGS. If + MakeCopy was specified as TRUE, then this pointer points to a block + of memory allocated with MIDL_user_allocate() which the caller is + responsible for freeing (using MIDL_user_free()). + + UsedCount - Receives the number of elements used in the array. + + LengthCount - Receives the total number of elements in the array (some + at the end may be unused). If this value is zero, then + UlongArray will be returned as NULL. + + +Return Values: + + STATUS_SUCCESS - The service completed successfully. + + STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not + be allocated. + +--*/ +{ + NTSTATUS + NtStatus; + + ULONG + Length; + + + SAMTRACE("SampGetUlongArrayAttribute"); + + + + // + // Make sure the requested attribute exists for the specified + // object type. + // + + SampValidateAttributeIndex( Context, AttributeIndex ); + + + + // + // Make the data valid + // + + NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES ); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + // + // Get the count of array elements. + // If this is zero, then return will a null buffer pointer. + // + + (*UsedCount) = *(SampObjectAttributeQualifier( Context, AttributeIndex)); + + + + + // + // Get the length of the attribute + // + + Length = SampObjectAttributeLength( Context, AttributeIndex ); + + (*LengthCount) = Length / sizeof(ULONG); + + ASSERT( (*UsedCount) <= (*LengthCount) ); + + if ((*LengthCount) == 0) { + (*UlongArray) = NULL; + return(STATUS_SUCCESS); + } + + + // + // If we are not to allocate memory, then just return a pointer + // to the attribute. + // + + if (MakeCopy == FALSE) { + (*UlongArray) = + (PULONG)SampObjectAttributeAddress( Context, AttributeIndex ); + return(STATUS_SUCCESS); + } + + + // + // Need to make a copy of the attribute + // + + (*UlongArray) = (PULONG)MIDL_user_allocate( Length ); + if ((*UlongArray) == NULL) { + return(STATUS_NO_MEMORY); + } + + + RtlCopyMemory( (*UlongArray), + SampObjectAttributeAddress( Context, AttributeIndex ), + Length ); + + return(STATUS_SUCCESS); + +} + + +NTSTATUS +SampSetUlongArrayAttribute( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex, + IN PULONG Attribute, + IN ULONG UsedCount, + IN ULONG LengthCount + ) + + +/*++ + + This API is used to replace a ULONG array attribute in an + object's variable length attributes. + + + + +Parameters: + + Context - Pointer to an object context block. + + AttributeIndex - Indicates the index (into the variable length attribute + array) of the attribute to be set. + + + Attribute - Points to the new ULONG array value. + + UsedCount - The number of used elements in the array. + + LengthCount - the total number of elements in the array (some at the + end may be unused). + + +Return Values: + + STATUS_SUCCESS - The service completed successfully. + + STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not + be allocated. + +--*/ +{ + NTSTATUS + NtStatus; + + SAMTRACE("SampSetUlongArrayAttribute"); + + ASSERT( LengthCount >= UsedCount ); + + // + // Make sure the requested attribute exists for the specified + // object type. + // + + SampValidateAttributeIndex( Context, AttributeIndex ); + + // + // Make the variable-length data valid + // + + NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES ); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + // + // Set the new attribute value... + // + + NtStatus = SampSetVariableAttribute( + Context, + AttributeIndex, + UsedCount, // Qualifier contains used element count + (PUCHAR)Attribute, + (LengthCount * sizeof(ULONG)) + ); + + return(NtStatus); + +} + + +NTSTATUS +SampGetLargeIntArrayAttribute( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex, + IN BOOLEAN MakeCopy, + OUT PLARGE_INTEGER *LargeIntArray, + OUT PULONG Count + ) + + +/*++ + + This API is used to get a copy of an array of LARGE_INTEGERs attribute or + a pointer to the attribute. If a pointer to the attribute is sought, + care must be taken to ensure the pointer is not used after it becomes + invalid. Actions that may cause an attribute pointer to become invalid + include setting an attribute value or dereferencing and then referencing + the object again. + + + + +Parameters: + + Context - Pointer to an object context block. + + AttributeIndex - Indicates the index (into the variable length attribute + array) of the attribute to be retrieved. + + MakeCopy - If TRUE, indicates that a copy of the attribute is to be made. + If FALSE, indicates a pointer to the attribute is desired without + making a copy. WARNING, if this is FALSE, the pointer is only + valid while the in-memory copy of the attribute remains in place. + Addition or replacement of any variable length attribute may + cause the attribute to be moved, and previously returned pointers + invalidated. + + LargeIntArray - Receives a pointer to the array of ULONGS. If + MakeCopy was specified as TRUE, then this pointer points to a block + of memory allocated with MIDL_user_allocate() which the caller is + responsible for freeing (using MIDL_user_free()). + + + Count - Receives the number of elements in the array. If this value + is zero, then LargeIntArray will be returned as NULL. + +Return Values: + + STATUS_SUCCESS - The service completed successfully. + + STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not + be allocated. + +--*/ +{ + NTSTATUS + NtStatus; + + ULONG + Length; + + + SAMTRACE("SampGetLargeIntArrayAttribute"); + + + + // + // Make sure the requested attribute exists for the specified + // object type. + // + + SampValidateAttributeIndex( Context, AttributeIndex ); + + + + // + // Make the data valid + // + + NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES ); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + // + // Get the count of array elements. + // If this is zero, then return will a null buffer pointer. + // + + (*Count) = *(SampObjectAttributeQualifier( Context, AttributeIndex)); + + if ((*Count) == 0) { + (*LargeIntArray) = NULL; + return(STATUS_SUCCESS); + } + + + + // + // Get the length of the attribute + // + + Length = SampObjectAttributeLength( Context, AttributeIndex ); + + ASSERT((*Count) == (Length / sizeof(LARGE_INTEGER)) ); + + + + // + // If we are not to allocate memory, then just return a pointer + // to the attribute. + // + + if (MakeCopy == FALSE) { + (*LargeIntArray) = + (PLARGE_INTEGER)SampObjectAttributeAddress( Context, AttributeIndex ); + return(STATUS_SUCCESS); + } + + + // + // Need to make a copy of the attribute + // + + (*LargeIntArray) = (PLARGE_INTEGER)MIDL_user_allocate( Length ); + if ((*LargeIntArray) == NULL) { + return(STATUS_NO_MEMORY); + } + + + RtlCopyMemory( (*LargeIntArray), + SampObjectAttributeAddress( Context, AttributeIndex ), + Length ); + + return(STATUS_SUCCESS); + +} + + +NTSTATUS +SampSetLargeIntArrayAttribute( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex, + IN PLARGE_INTEGER Attribute, + IN ULONG Count + ) + + +/*++ + + This API is used to replace a LARGE_INTEGER array attribute in an + object's variable length attributes. + + + + +Parameters: + + Context - Pointer to an object context block. + + AttributeIndex - Indicates the index (into the variable length attribute + array) of the attribute to be set. + + + Attribute - Points to the new LARGE_INTEGER array value. + + Count - The number of elements in the array. + + +Return Values: + + STATUS_SUCCESS - The service completed successfully. + + STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not + be allocated. + +--*/ +{ + NTSTATUS + NtStatus; + + SAMTRACE("SampSetLargeIntArrayAttribute"); + + + // + // Make sure the requested attribute exists for the specified + // object type. + // + + SampValidateAttributeIndex( Context, AttributeIndex ); + + // + // Make the variable-length data valid + // + + NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES ); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + // + // Set the new attribute value... + // + + NtStatus = SampSetVariableAttribute( + Context, + AttributeIndex, + Count, // Qualifier contains element count + (PUCHAR)Attribute, + (Count * sizeof(LARGE_INTEGER)) + ); + + return(NtStatus); + +} + + +NTSTATUS +SampGetSidArrayAttribute( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex, + IN BOOLEAN MakeCopy, + OUT PSID *SidArray, + OUT PULONG Length, + OUT PULONG Count + ) + + +/*++ + + This API is used to get a copy of an array of SIDs attribute or + a pointer to the attribute. If a pointer to the attribute is sought, + care must be taken to ensure the pointer is not used after it becomes + invalid. Actions that may cause an attribute pointer to become invalid + include setting an attribute value or dereferencing and then referencing + the object again. + + + NOTE: This routine does not define the structure of a SID array, + so this effectively is a GetRawDataAttribute routine. + + + +Parameters: + + Context - Pointer to an object context block. + + AttributeIndex - Indicates the index (into the variable length attribute + array) of the attribute to be retrieved. + + MakeCopy - If TRUE, indicates that a copy of the attribute is to be made. + If FALSE, indicates a pointer to the attribute is desired without + making a copy. WARNING, if this is FALSE, the pointer is only + valid while the in-memory copy of the attribute remains in place. + Addition or replacement of any variable length attribute may + cause the attribute to be moved, and previously returned pointers + invalidated. + + SidArray - Receives a pointer to the array of SIDs. If + MakeCopy was specified as TRUE, then this pointer points to a block + of memory allocated with MIDL_user_allocate() which the caller is + responsible for freeing (using MIDL_user_free()). + + + Count - Receives the number of elements in the array. If this value + is zero, then SidArray will be returned as NULL. + +Return Values: + + STATUS_SUCCESS - The service completed successfully. + + STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not + be allocated. + +--*/ +{ + NTSTATUS + NtStatus; + + + SAMTRACE("SampGetSidArrayAttribute"); + + + + // + // Make sure the requested attribute exists for the specified + // object type. + // + + SampValidateAttributeIndex( Context, AttributeIndex ); + + + + // + // Make the data valid + // + + NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES ); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + // + // Get the count of array elements. + // If this is zero, then return will a null buffer pointer. + // + + (*Count) = *(SampObjectAttributeQualifier( Context, AttributeIndex)); + + if ((*Count) == 0) { + (*SidArray) = NULL; + (*Length) = 0; + return(STATUS_SUCCESS); + } + + + + // + // Get the length of the attribute + // + + (*Length) = SampObjectAttributeLength( Context, AttributeIndex ); + + + + + // + // If we are not to allocate memory, then just return a pointer + // to the attribute. + // + + if (MakeCopy == FALSE) { + (*SidArray) = + (PSID)SampObjectAttributeAddress( Context, AttributeIndex ); + return(STATUS_SUCCESS); + } + + + // + // Need to make a copy of the attribute + // + + (*SidArray) = (PSID)MIDL_user_allocate( (*Length) ); + if ((*SidArray) == NULL) { + return(STATUS_NO_MEMORY); + } + + + RtlCopyMemory( (*SidArray), + SampObjectAttributeAddress( Context, AttributeIndex ), + (*Length) ); + + return(STATUS_SUCCESS); + +} + + +NTSTATUS +SampSetSidArrayAttribute( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex, + IN PSID Attribute, + IN ULONG Length, + IN ULONG Count + ) + + +/*++ + + This API is used to replace a SID array attribute in an + object's variable length attributes. + + + + +Parameters: + + Context - Pointer to an object context block. + + AttributeIndex - Indicates the index (into the variable length attribute + array) of the attribute to be set. + + + Attribute - Points to the new SID array value. + + Length - Number of byte in the attribute buffer. + + Count - Number of SIDs in the array. + + +Return Values: + + STATUS_SUCCESS - The service completed successfully. + + STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not + be allocated. + +--*/ +{ + NTSTATUS + NtStatus; + + SAMTRACE("SampSetSidArrayAttribute"); + + + // + // Make sure the requested attribute exists for the specified + // object type. + // + + SampValidateAttributeIndex( Context, AttributeIndex ); + + // + // Make the variable-length data valid + // + + NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES ); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + // + // Set the new attribute value... + // + + NtStatus = SampSetVariableAttribute( + Context, + AttributeIndex, + Count, // Qualifier contains element count + (PUCHAR)Attribute, + Length + ); + + return(NtStatus); + +} + + +NTSTATUS +SampGetLogonHoursAttribute( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex, + IN BOOLEAN MakeCopy, + OUT PLOGON_HOURS LogonHours + ) + + +/*++ + + This API is used to get a copy of a logon hours attribute or + a pointer to the attribute. If a pointer to the attribute is sought, + care must be taken to ensure the pointer is not used after it becomes + invalid. Actions that may cause an attribute pointer to become invalid + include setting an attribute value or dereferencing and then referencing + the object again. + + + + +Parameters: + + Context - Pointer to an object context block. + + AttributeIndex - Indicates the index (into the variable length attribute + array) of the attribute to be retrieved. + + MakeCopy - If TRUE, indicates that a copy of the attribute is to be made. + If FALSE, indicates a pointer to the attribute is desired without + making a copy. WARNING, if this is FALSE, the pointer is only + valid while the in-memory copy of the attribute remains in place. + Addition or replacement of any variable length attribute may + cause the attribute to be moved, and previously returned pointers + invalidated. + + LogonHours - Receives the logon hours information. If MakeCopy is TRUE + then the bitmap pointed to from within this structure will be a copy + of the attribute and must be deallocated uing MIDL_user_free(). + Otherwise, this same field will point to the bitmap in the on-disk + buffer. + + +Return Values: + + STATUS_SUCCESS - The service completed successfully. + + STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not + be allocated. + +--*/ +{ + NTSTATUS + NtStatus; + + ULONG + Length, + Units; + + + SAMTRACE("SampGetLogonHoursAttribute"); + + + + // + // Make sure the requested attribute exists for the specified + // object type. + // + + SampValidateAttributeIndex( Context, AttributeIndex ); + + + + // + // Make the data valid + // + + NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES ); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + // + // Get the time units. + // If this is zero, then return will a null buffer pointer. + // + + Units = *(SampObjectAttributeQualifier( Context, AttributeIndex)); + ASSERT(Units <= 0xFFFF); + LogonHours->UnitsPerWeek = (USHORT)Units; + + if (Units == 0) { + LogonHours->LogonHours = NULL; + return(STATUS_SUCCESS); + } + + + + + // + // If we are not to allocate memory, then just return a pointer + // to the attribute. + // + + if (MakeCopy == FALSE) { + LogonHours->LogonHours = + (PUCHAR)SampObjectAttributeAddress( Context, AttributeIndex ); + return(STATUS_SUCCESS); + } + + + // + // Get the length of the attribute + // + + Length = SampObjectAttributeLength( Context, AttributeIndex ); + ASSERT(Length <= 0xFFFF); + + + // + // Need to make a copy of the attribute + // + + LogonHours->LogonHours = + (PUCHAR)MIDL_user_allocate( Length ); + if (LogonHours->LogonHours == NULL) { + return(STATUS_NO_MEMORY); + } + + + RtlCopyMemory( LogonHours->LogonHours, + SampObjectAttributeAddress( Context, AttributeIndex ), + Length ); + + return(STATUS_SUCCESS); + +} + + + +NTSTATUS +SampSetLogonHoursAttribute( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex, + IN PLOGON_HOURS Attribute + ) + + +/*++ + + This API is used to replace a LOGON_HOURS attribute in an + object's variable length attributes. + + UnitsPerWeek are stored in the Qualifier field of the attribute. + + + + +Parameters: + + Context - Pointer to an object context block. + + AttributeIndex - Indicates the index (into the variable length attribute + array) of the attribute to be set. + + + Attribute - Points to the new LOGON_HOURS value. + + + + +Return Values: + + STATUS_SUCCESS - The service completed successfully. + + STATUS_NO_MEMORY - Memory to receive a copy of the attribute could not + be allocated. + +--*/ +{ + NTSTATUS NtStatus; + PUCHAR LogonHours; + ULONG Length; + USHORT UnitsPerWeek; + + SAMTRACE("SampSetLogonHoursAttribute"); + + // + // Make sure the requested attribute exists for the specified + // object type. + // + + SampValidateAttributeIndex( Context, AttributeIndex ); + + // + // Make the variable-length data valid + // + + NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES ); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + // + // Grab the UnitsPerWeek value for the logon_hours structure. + // We use this to calculate the length of the data. + // + + if ( Attribute == NULL ) { + UnitsPerWeek = 0; + LogonHours = NULL; + } else { + UnitsPerWeek = Attribute->UnitsPerWeek; + LogonHours = Attribute->LogonHours; + } + + // + // Validate the data - make sure that if the units per week are non-zero + // then the logon hours buffer is non-NULL. + // + + if ( (UnitsPerWeek != 0) && (LogonHours == NULL) ) { + + return(STATUS_INVALID_PARAMETER); + } + // + // Calculate length of logon_hours structure + // + + Length = (ULONG)((UnitsPerWeek + 7) / 8); + + // + // Set the new attribute value... + // + + NtStatus = SampSetVariableAttribute( + Context, + AttributeIndex, + (ULONG)UnitsPerWeek, // Qualifier contains units per week + LogonHours, + Length + ); + + return(NtStatus); + +} + + + + + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// private routines // +// // +/////////////////////////////////////////////////////////////////////////////// + +BOOLEAN +SampDsIsAlreadyValidData( + IN PSAMP_OBJECT Context, + IN ULONG AttributeGroup + ) + +/*++ + +Description: + + This routine determines whether or not the attributes are in memory or + not (i.e. valid or not). OnDisk should only be NULL for a newly created + context that has just been initialized, otherwise it is non-NULL. + +Parameters: + + Context - Pointer to an object context block. + + AttributeGroup - Flag, either SAMP_FIXED_ATTRIBUTES or SAMP_VARIABLE- + ATTRIBUTES. + +Return Values: + + This routine returns a flag, TRUE if the attributes are in memory, FALSE + otherwise. + +--*/ + +{ + BOOLEAN Flag = FALSE; + + SAMTRACE("SampDsIsAlreadyValidData"); + + if (NULL != Context->OnDisk) + { + if (AttributeGroup == SAMP_FIXED_ATTRIBUTES) + { + if (SampFixedAttributesValid(Context)) + { + Flag = TRUE; + } + } + else + { + ASSERT(AttributeGroup == SAMP_VARIABLE_ATTRIBUTES); + + if (SampVariableAttributesValid(Context)) + { + Flag = TRUE; + } + } + } + + return(Flag); +} + + + +NTSTATUS +SampDsPrepareAttrBlock( + IN ULONG FixedCount, + IN PSAMP_FIXED_ATTRIBUTE_TYPE_INFO FixedAttrIds[], + IN ULONG VarCount, + IN PSAMP_VAR_ATTRIBUTE_TYPE_INFO VarAttrIds[], + OUT PDSATTRBLOCK AttrBlock + ) + +/*++ + +Description: + + This routine sets up a DSATTRBLOCK in preparation to read the DS. An + attribute block is created containaing the attribute identifiers for + the attributes to read. + +Parameters: + + FixedCount - Number of fixed-length attributes. + + FixedAttrIds - Array of attribute IDs of the fixed-length attributes. + + VarCount - Number of variable-length attributes. + + VarAttrIds - Array of attribute IDs of the fixed-length attributes. + + AttrBlock - Pointer, the generated attribute block with IDs. + +Return Values: + + STATUS_SUCCESS if successful, error otherwise. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + ULONG i = 0; + ULONG AttrCount = FixedCount + VarCount; + PDSATTR Attributes = NULL; + + SAMTRACE("SampDsPrepareAttrBlock"); + + Attributes = RtlAllocateHeap(RtlProcessHeap(), + 0, + AttrCount * sizeof(DSATTR)); + + if (NULL != Attributes) + { + RtlZeroMemory(Attributes, (AttrCount * sizeof(DSATTR))); + + // This loop is set up to handle the case where both fixed-length + // and variable-length attribute buffers are passed in (which may + // be needed for combined attribute conversion). + + for (i = 0; i < AttrCount; i++) + { + if (i < FixedCount) + { + // Set the fixed-length attribute type/id. + + Attributes[i].attrTyp = FixedAttrIds[i].Type; + } + else + { + ASSERT(0 <= (i - FixedCount)); + + // Set the variable-length attribute type/id. + + Attributes[i].attrTyp = VarAttrIds[i - FixedCount].Type; + } + + // The read operation does not require setting up valCount or + // pAVal. + + Attributes[i].AttrVal.valCount = 0; + Attributes[i].AttrVal.pAVal = NULL; + } + + // Hook up the attributes to the top-level attrblock and return. + + AttrBlock->attrCount = AttrCount; + AttrBlock->pAttr = Attributes; + + NtStatus = STATUS_SUCCESS; + } + else + { + NtStatus = STATUS_NO_MEMORY; + } + + return(NtStatus); +} + + + +NTSTATUS +SampDsMakeAttrBlock( + IN INT ObjectType, + IN ULONG AttributeGroup, + OUT PDSATTRBLOCK AttrBlock + ) + +/*++ + +Description: + + This routine determines the object type and sets the count of fixed- + length and variable-length attributes for the object. SampDsPrepare- + AttrBlock to set up the DSATTRBLOCK. + +Parameters: + + ObjectType - SAM object ID. + + AttributeGroup - Flag, either SAMP_FIXED_ATTRIBUTES or SAMP_VARIABLE- + ATTRIBUTES. + + AttrBlock - Pointer, generated attribute block. + +Return Values: + + STATUS_SUCCESS if no errors. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + ULONG FixedLenAttrCount = 0; + ULONG VarLenAttrCount = 0; + + SAMTRACE("SampDsMakeAttrBlock"); + + switch(ObjectType) + { + + case SampServerObjectType: + FixedLenAttrCount = SAMP_SERVER_FIXED_ATTR_COUNT; + VarLenAttrCount = SAMP_SERVER_VARIABLE_ATTRIBUTES; + break; + + case SampDomainObjectType: + FixedLenAttrCount = SAMP_DOMAIN_FIXED_ATTR_COUNT; + VarLenAttrCount = SAMP_DOMAIN_VARIABLE_ATTRIBUTES; + break; + + case SampGroupObjectType: + FixedLenAttrCount = SAMP_GROUP_FIXED_ATTR_COUNT; + VarLenAttrCount = SAMP_GROUP_VARIABLE_ATTRIBUTES; + break; + + case SampAliasObjectType: + FixedLenAttrCount = SAMP_ALIAS_FIXED_ATTR_COUNT; + VarLenAttrCount = SAMP_ALIAS_VARIABLE_ATTRIBUTES; + break; + + case SampUserObjectType: + FixedLenAttrCount = SAMP_USER_FIXED_ATTR_COUNT; + VarLenAttrCount = SAMP_USER_VARIABLE_ATTRIBUTES; + break; + + default: + + ASSERT(FALSE && "Invalid SampObjectType"); + break; + + } + + if ((0 < FixedLenAttrCount) && (0 < VarLenAttrCount)) + { + if (SAMP_FIXED_ATTRIBUTES == AttributeGroup) + { + NtStatus = SampDsPrepareAttrBlock( + FixedLenAttrCount, + &SampFixedAttributeInfo[ObjectType][0], + 0, + NULL, + AttrBlock); + } + else + { + NtStatus = SampDsPrepareAttrBlock( + 0, + NULL, + VarLenAttrCount, + &SampVarAttributeInfo[ObjectType][0], + AttrBlock); + } + } + + return(NtStatus); +} + + + +NTSTATUS +SampDsConvertReadAttrBlock( + IN INT ObjectType, + IN ULONG AttributeGroup, + IN PDSATTRBLOCK AttrBlock, + OUT PVOID *SamAttributes, + OUT PULONG FixedLength, + OUT PULONG VariableLength + ) + +/*++ + +Description: + + This routine converts an attribute block (DSATTRBLOCK) into a SAM attri- + bute buffer. This is used to convert the resultant attributes from a DS + read into the SAM attribute format. + +Parameters: + + ObjectType - SAM object ID. + + AttributeGroup - Flag, either SAMP_FIXED_ATTRIBUTES or SAMP_VARIABLE- + ATTRIBUTES. + + AttrBlock - Pointer, input attribute block. + + SamAttributes - Pointer, returned SAM attributes. + + FixedLength - Pointer, byte count of the fixed-length attributes size. + + VariableLength - Pointer, byte count of the variable-length attributes + size. + +Return Values: + + STATUS_SUCCESS if no error. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + + SAMTRACE("SampDsConvertReadData"); + + // Initialize the returned lengths and buffer. + + *FixedLength = 0; + *VariableLength = 0; + *SamAttributes = NULL; + + if (SAMP_FIXED_ATTRIBUTES == AttributeGroup) + { + NtStatus = SampConvertAttrBlockToFixedLengthAttributes( + ObjectType, + AttrBlock, + SamAttributes, + FixedLength); + } + else + { + NtStatus = SampConvertAttrBlockToVarLengthAttributes( + ObjectType, + AttrBlock, + (SAMP_VARIABLE_LENGTH_ATTRIBUTE**)SamAttributes, + VariableLength); + } + + return(NtStatus); +} + + + +NTSTATUS +SampDsUpdateContextFixedAttributes( + IN PSAMP_OBJECT Context, + IN ULONG FixedLength, + IN PVOID SamAttributes, + IN BOOLEAN FirstTimeInitialization + ) + +/*++ + +Description: + + This routine updates the SAM context fixed-length attributes if the size + of the attributes have changed. For fixed-length attributes, this only + occurs when a revision of the fixed-length data structures has caused a + size change in the structures. + +Parameters: + + Context - Pointer to an object context block. + + FixedLength - Byte count of the fixed-length attributes. + + SamAttributes - Pointer, the SAM fixed-length attributes. + + FirstTimeInitialization - Flag, the first time that the attributes are + set, the SAM global buffer lengths, addresses, and offsets do not + all accurately reflect the actual memory allocated, so skip certain + calculations when this flag is set. + +Return Values: + + STATUS_SUCCESS if no error. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + ULONG NewLength = 0; + PBYTE Buffer = NULL; + PBYTE VariableData = NULL; + ULONG VariableLength = 0; + + SAMTRACE("SampDsUpdateContextFixedAttributes"); + + // The first time through, the variable-buffer length will be zero, so + // the new length is only the fixed length. Note that OnDiskAllocated is + // also zero (a global) so don't attempt to use the variable-buffer + // offset in the calculation. + + if (FirstTimeInitialization) + { + NewLength = FixedLength; + } + else + { + ASSERT(0 < Context->OnDiskAllocated); + NewLength = FixedLength + SampDsVariableBufferLength(Context); + + DebugPrint("OnDiskAllocated = %lu\n", Context->OnDiskAllocated); + DebugPrint("VarBufOffset = %lu\n", SampDsVariableBufferOffset(Context)); + DebugPrint("VarBufLenth = %lu\n", SampDsVariableBufferLength(Context)); + } + + Buffer = RtlAllocateHeap(RtlProcessHeap(), 0, NewLength); + + DebugPrint("FixedLength = %lu\n", FixedLength); + DebugPrint("NewLength = %lu\n", NewLength); + DebugPrint("Buffer Addr = 0x%lx\n", Buffer); + + if (NULL != Buffer) + { + // Zero the new buffer and copy the fixed-length attributes into it. + + RtlZeroMemory(Buffer, NewLength); + RtlCopyMemory(Buffer, SamAttributes, FixedLength); + + if (NULL != Context->OnDisk) + { + // Save the current address and length of the variable data, if + // it exists. The first time through, the variable data is NULL. + // Release the old buffer. + + VariableData = SampDsVariableBufferAddress(Context); + VariableLength = SampDsVariableBufferLength(Context); + RtlCopyMemory(Buffer + FixedLength, VariableData, VariableLength); + + // Free the old OnDisk buffer. + + RtlFreeHeap(RtlProcessHeap(), 0, Context->OnDisk); + } + + // Reset the context attribute buffer to the new buffer. + + Context->OnDisk = Buffer; + + // Release the memory for the input buffer + + RtlFreeHeap(RtlProcessHeap(), 0, SamAttributes); + + NtStatus = STATUS_SUCCESS; + } + else + { + NtStatus = STATUS_NO_MEMORY; + } + + return(NtStatus); +} + + + +NTSTATUS +SampDsUpdateContextVariableAttributes( + IN PSAMP_OBJECT Context, + IN ULONG VariableLength, + IN PVOID SamAttributes + ) + +/*++ + +Description: + + This routine updates the SAM context variable-length attributes if the + size of the attributes has changed. Unlike the fixed-length attributes, + this will occur frequently due to fact that these attributes are vari- + able length. + +Parameters: + + Context - Pointer to an object context block. + + FixedLength - Byte count of the fixed-length attributes. + + SamAttributes - Pointer, the SAM fixed-length attributes. + +Return Values: + + STATUS_SUCCESS if no error. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + ULONG NewLength = 0; + PBYTE Buffer = NULL; + PBYTE FixedData = NULL; + ULONG FixedLength = 0; + + SAMTRACE("SampDsUpdateContextVariableAttributes"); + + // Get the current fixed-buffer length, add the new variable length, and + // allocate the new buffer. + + NewLength = SampDsFixedBufferLength(Context) + VariableLength; + Buffer = RtlAllocateHeap(RtlProcessHeap(), 0, NewLength); + + if (NULL != Buffer) + { + RtlZeroMemory(Buffer, NewLength); + + if (NULL != Context->OnDisk) + { + // Get the fixed-length buffer address and length... + + FixedData = SampDsFixedBufferAddress(Context); + FixedLength = SampDsFixedBufferLength(Context); + + if ((NULL != FixedData) && (0 < FixedLength)) + { + // Copy the fixed data into the new buffer and append the + // variable-length data. + + RtlCopyMemory(Buffer, FixedData, FixedLength); + + RtlCopyMemory(Buffer + FixedLength, + SamAttributes, + VariableLength); + + // Release the old attribute buffer and reset OnDisk to + // point at the new buffer. + + RtlFreeHeap(RtlProcessHeap(), 0, Context->OnDisk); + Context->OnDisk = Buffer; + + // Release the temporary attribute buffer. + + RtlFreeHeap(RtlProcessHeap(), 0, SamAttributes); + + NtStatus = STATUS_SUCCESS; + } + } + + // Context->OnDisk should never be NULL in this routine. + + ASSERT(NULL != Context->OnDisk); + + } + else + { + NtStatus = STATUS_NO_MEMORY; + } + + return(NtStatus); +} + + + +NTSTATUS +SampUpdateOffsets( + IN PSAMP_OBJECT Context, + IN BOOLEAN FirstTimeInitialization + ) + +/*++ + +Description: + + This routine updates the buffer (OnDisk) offset and length information + that is stored in the object information (SAMP_OBJECT_INFORMATION) and + in the instance information (SAMP_OBJECT), after the attributes have + been successfully updated. + +Parameters: + + Context - Pointer to an object context block. + + AttributeGroup - Flag, either SAMP_FIXED_ATTRIBUTES or SAMP_VARIABLE- + ATTRIBUTES. + +Return Values: + + STATUS_SUCCESS if no error. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_SUCCESS; + INT ObjectType = Context->ObjectType; + ULONG FixedDataLength = 0; + ULONG VariableArrayLength = 0; + ULONG VariableDataOffset = 0; + ULONG VariableDataLength = 0; + ULONG TotalBufferLength = 0; + PSAMP_VARIABLE_LENGTH_ATTRIBUTE VariableArray = NULL; + ULONG i = 0; + ULONG AttributeCount = 0; + + // Determine the SAM object type and compute the length of the fixed- + // length attributes and the length of the variable-length attribute + // array, which will be used as buffer offsets. + + SAMTRACE("SampUpdateOffsets"); + + switch(ObjectType) + { + + case SampServerObjectType: + + FixedDataLength = sizeof(SAMP_V1_FIXED_LENGTH_SERVER); + AttributeCount = SAMP_SERVER_VARIABLE_ATTRIBUTES; + break; + + case SampDomainObjectType: + + FixedDataLength = sizeof(SAMP_V1_0A_FIXED_LENGTH_DOMAIN); + AttributeCount = SAMP_DOMAIN_VARIABLE_ATTRIBUTES; + break; + + case SampGroupObjectType: + + FixedDataLength = sizeof(SAMP_V1_0A_FIXED_LENGTH_GROUP); + AttributeCount = SAMP_GROUP_VARIABLE_ATTRIBUTES; + break; + + case SampAliasObjectType: + + FixedDataLength = sizeof(SAMP_V1_FIXED_LENGTH_ALIAS); + AttributeCount = SAMP_ALIAS_VARIABLE_ATTRIBUTES; + break; + + case SampUserObjectType: + + FixedDataLength = sizeof(SAMP_V1_0A_FIXED_LENGTH_USER); + AttributeCount = SAMP_USER_VARIABLE_ATTRIBUTES; + break; + + default: + + // Invalid object type specified. + + NtStatus = STATUS_INTERNAL_ERROR; + break; + + } + + VariableArrayLength = (AttributeCount * + sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE)); + + if (NT_SUCCESS(NtStatus)) + { + // First, update the object information offsets and lengths. + + SampObjectInformation[ObjectType].FixedDsAttributesOffset = + 0; + + SampObjectInformation[ObjectType].FixedDsLengthSize = + FixedDataLength; + + SampObjectInformation[ObjectType].VariableDsBufferOffset = + FixedDataLength; + + SampObjectInformation[ObjectType].VariableDsArrayOffset = + FixedDataLength; + + SampObjectInformation[ObjectType].VariableDsDataOffset = + FixedDataLength + VariableArrayLength; + + if (FALSE == FirstTimeInitialization) + { + // Get a pointer to the array of variable-length information and + // total up the lengths of these attributes. + + VariableArray = (PSAMP_VARIABLE_LENGTH_ATTRIBUTE) + ((PBYTE)(Context->OnDisk) + FixedDataLength); + + for (i = 0; i < AttributeCount; i++) + { + VariableDataLength = VariableDataLength + VariableArray[i].Length; + } + } + else + { + // The first time through, the attribute buffer only contains + // the fixed-length attributes, so make sure the lengths for + // the variable attributes are zero. + + VariableArrayLength = 0; + VariableDataLength = 0; + } + + TotalBufferLength = FixedDataLength + + VariableArrayLength + + VariableDataLength; + } + + // Finally, update the instance information of the object's context. + + Context->OnDiskAllocated = TotalBufferLength; + Context->OnDiskUsed = TotalBufferLength; + + // The DS routines do not allocate extra space at the end of the SAM + // OnDisk buffer, hence OnDiskFree is always zero. + + // BUG: Should allocate extra OnDisk buffer free space for growth. + + Context->OnDiskFree = 0; + + DebugPrint("OnDiskAllocated = %lu\n", Context->OnDiskAllocated); + DebugPrint("OnDiskUsed = %lu\n", Context->OnDiskUsed); + DebugPrint("OnDiskFree = %lu\n", Context->OnDiskFree); + + return(NtStatus); +} + + + +NTSTATUS +SampDsUpdateContextAttributes( + IN PSAMP_OBJECT Context, + IN ULONG AttributeGroup, + IN PVOID SamAttributes, + IN ULONG FixedLength, + IN ULONG VariableLength + ) + +/*++ + +Description: + + This routine updates an object's attribute buffer (OnDisk) for the given + context. If the new buffer size is the same as the old size, as in the + case of modifications, then a simple memory copy is performed, otherwise + helper routines are called to resize the buffer and copy the data. A sub- + sequent helper routine is called to update the context buffer lengths + and offsets. + +Parameters: + + Context - Pointer to an object context block. + +Return Values: + + STATUS_SUCCESS + +--*/ + +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + INT ObjectType = Context->ObjectType; + PBYTE FixedLengthData = NULL; + PBYTE VariableLengthData = NULL; + BOOLEAN FirstTimeInitialization = FALSE; + + SAMTRACE("SampDsUpdateContextAttributes"); + + // BUG: OnDisk may only be set to fixed or variable attributes. + // The current version of SAM contains a hack that resets all attributes + // whenever the variable-length ones are asked for. This allows both the + // fixed and variable attributes to be set even if FixedStoredSeparately + // is TRUE. This routine does not maintain that behavior, which may create + // problems for SAM. If so, the caller of this routine will need to call + // it twice, once for fixed and once for variable attributes in the cases + // where FixedStoredSeparately is TRUE. + + if (SAMP_FIXED_ATTRIBUTES == AttributeGroup) + { + // Update the fixed-length attributes. The first time through, OnDisk + // will be NULL. + + if ((NULL != Context->OnDisk) && + (SampDsFixedBufferLength(Context) == FixedLength)) + { + // The fixed-length data is the same size (i.e. not + // doing an upgrade), so copy the new attributes and + // release the buffer. + + FixedLengthData = SampDsFixedBufferAddress(Context); + + RtlCopyMemory(FixedLengthData, + (PBYTE)SamAttributes, + FixedLength); + + RtlFreeHeap(RtlProcessHeap(), 0, SamAttributes); + + NtStatus = STATUS_SUCCESS; + } + else + { + if (NULL == Context->OnDisk) + { + // If OnDisk is NULL, then this is the first time that the + // attribute buffer of the context has been set. + + FirstTimeInitialization = TRUE; + } + + // The new attributes are not the same size as the old ones. + + NtStatus = SampDsUpdateContextFixedAttributes(Context, + FixedLength, + SamAttributes, + FirstTimeInitialization); + + // Fixed-attribute size changes when the context attributes + // have been set for the first time (i.e. changed from zero to + // actual sizes), or when the fixed-length structures are + // changed in an upgrade scenario. + + if (NT_SUCCESS(NtStatus)) + { + NtStatus = SampUpdateOffsets(Context, FirstTimeInitialization); + } + } + + if (NT_SUCCESS(NtStatus)) + { + Context->FixedValid = TRUE; + } + else + { + Context->FixedValid = FALSE; + } + } + else + { + // Update the variable-length attributes. In the event that the + // attributes are stored separately, OnDisk will be NULL the first + // time through (for variable-length attributes). + + if ((NULL != Context->OnDisk) && + (SampDsVariableBufferLength(Context) == VariableLength)) + { + // The variable-length data is the same size, so copy the new + // attributes and release the buffer. + + VariableLengthData = SampDsVariableBufferAddress(Context); + + RtlCopyMemory(VariableLengthData, + (PBYTE)SamAttributes, + VariableLength); + + RtlFreeHeap(RtlProcessHeap(), 0, SamAttributes); + + NtStatus = STATUS_SUCCESS; + } + else + { + if (NULL == Context->OnDisk) + { + FirstTimeInitialization = TRUE; + } + + // The new attributes are not the same size as the old + // ones. + + NtStatus = SampDsUpdateContextVariableAttributes( + Context, + VariableLength, + SamAttributes); + + // Variable-attribute size changes when the context attributes + // have been set for the first time (i.e. changed from zero to + // actual sizes), or when the variable-length data has changed + // size. + + if (NT_SUCCESS(NtStatus)) + { + NtStatus = SampUpdateOffsets(Context, FirstTimeInitialization); + } + } + + if (NT_SUCCESS(NtStatus)) + { + Context->VariableValid = TRUE; + } + else + { + Context->VariableValid = FALSE; + } + } + + return(NtStatus); +} + + + +NTSTATUS +SampValidateDsAttributes( + IN PSAMP_OBJECT Context, + IN ULONG AttributeGroup + ) + +/*++ + +Description: + + Ensure specified attributes are in-memory. If they are not, then read + them from the DS backing store. This routine fetches all of the stored + attributes for a given SAM object. To read a single attribute, or a + subset of attributes, SampDsRead should be used to selectively fetch + attributes. Context->OnDisk is updated with the new attributes. + +Parameters: + + Context - Pointer to an object context block. + + AttributeGroup - identifies which kind of attributes are being validated + (SAMP_FIXED_ATTRIBUTES or SAMP_VARIABLE_ATTRIBUTES). + +Return Values: + + STATUS_SUCCESS - The attributes are in-memory. + + STATUS_NO_MEMORY - Memory could not be allocated to retrieve the + attributes. + + Other values as may be returned by registry API trying to retrieve + the attributes from backing store. + This routine returns a flag, TRUE if the attributes are in memory, FALSE + otherwise. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + ULONG Flags = 0; + INT ObjectType = -1; + DSATTRBLOCK ReadAttrBlock; + DSATTRBLOCK ResultAttrBlock; + ULONG FixedLength = 0; + ULONG VariableLength = 0; + PVOID SamAttributes = NULL; + + SAMTRACE("SampValidateDsAttributes"); + + // The data might already be in memory, so check it out. + + if (FALSE == SampDsIsAlreadyValidData(Context, AttributeGroup)) + { + if (NULL != Context->ObjectNameInDs) + { + ObjectType = Context->ObjectType; + + RtlZeroMemory(&ReadAttrBlock, sizeof(DSATTRBLOCK)); + RtlZeroMemory(&ResultAttrBlock, sizeof(DSATTRBLOCK)); + + // Construct the input ATTRBLOCK used to specify which attributes + // should be read from the DS. + + NtStatus = SampDsMakeAttrBlock(ObjectType, + AttributeGroup, + &ReadAttrBlock); + + if ((NT_SUCCESS(NtStatus)) && (NULL != ReadAttrBlock.pAttr)) + { + // Read the attributes from the DS, flags is currently unused. + + NtStatus = SampDsRead(Context->ObjectNameInDs, + Flags, + ObjectType, + &ReadAttrBlock, + &ResultAttrBlock); + + if ((NT_SUCCESS(NtStatus)) && (NULL != ResultAttrBlock.pAttr)) + { + // Convert the ATTRBLOCK into the appropriate SAM attri- + // butes, returning them in the SamAttributes buffer. Note + // that the returned lengths are as follows: + // + // FixedLength - The byte count of the returned fixed- + // length buffer + // + // VariableLength - The byte count of the returned var- + // able-length buffer. + + NtStatus = SampDsConvertReadAttrBlock(ObjectType, + AttributeGroup, + &ResultAttrBlock, + &SamAttributes, + &FixedLength, + &VariableLength); + + if ((NT_SUCCESS(NtStatus)) && (NULL != SamAttributes)) + { + NtStatus = SampDsUpdateContextAttributes( + Context, + AttributeGroup, + SamAttributes, + FixedLength, + VariableLength); + } + } + } + } + } + else + { + NtStatus = STATUS_SUCCESS; + } + + return(NtStatus); +} + + + +NTSTATUS +SampValidateRegAttributes( + IN PSAMP_OBJECT Context, + IN ULONG AttributeGroup + ) + +/*++ + + Ensure specified attributes are in-memory. + If they are not, then read them from backing store. + + + +Parameters: + + Context - Pointer to an object context block. + + AttributeGroup - identifies which kind of attributes are being + validated (SAMP_FIXED_ATTRIBUTES or SAMP_VARIABLE_ATTRIBUTES). + + + +Return Values: + + STATUS_SUCCESS - The attributes are in-memory. + + STATUS_NO_MEMORY - Memory could not be allocated to retrieve the + attributes. + + Other values as may be returned by registry API trying to retrieve + the attributes from backing store. + +--*/ +{ + NTSTATUS + NtStatus; + + ULONG + RequiredLength, + TotalRequiredLength, + BufferLength; + + PUCHAR + Buffer; + + PUNICODE_STRING + KeyAttributeName; + + BOOLEAN + CreatedObject = FALSE; + + SAMTRACE("SampValidateRegAttributes"); + + + // + // The data might already be in memory. + // + + if (AttributeGroup == SAMP_FIXED_ATTRIBUTES) { + if (SampFixedAttributesValid( Context )) { + ASSERT(Context->OnDisk != NULL); + return(STATUS_SUCCESS); + } + + } else { + + ASSERT( AttributeGroup == SAMP_VARIABLE_ATTRIBUTES ); + if (SampVariableAttributesValid( Context )) { + ASSERT(Context->OnDisk != NULL); + return(STATUS_SUCCESS); + } + } + + + + // + // Retrieve it from the registry, or allocate it if new. + // + + + NtStatus = SampGetAttributeBufferReadInfo( + Context, + AttributeGroup, + &Buffer, + &BufferLength, + &KeyAttributeName + ); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + if ( Context->RootKey != INVALID_HANDLE_VALUE ) { + + // + // Account exists on disk, so read in the attributes. + // + + NtStatus = SampReadRegistryAttribute( Context->RootKey, + Buffer, + BufferLength, + KeyAttributeName, + &RequiredLength + ); + + RequiredLength = SampDwordAlignUlong(RequiredLength); + + if ( ( SampObjectInformation[Context->ObjectType].FixedStoredSeparately ) && + ( AttributeGroup == SAMP_VARIABLE_ATTRIBUTES ) ) { + + // + // RequiredLength was returned to us as the length of the + // variable attributes on the disk. However, we're going + // to be using it to determine the total buffer size as well + // as to set how much of the buffer is in use, so we must add + // the size of the fixed stuff that preceeds the variable + // buffer. + // + + TotalRequiredLength = RequiredLength + + SampVariableBufferOffset( Context ); + + } else { + + // + // Either the attribute groups are read together, or we're + // reading in the fixed attribute group. Either way, we + // already have the total size we need. + // + + TotalRequiredLength = RequiredLength; + } + + if ((NtStatus == STATUS_BUFFER_TOO_SMALL) || + ( NtStatus == STATUS_BUFFER_OVERFLOW ) ) { + + NtStatus = SampExtendAttributeBuffer( Context, TotalRequiredLength ); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + NtStatus = SampGetAttributeBufferReadInfo( + Context, + AttributeGroup, + &Buffer, + &BufferLength, + &KeyAttributeName + ); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + NtStatus = SampReadRegistryAttribute( Context->RootKey, + Buffer, + BufferLength, + KeyAttributeName, + &RequiredLength + ); + + } + + } else { + + // + // We're creating a new object. + // + // Initialize the requiredlength to the amount of the buffer + // we have used when we created the empty attributes. This will + // be the value stored in OnDiskUsed. + // + // Note OnDiskUsed is only used by operations on the variable + // length attributes. + // + + TotalRequiredLength = SampVariableDataOffset(Context); + + ASSERT(TotalRequiredLength <= Context->OnDiskAllocated); + + CreatedObject = TRUE; + } + + + + // + // if we read something, indicate that the corresponding buffer + // (and maybe both) are now valid. + // + // Also set the used and free information for the buffer if necessary. + // + + if (NT_SUCCESS(NtStatus)) { + if (SampObjectInformation[Context->ObjectType].FixedStoredSeparately) { + + // + // only one attribute group was read in + // + + if (AttributeGroup == SAMP_FIXED_ATTRIBUTES) { + Context->FixedValid = TRUE; + Context->FixedDirty = FALSE; + } else { + + ASSERT(AttributeGroup == SAMP_VARIABLE_ATTRIBUTES); + Context->VariableValid = TRUE; + Context->VariableDirty = FALSE; + + Context->OnDiskUsed = SampDwordAlignUlong(TotalRequiredLength); + Context->OnDiskFree = Context->OnDiskAllocated - + Context->OnDiskUsed; + } + } else { + + // + // Both attribute groups read in. + // + + Context->FixedValid = TRUE; + Context->FixedDirty = FALSE; + + Context->VariableValid = TRUE; + Context->VariableDirty = FALSE; + + Context->OnDiskUsed = SampDwordAlignUlong(TotalRequiredLength); + Context->OnDiskFree = Context->OnDiskAllocated - + Context->OnDiskUsed; + } + } + + if (NT_SUCCESS(NtStatus) && !CreatedObject) { + + // + // make any adjustments necessary to bring the data + // just read in up to current revision format. + // + + NtStatus = SampUpgradeToCurrentRevision( + Context, + AttributeGroup, + Buffer, + RequiredLength, + &TotalRequiredLength + ); + } + +#ifdef SAM_DEBUG_ATTRIBUTES + if (SampDebugAttributes) { + DbgPrint("SampValidateAttributes - initialized the context :\n\n"); + SampDumpAttributes(Context); + } +#endif + + return(NtStatus); +} + + + +NTSTATUS +SampValidateAttributes( + IN PSAMP_OBJECT Context, + IN ULONG AttributeGroup + ) + +/*++ + +Routine Description: + + This routine determines from the object context whether to validate object + attributes residing in the registry or in the DS backing store, and then + calls the appropriate routine to do the work. + +Arguments: + + Context - Pointer, the object's SAM context. + + AttributeGroup - identifies which kind of attributes are being validated + (SAMP_FIXED_ATTRIBUTES or SAMP_VARIABLE_ATTRIBUTES). + +Return Value: + + STATUS_SUCCESS - attributes were checked and read from storage if neces- + sary without a problem, otherwise an error code is returned. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + + SAMTRACE("SampValidateAttributes"); + + if (NULL != Context) + { + if (IsDsObject(Context)) + { + if (NULL != Context->OnDisk) + { + // The SAM object attributes have been set atleast once, so + // update them if needed. + + NtStatus = SampValidateDsAttributes(Context, AttributeGroup); + } + else + { + // The SAM object attributes have never been set because this + // is a new context. First set the fixed-length attributes and + // then the variable-length ones. + + ASSERT(SAMP_FIXED_ATTRIBUTES == AttributeGroup); + + // If the OnDisk buffer is NULL, make sure that the fixed- + // length attributes are loaded first. + + NtStatus = SampValidateDsAttributes( + Context, + SAMP_FIXED_ATTRIBUTES); + + if (NT_SUCCESS(NtStatus)) + { + // BUG: Not setting up var-length attrs during init time. + + // The NT3.5 - NT4.0 SAM attempts to set both the fixed- + // length and variable-length attributes if the OnDisk + // buffer is NULL (i.e. this is the first time that the + // buffer is set). This may not be necessary in NT5 SAM, + // so explicitly loading the variable attributes now is + // skipped. They are lazily loaded into memory whenever + // any variable-length attribute is asked for (see the + // implementation of SampValidateAttributes). THIS MEANS + // THAT THE VARIABLE-LENGTH ATTRIBUTES CAN ONLY BE LOADED + // INTO MEMORY AFTER THE FIXED-LENGTH ONES BECAUSE THEY + // ARE APPENDED ONTO THE END OF THE OnDisk BUFFER. + + // NtStatus = SampValidateDsAttributes( + // Context, + // SAMP_VARIABLE_ATTRIBUTES); + } + } + } + else + { + NtStatus = SampValidateRegAttributes(Context, AttributeGroup); + } + } + else + { + NtStatus = STATUS_INVALID_PARAMETER; + } + + return(NtStatus); +} + + + +NTSTATUS +SampUpgradeToCurrentRevision( + IN PSAMP_OBJECT Context, + IN ULONG AttributeGroup, + IN OUT PUCHAR Buffer, + IN ULONG LengthOfDataRead, + IN OUT PULONG TotalRequiredLength + ) + +/*++ + + Make any changes necessary bring attributes just read in + from disk up to the current revision level format. + + When we upgrade our attribute format, we don't bother changing + all data on disk. We take a lazy update approach, and only change + the data as it is changed for other operations. This means that + data we read from disk may be from revision 1. When this is + detected, the data is copied into a current revision structure, + and a pointer to that buffer is returned. + + + + + NOTE: For future reference, GROUP and ALIAS objects have + a revision level stored as a "Qualifier" value associated + with the security descriptor attribute. The SERVER object + stores the revision level in its fixed length attributes. + +Parameters: + + Context - Pointer to an object context block. + + AttributeGroup - identifies which kind of attributes are being + validated (SAMP_FIXED_ATTRIBUTES or SAMP_VARIABLE_ATTRIBUTES). + + Buffer - Pointer to the buffer containing the attributes. + + LengthOfDataRead - This is an important value. It must be the value + returned from the registry on the read operation. This tells us + exactly how many bytes of data were retrieved from disk. + + TotalRequiredLength - Will be left unchanged if no update was + was required. If an updated was made, this will be adjusted + to reflect the new length of the data. + +Return Values: + + None. + + +--*/ + +{ + + LARGE_INTEGER + ZeroModifiedCount = {0,0}; + PULONG + Pointer; + NTSTATUS + NtStatus = STATUS_SUCCESS; + + SAMTRACE("SampUpgradeToCurrentRevision"); + + + // + // Note that Buffer points inside a buffer that is + // hung off the Context block. We don't need to re-allocate + // a new attributes buffer because we are only changing + // fixed-length attributes in this release (and the variable + // length attributes were placed beyond the end of the new + // format fixed-length data). + // + // The approach we take is to copy the current fixed-length + // contents into a temporary buffer, and then copy them back + // into the attribute buffer. This can be done with stack + // variables. + // + + // + // Switch on the type of objects that have gone through revision + // changes. + // + + switch (Context->ObjectType) { + case SampDomainObjectType: + + // + // Domain FIXED_LENGTH attributes have had the following + // revisions: + // + // Revision 0x00010001 - NT1.0 (Revision NOT stored in ) + // (record. ) + // (Must ascertain revision ) + // (by record length. ) + // + // Revision 0x00010002 - NT1.0a (Revision is first ULONG ) + // (in record. ) + + if (LengthOfDataRead == + (sizeof(SAMP_V1_0_FIXED_LENGTH_DOMAIN) + + FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)) ) { + + PSAMP_V1_0A_FIXED_LENGTH_DOMAIN + V1aFixed; + + SAMP_V1_0_FIXED_LENGTH_DOMAIN + V1Fixed, *OldV1Fixed; + + + // + // Update from revision 0x00010001 + // + // First, copy the current buffer contents into a temporary + // buffer. + // + + OldV1Fixed = (PSAMP_V1_0_FIXED_LENGTH_DOMAIN)(Buffer + + FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)); + + RtlMoveMemory(&V1Fixed, OldV1Fixed, sizeof(SAMP_V1_0_FIXED_LENGTH_DOMAIN)); + + + // + // Now copy it back in the new format + // + + V1aFixed = (PSAMP_V1_0A_FIXED_LENGTH_DOMAIN)OldV1Fixed; + + V1aFixed->CreationTime = V1Fixed.CreationTime; + V1aFixed->ModifiedCount = V1Fixed.ModifiedCount; + V1aFixed->MaxPasswordAge = V1Fixed.MaxPasswordAge; + V1aFixed->MinPasswordAge = V1Fixed.MinPasswordAge; + V1aFixed->ForceLogoff = V1Fixed.ForceLogoff; + V1aFixed->NextRid = V1Fixed.NextRid; + V1aFixed->PasswordProperties = V1Fixed.PasswordProperties; + V1aFixed->MinPasswordLength = V1Fixed.MinPasswordLength; + V1aFixed->PasswordHistoryLength = V1Fixed.PasswordHistoryLength; + V1aFixed->ServerState = V1Fixed.ServerState; + V1aFixed->ServerRole = V1Fixed.ServerRole; + V1aFixed->UasCompatibilityRequired = V1Fixed.UasCompatibilityRequired; + + + // + // And initialize fields new for this revision + // + + V1aFixed->Revision = SAMP_REVISION; + V1aFixed->LockoutDuration.LowPart = 0xCF1DCC00; // 30 minutes - low part + V1aFixed->LockoutDuration.HighPart = 0XFFFFFFFB; // 30 minutes - high part + V1aFixed->LockoutObservationWindow.LowPart = 0xCF1DCC00; // 30 minutes - low part + V1aFixed->LockoutObservationWindow.HighPart = 0XFFFFFFFB; // 30 minutes - high part + V1aFixed->LockoutThreshold = 0; // Disabled + + if (V1aFixed->ServerRole == DomainServerRolePrimary) { + V1aFixed->ModifiedCountAtLastPromotion = V1Fixed.ModifiedCount; + } else { + V1aFixed->ModifiedCountAtLastPromotion = ZeroModifiedCount; + } + } + + break; //out of switch + + + + case SampUserObjectType: + + // + // User FIXED_LENGTH attributes have had the following + // revisions: + // + // Revision 0x00010001 - NT1.0 (Revision NOT stored in ) + // (record. ) + // (Must ascertain revision ) + // (by record length. ) + // + // Revision 0x00010002 - NT1.0a (Revision is first ULONG ) + // (in record. ) + // Revision 0x00010002a - NT3.5 (Revision is first ULONG ) + // (in record, still ) + // (0x00010002. Must ) + // (ascertain revison by ) + // (by record length ) + + if (LengthOfDataRead == + (sizeof(SAMP_V1_FIXED_LENGTH_USER) + + FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)) ) { + + PSAMP_V1_0A_FIXED_LENGTH_USER + V1aFixed; + + SAMP_V1_FIXED_LENGTH_USER + V1Fixed, *OldV1Fixed; + + + // + // Update from revision 0x00010001 + // + // First, copy the current buffer contents into a temporary + // buffer. + // + + OldV1Fixed = (PSAMP_V1_FIXED_LENGTH_USER)(Buffer + + FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)); + RtlMoveMemory(&V1Fixed, OldV1Fixed, sizeof(SAMP_V1_FIXED_LENGTH_USER)); + + + // + // Now copy it back in the new format + // + + V1aFixed = (PSAMP_V1_0A_FIXED_LENGTH_USER)OldV1Fixed; + + + V1aFixed->LastLogon = V1Fixed.LastLogon; + V1aFixed->LastLogoff = V1Fixed.LastLogoff; + V1aFixed->PasswordLastSet = V1Fixed.PasswordLastSet; + V1aFixed->AccountExpires = V1Fixed.AccountExpires; + V1aFixed->UserId = V1Fixed.UserId; + V1aFixed->PrimaryGroupId = V1Fixed.PrimaryGroupId; + V1aFixed->UserAccountControl = V1Fixed.UserAccountControl; + V1aFixed->CountryCode = V1Fixed.CountryCode; + V1aFixed->CodePage = V1Fixed.CodePage; + V1aFixed->BadPasswordCount = V1Fixed.BadPasswordCount; + V1aFixed->LogonCount = V1Fixed.LogonCount; + V1aFixed->AdminCount = V1Fixed.AdminCount; + + // + // And initialize fields new for this revision + // + + V1aFixed->Revision = SAMP_REVISION; + V1aFixed->LastBadPasswordTime = SampHasNeverTime; + V1aFixed->OperatorCount = 0; + V1aFixed->Unused2 = 0; + + } else if ((LengthOfDataRead == + (sizeof(SAMP_V1_0_FIXED_LENGTH_USER) + + FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)) ) && + (AttributeGroup == SAMP_FIXED_ATTRIBUTES)) { + + PSAMP_V1_0A_FIXED_LENGTH_USER + V1aFixed; + + // + // Update from revision 0x00010002 + // + // Just set the added field at the end to 0. + // + + V1aFixed = (PSAMP_V1_0A_FIXED_LENGTH_USER)(Buffer + + FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)); + + V1aFixed->OperatorCount = 0; + V1aFixed->Unused2 = 0; + } + + break; //out of switch + + case SampGroupObjectType: + // + // Group FIXED_LENGTH attributes have had the following + // revisions: + // + // Revision 0x00010001 - NT1.0 (Revision NOT stored in ) + // (record. ) + // (Must ascertain revision ) + // (by first few ULONGs. ) + // + // Revision 0x00010002 - NT1.0a (Revision is first ULONG ) + // (in record. ) + + Pointer = (PULONG) (Buffer + FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)); + + // + // The old fixed length group had a RID in the first ULONG and + // an attributes field in the second. The attributes are in the + // first and last nibble of the field. Currently, the RID is in + // the second ULONG. Since all RIDs are more than one nibble, + // a rid will always have something set in the middle six nibbles. + // + + if ( ( Pointer[0] != SAMP_REVISION ) && + ( ( Pointer[1] & 0x0ffffff0 ) == 0 ) ) { + + PSAMP_V1_0A_FIXED_LENGTH_GROUP + V1aFixed; + + SAMP_V1_FIXED_LENGTH_GROUP + V1Fixed, *OldV1Fixed; + + ULONG TotalLengthRequired; + + // + // Calculate the length required for the new group information. + // It is the size of the old group plus enough space for the + // new fields in the new fixed attributes. + // + + TotalLengthRequired = SampDwordAlignUlong( + LengthOfDataRead + + sizeof(SAMP_V1_0A_FIXED_LENGTH_GROUP) - + sizeof(SAMP_V1_FIXED_LENGTH_GROUP) + ); + + + NtStatus = SampExtendAttributeBuffer( + Context, + TotalLengthRequired + ); + + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + // + // Get the new buffer pointer + // + + Buffer = Context->OnDisk; + + // + // Move the variable information up to make space for the + // fixed information + // + + RtlMoveMemory( + Buffer + SampFixedBufferOffset( Context ) + sizeof(SAMP_V1_0A_FIXED_LENGTH_GROUP), + Buffer + SampFixedBufferOffset( Context) + sizeof(SAMP_V1_FIXED_LENGTH_GROUP), + LengthOfDataRead - SampFixedBufferOffset( Context) - sizeof(SAMP_V1_FIXED_LENGTH_GROUP) + ); + + // + // Update from revision 0x00010001 + // + // First, copy the current buffer contents into a temporary + // buffer. + // + + OldV1Fixed = (PSAMP_V1_FIXED_LENGTH_GROUP)(Buffer + + FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)); + + RtlCopyMemory(&V1Fixed, OldV1Fixed, sizeof(SAMP_V1_FIXED_LENGTH_GROUP)); + + // + // Now copy it back in the new format + // + + V1aFixed = (PSAMP_V1_0A_FIXED_LENGTH_GROUP)OldV1Fixed; + + V1aFixed->Revision = SAMP_REVISION; + V1aFixed->Unused1 = 0; + V1aFixed->RelativeId = V1Fixed.RelativeId; + V1aFixed->Attributes = V1Fixed.Attributes; + V1aFixed->AdminCount = (V1Fixed.AdminGroup) ? TRUE : FALSE; + V1aFixed->OperatorCount = 0; + + // + // Update the indicator of how long the on disk structure + // is. + // + + Context->OnDiskUsed += (sizeof(SAMP_V1_0A_FIXED_LENGTH_GROUP) - sizeof(SAMP_V1_FIXED_LENGTH_GROUP)); + Context->OnDiskFree = Context->OnDiskAllocated - Context->OnDiskUsed; + } + + break; + + default: + + // + // The rest of the object types have not changed format + // and so need not be updated. + // + + break; //out of switch + + } + + return(NtStatus); +} + + +PUCHAR +SampObjectAttributeAddress( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex + ) + +/*++ + + Retrieve the address of a variable-length attribute. The attributes are + assumed to already be in-memory. The NT3.51-4.0 SAM stores attribute + offsets differently from the NT5 SAM. In the earlier versions (which + exclusively used the registry as the backing store), the attribute offset + value (in SAMP_VARIABLE_LENGTH_ATTRIBUTE) was self-relative to the end + of the attribute array. The NT5 version is self-relative from the start + of the array. + +Parameters: + + Context - Pointer to an object context block. + + AttributeIndex - Indicates the index (into the variable length attribute + array) of the attribute to be retrieved. + +Return Values: + + STATUS_SUCCESS - The attributes are in-memory. + + STATUS_NO_MEMORY - Memory could not be allocated to retrieve the + attributes. + + Other values as may be returned by registry API trying to retrieve + the attributes from backing store. + +--*/ + +{ + PSAMP_VARIABLE_LENGTH_ATTRIBUTE AttributeArray; + PUCHAR AttributeAddress; + + SAMTRACE("SampObjectAttributeAddress"); + + ASSERT(SampVariableAttributesValid(Context)); + + AttributeArray = (PSAMP_VARIABLE_LENGTH_ATTRIBUTE) + SampVariableArrayAddress(Context); + + if (IsDsObject(Context)) + { + // DS based attribute offsets are relative to the start of the + // attribute array. + + AttributeAddress = (PUCHAR)Context->OnDisk + + (SampVariableBufferOffset(Context) + + AttributeArray[AttributeIndex].Offset); + } + else + { + // Registry based attribute offsets are relative to the end of the + // attribute array. + + AttributeAddress = (PUCHAR)Context->OnDisk + + (SampVariableDataOffset(Context) + + AttributeArray[AttributeIndex].Offset); + } + + return(AttributeAddress); +} + + + +ULONG +SampObjectAttributeLength( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex + ) + +/*++ + + Retrieve the length of a variable-length attribute. + + + The attributes are assumed to already be in-memory. + + + +Parameters: + + Context - Pointer to an object context block. + + AttributeIndex - Indicates the index (into the variable length attribute + array) of the attribute whose length is to be retrieved. + + + + +Return Values: + + The length of the attribute (in bytes). + +--*/ +{ + PSAMP_VARIABLE_LENGTH_ATTRIBUTE + AttributeArray; + + SAMTRACE("SampObjectAttributeLength"); + + + ASSERT( SampVariableAttributesValid( Context ) ); + + AttributeArray = (PSAMP_VARIABLE_LENGTH_ATTRIBUTE) + SampVariableArrayAddress( Context ); + + return( AttributeArray[AttributeIndex].Length ); + +} + + +PULONG +SampObjectAttributeQualifier( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex + ) + +/*++ + + Retrieve the address of the qualifier field of a variable-length + attribute. + + The attributes are assumed to already be in-memory. + + + +Parameters: + + Context - Pointer to an object context block. + + AttributeIndex - Indicates the index (into the variable length attribute + array) of the attribute whose qualifier address is to be returned. + + + + +Return Values: + + The address of the specifed attribute's qualifier field. + +--*/ +{ + PSAMP_VARIABLE_LENGTH_ATTRIBUTE + AttributeArray; + + SAMTRACE("SampObjectAttributeQualifier"); + + + ASSERT( SampVariableAttributesValid( Context ) ); + + AttributeArray = (PSAMP_VARIABLE_LENGTH_ATTRIBUTE) + SampVariableArrayAddress( Context ); + + return( &(AttributeArray[AttributeIndex].Qualifier) ); + +} + + +NTSTATUS +SampGetAttributeBufferReadInfo( + IN PSAMP_OBJECT Context, + IN ULONG AttributeGroup, + OUT PUCHAR *Buffer, + OUT PULONG BufferLength, + OUT PUNICODE_STRING *KeyAttributeName + ) + +/*++ + + Get attribute buffer information needed to read data from + backing store. + + If there is currently no attribute buffer, then allocate one. + + +Parameters: + + Context - Pointer to an object context block. + + AttributeGroup - Indicates which attribute grouping you are + interested in. This is only interesting if the fixed and + variable-length attributes are stored separately. + + Buffer - Receives a pointer to the beginning of the appropriate + buffer (fixed or variable). This will be dword aligned. + If the attributes are stored together, this will point + to the beginning of the fixed-length attributes. + + BufferLength - Receives the number of bytes in the buffer. + + KeyAttributeName - Receives a pointer to the unicode name of the + attribute to read the attributes from. + + + + +Return Values: + + STATUS_SUCCESS - The attributes have been read. + + STATUS_NO_MEMORY - Memory could not be allocated to receive the + data from disk. + + Other values as may be returned reading from disk. + + +--*/ +{ + NTSTATUS + NtStatus = STATUS_SUCCESS; + + SAMTRACE("SampGetAttributeBufferReadInfo"); + + + // + // If the context block currently has no buffer info, then + // "extend" (create) it so we can return buffer information. + // + + if (Context->OnDiskAllocated == 0) { + + NtStatus = SampExtendAttributeBuffer( + Context, + SAMP_MINIMUM_ATTRIBUTE_ALLOC + ); + + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + } + + + + // + // Get the buffer address and length + // + + if (SampObjectInformation[Context->ObjectType].FixedStoredSeparately) { + + // + // stored separately. Address and length is dependent upon + // what is being asked for. Source registry attribute name + // is also. + // + + if (AttributeGroup == SAMP_FIXED_ATTRIBUTES) { + (*Buffer) = Context->OnDisk; + (*BufferLength) = SampVariableBufferOffset( Context ); + (*KeyAttributeName) = &SampFixedAttributeName; + } else { + (*Buffer) = SampVariableBufferAddress( Context ); + (*BufferLength) = SampVariableBufferLength( Context ); + (*KeyAttributeName) = &SampVariableAttributeName; + } + + } else { + + // + // Attributes stored together - doesn't matter which is being + // asked for. + // + + (*Buffer) = Context->OnDisk; + (*BufferLength) = Context->OnDiskAllocated; + (*KeyAttributeName) = &SampCombinedAttributeName; + } + + + return(NtStatus); +} + + + + + +NTSTATUS +SampExtendAttributeBuffer( + IN PSAMP_OBJECT Context, + IN ULONG NewSize + ) + + +/*++ + + This routine extends (or creates) an attribute buffer by allocating + a larger one. It then copies the existing buffer's contents into + the new buffer, if there is an existing buffer. + + If a new buffer can not be allocated, then the context block is + returned with the old buffer intact. + + If this call succeeds, the buffer will be at least as large as + that asked for (and perhaps larger). + + +Parameters: + + Context - Pointer to an object context block. + + NewSize - The number of bytes to allocate for the new buffer. + This value can not be less than the number of bytes currently + in use. + + + + + +Return Values: + + STATUS_SUCCESS - The attributes are in-memory. + + STATUS_NO_MEMORY - Memory could not be allocated to retrieve the + attributes. + + Other values as may be returned by registry API trying to retrieve + the attributes from backing store. + +--*/ + +{ + + PUCHAR + OldBuffer; + + ULONG + AllocationSize; + + SAMTRACE("SampExtendAttributeBuffer"); + + +#if DBG + if ( Context->VariableValid ) { + ASSERT(NewSize >= Context->OnDiskUsed); + } +#endif + + + // + // Is an allocation necessary? + // + + if (NewSize <= Context->OnDiskAllocated) { + return(STATUS_SUCCESS); + } + + + + OldBuffer = Context->OnDisk; + + + // + // Pad the extend to allow for future edits efficiently. + // + + AllocationSize = SampDwordAlignUlong(NewSize + SAMP_MINIMUM_ATTRIBUTE_PAD); + Context->OnDisk = RtlAllocateHeap( + RtlProcessHeap(), 0, + AllocationSize + ); + + if (Context->OnDisk == NULL) { + Context->OnDisk = OldBuffer; + return(STATUS_NO_MEMORY); + } + + + // + // Set the new allocated size + + Context->OnDiskAllocated = AllocationSize; + + // + // If there was no buffer originally, then zero the new buffer, mark + // it as being invalid, and return. + // + + if (OldBuffer == NULL) { + + RtlZeroMemory( (PVOID)Context->OnDisk, AllocationSize ); + + Context->FixedDirty = TRUE; + Context->VariableDirty = TRUE; + Context->FixedValid = FALSE; + Context->VariableValid = FALSE; + + return(STATUS_SUCCESS); + } + + + // + // Set the free size. Note that this information is only set if + // the variable data is valid. + // Used size remains the same. + // + + if (Context->VariableValid == TRUE) { + Context->OnDiskFree = AllocationSize - Context->OnDiskUsed; + ASSERT(Context->OnDiskUsed == SampDwordAlignUlong(Context->OnDiskUsed)); + } + + + // + // There was an old buffer (or else we would have exited earlier). + // If any data in it was valid, copy it to the new buffer. Free the + // old buffer. + // + + if ( Context->FixedValid ) { + + RtlCopyMemory( + Context->OnDisk, + OldBuffer, + SampFixedBufferLength( Context ) + SampFixedBufferOffset( Context ) + ); + } + + // + // Note: in thise case we may copy the fixed data twice, since if the + // variable data is not stored separately then SampVariableBufferOffset + // is zero. + // + + if ( Context->VariableValid ) { + + RtlCopyMemory( + SampVariableBufferAddress( Context ), + OldBuffer + SampVariableBufferOffset( Context ), + Context->OnDiskUsed - SampVariableBufferOffset( Context ) + ); + } + + RtlFreeHeap( RtlProcessHeap(), 0, OldBuffer ); + + return(STATUS_SUCCESS); +} + + + +NTSTATUS +SampReadRegistryAttribute( + IN HANDLE Key, + IN PUCHAR Buffer, + IN ULONG BufferLength, + IN PUNICODE_STRING AttributeName, + OUT PULONG RequiredLength + ) + +/*++ + + + Retrieve the address of a variable-length attribute. + + The attributes are assumed to already be in-memory. + + + +Parameters: + + Key - Handle to the key whose attribute is to be read. + + Buffer - Pointer to the buffer to receive the information. + + BufferLength - Length of the buffer receiving the information. + + AttributeName - The name of the attribute. + + + +Return Values: + + STATUS_SUCCESS - Successful completion. + + + STATUS_BUFFER_TOO_SMALL - The data could not be read because the + buffer was too small. + + Other values as may be returned by registry API trying to retrieve + the attribute from backing store. + +--*/ + +{ + NTSTATUS + NtStatus; + + SAMTRACE("SampReadRegistryAttribute"); + + + // + // Try to read the attribute + // + + NtStatus = NtQueryValueKey( Key, + AttributeName, //ValueName, + KeyValuePartialInformation, //KeyValueInformationClass + (PVOID)Buffer, + BufferLength, + RequiredLength + ); + + SampDumpNtQueryValueKey(AttributeName, + KeyValuePartialInformation, + Buffer, + BufferLength, + RequiredLength); + + return(NtStatus); + +} + + + + +NTSTATUS +SampSetVariableAttribute( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex, + IN ULONG Qualifier, + IN PUCHAR Buffer, + IN ULONG Length + ) + + +/*++ + + This API is used to set a new attribute value. The new attribute + value may be longer, shorter, or the same size as the current + attribute. The data in the attribute buffer will be shifted to + make room for a larger attribute value or to fill in room left by + a smaller attribute value. + + PERFORMANCE CONCERN: If you have a lot of attributes to set, it + is worthwhile to start with the smallest indexed attribute + and work up to the largest indexed attribute. + + + + +Parameters: + + Context - Pointer to an object context block. + + AttributeIndex - Indicates the index (into the variable length attribute + array) of the attribute to be set. Typically, all attributes beyond + this one will have their data shifted. + + Buffer - The address of the buffer containing the new attribute value. + May be NULL if Length is zero. + + Length - The length (in bytes) of the new attribute value. + + +Return Values: + + STATUS_SUCCESS - The service completed successfully. + + STATUS_NO_MEMORY - Memory to expand the attribute buffer could not + be allocated. + +--*/ +{ + NTSTATUS + NtStatus; + + + ULONG + OriginalAttributeLength, + AdditionalSpaceNeeded, + NewBufferLength, + MaximumAttributeIndex, + MoveLength, + i; + + LONG + OffsetDelta; + + PSAMP_VARIABLE_LENGTH_ATTRIBUTE + AttributeArray; + + + PUCHAR + NewStart, + OriginalStart; + + SAMTRACE("SampSetVariableAttribute"); + + // + // Make sure the requested attribute exists for the specified + // object type. + // + + SampValidateAttributeIndex( Context, AttributeIndex ); + + + + // + // Make the data valid + // + + NtStatus = SampValidateAttributes( Context, SAMP_VARIABLE_ATTRIBUTES ); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + // + // Allocate a new buffer if necessary + // + + OriginalAttributeLength = SampObjectAttributeLength(Context, AttributeIndex); + + if (OriginalAttributeLength < Length) { + + AdditionalSpaceNeeded = Length - OriginalAttributeLength; + + if (Context->OnDiskFree < AdditionalSpaceNeeded) { + + NewBufferLength = Context->OnDiskUsed + AdditionalSpaceNeeded; + ASSERT(NewBufferLength > Context->OnDiskAllocated); + + NtStatus = SampExtendAttributeBuffer( Context, NewBufferLength ); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + } + } + + // + // Get the address of the attribute array. + // + + AttributeArray = SampVariableArrayAddress( Context ); + + // + // Now shift following attribute values + // + + OffsetDelta = (LONG)(SampDwordAlignUlong(Length) - + SampDwordAlignUlong(OriginalAttributeLength)); + + MaximumAttributeIndex = SampVariableAttributeCount( Context ); + + if ((OffsetDelta != 0) && (AttributeIndex+1 < MaximumAttributeIndex)) { + + // + // Shift all attributes above this one up or down by the OffsetDelta + // + + if (IsDsObject(Context)) + { + // DS variable-length attribute offsets are relative to the start + // of the variable-length array. + + MoveLength = Context->OnDiskUsed - + ( SampVariableBufferOffset( Context ) + + AttributeArray[AttributeIndex+1].Offset ); + } + else + { + // Registry variable-length attribute offsets are relative to the + // end of the variable-length array. + + MoveLength = Context->OnDiskUsed - + ( SampVariableDataOffset( Context ) + + AttributeArray[AttributeIndex+1].Offset ); + } + + // + // Shift the data (if there is any) + // + + if (MoveLength != 0) { + + OriginalStart = SampObjectAttributeAddress( Context, AttributeIndex+1); + NewStart = (PUCHAR)(OriginalStart + OffsetDelta); + RtlMoveMemory( NewStart, OriginalStart, MoveLength ); + } + + + // + // Adjust the offset pointers + // + + for ( i=AttributeIndex+1; i<MaximumAttributeIndex; i++) { + AttributeArray[i].Offset = + (ULONG)(OffsetDelta + (LONG)(AttributeArray[i].Offset)); + } + } + + + + // + // Now set the length and qualifier, and copy in the new attribute value + // (if it is non-zero length) + // + + AttributeArray[AttributeIndex].Length = Length; + AttributeArray[AttributeIndex].Qualifier = Qualifier; + + if (Length != 0) { + + RtlCopyMemory( SampObjectAttributeAddress( Context, AttributeIndex ), + Buffer, + Length + ); + } + + + + // + // Adjust the Used and Free values + // + + Context->OnDiskUsed += OffsetDelta; + Context->OnDiskFree -= OffsetDelta; + + ASSERT(Context->OnDiskFree == Context->OnDiskAllocated - Context->OnDiskUsed); + + // + // Mark the variable attributes dirty + // + + Context->VariableDirty = TRUE; + + +#ifdef SAM_DEBUG_ATTRIBUTES + if (SampDebugAttributes) { + DbgPrint("SampSetVariableAttribute %d to length %#x, qualifier %#x:\n", AttributeIndex, Length, Qualifier); + SampDumpAttributes(Context); + } +#endif + + + return(STATUS_SUCCESS); + +} + + +VOID +SampFreeAttributeBuffer( + IN PSAMP_OBJECT Context + ) + +/*++ + + + Free the buffer used to keep in-memory copies of the on-disk + object attributes. + + +Parameters: + + Context - Pointer to the object context whose buffer is to + be freed. + + +Return Values: + + None. + +--*/ + +{ +#if DBG + if ( Context->FixedValid ) { ASSERT(Context->FixedDirty == FALSE); } + if ( Context->VariableValid) { ASSERT(Context->VariableDirty == FALSE); } + ASSERT(Context->OnDisk != NULL); + ASSERT(Context->OnDiskAllocated != 0); +#endif + + SAMTRACE("SampFreeAttributeBuffer"); + + RtlFreeHeap( RtlProcessHeap(), 0, Context->OnDisk ); + + Context->OnDisk = NULL; + Context->OnDiskAllocated = 0; + + // + // Mark all attributes as invalid + // + + Context->FixedValid = FALSE; + Context->VariableValid = FALSE; + + + return; +} + + + +#ifdef SAM_DEBUG_ATTRIBUTES +VOID +SampDumpAttributes( + IN PSAMP_OBJECT Context + ) + + +/*++ + + This is a debug-only API to dump out the attributes for a context + to the kernel debugger. + +Parameters: + + Context - Pointer to an object context block. + +Return Values: + + None. + +--*/ +{ + ULONG Index; + PSAMP_OBJECT_INFORMATION ObjectTypeInfo = &SampObjectInformation[Context->ObjectType]; + PSAMP_VARIABLE_LENGTH_ATTRIBUTE AttributeArray; + + AttributeArray = (PSAMP_VARIABLE_LENGTH_ATTRIBUTE) + SampVariableArrayAddress( Context ); + + + DbgPrint("Dumping context 0x%x\n", Context); + DbgPrint("\n"); + DbgPrint("TYPE INFO\n"); + DbgPrint("Object type name = %wZ\n", &ObjectTypeInfo->ObjectTypeName); + DbgPrint("Fixed stored separately = %s\n", ObjectTypeInfo->FixedStoredSeparately ? "TRUE" : "FALSE"); + DbgPrint("Fixed attributes offset = %#x\n", ObjectTypeInfo->FixedAttributesOffset); + DbgPrint("Fixed attributes size = %#x\n", ObjectTypeInfo->FixedLengthSize); + DbgPrint("Variable buffer offset = %#x\n", ObjectTypeInfo->VariableBufferOffset); + DbgPrint("Variable array offset = %#x\n", ObjectTypeInfo->VariableArrayOffset); + DbgPrint("Variable data offset = %#x\n", ObjectTypeInfo->VariableDataOffset); + DbgPrint("Variable attribute count = %d\n", ObjectTypeInfo->VariableAttributeCount); + DbgPrint("\n"); + DbgPrint("INSTANCE INFO\n"); + DbgPrint("RootName = %wZ\n", &Context->RootName); + DbgPrint("Fixed Valid = %s\n", Context->FixedValid ? "TRUE" : "FALSE"); + DbgPrint("Variable Valid = %s\n", Context->VariableValid ? "TRUE" : "FALSE"); + DbgPrint("Fixed Dirty = %s\n", Context->FixedDirty ? "TRUE" : "FALSE"); + DbgPrint("Variable Dirty = %s\n", Context->VariableDirty ? "TRUE" : "FALSE"); + DbgPrint("OnDiskAllocated = %#x\n", Context->OnDiskAllocated); + DbgPrint("OnDiskUsed = %#x\n", Context->OnDiskUsed); + DbgPrint("OnDiskFree = %#x\n", Context->OnDiskFree); + DbgPrint("\n"); + + if ( Context->VariableValid ) { + + for (Index = 0; Index < ObjectTypeInfo->VariableAttributeCount; Index ++) { + + DbgPrint("Attr %d: Qualifier = %#6x, Offset = %#6x, Length = %#6x\n", + Index, + AttributeArray[Index].Qualifier, + AttributeArray[Index].Offset, + AttributeArray[Index].Length + ); + SampDumpData(SampObjectAttributeAddress(Context, Index), + SampObjectAttributeLength(Context, Index)); + } + } + + DbgPrint("\n\n"); +} + + +VOID +SampDumpData( + IN PVOID Buffer, + IN ULONG Length + ) + + +/*++ + + This is a debug-only API to dump out a buffer in hex + +Parameters: + + Buffer - Pointer to data + + Length - number of bytes in data + +Return Values: + + None. + +--*/ +{ + ULONG Index; + + for (Index = 0; Index < Length; Index ++) { + + ULONG Value = (ULONG)(((PBYTE)Buffer)[Index]); + + if ((Index % 16) == 0) { + DbgPrint("\n "); + } + + DbgPrint("%02x ", Value & 0xff); + } + + if (Length > 0) { + DbgPrint("\n\n"); + } +} + +#endif diff --git a/private/newsam2/server/bldsam3.c b/private/newsam2/server/bldsam3.c new file mode 100644 index 000000000..27b85426e --- /dev/null +++ b/private/newsam2/server/bldsam3.c @@ -0,0 +1,5439 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + bldsam3.c + +Abstract: + + This module provides an initialization capability to SAM. + + + Approach + -------- + + This code has gone through a number of migrations that make it + less than obvious what is going on. To leverage off existing + code and yet extend it to the initialization of two domains, + with aliases, the following aproach has been taken: + + (1) Obtain the name and SID of the account domain. + + (2) Build the various security descriptors needed + in the two domains. These are kept in an array + and the index is used to specify which applies + to each new account. + + (3) Build up a list of alias memberships. These, too, + are selected by index, with one entry being the + empty set. + + +Author: + + Jim Kelly 3-May-1991. + +Revision History: + +--*/ + +#include <nt.h> +#include <ntsam.h> +#include "ntlsa.h" +#include <ntrtl.h> +#include <nturtl.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "samsrvp.h" + + + + + + +// +// Macro to round up a ULONG value to be dword aligned +// (i.e., be a multipleof 4). +// Note, this does not handle the case where the Ulong is greater +// than 0xfffffffc. +// + +#define SampDwordAlignUlong( v ) (((v)+3) & 0xfffffffc) + +// +// Constants used for Sam Global Data string buffers +// + +#define SAMP_MAXIMUM_INTERNAL_NAME_LENGTH ((USHORT) 0x00000200L) + + + + + +// +// domain selector +// + +typedef enum _SAMP_DOMAIN_SELECTOR { + + DomainBuiltin = 0, + DomainAccount + +} SAMP_DOMAIN_SELECTOR, *PSAMP_DOMAIN_SELECTOR; + +// +// Types of protection that may be assigned to accounts +// + +typedef ULONG SAMP_ACCOUNT_PROTECTION; + +#define SAMP_PROT_SAM_SERVER (0L) +#define SAMP_PROT_BUILTIN_DOMAIN (1L) +#define SAMP_PROT_ACCOUNT_DOMAIN (2L) +#define SAMP_PROT_ADMIN_ALIAS (3L) +#define SAMP_PROT_PWRUSER_ACCESSIBLE_ALIAS (4L) +#define SAMP_PROT_NORMAL_ALIAS (5L) +#define SAMP_PROT_ADMIN_GROUP (6L) +#define SAMP_PROT_NORMAL_GROUP (7L) +#define SAMP_PROT_ADMIN_USER (8L) +#define SAMP_PROT_NORMAL_USER (9L) +#define SAMP_PROT_GUEST_ACCOUNT (10L) +#define SAMP_PROT_TYPES (11L) + +// +// Protection information for SAM objects +// + +typedef struct _SAMP_PROTECTION { + + ULONG Length; + PSECURITY_DESCRIPTOR Descriptor; + PULONG RidToReplace; + BOOLEAN RidReplacementRequired; + +} SAMP_PROTECTION, *PSAMP_PROTECTION; + + + + + +/////////////////////////////////////////////////////////////////////// +// // +// Global variables // +// // +/////////////////////////////////////////////////////////////////////// + +static LSA_HANDLE SampBldPolicyHandle; //Handle to LSA policy object + +static NTSTATUS Status; + +static BOOLEAN SampRealSetupWasRun; //Indicates a real setup was run +static BOOLEAN SampDeveloperSetup; //Indicates a developer setup is running + +NT_PRODUCT_TYPE SampProductType; +static DOMAIN_SERVER_ROLE SampServerRole; +static PPOLICY_PRIMARY_DOMAIN_INFO SampBldPrimaryDomain = NULL; + + + +static PSID WorldSid, + LocalSystemSid, + AdminsAliasSid, + UsersAliasSid, + PowerUsersAliasSid, + AccountAliasSid, + AnySidInAccountDomain; + + +static PACL TokenDefaultDaclInformation; +static ULONG TokenDefaultDaclInformationSize; + +// +// Handle to the registry key in which the SAM database resides +// + +static HANDLE SamParentKey = NULL; + +// +// Handle to the root SAM key. +// This is the key that has the RXACT applied to it. +// + +static HANDLE SamKey = NULL; + +static PRTL_RXACT_CONTEXT SamRXactContext; + +// +// Assorted names, buffers, and values used during registry key creation +// + +static PSID DomainSid; +static PUNICODE_STRING DomainNameU, FullDomainNameU; +static UNICODE_STRING AccountInternalDomainNameU, BuiltinInternalDomainNameU; +static UNICODE_STRING AccountExternalDomainNameU, BuiltinExternalDomainNameU; +static UNICODE_STRING FullAccountInternalDomainNameU, FullBuiltinInternalDomainNameU; +static UNICODE_STRING DomainNamePrefixU, TemporaryNamePrefixU, KeyNameU, TempStringU; + +static WCHAR KeyNameBuffer[2000]; +static WCHAR TempStringBuffer[2000]; +static SID_IDENTIFIER_AUTHORITY BuiltinAuthority = SECURITY_NT_AUTHORITY; + + + + +// +// Values that get placed in registry keys... +// + +static LARGE_INTEGER DomainMaxPasswordAge = { 0, - 6L * 7L * 24L * 60L / 7L }; // 6 weeks +static LARGE_INTEGER ModifiedCount = {0,0}; +static UNICODE_STRING NullUnicodeString; + + + +// +// Array of protection information for SAM objects +// + +static SAMP_PROTECTION SampProtection[SAMP_PROT_TYPES]; + + + + + + + + +// +// Internal routine definitions +// + + + +VOID +SampGetDomainPolicy( VOID ); + +VOID +SampGetServerRole( VOID ); + +VOID +SampGetPrimaryDomainInfo( VOID ); + + +VOID +GetDomainSids( VOID ); + +VOID +SetDomainName( + IN BOOLEAN BuiltinDomain + ); + + +VOID +Usage ( VOID ); + + +NTSTATUS +Initialize( VOID ); + +BOOLEAN +InitializeSecurityDescriptors( VOID ); + +NTSTATUS +SampCreateDatabaseProtection( + PISECURITY_DESCRIPTOR SD + ); + +NTSTATUS +SampBuildNewProtection( + IN ULONG AceCount, + IN PSID *AceSid, + IN ACCESS_MASK *AceMask, + IN PGENERIC_MAPPING GenericMap, + IN BOOLEAN UserObject, + OUT PSAMP_PROTECTION Result + ); + +NTSTATUS +InitializeSam( VOID ); + +NTSTATUS +PrepDomain( + IN SAMP_DOMAIN_SELECTOR Domain + ); + + +VOID +SetCurrentDomain( + IN SAMP_DOMAIN_SELECTOR Domain + ); + + +NTSTATUS +CreateBuiltinDomain( VOID ); + +NTSTATUS +CreateAccountDomain( VOID ); + + + +NTSTATUS +CreateAlias( + IN PUNICODE_STRING AccountNameU, + IN PUNICODE_STRING AccountCommentU, + IN BOOLEAN SpecialAccount, + IN ULONG Rid, + IN ULONG ProtectionIndex + ); + + +NTSTATUS +CreateGroup( + IN PUNICODE_STRING AccountNameU, + IN PUNICODE_STRING AccountCommentU, + IN BOOLEAN SpecialAccount, + IN ULONG Rid, + IN BOOLEAN Admin + ); + + +NTSTATUS +CreateUser( + IN PUNICODE_STRING AccountNameU, + IN PUNICODE_STRING AccountCommentU, + IN BOOLEAN SpecialAccount, + IN ULONG UserRid, + IN ULONG PrimaryGroup, + IN BOOLEAN Admin, + IN ULONG UserControl, + IN ULONG ProtectionIndex + ); + + + +NTSTATUS +UpdateAliasXReference( + IN ULONG AliasRid, + IN PSID Sid + ); + + +NTSTATUS +OpenAliasMember( + IN PSID Sid, + OUT PHANDLE KeyHandle + ); + + +PSID +BuildPrimaryDomainSid( + ULONG Rid + ); + +PSID +BuildAccountSid( + SAMP_DOMAIN_SELECTOR Domain, + ULONG Rid + ); + + +NTSTATUS +OpenOrCreateAccountRidKey( + IN PSID Sid, + IN HANDLE AliasDomainHandle, + OUT PHANDLE KeyHandle + ); + +NTSTATUS +OpenOrCreateAliasDomainKey( + IN PSID Sid, + OUT PHANDLE KeyHandle + ); + +NTSTATUS +AppendAliasDomainNameToUnicodeString( + IN OUT PUNICODE_STRING Destination, + IN PSID Sid + ); + + + +NTSTATUS +SampInitilializeRegistry ( VOID ); + + +NTSTATUS +SampDetermineSetupEnvironment( VOID ); + + + +NTSTATUS +SampGetMessageStrings( + LPVOID Resource, + DWORD Index1, + PUNICODE_STRING String1, + DWORD Index2, + PUNICODE_STRING String2 OPTIONAL + ); + + + + +/////////////////////////////////////////////////////////////////////// +// // +// Routines // +// // +/////////////////////////////////////////////////////////////////////// + + +VOID +Usage ( + VOID + ) +/*++ + + +Routine Description: + + This routine prints the "Usage:" message. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + +#if DBG + BldPrint( "\n"); + BldPrint( "\n"); + + BldPrint( "We offer no assistance in this suicide.\n"); + BldPrint( "\n"); + BldPrint( "\n"); + BldPrint( "\n"); +#endif + + return; +} + + +VOID +UnexpectedProblem ( + VOID + ) +/*++ + + +Routine Description: + + This routine prints a message indicating that an unexpected + problem has occured. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + +#if DBG + BldPrint( "\n"); + BldPrint( "\n"); + BldPrint( " An unexpected problem has prevented the command from\n"); + BldPrint( " completing successfully. Please contact one of the\n"); + BldPrint( " members of the security group for assistance.\n"); + BldPrint( "\n"); +#endif + + return; + +} + +NTSTATUS +Initialize( + VOID + ) +/*++ + + +Routine Description: + + This routine performs initialization operations before creating + each domain. + + This includes: + + - Setting the correct default owner and DACL for registry key + operations. + + - opening the parent registry key for the SAM database. + + +Arguments: + + None. + +Return Value: + + TRUE - Indicates initialization was successful. + + FALSE - Indicates initialization was not successful. + +--*/ + +{ + OBJECT_ATTRIBUTES SamParentAttributes, PolicyObjectAttributes; + UNICODE_STRING SamParentNameU; + ULONG Disposition; + HANDLE Token; + TOKEN_OWNER LocalSystemOwner; + SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; + SID_IDENTIFIER_AUTHORITY WorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY; + PACL Dacl; + TOKEN_DEFAULT_DACL DefaultDacl; + BOOLEAN CompletionStatus; + BOOLEAN ProductTypeRetrieved; + + SAMTRACE("Initialize"); + + // + // Set up some of the well known account SIDs for use... + // + + WorldSid = (PSID)RtlAllocateHeap(RtlProcessHeap(), 0,RtlLengthRequiredSid( 1 )); + ASSERT(WorldSid != NULL); + RtlInitializeSid( WorldSid, &WorldSidAuthority, 1 ); + *(RtlSubAuthoritySid( WorldSid, 0 )) = SECURITY_WORLD_RID; + + AdminsAliasSid = RtlAllocateHeap(RtlProcessHeap(), 0,RtlLengthRequiredSid( 2 )); + ASSERT(AdminsAliasSid != NULL); + RtlInitializeSid( AdminsAliasSid, &BuiltinAuthority, 2 ); + *(RtlSubAuthoritySid( AdminsAliasSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID; + *(RtlSubAuthoritySid( AdminsAliasSid, 1 )) = DOMAIN_ALIAS_RID_ADMINS; + + PowerUsersAliasSid = RtlAllocateHeap(RtlProcessHeap(), 0,RtlLengthRequiredSid( 2 )); + ASSERT(PowerUsersAliasSid != NULL); + RtlInitializeSid( PowerUsersAliasSid, &BuiltinAuthority, 2 ); + *(RtlSubAuthoritySid( PowerUsersAliasSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID; + *(RtlSubAuthoritySid( PowerUsersAliasSid, 1 )) = DOMAIN_ALIAS_RID_POWER_USERS; + + UsersAliasSid = RtlAllocateHeap(RtlProcessHeap(), 0,RtlLengthRequiredSid( 2 )); + ASSERT(UsersAliasSid != NULL); + RtlInitializeSid( UsersAliasSid, &BuiltinAuthority, 2 ); + *(RtlSubAuthoritySid( UsersAliasSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID; + *(RtlSubAuthoritySid( UsersAliasSid, 1 )) = DOMAIN_ALIAS_RID_USERS; + + AccountAliasSid = RtlAllocateHeap(RtlProcessHeap(), 0,RtlLengthRequiredSid( 2 )); + ASSERT(AccountAliasSid != NULL); + RtlInitializeSid( AccountAliasSid, &BuiltinAuthority, 2 ); + *(RtlSubAuthoritySid( AccountAliasSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID; + *(RtlSubAuthoritySid( AccountAliasSid, 1 )) = DOMAIN_ALIAS_RID_ACCOUNT_OPS; + + LocalSystemSid = RtlAllocateHeap(RtlProcessHeap(), 0,RtlLengthRequiredSid( 1 )); + ASSERT(LocalSystemSid != NULL); + RtlInitializeSid( LocalSystemSid, &NtAuthority, 1 ); + *(RtlSubAuthoritySid( LocalSystemSid, 0 )) = SECURITY_LOCAL_SYSTEM_RID; + + // + // Setup a buffer to use for all our key-name constructions + // + + KeyNameU.MaximumLength = 2000; + KeyNameU.Buffer = KeyNameBuffer; + + // + // Setup temporary Unicode string buffer. + // + + TempStringU.Buffer = TempStringBuffer; + TempStringU.MaximumLength = 2000; + + // + // Get a handle to the LSA Policy object + // + + InitializeObjectAttributes( + &PolicyObjectAttributes, + NULL, // Name + 0, // Attributes + NULL, // Root + NULL // Security Descriptor + ); + + Status = LsaIOpenPolicyTrusted( &SampBldPolicyHandle ); + + if (!NT_SUCCESS(Status)) { + + KdPrint(("newsam\\server\\bldsam3: Couldn't open LSA Policy object.\n" + " Status: 0x%lx\n\n", Status)); + return(Status); + } + + // + // Get the product type. + // + + ProductTypeRetrieved = RtlGetNtProductType( &SampProductType ); + + if (!ProductTypeRetrieved) { + + KdPrint(("Couldn't retrieve product type\n")); + return(STATUS_UNSUCCESSFUL); + } + + // + // Figure out if we are being initialized following a real + // setup, or it this is a developer setup. + // + + SampDetermineSetupEnvironment(); + + // + // Domain name prefix is required by SampGetDomainPolicy() and + // so must be initialized before that call. + // + + RtlInitUnicodeString( &DomainNamePrefixU, L"Domains"); + + // + // Set up domain names/Sids. + // + + SampGetDomainPolicy(); + + if (!NT_SUCCESS(Status)) { + + return(Status); + } + + // + // Get the role of this machine. + // + + SampGetServerRole(); + + if (!NT_SUCCESS(Status)) { + + return(Status); + } + + // + // Get the primary domain info. + // + + SampGetPrimaryDomainInfo(); + +#ifndef USER_MODE_SAM + + // + // Set the default owner to SYSTEM, Should not do this if USER_MODE_SAM is specified + // + + Status = NtOpenProcessToken( + NtCurrentProcess(), + (TOKEN_ADJUST_DEFAULT | TOKEN_QUERY), + &Token + ); + SUCCESS_ASSERT(Status, " Couldn't open process token.\n"); + + Status = NtQueryInformationToken( + Token, + TokenDefaultDacl, + NULL, + 0, + &TokenDefaultDaclInformationSize + ); + + if (Status != STATUS_BUFFER_TOO_SMALL) { + + KdPrint(("Error: Unable to query default ACL information\n")); + return( Status ); + } + + TokenDefaultDaclInformation = RtlAllocateHeap(RtlProcessHeap(), 0, TokenDefaultDaclInformationSize ); + + Status = NtQueryInformationToken( + Token, + TokenDefaultDacl, + TokenDefaultDaclInformation, + TokenDefaultDaclInformationSize, + &TokenDefaultDaclInformationSize + ); + + SUCCESS_ASSERT(Status, " Couldn't query default ACL information.\n"); + + LocalSystemOwner.Owner = LocalSystemSid; + + Status = NtSetInformationToken( + Token, + TokenOwner, + &LocalSystemOwner, + (ULONG)sizeof(TOKEN_OWNER) + ); + + if (Status == STATUS_INVALID_OWNER) { +#if DBG + BldPrint(" This command must be issued from an account that may\n"); + BldPrint(" assign the default SYSTEM id as the owner of objects.\n"); + BldPrint(" \n"); + BldPrint(" Try logging on to the system account and issuing this\n"); + BldPrint(" command again.\n"); + BldPrint(" \n"); +#endif + return(Status); + } + + // + // Set our default protection so that only system and ADMINISTRATORS + // can get to the registry keys created. + // The protection established is + // + // Grant:LocalSystem:Read|Write|Execute|Delete + // Grant:Administrators:Read|Execute|Delete|write_dacl + // + // + // + + Dacl = RtlAllocateHeap( RtlProcessHeap(), 0, 256 ); + if (Dacl == NULL) { +#if DBG + BldPrint(" Exhausted Heap Space Allocating Dacl\n"); +#endif + return(STATUS_NO_MEMORY); + } + + Status = RtlCreateAcl( Dacl, 256, ACL_REVISION2); + SUCCESS_ASSERT(Status, " Failed to initialize Dacl\n"); + + Status = RtlAddAccessAllowedAce( + Dacl, + ACL_REVISION2, + (GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | DELETE ), + LocalSystemSid + ); + SUCCESS_ASSERT(Status, " Failed to add SYSTEM ACE to Dacl\n"); + DefaultDacl.DefaultDacl = Dacl; + + Status = RtlAddAccessAllowedAce( + Dacl, + ACL_REVISION2, + (GENERIC_READ | + GENERIC_EXECUTE | + DELETE | + WRITE_DAC), + AdminsAliasSid + ); + SUCCESS_ASSERT(Status, " Failed to add ADMIN alias ACE to Dacl\n"); + DefaultDacl.DefaultDacl = Dacl; + + Status = NtSetInformationToken( + Token, + TokenDefaultDacl, + &DefaultDacl, + (ULONG)sizeof(TOKEN_DEFAULT_DACL) + ); + SUCCESS_ASSERT(Status, " Couldn't assign default Dacl\n"); + Status = NtClose( Token ); + + RtlFreeHeap( RtlProcessHeap(), 0, Dacl ); // No longer needed + +#endif + + // + // Open a handle to the parent of the SAM registry location. + // This parent must already exist. + // + +#ifdef USER_MODE_SAM + RtlInitUnicodeString( &SamParentNameU, L"\\Registry\\Machine\\Software\\SECURITY" ); +#else + RtlInitUnicodeString( &SamParentNameU, L"\\Registry\\Machine\\Security" ); +#endif + + InitializeObjectAttributes( + &SamParentAttributes, + &SamParentNameU, + OBJ_CASE_INSENSITIVE, + NULL, + NULL + ); + Status = RtlpNtCreateKey( + &SamParentKey, + (KEY_READ | KEY_CREATE_SUB_KEY), + &SamParentAttributes, + 0, + NULL, + &Disposition + ); + + if ( !NT_SUCCESS(Status) ) { +#if DBG + BldPrint( "\n" ); + BldPrint( "\n" ); + BldPrint( " We seem to be having trouble opening the registry\n" ); + BldPrint( " database key in which the Security Account Manager\n" ); + BldPrint( " information resides. This registry key should have been\n" ); + BldPrint( " created at system startup time. Please see one of the\n" ); + BldPrint( " security group developers for assistance in analyzing the\n" ); + BldPrint( " the problem.\n" ); + BldPrint( " Indicate that the registry key creation status is 0x%lx \n", Status); + BldPrint( "\n" ); + BldPrint( "\n" ); +#endif + + return(Status); + } + + // + // Set up some values, names, and buffers for later use + // + + + NullUnicodeString.Buffer = NULL; + NullUnicodeString.Length = 0; + NullUnicodeString.MaximumLength = 0; + + + + TemporaryNamePrefixU.Buffer = RtlAllocateHeap(RtlProcessHeap(), 0, 256); + TemporaryNamePrefixU.Length = 0; + TemporaryNamePrefixU.MaximumLength = 256; + + KeyNameU.Buffer = RtlAllocateHeap(RtlProcessHeap(), 0, 256); + KeyNameU.Length = 0; + KeyNameU.MaximumLength = 256; + + // + // Set up Security Descriptors needed for initialization... + // + + CompletionStatus = InitializeSecurityDescriptors(); + + if (CompletionStatus) { + Status = STATUS_SUCCESS; + } else { + Status = STATUS_UNSUCCESSFUL; + } + + return(Status); +} + + +BOOLEAN +InitializeSecurityDescriptors( + VOID + ) + +/*++ + +Routine Description: + + This routine initializes security descriptors needed to create + a SAM database. + + + This routine expects all SIDs to be previously initialized. + +Arguments: + + None. + +Return Value: + + TRUE - Indicates initialization was successful. + + FALSE - Indicates initialization was not successful. + + + The security descriptors are pointed to by global variables. + +--*/ + +{ + PSID AceSid[10]; // Don't expect more than 10 ACEs in any of these. + ACCESS_MASK AceMask[10]; // Access masks corresponding to Sids + + ACCESS_MASK NotForThisProductType; // Used to mask product-specific access restrictions + + GENERIC_MAPPING SamServerMap = {SAM_SERVER_READ, + SAM_SERVER_WRITE, + SAM_SERVER_EXECUTE, + SAM_SERVER_ALL_ACCESS + }; + + GENERIC_MAPPING DomainMap = {DOMAIN_READ, + DOMAIN_WRITE, + DOMAIN_EXECUTE, + DOMAIN_ALL_ACCESS + }; + + GENERIC_MAPPING AliasMap = {ALIAS_READ, + ALIAS_WRITE, + ALIAS_EXECUTE, + ALIAS_ALL_ACCESS + }; + + GENERIC_MAPPING GroupMap = {GROUP_READ, + GROUP_WRITE, + GROUP_EXECUTE, + GROUP_ALL_ACCESS + }; + + GENERIC_MAPPING UserMap = {USER_READ, + USER_WRITE, + USER_EXECUTE, + USER_ALL_ACCESS + }; + + SAMTRACE("InitializeSecurityDescriptors"); + + // + // We need a number of different security descriptors: + // + + // + // + // The following security is assigned to + // + // - Builtin DOMAIN objects + // + // + // Owner: Administrators Alias + // Group: Administrators Alias + // + // Dacl: Grant Grant + // WORLD Administrators + // (Execute | Read) GenericRead | + // GenericExecute | + // DOMAIN_READ_OTHER_PARAMETERS | + // DOMAIN_ADMINISTER_SERVER | + // DOMAIN_CREATE_ALIAS + // + // Sacl: Audit + // Success | Fail + // WORLD + // (Write | Delete | WriteDacl | AccessSystemSecurity) + // + // + // + // + // + // The following security is assigned to + // + // - SAM_SERVER object + // - Account DOMAIN objects + // - The Administrators alias. + // - All groups in the ACCOUNT or BUILTIN domain that are + // made a member of the Administrators alias. + // + // Note: on WinNt systems, the ACLs do not grant DOMAIN_CREATE_GROUP. + // + // + // Owner: Administrators Alias + // Group: Administrators Alias + // + // Dacl: Grant Grant + // WORLD Administrators + // (Execute | Read) GenericAll + // + // Sacl: Audit + // Success | Fail + // WORLD + // (Write | Delete | WriteDacl | AccessSystemSecurity) + // + // + // + // All other aliases and groups must be assigned the following + // security: + // + // Owner: Administrators Alias + // Group: Administrators Alias + // + // Dacl: Grant Grant Grant + // WORLD Administrators AccountOperators Alias + // (Execute | Read) GenericAll GenericAll + // + // Sacl: Audit + // Success | Fail + // WORLD + // (Write | Delete | WriteDacl | AccessSystemSecurity) + // + // + // - All users in the ACCOUNT or BUILTIN domain that are + // made a member of the Administratos alias. This includes + // direct inclusion or indirect inclusion through group + // membership. + // + // + // The following security is assigned to: + // + // - All users in the ACCOUNT or BUILTIN domain that are + // made a member of the Administrators alias. This includes + // direct inclusion or indirect inclusion through group + // membership. + // + // + // Owner: Administrators Alias + // Group: Administrators Alias + // + // Dacl: Grant Grant Grant + // WORLD Administrators User's SID + // (Execute | Read) GenericAll GenericWrite + // + // Sacl: Audit + // Success | Fail + // WORLD + // (Write | Delete | WriteDacl | AccessSystemSecurity) + // + // + // + // + // All other users must be assigned the following + // security: + // + // Owner: AccountOperators Alias + // Group: AccountOperators Alias + // + // Dacl: Grant Grant Grant Grant + // WORLD Administrators Account Operators Alias User's SID + // (Execute | Read) GenericAll GenericAll GenericWrite + // + // Sacl: Audit + // Success | Fail + // WORLD + // (Write | Delete | WriteDacl | AccessSystemSecurity) + // + // except builtin GUEST, who can't change their own account info. + // + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // + // Note, however, that because we are going to cram these ACLs + // directly into the backing store, we must map the generic accesses + // beforehand. + // + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // + + + + + + + // + // Sam Server SD + // + + AceSid[0] = WorldSid; + AceMask[0] = (SAM_SERVER_EXECUTE | SAM_SERVER_READ); + + AceSid[1] = AdminsAliasSid; + AceMask[1] = (SAM_SERVER_ALL_ACCESS); + + + Status = SampBuildNewProtection( + 2, // AceCount + &AceSid[0], // AceSid array + &AceMask[0], // Ace Mask array + &SamServerMap, // GenericMap + FALSE, // Not user object + &SampProtection[SAMP_PROT_SAM_SERVER] // Result + ); + ASSERT(NT_SUCCESS(Status)); + + // + // Builtin Domain SD + // + + AceSid[0] = WorldSid; + AceMask[0] = (DOMAIN_EXECUTE | DOMAIN_READ); + + AceSid[1] = AdminsAliasSid; + AceMask[1] = (DOMAIN_EXECUTE | DOMAIN_READ | + DOMAIN_READ_OTHER_PARAMETERS | + DOMAIN_ADMINISTER_SERVER | + DOMAIN_CREATE_ALIAS); + + + Status = SampBuildNewProtection( + 2, // AceCount + &AceSid[0], // AceSid array + &AceMask[0], // Ace Mask array + &DomainMap, // GenericMap + FALSE, // Not user object + &SampProtection[SAMP_PROT_BUILTIN_DOMAIN] // Result + ); + ASSERT(NT_SUCCESS(Status)); + + // + // Account Domain SD + // + + if (SampProductType == NtProductLanManNt) { + NotForThisProductType = 0; + } else { + NotForThisProductType = DOMAIN_CREATE_GROUP; + } + + AceSid[0] = WorldSid; + AceMask[0] = (DOMAIN_EXECUTE | DOMAIN_READ) & ~NotForThisProductType; + + AceSid[1] = UsersAliasSid; + AceMask[1] = (DOMAIN_EXECUTE | DOMAIN_READ | DOMAIN_CREATE_ALIAS) + & ~NotForThisProductType; + + AceSid[2] = AdminsAliasSid; + AceMask[2] = (DOMAIN_ALL_ACCESS) & ~NotForThisProductType; + + AceSid[3] = PowerUsersAliasSid; + AceMask[3] = (DOMAIN_EXECUTE | DOMAIN_READ | DOMAIN_CREATE_USER | + DOMAIN_CREATE_ALIAS) + & ~NotForThisProductType; + + AceSid[4] = AccountAliasSid; + AceMask[4] = (DOMAIN_EXECUTE | DOMAIN_READ | DOMAIN_CREATE_USER | + DOMAIN_CREATE_GROUP | + DOMAIN_CREATE_ALIAS) + & ~NotForThisProductType; + + + Status = SampBuildNewProtection( + 5, // AceCount + &AceSid[0], // AceSid array + &AceMask[0], // Ace Mask array + &DomainMap, // GenericMap + FALSE, // Not user object + &SampProtection[SAMP_PROT_ACCOUNT_DOMAIN] // Result + ); + ASSERT(NT_SUCCESS(Status)); + + + + // + // Admin Alias SD + // + + AceSid[0] = WorldSid; + AceMask[0] = (ALIAS_EXECUTE | ALIAS_READ); + + AceSid[1] = AdminsAliasSid; + AceMask[1] = (ALIAS_ALL_ACCESS); + + + Status = SampBuildNewProtection( + 2, // AceCount + &AceSid[0], // AceSid array + &AceMask[0], // Ace Mask array + &AliasMap, // GenericMap + FALSE, // Not user object + &SampProtection[SAMP_PROT_ADMIN_ALIAS] // Result + ); + ASSERT(NT_SUCCESS(Status)); + + + + // + // Normal Alias SD + // + + AceSid[0] = WorldSid; + AceMask[0] = (ALIAS_EXECUTE | ALIAS_READ); + + AceSid[1] = AdminsAliasSid; + AceMask[1] = (ALIAS_ALL_ACCESS); + + AceSid[2] = AccountAliasSid; + AceMask[2] = (ALIAS_ALL_ACCESS); + + + Status = SampBuildNewProtection( + 3, // AceCount + &AceSid[0], // AceSid array + &AceMask[0], // Ace Mask array + &AliasMap, // GenericMap + FALSE, // Not user object + &SampProtection[SAMP_PROT_NORMAL_ALIAS] // Result + ); + ASSERT(NT_SUCCESS(Status)); + + + + + // + // Power User accessible Alias SD + // + + AceSid[0] = WorldSid; + AceMask[0] = (ALIAS_EXECUTE | ALIAS_READ); + + AceSid[1] = AdminsAliasSid; + AceMask[1] = (ALIAS_ALL_ACCESS); + + AceSid[2] = AccountAliasSid; + AceMask[2] = (ALIAS_ALL_ACCESS); + + AceSid[3] = PowerUsersAliasSid; + AceMask[3] = (ALIAS_ALL_ACCESS); + + + Status = SampBuildNewProtection( + 4, // AceCount + &AceSid[0], // AceSid array + &AceMask[0], // Ace Mask array + &AliasMap, // GenericMap + FALSE, // Not user object + &SampProtection[SAMP_PROT_PWRUSER_ACCESSIBLE_ALIAS] // Result + ); + ASSERT(NT_SUCCESS(Status)); + + + + + // + // Admin Group SD + // + + AceSid[0] = WorldSid; + AceMask[0] = (GROUP_EXECUTE | GROUP_READ); + + AceSid[1] = AdminsAliasSid; + AceMask[1] = (GROUP_ALL_ACCESS); + + + Status = SampBuildNewProtection( + 2, // AceCount + &AceSid[0], // AceSid array + &AceMask[0], // Ace Mask array + &GroupMap, // GenericMap + FALSE, // Not user object + &SampProtection[SAMP_PROT_ADMIN_GROUP] // Result + ); + ASSERT(NT_SUCCESS(Status)); + + + + // + // Normal GROUP SD + // + + AceSid[0] = WorldSid; + AceMask[0] = (GROUP_EXECUTE | GROUP_READ); + + AceSid[1] = AdminsAliasSid; + AceMask[1] = (GROUP_ALL_ACCESS); + + AceSid[2] = AccountAliasSid; + AceMask[2] = (GROUP_ALL_ACCESS); + + + Status = SampBuildNewProtection( + 3, // AceCount + &AceSid[0], // AceSid array + &AceMask[0], // Ace Mask array + &GroupMap, // GenericMap + FALSE, // Not user object + &SampProtection[SAMP_PROT_NORMAL_GROUP] // Result + ); + ASSERT(NT_SUCCESS(Status)); + + + + // + // Admin User SD + // + + AceSid[0] = WorldSid; + AceMask[0] = (USER_EXECUTE | USER_READ); + + AceSid[1] = AdminsAliasSid; + AceMask[1] = (USER_ALL_ACCESS); + + AceSid[2] = AnySidInAccountDomain; + AceMask[2] = (USER_WRITE); + + + Status = SampBuildNewProtection( + 3, // AceCount + &AceSid[0], // AceSid array + &AceMask[0], // Ace Mask array + &UserMap, // GenericMap + TRUE, // user object (rid replacement) + &SampProtection[SAMP_PROT_ADMIN_USER] // Result + ); + ASSERT(NT_SUCCESS(Status)); + + + // + // Normal User SD + // + + AceSid[0] = WorldSid; + AceMask[0] = (USER_EXECUTE | USER_READ); + + AceSid[1] = AdminsAliasSid; + AceMask[1] = (USER_ALL_ACCESS); + + AceSid[2] = AccountAliasSid; + AceMask[2] = (USER_ALL_ACCESS); + + AceSid[3] = AnySidInAccountDomain; + AceMask[3] = (USER_WRITE); + + + Status = SampBuildNewProtection( + 4, // AceCount + &AceSid[0], // AceSid array + &AceMask[0], // Ace Mask array + &UserMap, // GenericMap + TRUE, // user object (rid replacement) + &SampProtection[SAMP_PROT_NORMAL_USER] // Result + ); + ASSERT(NT_SUCCESS(Status)); + + + + // + // Builtin Guest Account SD + // Can't change own password or other setable fields + // + + AceSid[0] = WorldSid; + AceMask[0] = (USER_READ | USER_EXECUTE & ~(USER_CHANGE_PASSWORD)); + + AceSid[1] = AdminsAliasSid; + AceMask[1] = (USER_ALL_ACCESS); + + AceSid[2] = AccountAliasSid; + AceMask[2] = (USER_ALL_ACCESS); + + + + + Status = SampBuildNewProtection( + 3, // AceCount + &AceSid[0], // AceSid array + &AceMask[0], // Ace Mask array + &UserMap, // GenericMap + FALSE, // no rid replacement + &SampProtection[SAMP_PROT_GUEST_ACCOUNT] // Result + ); + ASSERT(NT_SUCCESS(Status)); + + + + return(TRUE); + +} + + +NTSTATUS +SampBuildNewProtection( + IN ULONG AceCount, + IN PSID *AceSid, + IN ACCESS_MASK *AceMask, + IN PGENERIC_MAPPING GenericMap, + IN BOOLEAN UserObject, + OUT PSAMP_PROTECTION Result + ) + +/*++ + + +Routine Description: + + This routine builds a self-relative security descriptor ready + to be applied to one of the SAM objects. + + If so indicated, a pointer to the last RID of the SID in the last + ACE of the DACL is returned and a flag set indicating that the RID + must be replaced before the security descriptor is applied to an object. + This is to support USER object protection, which must grant some + access to the user represented by the object. + + The owner and group of each security descriptor will be set + to: + + Owner: Administrators Alias + Group: Administrators Alias + + + The SACL of each of these objects will be set to: + + + Audit + Success | Fail + WORLD + (Write | Delete | WriteDacl | AccessSystemSecurity) & !ReadControl + + + +Arguments: + + AceCount - The number of ACEs to be included in the DACL. + + AceSid - Points to an array of SIDs to be granted access by the DACL. + If the target SAM object is a User object, then the last entry + in this array is expected to be the SID of an account within the + domain with the last RID not yet set. The RID will be set during + actual account creation. + + AceMask - Points to an array of accesses to be granted by the DACL. + The n'th entry of this array corresponds to the n'th entry of + the AceSid array. These masks should not include any generic + access types. + + GenericMap - Points to a generic mapping for the target object type. + + + UserObject - Indicates whether the target SAM object is a User object + or not. If TRUE (it is a User object), then the resultant + protection will be set up indicating Rid replacement is necessary. + + Result - Receives a pointer to the resultant protection information. + All access masks in ACLs in the result are mapped to standard and + specific accesses. + + +Return Value: + + TBS. + +--*/ +{ + + + + SECURITY_DESCRIPTOR Absolute; + PSECURITY_DESCRIPTOR Relative; + PACL TmpAcl; + PACCESS_ALLOWED_ACE TmpAce; + PSID TmpSid; + ULONG Length, i; + PULONG RidLocation; + BOOLEAN IgnoreBoolean; + ACCESS_MASK MappedMask; + + SAMTRACE("SampBuildNewProtection"); + + // + // The approach is to set up an absolute security descriptor that + // looks like what we want and then copy it to make a self-relative + // security descriptor. + // + + + Status = RtlCreateSecurityDescriptor( + &Absolute, + SECURITY_DESCRIPTOR_REVISION1 + ); + ASSERT( NT_SUCCESS(Status) ); + + + + // + // Owner + // + + Status = RtlSetOwnerSecurityDescriptor (&Absolute, AdminsAliasSid, FALSE ); + ASSERT(NT_SUCCESS(Status)); + + + + // + // Group + // + + Status = RtlSetGroupSecurityDescriptor (&Absolute, AdminsAliasSid, FALSE ); + ASSERT(NT_SUCCESS(Status)); + + + + + // + // Discretionary ACL + // + // Calculate its length, + // Allocate it, + // Initialize it, + // Add each ACE + // Add it to the security descriptor + // + + Length = (ULONG)sizeof(ACL); + for (i=0; i<AceCount; i++) { + + Length += RtlLengthSid( AceSid[i] ) + + (ULONG)sizeof(ACCESS_ALLOWED_ACE) - + (ULONG)sizeof(ULONG); //Subtract out SidStart field length + } + + TmpAcl = RtlAllocateHeap( RtlProcessHeap(), 0, Length ); + ASSERT(TmpAcl != NULL); + + + Status = RtlCreateAcl( TmpAcl, Length, ACL_REVISION2); + ASSERT( NT_SUCCESS(Status) ); + + for (i=0; i<AceCount; i++) { + MappedMask = AceMask[i]; + RtlMapGenericMask( &MappedMask, GenericMap ); + Status = RtlAddAccessAllowedAce ( + TmpAcl, + ACL_REVISION2, + MappedMask, + AceSid[i] + ); + ASSERT( NT_SUCCESS(Status) ); + } + + Status = RtlSetDaclSecurityDescriptor (&Absolute, TRUE, TmpAcl, FALSE ); + ASSERT(NT_SUCCESS(Status)); + + + + + // + // Sacl + // + + + Length = (ULONG)sizeof(ACL) + + RtlLengthSid( WorldSid ) + + (ULONG)sizeof(SYSTEM_AUDIT_ACE) - + (ULONG)sizeof(ULONG); //Subtract out SidStart field length + TmpAcl = RtlAllocateHeap( RtlProcessHeap(), 0, Length ); + ASSERT(TmpAcl != NULL); + + Status = RtlCreateAcl( TmpAcl, Length, ACL_REVISION2); + ASSERT( NT_SUCCESS(Status) ); + + Status = RtlAddAuditAccessAce ( + TmpAcl, + ACL_REVISION2, + (GenericMap->GenericWrite | DELETE | WRITE_DAC | ACCESS_SYSTEM_SECURITY) & ~READ_CONTROL, + WorldSid, + TRUE, //AuditSuccess, + TRUE //AuditFailure + ); + ASSERT( NT_SUCCESS(Status) ); + + Status = RtlSetSaclSecurityDescriptor (&Absolute, TRUE, TmpAcl, FALSE ); + ASSERT(NT_SUCCESS(Status)); + + + + + + + // + // Convert the Security Descriptor to Self-Relative + // + // Get the length needed + // Allocate that much memory + // Copy it + // Free the generated absolute ACLs + // + + Length = 0; + Status = RtlAbsoluteToSelfRelativeSD( &Absolute, NULL, &Length ); + ASSERT(Status == STATUS_BUFFER_TOO_SMALL); + + Relative = RtlAllocateHeap( RtlProcessHeap(), 0, Length ); + ASSERT(Relative != NULL); + Status = RtlAbsoluteToSelfRelativeSD(&Absolute, Relative, &Length ); + ASSERT(NT_SUCCESS(Status)); + + + RtlFreeHeap( RtlProcessHeap(), 0, Absolute.Dacl ); + RtlFreeHeap( RtlProcessHeap(), 0, Absolute.Sacl ); + + + + + // + // If the object is a user object, then get the address of the + // last RID of the SID in the last ACE in the DACL. + // + + if (UserObject == TRUE) { + + Status = RtlGetDaclSecurityDescriptor( + Relative, + &IgnoreBoolean, + &TmpAcl, + &IgnoreBoolean + ); + ASSERT(NT_SUCCESS(Status)); + Status = RtlGetAce ( TmpAcl, AceCount-1, (PVOID *)&TmpAce ); + ASSERT(NT_SUCCESS(Status)); + TmpSid = (PSID)(&TmpAce->SidStart), + + RidLocation = RtlSubAuthoritySid( + TmpSid, + (ULONG)(*RtlSubAuthorityCountSid( TmpSid ) - 1) + ); + } + + + + + + + + // + // Set the result information + // + + Result->Length = Length; + Result->Descriptor = Relative; + Result->RidToReplace = RidLocation; + Result->RidReplacementRequired = UserObject; + + + + return(Status); + +} + +VOID +SampGetDomainPolicy( + ) +/*++ + + +Routine Description: + + This routine builds the name strings for domains. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + ULONG Size; + PPOLICY_ACCOUNT_DOMAIN_INFO PolicyAccountDomainInfo; + + SAMTRACE("SampGetDomainPolicy"); + + // + // Builtin domain - Well-known External Name and Sid + // - Internal Name matches External Name + + RtlInitUnicodeString( &BuiltinInternalDomainNameU, L"Builtin"); + FullBuiltinInternalDomainNameU.Buffer = RtlAllocateHeap(RtlProcessHeap(), 0, 256); + FullBuiltinInternalDomainNameU.Length = 0; + FullBuiltinInternalDomainNameU.MaximumLength = 256; + RtlCopyUnicodeString( &FullBuiltinInternalDomainNameU, &DomainNamePrefixU ); + Status = RtlAppendUnicodeToString( &FullBuiltinInternalDomainNameU, L"\\" ); + RtlAppendUnicodeStringToString( &FullBuiltinInternalDomainNameU, &BuiltinInternalDomainNameU ); + + BuiltinExternalDomainNameU = BuiltinInternalDomainNameU; + + SampBuiltinDomainSid = RtlAllocateHeap(RtlProcessHeap(), 0,RtlLengthRequiredSid( 1 )); + ASSERT( SampBuiltinDomainSid != NULL ); + RtlInitializeSid( SampBuiltinDomainSid, &BuiltinAuthority, 1 ); + *(RtlSubAuthoritySid( SampBuiltinDomainSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID; + + // + // Account domain - Configurable External Name and Sid. + // + // The External Name and Sid are obtained from the + // Lsa Policy Object (PolicyAccountDomainInformation + // information class). For a DC, the External Name + // is the Domain Name and for a Wksta, the External + // Name is the Computer Name as at the time of the + // system load. + // + // For DC's the Internal Name is the Domain Name + // - For Wksta's the Internal Name is the constant name + // "Account". + // + // NOTE: The reason for these choices of Internal Name + // is to avoid having to change the SAM Database. + // + + Status = SampGetAccountDomainInfo( &PolicyAccountDomainInfo ); + + if (!NT_SUCCESS(Status)) { + + KdPrint(( "BLDSAM3: Couldn't retrieve policy information from LSA.\n" + " Status = 0x%lx\n", Status)); + return; + } + + SampAccountDomainSid = PolicyAccountDomainInfo->DomainSid; + + AccountExternalDomainNameU = PolicyAccountDomainInfo->DomainName; + + RtlInitUnicodeString( &AccountInternalDomainNameU, L"Account"); + + FullAccountInternalDomainNameU.Buffer = RtlAllocateHeap(RtlProcessHeap(), 0, 256); + FullAccountInternalDomainNameU.Length = 0; + FullAccountInternalDomainNameU.MaximumLength = SAMP_MAXIMUM_INTERNAL_NAME_LENGTH; + RtlCopyUnicodeString( &FullAccountInternalDomainNameU, &DomainNamePrefixU ); + Status = RtlAppendUnicodeToString( &FullAccountInternalDomainNameU, L"\\" ); + RtlAppendUnicodeStringToString( &FullAccountInternalDomainNameU, &AccountInternalDomainNameU ); + + // + // Now initialize a SID that can be used to represent accounts + // in this domain. Same as SampAccountDomainSid except with one + // extra sub-authority. It doesn't matter what the value of the + // last RID is because it is always replaced before use. + // + + Size = RtlLengthSid( SampAccountDomainSid ) + sizeof(ULONG); + AnySidInAccountDomain = RtlAllocateHeap( RtlProcessHeap(), 0, Size); + ASSERT( AnySidInAccountDomain != NULL ); + Status = RtlCopySid( Size, AnySidInAccountDomain, SampAccountDomainSid ); + ASSERT(NT_SUCCESS(Status)); + (*RtlSubAuthorityCountSid( AnySidInAccountDomain )) += 1; + + + // + // Set builtin as "current" domain + // + + SetCurrentDomain( DomainBuiltin ); + + return; +} + + +VOID +SetCurrentDomain( + IN SAMP_DOMAIN_SELECTOR Domain + ) +/*++ + + +Routine Description: + + This routine sets the current domain to be + either the account or builtin domain. + +Arguments: + + Domain - Specifies either builtin or account domain. + (DomainBuiltin or DomainAccount). + + +Return Value: + + None. + +--*/ +{ + + SAMTRACE("SetCurrentDomain"); + + + if (Domain == DomainBuiltin) { + + DomainNameU = &BuiltinInternalDomainNameU; + FullDomainNameU = &FullBuiltinInternalDomainNameU; + DomainSid = SampBuiltinDomainSid; + + } else { + + DomainNameU = &AccountInternalDomainNameU; + FullDomainNameU = &FullAccountInternalDomainNameU; + DomainSid = SampAccountDomainSid; + + } + + + + return; +} + +NTSTATUS +InitializeSam( + ) + +/*++ + +Routine Description: + + This routine initializes the SAM-level registry information. + It does not initialize any domains in the SAM. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + PSAMP_V1_FIXED_LENGTH_SERVER ServerFixedAttributes; + PSAMP_VARIABLE_LENGTH_ATTRIBUTE ServerVariableAttributeArray; + PVOID ServerVariableData; + OBJECT_ATTRIBUTES SamAttributes; + UNICODE_STRING SamNameU; + ULONG Disposition; + ULONG ServerAttributeLength; + SECURITY_DESCRIPTOR SecurityDescriptor; + BOOLEAN IgnoreBoolean; + PACL Dacl; + + SAMTRACE("InitializeSam"); + + // + // Build a system default Dacl to protect the SAM database + // with. + // + + Status = SampCreateDatabaseProtection( &SecurityDescriptor ); + if (!NT_SUCCESS(Status)) { + return(Status); + } + + // + // See if a remnant of a SAM database already exists + // + + RtlInitUnicodeString( &SamNameU, L"SAM" ); + + InitializeObjectAttributes( + &SamAttributes, + &SamNameU, + OBJ_CASE_INSENSITIVE, + SamParentKey, + &SecurityDescriptor + ); + Status = RtlpNtCreateKey( + &SamKey, + (KEY_READ | KEY_CREATE_SUB_KEY | KEY_WRITE), + &SamAttributes, + 0, + NULL, + &Disposition + ); + + Status = RtlGetDaclSecurityDescriptor( + &SecurityDescriptor, + &IgnoreBoolean, + &Dacl, + &IgnoreBoolean + ); + + if (Dacl != NULL) { + RtlFreeHeap( RtlProcessHeap(), 0, Dacl ); + } + ASSERT(SecurityDescriptor.Sacl == NULL); + ASSERT(SecurityDescriptor.Owner == NULL); + ASSERT(SecurityDescriptor.Group == NULL); + + if ( !NT_SUCCESS(Status) ) { + +#if DBG + BldPrint( "\n" ); + BldPrint( "\n" ); + BldPrint( " We seem to be having trouble creating the registry\n" ); + BldPrint( " database key in which the Security Account Manager\n" ); + BldPrint( " information resides. Please see one of the security\n" ); + BldPrint( " group developers for assistance in analyzing the\n" ); + BldPrint( " the problem.\n" ); + BldPrint( "\n" ); + BldPrint( "\n" ); +#endif + + return(Status); + } + + if ( Disposition != REG_CREATED_NEW_KEY ) { + +#if DBG + BldPrint( "\n" ); + BldPrint( "\n" ); + BldPrint( " I'm terribly sorry, but you have specified that a SAM\n" ); + BldPrint( " database be initialized and yet there is already a SAM\n" ); + BldPrint( " database in existance. If the SAM database is corrupt\n" ); + BldPrint( " or you would like to replace the existing domain anyway,\n" ); + BldPrint( " please delnode the existing database and re-issue this \n"); + BldPrint( " command. \n"); + BldPrint( " The SAM database is in ...\\registry\\Machine\\security\\sam.\n" ); + BldPrint( " Thank you.\n" ); + BldPrint( "\n" ); + BldPrint( "\n" ); +#endif + + Usage(); + + Status = NtClose( SamKey ); + + + return(Status); + } + + + // + // Initialize the registry transaction structure for SAM. + // + + Status = RtlInitializeRXact( SamKey, FALSE, &SamRXactContext ); + + if ( Status != STATUS_RXACT_STATE_CREATED ) { +#if DBG + BldPrint("\n"); + BldPrint(" The SAM database already has a structure in place.\n"); + BldPrint(" This indicates multiple initializations being performed\n"); + BldPrint(" simultaneously. Please be sure no other initializations\n"); + BldPrint(" are being performed and issue this command again.\n"); + BldPrint("\n"); + BldPrint("\n"); +#endif + + if ( Status == STATUS_SUCCESS ) { + + // + // Shouldn't happen, but let's program defensively. + // + + Status = STATUS_RXACT_INVALID_STATE; + } + + return(Status); + } + + // + // Start an RXACT to do the rest in ... + // + + Status = RtlStartRXact( SamRXactContext ); + SUCCESS_ASSERT(Status, " Starting transaction\n"); + + // + // Set the server's fixed and variable attributes + // + + ServerAttributeLength = sizeof( SAMP_V1_FIXED_LENGTH_SERVER ) + + ( SAMP_SERVER_VARIABLE_ATTRIBUTES * + sizeof( SAMP_VARIABLE_LENGTH_ATTRIBUTE ) ) + + SampProtection[SAMP_PROT_SAM_SERVER].Length; + + ServerFixedAttributes = (PSAMP_V1_FIXED_LENGTH_SERVER)RtlAllocateHeap( + RtlProcessHeap(), 0, + ServerAttributeLength + ); + + if ( ServerFixedAttributes == NULL ) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + } + + SUCCESS_ASSERT(Status, " Failed to create server attributes\n"); + + // + // The server revision on the a new SAM database may not be the same + // as the revision on the rest of SAM. This allows the server revision + // to indicate which bugs have been fixed in this SAM. + // + + ServerFixedAttributes->RevisionLevel = SAMP_SERVER_REVISION; + + ServerVariableAttributeArray = (PSAMP_VARIABLE_LENGTH_ATTRIBUTE) + ((PUCHAR)(ServerFixedAttributes) + + sizeof( SAMP_V1_FIXED_LENGTH_SERVER ) ); + + ServerVariableAttributeArray->Offset = 0; + ServerVariableAttributeArray->Length = + SampProtection[SAMP_PROT_SAM_SERVER].Length; + ServerVariableAttributeArray->Qualifier = SAMP_REVISION; + + ServerVariableData = (PVOID)( (PUCHAR)(ServerVariableAttributeArray) + + sizeof( SAMP_VARIABLE_LENGTH_ATTRIBUTE ) ); + + RtlCopyMemory( + ServerVariableData, + SampProtection[SAMP_PROT_SAM_SERVER].Descriptor, + SampProtection[SAMP_PROT_SAM_SERVER].Length + ); + + // + // Now write out the attributes via the RXACT. + // + + RtlInitUnicodeString( &SamNameU, NULL ); + + SampDumpRXact(SampRXactContext, + RtlRXactOperationSetValue, + &SamNameU, + INVALID_HANDLE_VALUE, + &SampCombinedAttributeName, + REG_BINARY, + (PVOID)ServerFixedAttributes, + ServerAttributeLength, + FIXED_LENGTH_SERVER_FLAG); + + Status = RtlAddAttributeActionToRXact( + SamRXactContext, + RtlRXactOperationSetValue, + &SamNameU, + INVALID_HANDLE_VALUE, + &SampCombinedAttributeName, + REG_BINARY, + (PVOID)ServerFixedAttributes, + ServerAttributeLength + ); + + SUCCESS_ASSERT(Status, " Failed to write out server attributes\n" ); + + RtlFreeHeap( RtlProcessHeap(), 0, ServerFixedAttributes ); + + // + // Create SAM\Domains + // + + Status = RtlAddActionToRXact( + SamRXactContext, + RtlRXactOperationSetValue, + &DomainNamePrefixU, + 0, + NULL, + 0 + ); + + SUCCESS_ASSERT(Status, " Failed to add domain key to log\n"); + + Status = RtlApplyRXactNoFlush( SamRXactContext ); + SUCCESS_ASSERT(Status, " Committing SAM INIT transaction\n"); + + return(Status); +} + + +NTSTATUS +SampCreateDatabaseProtection( + PISECURITY_DESCRIPTOR Sd + ) + + +/*++ + +Routine Description: + + This function allocates and initializes protection to assign to + the SAM database. + + Upon return, any non-zero pointers in the security descriptors + point to memory allocated from process heap. It is the caller's + responsibility to free this memory. + + + Protection is: + + System: All Access + Admin: ReadControl | WriteDac + +Arguments: + + Sd - Address of a security descriptor to initialize. + +Return Value: + + STATUS_SUCCESS - The Security descriptor has been initialize. + + STATUS_NO_MEMORY - couldn't allocate memory for the protection info. + +--*/ + + +{ + NTSTATUS + Status; + + ULONG + Length; + + USHORT + i; + + PACL + Dacl; + + PACE_HEADER + Ace; + + SAMTRACE("SampCreateDatabaseProtection"); + + + // + // Initialize the security descriptor. + // This call should not fail. + // + + Status = RtlCreateSecurityDescriptor( Sd, SECURITY_DESCRIPTOR_REVISION1 ); + ASSERT(NT_SUCCESS(Status)); + + Length = (ULONG)sizeof(ACL) + + (2*((ULONG)sizeof(ACCESS_ALLOWED_ACE))) + + RtlLengthSid( LocalSystemSid ) + + RtlLengthSid( AdminsAliasSid ) + + 8; // The 8 is just for good measure + + + Dacl = RtlAllocateHeap( RtlProcessHeap(), 0, Length ); + + if (Dacl == NULL) { + return(STATUS_NO_MEMORY); + } + + + Status = RtlCreateAcl (Dacl, Length, ACL_REVISION2 ); + ASSERT(NT_SUCCESS(Status)); + + // + // Add ACEs to the ACL... + // These calls should not be able to fail. + // + + Status = RtlAddAccessAllowedAce( + Dacl, + ACL_REVISION2, + (GENERIC_ALL ), + LocalSystemSid + ); + ASSERT(NT_SUCCESS(Status)); + + Status = RtlAddAccessAllowedAce( + Dacl, + ACL_REVISION2, +#ifdef USER_MODE_SAM + (GENERIC_ALL), +#else + (READ_CONTROL | WRITE_DAC), +#endif + AdminsAliasSid + ); + ASSERT(NT_SUCCESS(Status)); + + + // + // Now mark the ACEs as inheritable... + // + + for ( i=0; i<Dacl->AceCount; i++) { + + // + // Get the address of the next ACE + // (Shouldn't fail) + // + + Status = RtlGetAce( Dacl, (ULONG)i, &Ace ); + ASSERT(NT_SUCCESS(Status)); + + Ace->AceFlags |= (CONTAINER_INHERIT_ACE); + + } + + + // + // And add the ACL to the security descriptor. + // This call should not fail. + // + + Status = RtlSetDaclSecurityDescriptor( + Sd, + TRUE, // DaclPresent + Dacl, // Dacl OPTIONAL + FALSE // DaclDefaulted OPTIONAL + ); + ASSERT(NT_SUCCESS(Status)); + + + + return(STATUS_SUCCESS); + +} + + +NTSTATUS +CreateBuiltinDomain ( + ) + +/*++ + +Routine Description: + + This routine creates a new builtin domain. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + NTSTATUS Status; + UNICODE_STRING Name, Comment; + HMODULE AccountNamesResource; + + SAMTRACE("CreateBuiltinDomain"); + + // + // Get the message resource we need to get the account names from + // + + AccountNamesResource = (HMODULE) LoadLibrary( L"SAMSRV.DLL" ); + if (AccountNamesResource == NULL) { + return(STATUS_RESOURCE_DATA_NOT_FOUND); + } + + + + + // + // Prep the standard domain registry structure for this domain + // + + Status = PrepDomain(DomainBuiltin); + + if (!NT_SUCCESS(Status)) { + + return(Status); + } + + // + // Create the alias accounts with no members + // (Common to LanManNT and WinNT products) + // + + + Status = SampGetMessageStrings( + AccountNamesResource, + SAMP_ALIAS_NAME_ADMINS, + &Name, + SAMP_ALIAS_COMMENT_ADMINS, + &Comment + ); ASSERT(NT_SUCCESS(Status)); + + Status = CreateAlias(&Name, // AccountName + &Comment, // AccountComment + TRUE, // SpecialAccount + DOMAIN_ALIAS_RID_ADMINS, // Rid + SAMP_PROT_ADMIN_ALIAS // Protection + ); ASSERT(NT_SUCCESS(Status)); + LocalFree( Name.Buffer ); + LocalFree( Comment.Buffer ); + + Status = SampGetMessageStrings( + AccountNamesResource, + SAMP_ALIAS_NAME_USERS, + &Name, + SAMP_ALIAS_COMMENT_USERS, + &Comment + ); ASSERT(NT_SUCCESS(Status)); + + Status = CreateAlias(&Name, // AccountName + &Comment, // AccountComment + TRUE, // SpecialAccount + DOMAIN_ALIAS_RID_USERS, // Rid + SAMP_PROT_PWRUSER_ACCESSIBLE_ALIAS // Protection + ); ASSERT(NT_SUCCESS(Status)); + + LocalFree( Name.Buffer ); + LocalFree( Comment.Buffer ); + + Status = SampGetMessageStrings( + AccountNamesResource, + SAMP_ALIAS_NAME_GUESTS, + &Name, + SAMP_ALIAS_COMMENT_GUESTS, + &Comment + ); ASSERT(NT_SUCCESS(Status)); + + Status = CreateAlias(&Name, // AccountName + &Comment, // AccountComment + TRUE, // SpecialAccount + DOMAIN_ALIAS_RID_GUESTS, // Rid + SAMP_PROT_PWRUSER_ACCESSIBLE_ALIAS // Protection + ); ASSERT(NT_SUCCESS(Status)); + + LocalFree( Name.Buffer ); + LocalFree( Comment.Buffer ); + + Status = SampGetMessageStrings( + AccountNamesResource, + SAMP_ALIAS_NAME_BACKUP_OPS, + &Name, + SAMP_ALIAS_COMMENT_BACKUP_OPS, + &Comment + ); ASSERT(NT_SUCCESS(Status)); + + Status = CreateAlias(&Name, // AccountName + &Comment, // AccountComment + TRUE, // SpecialAccount + DOMAIN_ALIAS_RID_BACKUP_OPS, // Rid + SAMP_PROT_ADMIN_ALIAS // Protection + ); ASSERT(NT_SUCCESS(Status)); + + LocalFree( Name.Buffer ); + LocalFree( Comment.Buffer ); + + Status = SampGetMessageStrings( + AccountNamesResource, + SAMP_ALIAS_NAME_REPLICATOR, + &Name, + SAMP_ALIAS_COMMENT_REPLICATOR, + &Comment + ); ASSERT(NT_SUCCESS(Status)); + + Status = CreateAlias(&Name, // AccountName + &Comment, // AccountComment + TRUE, // SpecialAccount + DOMAIN_ALIAS_RID_REPLICATOR, // Rid + SAMP_PROT_NORMAL_ALIAS // Protection + ); ASSERT(NT_SUCCESS(Status)); + + LocalFree( Name.Buffer ); + LocalFree( Comment.Buffer ); + + + if (SampProductType == NtProductLanManNt) { + + // + // specific to LanManNT products + // + + Status = SampGetMessageStrings( + AccountNamesResource, + SAMP_ALIAS_NAME_SERVER_OPS, + &Name, + SAMP_ALIAS_COMMENT_SERVER_OPS, + &Comment + ); ASSERT(NT_SUCCESS(Status)); + + Status = CreateAlias(&Name, // AccountName + &Comment, // AccountComment + TRUE, // SpecialAccount + DOMAIN_ALIAS_RID_SYSTEM_OPS, // Rid + SAMP_PROT_ADMIN_ALIAS // Protection + ); ASSERT(NT_SUCCESS(Status)); + + LocalFree( Name.Buffer ); + LocalFree( Comment.Buffer ); + + + Status = SampGetMessageStrings( + AccountNamesResource, + SAMP_ALIAS_NAME_ACCOUNT_OPS, + &Name, + SAMP_ALIAS_COMMENT_ACCOUNT_OPS, + &Comment + ); ASSERT(NT_SUCCESS(Status)); + + Status = CreateAlias(&Name, // AccountName + &Comment, // AccountComment + TRUE, // SpecialAccount + DOMAIN_ALIAS_RID_ACCOUNT_OPS, // Rid + SAMP_PROT_ADMIN_ALIAS // Protection + ); ASSERT(NT_SUCCESS(Status)); + + LocalFree( Name.Buffer ); + LocalFree( Comment.Buffer ); + + + Status = SampGetMessageStrings( + AccountNamesResource, + SAMP_ALIAS_NAME_PRINT_OPS, + &Name, + SAMP_ALIAS_COMMENT_PRINT_OPS, + &Comment + ); ASSERT(NT_SUCCESS(Status)); + + Status = CreateAlias(&Name, // AccountName + &Comment, // AccountComment + TRUE, // SpecialAccount + DOMAIN_ALIAS_RID_PRINT_OPS, // Rid + SAMP_PROT_ADMIN_ALIAS // Protection + ); ASSERT(NT_SUCCESS(Status)); + + LocalFree( Name.Buffer ); + LocalFree( Comment.Buffer ); + + + } else { + + // + // specific to WinNT products + // + + Status = SampGetMessageStrings( + AccountNamesResource, + SAMP_ALIAS_NAME_POWER_USERS, + &Name, + SAMP_ALIAS_COMMENT_POWER_USERS, + &Comment + ); ASSERT(NT_SUCCESS(Status)); + + Status = CreateAlias(&Name, // AccountName + &Comment, // AccountComment + TRUE, // SpecialAccount + DOMAIN_ALIAS_RID_POWER_USERS, // Rid + SAMP_PROT_PWRUSER_ACCESSIBLE_ALIAS // Protection + ); ASSERT(NT_SUCCESS(Status)); + + LocalFree( Name.Buffer ); + LocalFree( Comment.Buffer ); + + + } + + return(Status); +} + + + +NTSTATUS +CreateAccountDomain ( + ) +/*++ + + +Routine Description: + + This routine creates a new account domain using information + from the configuration database and based upon the system's + product type. + + If the product is a WinNt system, then the domain's name is + "Account". If the product is a LanManNT system, then the + domain's name is retrieved from the configuration information. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + + NTSTATUS Status; + UNICODE_STRING Name, Comment; + HMODULE AccountNamesResource; + ULONG AccountControl; + ULONG PrimaryGroup; + + SAMTRACE("CreateAccountDomain"); + + + // + // Get the message resource we need to get the account names from + // + + AccountNamesResource = (HMODULE) LoadLibrary( L"SAMSRV.DLL" ); + if (AccountNamesResource == NULL) { + DbgPrint("BLDSAM3: Error loading library - error is 0x%lx", GetLastError()); + return(STATUS_RESOURCE_DATA_NOT_FOUND); + } + + + + // + // Prep the standard domain registry structure for this domain + // + + Status = PrepDomain(DomainAccount); + + if (!NT_SUCCESS(Status)) { + + return(Status); + } + + // + // Create the group accounts with no members + // + + if ((SampProductType == NtProductWinNt) || + (SampProductType == NtProductServer)) { + + // + // WinNt systems only have one group (called 'None'). + // This group has the same RID as the 'Domain Users' group. + // + + Status = SampGetMessageStrings( + AccountNamesResource, + SAMP_GROUP_NAME_NONE, + &Name, + SAMP_GROUP_COMMENT_NONE, + &Comment + ); ASSERT(NT_SUCCESS(Status)); + + Status = CreateGroup(&Name, // AccountName + &Comment, // AccountComment + TRUE, // SpecialAccount + DOMAIN_GROUP_RID_USERS, // Rid + FALSE // Admin + ); ASSERT(NT_SUCCESS(Status)); + + LocalFree( Name.Buffer ); + LocalFree( Comment.Buffer ); + + } else { + + // + // LanManNT + // + + // + // USERS global group + // + + Status = SampGetMessageStrings( + AccountNamesResource, + SAMP_GROUP_NAME_USERS, + &Name, + SAMP_GROUP_COMMENT_USERS, + &Comment + ); ASSERT(NT_SUCCESS(Status)); + + Status = CreateGroup(&Name, // AccountName + &Comment, // AccountComment + TRUE, // SpecialAccount + DOMAIN_GROUP_RID_USERS, // Rid + FALSE // Admin + ); ASSERT(NT_SUCCESS(Status)); + + LocalFree( Name.Buffer ); + LocalFree( Comment.Buffer ); + + // + // ADMINS global group + // + + Status = SampGetMessageStrings( + AccountNamesResource, + SAMP_GROUP_NAME_ADMINS, + &Name, + SAMP_GROUP_COMMENT_ADMINS, + &Comment + ); ASSERT(NT_SUCCESS(Status)); + + Status = CreateGroup(&Name, // AccountName + &Comment, // AccountComment + TRUE, // SpecialAccount + DOMAIN_GROUP_RID_ADMINS, // Rid + TRUE // Admin + ); ASSERT(NT_SUCCESS(Status)); + + LocalFree( Name.Buffer ); + LocalFree( Comment.Buffer ); + + + // + // GUESTS global group + // + + Status = SampGetMessageStrings( + AccountNamesResource, + SAMP_GROUP_NAME_GUESTS, + &Name, + SAMP_GROUP_COMMENT_GUESTS, + &Comment + ); ASSERT(NT_SUCCESS(Status)); + + Status = CreateGroup(&Name, // AccountName + &Comment, // AccountComment + TRUE, // SpecialAccount + DOMAIN_GROUP_RID_GUESTS, // Rid + FALSE // Admin + ); ASSERT(NT_SUCCESS(Status)); + + LocalFree( Name.Buffer ); + LocalFree( Comment.Buffer ); + + } + + // + // create the user accounts ... + // These are automatically added to the "Domain Users" group + // (except for Guest). + // + + Status = SampGetMessageStrings( + AccountNamesResource, + SAMP_USER_NAME_ADMIN, + &Name, + SAMP_USER_COMMENT_ADMIN, + &Comment + ); ASSERT(NT_SUCCESS(Status)); + + Status = CreateUser( &Name, // AccountName + &Comment, // AccountComment + TRUE, // SpecialAccount + DOMAIN_USER_RID_ADMIN, // UserRid + DOMAIN_GROUP_RID_USERS, // PrimaryGroup + TRUE, // Admin flag + USER_NORMAL_ACCOUNT | + USER_DONT_EXPIRE_PASSWORD, // AccountControl + SAMP_PROT_ADMIN_USER // ProtectionIndex + ); ASSERT(NT_SUCCESS(Status)); + + LocalFree( Name.Buffer ); + LocalFree( Comment.Buffer ); + + + Status = SampGetMessageStrings( + AccountNamesResource, + SAMP_USER_NAME_GUEST, + &Name, + SAMP_USER_COMMENT_GUEST, + &Comment + ); ASSERT(NT_SUCCESS(Status)); + + // + // Disable user account on all NT systems + // + + AccountControl = USER_NORMAL_ACCOUNT | + USER_DONT_EXPIRE_PASSWORD | + USER_ACCOUNT_DISABLED; + + if (SampProductType == NtProductLanManNt) { + + // + // Guest group is in GUESTS global group for LmNT systems. + // + + PrimaryGroup = DOMAIN_GROUP_RID_GUESTS; + + } else { + + // + // There isn't a GUESTS global group on WinNt systems. + // Put the guest in the NONE group (same as USERS group). + // + + PrimaryGroup = DOMAIN_GROUP_RID_USERS; + + } + + + Status = CreateUser( &Name, // AccountName + &Comment, // AccountComment + TRUE, // SpecialAccount + DOMAIN_USER_RID_GUEST, // UserRid + PrimaryGroup, // PrimaryGroup + FALSE, // Admin flag + AccountControl, // AccountControl + SAMP_PROT_GUEST_ACCOUNT // ProtectionIndex + ); ASSERT(NT_SUCCESS(Status)); + + LocalFree( Name.Buffer ); + LocalFree( Comment.Buffer ); + + return(Status); +} + + +NTSTATUS +PrepDomain( + IN SAMP_DOMAIN_SELECTOR Domain + ) + +/*++ + +Routine Description: + + This routine adds the domain level definitions to the operation log. + +Arguments: + + Domain - Indicates which domain is being prep'd + +Return Value: + + TBS + +--*/ + +{ + PSAMP_V1_0A_FIXED_LENGTH_DOMAIN DomainFixedAttributes; + PSAMP_VARIABLE_LENGTH_ATTRIBUTE DomainVariableAttributeArray; + PSAMP_VARIABLE_LENGTH_ATTRIBUTE DomainVariableAttributeArrayStart; + PVOID DomainVariableData; + ULONG DomainAttributeLength; + ULONG ProtectionIndex; + ULONG UserCount, GroupCount, AliasCount; + + SAMTRACE("PrepDomain"); + + // + // Set current domain + // + + SetCurrentDomain( Domain ); + + // + // Select correct protection, and the number of accounts we're going + // to create + // + + if (Domain == DomainBuiltin) { + + ProtectionIndex = SAMP_PROT_BUILTIN_DOMAIN; + + UserCount = 0; + GroupCount = 0; + + if (SampProductType == NtProductLanManNt) { + + // + // Admins, BackupOps, Guests, Replicator, Users, SysOps, + // AcctOps, PrintOps + // + + AliasCount = 8; + + } else { + // + // Admins, BackupOps, Guests, Replicator, Users, Power Users + // + + AliasCount = 6; + + } + + } else { + + ProtectionIndex = SAMP_PROT_ACCOUNT_DOMAIN; + + AliasCount = 0; + UserCount = 2; // Administrator, Guest + + if (SampProductType == NtProductLanManNt) { + + GroupCount = 3; // Users, Administrators, Guests + + } else { + + GroupCount = 1; // "None" + } + } + + // + // Use a transaction. + // + + Status = RtlStartRXact( SamRXactContext ); + SUCCESS_ASSERT(Status, " Starting transaction\n"); + + // + // Create SAM\Domains\(DomainName) (KeyValueType is revision level) + // + + RtlCopyUnicodeString( &KeyNameU, FullDomainNameU ); + + // + // Set the domain's fixed and variable attributes. + // + + DomainFixedAttributes = (PSAMP_V1_0A_FIXED_LENGTH_DOMAIN)RtlAllocateHeap( + RtlProcessHeap(), 0, + sizeof( SAMP_V1_0A_FIXED_LENGTH_DOMAIN ) + ); + + if ( DomainFixedAttributes == NULL ) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + } + + SUCCESS_ASSERT(Status, " Failed to create domain fixed attributes\n"); + + DomainFixedAttributes->Revision = SAMP_REVISION; + DomainFixedAttributes->MinPasswordLength = 0; + DomainFixedAttributes->PasswordHistoryLength = 0; + DomainFixedAttributes->PasswordProperties = 0L; + DomainFixedAttributes->NextRid = 1000; + DomainFixedAttributes->ServerState = DomainServerEnabled; + DomainFixedAttributes->ServerRole = SampServerRole; + NtQuerySystemTime( &(DomainFixedAttributes->CreationTime) ); + DomainFixedAttributes->ModifiedCount = ModifiedCount; + DomainFixedAttributes->MaxPasswordAge = DomainMaxPasswordAge; + DomainFixedAttributes->MinPasswordAge = SampImmediatelyDeltaTime; + DomainFixedAttributes->ForceLogoff = SampNeverDeltaTime; + DomainFixedAttributes->UasCompatibilityRequired = TRUE; + DomainFixedAttributes->LockoutDuration.LowPart = 0xCF1DCC00; // 30 minutes - low part + DomainFixedAttributes->LockoutDuration.HighPart = 0XFFFFFFFB; // 30 minutes - high part + DomainFixedAttributes->LockoutObservationWindow.LowPart = 0xCF1DCC00; // 30 minutes - low part + DomainFixedAttributes->LockoutObservationWindow.HighPart = 0XFFFFFFFB; // 30 minutes - high part + DomainFixedAttributes->LockoutThreshold = 0; // Disabled + DomainFixedAttributes->ModifiedCountAtLastPromotion = ModifiedCount; + + DomainAttributeLength = SampDwordAlignUlong(RtlLengthSid( DomainSid ) ) + + ( SAMP_DOMAIN_VARIABLE_ATTRIBUTES * + sizeof( SAMP_VARIABLE_LENGTH_ATTRIBUTE ) ) + + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length); + + DomainVariableAttributeArrayStart = (PSAMP_VARIABLE_LENGTH_ATTRIBUTE) + RtlAllocateHeap( + RtlProcessHeap(), 0, + DomainAttributeLength + ); + + if ( DomainVariableAttributeArrayStart == NULL ) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + } + + SUCCESS_ASSERT(Status, " Failed to create domain variable attributes\n"); + + DomainVariableAttributeArray = DomainVariableAttributeArrayStart; + + DomainVariableAttributeArray->Offset = 0; + DomainVariableAttributeArray->Length = + SampProtection[ProtectionIndex].Length; + DomainVariableAttributeArray->Qualifier = SAMP_REVISION; + + DomainVariableAttributeArray++; + + DomainVariableAttributeArray->Offset = + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length); + DomainVariableAttributeArray->Length = + RtlLengthSid( DomainSid ); + DomainVariableAttributeArray->Qualifier = 0; + + DomainVariableAttributeArray++; + + DomainVariableAttributeArray->Offset = + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) + + SampDwordAlignUlong(RtlLengthSid( DomainSid )); + DomainVariableAttributeArray->Length = 0; + DomainVariableAttributeArray->Qualifier = 0; + + DomainVariableAttributeArray++; + + DomainVariableAttributeArray->Offset = + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) + + SampDwordAlignUlong(RtlLengthSid( DomainSid )); + DomainVariableAttributeArray->Length = 0; + DomainVariableAttributeArray->Qualifier = 0; + + DomainVariableData = (PVOID)( (PUCHAR)(DomainVariableAttributeArray) + + sizeof( SAMP_VARIABLE_LENGTH_ATTRIBUTE ) ); + + RtlCopyMemory( + DomainVariableData, + SampProtection[ProtectionIndex].Descriptor, + SampProtection[ProtectionIndex].Length + ); + + RtlCopyMemory( + (PVOID)((PUCHAR)(DomainVariableData) + + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length)), + DomainSid, + RtlLengthSid( DomainSid ) + ); + + // + // Now write out the attributes via the RXACT. + // + + SampDumpRXact(SampRXactContext, + RtlRXactOperationSetValue, + &KeyNameU, + INVALID_HANDLE_VALUE, + &SampFixedAttributeName, + REG_BINARY, + (PVOID)DomainFixedAttributes, + sizeof( SAMP_V1_0A_FIXED_LENGTH_DOMAIN ), + FIXED_LENGTH_DOMAIN_FLAG); + + Status = RtlAddAttributeActionToRXact( + SamRXactContext, + RtlRXactOperationSetValue, + &KeyNameU, + INVALID_HANDLE_VALUE, + &SampFixedAttributeName, + REG_BINARY, + (PVOID)DomainFixedAttributes, + sizeof( SAMP_V1_0A_FIXED_LENGTH_DOMAIN ) + ); + + SUCCESS_ASSERT(Status, " Failed to write out domain fixed attributes\n" ); + RtlFreeHeap( RtlProcessHeap(), 0, DomainFixedAttributes ); + + SampDumpRXact(SampRXactContext, + RtlRXactOperationSetValue, + &KeyNameU, + INVALID_HANDLE_VALUE, + &SampVariableAttributeName, + REG_BINARY, + (PUCHAR)DomainVariableAttributeArrayStart, + sizeof( SAMP_V1_0A_FIXED_LENGTH_DOMAIN ), + VARIABLE_LENGTH_ATTRIBUTE_FLAG); + + Status = RtlAddAttributeActionToRXact( + SamRXactContext, + RtlRXactOperationSetValue, + &KeyNameU, + INVALID_HANDLE_VALUE, + &SampVariableAttributeName, + REG_BINARY, + (PVOID)DomainVariableAttributeArrayStart, + DomainAttributeLength + ); + + RtlFreeHeap( RtlProcessHeap(), 0, DomainVariableAttributeArrayStart ); + SUCCESS_ASSERT(Status, " Failed to write out domain variable attributes\n" ); + + // + // Create SAM\Domains\(DomainName)\Users + // + + RtlCopyUnicodeString( &KeyNameU, FullDomainNameU ); + Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Users" ); + SUCCESS_ASSERT(Status, " Failed to append to unicode: \\Users\n" ); + + Status = RtlAddActionToRXact( + SamRXactContext, + RtlRXactOperationSetValue, + &KeyNameU, + UserCount, + NULL, + 0 + ); + + SUCCESS_ASSERT(Status, " Failed to add Users key to log\n"); + + // + // Create SAM\Domains\(DomainName)\Users\Names + // + + RtlCopyUnicodeString( &KeyNameU, FullDomainNameU ); + Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Users\\Names" ); + SUCCESS_ASSERT(Status, " Failed to append to unicode: \\Users\\Names\n" ); + Status = RtlAddActionToRXact( + SamRXactContext, + RtlRXactOperationSetValue, + &KeyNameU, + 0, + NULL, + 0 + ); + SUCCESS_ASSERT(Status, " Failed to add Users/Names key to log\n"); + + // + // Create SAM\Domains\(DomainName)\Groups + // + + RtlCopyUnicodeString( &KeyNameU, FullDomainNameU ); + Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Groups" ); + SUCCESS_ASSERT(Status, " Failed to append Groups key name to unicode\n" ); + Status = RtlAddActionToRXact( + SamRXactContext, + RtlRXactOperationSetValue, + &KeyNameU, + GroupCount, + NULL, + 0 + ); + SUCCESS_ASSERT(Status, " Failed to add Groups key to log\n"); + + // + // Create SAM\Domains\(DomainName)\Groups\Names + // + + RtlCopyUnicodeString( &KeyNameU, FullDomainNameU ); + Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Groups\\Names" ); + SUCCESS_ASSERT(Status, " Failed to append Groups key name to unicode\n" ); + Status = RtlAddActionToRXact( + SamRXactContext, + RtlRXactOperationSetValue, + &KeyNameU, + 0, + NULL, + 0 + ); + SUCCESS_ASSERT(Status, " Failed to add Groups key to log\n"); + + // + // Create SAM\Domains\(DomainName)\Aliases + // + + RtlCopyUnicodeString( &KeyNameU, FullDomainNameU ); + Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Aliases" ); + SUCCESS_ASSERT(Status, " Failed to append Aliases key name to unicode\n" ); + Status = RtlAddActionToRXact( + SamRXactContext, + RtlRXactOperationSetValue, + &KeyNameU, + AliasCount, + NULL, + 0 + ); + SUCCESS_ASSERT(Status, " Failed to add aliases key to log\n"); + + // + // Create SAM\Domains\(DomainName)\Aliases\Names + // + + RtlCopyUnicodeString( &KeyNameU, FullDomainNameU ); + Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Aliases\\Names" ); + SUCCESS_ASSERT(Status, " Failed to append Aliases key name to unicode\n" ); + Status = RtlAddActionToRXact( + SamRXactContext, + RtlRXactOperationSetValue, + &KeyNameU, + 0, + NULL, + 0 + ); + SUCCESS_ASSERT(Status, " Failed to add Aliases\\Names key to log\n"); + + // + // Create SAM\Domains\(DomainName)\Aliases\Members + // + + RtlCopyUnicodeString( &KeyNameU, FullDomainNameU ); + Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Aliases\\Members" ); + SUCCESS_ASSERT(Status, " Failed to append Aliases\\Members key name to unicode\n" ); + Status = RtlAddActionToRXact( + SamRXactContext, + RtlRXactOperationSetValue, + &KeyNameU, + 0, // Domain Count + NULL, + 0 + ); + SUCCESS_ASSERT(Status, " Failed to add Aliases\\Members key to log\n"); + + // + // Commit these additions... + // + + Status = RtlApplyRXactNoFlush( SamRXactContext ); + SUCCESS_ASSERT(Status, " Failed to commit domain initialization.\n"); + + return Status; +} + + +NTSTATUS +CreateAlias( + IN PUNICODE_STRING AccountNameU, + IN PUNICODE_STRING AccountCommentU, + IN BOOLEAN SpecialAccount, + IN ULONG Rid, + IN ULONG ProtectionIndex + ) + +/*++ + +Routine Description: + + This routine adds the keys necessary to create an alias. It also applies + the appropriate protection to the alias. + +Arguments: + + AccountNameU - The Unicode name of the Alias. + + AccountCommentU - A Unicode comment to put in the object's variable data. + + SpecialAccount - A boolean indicating whether or not the account + is special. Special accounts are marked as such and can not + be deleted. + + Rid - The RID of the account. + + + Admin - Indicates whether the account is in the Administrators alias + or not. TRUE means it is, FALSE means it isn't. + +Return Value: + + TBS + +--*/ + +{ + PSAMP_V1_FIXED_LENGTH_ALIAS AliasFixedAttributes; + PSAMP_VARIABLE_LENGTH_ATTRIBUTE AliasVariableAttributeArray; + PVOID AliasVariableData; + PSID Sid1, Sid2; + PSID AliasMembers = NULL; + ULONG MemberCount, TotalLength, AliasAttributeLength; + UNICODE_STRING AliasNameU, AliasCommentU; + + SAMTRACE("CreateAlias"); + + AliasNameU = *AccountNameU; + AliasCommentU = *AccountCommentU; + + // + // Set the account specific RID in the DACL's if necessary + // + + if ( SampProtection[ProtectionIndex].RidReplacementRequired == TRUE ) { + + (*SampProtection[ProtectionIndex].RidToReplace) = Rid; + } + + // + // Use a transaction. + // + + Status = RtlStartRXact( SamRXactContext ); + SUCCESS_ASSERT(Status, " Failed to start Alias addition transaction\n"); + + // + // Add Aliases\Names\(AccountName) [ Rid, ] + // + + RtlCopyUnicodeString( &KeyNameU, FullDomainNameU ); + Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Aliases\\Names\\" ); + SUCCESS_ASSERT(Status, " Failed to append \\aliases\\names to keyname\n"); + Status = RtlAppendUnicodeStringToString( &KeyNameU, &AliasNameU); + SUCCESS_ASSERT(Status, " Failed to append Alias account name to\n"); + Status = RtlAddActionToRXact( + SamRXactContext, + RtlRXactOperationSetValue, + &KeyNameU, + Rid, + NULL, + 0 + ); + SUCCESS_ASSERT(Status, " Failed to add Aliases\\Names\\(AliasName) to log\n"); + + + // + // Set the Members attribute. We know which accounts are supposed + // to be members of which aliases, so we'll build the memberships in + // automatically. + // + // Each domain has a list of SIDs that are members of its aliases. + // We'll update these values at the same time that we set the alias + // members by calling UpdateAliasXReference(). Currently, that only + // happens in the builtin domain, where things look like this: + // + // BuiltinDomainSid + // AdminUserRid - Admins alias (WinNt + primary domain) + // UserUserRid - Users alias (WinNt + developer setup), + // Power users alias (WinNt + developer setup) + // AccountDomainSid + // AdminUserRid - Admins alias (always) + // GuestUserRid - Guests alias (always) + // UserGroupRid - Users alias, (always) + // Power users alias (WinNt + developer setup) + // AdminGroupRid - Admins alias (LanManNt only) + // + + MemberCount = 0; + TotalLength = 0; + + switch ( Rid ) { + + case DOMAIN_ALIAS_RID_ADMINS: { + + MemberCount = 1; + + Sid1 = BuildAccountSid( DomainAccount, DOMAIN_USER_RID_ADMIN ); + if ( Sid1 == NULL ) { + SUCCESS_ASSERT( STATUS_INSUFFICIENT_RESOURCES, " Could not allocate Sid\n" ); + } + + if ( SampProductType == NtProductLanManNt ) { + + MemberCount = 2; + + Sid2 = BuildAccountSid( DomainAccount, DOMAIN_GROUP_RID_ADMINS ); + if ( Sid2 == NULL ) { + SUCCESS_ASSERT( STATUS_INSUFFICIENT_RESOURCES, " Could not allocate Sid\n" ); + } + } + + if ( ( SampProductType != NtProductLanManNt ) && + ( SampBldPrimaryDomain != NULL ) ) { + + MemberCount = 2; + + Sid2 = BuildPrimaryDomainSid( DOMAIN_GROUP_RID_ADMINS ); + if ( Sid2 == NULL ) { + SUCCESS_ASSERT( STATUS_INSUFFICIENT_RESOURCES, " Could not allocate Sid\n" ); + } + } + + break; + } + + case DOMAIN_ALIAS_RID_USERS: { + + MemberCount = 0; + + if ( (SampProductType == NtProductWinNt) + || (SampProductType == NtProductServer) ) { + + if ( SampBldPrimaryDomain != NULL ) { + + MemberCount = 1; + Sid1 = BuildPrimaryDomainSid( DOMAIN_GROUP_RID_USERS ); + + if ( Sid1 == NULL ) { + + SUCCESS_ASSERT( STATUS_INSUFFICIENT_RESOURCES, " Could not allocate Sid\n" ); + } + } + + } else { + + // + // + if (SampProductType == NtProductLanManNt ) { + + // + // NTAS systems have the USERS global group in + // the USERS alias. + // + + MemberCount = 1; + Sid1 = BuildAccountSid( + DomainAccount, + DOMAIN_GROUP_RID_USERS + ); + + if ( Sid1 == NULL ) { + SUCCESS_ASSERT( STATUS_INSUFFICIENT_RESOURCES, " Could not allocate Sid\n" ); + } + } else { + + // + // WinNT systems have the ADMINISTRATOR user account + // in the USERS alias. The None group is NOT in this + // alias because even guests are in the None group. + // + + MemberCount = 1; + Sid1 = BuildAccountSid( + DomainAccount, + DOMAIN_USER_RID_ADMIN + ); + + if ( Sid1 == NULL ) { + SUCCESS_ASSERT( STATUS_INSUFFICIENT_RESOURCES, " Could not allocate Sid\n" ); + } + } + } + + break; + } + + case DOMAIN_ALIAS_RID_GUESTS: { + + + if ( (SampProductType == NtProductWinNt) + || (SampProductType == NtProductServer) ) { + + // + // WinNT system - make our GUEST user account a member of + // the GUESTS alias. + // + + MemberCount = 1; + Sid1 = BuildAccountSid( DomainAccount, DOMAIN_USER_RID_GUEST ); + + + // + // If we are in a primary domain, then add that domain's + // GUESTS global group to the alias as well. + // + + if ( SampBldPrimaryDomain != NULL ) { + + MemberCount += 1; + Sid2 = BuildPrimaryDomainSid( DOMAIN_GROUP_RID_GUESTS ); + if ( Sid2 == NULL ) { + SUCCESS_ASSERT( STATUS_INSUFFICIENT_RESOURCES, " Could not allocate Sid\n" ); + } + } + } else { + + // + // NTAS System - Just make the GUESTS global group + // a member of the GUESTS alias. + // + + MemberCount = 1; + Sid1 = BuildPrimaryDomainSid( DOMAIN_GROUP_RID_GUESTS ); + if ( Sid1 == NULL ) { + SUCCESS_ASSERT( STATUS_INSUFFICIENT_RESOURCES, " Could not allocate Sid\n" ); + } + } + + + break; + } + + case DOMAIN_ALIAS_RID_POWER_USERS: { + + if ( ( SampProductType != NtProductLanManNt ) && + ( SampDeveloperSetup ) ) { + + MemberCount = 1; + + Sid1 = BuildAccountSid( DomainAccount, DOMAIN_GROUP_RID_USERS ); + + if ( Sid1 == NULL ) { + SUCCESS_ASSERT( STATUS_INSUFFICIENT_RESOURCES, " Could not allocate Sid\n" ); + } + + if ( SampBldPrimaryDomain != NULL ) { + + MemberCount = 2; + + Sid2 = BuildPrimaryDomainSid( DOMAIN_GROUP_RID_USERS ); + + if ( Sid2 == NULL ) { + + SUCCESS_ASSERT( STATUS_INSUFFICIENT_RESOURCES, " Could not allocate Sid\n" ); + } + } + } + + break; + } + + case DOMAIN_ALIAS_RID_ACCOUNT_OPS: + case DOMAIN_ALIAS_RID_SYSTEM_OPS: + case DOMAIN_ALIAS_RID_PRINT_OPS: + case DOMAIN_ALIAS_RID_BACKUP_OPS: + case DOMAIN_ALIAS_RID_REPLICATOR: { + + break; + } + + default: { + + SUCCESS_ASSERT(STATUS_UNSUCCESSFUL, " Bad Alias RID\n"); + break; + } + }; + + if ( MemberCount > 0 ) { + + TotalLength = RtlLengthSid( Sid1 ); + if ( MemberCount == 2 ) { + + TotalLength += RtlLengthSid( Sid2 ); + } + + AliasMembers = RtlAllocateHeap( RtlProcessHeap(), 0, TotalLength ); + if ( AliasMembers == NULL ) { + SUCCESS_ASSERT( STATUS_INSUFFICIENT_RESOURCES, " Could not allocate AliasMembers\n" ); + } + + Status = RtlCopySid( RtlLengthSid( Sid1 ), AliasMembers, Sid1 ); + SUCCESS_ASSERT( Status, " Couldn't copy Sid1\n" ); + + Status = UpdateAliasXReference( Rid, Sid1 ); + SUCCESS_ASSERT( Status, " Couldn't update alias xref\n" ); + + if ( MemberCount == 2 ) { + + Status = RtlCopySid( + RtlLengthSid( Sid2 ), + (PSID)((PUCHAR)AliasMembers + RtlLengthSid( Sid1 ) ), + Sid2 ); + SUCCESS_ASSERT( Status, " Couldn't copy Sid2\n" ); + + Status = UpdateAliasXReference( Rid, Sid2 ); + RtlFreeHeap( RtlProcessHeap(), 0, Sid2 ); + SUCCESS_ASSERT( Status, " Couldn't update alias xref\n" ); + } + + RtlFreeHeap( RtlProcessHeap(), 0, Sid1 ); + } + + + // + // Set the alias's fixed and variable attributes + // + + AliasAttributeLength = sizeof( SAMP_V1_FIXED_LENGTH_ALIAS ) + + ( SAMP_ALIAS_VARIABLE_ATTRIBUTES * + sizeof( SAMP_VARIABLE_LENGTH_ATTRIBUTE ) ) + + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) + + SampDwordAlignUlong(AccountNameU->Length) + + SampDwordAlignUlong(AccountCommentU->Length) + + TotalLength; + + AliasFixedAttributes = (PSAMP_V1_FIXED_LENGTH_ALIAS)RtlAllocateHeap( + RtlProcessHeap(), 0, + AliasAttributeLength + ); + + if ( AliasFixedAttributes == NULL ) { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + SUCCESS_ASSERT(Status, " Failed to create alias attributes\n"); + + AliasFixedAttributes->RelativeId = Rid; + + AliasVariableAttributeArray = (PSAMP_VARIABLE_LENGTH_ATTRIBUTE) + ((PUCHAR)(AliasFixedAttributes) + + sizeof( SAMP_V1_FIXED_LENGTH_ALIAS ) ); + + AliasVariableAttributeArray->Offset = 0; + AliasVariableAttributeArray->Length = + SampProtection[ProtectionIndex].Length; + AliasVariableAttributeArray->Qualifier = SAMP_REVISION; + + AliasVariableAttributeArray++; + + AliasVariableAttributeArray->Offset = + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length); + AliasVariableAttributeArray->Length = AliasNameU.Length; + AliasVariableAttributeArray->Qualifier = 0; + + AliasVariableAttributeArray++; + + AliasVariableAttributeArray->Offset = + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) + + SampDwordAlignUlong(AccountNameU->Length); + AliasVariableAttributeArray->Length = AliasCommentU.Length; + AliasVariableAttributeArray->Qualifier = 0; + + AliasVariableAttributeArray++; + + AliasVariableAttributeArray->Offset = + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) + + SampDwordAlignUlong(AliasNameU.Length) + + SampDwordAlignUlong(AliasCommentU.Length); + AliasVariableAttributeArray->Length = TotalLength; + AliasVariableAttributeArray->Qualifier = MemberCount; + + AliasVariableData = (PVOID)( (PUCHAR)(AliasVariableAttributeArray) + + sizeof( SAMP_VARIABLE_LENGTH_ATTRIBUTE ) ); + + RtlCopyMemory( + AliasVariableData, + SampProtection[ProtectionIndex].Descriptor, + SampProtection[ProtectionIndex].Length + ); + + RtlCopyMemory( + (PVOID)((PUCHAR)(AliasVariableData) + + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length)), + AccountNameU->Buffer, + AccountNameU->Length + ); + + RtlCopyMemory( + (PVOID)((PUCHAR)(AliasVariableData) + + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) + + SampDwordAlignUlong(AliasNameU.Length)), + AccountCommentU->Buffer, + AccountCommentU->Length + ); + + RtlCopyMemory( + (PVOID)((PUCHAR)(AliasVariableData) + + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) + + SampDwordAlignUlong(AliasNameU.Length) + + SampDwordAlignUlong(AliasCommentU.Length)), + AliasMembers, + TotalLength + ); + + if ( AliasMembers != NULL ) { + + RtlFreeHeap( RtlProcessHeap(), 0, AliasMembers ); + } + + // + // Create Aliases\(AliasRid) [Revision,] key + // + + RtlCopyUnicodeString( &KeyNameU, FullDomainNameU ); + Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Aliases\\" ); + SUCCESS_ASSERT(Status, " Failed to append \\aliases\\ to keyname\n"); + + // + // Convert the Rid to a Unicode String with leading zero's + // + + Status = SampRtlConvertUlongToUnicodeString( + Rid, + 16, + 8, + FALSE, + &KeyNameU + ); + + SUCCESS_ASSERT(Status, " CreateAlias' SampRtlConvertUlongToUnicodeString failed\n"); + + // + // Now write out the attributes via the RXACT. + // + + SampDumpRXact(SampRXactContext, + RtlRXactOperationSetValue, + &KeyNameU, + INVALID_HANDLE_VALUE, + &SampCombinedAttributeName, + REG_BINARY, + (PVOID)AliasFixedAttributes, + AliasAttributeLength, + FIXED_LENGTH_ALIAS_FLAG); + + Status = RtlAddAttributeActionToRXact( + SamRXactContext, + RtlRXactOperationSetValue, + &KeyNameU, + INVALID_HANDLE_VALUE, + &SampCombinedAttributeName, + REG_BINARY, + (PVOID)AliasFixedAttributes, + AliasAttributeLength + ); + + SUCCESS_ASSERT(Status, " Failed to write out alias attributes\n" ); + + RtlFreeHeap( RtlProcessHeap(), 0, AliasFixedAttributes ); + + // + // Commit these additions... + // + + Status = RtlApplyRXactNoFlush( SamRXactContext ); + SUCCESS_ASSERT(Status, " Failed to commit Alias addition.\n"); + + return Status; + DBG_UNREFERENCED_PARAMETER(SpecialAccount); + +} + + +NTSTATUS +CreateGroup( + IN PUNICODE_STRING AccountNameU, + IN PUNICODE_STRING AccountCommentU, + IN BOOLEAN SpecialAccount, + IN ULONG Rid, + IN BOOLEAN Admin + ) + +/*++ + +Routine Description: + + This routine adds the keys necessary to create a group. It also applies + the appropriate protection to the group. + +Arguments: + + AccountNameU - The Unicode name of the group. + + AccountCommentU - A Unicode comment to put in the object's variable data. + + SpecialAccount - A boolean indicating whether or not the account + is special. Special accounts are marked as such and can not + be deleted. + + Rid - The RID of the account. + + Admin - Indicates whether the account is in the Administrators alias + or not. TRUE means it is, FALSE means it isn't. + +Return Value: + + TBS + +--*/ + +{ + PSAMP_V1_0A_FIXED_LENGTH_GROUP GroupFixedAttributes; + PSAMP_VARIABLE_LENGTH_ATTRIBUTE GroupVariableAttributeArray; + PVOID GroupVariableData; + + ULONG Attributes, ProtectionIndex, GroupCount, GroupAttributeLength; + ULONG GroupMembers[2]; + + UNICODE_STRING GroupNameU, GroupCommentU; + + SAMTRACE("CreateGroup"); + + GroupNameU = *AccountNameU; + GroupCommentU = *AccountCommentU; + + Attributes = (SE_GROUP_MANDATORY | + SE_GROUP_ENABLED_BY_DEFAULT | + SE_GROUP_ENABLED); + + // + // Set the correct protection. + // + + if (Admin == TRUE) { + + ProtectionIndex = SAMP_PROT_ADMIN_GROUP; + + } else { + + ProtectionIndex = SAMP_PROT_NORMAL_GROUP; + } + + // + // Set the account specific RID in the DACL's if necessary + // + + if ( SampProtection[ProtectionIndex].RidReplacementRequired == TRUE ) { + + (*SampProtection[ProtectionIndex].RidToReplace) = Rid; + } + + // + // Use a transaction + // + + Status = RtlStartRXact( SamRXactContext ); + SUCCESS_ASSERT(Status, " Failed to start group addition transaction\n"); + + // + // Add Groups\Names\(GroupName) [ Rid, ] + // + + RtlCopyUnicodeString( &KeyNameU, FullDomainNameU ); + Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Groups\\Names\\" ); + SUCCESS_ASSERT(Status, " Failed to append \\Groups\\Names\n"); + Status = RtlAppendUnicodeStringToString( &KeyNameU, AccountNameU); + SUCCESS_ASSERT(Status, " Failed to append AccountName\n"); + + Status = RtlAddActionToRXact( + SamRXactContext, + RtlRXactOperationSetValue, + &KeyNameU, + Rid, + NULL, + 0 + ); + + SUCCESS_ASSERT(Status, " Failed to add Groups\\Names\\(GroupName) to log\n"); + + // + // Create Groups\(GroupRid) [Revision,] key + // + + RtlCopyUnicodeString( &KeyNameU, FullDomainNameU ); + Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Groups\\" ); + SUCCESS_ASSERT(Status, " Failed to append \\Groups\\\n"); + + Status = SampRtlConvertUlongToUnicodeString( + Rid, + 16, + 8, + FALSE, + &KeyNameU + ); + + SUCCESS_ASSERT(Status, "CreateGroup: Failed to append Rid Name\n"); + + // + // Set the Members attribute. The Admin and Guest users are always + // members of the Users group. If there is an Admins group, then the + // Admin user is a member of it. + // + + GroupCount = 0; + if ( (Rid == DOMAIN_GROUP_RID_USERS) || + (Rid == DOMAIN_GROUP_RID_ADMINS) ) { + + GroupMembers[GroupCount] = DOMAIN_USER_RID_ADMIN; + GroupCount++; + + } + + // + // Guests are only members of the Guest group on NTAS systems. + // On WinNT systems they are members of NONE (which is the sam + // as USERS + // + + if ( (Rid == DOMAIN_GROUP_RID_GUESTS) && + (SampProductType == NtProductLanManNt) ) { + + GroupMembers[GroupCount] = DOMAIN_USER_RID_GUEST; + GroupCount++; + } + + if ( (Rid == DOMAIN_GROUP_RID_USERS) && + ((SampProductType == NtProductWinNt) + || (SampProductType == NtProductServer)) ) { + + GroupMembers[GroupCount] = DOMAIN_USER_RID_GUEST; + GroupCount++; + } + + // + // Set the group's fixed and variable attributes + // + + GroupAttributeLength = sizeof( SAMP_V1_0A_FIXED_LENGTH_GROUP ) + + ( SAMP_GROUP_VARIABLE_ATTRIBUTES * + sizeof( SAMP_VARIABLE_LENGTH_ATTRIBUTE ) ) + + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) + + SampDwordAlignUlong(GroupNameU.Length) + + SampDwordAlignUlong(GroupCommentU.Length) + + ( GroupCount * sizeof( ULONG ) ); + + GroupFixedAttributes = (PSAMP_V1_0A_FIXED_LENGTH_GROUP)RtlAllocateHeap( + RtlProcessHeap(), 0, + GroupAttributeLength + ); + + if ( GroupFixedAttributes == NULL ) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + } + + SUCCESS_ASSERT(Status, " Failed to create group attributes\n"); + + GroupFixedAttributes->RelativeId = Rid; + GroupFixedAttributes->Attributes = Attributes; + GroupFixedAttributes->AdminCount = Admin ? 1 : 0; + GroupFixedAttributes->OperatorCount = 0; + GroupFixedAttributes->Revision = SAMP_REVISION; + + GroupVariableAttributeArray = (PSAMP_VARIABLE_LENGTH_ATTRIBUTE) + ((PUCHAR)(GroupFixedAttributes) + + sizeof( SAMP_V1_0A_FIXED_LENGTH_GROUP ) ); + + GroupVariableAttributeArray->Offset = 0; + GroupVariableAttributeArray->Length = + SampProtection[ProtectionIndex].Length; + GroupVariableAttributeArray->Qualifier = SAMP_REVISION; + + GroupVariableAttributeArray++; + + GroupVariableAttributeArray->Offset = + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length); + GroupVariableAttributeArray->Length = GroupNameU.Length; + GroupVariableAttributeArray->Qualifier = 0; + + GroupVariableAttributeArray++; + + GroupVariableAttributeArray->Offset = + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) + + SampDwordAlignUlong(GroupNameU.Length); + GroupVariableAttributeArray->Length = GroupCommentU.Length; + GroupVariableAttributeArray->Qualifier = 0; + + GroupVariableAttributeArray++; + + GroupVariableAttributeArray->Offset = + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) + + SampDwordAlignUlong(GroupNameU.Length) + + SampDwordAlignUlong(GroupCommentU.Length); + GroupVariableAttributeArray->Length = GroupCount * sizeof( ULONG ); + GroupVariableAttributeArray->Qualifier = GroupCount; + + GroupVariableData = (PVOID)( (PUCHAR)(GroupVariableAttributeArray) + + sizeof( SAMP_VARIABLE_LENGTH_ATTRIBUTE ) ); + + RtlCopyMemory( + GroupVariableData, + SampProtection[ProtectionIndex].Descriptor, + SampProtection[ProtectionIndex].Length + ); + + RtlCopyMemory( + (PVOID)((PUCHAR)(GroupVariableData) + + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length)), + GroupNameU.Buffer, + GroupNameU.Length + ); + + RtlCopyMemory( + (PVOID)((PUCHAR)(GroupVariableData) + + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) + + SampDwordAlignUlong(GroupNameU.Length)), + GroupCommentU.Buffer, + GroupCommentU.Length + ); + + RtlCopyMemory( + (PVOID)((PUCHAR)(GroupVariableData) + + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) + + SampDwordAlignUlong(GroupNameU.Length) + + SampDwordAlignUlong(GroupCommentU.Length)), + GroupMembers, + GroupCount * sizeof( ULONG ) + ); + + // + // Now write out the attributes via the RXACT. + // + + SampDumpRXact(SampRXactContext, + RtlRXactOperationSetValue, + &KeyNameU, + INVALID_HANDLE_VALUE, + &SampCombinedAttributeName, + REG_BINARY, + (PVOID)GroupFixedAttributes, + GroupAttributeLength, + FIXED_LENGTH_GROUP_FLAG); + + Status = RtlAddAttributeActionToRXact( + SamRXactContext, + RtlRXactOperationSetValue, + &KeyNameU, + INVALID_HANDLE_VALUE, + &SampCombinedAttributeName, + REG_BINARY, + (PVOID)GroupFixedAttributes, + GroupAttributeLength + ); + SUCCESS_ASSERT(Status, " Failed to write out group attributes\n" ); + + RtlFreeHeap( RtlProcessHeap(), 0, GroupFixedAttributes ); + + // + // Commit these additions... + // + + Status = RtlApplyRXactNoFlush( SamRXactContext ); + SUCCESS_ASSERT(Status, " Failed to commit group addition.\n"); + + return Status; + DBG_UNREFERENCED_PARAMETER(SpecialAccount); +} + + +NTSTATUS +CreateUser( + IN PUNICODE_STRING AccountNameU, + IN PUNICODE_STRING AccountCommentU, + IN BOOLEAN SpecialAccount, + IN ULONG UserRid, + IN ULONG PrimaryGroup, + IN BOOLEAN Admin, + IN ULONG UserControl, + IN ULONG ProtectionIndex + ) + +/*++ + + +Routine Description: + + This routine adds keys for a single user. + This routine adds the keys necessary to create a user. It also applies + the appropriate protection to the user (protection differs for some + standard users). + + +Arguments: + + AccountNameU - The Unicode name of the user. + + AccountCommentU - A Unicode comment to put in the object's variable data. + + SpecialAccount - A boolean indicating whether or not the account + is special. Special accounts are marked as such and can not + be deleted. + + UserRid - The RID of the user account. + + PrimaryGroup - The RID of the account's primary group. The user + does not have to be a member of the group. In fact, it doesn't + have to be a group. In fact, no checking is done to see if it + is even a valid account. + + Admin - Indicates whether the account is in the Administrators alias + or not. TRUE means it is, FALSE means it isn't. + + ProtectionIndex - Indicates which security descriptor to use to protect + this object. + + +Return Value: + + TBS + +--*/ + +{ + PSAMP_V1_0A_FIXED_LENGTH_USER UserFixedAttributes; + PSAMP_VARIABLE_LENGTH_ATTRIBUTE UserVariableAttributeArray; + PSAMP_VARIABLE_LENGTH_ATTRIBUTE UserVariableAttributeArrayStart; + PVOID UserVariableData; + GROUP_MEMBERSHIP GroupMembership[2]; + + WCHAR RidNameBuffer[9], GroupIndexNameBuffer[9]; + ULONG GroupCount, UserAttributeLength; + + // SAM BUG 42367 FIX - ChrisMay 7/1/96. + + BOOLEAN DomainAdminMember = FALSE; + + UNICODE_STRING UserNameU, UserCommentU; + + SAMTRACE("CreateUser"); + + UserNameU = *AccountNameU; + UserCommentU = *AccountCommentU; + + + // + // Set the account specific RID in the DACL's if necessary + // + + if ( SampProtection[ProtectionIndex].RidReplacementRequired == TRUE ) { + + (*SampProtection[ProtectionIndex].RidToReplace) = UserRid; + } + + // + // Use a transaction. + // + + Status = RtlStartRXact( SamRXactContext ); + SUCCESS_ASSERT(Status, " Failed to start user addition transaction\n"); + + RidNameBuffer[8] = 0; + GroupIndexNameBuffer[8] = 0; + + RtlCopyUnicodeString( &KeyNameU, FullDomainNameU ); + Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Users\\Names\\" ); + SUCCESS_ASSERT(Status, " Failed to append \\Users\\Names\\\n"); + Status = RtlAppendUnicodeStringToString( &KeyNameU, &UserNameU); + SUCCESS_ASSERT(Status, " Failed to append User Account Name\n"); + Status = RtlAddActionToRXact( + SamRXactContext, + RtlRXactOperationSetValue, + &KeyNameU, + UserRid, + NULL, + 0 + ); + SUCCESS_ASSERT(Status, " Failed to add Users\\Names\\(Name) to log\n"); + + // + // Create Users\(UserRid) key + // (KeyValueType is revision, KeyValue is SecurityDescriptor) + // + + RtlCopyUnicodeString( &KeyNameU, FullDomainNameU ); + Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Users\\" ); + SUCCESS_ASSERT(Status, " Failed to append \\Users\\\n"); + + Status = SampRtlConvertUlongToUnicodeString( + UserRid, + 16, + 8, + FALSE, + &KeyNameU + ); + + SUCCESS_ASSERT(Status, " CreateUser: Failed to append UserRid Name\n"); + + // + // Set the Groups attribute. + // Everybody except GUEST is a member of the Users group. + // On WindowsNT systems (as opposed to NTAS systems) even GUEST + // is a member of the Users group. + // On LanManNt systems, the Admin is a member of the Admins group. + // + + GroupCount = 0; + + if ( (UserRid != DOMAIN_USER_RID_GUEST) || + (SampProductType != NtProductLanManNt) ) { + + GroupMembership[GroupCount].RelativeId = DOMAIN_GROUP_RID_USERS; + GroupMembership[GroupCount].Attributes = SE_GROUP_MANDATORY | + SE_GROUP_ENABLED_BY_DEFAULT | + SE_GROUP_ENABLED; + GroupCount++; + } + + if ( (UserRid == DOMAIN_USER_RID_GUEST) && + (SampProductType == NtProductLanManNt) ) { + + GroupMembership[GroupCount].RelativeId = DOMAIN_GROUP_RID_GUESTS; + GroupMembership[GroupCount].Attributes = SE_GROUP_MANDATORY | + SE_GROUP_ENABLED_BY_DEFAULT | + SE_GROUP_ENABLED; + GroupCount++; + } + + + if ( ( UserRid == DOMAIN_USER_RID_ADMIN ) && + ( SampProductType == NtProductLanManNt ) ) { + + GroupMembership[GroupCount].RelativeId = DOMAIN_GROUP_RID_ADMINS; + GroupMembership[GroupCount].Attributes = SE_GROUP_MANDATORY | + SE_GROUP_ENABLED_BY_DEFAULT | + SE_GROUP_ENABLED; + GroupCount++; + + // SAM BUG 42367 FIX - ChrisMay 7/1/96. + + DomainAdminMember = TRUE; + } + + // + // Set the user's fixed and variable attributes + // + + UserFixedAttributes = (PSAMP_V1_0A_FIXED_LENGTH_USER)RtlAllocateHeap( + RtlProcessHeap(), 0, + sizeof( SAMP_V1_0A_FIXED_LENGTH_USER ) + ); + + if ( UserFixedAttributes == NULL ) { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + + SUCCESS_ASSERT(Status, " Failed to create user fixed attributes\n"); + + UserFixedAttributes->Revision = SAMP_REVISION; + + UserFixedAttributes->CountryCode = 0; + UserFixedAttributes->CodePage = 0; + UserFixedAttributes->BadPasswordCount = 0; + UserFixedAttributes->LogonCount = 0; + UserFixedAttributes->OperatorCount = 0; + UserFixedAttributes->Unused1 = 0; + UserFixedAttributes->Unused2 = 0; + + if ( Admin ) { + + // SAM BUG 42367 FIX - ChrisMay 7/1/96. + + // UserFixedAttributes->AdminCount = 1; + + // If the user is an admin and a member of Domain Admins, set the + // count to two. + + if (DomainAdminMember) + { + UserFixedAttributes->AdminCount = 2; + } + else + { + UserFixedAttributes->AdminCount = 1; + } + + } else { + + UserFixedAttributes->AdminCount = 0; + } + + UserFixedAttributes->UserAccountControl = UserControl; + UserFixedAttributes->UserId = UserRid; + UserFixedAttributes->PrimaryGroupId = PrimaryGroup; + UserFixedAttributes->LastLogon = SampHasNeverTime; + UserFixedAttributes->LastLogoff = SampHasNeverTime; + UserFixedAttributes->PasswordLastSet = SampHasNeverTime; + UserFixedAttributes->AccountExpires = SampWillNeverTime; + UserFixedAttributes->LastBadPasswordTime = SampHasNeverTime; + + + UserAttributeLength = SampDwordAlignUlong(UserNameU.Length) + + SampDwordAlignUlong(UserCommentU.Length) + + SampDwordAlignUlong( GroupCount * + sizeof( GROUP_MEMBERSHIP ) ) + + ( SAMP_USER_VARIABLE_ATTRIBUTES * + sizeof( SAMP_VARIABLE_LENGTH_ATTRIBUTE ) ) + + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length); + + UserVariableAttributeArrayStart = (PSAMP_VARIABLE_LENGTH_ATTRIBUTE) + RtlAllocateHeap( + RtlProcessHeap(), 0, + UserAttributeLength + ); + + if ( UserVariableAttributeArrayStart == NULL ) { + + Status = STATUS_INSUFFICIENT_RESOURCES; + } + SUCCESS_ASSERT(Status, " Failed to create user variable attributes\n"); + + UserVariableAttributeArray = UserVariableAttributeArrayStart; + + UserVariableAttributeArray->Offset = 0; + UserVariableAttributeArray->Length = + SampProtection[ProtectionIndex].Length; + UserVariableAttributeArray->Qualifier = SAMP_REVISION; + + UserVariableAttributeArray++; + + UserVariableAttributeArray->Offset = + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length); + UserVariableAttributeArray->Length = UserNameU.Length; + UserVariableAttributeArray->Qualifier = 0; + + UserVariableAttributeArray++; + + UserVariableAttributeArray->Offset = + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) + + SampDwordAlignUlong(UserNameU.Length); + UserVariableAttributeArray->Length = 0; + UserVariableAttributeArray->Qualifier = 0; + + UserVariableAttributeArray++; + + UserVariableAttributeArray->Offset = + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) + + SampDwordAlignUlong(UserNameU.Length); + UserVariableAttributeArray->Length = UserCommentU.Length; + UserVariableAttributeArray->Qualifier = 0; + + UserVariableAttributeArray++; + + UserVariableAttributeArray->Offset = + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) + + SampDwordAlignUlong(UserNameU.Length) + + SampDwordAlignUlong(UserCommentU.Length); + UserVariableAttributeArray->Length = 0; + UserVariableAttributeArray->Qualifier = 0; + + UserVariableAttributeArray++; + + UserVariableAttributeArray->Offset = + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) + + SampDwordAlignUlong(UserNameU.Length) + + SampDwordAlignUlong(UserCommentU.Length); + UserVariableAttributeArray->Length = 0; + UserVariableAttributeArray->Qualifier = 0; + + UserVariableAttributeArray++; + + UserVariableAttributeArray->Offset = + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) + + SampDwordAlignUlong(UserNameU.Length) + + SampDwordAlignUlong(UserCommentU.Length); + UserVariableAttributeArray->Length = 0; + UserVariableAttributeArray->Qualifier = 0; + + UserVariableAttributeArray++; + + UserVariableAttributeArray->Offset = + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) + + SampDwordAlignUlong(UserNameU.Length) + + SampDwordAlignUlong(UserCommentU.Length); + UserVariableAttributeArray->Length = 0; + UserVariableAttributeArray->Qualifier = 0; + + UserVariableAttributeArray++; + + UserVariableAttributeArray->Offset = + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) + + SampDwordAlignUlong(UserNameU.Length) + + SampDwordAlignUlong(UserCommentU.Length); + UserVariableAttributeArray->Length = 0; + UserVariableAttributeArray->Qualifier = 0; + + UserVariableAttributeArray++; + + UserVariableAttributeArray->Offset = + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) + + SampDwordAlignUlong(UserNameU.Length) + + SampDwordAlignUlong(UserCommentU.Length); + UserVariableAttributeArray->Length = 0; + UserVariableAttributeArray->Qualifier = 0; + + UserVariableAttributeArray++; + + UserVariableAttributeArray->Offset = + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) + + SampDwordAlignUlong(UserNameU.Length) + + SampDwordAlignUlong(UserCommentU.Length); + UserVariableAttributeArray->Length = 0; + UserVariableAttributeArray->Qualifier = 0; + + UserVariableAttributeArray++; + + UserVariableAttributeArray->Offset = + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) + + SampDwordAlignUlong(UserNameU.Length) + + SampDwordAlignUlong(UserCommentU.Length); + UserVariableAttributeArray->Length = 0; + UserVariableAttributeArray->Qualifier = 0; + + UserVariableAttributeArray++; + + UserVariableAttributeArray->Offset = + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) + + SampDwordAlignUlong(UserNameU.Length) + + SampDwordAlignUlong(UserCommentU.Length); + UserVariableAttributeArray->Length = GroupCount * sizeof( GROUP_MEMBERSHIP ); + UserVariableAttributeArray->Qualifier = GroupCount; + + UserVariableAttributeArray++; + + UserVariableAttributeArray->Offset = + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) + + SampDwordAlignUlong(UserNameU.Length) + + SampDwordAlignUlong(UserCommentU.Length) + + SampDwordAlignUlong((GroupCount * sizeof( GROUP_MEMBERSHIP ))); + UserVariableAttributeArray->Length = 0; + UserVariableAttributeArray->Qualifier = 0; + + UserVariableAttributeArray++; + + UserVariableAttributeArray->Offset = + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) + + SampDwordAlignUlong(UserNameU.Length) + + SampDwordAlignUlong(UserCommentU.Length) + + SampDwordAlignUlong((GroupCount * sizeof( GROUP_MEMBERSHIP ))); + UserVariableAttributeArray->Length = 0; + UserVariableAttributeArray->Qualifier = 0; + + UserVariableAttributeArray++; + + UserVariableAttributeArray->Offset = + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) + + SampDwordAlignUlong(UserNameU.Length) + + SampDwordAlignUlong(UserCommentU.Length) + + SampDwordAlignUlong((GroupCount * sizeof( GROUP_MEMBERSHIP ))); + UserVariableAttributeArray->Length = 0; + UserVariableAttributeArray->Qualifier = 0; + + UserVariableAttributeArray++; + + UserVariableAttributeArray->Offset = + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) + + SampDwordAlignUlong(UserNameU.Length) + + SampDwordAlignUlong(UserCommentU.Length) + + SampDwordAlignUlong((GroupCount * sizeof( GROUP_MEMBERSHIP ))); + UserVariableAttributeArray->Length = 0; + UserVariableAttributeArray->Qualifier = 0; + + UserVariableData = (PVOID)( (PUCHAR)(UserVariableAttributeArray) + + sizeof( SAMP_VARIABLE_LENGTH_ATTRIBUTE ) ); + + RtlCopyMemory( + UserVariableData, + SampProtection[ProtectionIndex].Descriptor, + SampProtection[ProtectionIndex].Length + ); + + RtlCopyMemory( + (PVOID)((PUCHAR)(UserVariableData) + + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length)), + UserNameU.Buffer, + UserNameU.Length + ); + + RtlCopyMemory( + (PVOID)((PUCHAR)(UserVariableData) + + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) + + SampDwordAlignUlong(UserNameU.Length)), + UserCommentU.Buffer, + UserCommentU.Length + ); + + RtlCopyMemory( + (PVOID)((PUCHAR)(UserVariableData) + + SampDwordAlignUlong(SampProtection[ProtectionIndex].Length) + + SampDwordAlignUlong(UserNameU.Length) + + SampDwordAlignUlong(UserCommentU.Length)), + &GroupMembership, + GroupCount * sizeof( GROUP_MEMBERSHIP ) + ); + + // + // Now write out the attributes via the RXACT. + // + + SampDumpRXact(SampRXactContext, + RtlRXactOperationSetValue, + &KeyNameU, + INVALID_HANDLE_VALUE, + &SampFixedAttributeName, + REG_BINARY, + (PVOID)UserFixedAttributes, + sizeof( SAMP_V1_0A_FIXED_LENGTH_USER ), + FIXED_LENGTH_USER_FLAG); + + Status = RtlAddAttributeActionToRXact( + SamRXactContext, + RtlRXactOperationSetValue, + &KeyNameU, + INVALID_HANDLE_VALUE, + &SampFixedAttributeName, + REG_BINARY, + (PVOID)UserFixedAttributes, + sizeof( SAMP_V1_0A_FIXED_LENGTH_USER ) + ); + SUCCESS_ASSERT(Status, " Failed to write out user fixed attributes\n" ); + + RtlFreeHeap( RtlProcessHeap(), 0, UserFixedAttributes ); + + SampDumpRXact(SampRXactContext, + RtlRXactOperationSetValue, + &KeyNameU, + INVALID_HANDLE_VALUE, + &SampVariableAttributeName, + REG_BINARY, + (PVOID)UserVariableAttributeArrayStart, + UserAttributeLength, + VARIABLE_LENGTH_ATTRIBUTE_FLAG); + + Status = RtlAddAttributeActionToRXact( + SamRXactContext, + RtlRXactOperationSetValue, + &KeyNameU, + INVALID_HANDLE_VALUE, + &SampVariableAttributeName, + REG_BINARY, + (PVOID)UserVariableAttributeArrayStart, + UserAttributeLength + ); + SUCCESS_ASSERT(Status, " Failed to write out user variable attributes\n" ); + + RtlFreeHeap( RtlProcessHeap(), 0, UserVariableAttributeArrayStart ); + + // + // Commit these additions... + // + + Status = RtlApplyRXactNoFlush( SamRXactContext ); + SUCCESS_ASSERT(Status, " Failed to commit user addition.\n"); + + return Status; + + DBG_UNREFERENCED_PARAMETER(SpecialAccount); + DBG_UNREFERENCED_PARAMETER(UserControl); +} + + + +PSID +BuildPrimaryDomainSid( + ULONG Rid + ) +{ + NTSTATUS Status; + PSID SourceDomainSid, NewSid; + ULONG SidLength, SubAuthorityCount; + + SourceDomainSid = SampBldPrimaryDomain->Sid; + + SidLength = RtlLengthSid( SourceDomainSid ) + sizeof(ULONG); + NewSid = RtlAllocateHeap( RtlProcessHeap(), 0, SidLength ); + if (NewSid != NULL) { + + Status = RtlCopySid (SidLength, NewSid, SourceDomainSid ); + ASSERT(NT_SUCCESS(Status)); + + (*RtlSubAuthorityCountSid( NewSid )) += 1; + SubAuthorityCount = (ULONG)(*RtlSubAuthorityCountSid( NewSid )); + (*RtlSubAuthoritySid( NewSid, SubAuthorityCount-1)) = Rid; + + } + + + return(NewSid); + + +} + + + + +PSID +BuildAccountSid( + SAMP_DOMAIN_SELECTOR Domain, + ULONG Rid + ) +{ + NTSTATUS Status; + PSID SourceDomainSid, NewSid; + ULONG SidLength, SubAuthorityCount; + + + if (Domain == DomainBuiltin) { + SourceDomainSid = SampBuiltinDomainSid; + } else { + SourceDomainSid = SampAccountDomainSid; + } + + SidLength = RtlLengthSid( SourceDomainSid ) + sizeof(ULONG); + NewSid = RtlAllocateHeap( RtlProcessHeap(), 0, SidLength ); + if (NewSid != NULL) { + + Status = RtlCopySid (SidLength, NewSid, SourceDomainSid ); + ASSERT(NT_SUCCESS(Status)); + + (*RtlSubAuthorityCountSid( NewSid )) += 1; + SubAuthorityCount = (ULONG)(*RtlSubAuthorityCountSid( NewSid )); + (*RtlSubAuthoritySid( NewSid, SubAuthorityCount-1)) = Rid; + + } + + + return(NewSid); + + +} + + + +NTSTATUS +UpdateAliasXReference( + IN ULONG AliasRid, + IN PSID Sid + ) + +/*++ + + +Routine Description: + + This routine updates the set of alias member SIDs either by adding + specified SID (if it isn't already an alias member) or incrementing + its count (if it is already an alias member). + + + The BUILTIN domain is updated. + + + +Arguments: + + + Sid - member Sid to update. + + + + +Return Value: + + TBS + +--*/ + +{ + NTSTATUS IgnoreStatus; + + HANDLE KeyHandle; + + SAMTRACE("UpdateAliasXReference"); + + + + if (RtlSubAuthorityCountSid( Sid ) == 0) { + return(STATUS_INVALID_SID); + } + + + // + // Open the domain key for this alias member. + // + + SetCurrentDomain( DomainBuiltin ); + Status = OpenAliasMember( Sid, &KeyHandle ); + + + if (NT_SUCCESS(Status)) { + + ULONG MembershipCount, + KeyValueLength, + OldKeyValueLength, + i; + PULONG MembershipArray; + + // + // Retrieve the length of the current membership buffer + // and allocate one large enough for that plus another member. + // + + KeyValueLength = 0; + Status = RtlpNtQueryValueKey( KeyHandle, + &MembershipCount, + NULL, + &KeyValueLength, + NULL); + + SampDumpRtlpNtQueryValueKey(&MembershipCount, + NULL, + &KeyValueLength, + NULL); + + if (NT_SUCCESS(Status) || (Status == STATUS_BUFFER_OVERFLOW)) { + + KeyValueLength += sizeof(ULONG); + MembershipArray = RtlAllocateHeap( RtlProcessHeap(), 0, KeyValueLength ); + + + if (MembershipArray == NULL) { + Status = STATUS_INSUFFICIENT_RESOURCES; + } else { + + OldKeyValueLength = KeyValueLength; + Status = RtlpNtQueryValueKey( + KeyHandle, + NULL, + MembershipArray, + &OldKeyValueLength, + NULL); + + SampDumpRtlpNtQueryValueKey(NULL, + MembershipArray, + &OldKeyValueLength, + NULL); + + if (NT_SUCCESS(Status)) { + + // + // See if the account is already a member ... + // + + for (i = 0; i<MembershipCount ; i++ ) { + if ( MembershipArray[i] == AliasRid ) + { + Status = STATUS_MEMBER_IN_ALIAS; + } + } + + if (NT_SUCCESS(Status)) { + + // + // Add the Aliasrid to the end + // + + MembershipCount += 1; + MembershipArray[MembershipCount-1] = AliasRid; + + // + // And write it out. + // + + Status = RtlpNtSetValueKey( + KeyHandle, + MembershipCount, + MembershipArray, + KeyValueLength + ); + + SampDumpRtlpNtSetValueKey(MembershipCount, + MembershipArray, + KeyValueLength); + } + } + + RtlFreeHeap(RtlProcessHeap(), 0, MembershipArray); + } + + } + + IgnoreStatus = NtClose( KeyHandle ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + } + + + + return( Status ); + +} + + +NTSTATUS +OpenAliasMember( + IN PSID Sid, + OUT PHANDLE KeyHandle + ) + +/*++ + +Routine Description: + + This routine opens the registry key containing the alias + xreference for the specified SID. If either this key, or + its corresponding parent key doesn't exist, it (they) will + be created. + + If a new domain-level key is created, the DomainCount in the + ALIASES\MEMBERS key is incremented as well. + + +Arguments: + + Sid - The SID that is an alias member. + + KeyHandle - Receives a handle to the registry key for this alias + member account xreference. + +Return Value: + + None. + +--*/ + +{ + + NTSTATUS IgnoreStatus; + HANDLE AliasDomainHandle; + + SAMTRACE("OpenAliasMember"); + + // + // Open or create the domain-level key. + // + + + Status = OpenOrCreateAliasDomainKey( Sid, &AliasDomainHandle ); + + if (NT_SUCCESS(Status)) { + + + // + // Open or create the account-rid key + // + + Status = OpenOrCreateAccountRidKey( Sid, + AliasDomainHandle, + KeyHandle + ); + + IgnoreStatus = NtClose( AliasDomainHandle ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + return(Status); + + +} + + + +NTSTATUS +OpenOrCreateAccountRidKey( + IN PSID Sid, + IN HANDLE AliasDomainHandle, + OUT PHANDLE KeyHandle + ) + +/*++ + +Routine Description: + + This routine opens an account xreference key for an alias + member SID. + + If this key doesn't exist, it will be created. + + If a new key is created, the RidCount in the AliasDomainHandle + key is incremented as well. + + +Arguments: + + Sid - The SID that is an alias member. + + AliasDomainHandle + + KeyHandle - Receives a handle to the registry key for this alias + member domain xreference. + +Return Value: + + None. + +--*/ + +{ + OBJECT_ATTRIBUTES ObjectAttributes; + ULONG Disposition; + ULONG Rid; + + SAMTRACE("OpenOrCreateAccountRidKey"); + + if (RtlSubAuthorityCountSid( Sid ) == 0) { + return(STATUS_INVALID_SID); + } + + Rid = (*RtlSubAuthoritySid(Sid, (ULONG)(*RtlSubAuthorityCountSid(Sid))-1)); + + // + // Build the Unicode Key for this Rid. + // + + KeyNameU.Length = (USHORT) 0; + + Status = SampRtlConvertUlongToUnicodeString( + Rid, + 16, + 8, + FALSE, + &KeyNameU + ); + + // + // Open this key relative to the alias domain key + // + + InitializeObjectAttributes( + &ObjectAttributes, + &KeyNameU, + OBJ_CASE_INSENSITIVE, + AliasDomainHandle, + NULL + ); + Status = RtlpNtCreateKey( + KeyHandle, + (KEY_READ | KEY_WRITE), + &ObjectAttributes, + 0, //Options + NULL, //Provider + &Disposition + ); + + if (NT_SUCCESS(Status)) { + + if (Disposition == REG_CREATED_NEW_KEY) { + + // + // Update the AccountRid count in the alias domain key + // + + ULONG MembershipCount; + + + // + // Retrieve the current domain count and increment it by 1. + // + + Status = RtlpNtQueryValueKey( AliasDomainHandle, + &MembershipCount, + NULL, + NULL, + NULL); + + SampDumpRtlpNtQueryValueKey(&MembershipCount, + NULL, + NULL, + NULL); + + if (NT_SUCCESS(Status)) { + + MembershipCount += 1; + + // + // Write it back out. + // + + Status = RtlpNtSetValueKey( + AliasDomainHandle, + MembershipCount, + NULL, + 0 + ); + + SampDumpRtlpNtSetValueKey(MembershipCount, + NULL, + 0); + } + + // + // Now write out the AccountRid key info + // + + Status = RtlpNtSetValueKey( + *KeyHandle, + 0, //Not yet a member of any aliases + NULL, + 0 + ); + + SampDumpRtlpNtSetValueKey(0, + NULL, + 0); + } + } + + return(Status); +} + + + +NTSTATUS +OpenOrCreateAliasDomainKey( + IN PSID Sid, + OUT PHANDLE KeyHandle + ) + +/*++ + +Routine Description: + + This routine opens a domain xreference key for an alias + member SID. + + If this key doesn't exist, it will be created. + + If a new key is created, the DomainCount in the + ALIASES\MEMBERS key is incremented as well. + + +Arguments: + + Sid - The SID that is an alias member. + + KeyHandle - Receives a handle to the registry key for this alias + member domain xreference. + +Return Value: + + None. + +--*/ + +{ + NTSTATUS IgnoreStatus; + OBJECT_ATTRIBUTES ObjectAttributes; + ULONG Disposition; + + SAMTRACE("OpenOrCreateAliasDomainKey"); + + RtlCopyUnicodeString( &KeyNameU, FullDomainNameU ); + Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Aliases" ); + Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Members\\" ); + Status = AppendAliasDomainNameToUnicodeString( &KeyNameU, Sid ); + + InitializeObjectAttributes( + &ObjectAttributes, + &KeyNameU, + OBJ_CASE_INSENSITIVE, + SamKey, + NULL + ); + Status = RtlpNtCreateKey( + KeyHandle, + (KEY_READ | KEY_WRITE), + &ObjectAttributes, + 0, //Options + NULL, //Provider + &Disposition + ); + + if (NT_SUCCESS(Status)) { + + if (Disposition == REG_CREATED_NEW_KEY) { + + HANDLE TmpHandle; + + // + // Update the Domain count + // + + RtlCopyUnicodeString( &KeyNameU, FullDomainNameU ); + Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Aliases" ); + Status = RtlAppendUnicodeToString( &KeyNameU, L"\\Members\\" ); + + InitializeObjectAttributes( + &ObjectAttributes, + &KeyNameU, + OBJ_CASE_INSENSITIVE, + SamKey, + NULL + ); + + SampDumpNtOpenKey((KEY_READ | KEY_WRITE), &ObjectAttributes, 0); + + Status = RtlpNtOpenKey( + &TmpHandle, + (KEY_READ | KEY_WRITE), + &ObjectAttributes, + 0 + ); + ASSERT(NT_SUCCESS(Status)); + + if (NT_SUCCESS(Status)) { + + ULONG MembershipCount; + + + // + // Retrieve the current domain count and increment it by 1. + // + + Status = RtlpNtQueryValueKey( TmpHandle, + &MembershipCount, + NULL, + NULL, + NULL); + + SampDumpRtlpNtQueryValueKey(&MembershipCount, + NULL, + NULL, + NULL); + + if (NT_SUCCESS(Status)) { + + MembershipCount += 1; + + // + // Write it back out. + // + + Status = RtlpNtSetValueKey( + TmpHandle, + MembershipCount, + NULL, + 0 + ); + + SampDumpRtlpNtSetValueKey(MembershipCount, + NULL, + 0); + } + + IgnoreStatus = NtClose( TmpHandle ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + } + } + } + + return(Status); +} + + +NTSTATUS +AppendAliasDomainNameToUnicodeString( + IN OUT PUNICODE_STRING Destination, + IN PSID Sid + ) + +{ + UCHAR OriginalCount; + + SAMTRACE("AppendAliasDomainNameToUnicodeString"); + + // + // Save the current sub-authority count and decrement it by one. + // + + OriginalCount = (*RtlSubAuthorityCountSid(Sid)); + (*RtlSubAuthorityCountSid(Sid)) = OriginalCount -1; + + // + // Convert the Sid to a Unicode String and place it in the global + // temporary Unicode String buffer. + // + + Status = RtlConvertSidToUnicodeString( &TempStringU, Sid, TRUE); + + (*RtlSubAuthorityCountSid(Sid)) = OriginalCount; + + if (NT_SUCCESS(Status)) { + + Status = RtlAppendUnicodeStringToString( Destination, &TempStringU ); + } + + return(Status); +} + + + +VOID +SampGetServerRole( + VOID + ) + +/*++ + +Routine Description: + + This routine retrieves the server role from the LSA policy database + and places it in the global variable SampServerRole. + + +Arguments: + + None. + + +Return Value: + + (placed in the global variable (Status) ) + + STATUS_SUCCESS - Succeeded. + + Other status values that may be returned from: + + LsarQueryInformationPolicy() +--*/ + +{ + NTSTATUS IgnoreStatus; + PPOLICY_LSA_SERVER_ROLE_INFO ServerRoleInfo = NULL; + + SAMTRACE("SampGetServerRole"); + + // + // Query the server role information + // + + Status = LsarQueryInformationPolicy( + SampBldPolicyHandle, + PolicyLsaServerRoleInformation, + (PLSAPR_POLICY_INFORMATION *)&ServerRoleInfo + ); + + if (NT_SUCCESS(Status)) { + + if (ServerRoleInfo->LsaServerRole == PolicyServerRolePrimary) { + + SampServerRole = DomainServerRolePrimary; + + } else { + + SampServerRole = DomainServerRoleBackup; + } + + IgnoreStatus = LsaFreeMemory( ServerRoleInfo ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + return; +} + + + + +VOID +SampGetPrimaryDomainInfo( + VOID + ) + +/*++ + +Routine Description: + + This routine retrieves the primary domain name/sid from the + LSA policy database and places it in the global variable + SampBldPrimaryDomain. + + +Arguments: + + None. + + +Return Value: + + (placed in the global variable (Status) ) + + STATUS_SUCCESS - Succeeded. + + Other status values that may be returned from: + + LsarQueryInformationPolicy() + + NOTE: The Rdr and Bowser components of the LanmanWorkstation + service rely on there always being a primary domain name. + For this reason Network SETUP always supplies a default + "workgroup" name, which is set as the primary domain name. + In this case, the name is present but the SID is NULL; + this is equivalent to NOT having a primary domain at all. + +--*/ + +{ + SAMTRACE("SampGetPrimaryDomainInfo"); + + SampBldPrimaryDomain = NULL; + + Status = LsarQueryInformationPolicy( + SampBldPolicyHandle, + PolicyPrimaryDomainInformation, + (PLSAPR_POLICY_INFORMATION *) &SampBldPrimaryDomain + ); + + if (NT_SUCCESS(Status) && ( SampBldPrimaryDomain->Sid == NULL )) { + + LsaIFree_LSAPR_POLICY_INFORMATION( + PolicyPrimaryDomainInformation, + (PLSAPR_POLICY_INFORMATION) SampBldPrimaryDomain + ); + + SampBldPrimaryDomain = NULL; + } + + return; +} + + + + +VOID +SampRestoreDefaultDacl( + VOID + ) + +/*++ + +Routine Description: + + Restores the saved default DACL to the current token. + +Arguments: + + None. + + +Return Value: + + None. + +--*/ + +{ + NTSTATUS Status; + HANDLE Token; + + SAMTRACE("SampRestoreDefaultDacl"); + + Status = NtOpenProcessToken( + NtCurrentProcess(), + (TOKEN_ADJUST_DEFAULT), + &Token + ); + + + ASSERT(NT_SUCCESS( Status )); + + Status = NtSetInformationToken ( + Token, + TokenDefaultDacl, + TokenDefaultDaclInformation, + TokenDefaultDaclInformationSize + ); + + ASSERT(NT_SUCCESS( Status )); + + NtClose( Token ); + +} + + +NTSTATUS +SampDetermineSetupEnvironment( VOID ) + + +/*++ + +Routine Description: + + This function checks to see whether we are running folloing + a formal SETUP. If not, it is assumed we are running to + perform a developer's setup. + + Global variables are set to indicate our setup environment. + + + BOOLEAN SampRealSetupWasRun; //Indicates a real setup was run + BOOLEAN SampDeveloperSetup; //Indicates a developer setup is running + + +Arguments: + + None. + +Return Value: + + +--*/ + +{ + NTSTATUS NtStatus, TmpStatus; + HANDLE InstallationEvent; + OBJECT_ATTRIBUTES EventAttributes; + UNICODE_STRING EventName; + + SAMTRACE("SampDetermineSetupEnvironment"); + + SampRealSetupWasRun = FALSE; + SampDeveloperSetup = FALSE; + + // + // If the following event exists, it is an indication that + // a real setup was run. + // + + RtlInitUnicodeString( &EventName, L"\\INSTALLATION_SECURITY_HOLD"); + InitializeObjectAttributes( &EventAttributes, &EventName, 0, 0, NULL ); + + NtStatus = NtOpenEvent( + &InstallationEvent, + SYNCHRONIZE, + &EventAttributes + ); + + if ( NT_SUCCESS(NtStatus)) { + + // + // The event exists - installation created it and will signal it + // when it is ok to proceed with security initialization. + // + + SampRealSetupWasRun = TRUE; + + TmpStatus = NtClose( InstallationEvent ); + ASSERT(NT_SUCCESS(TmpStatus)); + + } else { + SampDeveloperSetup = TRUE; + } + + + + return(NtStatus); + +} + + + +NTSTATUS +SampInitializeRegistry ( + VOID + ) +/*++ + +Routine Description: + + This routine initializes a SAM database in the registry. + +Arguments: + + NONE + +Return Value: + + STATUS_SUCCESS or an error received along the way. + +--*/ + +{ + NTSTATUS IgnoreStatus; + + SAMTRACE("SampInitializeRegistry"); + + Status = Initialize(); + if (!NT_SUCCESS(Status)) { + return( Status ); + } + + // + // Initialize SAM-level registry structures + // + + Status = InitializeSam( ); + if (!NT_SUCCESS(Status)) {return(Status);} + + // + // OK, we have a SAM key. + // Create each of the domains. + // + + Status = CreateBuiltinDomain( ); if (!NT_SUCCESS(Status)) {return(Status);} + Status = CreateAccountDomain( ); if (!NT_SUCCESS(Status)) {return(Status);} + + // + // all done + // + + // + // Close our handle to LSA. Ignore any errors. + // + + IgnoreStatus = LsarClose( (PLSAPR_HANDLE) &SampBldPolicyHandle ); + SampBldPolicyHandle = NULL; + + // + // We modified the default DACL in the token, put it back to what + // it was here. + // + + SampRestoreDefaultDacl(); + + // + // Free up the transaction context we created + // + + RtlFreeHeap( RtlProcessHeap(), 0, SamRXactContext ); + SamRXactContext = NULL; + + // + // Close the database root key after flushing all the changes we made. + // + + Status = NtFlushKey( SamKey ); + + if (NT_SUCCESS(Status)) { + + IgnoreStatus = NtClose( SamKey ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + } else { + + KdPrint(("SAMSRV: FlushKey failed, database not built. Status: 0x%lx\n", Status)); + IgnoreStatus = NtClose( SamKey ); + } + + SamKey = NULL; + + // + // Close the database root parent key + // + + if (SamParentKey != NULL) { + IgnoreStatus = NtClose( SamParentKey ); + } + + return( Status ); +} + + +NTSTATUS +SampGetMessageStrings( + LPVOID Resource, + DWORD Index1, + PUNICODE_STRING String1, + DWORD Index2, + PUNICODE_STRING String2 OPTIONAL + ) + + +/*++ + +Routine Description: + + This gets 1 or 2 message strings values from a resource message table. + The string buffers are allocated and the strings initialized properly. + + The string buffers must be freed using LocalFree() when no longer needed. + +Arguments: + + Resource - points to the resource table. + + Index1 - Index of first message to retrieve. + + String1 - Points to a UNICODE_STRING structure to receive the first + message string. + + Index2 - Index of second message to retrieve. + + String2 - Points to a UNICODE_STRING structure to receive the first + message string. If this parameter is NULL, then only one message + string is retrieved. + +Return Value: + + None. + +--*/ + + +{ + + SAMTRACE("SampGetMessageStrings"); + + String1->Buffer = NULL; + + String1->MaximumLength = (USHORT) FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | + FORMAT_MESSAGE_ALLOCATE_BUFFER, + Resource, + Index1, + 0, // Use caller's language + (LPWSTR)&(String1->Buffer), + 0, + NULL + ); + + if (String1->Buffer == NULL) { + return(STATUS_RESOURCE_DATA_NOT_FOUND); + } else { + + // + // Note that we are retrieving a message from a message file. + // This message will have a cr/lf tacked on the end of it + // (0x0d 0x0a) that we don't want to be part of our returned + // strings. Also note that FormatMessage() returns a character + // count, not a byte count. So, we have to do some adjusting + // to make the string lengths correct. + // + + String1->MaximumLength -= 2; // For the cr/lf we don't want. + String1->MaximumLength *= sizeof(WCHAR); // to make it a byte count + String1->Length = String1->MaximumLength; + } + + + if (!ARGUMENT_PRESENT(String2)) { + return(STATUS_SUCCESS); + } + + String2->Buffer = NULL; + String2->MaximumLength = (USHORT) FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | + FORMAT_MESSAGE_ALLOCATE_BUFFER, + Resource, + Index2, + 0, // Use caller's language + (LPWSTR)&(String2->Buffer), + 0, + NULL + ); + + if (String2->Buffer == NULL) { + LocalFree( String1->Buffer ); + return(STATUS_RESOURCE_DATA_NOT_FOUND); + } else { + + // + // Note that we are retrieving a message from a message file. + // This message will have a cr/lf tacked on the end of it + // (0x0d 0x0a) that we don't want to be part of our returned + // strings. Also note that FormatMessage() returns a character + // count, not a byte count. So, we have to do some adjusting + // to make the string lengths correct. + // + + String2->MaximumLength -= 2; // For the cr/lf we don't want. + String2->MaximumLength *= sizeof(WCHAR); // to make it a byte count + String2->Length = String2->MaximumLength; + } + + + + return(STATUS_SUCCESS); + +} diff --git a/private/newsam2/server/close.c b/private/newsam2/server/close.c new file mode 100644 index 000000000..8443b329f --- /dev/null +++ b/private/newsam2/server/close.c @@ -0,0 +1,181 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + close.c + +Abstract: + + This file contains the object close routine for SAM objects. + + +Author: + + Jim Kelly (JimK) 4-July-1991 + +Environment: + + User Mode - Win32 + +Revision History: + + +--*/ + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include <samsrvp.h> + + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// private service prototypes // +// // +/////////////////////////////////////////////////////////////////////////////// + + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Routines // +// // +/////////////////////////////////////////////////////////////////////////////// + + + + + + +NTSTATUS +SamrCloseHandle( + IN OUT SAMPR_HANDLE * SamHandle + ) + +/*++ + +Routine Description: + + This service closes a handle for any type of SAM object. + + Any race conditions that may occur with respect to attempts to + close a handle that is just becoming invalid by other means are + expected to be handled by the RPC runtime. That is, this service + will never be called by the RPC runtime when the handle value is + no longer valid. It will also never call this routine when there + is another call outstanding with this same context handle. + +Arguments: + + SamHandle - A valid handle to a SAM object. + +Return Value: + + + STATUS_SUCCESS - The handle has successfully been closed. + + Others that might be returned by: + + SampLookupcontext() + + +--*/ +{ + NTSTATUS NtStatus; + PSAMP_OBJECT Context; + SAMP_OBJECT_TYPE FoundType; + + SAMTRACE("SamrCloseHandle"); + + Context = (PSAMP_OBJECT)(* SamHandle); + + // + // Grab a read lock + // + + SampAcquireReadLock(); + + NtStatus = SampLookupContext( + Context, //Context + 0, //DesiredAccess + SampUnknownObjectType, //ExpectedType + &FoundType //FoundType + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Mark it for delete and remove the reference caused by + // context creation (representing the handle reference). + // + + SampDeleteContext( Context ); + + // + // And drop our reference from the lookup operation + // + + SampDeReferenceContext( Context, FALSE ); + + // + // Tell RPC that the handle is no longer valid... + // + + (*SamHandle) = NULL; + } + + // + // Free read lock + // + + SampReleaseReadLock(); + + if ( ( NT_SUCCESS( NtStatus ) ) && + ( FoundType == SampServerObjectType ) && + ( !(LastUnflushedChange.QuadPart == SampHasNeverTime.QuadPart) ) ) { + + // + // Some app is closing the server object after having made + // changes. We should make sure that the changes get + // flushed to disk before the app exits. We need to get + // the write lock for this. + // + + FlushImmediately = TRUE; + + NtStatus = SampAcquireWriteLock(); + + if ( NT_SUCCESS( NtStatus ) ) { + + if ( !(LastUnflushedChange.QuadPart ==SampHasNeverTime.QuadPart) ) { + + // + // Nobody flushed while we were waiting for the + // write lock. So flush the changes now. + // + + NtStatus = NtFlushKey( SampKey ); + + if ( NT_SUCCESS( NtStatus ) ) { + + FlushImmediately = FALSE; + LastUnflushedChange = SampHasNeverTime; + } + } + + SampReleaseWriteLock( FALSE ); + } + } + + return(NtStatus); +} diff --git a/private/newsam2/server/context.c b/private/newsam2/server/context.c new file mode 100644 index 000000000..b6acdc02c --- /dev/null +++ b/private/newsam2/server/context.c @@ -0,0 +1,1936 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + context.c + +Abstract: + + This file contains services for operating on internal context blocks. + + +Author: + + Jim Kelly (JimK) 4-July-1991 + +Environment: + + User Mode - Win32 + +Revision History: + 31 - May 1996 Murlis + Ported to use the NT5 DS. + + ChrisMay 10-Jun-96 + Added initialization of Context->ObjectNameInDs and IsDsObject data + members. Note that this causes context objects to be created with a + default storage of type registry (instead of DS). + + 6-16-96 + Moved DS object decision to SampCreateContext for account objects. + +--*/ + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include <samsrvp.h> +#include <dslayer.h> + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// private service prototypes // +// // +/////////////////////////////////////////////////////////////////////////////// + +// +// Context validation services. +// The service to invalidate a context is visible outside this file and so +// its prototype is in samsrvp.h. +// + +VOID +SampAddNewValidContextAddress( + IN PSAMP_OBJECT NewContext + ); + +NTSTATUS +SampValidateContextAddress( + IN PSAMP_OBJECT Context + ); + + +NTSTATUS +SampCheckIfObjectExists( + IN PSAMP_OBJECT Context + ); + +BOOLEAN +SampIsObjectLocated( + IN PSAMP_OBJECT Context + ); + +NTSTATUS +SampLocateObject( + IN PSAMP_OBJECT Context, + IN ULONG Rid + ); + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Routines // +// // +/////////////////////////////////////////////////////////////////////////////// + + + + + + + + + +PSAMP_OBJECT +SampCreateContext( + IN SAMP_OBJECT_TYPE Type, + IN BOOLEAN TrustedClient + ) + +/*++ + +Routine Description: + + This service creates a new object context block of the specified type. + + If the context block is for either a user or group object type, then + it will be added to the list of contexts for the transaction domain. + + THIS SERVICE MUST BE CALLED WITH THE SampLock HELD FOR WRITE ACCESS. + + + Upon return: + + - The ObjectType field will be set to the passed value. + + - The Reference count field will be set to 1, + + - The GrantedAccess field will be zero. + + - The TrustedClient field will be set according to the passed + value. + + - The Valid flag will be TRUE. + + All other fields must be filled in by the creator. + + +Arguments: + + Type - Specifies the type of context block being created. + + TrustedClient - Indicates whether the client is a trusted component + of the operating syste. If so, than all access checks are + circumvented. + + + +Return Value: + + + Non-Null - Pointer to a context block. + + NULL - Insufficient resources. No context block allocated. + + +--*/ +{ + + PSAMP_OBJECT Context; + + SAMTRACE("SampCreateContext"); + + if (!TrustedClient) { + if (SampActiveContextCount >= SAMP_MAXIMUM_ACTIVE_CONTEXTS) { + return(NULL); + } + + SampActiveContextCount += 1; + } + + + Context = MIDL_user_allocate( sizeof(SAMP_OBJECT) ); + if (Context != NULL) { + +#if SAMP_DIAGNOSTICS + IF_SAMP_GLOBAL( CONTEXT_TRACKING ) { + SampDiagPrint( CONTEXT_TRACKING, ("Creating ") ); + if (Type == SampServerObjectType) SampDiagPrint(CONTEXT_TRACKING, ("Server ")); + if (Type == SampDomainObjectType) SampDiagPrint(CONTEXT_TRACKING, (" Domain ")); + if (Type == SampGroupObjectType) SampDiagPrint(CONTEXT_TRACKING, (" Group ")); + if (Type == SampAliasObjectType) SampDiagPrint(CONTEXT_TRACKING, (" Alias ")); + if (Type == SampUserObjectType) SampDiagPrint(CONTEXT_TRACKING, (" User ")); + SampDiagPrint(CONTEXT_TRACKING, ("context : 0x%lx\n", Context )); + } +#endif //SAMP_DIAGNOSTICS + + + Context->ObjectType = Type; + Context->ReferenceCount = 1; // Represents RPCs held context handle value + Context->GrantedAccess = 0; + + Context->RootKey = INVALID_HANDLE_VALUE; + RtlInitUnicodeString(&Context->RootName, NULL); + + Context->TrustedClient = TrustedClient; + Context->MarkedForDelete = FALSE; + Context->AuditOnClose = FALSE; + + Context->OnDisk = NULL; + Context->OnDiskAllocated = 0; + Context->FixedValid = FALSE; + Context->VariableValid = FALSE; + + // + // The following are meaningless at this point because of the + // values of the variables above, but we'll set them just to be + // neat. + // + + Context->FixedDirty = FALSE; + Context->VariableDirty = FALSE; + + Context->OnDiskUsed = 0; + Context->OnDiskFree = 0; + + // + // Inititialize the IsDSObject to Registry Object and + // Object Name in DS to NULL. Later we will find out where + // the Object should actually exist + // + + SetRegistryObject(Context); + Context->ObjectNameInDs = NULL; + + // + // Add this new context to the set of valid contexts ... + // + + SampAddNewValidContextAddress( Context ); + + + // + // User and group context blocks are kept on linked lists + // from the domain's in-memory structure. Insert in the + // Appropriate List and then additionally for Account Objects + // Make the DS/Registry decision by looking at the TransactionDomain + // + + + Context->DomainIndex = SampTransactionDomainIndex; + + switch (Type) { + + case SampDomainObjectType: + InitializeListHead( + &(Context->TypeBody.Domain.DsEnumerationContext)); + ////////////////////////////////////////////////////// + // // + // Warning This case falls into the next one // + // // + ////////////////////////////////////////////////////// + case SampServerObjectType: + + InsertTailList( + &SampContextListHead, + &Context->ContextListEntry + ); + break; + + case SampUserObjectType: + + // We must have set the Transaction Domain + ASSERT(SampTransactionWithinDomain); + + // Set the DS or Registry Object Part + if (IsDsObject(SampDefinedDomains[SampTransactionDomainIndex].Context)) + SetDsObject(Context); + + // Insert into List + InsertTailList( + &SampDefinedDomains[SampTransactionDomainIndex].UserContextHead, + &Context->ContextListEntry + ); + break; + + case SampGroupObjectType: + + // We must have set the Transaction Domain + ASSERT(SampTransactionWithinDomain); + + // Set the DS or Registry Object Part + if (IsDsObject(SampDefinedDomains[SampTransactionDomainIndex].Context)) + SetDsObject(Context); + + // Insert into List + InsertTailList( + &SampDefinedDomains[SampTransactionDomainIndex].GroupContextHead, + &Context->ContextListEntry + ); + break; + + case SampAliasObjectType: + + // We must have set the Transaction Domain + ASSERT(SampTransactionWithinDomain); + + // Set the DS or Registry Object Part + if (IsDsObject(SampDefinedDomains[SampTransactionDomainIndex].Context)) + SetDsObject(Context); + + // Insert into List + InsertTailList( + &SampDefinedDomains[SampTransactionDomainIndex].AliasContextHead, + &Context->ContextListEntry + ); + break; + } + } + + return(Context); +} + + +VOID +SampDeleteContext( + IN PSAMP_OBJECT Context + ) + +/*++ + +Routine Description: + + This service marks a context object for delete and dereferences it. + If this causes the reference count to go to zero, then the context + block will be immediately deleted (deallocated). Otherwise, the + context block will be deleted when the reference count finally does + go to zero. + + + THIS SERVICE MUST BE CALLED WITH THE SampLock HELD FOR WRITE ACCESS. + + +Arguments: + + Context - Pointer to the context block to delete. + +Return Value: + + None. + + + +--*/ +{ + NTSTATUS IgnoreStatus; + + SAMTRACE("SampDeleteContext"); + + Context->MarkedForDelete = TRUE; + + // + // Audit the close of this context. + // + + (VOID) NtCloseObjectAuditAlarm ( + &SampSamSubsystem, + (PVOID)Context, + Context->AuditOnClose + ); + + // + // Remove this context from the valid context set. + // Note that the context may have already been removed. This is + // not an error. + // + + SampInvalidateContextAddress( Context ); + + + // + // User and group context blocks are kept on linked lists + // from the domain's in-memory structure. Domain and + // server context blocks are kept on a global in-memory list. + // They are removed when they are marked for delete. + // + + RemoveEntryList(&Context->ContextListEntry); + + // + // We have to call dereference to counter the initial count of 1 + // put on by create. + // + + IgnoreStatus = SampDeReferenceContext( Context, FALSE ); + + return; + +} + + +NTSTATUS +SampLookupContext( + IN PSAMP_OBJECT Context, + IN ACCESS_MASK DesiredAccess, + IN SAMP_OBJECT_TYPE ExpectedType, + OUT PSAMP_OBJECT_TYPE FoundType + ) + +/*++ + +Routine Description: + + This service: + + - Checks to make sure the Service state is one in which an + object can be looked up (i.e., not Initializing or Terminating). + + - Makes sure the Service state is compatible with the lookup. + Non-trusted clients can only perform lookups when the Service + state is Enabled. If the client isn't trusted and the context + is for a group or user, then the state of that object's domain + must also be enabled + + - Checks to make sure the context block represents the + type of object expected, and, if so: + + - Checks to see that the caller has the requested (desired) + access, and, if so: + + - Makes sure the object still exists, and opens it if it + does. Servers and domains can't be deleted, and so + their handle is left open. + + - References the context block + + + Note that if the block is marked as TrustedClient, then access will + always be granted unless service state prevents it. + + Also, if the ExpectedType is specified to be unknown, then any type + of context will be accepted. + + + + If the type of object is found to be , Domain, Group or User, then the + this service will set the transaction domain. + + THIS SERVICE MUST BE CALLED WITH THE SampLock HELD FOR WRITE ACCESS. + + +Arguments: + + Context - Pointer to the context block to look-up. + + DesiredAccess - The type of access the client is requesting to this + object. A zero-valued access mask may be specified. In this case, + the calling routine must do access validation. + + ExpectedType - The type of object expected. This may be unknown. In + this case, the DesiredAccess should only include access types that + apply to any type of object (e.g., Delete, WriteDacl, et cetera). + + FoundType - Receives the type of context actually found. + +Return Value: + + STATUS_SUCCESS - The context was found to be the type expected (or any + type if ExpectedType was unknown) and the DesiredAccesses are all + granted. + + STATUS_OBJECT_TYPE_MISMATCH - Indicates the context was not the expected + type. + + STATUS_ACCESS_DENIED - The desired access is not granted by this context. + + +--*/ +{ + NTSTATUS NtStatus; + + SAMTRACE("SampLookupContext"); + + + // + // Make sure we are in a legitimate state to at least access + // a context block. If we are initializing we have somehow allowed + // a connect through. This should never happen. + // If we are terminating, clients may still have handles (since we + // have no way to tell RPC they are no longer valid without the client + // calling us, Argh!). However, since we are terminating, the blocks + // are being cleaned up and may no longer be allocated. + // + + ASSERT( SampServiceState != SampServiceInitializing ); + if ( SampServiceState == SampServiceTerminating ) { + return(STATUS_INVALID_SERVER_STATE); + } + + + // + // Make sure the passed context address is (still) valid. + // + + NtStatus = SampValidateContextAddress( Context ); + if ( !NT_SUCCESS(NtStatus) ) { + return(NtStatus); + } + + + + // + // Check type + // + + (*FoundType) = Context->ObjectType; + if (ExpectedType != SampUnknownObjectType) { + if (ExpectedType != (*FoundType)) { + return(STATUS_OBJECT_TYPE_MISMATCH); + } + } + + // + // if the object is either user or group, then we need to set the + // transaction domain. + + if ((Context->ObjectType == SampDomainObjectType) || + (Context->ObjectType == SampGroupObjectType) || + (Context->ObjectType == SampAliasObjectType) || + (Context->ObjectType == SampUserObjectType) ) { + + SampSetTransactionDomain( Context->DomainIndex ); + + } + + + + + // + // If the client isn't trusted, then there are a number of things + // that will prevent them from continuing... + // + + // If the service isn't enabled, we allow trusted clients to continue, + // but reject non-trusted client lookups. + // + + if ( !Context->TrustedClient ) { + + // + // The SAM service must be enabled + // + + if (SampServiceState != SampServiceEnabled) { + return(STATUS_INVALID_SERVER_STATE); + } + + + // + // If the access is to a USER or GROUP and the client isn't trusted + // then the domain must be enabled or the operation is rejected. + // + + if ( (Context->ObjectType == SampUserObjectType) || + (Context->ObjectType == SampAliasObjectType) || + (Context->ObjectType == SampGroupObjectType) ) { + if (SampDefinedDomains[Context->DomainIndex].CurrentFixed.ServerState + != DomainServerEnabled) { + return(STATUS_INVALID_DOMAIN_STATE); + } + } + + } + + // + // Check the desired access ... + // + // There are several special cases: + // + // 1) The client is trusted. This is granted with no access check + // or role consistency check. + // + // 2) The caller specified 0 for desired access. This is used + // to close handles and is granted with no access check. + // + // 3) The role of the domain (for domain object operations) is + // inconsistent with the desired access. + // + // + + if ( (!Context->TrustedClient) ) { + + if (DesiredAccess != 0) { + + if (!RtlAreAllAccessesGranted( Context->GrantedAccess, DesiredAccess)) { + return(STATUS_ACCESS_DENIED); + } + } + + if ( (Context->ObjectType == SampDomainObjectType) || + (Context->ObjectType == SampGroupObjectType) || + (Context->ObjectType == SampAliasObjectType) || + (Context->ObjectType == SampUserObjectType) + ) { + // + // The state of the domain may have changed while the caller had + // the object open. In this case, the granted access mask may + // provide a write operation, but the role of the domain no longer + // allows un-trusted clients to perform write operations. + // + // Yuch. + // + + if (SampDefinedDomains[Context->DomainIndex].CurrentFixed.ServerRole + != DomainServerRolePrimary) { + + if (RtlAreAnyAccessesGranted( + SampObjectInformation[ Context->ObjectType ].WriteOperations, + DesiredAccess) ) { + return(STATUS_INVALID_DOMAIN_ROLE); + } + } + } + } + + + + // + // Make sure the object is still around (that is, somebody didn't delete + // it right out from under us). + // + + NtStatus = SampCheckIfObjectExists(Context); + + if (NT_SUCCESS(NtStatus)) { + + // + // Reference the context + // + + Context->ReferenceCount ++; + } + + + return(NtStatus); + +} + + + + + +VOID +SampReferenceContext( + IN PSAMP_OBJECT Context + ) + +/*++ + +Routine Description: + + This service increments a context block's reference count. + + THIS SERVICE MUST BE CALLED WITH THE SampLock HELD FOR WRITE ACCESS. + + +Arguments: + + Context - Pointer to the context block to dreference. + +Return Value: + + None. + +--*/ +{ + SAMTRACE("SampReferenceContext"); + + Context->ReferenceCount++; + + return; +} + + + +NTSTATUS +SampDeReferenceContext( + IN PSAMP_OBJECT Context, + IN BOOLEAN Commit + ) + +/*++ + +Routine Description: + + This service decrements a context block's reference count. + If the reference count drops to zero, then the MarkedForDelete + flag is checked. If it is true, then the context block is + deallocated. + + The attribute buffers are always deleted. + + + THIS SERVICE MUST BE CALLED WITH THE SampLock HELD FOR WRITE ACCESS. + + +Arguments: + + Context - Pointer to the context block to de-reference. + + Commit - if TRUE, the attribute buffers will be added to the RXACT. + Otherwise, they will just be ignored. + +Return Value: + + + STATUS_SUCCESS - The service completed successfully. + + Errors may be returned from SampStoreObjectAttributes(). + + +--*/ +{ + NTSTATUS NtStatus, IgnoreStatus; + BOOLEAN TrustedClient; + + SAMTRACE("SampDeReferenceContext"); + + ASSERT( Context->ReferenceCount != 0 ); + Context->ReferenceCount --; + + TrustedClient = Context->TrustedClient; + + NtStatus = STATUS_SUCCESS; + + if ( Context->OnDisk != NULL ) { + + // + // There are attribute buffers for this context. Flush them if + // asked to do so. + // Use existing open keys + // + + if ( Commit ) { + + NtStatus = SampStoreObjectAttributes(Context, TRUE); + +#if DBG + // + // SampFreeAttributeBuffer will assert if we try to free dirty + // buffers. This is generally useful, but if we're aborting + // (which is this case, Commit = FALSE) we don't want the assert + // so avoid it by pretending the buffers aren't dirty. + // + + } else { + + Context->FixedDirty = FALSE; + Context->VariableDirty = FALSE; +#endif + + } + + // + // Free the buffer that was being used to hold attributes. + // + + SampFreeAttributeBuffer( Context ); + } + + if (Context->ReferenceCount == 0) { + + // + // ReferenceCount has dropped to 0, see if we should delete this + // context. + // + + if (Context->MarkedForDelete == TRUE) { + + // + // Close the context block's root key. + // Domain and server contexts contain root key + // handles that are shared - so don't clean-up these + // if they match the ones in memory. + // + + switch (Context->ObjectType) { + + case SampServerObjectType: + + if ((Context->RootKey != SampKey) && + (Context->RootKey != INVALID_HANDLE_VALUE)) { + + IgnoreStatus = NtClose( Context->RootKey ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + break; + + case SampDomainObjectType: + if (IsDsObject(Context)) + { + + // Free Any enumeration Context's + + LIST_ENTRY * EnumerationList= (LIST_ENTRY *) + &(Context->TypeBody.Domain.DsEnumerationContext); + LIST_ENTRY * CurrentItem = EnumerationList->Flink ; + + + while(!IsListEmpty(EnumerationList)) + { + LIST_ENTRY * ListItem; + + ListItem = RemoveTailList(EnumerationList); + SampFreeRestart(((PSAMP_DS_ENUMERATION_CONTEXT) + ListItem)->Restart); + MIDL_user_free(ListItem); + } + + // Free the DsName + MIDL_user_free(Context->ObjectNameInDs); + Context->ObjectNameInDs = NULL; + + } + else + { + + // Free all the Key Stuff + if ((Context->RootKey != SampDefinedDomains[Context->DomainIndex].Context->RootKey) && + (Context->RootKey != INVALID_HANDLE_VALUE)) + { + + IgnoreStatus = NtClose( Context->RootKey ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + } + + break; + + default: + + if (IsDsObject(Context)) + { + // Free the DSName + MIDL_user_free(Context->ObjectNameInDs); + Context->ObjectNameInDs = NULL; + } + else + { + + // + // Close the root key handle + // + + if (Context->RootKey != INVALID_HANDLE_VALUE) + { + + IgnoreStatus = NtClose( Context->RootKey ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + // + // Free the root key name + // + + SampFreeUnicodeString( &(Context->RootName) ); + } + } + + +#if SAMP_DIAGNOSTICS + IF_SAMP_GLOBAL( CONTEXT_TRACKING ) { + SampDiagPrint( CONTEXT_TRACKING, ("Deallocating ") ); + if (Context->ObjectType == SampServerObjectType) SampDiagPrint(CONTEXT_TRACKING, ("Server ")); + if (Context->ObjectType == SampDomainObjectType) SampDiagPrint(CONTEXT_TRACKING, (" Domain ")); + if (Context->ObjectType == SampGroupObjectType) SampDiagPrint(CONTEXT_TRACKING, (" Group ")); + if (Context->ObjectType == SampAliasObjectType) SampDiagPrint(CONTEXT_TRACKING, (" Alias ")); + if (Context->ObjectType == SampUserObjectType) SampDiagPrint(CONTEXT_TRACKING, (" User ")); + SampDiagPrint(CONTEXT_TRACKING, ("context : 0x%lx\n", Context )); + } +#endif //SAMP_DIAGNOSTICS + + MIDL_user_free( Context ); + + + + // + // Decrement the number of active opens + // + + if (!TrustedClient) { + ASSERT( SampActiveContextCount >= 1 ); + SampActiveContextCount -= 1; + } + + } + } + +#if DBG + // + // Make sure a commit worked. + // + + if (Commit) { + if (!NT_SUCCESS(NtStatus)) { + SampDiagPrint(DISPLAY_STORAGE_FAIL, + ("SAM: Commit failure, status: 0x%lx\n", + NtStatus) ); + IF_SAMP_GLOBAL( BREAK_ON_STORAGE_FAIL ) { + ASSERT(NT_SUCCESS(NtStatus)); + } + } + } +#endif //DBG + + + return( NtStatus ); +} + + +VOID +SampInvalidateContextAddress( + IN PSAMP_OBJECT Context + ) + +/*++ + +Routine Description: + + This service removes a context from the set of valid contexts. + + Note that we may have already removed the context. This is not an + error is expected to happen in the case where an object (like a user + or group) is deleted out from under an open handle. + + + + THIS SERVICE MUST BE CALLED WITH THE SampLock HELD FOR WRITE ACCESS. + + +Arguments: + + Context - Pointer to the context block to be removed from the set + of valid contexts. The ObjectType field of this context must + be valid. + +Return Value: + + None. + + + +--*/ +{ + + SAMTRACE("SampInvalidateContextAddress"); + + + ASSERT( (Context->ObjectType == SampUserObjectType) || + (Context->ObjectType == SampGroupObjectType) || + (Context->ObjectType == SampAliasObjectType) || + (Context->ObjectType == SampDomainObjectType) || + (Context->ObjectType == SampServerObjectType) + ); + + Context->Valid = FALSE; + +} + + + +VOID +SampInvalidateGroupContexts( + IN ULONG Rid + ) + + +/*++ + +Routine Description: + + This service marks all group contexts open to the specified + group as being invalid. This is typically done because the + object has been deleted while there were open handles. All + registry keys related to this context are closed. + + This is done by walking the list of group contexts hung off + the permanent in-memory domain structure. + + THIS IS AN IRRIVERSIBLE OPERATION. + + + + + + THIS SERVICE MUST BE CALLED WITH THE SampLock HELD FOR WRITE ACCESS. + + +Arguments: + + Rid - The RID of the group being invalidated. + + + +Return Value: + + None. + +--*/ +{ + +NTSTATUS IgnoreStatus; +PLIST_ENTRY Head, NextEntry; +PSAMP_OBJECT NextContext; + + + SAMTRACE("SampInvalidateGroupContexts"); + + + Head = &SampDefinedDomains[SampTransactionDomainIndex].GroupContextHead; + + // + // Walk the list of active contexts checking for RID matches + // + + NextEntry = Head->Flink; + + while (NextEntry != Head) { + NextContext = CONTAINING_RECORD( + NextEntry, + SAMP_OBJECT, + ContextListEntry + ); + + if ( (Rid == NextContext->TypeBody.Group.Rid) && + (NextContext->Valid == TRUE) + ) { + NextContext->Valid = FALSE; + + SampDiagPrint( CONTEXT_TRACKING, ("SAM: Invalidating group context 0x%lx : <%wZ>, handle = 0x%lx\n", NextContext, &NextContext->RootName, NextContext->RootKey)); + + if (NextContext->RootKey != INVALID_HANDLE_VALUE) { + + SampDiagPrint( CONTEXT_TRACKING, ("SAM: Closing handle 0x%lx\n", NextContext->RootKey)); + + IgnoreStatus = NtClose(NextContext->RootKey); + ASSERT(NT_SUCCESS(IgnoreStatus)); + NextContext->RootKey = INVALID_HANDLE_VALUE; + } + } + + NextEntry = NextEntry->Flink; + } + + + return; +} + + + +VOID +SampInvalidateAliasContexts( + IN ULONG Rid + ) + + +/*++ + +Routine Description: + + This service marks all alias contexts open to the specified + alias as being invalid. This is typically done because the + object has been deleted while there were open handles. All + registry keys related to this context are closed. + + This is done by walking the list of alias contexts hung off + the permanent in-memory domain structure. + + THIS IS AN IRRIVERSIBLE OPERATION. + + + + + + THIS SERVICE MUST BE CALLED WITH THE SampLock HELD FOR WRITE ACCESS. + + +Arguments: + + Rid - The RID of the alias being invalidated. + + + +Return Value: + + None. + +--*/ +{ + +NTSTATUS IgnoreStatus; +PLIST_ENTRY Head, NextEntry; +PSAMP_OBJECT NextContext; + + + SAMTRACE("SampInvalidateAliasContexts"); + + + Head = &SampDefinedDomains[SampTransactionDomainIndex].AliasContextHead; + + // + // Walk the list of active contexts checking for RID matches + // + + NextEntry = Head->Flink; + + while (NextEntry != Head) { + NextContext = CONTAINING_RECORD( + NextEntry, + SAMP_OBJECT, + ContextListEntry + ); + + if ( (Rid == NextContext->TypeBody.Alias.Rid) && + (NextContext->Valid == TRUE) + ) { + NextContext->Valid = FALSE; + + SampDiagPrint( CONTEXT_TRACKING, ("SAM: Invalidating alias context 0x%lx : <%wZ>, handle = 0x%lx\n", NextContext, &NextContext->RootName, NextContext->RootKey)); + + if (NextContext->RootKey != INVALID_HANDLE_VALUE) { + + SampDiagPrint( CONTEXT_TRACKING, ("SAM: Closing handle 0x%lx\n", NextContext->RootKey)); + + IgnoreStatus = NtClose(NextContext->RootKey); + ASSERT(NT_SUCCESS(IgnoreStatus)); + NextContext->RootKey = INVALID_HANDLE_VALUE; + } + } + + NextEntry = NextEntry->Flink; + } + + + return; +} + + +VOID +SampInvalidateUserContexts( + IN ULONG Rid + ) + + +/*++ + +Routine Description: + + This service marks all group contexts open to the specified + group as being invalid. This is typically done because the + object has been deleted while there were open handles. All + registry keys related to this context are closed. + + + This is done by walking the list of group contexts hung off + the permanent in-memory domain structure. + + + THIS IS AN IRRIVERSIBLE OPERATION. + + + + THIS SERVICE MUST BE CALLED WITH THE SampLock HELD FOR WRITE ACCESS. + + +Arguments: + + Rid - The RID of the group being invalidated. + + + +Return Value: + + None. + + + +--*/ +{ + +NTSTATUS IgnoreStatus; +PLIST_ENTRY Head, NextEntry; +PSAMP_OBJECT NextContext; + + + SAMTRACE("SampInvalidateUserContexts"); + + + Head = &SampDefinedDomains[SampTransactionDomainIndex].UserContextHead; + + // + // Walk the list of active contexts checking for RID matches + // + + NextEntry = Head->Flink; + + while (NextEntry != Head) { + NextContext = CONTAINING_RECORD( + NextEntry, + SAMP_OBJECT, + ContextListEntry + ); + + if ( (Rid == NextContext->TypeBody.User.Rid) && + (NextContext->Valid == TRUE) + ) { + NextContext->Valid = FALSE; + + SampDiagPrint( CONTEXT_TRACKING, ("SAM: Invalidating user context 0x%lx : <%wZ>, handle = 0x%lx\n", NextContext, &NextContext->RootName, NextContext->RootKey)); + + if (NextContext->RootKey != INVALID_HANDLE_VALUE) { + + SampDiagPrint( CONTEXT_TRACKING, ("SAM: Closing handle 0x%lx\n", NextContext->RootKey)); + + IgnoreStatus = NtClose(NextContext->RootKey); + ASSERT(NT_SUCCESS(IgnoreStatus)); + NextContext->RootKey = INVALID_HANDLE_VALUE; + } + } + + NextEntry = NextEntry->Flink; + } +} + + +VOID +SampInvalidateContextListKeys( + IN PLIST_ENTRY Head, + IN BOOLEAN Close + ) + +/*++ + +Routine Description: + + Marks all registry handles invalid in the contexts in the passed list. + Used after a registry hive refresh. + +Arguments: + + Close : If TRUE the registry handles are closed before invalidation + +Return Value: + + None. + +--*/ +{ + NTSTATUS IgnoreStatus; + PLIST_ENTRY NextEntry; + + SAMTRACE("SampInvalidateContextListKeys"); + + NextEntry = Head->Flink; + + while (NextEntry != Head) { + + PSAMP_OBJECT NextContext; + + NextContext = CONTAINING_RECORD( + NextEntry, + SAMP_OBJECT, + ContextListEntry + ); + + SampDiagPrint( CONTEXT_TRACKING, ("SAM: Invalidating key for context 0x%lx : <%wZ>, handle = 0x%lx\n", NextContext, &NextContext->RootName, NextContext->RootKey)); + + if (Close && (NextContext->RootKey != INVALID_HANDLE_VALUE)) { + + SampDiagPrint( CONTEXT_TRACKING, ("SAM: Closing handle 0x%lx\n", NextContext->RootKey)); + + IgnoreStatus = NtClose( NextContext->RootKey ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + NextContext->RootKey = INVALID_HANDLE_VALUE; + + NextEntry = NextEntry->Flink; + } +} + + + + +#ifdef SAMP_DIAGNOSTICS +VOID +SampDumpContext( + IN PSAMP_OBJECT Context + ) + + +/*++ + +Routine Description: + + This service prints out info on a context to debugger + +Arguments: + + Context - a context + +Return Value: + + None. + + + +--*/ +{ + PSTR Type; + + switch (Context->ObjectType) { + case SampServerObjectType: + Type = "S"; + break; + case SampDomainObjectType: + if (Context == SampDefinedDomains[Context->DomainIndex].Context) { + Type = "d"; + } else { + Type = "D"; + } + break; + case SampUserObjectType: + Type = "U"; + break; + case SampAliasObjectType: + Type = "A"; + break; + case SampGroupObjectType: + Type = "G"; + break; + } + + KdPrint(("%s 0x%8x %2d 0x%8x %s %s %s %wZ\n", + Type, + Context, + Context->ReferenceCount, + Context->RootKey, + Context->MarkedForDelete ? "D": " ", + Context->Valid ? " ": "NV", + Context->TrustedClient ? "TC": " ", + &Context->RootName + )); +} + + +VOID +SampDumpContexts( + VOID + ) + +/*++ + +Routine Description: + + Prints out info on all contexts + +Arguments: + + +Return Value: + + None. + +--*/ +{ + PLIST_ENTRY NextEntry; + PLIST_ENTRY Head; + ULONG Servers; + ULONG Domains; + ULONG Domain0Users; + ULONG Domain0Aliases; + ULONG Domain0Groups; + ULONG Domain1Users; + ULONG Domain1Aliases; + ULONG Domain1Groups; + + Domains = 0; + Servers = 0; + + Head = &SampContextListHead; + NextEntry = Head->Flink; + while (NextEntry != Head) { + + PSAMP_OBJECT NextContext; + + NextContext = CONTAINING_RECORD( + NextEntry, + SAMP_OBJECT, + ContextListEntry + ); + + switch (NextContext->ObjectType) { + case SampServerObjectType: + (Servers)++; + break; + case SampDomainObjectType: + (Domains)++; + break; + default: + ASSERT(FALSE); + break; + } + + SampDumpContext(NextContext); + + NextEntry = NextEntry->Flink; + } + + + + + Domain0Users = 0; + Head = &SampDefinedDomains[0].UserContextHead; + NextEntry = Head->Flink; + while (NextEntry != Head) { + + PSAMP_OBJECT NextContext; + + (Domain0Users) ++; + + NextContext = CONTAINING_RECORD( + NextEntry, + SAMP_OBJECT, + ContextListEntry + ); + + ASSERT (NextContext->ObjectType == SampUserObjectType); + + SampDumpContext(NextContext); + + NextEntry = NextEntry->Flink; + } + + + Domain1Users = 0; + Head = &SampDefinedDomains[1].UserContextHead; + NextEntry = Head->Flink; + while (NextEntry != Head) { + + PSAMP_OBJECT NextContext; + + (Domain1Users) ++; + + NextContext = CONTAINING_RECORD( + NextEntry, + SAMP_OBJECT, + ContextListEntry + ); + + ASSERT (NextContext->ObjectType == SampUserObjectType); + + SampDumpContext(NextContext); + + NextEntry = NextEntry->Flink; + } + + + Domain0Groups = 0; + Head = &SampDefinedDomains[0].GroupContextHead; + NextEntry = Head->Flink; + while (NextEntry != Head) { + + PSAMP_OBJECT NextContext; + + (Domain0Groups) ++; + + NextContext = CONTAINING_RECORD( + NextEntry, + SAMP_OBJECT, + ContextListEntry + ); + + ASSERT (NextContext->ObjectType == SampGroupObjectType); + + SampDumpContext(NextContext); + + NextEntry = NextEntry->Flink; + } + + + Domain1Groups = 0; + Head = &SampDefinedDomains[1].GroupContextHead; + NextEntry = Head->Flink; + while (NextEntry != Head) { + + PSAMP_OBJECT NextContext; + + (Domain1Groups) ++; + + NextContext = CONTAINING_RECORD( + NextEntry, + SAMP_OBJECT, + ContextListEntry + ); + + ASSERT (NextContext->ObjectType == SampGroupObjectType); + + SampDumpContext(NextContext); + + NextEntry = NextEntry->Flink; + } + + + Domain0Aliases = 0; + Head = &SampDefinedDomains[0].AliasContextHead; + NextEntry = Head->Flink; + while (NextEntry != Head) { + + PSAMP_OBJECT NextContext; + + (Domain0Aliases) ++; + + NextContext = CONTAINING_RECORD( + NextEntry, + SAMP_OBJECT, + ContextListEntry + ); + + ASSERT (NextContext->ObjectType == SampAliasObjectType); + + SampDumpContext(NextContext); + + NextEntry = NextEntry->Flink; + } + + + Domain1Aliases = 0; + Head = &SampDefinedDomains[1].AliasContextHead; + NextEntry = Head->Flink; + while (NextEntry != Head) { + + PSAMP_OBJECT NextContext; + + (Domain1Aliases) ++; + + NextContext = CONTAINING_RECORD( + NextEntry, + SAMP_OBJECT, + ContextListEntry + ); + + ASSERT (NextContext->ObjectType == SampAliasObjectType); + + SampDumpContext(NextContext); + + NextEntry = NextEntry->Flink; + } + + + KdPrint(("SAM: Active untrusted contexts = %d\n", SampActiveContextCount)); + KdPrint((" Server = %4d Domain = %4d\n", Servers, Domains)); + KdPrint((" Users0 = %4d Groups0 = %4d Aliases0 = %4d\n", Domain0Users, Domain0Aliases, Domain0Groups)); + KdPrint((" Users1 = %4d Groups1 = %4d Aliases1 = %4d\n", Domain1Users, Domain1Aliases, Domain1Groups)); + + + +} +#endif //SAMP_DIAGNOSTICS + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// private service Implementations // +// // +/////////////////////////////////////////////////////////////////////////////// + +VOID +SampAddNewValidContextAddress( + IN PSAMP_OBJECT NewContext + ) + + +/*++ + +Routine Description: + + This service adds the new context to the set of valid contexts. + + + THIS SERVICE MUST BE CALLED WITH THE SampLock HELD FOR WRITE ACCESS. + + +Arguments: + + NewContext - Pointer to the context block to be added to the set + of valid contexts. The ObjectType field of this context must + be set. + + +Return Value: + + None. + + + +--*/ +{ + SAMTRACE("SampAddNewValidContextAddress"); + + ASSERT( (NewContext->ObjectType == SampUserObjectType) || + (NewContext->ObjectType == SampGroupObjectType) || + (NewContext->ObjectType == SampAliasObjectType) || + (NewContext->ObjectType == SampDomainObjectType) || + (NewContext->ObjectType == SampServerObjectType) + ); + + + NewContext->Valid = TRUE; + + +} + + +NTSTATUS +SampValidateContextAddress( + IN PSAMP_OBJECT Context + ) + +/*++ + +Routine Description: + + This service checks to make sure a context is still valid. + + Note that even though RPC still thinks we have a context related + to a SAM_HANDLE, we may, in fact, have deleted it out from under + the user. Since there is no way to inform RPC of this, we must + suffer, and wait until RPC calls us (either with a call by the client + or to rundown the context handle). This sucks, but there apparently + isn't any other way around it. + + + + WARNING - IT IS ASSUMED THE CONTEXT WAS ONCE VALID. IT MAY HAVE + BEEN INVALIDATED, BUT IF YOU ARE CALLING THIS ROUTINE + IT BETTER STILL HAVE A NON-ZERO REFERENCE COUNT. THIS + COULD BE CHANGED IN THE FUTURE, BUT IT WOULD REQUIRE + KEEPING A LIST OF VALID DOMAINS AND PERFORMING THE BULK + OF THIS ROUTINE INSIDE A TRY-EXCEPT CLAUSE. YOU COULD + LOCATE THE CONTEXT'S DOMAIN (WHICH MIGHT ACCESS VIOLATE) + AND THEN MAKE SURE THAT DOMAIN IS VALID. THEN WALK THAT + DOMAIN'S LIST TO ENSURE THE USER OR GROUP IS VALID. + + THIS SERVICE MUST BE CALLED WITH THE SampLock HELD FOR WRITE ACCESS. + + +Arguments: + + Context - Pointer to the context block to be validated as still being + a valid context. The ObjectType field of this context must + be valid. + +Return Value: + + STATUS_SUCCESS - The context is still valid. + + STATUS_INVALID_HANDLE - The context is no longer valid and the handle + that caused the reference should be invalidated as well. When the + handle is invalidated, the context should be closed (deleted). + + STATUS_NO_SUCH_CONTEXT - This value is not yet returned by this routine. + It may be added in the future to distinguish between an attempt to + use a context that has been invalidated and an attempt to use a + context that doesn't exist. The prior being a legitimate condition, + the later representing a bug-check condition. + + + +--*/ +{ + + SAMTRACE("SampValidateContextAddress"); + + ASSERT( (Context->ObjectType == SampUserObjectType) || + (Context->ObjectType == SampGroupObjectType) || + (Context->ObjectType == SampAliasObjectType) || + (Context->ObjectType == SampDomainObjectType) || + (Context->ObjectType == SampServerObjectType) + ); + + + if (Context->Valid != TRUE) { + return(STATUS_INVALID_HANDLE); + + } + + return(STATUS_SUCCESS); + +} + +NTSTATUS +SampCheckIfObjectExists( + IN PSAMP_OBJECT Context + ) +/*++ + + Routine Description: + + Checks to see if the object exists in the DS/ Registry. + Will fill out the following information + + 1. If DS object its DSNAME, which must exist + 2. If Registry object, Open its Root Key and fill out + the handle of the Root Key in the registry. + + IMPORTANT NOTE: + + In the Registry Case of SAM once the key is open nobody can + delete the object. However in the DS case this is not so. Currently + we have only the DS Name of the Object in Hand, and we have no way + of Locking the Object, for access. Since this is the case + + Arguments: + Context -- Pointer to a Context block desribing the Object + Rid -- Rid of the desired Object + + Return Values: + STATUS_SUCCESS if everything succeeds + Error codes from Registry Manipulation / DsLayer. +--*/ + +{ + NTSTATUS NtStatus = STATUS_SUCCESS; + ULONG Rid; + + + + // + // Check wether the Object is located or not. + // Location is the process of finding out + // 1. DSNAME of the object if it is in the DS. An object with this + // DSNAME must exist. + // + // 2. The Root Key HANDLE of the Object if it is in the Registry + // + + if (!SampIsObjectLocated(Context)) { + + // + // No we first need to locate the Object. + // This is done by using the Rid for Account Objects + // For Domain Objects we already cache all the defined domains, so fill out + // From the Cache + // BUG: For Server Objects Don't know what to do. + // + + switch (Context->ObjectType) { + + case SampGroupObjectType: + SampDiagPrint( CONTEXT_TRACKING, ("SAM: Reopened group handle <%wZ>,", &Context->RootName)); + Rid = Context->TypeBody.Group.Rid; + break; + + case SampAliasObjectType: + SampDiagPrint( CONTEXT_TRACKING, ("SAM: Reopened alias handle <%wZ>,", &Context->RootName)); + Rid = Context->TypeBody.Alias.Rid; + break; + + case SampUserObjectType: + SampDiagPrint( CONTEXT_TRACKING, ("SAM: Reopened user handle <%wZ>,", &Context->RootName)); + Rid = Context->TypeBody.User.Rid; + break; + + case SampDomainObjectType: + + // + // Domain objects share the root key and the object name in the DS that + // we keep around in the in memory domain context for each domain + // + + + ASSERT(Context != SampDefinedDomains[Context->DomainIndex].Context); + + Context->RootKey = SampDefinedDomains[Context->DomainIndex].Context->RootKey; + Context->ObjectNameInDs = SampDefinedDomains[Context->DomainIndex].Context->ObjectNameInDs; + Context->ObjectNameInDs = SampDefinedDomains[Context->DomainIndex].Context->ObjectNameInDs; + + ASSERT(SampIsObjectLocated(Context)); + + SampDiagPrint( CONTEXT_TRACKING, ("SAM: Recopied domain context handle <%wZ>, 0x%lx\n", &Context->RootName, Context->RootKey)); + goto ObjectLocated; + + case SampServerObjectType: + + // + // Server objects share our global root key + // + + + Context->RootKey = SampKey; + ASSERT(SampIsObjectLocated(Context)); + + SampDiagPrint( CONTEXT_TRACKING, ("SAM: Recopied server context handle <%wZ>, 0x%lx\n", &Context->RootName, Context->RootKey)); + goto ObjectLocated; + } + + // + // Go open the appropriate account key/ or find object Name from RID + // + + NtStatus = SampLocateObject(Context, Rid); + +ObjectLocated: + ;; + + + + } + + return NtStatus; +} + + +BOOLEAN +SampIsObjectLocated( + IN PSAMP_OBJECT Context + ) +/*++ + + Description: + Checks if an object has been located in the DS or in the registry + An Object being Located implies the Following + 1. For a DS Object we have the DS Name + 2. For a Registry Object we have a Valid Open Registry Key for + the Object. + + Arguments: + Context -- Pointer to a Context block desribing the Object + + Return Values: + TRUE -- If Conditions above are satisfied + FALSE -- If Conditions above are not satisfied +--*/ +{ + if (IsDsObject(Context)) + return (Context->ObjectNameInDs != NULL); + else + return (Context->RootKey != INVALID_HANDLE_VALUE); +} + + +NTSTATUS +SampLocateObject( + IN PSAMP_OBJECT Context, + IN ULONG Rid + ) +/*++ + + Description: + Uses the Rid to find the Object in either the DS or + the Registry. + + + NOTE: + This routine is meaningful for Context's that represent + Account Objects Only. + + Arguments: + Context -- Pointer to a Context block desribing the Object + Rid -- Rid of the desired Object + + Return Values: + STATUS_SUCCESS if everything succeeds + Error codes from Registry Manipulation / DsLayer. +--*/ + +{ + + NTSTATUS Status = STATUS_SUCCESS; + PSAMP_OBJECT DomainContext = NULL; + OBJECT_ATTRIBUTES ObjectAttributes; + + + + // + // This routine can be called only for Account Objects + // + + ASSERT((Context->ObjectType == SampGroupObjectType) + || (Context->ObjectType == SampAliasObjectType) + || (Context->ObjectType == SampUserObjectType) + ); + + // + // Get the Domain Object, as we will need this to find out + // to find out in which domain we look for the Rid. + // + DomainContext = SampDefinedDomains[Context->DomainIndex].Context; + + // Now Make the Decision + if (IsDsObject(Context)) + { + // + // Object is in the DS + // + + // Look it up using the Rid + Status = SampDsLookupObjectByRid(DomainContext->ObjectNameInDs, Rid, &Context->ObjectNameInDs); + if (!NT_SUCCESS(Status)) + { + Context->ObjectNameInDs = NULL; + } + + } + else + { + // Object Should be in Registry + SetRegistryObject(Context); + InitializeObjectAttributes( + &ObjectAttributes, + &Context->RootName, + OBJ_CASE_INSENSITIVE, + SampKey, + NULL + ); + + SampDumpNtOpenKey((KEY_READ | KEY_WRITE), &ObjectAttributes, 0); + + // Try opening the Key + Status = RtlpNtOpenKey( + &Context->RootKey, + (KEY_READ | KEY_WRITE), + &ObjectAttributes, + 0 + ); + + if (!NT_SUCCESS(Status)) + { + Context->RootKey = INVALID_HANDLE_VALUE; + } + } + + return Status; + +} diff --git a/private/newsam2/server/dbgutil.c b/private/newsam2/server/dbgutil.c new file mode 100644 index 000000000..9739bcfb0 --- /dev/null +++ b/private/newsam2/server/dbgutil.c @@ -0,0 +1,1958 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + dbgutil.c + +Abstract: + + This file contains supplimental debugging and diagnostic routines for SAM. + + +Author: + + Chris Mayhall (ChrisMay) 04-Apr-1996 + +Environment: + + User Mode - Win32 + +Revision History: + + 04-Apr-1996 ChrisMay + Created. + 08-Apr-1996 ChrisMay + Added enumeration routines. + 15-Apr-1996 ChrisMay + Added query routines. + +--*/ + +// +// Includes +// + +#include <samsrvp.h> + + + +// +// Constants +// + +#define DBG_BUFFER_SIZE 4096 + + + +// +// Private Helper Routines +// + +VOID +SamIDebugOutput( + IN LPSTR DebugMessage + ) + +/*++ + +Routine Description: + + This routine displays a message on the debugger. + +Parameters: + + DebugMessage - Pointer to the message string. + +Return Values: + + None. + +--*/ + +{ + CHAR Buffer[DBG_BUFFER_SIZE]; + sprintf(Buffer, "%-30s", DebugMessage); + OutputDebugStringA(Buffer); +} + + + +VOID +SamIDebugFileLineOutput( + IN LPSTR FileName, + IN ULONG LineNumber + ) +{ + + CHAR Buffer[DBG_BUFFER_SIZE]; + sprintf(Buffer, "[File = %s Line = %lu]\n", FileName, LineNumber); + OutputDebugStringA(Buffer); +} + + + +VOID +wcstombsp( + IN LPSTR Destination, + IN LPWSTR Source, + IN ULONG Size + ) +{ + ULONG Index; + + for (Index = 0; Index < Size; Index++) + { + if (Source[Index] != L'\0') + { + Destination[Index] = (CHAR)(Source[Index]); + } + } + Destination[Size] = '\0'; +} + + + +VOID +SampDumpBinaryData( + PBYTE pData, + DWORD cbData + ) +{ + DWORD i; + BYTE AsciiLine[16]; + BYTE BinaryLine[16]; + CHAR Buffer[DBG_BUFFER_SIZE]; + + if (0 == cbData) + { + OutputDebugStringA("Zero-Length Data\n"); + return; + } + + if (cbData > DBG_BUFFER_SIZE) + { + OutputDebugStringA("ShowBinaryData - truncating display to 256 bytes\n"); + cbData = 256; + } + + for (; cbData > 0 ;) + { + for (i = 0; i < 16 && cbData > 0 ; i++, cbData--) + { + BinaryLine[i] = *pData; + (isprint(*pData)) ? (AsciiLine[i] = *pData) : (AsciiLine[i] = '.'); + pData++; + } + + if (i < 15) + { + for (; i < 16 ; i++) + { + BinaryLine[i] = ' '; + AsciiLine[i] = ' '; + } + } + + sprintf(Buffer, + "%02x %02x %02x %02x %02x %02x %02x %02x - %02x %02x %02x %02x %02x %02x %02x %02x\t", + BinaryLine[0], + BinaryLine[1], + BinaryLine[2], + BinaryLine[3], + BinaryLine[4], + BinaryLine[5], + BinaryLine[6], + BinaryLine[7], + BinaryLine[8], + BinaryLine[9], + BinaryLine[10], + BinaryLine[11], + BinaryLine[12], + BinaryLine[13], + BinaryLine[14], + BinaryLine[15]); + + OutputDebugStringA(Buffer); + + sprintf(Buffer, + "%c%c%c%c%c%c%c%c - %c%c%c%c%c%c%c%c\n", + AsciiLine[0], + AsciiLine[1], + AsciiLine[2], + AsciiLine[3], + AsciiLine[4], + AsciiLine[5], + AsciiLine[6], + AsciiLine[7], + AsciiLine[8], + AsciiLine[9], + AsciiLine[10], + AsciiLine[11], + AsciiLine[12], + AsciiLine[13], + AsciiLine[14], + AsciiLine[15]); + + OutputDebugStringA(Buffer); + } +} + + + +// +// Set Value Key Routines +// + +VOID +SamIDumpNtSetValueKey( + IN PUNICODE_STRING ValueName, + IN ULONG TitleIndex, + IN ULONG Type, + IN PVOID Data, + IN ULONG DataSize + ) +{ + CHAR Buffer[DBG_BUFFER_SIZE]; + + if (NULL != ValueName) + { + ANSI_STRING AnsiString; + + RtlUnicodeStringToAnsiString(&AnsiString, + ValueName, + TRUE); + + sprintf(Buffer, + "%s\n%-30s = %s\n", + "Set Value Key:", + "ValueName", + AnsiString.Buffer); + + RtlFreeAnsiString(&AnsiString); + } + else + { + sprintf(Buffer, + "%s\n%-30s = %s\n", + "Set Value Key:", + "ValueName", + NULL); + } + + OutputDebugStringA(Buffer); + + sprintf(Buffer, + "%-30s = %lu\n", + "TitleIndex", + TitleIndex); + + OutputDebugStringA(Buffer); + + sprintf(Buffer, + "%-30s = %lu\n", + "Type", + Type); + + OutputDebugStringA(Buffer); + + if (NULL != Data) + { + // BUG: Need a display routine for the data. + + sprintf(Buffer, + "%-30s = %s\n", + "Data", + "BINARY DATA"); + } + else + { + sprintf(Buffer, + "%-30s = %s\n", + "Data", + NULL); + } + + OutputDebugStringA(Buffer); + + sprintf(Buffer, + "%-30s = %lu\n\n", + "DataSize", + DataSize); + + OutputDebugStringA(Buffer); +} + + + +VOID +SamIDumpRtlpNtSetValueKey( + IN ULONG Type, + IN PVOID Data, + IN ULONG DataSize + ) +{ + CHAR Buffer[DBG_BUFFER_SIZE]; + + sprintf(Buffer, + "%s\n%-30s = %lu\n", + "Set Value Key:", + "Type", + Type); + + OutputDebugStringA(Buffer); + + if (NULL != Data) + { + // BUG: Need a display routine for the data. + + sprintf(Buffer, + "%-30s = %s\n", + "Data", + "ARRAY OF ULONG"); + } + else + { + sprintf(Buffer, + "%-30s = %s\n", + "Data", + NULL); + } + + OutputDebugStringA(Buffer); + + sprintf(Buffer, + "%-30s = %lu\n\n", + "DataSize", + DataSize); + + OutputDebugStringA(Buffer); +} + + + +// +// Query Routines +// + +VOID +SamIDumpNtQueryKey( + IN KEY_INFORMATION_CLASS KeyInformationClass, + IN PVOID KeyInformation, + IN ULONG Length, + IN PULONG ResultLength + ) +{ + CHAR Buffer[DBG_BUFFER_SIZE]; + + // This routine dumps the parameters after returning from the NtQueryKey + // routine. The KeyInformation is a PVOID buffer that is mapped to one of + // the KeyInformationClass structures. The case-label values correspond + // to the values in the KEY_INFORMATION_CLASS enum. Note that the Length + // parameter is used to specify the buffer length. This is done because + // the data-length member inside each structure seems to always be set to + // zero--why? + + sprintf(Buffer, + "%s\n%-30s = %lu\n", + "Query Key:", + "KeyInformationClass", + KeyInformationClass); + + OutputDebugStringA(Buffer); + + if (NULL != KeyInformation) + { + CHAR BufferTmp[DBG_BUFFER_SIZE]; + PKEY_BASIC_INFORMATION KeyBasicInformation; + PKEY_FULL_INFORMATION KeyFullInformation; + PKEY_NODE_INFORMATION KeyNodeInformation; + + switch(KeyInformationClass) + { + case 0: // KeyBasicInformation + // Basic information's Name member is an array of WCHAR. + KeyBasicInformation = KeyInformation; + wcstombsp(BufferTmp, + KeyBasicInformation->Name, + wcslen(KeyBasicInformation->Name)); + sprintf(Buffer, + "%s\n%-30s = 0x%lx:0xlx\n%-30s = %lu\n%-30s = %lu\n%-30s = %lu\n%-30s = %s\n", + "KeyInformation:", + "LastWriteTime", + KeyBasicInformation->LastWriteTime.HighPart, + KeyBasicInformation->LastWriteTime.LowPart, + "TitleIndex", + KeyBasicInformation->TitleIndex, + "NameLength", + KeyBasicInformation->NameLength, + "Name", + // BufferTmp); + "BINARY DATA FOLLOWS:"); + + // Displaying the data as an LPWSTR doesn't work, so just dump the + // bytes. + + OutputDebugStringA(Buffer); + + SampDumpBinaryData((PBYTE)KeyBasicInformation->Name, + // KeyBasicInformation->NameLength); + Length); + + break; + + case 1: // KeyNodeInformation + // Node information's Name member is an array of WCHAR. + KeyNodeInformation = KeyInformation; + wcstombsp(BufferTmp, + (LPWSTR)KeyNodeInformation->Name, + wcslen((LPWSTR)KeyNodeInformation->Name)); + sprintf(Buffer, + "%s\n%-30s = 0x%lx:0x%lx\n%-30s = %lu\n%-30s = %lu\n%-30s = %lu\n%-30s = %lu\n%-30s = %s\n", + "KeyInformation:", + "LastWriteTime", + KeyNodeInformation->LastWriteTime.HighPart, + KeyNodeInformation->LastWriteTime.LowPart, + "TitleIndex", + KeyNodeInformation->TitleIndex, + "ClassOffset", + KeyNodeInformation->ClassOffset, + "ClassLength", + KeyNodeInformation->ClassLength, + "NameLength", + KeyNodeInformation->NameLength, + "Name", + // BufferTmp); + "BINARY DATA FOLLOWS:"); + + // Displaying the data as an LPWSTR doesn't work, so just dump the + // bytes. + + OutputDebugStringA(Buffer); + + SampDumpBinaryData((PBYTE)KeyNodeInformation->Name, + // KeyNodeInformation->NameLength); + Length); + + break; + + case 2: // KeyFullInformation + + KeyFullInformation = KeyInformation; + + // Full information's Class member is an array of WCHAR. + + // wcstombsp(BufferTmp, + // KeyFullInformation->Class, + // wcslen(KeyFullInformation->Class)); + + sprintf(Buffer, + "%s\n%-30s = 0x%lx:0x%lx\n%-30s = %lu\n%-30s = %lu\n%-30s = %lu\n%-30s = %lu\n%-30s = %lu\n%-30s = %lu\n%-30s = %lu\n%-30s = %lu\n%-30s = %lu\n%-30s = %s\n", + "KeyInformation:", + "LastWriteTime", + KeyFullInformation->LastWriteTime.HighPart, + KeyFullInformation->LastWriteTime.LowPart, + "TitleIndex", + KeyFullInformation->TitleIndex, + "ClassOffset", + KeyFullInformation->ClassOffset, + "ClassLength", + KeyFullInformation->ClassLength, + "SubKeys", + KeyFullInformation->SubKeys, + "MaxNameLen", + KeyFullInformation->MaxNameLen, + "MaxClassLen", + KeyFullInformation->MaxClassLen, + "Values", + KeyFullInformation->Values, + "MaxValueNameLen", + KeyFullInformation->MaxValueNameLen, + "MaxValueDataLen", + KeyFullInformation->MaxValueDataLen, + "Class", + // BufferTmp); + "BINARY DATA FOLLOWS:"); + + // Displaying the data as an LPWSTR doesn't work, so just dump the + // bytes. + + OutputDebugStringA(Buffer); + + SampDumpBinaryData((PBYTE)KeyFullInformation->Class, + // KeyFullInformation->ClassLength); + Length); + + break; + + default: + break; + } + } + else + { + sprintf(Buffer, + "%-30s = %s\n", + "KeyInformation", + NULL); + + OutputDebugStringA(Buffer); + } + + // OutputDebugStringA(Buffer); + + sprintf(Buffer, + "%-30s = %lu\n", + "Length", + Length); + + OutputDebugStringA(Buffer); + + if (NULL != ResultLength) + { + sprintf(Buffer, + "%-30s = %lu\n\n", + "ResultLength", + ResultLength); + } + else + { + sprintf(Buffer, + "%-30s = %s\n\n", + "ResultLength", + NULL); + } + + OutputDebugStringA(Buffer); +} + + + +VOID +SamIDumpNtQueryValueKey( + IN PUNICODE_STRING ValueName, + IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, + IN PVOID KeyValueInformation, + IN ULONG Length, + IN PULONG ResultLength + ) +{ + CHAR Buffer[DBG_BUFFER_SIZE]; + + // This routine dumps the parameters after returning from NtQueryValueKey + // routine. The KeyValueInformation is a PVOID buffer that is mapped to + // one of the KeyInformationClass structures. The case-label values corre- + // spond to the values in the KEY_VALUE_INFORMATION_CLASS enum. + + if (NULL != ValueName) + { + ANSI_STRING AnsiString; + + RtlUnicodeStringToAnsiString(&AnsiString, + ValueName, + TRUE); + + sprintf(Buffer, + "%s\n%-30s = %s\n", + "Query Value Key:", + "ValueName", + AnsiString.Buffer); + + RtlFreeAnsiString(&AnsiString); + } + else + { + sprintf(Buffer, + "%s\n%-30s = %s\n", + "Query Value Key:", + "ValueName", + NULL); + } + + OutputDebugStringA(Buffer); + + sprintf(Buffer, + "%-30s = %lu\n", + "KeyValueInformationClass", + KeyValueInformationClass); + + OutputDebugStringA(Buffer); + + if (NULL != KeyValueInformation) + { + CHAR BufferTmp[DBG_BUFFER_SIZE]; + PKEY_VALUE_BASIC_INFORMATION KeyValueBasicInformation; + PKEY_VALUE_FULL_INFORMATION KeyValueFullInformation; + PKEY_VALUE_PARTIAL_INFORMATION KeyValuePartialInformation; + + switch(KeyValueInformationClass) + { + case 0: // KeyValueBasicInformation + // Basic information's Name member is an array of WCHAR. + KeyValueBasicInformation = KeyValueInformation; + wcstombsp(BufferTmp, + KeyValueBasicInformation->Name, + wcslen(KeyValueBasicInformation->Name)); + sprintf(Buffer, + "%-30s = %lu\n%-30s = %lu\n%-30s = %lu\n%-30s = %s\n", + "TitleIndex", + KeyValueBasicInformation->TitleIndex, + "Type", + KeyValueBasicInformation->Type, + "NameLength", + KeyValueBasicInformation->NameLength, + "Name", + //BufferTmp); + "BINARY DATA FOLLOWS:"); + + // Displaying the data as an LPWSTR doesn't work, so just dump the + // bytes. + + OutputDebugStringA(Buffer); + SampDumpBinaryData((PBYTE)KeyValueBasicInformation->Name, + KeyValueBasicInformation->NameLength); + break; + + case 1: // KeyValueFullInformation + // Full information's Name member is an array of WCHAR. + KeyValueFullInformation = KeyValueInformation; + wcstombsp(BufferTmp, + KeyValueFullInformation->Name, + wcslen(KeyValueFullInformation->Name)); + sprintf(Buffer, + "%-30s = %lu\n%-30s = %lu\n%-30s = %lu\n%-30s = %lu\n%-30s = %lu\n%-30s = %s\n", + "TitleIndex", + KeyValueFullInformation->TitleIndex, + "Type", + KeyValueFullInformation->Type, + "DataOffset", + KeyValueFullInformation->DataOffset, + "DataLength", + KeyValueFullInformation->DataLength, + "NameLength", + KeyValueFullInformation->NameLength, + "Name", + //BufferTmp); + "BINARY DATA FOLLOWS:"); + + // Displaying the data as an LPWSTR doesn't work, so just dump the + // bytes. + + OutputDebugStringA(Buffer); + SampDumpBinaryData((PBYTE)KeyValueFullInformation->Name, + KeyValueFullInformation->NameLength); + break; + + case 2: // KeyValuePartialInformation + + KeyValuePartialInformation = KeyValueInformation; + + // Partial information's Data member is an array of UCHAR. + + sprintf(Buffer, + "%-30s = %lu\n%-30s = %lu\n%-30s = %lu\n%-30s = %s\n", + "TitleIndex", + KeyValuePartialInformation->TitleIndex, + "Type", + KeyValuePartialInformation->Type, + "DataLength", + KeyValuePartialInformation->DataLength, + "Data", + // KeyValuePartialInformation->Data); + "BINARY DATA FOLLOWS:"); + + OutputDebugStringA(Buffer); + + // First, dump the buffer as a raw byte stream. + + SampDumpBinaryData(KeyValuePartialInformation->Data, + KeyValuePartialInformation->DataLength); + + // Then, determine object type and dump the data in SAM struct + // format. + + switch(KeyValuePartialInformation->Type) + { + + case 0: // Server Object + break; + + case 1: // Domain Object + break; + + case 2: // Group Object + break; + + case 3: // Alias Object + + // Dump the alias object's fixed attributes. + + // BUG: What about Basic and Full information? + + // SampDumpPSAMP_V1_FIXED_LENGTH_ALIAS( + // KeyValuePartialInformation->Data, + // 0); + + // Dump the alias object's variable attribute array. + + //SampDumpAliasVariableAttributeArray( + // KeyValuePartialInformation->Data); + + // Dump the alias object's Variable attributes. + + + break; + + case 4: // User Object + break; + + default: // Unknown Object + break; + + } + + break; + + default: + break; + } + } + else + { + sprintf(Buffer, + "%-30s = %s\n", + "KeyValueInformation", + NULL); + + OutputDebugStringA(Buffer); + } + + // OutputDebugStringA(Buffer); + + sprintf(Buffer, + "%-30s = %lu\n", + "Length", + Length); + + OutputDebugStringA(Buffer); + + if (NULL != ResultLength) + { + sprintf(Buffer, + "%-30s = %lu\n\n", + "ResultLength", + ResultLength); + } + else + { + sprintf(Buffer, + "%-30s = %s\n\n", + "ResultLength", + NULL); + } + + OutputDebugStringA(Buffer); +} + + + +VOID +SamIDumpRtlpNtQueryValueKey( + IN PULONG KeyValueType, + IN PVOID KeyValue, + IN PULONG KeyValueLength, + IN PLARGE_INTEGER LastWriteTime + ) +{ + CHAR Buffer[DBG_BUFFER_SIZE]; + + if (NULL != KeyValueType) + { + sprintf(Buffer, + "%s\n%-30s = 0x%lx\n", + "Query Value Key:", + "KeyValueType", + *KeyValueType); + } + else + { + sprintf(Buffer, + "%s\n%-30s = %s\n", + "Query Value Key:", + "KeyValueType", + NULL); + } + + OutputDebugStringA(Buffer); + + if (NULL != KeyValue) + { + SampDumpBinaryData((PBYTE)KeyValue, *KeyValueLength); + OutputDebugStringA("\n"); + } + else + { + sprintf(Buffer, + "%-30s = %s\n", + "KeyValue", + NULL); + } + + OutputDebugStringA(Buffer); + + if (NULL != KeyValueLength) + { + sprintf(Buffer, + "%-30s = %lu\n", + "KeyValueLength", + *KeyValueLength); + } + else + { + sprintf(Buffer, + "%-30s = %s\n", + "KeyValueLength", + NULL); + } + + OutputDebugStringA(Buffer); + + if (NULL != LastWriteTime) + { + sprintf(Buffer, + "%-30s = 0x%lx:0x%lx\n\n", + "LastWriteTime", + LastWriteTime->HighPart, + LastWriteTime->LowPart); + } + else + { + sprintf(Buffer, + "%-30s = %s\n\n", + "LastWriteTime", + NULL); + } + + OutputDebugStringA(Buffer); +} + + + +// +// Enumeration Routines +// + +VOID +SamIDumpNtEnumerateKey( + IN ULONG Index, + IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, + IN PVOID KeyValueInformation, + IN ULONG Length, + IN PULONG ResultLength + ) +{ + CHAR Buffer[DBG_BUFFER_SIZE]; + + sprintf(Buffer, + "%s\n%-30s = %lu\n", + "Enumerate Key:", + "Index", + Index); + + OutputDebugStringA(Buffer); + + sprintf(Buffer, + "%-30s = %lu\n", + "KeyValueInformationClass", + KeyValueInformationClass); + + OutputDebugStringA(Buffer); + + if (NULL != KeyValueInformation) + { + CHAR BufferTmp[DBG_BUFFER_SIZE]; + PKEY_VALUE_BASIC_INFORMATION KeyValueBasicInformation; + PKEY_VALUE_FULL_INFORMATION KeyValueFullInformation; + PKEY_VALUE_PARTIAL_INFORMATION KeyValuePartialInformation; + + switch(KeyValueInformationClass) + { + case 0: // KeyValueBasicInformation + // Full information's Name member is an array of WCHAR. + KeyValueBasicInformation = KeyValueInformation; + wcstombsp(BufferTmp, + KeyValueBasicInformation->Name, + wcslen(KeyValueBasicInformation->Name)); + sprintf(Buffer, + "%-30s = %lu\n%-30s = %lu\n%-30s = %lu\n%-30s = %s\n", + "TitleIndex", + KeyValueBasicInformation->TitleIndex, + "Type", + KeyValueBasicInformation->Type, + "NameLength", + KeyValueBasicInformation->NameLength, + "Name", + BufferTmp); + break; + + case 1: // KeyValueFullInformation + // Full information's Name member is an array of WCHAR. + KeyValueFullInformation = KeyValueInformation; + wcstombsp(BufferTmp, + KeyValueFullInformation->Name, + wcslen(KeyValueFullInformation->Name)); + sprintf(Buffer, + "%-30s = %lu\n%-30s = %lu\n%-30s = %lu\n%-30s = %lu\n%-30s = %lu\n%-30s = %s\n", + "TitleIndex", + KeyValueFullInformation->TitleIndex, + "Type", + KeyValueFullInformation->Type, + "DataOffset", + KeyValueFullInformation->DataOffset, + "DataLength", + KeyValueFullInformation->DataLength, + "NameLength", + KeyValueFullInformation->NameLength, + "Name", + BufferTmp); + break; + + case 2: // KeyValuePartialInformation + // Partial information's Data member is an array of UCHAR. + KeyValuePartialInformation = KeyValueInformation; + + // BUG: Need a display routine for the data. + + sprintf(Buffer, + "%-30s = %lu\n%-30s = %lu\n%-30s = %lu\n%-30s = %s\n", + "TitleIndex", + KeyValuePartialInformation->TitleIndex, + "Type", + KeyValuePartialInformation->Type, + "DataLength", + KeyValuePartialInformation->DataLength, + "Data", + // KeyValuePartialInformation->Data); + "BINARY DATA FOLLOWS:"); + OutputDebugStringA(Buffer); + SampDumpBinaryData(KeyValuePartialInformation->Data, + KeyValuePartialInformation->DataLength); + break; + + default: + break; + } + } + else + { + sprintf(Buffer, + "%-30s = %s\n", + "KeyValueInformation", + NULL); + + OutputDebugStringA(Buffer); + } + + // OutputDebugStringA(Buffer); + + sprintf(Buffer, + "%-30s = %lu\n", + "Length", + Length); + + OutputDebugStringA(Buffer); + + if (NULL != ResultLength) + { + sprintf(Buffer, + "%-30s = %lu\n\n", + "ResultLength", + ResultLength); + } + else + { + sprintf(Buffer, + "%-30s = %s\n\n", + "ResultLength", + NULL); + } + + OutputDebugStringA(Buffer); +} + + + +VOID +SamIDumpRtlpNtEnumerateSubKey( + IN PUNICODE_STRING SubKeyName, + IN PSAM_ENUMERATE_HANDLE Index, + IN LARGE_INTEGER LastWriteTime + ) +{ + CHAR Buffer[DBG_BUFFER_SIZE]; + ANSI_STRING AnsiString; + + RtlUnicodeStringToAnsiString(&AnsiString, + SubKeyName, + TRUE); + + sprintf(Buffer, + "%s\n%-30s = %s\n%-30s = %lu\n%-30s = 0x%lx:0x%lx\n\n", + "Enumerate SubKey:", + "SubKeyName", + AnsiString.Buffer, + "Index", + *Index, + "LastWriteTime", + LastWriteTime.HighPart, + LastWriteTime.LowPart); + + OutputDebugStringA(Buffer); + + RtlFreeAnsiString(&AnsiString); +} + + + +// +// Security Descriptor Component Routines +// + +VOID +SampDumpSecurityDescriptorSubAuthority( + IN UCHAR SubAuthorityCount, + IN ULONG SubAuthority[] + ) +{ + CHAR Buffer[DBG_BUFFER_SIZE]; + INT Count = (INT)SubAuthorityCount; + INT Index = 0; + + for (Index = 0; Index < Count; Index++) + { + sprintf(Buffer, + "%s\n%-30s = %lu\n", + "SubAuthority:", + "SubAuthority Element", + SubAuthority[Index]); + } + + OutputDebugStringA(Buffer); +} + + + +VOID +SampDumpSecurityDescriptorOwner( + IN PISID Owner + ) +{ + CHAR Buffer[DBG_BUFFER_SIZE]; + + sprintf(Buffer, + "%s\n%-30s = %du\n%-30s = %du\n%-30s = %du%du%du%du%du%du\n", + "Owner:", + "Revision", + Owner->Revision, + "SubAuthorityCount", + Owner->SubAuthorityCount, + "IdentifierAuthority", + Owner->IdentifierAuthority.Value[0], + Owner->IdentifierAuthority.Value[1], + Owner->IdentifierAuthority.Value[2], + Owner->IdentifierAuthority.Value[3], + Owner->IdentifierAuthority.Value[4], + Owner->IdentifierAuthority.Value[5]); + + OutputDebugStringA(Buffer); + + SampDumpSecurityDescriptorSubAuthority(Owner->SubAuthorityCount, + Owner->SubAuthority); +} + + + +VOID +SampDumpSecurityDescriptorGroup( + IN PISID Group + ) +{ + CHAR Buffer[DBG_BUFFER_SIZE]; + + sprintf(Buffer, + "%s\n%-30s = %du\n%-30s = %du\n%-30s = %du%du%du%du%du%du\n", + "Group:", + "Revision", + Group->Revision, + "SubAuthorityCount", + Group->SubAuthorityCount, + "IdentifierAuthority", + Group->IdentifierAuthority.Value[0], + Group->IdentifierAuthority.Value[1], + Group->IdentifierAuthority.Value[2], + Group->IdentifierAuthority.Value[3], + Group->IdentifierAuthority.Value[4], + Group->IdentifierAuthority.Value[5]); + + OutputDebugStringA(Buffer); + + SampDumpSecurityDescriptorSubAuthority(Group->SubAuthorityCount, + Group->SubAuthority); +} + + + +// +// ACL Routines +// + +VOID +SampDumpAcl( + IN PACL Acl + ) +{ + CHAR Buffer[DBG_BUFFER_SIZE]; + + sprintf(Buffer, + "%s\n%-30s = %du\n%-30s = %du\n%-30s = %du\n%-30s = %du\n%-30s = %du\n", + "Acl:", + "AclRevision", + Acl->AclRevision, + "Sbz1", + Acl->Sbz1, + "ACL Size", + Acl->AclSize, + "ACE Count", + Acl->AceCount, + "Sbz2", + Acl->Sbz2); + + OutputDebugStringA(Buffer); +} + + + +// +// Security Descriptor Routines +// + +VOID +SampDumpSecurityDescriptor( + IN PISECURITY_DESCRIPTOR SecurityDescriptor + ) +{ + CHAR Buffer[DBG_BUFFER_SIZE]; + + if (NULL != SecurityDescriptor) + { + // Note that the SECURITY_DESCRIPTOR is intended to be treated as an + // opaque blob so that future changes are compatible with previous + // versions. + + // Revision is actually represented as a UCHAR, but it is displayed as + // a "du" in this routine. + + sprintf(Buffer, + "%s\n%-30s = %du\n%-30s = %du\n%-30s = %du\n%-30s = 0x%lx\n", + "SecurityDescriptor:", + "Revision", + SecurityDescriptor->Revision, + "Sbz1", + SecurityDescriptor->Sbz1, + "Control", + SecurityDescriptor->Control); + + OutputDebugStringA(Buffer); + + SampDumpSecurityDescriptorOwner(SecurityDescriptor->Owner); + SampDumpSecurityDescriptorGroup(SecurityDescriptor->Group); + SampDumpAcl(SecurityDescriptor->Sacl); + SampDumpAcl(SecurityDescriptor->Dacl); + } + else + { + sprintf(Buffer, "%-30s = %s\n", "SecurityDescriptor:", NULL); + OutputDebugStringA(Buffer); + } + +} + + + +// +// Quality Of Service Routines +// + +VOID +SampDumpSecurityQualityOfService( + IN PSECURITY_QUALITY_OF_SERVICE SecurityQualityOfService + ) +{ + CHAR Buffer[DBG_BUFFER_SIZE]; + + if (NULL != SecurityQualityOfService) + { + sprintf(Buffer, + "%s\n%-30s = %lu\n%-30s = %du\n%-30s = %du\n%-30s = %du\n", + "SecurityQualityOfService:", + "Length", + SecurityQualityOfService->Length, + "ImpersonationLevel", + SecurityQualityOfService->ImpersonationLevel, + "ContextTrackingMode", + SecurityQualityOfService->ContextTrackingMode, + "EffectiveOnly", + SecurityQualityOfService->EffectiveOnly); + } + else + { + sprintf(Buffer, "%-30s = %s\n", "SecurityQualityOfService:", NULL); + } + + OutputDebugStringA(Buffer); +} + + + +// +// Object Attribute Routines +// + +VOID +SampDumpObjectAttributes( + IN POBJECT_ATTRIBUTES ObjectAttributes + ) +{ + CHAR Buffer[DBG_BUFFER_SIZE]; + ANSI_STRING AnsiString; + + RtlUnicodeStringToAnsiString(&AnsiString, + ObjectAttributes->ObjectName, + TRUE); + + sprintf(Buffer, + "%s\n%-30s = %lu\n%-30s = 0x%lx\n%-30s = %s\n%-30s = 0x%lx\n", + "ObjectAttributes:", + "Length", + ObjectAttributes->Length, + "RootDirectory Handle", + ObjectAttributes->RootDirectory, + "ObjectName", + AnsiString.Buffer, + "Attributes", + ObjectAttributes->Attributes); + + OutputDebugStringA(Buffer); + + RtlFreeAnsiString(&AnsiString); + + SampDumpSecurityDescriptor(ObjectAttributes->SecurityDescriptor); + SampDumpSecurityQualityOfService(ObjectAttributes->SecurityQualityOfService); +} + + + +// +// Open Key Routines +// + +VOID +SamIDumpNtOpenKey( + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN ULONG Options + ) +{ + + CHAR Buffer[DBG_BUFFER_SIZE]; + + sprintf(Buffer, + "%s\n%-30s = 0x%lx\n%-30s = 0x%lx\n", + "Open Registry Key:", + "DesiredAccess", + DesiredAccess, + "Options", + Options); + + OutputDebugStringA(Buffer); + + SampDumpObjectAttributes(ObjectAttributes); + + OutputDebugStringA("\n"); +} + + + +// +// V1_0A Routines +// + +VOID +SampDumpPSAMP_V1_FIXED_LENGTH_SERVER( + IN PVOID NewValue, + IN ULONG NewValueLength + ) +{ + CHAR Buffer[DBG_BUFFER_SIZE]; + PSAMP_V1_FIXED_LENGTH_SERVER TempBuffer = NewValue; + + sprintf(Buffer, + "%s\n%-30s = %lu\n", + "SAMP_V1_FIXED_LENGTH_SERVER Buffer:", + "RevisionLevel", + TempBuffer->RevisionLevel); + + OutputDebugStringA(Buffer); + + OutputDebugStringA("\n"); +} + + + +VOID +SampDumpPSAMP_V1_0A_FIXED_LENGTH_DOMAIN( + IN PVOID NewValue, + IN ULONG NewValueLength + ) +{ + CHAR Buffer[DBG_BUFFER_SIZE]; + PSAMP_V1_0A_FIXED_LENGTH_DOMAIN TempBuffer = NewValue; + + sprintf(Buffer, + + "%s\n%-30s = %lu\n%-30s = %lu\n%-30s = %l:%lu\n%-30s = %l:%lu\n%-30s = %l:%lu\n%-30s = %l:%lu\n%-30s = %l:%lu\n%-30s = %l:%lu\n%-30s = %l:%lu\n%-30s = %l:%lu\n%-30s = %lu\n%-30s = %lu\n%-30s = %du\n\ +%-30s = %du\n%-30s = %du\n%-30s = %du\n%-30s = %du\n%-30s = %du\n\n", + + "SAMP_V1_OA_FIXED_LENGTH_DOMAIN Buffer:", + + "Revision", + TempBuffer->Revision, + + "Unused1", + TempBuffer->Unused1, + + "Creation Time", + TempBuffer->CreationTime.HighPart, + TempBuffer->CreationTime.LowPart, + + "Modified Count", + TempBuffer->ModifiedCount.HighPart, + TempBuffer->ModifiedCount.LowPart, + + "MaxPasswordAge", + TempBuffer->MaxPasswordAge.HighPart, + TempBuffer->MaxPasswordAge.LowPart, + + "MinPasswordAge", + TempBuffer->MinPasswordAge.HighPart, + TempBuffer->MinPasswordAge.LowPart, + + "ForceLogoff", + TempBuffer->ForceLogoff.HighPart, + TempBuffer->ForceLogoff.LowPart, + + "LockoutDuration", + TempBuffer->LockoutDuration.HighPart, + TempBuffer->LockoutDuration.LowPart, + + "LockoutObservationWindow", + TempBuffer->LockoutObservationWindow.HighPart, + TempBuffer->LockoutObservationWindow.LowPart, + + "ModifiedCountAtLastPromotion", + TempBuffer->ModifiedCountAtLastPromotion.HighPart, + TempBuffer->ModifiedCountAtLastPromotion.LowPart, + + "NextRid", + TempBuffer->NextRid, + + "PasswordProperties", + TempBuffer->PasswordProperties, + + "MinPasswordLength", + TempBuffer->MinPasswordLength, + + "PasswordHistoryLength", + TempBuffer->PasswordHistoryLength, + + "LockoutThreshold", + TempBuffer->LockoutThreshold, + + "ServerState", + TempBuffer->ServerState, + + "ServerRole", + TempBuffer->ServerRole, + + "UasCompatibilityRequired", + TempBuffer->UasCompatibilityRequired); + + OutputDebugStringA(Buffer); +} + + + +// +// Variable Length Attribute Routines +// + +VOID +SampDumpSAMP_VARIABLE_LENGTH_ATTRIBUTE( + IN PVOID NewValue, + IN ULONG NewValueLength + ) +{ + CHAR Buffer[DBG_BUFFER_SIZE]; + BYTE *TempBuffer = NewValue; + + #if 0 + + // BUGBUG: Incoming buffer is a self-describing binary stream. + + PSAMP_VARIABLE_LENGTH_ATTRIBUTE TempBuffer = NewValue; + + sprintf(Buffer, + "%s\n%-30s = %l\n%-30s = %lu\n%-30s = %lu\n\n", + "SAMP_VARIABLE_LENGTH_ATTRIBUTE Buffer:", + "Offset", + TempBuffer->Offset, + "Length", + TempBuffer->Length, + "Qualifier", + TempBuffer->Qualifier); + + #endif + + sprintf(Buffer, + "%-30s = %lu\n\n", + "NewValueLength", + NewValueLength); + + OutputDebugStringA(Buffer); +} + + + +// +// Fixed Length Attribute Routines +// + +#if 0 + +VOID +SampDumpPSAMP_V1_FIXED_LENGTH_ALIAS( + IN PVOID NewValue, + IN ULONG NewValueLength + ) +{ + CHAR Buffer[DBG_BUFFER_SIZE]; + PSAMP_V1_FIXED_LENGTH_ALIAS TempBuffer = NewValue; + + // BUG: NewValueLength is unnecessary for this fixed length attribute. + + sprintf(Buffer, + "%s\n%-30s = %lu\n\n", + "SAMP_V1_FIXED_LENGTH_ALIAS Buffer:", + "RelativeId", + TempBuffer->RelativeId); + + OutputDebugStringA(Buffer); +} + + + +#endif + +VOID +SampDumpPSAMP_V1_0A_FIXED_LENGTH_GROUP( + IN PVOID NewValue, + IN ULONG NewValueLength + ) +{ + CHAR Buffer[DBG_BUFFER_SIZE]; + PSAMP_V1_FIXED_LENGTH_GROUP TempBuffer = NewValue; + + sprintf(Buffer, + "%s\n%-30s = %lu\n%-30s = %lu\n%-30s = %lu\n\n", + "SAMP_V1_OA_FIXED_LENGTH_GROUP Buffer:", + "RelativeId", + TempBuffer->RelativeId, + "Attributes", + TempBuffer->Attributes, + "AdminGroup", + TempBuffer->AdminGroup); + + OutputDebugStringA(Buffer); +} + + + +VOID +SampDumpPSAMP_V1_0A_FIXED_LENGTH_USER( + IN PVOID NewValue, + IN ULONG NewValueLength + ) +{ + CHAR Buffer[DBG_BUFFER_SIZE]; + PSAMP_V1_0A_FIXED_LENGTH_USER TempBuffer = NewValue; + + sprintf(Buffer, + + "%s\n%-30s = %lu\n%-30s = %lu\n%-30s = %l:%lu\n%-30s = %l:%lu\n%-30s = %l:%lu\n%-30s = %l:%lu\n%-30s = %l:%lu\n%-30s = %lu\n%-30s = %lu\n%-30s = %lu\n%-30s = %du\n%-30s = %du\n\ +%-30s = %lu\n%-30s = %lu\n%-30s = %lu\n%-30s = %lu\n%-30s = %lu\n\n", + + "SAMP_V1_OA_FIXED_LENGTH_USER Buffer:", + + "Revision", + TempBuffer->Revision, + + "Unused1", + TempBuffer->Unused1, + + "LastLogon", + TempBuffer->LastLogon.HighPart, + TempBuffer->LastLogon.LowPart, + + "LastLogoff", + TempBuffer->LastLogoff.HighPart, + TempBuffer->LastLogoff.LowPart, + + "PasswordLastSet", + TempBuffer->PasswordLastSet.HighPart, + TempBuffer->PasswordLastSet.LowPart, + + "AccountExpires", + TempBuffer->AccountExpires.HighPart, + TempBuffer->AccountExpires.LowPart, + + "LastBadPasswordTime", + TempBuffer->LastBadPasswordTime.HighPart, + TempBuffer->LastBadPasswordTime.LowPart, + + "UserId", + TempBuffer->UserId, + + "PrimaryGroupId", + TempBuffer->PrimaryGroupId, + + "UserAccountControl", + TempBuffer->UserAccountControl, + + "CountryCode", + TempBuffer->CountryCode, + + "CodePage", + TempBuffer->CodePage, + + "BadPasswordCount", + TempBuffer->BadPasswordCount, + + "LogonCount", + TempBuffer->LogonCount, + + "AdminCount", + TempBuffer->AdminCount, + + "Unused2", + TempBuffer->Unused2, + + "OperatorCount", + TempBuffer->OperatorCount); + + OutputDebugStringA(Buffer); +} + + + +VOID +SampDumpSampFixedBufferAddress( + IN PVOID NewValue, + IN ULONG NewValueLength + ) +{ + CHAR Buffer[DBG_BUFFER_SIZE]; + + sprintf(Buffer, + "%-30s = 0x%lx\n%-30s = %lu\n%-30s = %s\n", + "BufferAddress", + NewValue, + "BufferLength", + NewValueLength, + "Buffer", + "BINARY DATA FOLLOWS:"); + + OutputDebugStringA(Buffer); + + SampDumpBinaryData((PBYTE)NewValue, NewValueLength); + OutputDebugStringA("\n"); +} + + + +VOID +SampDumpBuffer( + IN PVOID BufferAddress, + IN ULONG BufferLength + ) + +/*++ + +Routine Description: + + This routine dumps the address and length of the attribute buffer. + +Parameters: + + BufferAddress - self explanatory. + + BufferLength - self explanatory. + +Return Values: + + None. + +--*/ + +{ + CHAR Buffer[DBG_BUFFER_SIZE]; + + sprintf(Buffer, + "%-30s = 0x%lx\n", + "BufferAddress", + BufferAddress); + + OutputDebugStringA(Buffer); + + sprintf(Buffer, + "%-30s = %lu\n", + "BufferLength", + BufferLength); + + OutputDebugStringA(Buffer); + + sprintf(Buffer, + "%-30s = %s\n\n", + "Buffer Content", + BufferAddress); + + OutputDebugStringA(Buffer); +} + + + +// +// RXact Routines +// + +VOID +SampDumpRXactLog( + IN PRTL_RXACT_LOG TransactionLog + ) + +/*++ + +Routine Description: + + This routine dumps a (registry) transaction log structure. + +Parameters: + + TransactionLog - Pointer to the transaction log. + +Return Values: + + None. + +--*/ + +{ + CHAR Buffer[DBG_BUFFER_SIZE]; + + sprintf(Buffer, + "%s\n%-30s = %lu\n%-30s = %lu\n%-30s = %lu\n", + "Transaction Log:", + "OperationCount", + TransactionLog->OperationCount, + "LogSize", + TransactionLog->LogSize, + "LogSizeInUse", + TransactionLog->LogSizeInUse); + + OutputDebugStringA(Buffer); +} + + + +VOID +SampDumpRXactContext( + IN PRTL_RXACT_CONTEXT TransactionContext + ) + +/*++ + +Routine Description: + + This routine dumps a (registry) transaction context. + +Parameters: + + TransactionContext - Pointer to the transaction context. + +Return Values: + + None. + +--*/ + +{ + CHAR Buffer[DBG_BUFFER_SIZE]; + + sprintf(Buffer, + "%s\n%-30s = 0x%lx\n%-30s = 0x%lx\n%-30s = %d\n", + "Transaction Context:", + "RootRegistryKey Handle", + TransactionContext->RootRegistryKey, + "RXactKey Handle", + TransactionContext->RXactKey, + "HandlesValid", + TransactionContext->HandlesValid); + + OutputDebugStringA(Buffer); + + SampDumpRXactLog(TransactionContext->RXactLog); +} + + + +VOID +SampDumpRXactOperation( + IN RTL_RXACT_OPERATION Operation + ) + +/*++ + +Routine Description: + + This routine dumps an operation value. + +Parameters: + + Operation - The operation value. + +Return Values: + + None. + +--*/ + +{ + CHAR Buffer[DBG_BUFFER_SIZE]; + + sprintf(Buffer, + "%-30s = %lu\n", + "Operation", + Operation); + + OutputDebugStringA(Buffer); +} + + + +VOID +SampDumpSubKeyNameAndKey( + IN PUNICODE_STRING SubKeyName, + IN HANDLE KeyHandle + ) + +/*++ + +Routine Description: + + This routine dumps the registry root name and root-key handle value. + +Parameters: + + SubKeyName - Pointer to a counted string that is the root name. + + KeyHandle - Handle of the registry's root key. + +Return Values: + + None. + +--*/ + +{ + CHAR Buffer[DBG_BUFFER_SIZE]; + ANSI_STRING AnsiString; + + RtlUnicodeStringToAnsiString(&AnsiString, SubKeyName, TRUE); + + sprintf(Buffer, + "%-30s = %s\n", + "SubKeyName", + AnsiString.Buffer); + + OutputDebugStringA(Buffer); + + RtlFreeAnsiString(&AnsiString); + + sprintf(Buffer, + "%-30s = 0x%lx\n", + "KeyHandle", + KeyHandle); + + OutputDebugStringA(Buffer); +} + + + +VOID +SampDumpAttributeName( + IN PUNICODE_STRING AttributeName + ) + +/*++ + +Routine Description: + + This routine dumps a combined-attribute name. + +Parameters: + + AttributeName - Pointer to the string containing the name. + +Return Values: + + None. + +--*/ + +{ + CHAR Buffer[DBG_BUFFER_SIZE]; + ANSI_STRING AnsiString; + + RtlUnicodeStringToAnsiString(&AnsiString, AttributeName, TRUE); + + sprintf(Buffer, + "%-30s = %s\n", + "AttributeName", + AnsiString.Buffer); + + OutputDebugStringA(Buffer); + + RtlFreeAnsiString(&AnsiString); +} + + + +VOID +SampDumpKeyType( + IN ULONG RegistryKeyType + ) + +/*++ + +Routine Description: + + This routine dumps a registry key type. + +Parameters: + + RegistryKeyType - self explanatory. + +Return Values: + + None. + +--*/ + +{ + CHAR Buffer[DBG_BUFFER_SIZE]; + + sprintf(Buffer, + "%-30s = %lu\n", + "RegistryKeyType", + RegistryKeyType); + + OutputDebugStringA(Buffer); +} + + + +VOID +SamIDumpRXact( + IN PRTL_RXACT_CONTEXT TransactionContext, + IN RTL_RXACT_OPERATION Operation, + IN PUNICODE_STRING SubKeyName, + IN HANDLE KeyHandle, + IN PUNICODE_STRING AttributeName, + IN ULONG RegistryKeyType, + IN PVOID NewValue, + IN ULONG NewValueLength, + IN ULONG NewValueType + ) + +/*++ + +Routine Description: + + This routine dumps a (registry) transaction, just before the call to + RtlAddAttributeActionToRXact. + +Parameters: + + (See individual output routines above for the descriptions) + +Return Values: + + None. + +--*/ + +{ + SampDumpRXactContext(TransactionContext); + SampDumpRXactOperation(Operation); + SampDumpSubKeyNameAndKey(SubKeyName, KeyHandle); + SampDumpAttributeName(AttributeName); + SampDumpKeyType(RegistryKeyType); + + switch(NewValueType) + { + case FIXED_LENGTH_SERVER_FLAG: + SampDumpPSAMP_V1_FIXED_LENGTH_SERVER(NewValue, NewValueLength); + break; + + case FIXED_LENGTH_DOMAIN_FLAG: + SampDumpPSAMP_V1_0A_FIXED_LENGTH_DOMAIN(NewValue, NewValueLength); + break; + + case FIXED_LENGTH_ALIAS_FLAG: + // SampDumpPSAMP_V1_FIXED_LENGTH_ALIAS(NewValue, NewValueLength); + break; + + case FIXED_LENGTH_GROUP_FLAG: + SampDumpPSAMP_V1_0A_FIXED_LENGTH_GROUP(NewValue, NewValueLength); + break; + + case FIXED_LENGTH_USER_FLAG: + SampDumpPSAMP_V1_0A_FIXED_LENGTH_USER(NewValue, NewValueLength); + break; + + case VARIABLE_LENGTH_ATTRIBUTE_FLAG: + SampDumpSAMP_VARIABLE_LENGTH_ATTRIBUTE(NewValue, NewValueLength); + break; + + case FixedBufferAddressFlag: + SampDumpSampFixedBufferAddress(NewValue, NewValueLength); + break; + + default: + SampDumpBuffer(NewValue, NewValueLength); + break; + } +} + + + diff --git a/private/newsam2/server/dbgutilp.h b/private/newsam2/server/dbgutilp.h new file mode 100644 index 000000000..62d7fed06 --- /dev/null +++ b/private/newsam2/server/dbgutilp.h @@ -0,0 +1,183 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + dbgutilp.h + +Abstract: + + This file contains definitions private to the SAM server program. + +Author: + + Chris Mayhall (ChrisMay) 04-Apr-1996 + +Environment: + + User Mode - Win32 + +Revision History: + + 04-Apr-1996 ChrisMay + Created. + +--*/ + +#ifndef _DBGUTILP_ +#define _DBGUTILP_ + +// Setting SAM_DUMP to 0 disables private debugging output and traces in the +// samsrv.dll; setting it to 1 enables debugging output. The dump routines +// display parameters associated with registry calls throughout samsrv.dll. +// +// NOTE: At some future date, we may want to define per-module dump/trace +// flags to reduce the amount of output going to the debugger. + +#define SAMP_DUMP 0 +#define SAMP_TRACE 0 + +#if (SAMP_DBG == 1) + +#define SampDebugOutput(a) SamIDebugOutput(a) +#define SampDumpNtSetValueKey(a, b, c, d, e) SamIDebugFileLineOutput(__FILE__, __LINE__); SamIDumpNtSetValueKey(a, b, c, d, e) +#define SampDumpRtlpNtSetValueKey(a, b, c) SamIDebugFileLineOutput(__FILE__, __LINE__); SamIDumpRtlpNtSetValueKey(a, b, c) +#define SampDumpNtEnumerateKey(a, b, c, d, e) SamIDebugFileLineOutput(__FILE__, __LINE__); SamIDumpNtEnumerateKey(a, b, c, d, e) +#define SampDumpRtlpNtEnumerateSubKey(a, b, c) SamIDebugFileLineOutput(__FILE__, __LINE__); SamIDumpRtlpNtEnumerateSubKey(a, b, c) +#define SampDumpNtOpenKey(a, b, c) SamIDebugFileLineOutput(__FILE__, __LINE__); SamIDumpNtOpenKey(a, b, c); +#define SampDumpNtQueryKey(a, b, c, d) SamIDebugFileLineOutput(__FILE__, __LINE__); SamIDumpNtQueryKey(a, b, c, d) +#define SampDumpNtQueryValueKey(a, b, c, d, e) SamIDebugFileLineOutput(__FILE__, __LINE__); SamIDumpNtQueryValueKey(a, b, c, d, e) +#define SampDumpRtlpNtQueryValueKey(a, b, c, d) SamIDebugFileLineOutput(__FILE__, __LINE__); SamIDumpRtlpNtQueryValueKey(a, b, c, d) +#define SampDumpRXact(a, b, c, d, e, f, g, h, i) SamIDebugFileLineOutput(__FILE__, __LINE__); SamIDumpRXact(a, b, c, d, e, f, g, h, i) + +#else + +#define SampDebugOutput(a) +#define SampDumpNtSetValueKey(a, b, c, d, e) +#define SampDumpRtlpNtSetValueKey(a, b, c) +#define SampDumpNtEnumerateKey(a, b, c, d, e) +#define SampDumpRtlpNtEnumerateSubKey(a, b, c) +#define SampDumpNtOpenKey(a, b, c) +#define SampDumpNtQueryKey(a, b, c, d) +#define SampDumpNtQueryValueKey(a, b, c, d, e) +#define SampDumpRtlpNtQueryValueKey(a, b, c, d) +#define SampDumpRXact(a, b, c, d, e, f, g, h, i) + +#endif + +// Similarly, SAMP_TRACE will cause a per-routine trace of all routines that +// are internal to samsrv.dll to be displayed on the debugger. This is useful +// when a particular call sequence needs to be understood, such as during +// system boot up time, adding an account, etc. + +#if (SAMP_TRACE == 1) + +#define SAMTRACE(a) SamIDebugOutput(a); SamIDebugFileLineOutput(__FILE__, __LINE__); + +#else + +#define SAMTRACE(a) + +#endif + +// These debugging flags are used in the dumping routines to help identify +// what kind of SAM object is being dumped. + +#define FIXED_LENGTH_SERVER_FLAG 0 +#define FIXED_LENGTH_DOMAIN_FLAG 1 +#define FIXED_LENGTH_ALIAS_FLAG 2 +#define FIXED_LENGTH_GROUP_FLAG 3 +#define FIXED_LENGTH_USER_FLAG 4 +#define VARIABLE_LENGTH_ATTRIBUTE_FLAG 5 +#define FixedBufferAddressFlag 6 + +VOID +SamIDebugOutput( + IN LPSTR DebugMessage + ); + +VOID +SamIDebugFileLineOutput( + IN LPSTR FileName, + IN ULONG LineNumber + ); + +VOID +SamIDumpNtSetValueKey( + IN PUNICODE_STRING ValueName, + IN ULONG TitleIndex, + IN ULONG Type, + IN PVOID Data, + IN ULONG DataSize + ); + +VOID +SamIDumpRtlpNtSetValueKey( + IN ULONG Type, + IN PVOID Data, + IN ULONG DataSize + ); + +VOID +SamIDumpNtEnumerateKey( + IN ULONG Index, + IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, + IN PVOID KeyValueInformation, + IN ULONG Length, + IN PULONG ResultLength + ); + +VOID +SamIDumpRtlpNtEnumerateSubKey( + IN PUNICODE_STRING SubKeyName, + IN PSAM_ENUMERATE_HANDLE Index, + IN LARGE_INTEGER LastWriteTime + ); + +VOID +SamIDumpNtOpenKey( + IN ACCESS_MASK DesiredAccess, + IN POBJECT_ATTRIBUTES ObjectAttributes, + IN ULONG Options + ); + +VOID +SamIDumpNtQueryKey( + IN KEY_INFORMATION_CLASS KeyInformationClass, + IN PVOID KeyInformation, + IN ULONG Length, + IN PULONG ResultLength + ); + +VOID +SamIDumpNtQueryValueKey( + IN PUNICODE_STRING ValueName, + IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, + IN PVOID KeyValueInformation, + IN ULONG Length, + IN PULONG ResultLength + ); + +VOID +SamIDumpRtlpNtQueryValueKey( + IN PULONG KeyValueType, + IN PVOID KeyValue, + IN PULONG KeyValueLength, + IN PLARGE_INTEGER LastWriteTime + ); + +VOID +SamIDumpRXact( + IN PRTL_RXACT_CONTEXT TransactionContext, + IN RTL_RXACT_OPERATION Operation, + IN PUNICODE_STRING SubKeyName, + IN HANDLE KeyHandle, + IN PUNICODE_STRING AttributeName, + IN ULONG RegistryKeyType, + IN PVOID NewValue, + IN ULONG NewValueLength, + IN ULONG NewValueType + ); + +#endif //_DBGUTILP_ diff --git a/private/newsam2/server/dirs b/private/newsam2/server/dirs new file mode 100644 index 000000000..772cd2820 --- /dev/null +++ b/private/newsam2/server/dirs @@ -0,0 +1,23 @@ +!IF 0 + +Copyright (c) 1989 - 1996 Microsoft Corporation + +Abstract: + + This file specifies the sub-directories of the current directory that + contain component makefiles. + +Author: + + Chris Mayhall (ChrisMay) 03-Jul-1996 + +History: + + Chris Mayhall (ChrisMay) 03-Jul-1996 + Reorganized makefiles for NTDS builds and ntds.exe. + +NOTE: Commented description of this file is in \nt\oak\bin\dirs.tpl + +!ENDIF + +DIRS=dll exe diff --git a/private/newsam2/server/display.c b/private/newsam2/server/display.c new file mode 100644 index 000000000..f6a46f434 --- /dev/null +++ b/private/newsam2/server/display.c @@ -0,0 +1,5847 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + display.c + +Abstract: + + This file contains services for maintaining the cached display + information. + + The information is stored in multiple tables because there are + multiple formats it must be returned in. The tables maintained + include: + + AccountsByRid - includes all user and global group accounts + by RID. Aliases may be added to this list at some time + in the future. + + NormalUsersByName - Normal user accounts, sorted by name. + + MachinesByName - Machine user accounts, sorted by name. + + InterDomainByName - Interdomain trust accounts, sorted by + name. + + GroupsByName - Global group accounts, sorted by name. + + + Any time an entry is placed in or removed from one of "ByName" + tables, it is also placed in or removed from the "ByRid" table. + + User and machine accounts are added to the display cache in one + operation. So, there is a single boolean flag indicating whether + or not these tables are valid. The groups are maintained in a + separate table, and so there is another flag indicating whether + or not that table is valid. + + The Rid table is only valid if both the group table and the + user/machine tables are valid. + + + +Author: + + Dave Chalmers (Davidc) 1-April-1992 + +Environment: + + User Mode - Win32 + +Revision History: + + +--*/ + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include <samsrvp.h> + + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// private service prototypes // +// // +/////////////////////////////////////////////////////////////////////////////// + + +NTSTATUS +SampCreateDisplayInformation ( + DOMAIN_DISPLAY_INFORMATION DisplayType + ); + + +VOID +SampDeleteDisplayInformation ( + PSAMP_DOMAIN_DISPLAY_INFORMATION DisplayInformation, + SAMP_OBJECT_TYPE ObjectType + ); + +NTSTATUS +SampRetrieveDisplayInfoFromDisk( + PSAMP_DOMAIN_DISPLAY_INFORMATION DisplayInformation, + SAMP_OBJECT_TYPE ObjectType + ); + +NTSTATUS +SampAddDisplayAccount ( + PSAMP_DOMAIN_DISPLAY_INFORMATION DisplayInformation, + SAMP_OBJECT_TYPE ObjectType, + PSAMP_ACCOUNT_DISPLAY_INFO AccountInfo + ); + +NTSTATUS +SampDeleteDisplayAccount ( + PSAMP_DOMAIN_DISPLAY_INFORMATION DisplayInformation, + SAMP_OBJECT_TYPE ObjectType, + PSAMP_ACCOUNT_DISPLAY_INFO AccountInfo + ); + +NTSTATUS +SampUpdateDisplayAccount( + PSAMP_DOMAIN_DISPLAY_INFORMATION DisplayInformation, + SAMP_OBJECT_TYPE ObjectType, + PSAMP_ACCOUNT_DISPLAY_INFO AccountInfo + ); + +NTSTATUS +SampTallyTableStatistics ( + PSAMP_DOMAIN_DISPLAY_INFORMATION DisplayInformation, + SAMP_OBJECT_TYPE ObjectType + ); + +NTSTATUS +SampEmptyGenericTable2 ( + PRTL_GENERIC_TABLE2 Table, + BOOLEAN FreeElements + ); + +PVOID +SampGenericTable2Allocate ( + CLONG BufferSize + ); + +VOID +SampGenericTable2Free ( + PVOID Buffer + ); + +RTL_GENERIC_COMPARE_RESULTS +SampCompareUserNodeByName ( + PVOID Node1, + PVOID Node2 + ); + +RTL_GENERIC_COMPARE_RESULTS +SampCompareMachineNodeByName ( // Also used for Interdomain trust accounts + PVOID Node1, + PVOID Node2 + ); + +RTL_GENERIC_COMPARE_RESULTS +SampCompareGroupNodeByName ( + PVOID Node1, + PVOID Node2 + ); + +RTL_GENERIC_COMPARE_RESULTS +SampCompareNodeByRid ( + PVOID Node1, + PVOID Node2 + ); + +NTSTATUS +SampInitializeUserInfo( + PSAMP_ACCOUNT_DISPLAY_INFO AccountInfo, + PDOMAIN_DISPLAY_USER *UserInfo, + BOOLEAN CopyData + ); + +NTSTATUS +SampInitializeMachineInfo( // Also used for Interdomain trust accounts + PSAMP_ACCOUNT_DISPLAY_INFO AccountInfo, + PDOMAIN_DISPLAY_MACHINE *MachineInfo, + BOOLEAN CopyData + ); + +NTSTATUS +SampInitializeGroupInfo( + PSAMP_ACCOUNT_DISPLAY_INFO AccountInfo, + PDOMAIN_DISPLAY_GROUP *GroupInfo, + BOOLEAN CopyData + ); + +NTSTATUS +SampDuplicateUserInfo( + PDOMAIN_DISPLAY_USER Destination, + PDOMAIN_DISPLAY_USER Source, + ULONG Index + ); + +NTSTATUS +SampDuplicateMachineInfo( // Also used for Interdomain trust accounts + PDOMAIN_DISPLAY_MACHINE Destination, + PDOMAIN_DISPLAY_MACHINE Source, + ULONG Index + + ); + +NTSTATUS +SampDuplicateGroupInfo( + PDOMAIN_DISPLAY_GROUP Destination, + PDOMAIN_DISPLAY_GROUP Source, + ULONG Index + ); + +NTSTATUS +SampDuplicateOemUserInfo( + PDOMAIN_DISPLAY_OEM_USER Destination, + PDOMAIN_DISPLAY_USER Source, + ULONG Index + ); + +NTSTATUS +SampDuplicateOemGroupInfo( + PDOMAIN_DISPLAY_OEM_GROUP Destination, + PDOMAIN_DISPLAY_GROUP Source, + ULONG Index + ); + + +VOID +SampFreeUserInfo( + PDOMAIN_DISPLAY_USER UserInfo + ); + +VOID +SampFreeMachineInfo( + PDOMAIN_DISPLAY_MACHINE MachineInfo + ); + +VOID +SampFreeGroupInfo( + PDOMAIN_DISPLAY_GROUP GroupInfo + ); + +VOID +SampFreeOemUserInfo( + PDOMAIN_DISPLAY_OEM_USER UserInfo + ); + +VOID +SampFreeOemGroupInfo( + PDOMAIN_DISPLAY_OEM_GROUP GroupInfo + ); + +VOID +SampSwapUserInfo( + PDOMAIN_DISPLAY_USER Info1, + PDOMAIN_DISPLAY_USER Info2 + ); + +VOID +SampSwapMachineInfo( // Also used for Interdomain trust accounts + PDOMAIN_DISPLAY_MACHINE Info1, + PDOMAIN_DISPLAY_MACHINE Info2 + ); + +VOID +SampSwapGroupInfo( + PDOMAIN_DISPLAY_GROUP Info1, + PDOMAIN_DISPLAY_GROUP Info2 + ); + +ULONG +SampBytesRequiredUserNode ( + PDOMAIN_DISPLAY_USER Node + ); + +ULONG +SampBytesRequiredMachineNode ( // Also used for Interdomain trust accounts + PDOMAIN_DISPLAY_MACHINE Node + ); + +ULONG +SampBytesRequiredGroupNode ( + PDOMAIN_DISPLAY_GROUP Node + ); + +ULONG +SampBytesRequiredOemUserNode ( + PDOMAIN_DISPLAY_OEM_USER Node + ); + +ULONG +SampBytesRequiredOemGroupNode ( + PDOMAIN_DISPLAY_OEM_GROUP Node + ); + + +VOID +SampDisplayDiagnostic( VOID ); + +VOID +SampDisplayDiagEnumRids( VOID ); + + + +LONG +SampCompareDisplayStrings( + IN PUNICODE_STRING String1, + IN PUNICODE_STRING String2, + IN BOOLEAN IgnoreCase + ); + + +// +// Macros for deciding whether an account is: +// +// A normal user account +// +// A machine account +// +// An Interdomain trust account +// +// Included in the display cache +// +// + +#define USER_ACCOUNT(AccountControl) ((AccountControl & \ + (USER_NORMAL_ACCOUNT | \ + USER_TEMP_DUPLICATE_ACCOUNT)) != 0) + +#define MACHINE_ACCOUNT(AccountControl) ((AccountControl & \ + (USER_WORKSTATION_TRUST_ACCOUNT | \ + USER_SERVER_TRUST_ACCOUNT)) != 0) + + +#define INTERDOMAIN_ACCOUNT(AccountControl) (((AccountControl) & \ + (USER_INTERDOMAIN_TRUST_ACCOUNT)) != 0) + + +#define DISPLAY_ACCOUNT(AccountControl) (USER_ACCOUNT(AccountControl) || \ + MACHINE_ACCOUNT(AccountControl) || \ + INTERDOMAIN_ACCOUNT(AccountControl)) + + + +// +// Test to see if Rid table is valid +// +// BOOLEAN +// SampRidTableValid( IN ULONG DomainIndex ) +// + +#define SampRidTableValid(DI) ( \ + (SampDefinedDomains[DI].DisplayInformation.UserAndMachineTablesValid) && \ + (SampDefinedDomains[DI].DisplayInformation.GroupTableValid) \ + ) + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Private data types // +// // +/////////////////////////////////////////////////////////////////////////////// + + +// +// All entries in the display cache are expected to start with this +// data structure. +// + +typedef struct _SAMP_DISPLAY_ENTRY_HEADER { + + // + // The index field plays two roles. Within the generic table, + // it is used to indicate which type of account this is. The + // valid types are: SAM_USER_ACCOUNT, SAM_GLOBAL_GROUP_ACCOUNT, + // or SAM_LOCAL_GROUP_ACCOUNT. + // + // Otherwise, this field is filled in just before being returned + // to query and other client calls. + // + + + ULONG Index; + + + // + // The RID of the account + // + + ULONG Rid; + +} SAMP_DISPLAY_ENTRY_HEADER, *PSAMP_DISPLAY_ENTRY_HEADER; + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Module-wide variables // +// // +/////////////////////////////////////////////////////////////////////////////// + + +LCID SampSystemDefaultLCID; + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// RPC exported routines // +// // +/////////////////////////////////////////////////////////////////////////////// + + +NTSTATUS +SamrQueryDisplayInformation ( + IN SAMPR_HANDLE DomainHandle, + IN DOMAIN_DISPLAY_INFORMATION DisplayInformation, + IN ULONG Index, + IN ULONG EntriesRequested, + IN ULONG PreferredMaximumLength, + OUT PULONG TotalAvailable, + OUT PULONG TotalReturned, + OUT PSAMPR_DISPLAY_INFO_BUFFER Buffer + ) + +/*++ + +Routine Description: + + Thin wrapper around SamrQueryDisplayInformation3(). + + Provided for compatibility with down-level clients. + +--*/ +{ + return( SamrQueryDisplayInformation3( + DomainHandle, + DisplayInformation, + Index, + EntriesRequested, + PreferredMaximumLength, + TotalAvailable, + TotalReturned, + Buffer + ) ); +} + +NTSTATUS +SamrQueryDisplayInformation2 ( + IN SAMPR_HANDLE DomainHandle, + IN DOMAIN_DISPLAY_INFORMATION DisplayInformation, + IN ULONG Index, + IN ULONG EntriesRequested, + IN ULONG PreferredMaximumLength, + OUT PULONG TotalAvailable, + OUT PULONG TotalReturned, + OUT PSAMPR_DISPLAY_INFO_BUFFER Buffer + ) + +/*++ + +Routine Description: + + Thin wrapper around SamrQueryDisplayInformation3(). + + Provided for compatibility with down-level clients. + +--*/ +{ + return( SamrQueryDisplayInformation3( + DomainHandle, + DisplayInformation, + Index, + EntriesRequested, + PreferredMaximumLength, + TotalAvailable, + TotalReturned, + Buffer + ) ); +} + +NTSTATUS +SamrQueryDisplayInformation3 ( + IN SAMPR_HANDLE DomainHandle, + IN DOMAIN_DISPLAY_INFORMATION DisplayInformation, + IN ULONG Index, + IN ULONG EntriesRequested, + IN ULONG PreferredMaximumLength, + OUT PULONG TotalAvailable, + OUT PULONG TotalReturned, + OUT PSAMPR_DISPLAY_INFO_BUFFER Buffer + ) + +/*++ + +Routine Description: + + This routine provides fast return of information commonly + needed to be displayed in user interfaces. + + NT User Interface has a requirement for quick enumeration of SAM + accounts for display in list boxes. (Replication has similar but + broader requirements.) + + The netui listboxes all contain similar information. e.g: + + o AccountControl, the bits that identify the account type, + eg, HOME, REMOTE, SERVER, WORKSTATION, etc. + + o Logon name (machine name for computers) + + o Full name (not used for computers) + + o Comment (admin comment for users) + + SAM maintains this data locally in two sorted indexed cached + lists identified by infolevels. + + o DomainDisplayUser: HOME and REMOTE user accounts only + + o DomainDisplayMachine: SERVER and WORKSTATION accounts only + + Note that trust accounts, groups, and aliases are not in either of + these lists. + + + Added for NT1.0A - + + o Group enumeration has been added in NT1.0A + with the following characteristic: + + We did not change the RPC interface ID. This allows + callers to continue to call down-level servers. However, + down-level servers will return an error if they passed + this information level. + + o OEM string info levels were added for jimh (Chicago). These + info levels dramatically reduce the memory needed to query + the limited information that Chicago is interested in. + + +Parameters: + + DomainHandle - A handle to an open domain for DOMAIN_LIST_ACCOUNTS. + + DisplayInformation - Indicates which information is to be enumerated. + + Index - The index of the first entry to be retrieved. + + PreferedMaximumLength - A recommended upper limit to the number of + bytes to be returned. The returned information is allocated by + this routine. + + TotalAvailable - Total number of bytes availabe in the specified info + class. + + TotalReturned - Number of bytes actually returned for this call. Zero + indicates there are no entries with an index as large as that + specified. + + ReturnedEntryCount - Number of entries returned by this call. Zero + indicates there are no entries with an index as large as that + specified. + + + Buffer - Receives a pointer to a buffer containing a (possibly) + sorted list of the requested information. This buffer is + allocated by this routine and contains the following + structure: + + + DomainDisplayMachine --> An array of ReturnedEntryCount elements + of type DOMAIN_DISPLAY_USER. This is + followed by the bodies of the various + strings pointed to from within the + DOMAIN_DISPLAY_USER structures. + + DomainDisplayMachine --> An array of ReturnedEntryCount elements + of type DOMAIN_DISPLAY_MACHINE. This is + followed by the bodies of the various + strings pointed to from within the + DOMAIN_DISPLAY_MACHINE structures. + + DomainDisplayGroup --> An array of ReturnedEntryCount elements + of type DOMAIN_DISPLAY_GROUP. This is + followed by the bodies of the various + strings pointed to from within the + DOMAIN_DISPLAY_GROUP structures. + + DomainDisplayOemUser --> An array of ReturnedEntryCount elements + of type DOMAIN_DISPLAY_OEM_USER. This is + followed by the bodies of the various + strings pointed to from within the + DOMAIN_DISPLAY_OEM_user structures. + + DomainDisplayOemGroup --> An array of ReturnedEntryCount elements + of type DOMAIN_DISPLAY_OEM_GROUP. This is + followed by the bodies of the various + strings pointed to from within the + DOMAIN_DISPLAY_OEM_GROUP structures. + + +Return Values: + + STATUS_SUCCESS - normal, successful completion. + + STATUS_ACCESS_DENIED - The specified handle was not opened for + the necessary access. + + STATUS_INVALID_HANDLE - The specified handle is not that of an + opened Domain object. + + STATUS_INVALID_INFO_CLASS - The requested class of information + is not legitimate for this service. + + + + + +--*/ +{ + NTSTATUS + NtStatus, + IgnoreStatus; + + PSAMP_OBJECT + DomainContext; + + SAMP_OBJECT_TYPE + FoundType; + + PSAMP_DEFINED_DOMAINS + Domain; + + PSAMPR_DOMAIN_DISPLAY_USER + UserElement; + + PSAMPR_DOMAIN_DISPLAY_MACHINE + MachineElement; + + PSAMPR_DOMAIN_DISPLAY_GROUP + GroupElement; + + + ULONG + ReturnedBytes = 0, + ReturnedItems = 0; + + PVOID + RestartKey; + + // + // Prepare for failure + // + + *TotalAvailable = 0; + *TotalReturned = 0; + + switch (DisplayInformation) { + case DomainDisplayUser: + Buffer->UserInformation.EntriesRead = 0; + Buffer->UserInformation.Buffer = NULL; + break; + + case DomainDisplayMachine: + Buffer->MachineInformation.EntriesRead = 0; + Buffer->MachineInformation.Buffer = NULL; + break; + + case DomainDisplayGroup: + Buffer->GroupInformation.EntriesRead = 0; + Buffer->GroupInformation.Buffer = NULL; + break; + + case DomainDisplayOemUser: + Buffer->OemUserInformation.EntriesRead = 0; + Buffer->OemUserInformation.Buffer = NULL; + break; + + case DomainDisplayOemGroup: + Buffer->OemGroupInformation.EntriesRead = 0; + Buffer->OemGroupInformation.Buffer = NULL; + break; + + default: + return(STATUS_INVALID_INFO_CLASS); + } + + // + // If they don't want anything, that's what they'll get + // + + if (EntriesRequested == 0) { + return(STATUS_SUCCESS); + } + + // + // Make sure we don't try to allocate too much memory on + // the user's behalf + // + + if (EntriesRequested > 5000) { + EntriesRequested = 5000; + } + + // + // Grab the read lock + // + + SampAcquireReadLock(); + + // + // Validate type of, and access to object. + // + + DomainContext = (PSAMP_OBJECT)DomainHandle; + NtStatus = SampLookupContext( + DomainContext, + DOMAIN_LIST_ACCOUNTS, + SampDomainObjectType, // ExpectedType + &FoundType + ); + + if (NT_SUCCESS(NtStatus)) { + + Domain = &SampDefinedDomains[ DomainContext->DomainIndex ]; + + + + // + // Set up the common loop statistics + // + + ReturnedBytes = 0; + ReturnedItems = 0; + + + switch (DisplayInformation) { + + case DomainDisplayUser: + + + // + // Recreate our cached data if necessary + // + + NtStatus = SampCreateDisplayInformation(DomainDisplayUser); + + // + // Set the Restart Key from the passed in index + // + + UserElement = RtlRestartKeyByIndexGenericTable2( + &Domain->DisplayInformation.UserTable, + Index, + &RestartKey + ); + + if (UserElement == NULL) { + NtStatus = STATUS_SUCCESS; + Buffer->GroupInformation.EntriesRead = 0; + *TotalReturned = 0; + *TotalAvailable = 0; // Not supported for this info level + break; // out of switch + } + + + // + // Allocate space for array of elements + // + + Buffer->UserInformation.Buffer = MIDL_user_allocate( + EntriesRequested * sizeof(SAMPR_DOMAIN_DISPLAY_USER)); + + if (Buffer->UserInformation.Buffer == NULL) { + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + break; // out of switch + } + + // + // Prepare default return value + // + + NtStatus = STATUS_MORE_ENTRIES; + + // + // Increment the index value for assignment in our return + // buffer + // + + Index++; + + do { + NTSTATUS TempStatus; + + // + // Store a copy of this element in the return buffer. + // + + TempStatus = SampDuplicateUserInfo( + (PDOMAIN_DISPLAY_USER) + &(Buffer->UserInformation.Buffer[ReturnedItems]), + (PDOMAIN_DISPLAY_USER)UserElement, + Index); + Index++; + + if (!NT_SUCCESS(TempStatus)) { + + // + // Free up everything we've allocated so far + // + + while(ReturnedItems > 0) { + ReturnedItems --; + SampFreeUserInfo((PDOMAIN_DISPLAY_USER) + &(Buffer->UserInformation.Buffer[ReturnedItems])); + } + + MIDL_user_free(Buffer->UserInformation.Buffer); + Buffer->UserInformation.Buffer = NULL; + + NtStatus = TempStatus; + break; // out of do loop + } + + // + // Update loop statistics + // + + ReturnedBytes += SampBytesRequiredUserNode( + (PDOMAIN_DISPLAY_USER)UserElement); + ReturnedItems ++; + + // + // Go find the next element + // + + UserElement = RtlEnumerateGenericTable2( + &Domain->DisplayInformation.UserTable, + &RestartKey + ); + + if (UserElement == NULL) { + NtStatus = STATUS_SUCCESS; + break; // out of do loop + } + + + } while ( (ReturnedBytes < PreferredMaximumLength) && + (ReturnedItems < EntriesRequested) ); + + // + // Update output parameters + // + + if (NT_SUCCESS(NtStatus)) { + Buffer->UserInformation.EntriesRead = ReturnedItems; + *TotalReturned = ReturnedBytes; + *TotalAvailable = Domain->DisplayInformation.TotalBytesInUserTable; + } + + break; // out of switch + + + case DomainDisplayMachine: + + // + // Recreate our cached data if necessary + // + + NtStatus = SampCreateDisplayInformation(DomainDisplayMachine); + + // + // Set the Restart Key from the passed in index + // + + MachineElement = RtlRestartKeyByIndexGenericTable2( + &Domain->DisplayInformation.MachineTable, + Index, + &RestartKey + ); + + if (MachineElement == NULL) { + NtStatus = STATUS_SUCCESS; + Buffer->GroupInformation.EntriesRead = 0; + *TotalReturned = 0; + *TotalAvailable = 0; // Not supported for this info level + break; // out of switch + } + + // + // Allocate space for array of elements + // + + Buffer->MachineInformation.Buffer = MIDL_user_allocate( + EntriesRequested * sizeof(SAMPR_DOMAIN_DISPLAY_MACHINE)); + + if (Buffer->MachineInformation.Buffer == NULL) { + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + break; // out of switch + } + + // + // Prepare default return value + // + + NtStatus = STATUS_MORE_ENTRIES; + + // + // Increment the index value for assignment in our return + // buffer + // + + Index++; + + do { + NTSTATUS TempStatus; + + // + // Store a copy of this element in the return buffer. + // + + TempStatus = SampDuplicateMachineInfo( + (PDOMAIN_DISPLAY_MACHINE) + &(Buffer->MachineInformation.Buffer[ReturnedItems]), + (PDOMAIN_DISPLAY_MACHINE)MachineElement, + Index); + Index++; + + if (!NT_SUCCESS(TempStatus)) { + + // + // Free up everything we've allocated so far + // + + while(ReturnedItems > 0) { + ReturnedItems--; + SampFreeMachineInfo((PDOMAIN_DISPLAY_MACHINE) + &(Buffer->MachineInformation.Buffer[ReturnedItems])); + } + + MIDL_user_free(Buffer->MachineInformation.Buffer); + Buffer->MachineInformation.Buffer = NULL; + + NtStatus = TempStatus; + break; // out of do loop + } + + // + // Update loop statistics + // + + ReturnedBytes += SampBytesRequiredMachineNode( + (PDOMAIN_DISPLAY_MACHINE)MachineElement); + ReturnedItems ++; + + // + // Go find the next element + // + + MachineElement = RtlEnumerateGenericTable2( + &Domain->DisplayInformation.MachineTable, + &RestartKey + ); + + if (MachineElement == NULL) { + NtStatus = STATUS_SUCCESS; + break; // out of do loop + } + + + } while ( (ReturnedBytes < PreferredMaximumLength) && + (ReturnedItems < EntriesRequested) ); + + // + // Update output parameters + // + + if (NT_SUCCESS(NtStatus)) { + Buffer->MachineInformation.EntriesRead = ReturnedItems; + *TotalReturned = ReturnedBytes; + *TotalAvailable = Domain->DisplayInformation.TotalBytesInMachineTable; + } + + break; // out of switch + + + case DomainDisplayGroup: + + + // + // Recreate our cached data if necessary + // + + NtStatus = SampCreateDisplayInformation(DomainDisplayGroup); + + // + // Set the Restart Key from the passed in index + // + + GroupElement = RtlRestartKeyByIndexGenericTable2( + &Domain->DisplayInformation.GroupTable, + Index, + &RestartKey + ); + + if (GroupElement == NULL) { + NtStatus = STATUS_SUCCESS; + Buffer->GroupInformation.EntriesRead = 0; + *TotalReturned = 0; + *TotalAvailable = 0; // Not supported for this info level + break; // out of switch + } + + // + // Allocate space for array of elements + // + + Buffer->GroupInformation.Buffer = MIDL_user_allocate( + EntriesRequested * sizeof(SAMPR_DOMAIN_DISPLAY_GROUP)); + + if (Buffer->GroupInformation.Buffer == NULL) { + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + break; // out of switch + } + + // + // Prepare default return value + // + + NtStatus = STATUS_MORE_ENTRIES; + + // + // Increment the index value for assignment in our return + // buffer + // + + Index++; + + do { + NTSTATUS TempStatus; + + // + // Store a copy of this element in the return buffer. + // + + TempStatus = SampDuplicateGroupInfo( + (PDOMAIN_DISPLAY_GROUP) + &(Buffer->GroupInformation.Buffer[ReturnedItems]), + (PDOMAIN_DISPLAY_GROUP)GroupElement, + Index); + Index++; + + if (!NT_SUCCESS(TempStatus)) { + + // + // Free up everything we've allocated so far + // + + while(ReturnedItems > 0) { + ReturnedItems--; + SampFreeGroupInfo((PDOMAIN_DISPLAY_GROUP) + &(Buffer->GroupInformation.Buffer[ReturnedItems])); + } + + MIDL_user_free(Buffer->GroupInformation.Buffer); + Buffer->GroupInformation.Buffer = NULL; + + NtStatus = TempStatus; + break; // out of do loop + } + + // + // Update loop statistics + // + + ReturnedBytes += SampBytesRequiredGroupNode( + (PDOMAIN_DISPLAY_GROUP)GroupElement); + ReturnedItems ++; + + // + // Go find the next element + // + + GroupElement = RtlEnumerateGenericTable2( + &Domain->DisplayInformation.GroupTable, + &RestartKey + ); + + if (GroupElement == NULL) { + NtStatus = STATUS_SUCCESS; + break; // out of do loop + } + + + } while ( (ReturnedBytes < PreferredMaximumLength) && + (ReturnedItems < EntriesRequested) ); + + // + // Update output parameters + // + + if (NT_SUCCESS(NtStatus)) { + Buffer->GroupInformation.EntriesRead = ReturnedItems; + *TotalReturned = ReturnedBytes; + *TotalAvailable = Domain->DisplayInformation.TotalBytesInGroupTable; + } + + break; // out of switch + + case DomainDisplayOemUser: + + + // + // Recreate our cached data if necessary + // + + NtStatus = SampCreateDisplayInformation(DomainDisplayUser); + + // + // Set the Restart Key from the passed in index + // + + UserElement = RtlRestartKeyByIndexGenericTable2( + &Domain->DisplayInformation.UserTable, + Index, + &RestartKey + ); + + if (UserElement == NULL) { + NtStatus = STATUS_SUCCESS; + Buffer->GroupInformation.EntriesRead = 0; + *TotalReturned = 0; + *TotalAvailable = 0; // Not supported for this info level + break; // out of switch + } + + + // + // Allocate space for array of elements + // + + Buffer->UserInformation.Buffer = MIDL_user_allocate( + EntriesRequested * sizeof(SAMPR_DOMAIN_DISPLAY_OEM_USER)); + + if (Buffer->OemUserInformation.Buffer == NULL) { + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + break; // out of switch + } + + // + // Prepare default return value + // + + NtStatus = STATUS_MORE_ENTRIES; + + // + // Increment the index value for assignment in our return + // buffer + // + + Index++; + + do { + NTSTATUS TempStatus; + + // + // Store a copy of this element in the return buffer. + // + + TempStatus = SampDuplicateOemUserInfo( + (PDOMAIN_DISPLAY_OEM_USER) + &(Buffer->OemUserInformation.Buffer[ReturnedItems]), + (PDOMAIN_DISPLAY_USER)UserElement, + Index); + Index++; + + if (!NT_SUCCESS(TempStatus)) { + + // + // Free up everything we've allocated so far + // + + while(ReturnedItems > 0) { + ReturnedItems --; + SampFreeOemUserInfo((PDOMAIN_DISPLAY_OEM_USER) + &(Buffer->UserInformation.Buffer[ReturnedItems])); + } + + MIDL_user_free(Buffer->OemUserInformation.Buffer); + Buffer->OemUserInformation.Buffer = NULL; + + NtStatus = TempStatus; + break; // out of do loop + } + + // + // Update loop statistics + // + + ReturnedBytes += + SampBytesRequiredOemUserNode( + (PDOMAIN_DISPLAY_OEM_USER) + &(Buffer->OemUserInformation.Buffer[ReturnedItems])); + ReturnedItems ++; + + // + // Go find the next element + // + + UserElement = RtlEnumerateGenericTable2( + &Domain->DisplayInformation.UserTable, + &RestartKey + ); + + if (UserElement == NULL) { + NtStatus = STATUS_SUCCESS; + break; // out of do loop + } + + + } while ( (ReturnedBytes < PreferredMaximumLength) && + (ReturnedItems < EntriesRequested) ); + + // + // Update output parameters + // + + if (NT_SUCCESS(NtStatus)) { + Buffer->UserInformation.EntriesRead = ReturnedItems; + *TotalReturned = ReturnedBytes; + *TotalAvailable = 0; // Not supported for this info level + } + + break; // out of switch + + + case DomainDisplayOemGroup: + + + // + // Recreate our cached data if necessary + // + + NtStatus = SampCreateDisplayInformation(DomainDisplayGroup); + + // + // Set the Restart Key from the passed in index + // + + GroupElement = RtlRestartKeyByIndexGenericTable2( + &Domain->DisplayInformation.GroupTable, + Index, + &RestartKey + ); + + if (GroupElement == NULL) { + NtStatus = STATUS_SUCCESS; + Buffer->GroupInformation.EntriesRead = 0; + *TotalReturned = 0; + *TotalAvailable = 0; // Not supported for this info level + break; // out of switch + } + + + // + // Allocate space for array of elements + // + + Buffer->GroupInformation.Buffer = MIDL_user_allocate( + EntriesRequested * sizeof(SAMPR_DOMAIN_DISPLAY_OEM_GROUP)); + + if (Buffer->OemGroupInformation.Buffer == NULL) { + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + break; // out of switch + } + + // + // Prepare default return value + // + + NtStatus = STATUS_MORE_ENTRIES; + + // + // Increment the index value for assignment in our return + // buffer + // + + Index++; + + do { + NTSTATUS TempStatus; + + // + // Store a copy of this element in the return buffer. + // + + TempStatus = SampDuplicateOemGroupInfo( + (PDOMAIN_DISPLAY_OEM_GROUP) + &(Buffer->OemGroupInformation.Buffer[ReturnedItems]), + (PDOMAIN_DISPLAY_GROUP)GroupElement, + Index); + Index++; + + if (!NT_SUCCESS(TempStatus)) { + + // + // Free up everything we've allocated so far + // + + while(ReturnedItems > 0) { + ReturnedItems --; + SampFreeOemGroupInfo((PDOMAIN_DISPLAY_OEM_GROUP) + &(Buffer->GroupInformation.Buffer[ReturnedItems])); + } + + MIDL_user_free(Buffer->OemGroupInformation.Buffer); + Buffer->OemGroupInformation.Buffer = NULL; + + NtStatus = TempStatus; + break; // out of do loop + } + + // + // Update loop statistics + // + + ReturnedBytes += + SampBytesRequiredOemGroupNode( + (PDOMAIN_DISPLAY_OEM_GROUP) + &(Buffer->OemGroupInformation.Buffer[ReturnedItems])); + ReturnedItems ++; + + // + // Go find the next element + // + + GroupElement = RtlEnumerateGenericTable2( + &Domain->DisplayInformation.GroupTable, + &RestartKey + ); + + if (GroupElement == NULL) { + NtStatus = STATUS_SUCCESS; + break; // out of do loop + } + + + } while ( (ReturnedBytes < PreferredMaximumLength) && + (ReturnedItems < EntriesRequested) ); + + // + // Update output parameters + // + + if (NT_SUCCESS(NtStatus)) { + Buffer->GroupInformation.EntriesRead = ReturnedItems; + *TotalReturned = ReturnedBytes; + *TotalAvailable = 0; // Not supported for this info level + } + + break; // out of switch + + } + + // + // De-reference the object + // + + IgnoreStatus = SampDeReferenceContext( DomainContext, FALSE); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + // + // Free the read lock + // + + SampReleaseReadLock(); + + + + return(NtStatus); +} + + + +NTSTATUS +SamrGetDisplayEnumerationIndex ( + IN SAMPR_HANDLE DomainHandle, + IN DOMAIN_DISPLAY_INFORMATION DisplayInformation, + IN PRPC_UNICODE_STRING Prefix, + OUT PULONG Index + ) + +/*++ + +Routine Description: + + This wrapper around SamrGetDisplayEnumerationIndex2(). + + Provided for compatibility with down-level clients. + + +--*/ +{ + + return(SamrGetDisplayEnumerationIndex2( DomainHandle, + DisplayInformation, + Prefix, + Index + ) ); +} + +NTSTATUS +SamrGetDisplayEnumerationIndex2 ( + IN SAMPR_HANDLE DomainHandle, + IN DOMAIN_DISPLAY_INFORMATION DisplayInformation, + IN PRPC_UNICODE_STRING Prefix, + OUT PULONG Index + ) + +/*++ + +Routine Description: + + This routine returns the index of the entry which alphabetically + immediatly preceeds a specified prefix. If no such entry exists, + then zero is returned as the index. + +Parameters: + + DomainHandle - A handle to an open domain for DOMAIN_LIST_ACCOUNTS. + + DisplayInformation - Indicates which sorted information class is + to be searched. + + Prefix - The prefix to compare. + + Index - Receives the index of the entry of the information class + with a LogonName (or MachineName) which immediatly preceeds the + provided prefix string. If there are no elements which preceed + the prefix, then zero is returned. + + +Return Values: + + STATUS_SUCCESS - normal, successful completion. + + STATUS_ACCESS_DENIED - The specified handle was not opened for + the necessary access. + + STATUS_INVALID_HANDLE - The specified handle is not that of an + opened Domain object. + + STATUS_NO_MORE_ENTRIES - There are no entries for this information class. + + +--*/ +{ + NTSTATUS + NtStatus, + IgnoreStatus; + + PSAMP_OBJECT + DomainContext; + + SAMP_OBJECT_TYPE + FoundType; + + PSAMP_DEFINED_DOMAINS + Domain; + + PRTL_GENERIC_TABLE2 + Table; + + DOMAIN_DISPLAY_USER + UserElement; + + DOMAIN_DISPLAY_MACHINE + MachineElement; + + DOMAIN_DISPLAY_GROUP + GroupElement; + + RTL_GENERIC_COMPARE_RESULTS + CompareResult; + + PRTL_GENERIC_2_COMPARE_ROUTINE + CompareRoutine; + + PVOID + Element, + NextElement, + RestartKey; + + ULONG + CurrentIndex; + + + // + // Check the information class + // + + if ((DisplayInformation != DomainDisplayUser) && + (DisplayInformation != DomainDisplayMachine) && + (DisplayInformation != DomainDisplayGroup) + ) { + + return(STATUS_INVALID_INFO_CLASS); + } + + + // + // Grab the read lock + // + + SampAcquireReadLock(); + + + + // + // Validate type of, and access to object. + // + + DomainContext = (PSAMP_OBJECT)DomainHandle; + NtStatus = SampLookupContext( + DomainContext, + DOMAIN_LIST_ACCOUNTS, + SampDomainObjectType, // ExpectedType + &FoundType + ); + + + if (NT_SUCCESS(NtStatus)) { + + Domain = &SampDefinedDomains[ DomainContext->DomainIndex ]; + + // + // Set default return value + // + + (*Index) = 0; + + // + // Recreate our cached data if necessary + // + + NtStatus = SampCreateDisplayInformation(DisplayInformation); + + if (NT_SUCCESS(NtStatus)) { + + // + // Set up + // The table to search, + // The comparison routine to use, + // An appropriate element for the search. + // + + switch (DisplayInformation) { + + case DomainDisplayUser: + + Table = &Domain->DisplayInformation.UserTable; + CompareRoutine = SampCompareUserNodeByName; + + Element = (PVOID)&UserElement; + UserElement.LogonName = *(PUNICODE_STRING)Prefix; + + break; // out of switch + + case DomainDisplayMachine: + + Table = &Domain->DisplayInformation.MachineTable; + CompareRoutine = SampCompareMachineNodeByName; + + Element = (PVOID)&MachineElement; + MachineElement.Machine = *(PUNICODE_STRING)Prefix; + + break; // out of switch + + + case DomainDisplayGroup: + + Table = &Domain->DisplayInformation.GroupTable; + CompareRoutine = SampCompareGroupNodeByName; + + Element = (PVOID)&GroupElement; + GroupElement.Group = *(PUNICODE_STRING)Prefix; + + break; // out of switch + } + + + if (RtlIsGenericTable2Empty(Table)) { + + NtStatus = STATUS_NO_MORE_ENTRIES; + + } else { + + // + // Now compare each entry until we find the one asked + // for. + // + + CurrentIndex = 0; + + RestartKey = NULL; + for (NextElement = RtlEnumerateGenericTable2(Table, &RestartKey); + NextElement != NULL; + NextElement = RtlEnumerateGenericTable2(Table, &RestartKey)) { + + // + // Compare with passed in element + // + + CompareResult = (*CompareRoutine)( NextElement, Element ); + if (CompareResult != GenericLessThan) { + break; // break out of for loop + } + + CurrentIndex++; + } + + // + // CurrentIndex has the return value in it. + // + + ASSERT( CurrentIndex <= RtlNumberElementsGenericTable2(Table) ); + + (*Index) = CurrentIndex; + NtStatus = STATUS_SUCCESS; + } + } + + // + // De-reference the object + // + + IgnoreStatus = SampDeReferenceContext( DomainContext, FALSE); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + } + + // + // Free the read lock + // + + SampReleaseReadLock(); + + + return(NtStatus); +} + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Routines available to trusted clients in SAM's process // +// // +/////////////////////////////////////////////////////////////////////////////// + + +NTSTATUS +SamIEnumerateAccountRids( + IN SAMPR_HANDLE DomainHandle, + IN ULONG AccountTypesMask, + IN ULONG StartingRid, + IN ULONG PreferedMaximumLength, + OUT PULONG ReturnCount, + OUT PULONG *AccountRids + ) + +/*++ + +Routine Description: + + Provide a list of account RIDs. The caller may ask for one or + more types of account rids in a single call. + + The returned rids are in ascending value order. + + WARNING - This routine is only callable by trusted clients. + Therefore, parameter checking is only performed + in checked-build systems. + +Parameters: + + DomainHandle - handle to the domain whose accounts are to be + enumerated. + + AccountTypesMask - Mask indicating which types of accounts + the caller wants enumerated. These included: + + SAM_USER_ACCOUNT + SAM_GLOBAL_GROUP_ACCOUNT + SAM_LOCAL_GROUP_ACCOUNT (not yet supported) + + StartingRid - A rid that is less than the lowest value rid to be + included in the enumeration. + + + PreferedMaximumLength - Provides a restriction on how much memory + may be returned in this call. This is not a hard upper limit, + but serves as a guideline. + + ReturnCount - Receives a count of the number of rids returned. + + AccountRids - Receives a pointer to an array of rids. If + ReturnCount is zero, then this will be returned as NULL. + Otherwise, it will point to an array containing ReturnCount + rids. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully, and there + are no additional entries. + + STATUS_MORE_ENTRIES - There are more entries, so call again. + This is a successful return. + + STATUS_INVALID_INFO_CLASS - The specified AccountTypesMask contained + unknown or unsupported account types. + + STATUS_NO_MEMORY - Could not allocate pool to complete the call. + +--*/ +{ + + NTSTATUS + NtStatus, + IgnoreStatus; + + PSAMP_OBJECT + DomainContext; + + SAMP_OBJECT_TYPE + FoundType; + + PSAMP_DEFINED_DOMAINS + Domain; + + PRTL_GENERIC_TABLE2 + Table; + + ULONG + MaxEntries, + Count, + AccountType; + + PVOID + RestartKey; + + PSAMP_DISPLAY_ENTRY_HEADER + Element; + + SAMP_DISPLAY_ENTRY_HEADER + RestartValue; + + // + // Prepare for failure + // + + (*ReturnCount) = 0; + (*AccountRids) = NULL; + +#if DBG + + if ( (AccountTypesMask & ~( SAM_USER_ACCOUNT | SAM_GLOBAL_GROUP_ACCOUNT)) + != 0 ) { + return(STATUS_INVALID_INFO_CLASS); + } + + +#endif //DBG + + // + // Grab the read lock + // + + SampAcquireReadLock(); + + // + // Validate type of, and access to object. + // + + DomainContext = (PSAMP_OBJECT)DomainHandle; + NtStatus = SampLookupContext( + DomainContext, + 0, // Trusted clients only + SampDomainObjectType, // ExpectedType + &FoundType + ); + + if (NT_SUCCESS(NtStatus)) { + + Domain = &SampDefinedDomains[ DomainContext->DomainIndex ]; + Table = &Domain->DisplayInformation.RidTable; + + // + // If the RID table isn't valid, force it to be made valid. + // + + if (!SampRidTableValid(DomainContext->DomainIndex)) { + NtStatus = SampCreateDisplayInformation ( DomainDisplayUser ); //User and machine + if (NT_SUCCESS(NtStatus)) { + NtStatus = SampCreateDisplayInformation ( DomainDisplayGroup ); + } + } + + if (NT_SUCCESS(NtStatus)) { + + // + // Allocate a return buffer. + // Only allocate as much as we can use. + // This is limited either by PreferedMaximumLength + // or the number of entries in the table. + // + + MaxEntries = + ( PreferedMaximumLength / sizeof(ULONG) ); + + if (MaxEntries == 0) { + MaxEntries = 1; // Always return at least one + } + + if (MaxEntries > RtlNumberElementsGenericTable2(Table) ) { + MaxEntries = RtlNumberElementsGenericTable2(Table); + } + + PreferedMaximumLength = MaxEntries * + sizeof(SAMP_DISPLAY_ENTRY_HEADER); + + (*AccountRids) = MIDL_user_allocate( PreferedMaximumLength ); + if ((*AccountRids) == NULL) { + STATUS_NO_MEMORY; + } + + // + // Get the restart key based upon the passed in RID. + // + + Table = &Domain->DisplayInformation.RidTable; + RestartValue.Rid = StartingRid; + + Element = RtlRestartKeyByValueGenericTable2( + Table, + &RestartValue, + &RestartKey + ); + + // + // Now we may loop obtaining entries until we reach + // either MaxEntries or the end of the table. + // + // WARNING - there is one special case that we have to + // take care of. If the returned Element is not null, + // but the RestartKey is null, then the caller has + // asked for an enumeration and passed in the last rid + // defined. If we aren't careful, this will cause an + // enumeration to be started from the beginning of the + // list again. Instead, return status indicating we have + // no more entries. + // + + Count = 0; + if (((Element != NULL) && (RestartKey == NULL))) { + + Element = NULL; // Used to signify no more entries found + + } else { + + for (Element = RtlEnumerateGenericTable2(Table, &RestartKey); + ( (Element != NULL) && (Count < MaxEntries) ); + Element = RtlEnumerateGenericTable2(Table, &RestartKey)) { + + // + // Make sure this is an account that was asked for + // + + AccountType = Element->Index; + if ((AccountType & AccountTypesMask) != 0) { + (*AccountRids)[Count] = Element->Rid; + Count++; + } + } + } + + // + // Now figure out what we have done: + // + // Returned all entries in table => STATUS_SUCCESS + // More entries to return => STATUS_MORE_ENTRIES + // + // Count == 0 => free AccountRid array. + // + + if (Element == NULL) { + NtStatus = STATUS_SUCCESS; + } else { + NtStatus = STATUS_MORE_ENTRIES; + } + + if (Count == 0) { + MIDL_user_free( (*AccountRids) ); + (*AccountRids) = NULL; + } + + (*ReturnCount) = Count; + + } + + // + // De-reference the object + // + + IgnoreStatus = SampDeReferenceContext( DomainContext, FALSE); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + // + // Free the read lock + // + + SampReleaseReadLock(); + + + + return(NtStatus); + + +} + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Routines available to other SAM modules // +// // +/////////////////////////////////////////////////////////////////////////////// + + + +NTSTATUS +SampInitializeDisplayInformation ( + ULONG DomainIndex + ) + +/*++ + +Routine Description: + + This routines initializes the display information structure. + This involves initializing the User, Machine and Group trees (empty), + and setting the Valid flag to FALSE. + + If this is the account domain, we also create the display information. + +Parameters: + + DomainIndex - An index into the DefinedDomains array. This array + contains information about the domain being openned, + including its name. + +Return Values: + + STATUS_SUCCESS - normal, successful completion. + +--*/ +{ + PSAMP_DOMAIN_DISPLAY_INFORMATION DisplayInformation; + + // + // This must be initialized before we use SampCompareDisplayStrings(). + // + + SampSystemDefaultLCID = GetSystemDefaultLCID(); + + DisplayInformation = &SampDefinedDomains[DomainIndex].DisplayInformation; + + RtlInitializeGenericTable2( + &DisplayInformation->UserTable, + SampCompareUserNodeByName, + SampGenericTable2Allocate, + SampGenericTable2Free); + + RtlInitializeGenericTable2( + &DisplayInformation->MachineTable, + SampCompareMachineNodeByName, + SampGenericTable2Allocate, + SampGenericTable2Free); + + RtlInitializeGenericTable2( + &DisplayInformation->InterdomainTable, + SampCompareMachineNodeByName, + SampGenericTable2Allocate, + SampGenericTable2Free); + + RtlInitializeGenericTable2( + &DisplayInformation->GroupTable, + SampCompareGroupNodeByName, + SampGenericTable2Allocate, + SampGenericTable2Free); + + RtlInitializeGenericTable2( + &DisplayInformation->RidTable, + SampCompareNodeByRid, + SampGenericTable2Allocate, + SampGenericTable2Free); + + DisplayInformation->UserAndMachineTablesValid = FALSE; + DisplayInformation->GroupTableValid = FALSE; + + + if ( SampProductType == NtProductLanManNt && + DomainIndex == SampDefinedDomainsCount - 1 ) { + // + // Grab the read lock and indicate which domain the transaction is in + // + + SampAcquireReadLock(); + SampSetTransactionDomain( DomainIndex ); + + // + // Populate the Display Cache + // + + SAMTRACE("MURLI.... WE DIE HERE"); + + (VOID) SampCreateDisplayInformation(DomainDisplayUser); + (VOID) SampCreateDisplayInformation(DomainDisplayGroup); + + SAMTRACE("MURLI.... NOPE WE SURVIVED"); + + // + // Free the read lock + // + + SampReleaseReadLock(); + } + + return(STATUS_SUCCESS); + +} + + + +VOID +SampDeleteDisplayInformation ( + PSAMP_DOMAIN_DISPLAY_INFORMATION DisplayInformation, + SAMP_OBJECT_TYPE ObjectType + ) + +/*++ + +Routine Description: + + This routines frees up any resources used by the display information. + + + Note: It use to be that we could selectively invalidate + portions of the display cache (e.g., users, or groups). + With the addition of the RID table, this becomes + problematic. So, now the approach is to flush all tables + for a domain if any the tables in that domain are flushed. + + +Parameters: + + DisplayInformation - The display information structure to delete. + + ObjectType - Indicates which table to delete the information from. + +Return Values: + + STATUS_SUCCESS - normal, successful completion. + +--*/ +{ + NTSTATUS NtStatus; + + + + // + // Empty the user table and check it really is empty + // + + NtStatus = SampEmptyGenericTable2(&DisplayInformation->UserTable, FALSE); + ASSERT(NT_SUCCESS(NtStatus)); + + ASSERT(RtlIsGenericTable2Empty(&DisplayInformation->UserTable)); + + DisplayInformation->TotalBytesInUserTable = 0; + + + + // + // Empty the machine table and check it really is empty + // + + NtStatus = SampEmptyGenericTable2(&DisplayInformation->MachineTable, FALSE); + ASSERT(NT_SUCCESS(NtStatus)); + + ASSERT(RtlIsGenericTable2Empty(&DisplayInformation->MachineTable)); + + DisplayInformation->TotalBytesInMachineTable = 0; + + + + // + // Empty the Interdomain table and check it really is empty + // + + NtStatus = SampEmptyGenericTable2(&DisplayInformation->InterdomainTable, FALSE); + ASSERT(NT_SUCCESS(NtStatus)); + + ASSERT(RtlIsGenericTable2Empty(&DisplayInformation->InterdomainTable)); + + DisplayInformation->TotalBytesInInterdomainTable = 0; + + + + // + // Empty the Group table and check it really is empty + // + + NtStatus = SampEmptyGenericTable2(&DisplayInformation->GroupTable, FALSE); + ASSERT(NT_SUCCESS(NtStatus)); + + ASSERT(RtlIsGenericTable2Empty(&DisplayInformation->GroupTable)); + + DisplayInformation->TotalBytesInGroupTable = 0; + + + + + // + // Empty the Rid table and check it really is empty. + // + + + NtStatus = SampEmptyGenericTable2(&DisplayInformation->RidTable, FALSE); + ASSERT(NT_SUCCESS(NtStatus)); + + ASSERT(RtlIsGenericTable2Empty(&DisplayInformation->RidTable)); + + DisplayInformation->TotalBytesInRidTable = 0; + +} + + + +NTSTATUS +SampMarkDisplayInformationInvalid ( + SAMP_OBJECT_TYPE ObjectType + ) + +/*++ + +Routine Description: + + This routine invalidates any cached display information. This + causes it to be recreated the next time a client queries it. + Later we will probably start/restart a thread here and have it + re-create the display information in the background. + + Note: THIS ROUTINE REFERENCES THE CURRENT TRANSACTION DOMAIN + (ESTABLISHED USING SampSetTransactioDomain()). THIS + SERVICE MAY ONLY BE CALLED AFTER SampSetTransactionDomain() + AND BEFORE SampReleaseWriteLock(). + + Another Note: It use to be that we could selectively invalidate + portions of the display cache (e.g., users, or groups). + With the addition of the RID table, this becomes + problematic. So, now the approach is to flush all tables + for a domain if any the tables in that domain are flushed. + + +Parameters: + + ObjectType - SampUserObjectType or SampGroupObjectType. Only the + appropriate tables will be marked Invalid. For User type, the + user and machine tables will be marked Invalid. For Group type, + the group table will be marked Invalid. + +Return Values: + + STATUS_SUCCESS - normal, successful completion. + +--*/ +{ + PSAMP_DEFINED_DOMAINS Domain; + + ASSERT(SampTransactionWithinDomain == TRUE); + + SampDiagPrint(DISPLAY_CACHE, + ("SAM: MarkDisplayInformationInvalid : Emptying cache\n")); + + // + // Get pointer to the current domain structure + // + + Domain = &SampDefinedDomains[SampTransactionDomainIndex]; + + // + // Delete any cached data + // + + SampDeleteDisplayInformation(&Domain->DisplayInformation, ObjectType); + + // + // Set the Valid flag to FALSE + // + + Domain->DisplayInformation.UserAndMachineTablesValid = FALSE; + Domain->DisplayInformation.GroupTableValid = FALSE; + + + return(STATUS_SUCCESS); + +} + + + +NTSTATUS +SampCreateDisplayInformation ( + DOMAIN_DISPLAY_INFORMATION DisplayType + ) + +/*++ + +Routine Description: + + This routine builds the cached display information for the current + domain. + + Note: THIS ROUTINE REFERENCES THE CURRENT TRANSACTION DOMAIN + (ESTABLISHED USING SampSetTransactioDomain()). THIS + SERVICE MAY ONLY BE CALLED AFTER SampSetTransactionDomain() + AND BEFORE SampReleaseReadLock(). + +Parameters: + + DisplayType - Indicates which type of display information is + being created. This leads us to the appropriate table(s). + +Return Values: + + STATUS_SUCCESS - normal, successful completion. + +--*/ +{ + NTSTATUS NtStatus; + PSAMP_DEFINED_DOMAINS Domain; + PSAMP_DOMAIN_DISPLAY_INFORMATION DisplayInformation; + + SAMTRACE("SampCreateDisplayInformation"); + + ASSERT(SampTransactionWithinDomain == TRUE); + + Domain = &SampDefinedDomains[SampTransactionDomainIndex]; + + + DisplayInformation = &Domain->DisplayInformation; + + switch (DisplayType) { + case DomainDisplayUser: + case DomainDisplayMachine: + + // + // If the cache is valid, nothing to do + // + + if (DisplayInformation->UserAndMachineTablesValid) { + + SampDiagPrint(DISPLAY_CACHE, + ("SAM: CreateDisplayInformation : User/Machine Cache is valid, nothing to do\n")); + return(STATUS_SUCCESS); + }; + + + SampDiagPrint(DISPLAY_CACHE, + ("SAM: CreateDisplayInformation : Creating user/machine cache...\n")); + + ASSERT(RtlIsGenericTable2Empty(&DisplayInformation->UserTable)); + ASSERT(RtlIsGenericTable2Empty(&DisplayInformation->MachineTable)); + ASSERT(RtlIsGenericTable2Empty(&DisplayInformation->InterdomainTable)); + + + NtStatus = SampRetrieveDisplayInfoFromDisk( DisplayInformation, SampUserObjectType ); + if (NT_SUCCESS(NtStatus)) { + NtStatus = SampTallyTableStatistics(DisplayInformation, SampUserObjectType); + } + + // + // Clean up on error + + if (!NT_SUCCESS(NtStatus)) { + SampDiagPrint(DISPLAY_CACHE, + ("SAM: CreateDisplayInformation FAILED: 0x%lx\n", NtStatus)); + + SampDeleteDisplayInformation(&Domain->DisplayInformation, SampUserObjectType); + } else { + Domain->DisplayInformation.UserAndMachineTablesValid = TRUE; + } + + break; // out of switch + + + case DomainDisplayGroup: + + // + // If the cache is valid, nothing to do + // + + if (DisplayInformation->GroupTableValid) { + + SampDiagPrint(DISPLAY_CACHE, + ("SAM: CreateDisplayInformation : Group Cache is valid, nothing to do\n")); + + return(STATUS_SUCCESS); + }; + + + SampDiagPrint(DISPLAY_CACHE, + ("SAM: CreateDisplayInformation : Creating group cache...\n")); + + ASSERT(RtlIsGenericTable2Empty(&DisplayInformation->GroupTable)); + + + NtStatus = SampRetrieveDisplayInfoFromDisk( DisplayInformation, SampGroupObjectType ); + if (NT_SUCCESS(NtStatus)) { + NtStatus = SampTallyTableStatistics(DisplayInformation, SampGroupObjectType); + } + + // + // Clean up on error + + if (!NT_SUCCESS(NtStatus)) { + SampDiagPrint(DISPLAY_CACHE, + ("SAM: CreateDisplayInformation FAILED: 0x%lx\n", NtStatus)); + SampDeleteDisplayInformation(&Domain->DisplayInformation, SampGroupObjectType); + } else { + Domain->DisplayInformation.GroupTableValid = TRUE; + } + + break; // out of switch + } + + return(NtStatus); +} + + +NTSTATUS +SampRetrieveDisplayInfoFromDisk( + PSAMP_DOMAIN_DISPLAY_INFORMATION DisplayInformation, + SAMP_OBJECT_TYPE ObjectType + ) + +{ + NTSTATUS NtStatus; + SAM_ENUMERATE_HANDLE EnumerationContext; + PSAMPR_ENUMERATION_BUFFER EnumerationBuffer; + ULONG i; + ULONG CountReturned; + BOOLEAN MoreEntries; + + + SAMTRACE("SampRetrieveDisplayInfoFromDisk"); + + // + // Enumerate the accounts. + // For each account, get the relevant information on it, + // and add to either the UserTable, MachineTable, or GroupTable. + // + + EnumerationContext = 0; + + do { + + NtStatus = SampEnumerateAccountNames( + ObjectType, + &EnumerationContext, + &EnumerationBuffer, + 200000, // PreferedMaximumLength + 0L, // no filter + &CountReturned, + FALSE // trusted client + ); + if (!NT_SUCCESS(NtStatus)) { + SampDiagPrint( DISPLAY_CACHE_ERRORS, + ("SAM: Retrieve Info From Disk - " + "Error enumerating account names (0x%lx)\n", + NtStatus) ); + break; + } + + // + // Make a note if there are more entries + // + + MoreEntries = (NtStatus == STATUS_MORE_ENTRIES); + + + // + // For each account, get the necessary information for it + // and add to the appropriate display information table + // + + for (i = 0; i < EnumerationBuffer->EntriesRead; i++) { + + ULONG AccountRid = + EnumerationBuffer->Buffer[i].RelativeId; + PUNICODE_STRING AccountName = + (PUNICODE_STRING)&(EnumerationBuffer->Buffer[i].Name); + SAMP_V1_0A_FIXED_LENGTH_USER UserV1aFixed; // Contains account control + SAMP_V1_0A_FIXED_LENGTH_GROUP GroupV1Fixed; // Contains attributes + SAMP_ACCOUNT_DISPLAY_INFO AccountInfo; + PSAMP_OBJECT AccountContext; + + + // + // Open a context to the account + // + + NtStatus = SampCreateAccountContext( + ObjectType, + AccountRid, + TRUE, // Trusted client + TRUE, // Account exists + &AccountContext + ); + + if (!NT_SUCCESS(NtStatus)) { + SampDiagPrint( DISPLAY_CACHE_ERRORS, + ("SAM: Retrieve Info From Disk - " + "Error Creating account context (0x%lx)\n", + NtStatus) ); + break; // out of for loop + } + + + // + // Get the account control information + // + + switch (ObjectType) { + case SampUserObjectType: + + NtStatus = SampRetrieveUserV1aFixed(AccountContext, &UserV1aFixed); + if (!NT_SUCCESS(NtStatus)) { + SampDeleteContext( AccountContext ); + SampDiagPrint( DISPLAY_CACHE_ERRORS, + ("SAM: Retrieve USER From Disk - " + "Error getting V1a Fixed (0x%lx)\n", + NtStatus) ); + break; // out of for loop + } + + + // + // If this is not an account we're interested in skip it + // + + if (!DISPLAY_ACCOUNT(UserV1aFixed.UserAccountControl)) { + SampDeleteContext( AccountContext ); + continue; // next account + } + + + + // + // Get the admin comment + // + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_ADMIN_COMMENT, + FALSE, // Don't make copy + &AccountInfo.Comment + ); + + if (!NT_SUCCESS(NtStatus)) { + SampDeleteContext( AccountContext ); + SampDiagPrint( DISPLAY_CACHE_ERRORS, + ("SAM: Retrieve USER From Disk - " + "Error getting admin comment (0x%lx)\n", + NtStatus) ); + break; // out of for loop + } + + + // + // Get the full name + // + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_FULL_NAME, + FALSE, // Don't make copy + &AccountInfo.FullName + ); + + if (!NT_SUCCESS(NtStatus)) { + SampDeleteContext( AccountContext ); + SampDiagPrint( DISPLAY_CACHE_ERRORS, + ("SAM: Retrieve USER From Disk - " + "Error getting full name (0x%lx)\n", + NtStatus) ); + break; // out of for loop + } + + // + // Set the account control + // + + AccountInfo.AccountControl = UserV1aFixed.UserAccountControl; + + break; // out of switch + + case SampGroupObjectType: + + NtStatus = SampRetrieveGroupV1Fixed(AccountContext, &GroupV1Fixed); + if (!NT_SUCCESS(NtStatus)) { + SampDeleteContext( AccountContext ); + SampDiagPrint( DISPLAY_CACHE_ERRORS, + ("SAM: Retrieve GROUP From Disk - " + "Error getting V1 fixed (0x%lx)\n", + NtStatus) ); + break; // out of for loop + } + + // + // Get the admin comment + // + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_GROUP_ADMIN_COMMENT, + FALSE, // Don't make copy + &AccountInfo.Comment + ); + if (!NT_SUCCESS(NtStatus)) { + SampDeleteContext( AccountContext ); + SampDiagPrint( DISPLAY_CACHE_ERRORS, + ("SAM: Retrieve GROUP From Disk - " + "Error getting admin comment (0x%lx)\n", + NtStatus) ); + break; // out of for loop + } + + // + // Set the attributes + // + + AccountInfo.AccountControl = GroupV1Fixed.Attributes; + + break; // out of switch + } + + + // + // Now add this account to the cached data + // + + AccountInfo.Rid = AccountRid; + AccountInfo.Name = *((PUNICODE_STRING)(&EnumerationBuffer->Buffer[i].Name)); + + NtStatus = SampAddDisplayAccount(DisplayInformation, + ObjectType, + &AccountInfo); + + // + // We're finished with the account context + // + + SampDeleteContext( AccountContext ); + + // + // Check the result of adding the account to the cache + // + + if (!NT_SUCCESS(NtStatus)) { + break; // out of for loop + } + + + } // end_for + + + // + // Free up the enumeration buffer returned + // + + SamIFree_SAMPR_ENUMERATION_BUFFER(EnumerationBuffer); + + + } while ( MoreEntries ); + + return(NtStatus); + +} + + + +NTSTATUS +SampUpdateDisplayInformation ( + PSAMP_ACCOUNT_DISPLAY_INFO OldAccountInfo OPTIONAL, + PSAMP_ACCOUNT_DISPLAY_INFO NewAccountInfo OPTIONAL, + SAMP_OBJECT_TYPE ObjectType + ) + +/*++ + +Routine Description: + + This routines updates the cached display information to reflect + changes to a single account. + + If any error occurs, this routine marks the cached information + Invalid so it will get fixed during re-creation. + + Note: THIS ROUTINE REFERENCES THE CURRENT TRANSACTION DOMAIN + (ESTABLISHED USING SampSetTransactioDomain()). THIS + SERVICE MAY ONLY BE CALLED AFTER SampSetTransactionDomain() + AND BEFORE SampReleaseWriteLock(). + +Parameters: + + OldAccountInfo - The old information for this account. If this is NULL + then the account is being added. + The only fields required in the OldAccountInfo are + Name + AccountControl + Rid + + NewAccountInfo - The new information for this account. If this is NULL + then the account is being deleted. + + + ObjectType - Indicates whether the account is a user account or + group account. + +Return Values: + + STATUS_SUCCESS - normal, successful completion. + +--*/ +{ + NTSTATUS NtStatus = STATUS_SUCCESS; + PSAMP_DEFINED_DOMAINS Domain; + PSAMP_DOMAIN_DISPLAY_INFORMATION DisplayInformation; + BOOLEAN DoUpdate; + + ASSERT( ARGUMENT_PRESENT(OldAccountInfo) || + ARGUMENT_PRESENT(NewAccountInfo) + ); + + + ASSERT(SampTransactionWithinDomain == TRUE); + Domain = &SampDefinedDomains[SampTransactionDomainIndex]; + DisplayInformation = &Domain->DisplayInformation; + + + IF_SAMP_GLOBAL( DISPLAY_CACHE ) { + + if (ARGUMENT_PRESENT(OldAccountInfo) && ARGUMENT_PRESENT(NewAccountInfo)) { + SampDiagPrint(DISPLAY_CACHE, + ("SAM: UpdateDisplayInformation : Updating cache for old account <%wZ>, new <%wZ>\n", + &OldAccountInfo->Name, &NewAccountInfo->Name)); + } + if (!ARGUMENT_PRESENT(OldAccountInfo) && ARGUMENT_PRESENT(NewAccountInfo)) { + SampDiagPrint(DISPLAY_CACHE, + ("SAM: UpdateDisplayInformation : Adding account <%wZ> to cache\n", + &NewAccountInfo->Name)); + } + if (ARGUMENT_PRESENT(OldAccountInfo) && !ARGUMENT_PRESENT(NewAccountInfo)) { + SampDiagPrint(DISPLAY_CACHE, + ("SAM: UpdateDisplayInformation : Deleting account <%wZ> from cache\n", + &OldAccountInfo->Name)); + } + } //end_IF_SAMP_GLOBAL + + + switch (ObjectType) { + + case SampUserObjectType: + + // + // If the cache is Invalid there's nothing to do + // + + if (!DisplayInformation->UserAndMachineTablesValid) { + + SampDiagPrint(DISPLAY_CACHE, + ("SAM: UpdateDisplayInformation : User Cache is Invalid, nothing to do\n")); + + return(STATUS_SUCCESS); + }; + + + // + // If this is an update to an existing account then try + // to do an inplace update of the cache. + // If this fails because it's too complex etc, then revert to + // the less efficient method of deleting the old, then adding the new. + // + + DoUpdate = FALSE; + if (ARGUMENT_PRESENT(OldAccountInfo) && ARGUMENT_PRESENT(NewAccountInfo)) { + + // + // We can only do an update if both old and new accounts + // are types that we keep in the display cache. + // + + if ( DISPLAY_ACCOUNT(OldAccountInfo->AccountControl) && + DISPLAY_ACCOUNT(NewAccountInfo->AccountControl) ) { + + // + // We can only do an update if the account is still of + // the same type. i.e. it hasn't jumped cache table. + // + + if ( (USER_ACCOUNT(OldAccountInfo->AccountControl) == + USER_ACCOUNT(NewAccountInfo->AccountControl)) && + (MACHINE_ACCOUNT(OldAccountInfo->AccountControl) == + MACHINE_ACCOUNT(NewAccountInfo->AccountControl)) ) { + + // + // We can only do an update if the account name hasn't changed + // + + if (RtlEqualUnicodeString( &OldAccountInfo->Name, + &NewAccountInfo->Name, + FALSE // Case sensitive + )) { + // + // Everything has been checked out - we can do an update + // + + DoUpdate = TRUE; + } + } + } + } + + break; // out of switch + + case SampGroupObjectType: + + // + // If the cache is already Invalid there's nothing to do + // + + if (!DisplayInformation->GroupTableValid) { + + SampDiagPrint(DISPLAY_CACHE, + ("SAM: UpdateDisplayInformation : Group Cache is Invalid, nothing to do\n")); + + return(STATUS_SUCCESS); + }; + + + // + // If this is an update to an existing account then try + // and do an inplace update of the cache. + // If this fails because it's too complex etc, then revert to + // the less efficient method of deleting the old, then adding the new. + // + + DoUpdate = FALSE; + if (ARGUMENT_PRESENT(OldAccountInfo) && ARGUMENT_PRESENT(NewAccountInfo)) { + + // + // We can only do an update if the account name hasn't changed + // + + if (RtlEqualUnicodeString( &OldAccountInfo->Name, + &NewAccountInfo->Name, + FALSE // Case sensitive + )) { + DoUpdate = TRUE; + } + } + + break; // out of switch + } + + + // + // Do an update if possible, otherwise do delete then insert + // + + if (DoUpdate) { + + NtStatus = SampUpdateDisplayAccount(DisplayInformation, + ObjectType, + NewAccountInfo); + + } else { + + NtStatus = STATUS_SUCCESS; + + // + // Delete the old account + // + + if (ARGUMENT_PRESENT(OldAccountInfo)) { + NtStatus = SampDeleteDisplayAccount(DisplayInformation, + ObjectType, + OldAccountInfo); + } + + // + // Add the new account + // + + if (NT_SUCCESS(NtStatus) && ARGUMENT_PRESENT(NewAccountInfo)) { + NtStatus = SampAddDisplayAccount(DisplayInformation, + ObjectType, + NewAccountInfo); + } + + // + // Re-tally the cache + // + + if (NT_SUCCESS(NtStatus)) { + NtStatus = SampTallyTableStatistics(DisplayInformation, ObjectType); + } + } + + + + if (!NT_SUCCESS(NtStatus)) { + + // + // Something screwed up. + // Mark the cache Invalid - it will get rebuilt from scratch + // at the next query. + // + + KdPrint(("SAM: The display cache is inconsistent, forcing rebuild\n")); + ASSERT(FALSE); + + NtStatus = SampMarkDisplayInformationInvalid(ObjectType); + ASSERT(NT_SUCCESS(NtStatus)); + + NtStatus = STATUS_SUCCESS; + } + + + return(NtStatus); +} + + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Routines available within this module only // +// // +/////////////////////////////////////////////////////////////////////////////// + + + + +NTSTATUS +SampDeleteDisplayAccount ( + PSAMP_DOMAIN_DISPLAY_INFORMATION DisplayInformation, + SAMP_OBJECT_TYPE ObjectType, + PSAMP_ACCOUNT_DISPLAY_INFO AccountInfo + ) + +/*++ + +Routine Description: + + This routines deletes the specified account from the cached display + information. It is asummed that if this account is a cached type it + will appear in the appropriate cache table. + +Parameters: + + DisplayInformation - Pointer to cached display information + + ObjectType - Indicates which table(s) to look for the account in. + + AccountInfo - The account to be deleted. + +Return Values: + + STATUS_SUCCESS - normal, successful completion. + + STATUS_INTERNAL_ERROR - the account is a cached type yet could not be + found in the cached data. +--*/ +{ + NTSTATUS NtStatus; + ULONG Control = AccountInfo->AccountControl; + BOOLEAN Success; + + // + // We expect the cache to be valid + // +#if DBG + switch (ObjectType) { + case SampUserObjectType: + ASSERT(DisplayInformation->UserAndMachineTablesValid); + break; //out of switch + + case SampGroupObjectType: + ASSERT(DisplayInformation->GroupTableValid); + break; //out of switch + } +#endif //DBG + + + SampDiagPrint(DISPLAY_CACHE, + ("SAM: DeleteDisplayAccount : Deleting account <%wZ>\n", &AccountInfo->Name)); + + + + switch (ObjectType) { + case SampUserObjectType: + + if (USER_ACCOUNT(Control)) { + + DOMAIN_DISPLAY_USER LocalUserInfo; + PDOMAIN_DISPLAY_USER UserInfo; + + SampDiagPrint(DISPLAY_CACHE, + ("SAM: DeleteDisplayAccount : Deleting account from user table\n")); + + UserInfo = &LocalUserInfo; + NtStatus = SampInitializeUserInfo(AccountInfo, &UserInfo, FALSE); + if (NT_SUCCESS(NtStatus)) { + // + // Delete the account from the user table + // + + Success = RtlDeleteElementGenericTable2( + &DisplayInformation->UserTable, + (PVOID)UserInfo); + if (!Success) { + SampDiagPrint(DISPLAY_CACHE, + ("SAM: DeleteDisplayAccount : Failed to delete element from user table\n")); + ASSERT(FALSE); + NtStatus = STATUS_INTERNAL_ERROR; + } else { + + // + // Now remove it to the RID table + // + + (VOID)RtlDeleteElementGenericTable2( + &DisplayInformation->RidTable, + (PVOID)UserInfo); + if (!Success) { + SampDiagPrint(DISPLAY_CACHE, + ("SAM: DeleteDisplayAccount : Failed to delete element from RID table\n")); + NtStatus = STATUS_INTERNAL_ERROR; + ASSERT(Success); + } + } + } + + + } else if (MACHINE_ACCOUNT(Control)) { + + DOMAIN_DISPLAY_MACHINE LocalMachineInfo; + PDOMAIN_DISPLAY_MACHINE MachineInfo; + + SampDiagPrint(DISPLAY_CACHE, + ("SAM: DeleteDisplayAccount : Deleting account from machine table\n")); + + MachineInfo = &LocalMachineInfo; + NtStatus = SampInitializeMachineInfo(AccountInfo, &MachineInfo, FALSE); + if (NT_SUCCESS(NtStatus)) { + + // + // Delete the account from the machine table + // + + Success = RtlDeleteElementGenericTable2( + &DisplayInformation->MachineTable, + (PVOID)MachineInfo); + if (!Success) { + SampDiagPrint(DISPLAY_CACHE, + ("SAM: DeleteDisplayAccount : Failed to delete element from machine table\n")); + ASSERT(FALSE); + NtStatus = STATUS_INTERNAL_ERROR; + } else { + + // + // Now remove it to the RID table + // + + Success = RtlDeleteElementGenericTable2( + &DisplayInformation->RidTable, + (PVOID)MachineInfo); + if (!Success) { + SampDiagPrint(DISPLAY_CACHE, + ("SAM: DeleteDisplayAccount : Failed to delete element from RID table\n")); + NtStatus = STATUS_INTERNAL_ERROR; + ASSERT(Success); + } + } + } + + } else if (INTERDOMAIN_ACCOUNT(Control)) { + + // + // Interdomain account + // + + DOMAIN_DISPLAY_MACHINE LocalInterdomainInfo; + PDOMAIN_DISPLAY_MACHINE InterdomainInfo; + + SampDiagPrint(DISPLAY_CACHE, + ("SAM: DeleteDisplayAccount : Deleting account from Interdomain table\n")); + + InterdomainInfo = &LocalInterdomainInfo; + NtStatus = SampInitializeMachineInfo(AccountInfo, &InterdomainInfo, FALSE); + if (NT_SUCCESS(NtStatus)) { + + // + // Delete the account from the Interdomain table + // + + Success = RtlDeleteElementGenericTable2( + &DisplayInformation->InterdomainTable, + (PVOID)InterdomainInfo); + if (!Success) { + SampDiagPrint(DISPLAY_CACHE, + ("SAM: DeleteDisplayAccount : Failed to delete element from Interdomain table\n")); + ASSERT(FALSE); + NtStatus = STATUS_INTERNAL_ERROR; + } else { + + // + // Now remove it to the RID table + // + + Success = RtlDeleteElementGenericTable2( + &DisplayInformation->RidTable, + (PVOID)InterdomainInfo); + if (!Success) { + SampDiagPrint(DISPLAY_CACHE, + ("SAM: DeleteDisplayAccount : Failed to delete element from RID table\n")); + NtStatus = STATUS_INTERNAL_ERROR; + ASSERT(Success); + } + } + } + + } else { + + // + // This account is not one that we cache - nothing to do + // + + NtStatus = STATUS_SUCCESS; + + SampDiagPrint(DISPLAY_CACHE, + ("SAM: DeleteDisplayAccount : Account is not one that we cache, account control = 0x%lx\n", Control)); + } + + + break; //out of switch + + + + + + case SampGroupObjectType: + + { + + DOMAIN_DISPLAY_GROUP LocalGroupInfo; + PDOMAIN_DISPLAY_GROUP GroupInfo; + + SampDiagPrint(DISPLAY_CACHE, + ("SAM: DeleteDisplayAccount : Deleting account from Group table\n")); + + GroupInfo = &LocalGroupInfo; + NtStatus = SampInitializeGroupInfo(AccountInfo, &GroupInfo, FALSE); + if (NT_SUCCESS(NtStatus)) { + // + // Delete the account from the Group table + // + + Success = RtlDeleteElementGenericTable2( + &DisplayInformation->GroupTable, + (PVOID)GroupInfo); + if (!Success) { + SampDiagPrint(DISPLAY_CACHE, + ("SAM: DeleteDisplayAccount : Failed to delete element from Group table\n")); + ASSERT(FALSE); + NtStatus = STATUS_INTERNAL_ERROR; + } else { + + // + // Now remove it to the RID table + // + + (VOID)RtlDeleteElementGenericTable2( + &DisplayInformation->RidTable, + (PVOID)GroupInfo); + if (!Success) { + SampDiagPrint(DISPLAY_CACHE, + ("SAM: DeleteDisplayAccount : Failed to delete element from RID table\n")); + NtStatus = STATUS_INTERNAL_ERROR; + ASSERT(Success); + } + } + } + + break; //out of switch + } + + } + + + return(STATUS_SUCCESS); +} + + + +NTSTATUS +SampAddDisplayAccount ( + PSAMP_DOMAIN_DISPLAY_INFORMATION DisplayInformation, + SAMP_OBJECT_TYPE ObjectType, + PSAMP_ACCOUNT_DISPLAY_INFO AccountInfo + ) + +/*++ + +Routine Description: + + This routines adds the specified account to the cached display + information as appropriate to its type. + +Parameters: + + DisplayInformation - Pointer to cached display information + + ObjectType - SampUserObjectType or SampGroupObjectType. Helps + determine which table it goes into. + + AccountInfo - The account to be added. + +Return Values: + + STATUS_SUCCESS - normal, successful completion. + + STATUS_INTERNAL_ERROR - the account already existed in the cache +--*/ +{ + NTSTATUS + NtStatus; + + ULONG + Control = AccountInfo->AccountControl; + + BOOLEAN + NewElement; + + SampDiagPrint(DISPLAY_CACHE, + ("SAM: AddDisplayAccount : Adding account <%wZ>\n", &AccountInfo->Name)); + + + if (ObjectType == SampGroupObjectType) { + + PDOMAIN_DISPLAY_GROUP GroupInfo; + + SampDiagPrint(DISPLAY_CACHE, + ("SAM: AddDisplayAccount : Adding account to group table\n")); + + NtStatus = SampInitializeGroupInfo(AccountInfo, &GroupInfo, TRUE); + if (NT_SUCCESS(NtStatus)) { + + // + // Add the account to the Group table + // + + (VOID)RtlInsertElementGenericTable2( + &DisplayInformation->GroupTable, + GroupInfo, + &NewElement); + if (!NewElement) { + SampDiagPrint(DISPLAY_CACHE_ERRORS, + ("SAM: AddDisplayAccount : Account already exists in GROUP table\n")); + ASSERT(FALSE); + SampFreeGroupInfo(GroupInfo); + NtStatus = STATUS_INTERNAL_ERROR; + } else { + + // + // Now add it to the RID table + // + + (VOID)RtlInsertElementGenericTable2( + &DisplayInformation->RidTable, + GroupInfo, + &NewElement); + if (!NewElement) { + SampDiagPrint(DISPLAY_CACHE_ERRORS, + ("SAM: AddDisplayAccount : Account already exists in RID table\n")); + NtStatus = STATUS_INTERNAL_ERROR; + ASSERT(NewElement); + } + + } + } + + } else { + + ASSERT(ObjectType == SampUserObjectType); + + if (USER_ACCOUNT(Control)) { + + PDOMAIN_DISPLAY_USER UserInfo; + + SampDiagPrint(DISPLAY_CACHE, + ("SAM: AddDisplayAccount : Adding account to user table\n")); + + NtStatus = SampInitializeUserInfo(AccountInfo, &UserInfo, TRUE); + if (NT_SUCCESS(NtStatus)) { + + // + // Add the account to the normal user table + // + + (VOID)RtlInsertElementGenericTable2( + &DisplayInformation->UserTable, + UserInfo, + &NewElement); + if (!NewElement) { + SampDiagPrint(DISPLAY_CACHE_ERRORS, + ("SAM: AddDisplayAccount : Account already exists in USER table\n")); + ASSERT(FALSE); + SampFreeUserInfo(UserInfo); + NtStatus = STATUS_INTERNAL_ERROR; + } else { + + // + // Now add it to the RID table + // + + (VOID)RtlInsertElementGenericTable2( + &DisplayInformation->RidTable, + UserInfo, + &NewElement); + + if (!NewElement) { + SampDiagPrint(DISPLAY_CACHE_ERRORS, + ("SAM: AddDisplayAccount : Account already exists in RID table\n")); + ASSERT(NewElement); + NtStatus = STATUS_INTERNAL_ERROR; + } + + } + } + + } else if (MACHINE_ACCOUNT(Control)) { + + PDOMAIN_DISPLAY_MACHINE MachineInfo; + + SampDiagPrint(DISPLAY_CACHE, + ("SAM: AddDisplayAccount : Adding account to machine table\n")); + + NtStatus = SampInitializeMachineInfo(AccountInfo, &MachineInfo, TRUE); + if (NT_SUCCESS(NtStatus)) { + + // + // Add the account to the machine table + // + + (VOID)RtlInsertElementGenericTable2( + &DisplayInformation->MachineTable, + MachineInfo, + &NewElement); + if (!NewElement) { + SampDiagPrint(DISPLAY_CACHE, + ("SAM: AddDisplayAccount : Account already exists in MACHINE table\n")); + ASSERT(FALSE); + SampFreeMachineInfo(MachineInfo); + NtStatus = STATUS_INTERNAL_ERROR; + } else { + + // + // Now add it to the RID table + // + + (VOID)RtlInsertElementGenericTable2( + &DisplayInformation->RidTable, + MachineInfo, + &NewElement); + + if (!NewElement) { + SampDiagPrint(DISPLAY_CACHE, + ("SAM: AddDisplayAccount : Account already exists in RID table\n")); + ASSERT(NewElement); + NtStatus = STATUS_INTERNAL_ERROR; + } + + } + } + } else if (INTERDOMAIN_ACCOUNT(Control)) { + + PDOMAIN_DISPLAY_MACHINE InterdomainInfo; + + SampDiagPrint(DISPLAY_CACHE, + ("SAM: AddDisplayAccount : Adding account to Interdomain table\n")); + + NtStatus = SampInitializeMachineInfo(AccountInfo, &InterdomainInfo, TRUE); + if (NT_SUCCESS(NtStatus)) { + + // + // Add the account to the Interdomain table + // + + (VOID)RtlInsertElementGenericTable2( + &DisplayInformation->InterdomainTable, + InterdomainInfo, + &NewElement); + if (!NewElement) { + SampDiagPrint(DISPLAY_CACHE, + ("SAM: AddDisplayAccount : Account already exists in Interdomain table\n")); + ASSERT(FALSE); + SampFreeMachineInfo(InterdomainInfo); + NtStatus = STATUS_INTERNAL_ERROR; + } else { + + // + // Now add it to the RID table + // + + (VOID)RtlInsertElementGenericTable2( + &DisplayInformation->RidTable, + InterdomainInfo, + &NewElement); + + if (!NewElement) { + SampDiagPrint(DISPLAY_CACHE, + ("SAM: AddDisplayAccount : Account already exists in RID table\n")); + ASSERT(NewElement); + NtStatus = STATUS_INTERNAL_ERROR; + } + + } + } + + } else { + + // + // This account is not one that we cache - nothing to do + // + + SampDiagPrint(DISPLAY_CACHE, + ("SAM: AddDisplayAccount : Account is not one that we cache, account control = 0x%lx\n", Control)); + + NtStatus = STATUS_SUCCESS; + } + } + + return(NtStatus); +} + + + +NTSTATUS +SampUpdateDisplayAccount( + PSAMP_DOMAIN_DISPLAY_INFORMATION DisplayInformation, + SAMP_OBJECT_TYPE ObjectType, + PSAMP_ACCOUNT_DISPLAY_INFO AccountInfo + ) + +/*++ + +Routine Description: + + This routines attempts to update an account in the display cache. + + Note: THIS ROUTINE REFERENCES THE CURRENT TRANSACTION DOMAIN + (ESTABLISHED USING SampSetTransactioDomain()). THIS + SERVICE MAY ONLY BE CALLED AFTER SampSetTransactionDomain() + AND BEFORE SampReleaseWriteLock(). + +Parameters: + + DisplayInformation - Pointer to cached display information + + ObjectType - Indicates whether the account is a user account or + group account. + + AccountInfo - The new information for this account. + +Return Values: + + STATUS_SUCCESS - normal, successful completion. + + +Notes: + + The account must be a cached type (MACHINE/USER/GROUP) + +--*/ +{ + NTSTATUS NtStatus; + + SampDiagPrint(DISPLAY_CACHE, + ("SAM: UpdateDisplayAccount : Updating cached account <%wZ>\n", + &AccountInfo->Name)); + +#if SAMP_DIAGNOSTICS + { + UNICODE_STRING + SampDiagAccountName; + + RtlInitUnicodeString( &SampDiagAccountName, L"SAMP_DIAG" ); + + if (RtlEqualUnicodeString(&AccountInfo->Name, &SampDiagAccountName, FALSE)) { + SampDisplayDiagnostic(); + } + + } +#endif //SAMP_DIAGNOSTICS + + + // + // We should only be called when the cache is valid. + // + + switch (ObjectType) { + case SampUserObjectType: + + ASSERT(DisplayInformation->UserAndMachineTablesValid); + + // + // The account must be one that we cache + // + + ASSERT( DISPLAY_ACCOUNT(AccountInfo->AccountControl) ); + + // + // Go find the account in the appropriate table and update it's fields. + // + + if (USER_ACCOUNT(AccountInfo->AccountControl)) { + + PDOMAIN_DISPLAY_USER UserInfo; + + // + // Allocate space for and initialize the new data + // + + NtStatus = SampInitializeUserInfo(AccountInfo, &UserInfo, TRUE); + if (NT_SUCCESS(NtStatus)) { + + PDOMAIN_DISPLAY_USER FoundElement; + + // + // Search for the account in the user table + // + + FoundElement = RtlLookupElementGenericTable2( + &DisplayInformation->UserTable, + UserInfo); + + if (FoundElement == NULL) { + SampDiagPrint(DISPLAY_CACHE, + ("SAM: UpdateDisplayAccount : Account <%wZ> not found in user table\n", &AccountInfo->Name)); + ASSERT(FALSE); + SampFreeUserInfo(UserInfo); + NtStatus = STATUS_INTERNAL_ERROR; + + } else { + + // + // We found it. Check the old and new match where we expect. + // Can't change either the logon name or RID by this routine. + // + + ASSERT(RtlEqualUnicodeString(&FoundElement->LogonName, &UserInfo->LogonName, FALSE)); + ASSERT(FoundElement->Rid == UserInfo->Rid); + + // + // Free up the existing data in the account element + // (all the strings) and replace it with the new data. + // Don't worry about the index value. It isn't + // valid in the table. + // + + SampSwapUserInfo(FoundElement, UserInfo); + SampFreeUserInfo(UserInfo); + } + } + + } else if (MACHINE_ACCOUNT(AccountInfo->AccountControl)) { + + PDOMAIN_DISPLAY_MACHINE MachineInfo; + + // + // Allocate space for and initialize the new data + // + + NtStatus = SampInitializeMachineInfo(AccountInfo, &MachineInfo, TRUE); + if (NT_SUCCESS(NtStatus)) { + + PDOMAIN_DISPLAY_MACHINE FoundElement; + + // + // Search for the account in the user table + // + + FoundElement = RtlLookupElementGenericTable2( + &DisplayInformation->MachineTable, + MachineInfo); + + if (FoundElement == NULL) { + SampDiagPrint(DISPLAY_CACHE, + ("SAM: UpdateDisplayAccount : Account <%wZ> not found in machine table\n", &AccountInfo->Name)); + ASSERT(FALSE); + SampFreeMachineInfo(MachineInfo); + NtStatus = STATUS_INTERNAL_ERROR; + + } else { + + // + // We found it. Check the old and new match where we expect. + // Can't change either the account name or RID by this routine. + // + + ASSERT(RtlEqualUnicodeString(&FoundElement->Machine, &MachineInfo->Machine, FALSE)); + ASSERT(FoundElement->Rid == MachineInfo->Rid); + + // + // Free up the existing data in the account element + // (all the strings) and replace it with the new data. + // Don't worry about the index value. It isn't + // valid in the table. + // + + SampSwapMachineInfo(FoundElement, MachineInfo); + SampFreeMachineInfo(MachineInfo); + } + } + + } else if (INTERDOMAIN_ACCOUNT(AccountInfo->AccountControl)) { + + PDOMAIN_DISPLAY_MACHINE InterdomainInfo; + + // + // Allocate space for and initialize the new data + // + + NtStatus = SampInitializeMachineInfo(AccountInfo, &InterdomainInfo, TRUE); + if (NT_SUCCESS(NtStatus)) { + + PDOMAIN_DISPLAY_MACHINE FoundElement; + + // + // Search for the account in the user table + // + + FoundElement = RtlLookupElementGenericTable2( + &DisplayInformation->InterdomainTable, + InterdomainInfo); + + if (FoundElement == NULL) { + SampDiagPrint(DISPLAY_CACHE, + ("SAM: UpdateDisplayAccount : Account <%wZ> not found in Interdomain table\n", &AccountInfo->Name)); + ASSERT(FALSE); + SampFreeMachineInfo(InterdomainInfo); + NtStatus = STATUS_INTERNAL_ERROR; + + } else { + + // + // We found it. Check the old and new match where we expect. + // Can't change either the account name or RID by this routine. + // + + ASSERT(RtlEqualUnicodeString(&FoundElement->Machine, &InterdomainInfo->Machine, FALSE)); + ASSERT(FoundElement->Rid == InterdomainInfo->Rid); + + // + // Free up the existing data in the account element + // (all the strings) and replace it with the new data. + // Don't worry about the index value. It isn't + // valid in the table. + // + + SampSwapMachineInfo(FoundElement, InterdomainInfo); + SampFreeMachineInfo(InterdomainInfo); + } + } + } + + + break; // out of switch + + case SampGroupObjectType: + { + PDOMAIN_DISPLAY_GROUP GroupInfo; + + ASSERT(DisplayInformation->GroupTableValid); + + // + // Allocate space for and initialize the new data + // + + NtStatus = SampInitializeGroupInfo(AccountInfo, &GroupInfo, TRUE); + if (NT_SUCCESS(NtStatus)) { + + PDOMAIN_DISPLAY_GROUP FoundElement; + + // + // Search for the account in the group table + // + + FoundElement = RtlLookupElementGenericTable2( + &DisplayInformation->GroupTable, + GroupInfo); + + if (FoundElement == NULL) { + SampDiagPrint(DISPLAY_CACHE, + ("SAM: UpdateDisplayAccount : Account <%wZ> not found in group table\n", &AccountInfo->Name)); + ASSERT(FALSE); + SampFreeGroupInfo(GroupInfo); + NtStatus = STATUS_INTERNAL_ERROR; + + } else { + + // + // We found it. Check the old and new match where we expect. + // Can't change either the account name or RID by this routine. + // + + ASSERT(RtlEqualUnicodeString(&FoundElement->Group, &GroupInfo->Group, FALSE)); + ASSERT(FoundElement->Rid == GroupInfo->Rid); + + // + // Free up the existing data in the account element + // (all the strings) and replace it with the new data. + // Don't worry about the index value. It isn't + // valid in the table. + // + + SampSwapGroupInfo(FoundElement, GroupInfo); + SampFreeGroupInfo(GroupInfo); + } + } + } + + break; // out of switch + + } // end_switch + + + + return(NtStatus); +} + + + +NTSTATUS +SampTallyTableStatistics ( + PSAMP_DOMAIN_DISPLAY_INFORMATION DisplayInformation, + SAMP_OBJECT_TYPE ObjectType + ) + +/*++ + +Routine Description: + + This routines runs through the cached data tables and totals + up the number of bytes in all elements of each table and stores + in the displayinfo. + +Parameters: + + DisplayInformation - The display information structure to tally. + + ObjectType - Indicates which table(s) to tally. + +Return Values: + + STATUS_SUCCESS - normal, successful completion. + +--*/ +{ + PVOID Node; + PVOID RestartKey; + + + switch (ObjectType) { + case SampUserObjectType: + + DisplayInformation->TotalBytesInUserTable = 0; + RestartKey = NULL; + + for (Node = RtlEnumerateGenericTable2( &DisplayInformation->UserTable, + &RestartKey); + Node != NULL; + Node = RtlEnumerateGenericTable2( &DisplayInformation->UserTable, + &RestartKey) + ) { + + DisplayInformation->TotalBytesInUserTable += + SampBytesRequiredUserNode((PDOMAIN_DISPLAY_USER)Node); + } + + DisplayInformation->TotalBytesInMachineTable = 0; + RestartKey = NULL; + + for (Node = RtlEnumerateGenericTable2( &DisplayInformation->MachineTable, + &RestartKey); + Node != NULL; + Node = RtlEnumerateGenericTable2( &DisplayInformation->MachineTable, + &RestartKey) + ) { + + + DisplayInformation->TotalBytesInMachineTable += + SampBytesRequiredMachineNode((PDOMAIN_DISPLAY_MACHINE)Node); + } + + break; // out of switch + + + case SampGroupObjectType: + + DisplayInformation->TotalBytesInGroupTable = 0; + RestartKey = NULL; + + for (Node = RtlEnumerateGenericTable2( &DisplayInformation->GroupTable, + &RestartKey); + Node != NULL; + Node = RtlEnumerateGenericTable2( &DisplayInformation->GroupTable, + &RestartKey) + ) { + + + DisplayInformation->TotalBytesInGroupTable += + SampBytesRequiredGroupNode((PDOMAIN_DISPLAY_GROUP)Node); + } + + break; // out of switch + + } // end_switch + return(STATUS_SUCCESS); +} + + + +NTSTATUS +SampEmptyGenericTable2 ( + PRTL_GENERIC_TABLE2 Table, + BOOLEAN FreeElements + ) + +/*++ + +Routine Description: + + This routines deletes all elements in the specified table. + +Parameters: + + Table - The table whose elements are to be deleted. + + FreeElements - Indicates whether or not the element bodies + should also be freed. + +Return Values: + + STATUS_SUCCESS - normal, successful completion. + +--*/ +{ + BOOLEAN Deleted; + PVOID Element; + ULONG RestartKey; + + RestartKey = 0; // Always get the first element + while ((Element = RtlEnumerateGenericTable2( Table, (PVOID *)&RestartKey)) != NULL) { + + Deleted = RtlDeleteElementGenericTable2(Table, Element); + ASSERT(Deleted); + + if (FreeElements) { + MIDL_user_free( Element ); + } + + RestartKey = 0; + } + + return(STATUS_SUCCESS); +} + + + +NTSTATUS +SampInitializeUserInfo( + PSAMP_ACCOUNT_DISPLAY_INFO AccountInfo, + PDOMAIN_DISPLAY_USER *UserInfo, + BOOLEAN CopyData + ) + +/*++ + +Routine Description: + + This routines initializes the passed user info structure from the + AccountInfo parameter. + +Parameters: + + AccountInfo - The account information + + UserInfo - Pointer to the pointer to the user structure to initialize. + If CopyData is TRUE, then a pointer to the user structure will be + returned to this argument. + + CopyData - FALSE - the UserInfo structure points to the same data as + the AccountInfo structure + TRUE - space for the data is allocated and all data copied + out of the AccountInfo structure into it. + +Return Values: + + STATUS_SUCCESS - UserInfo initialized successfully. + + STATUS_NO_MEMORY - Heap could not be allocated to copy the data. + +--*/ +{ + NTSTATUS + NtStatus; + + PDOMAIN_DISPLAY_USER + UI; + + if (CopyData) { + (*UserInfo) = MIDL_user_allocate( sizeof(DOMAIN_DISPLAY_USER) ); + if ((*UserInfo) == NULL) { + SampDiagPrint(DISPLAY_CACHE_ERRORS, + ("SAM: Init User Info: failed to allocate %d bytes\n", + sizeof(DOMAIN_DISPLAY_USER)) ); + return(STATUS_NO_MEMORY); + } + } + + UI = (*UserInfo); + + + UI->Rid = AccountInfo->Rid; + UI->AccountControl = AccountInfo->AccountControl; + + if (CopyData) { + + // + // Set all strings to NULL initially + // + + RtlInitUnicodeString(&UI->LogonName, NULL); + RtlInitUnicodeString(&UI->AdminComment, NULL); + RtlInitUnicodeString(&UI->FullName, NULL); + + // + // Copy source data into destination + // + + NtStatus = SampDuplicateUnicodeString(&UI->LogonName, + &AccountInfo->Name); + if (NT_SUCCESS(NtStatus)) { + NtStatus = SampDuplicateUnicodeString(&UI->AdminComment, + &AccountInfo->Comment); + if (NT_SUCCESS(NtStatus)) { + NtStatus = SampDuplicateUnicodeString(&UI->FullName, + &AccountInfo->FullName); + } + } + + // + // Clean up on failure + // + + if (!NT_SUCCESS(NtStatus)) { + SampDiagPrint(DISPLAY_CACHE_ERRORS, + ("SAM: SampInitializeUserInfo failed, status = 0x%lx\n", NtStatus)); + SampFreeUserInfo(UI); + } + + } else { + + // + // Refer to source data directly + // + + UI->LogonName = AccountInfo->Name; + UI->AdminComment = AccountInfo->Comment; + UI->FullName = AccountInfo->FullName; + + NtStatus = STATUS_SUCCESS; + } + + + // + // In the Generic Table, the Index field is used to tag the type + // of account so we can filter enumerations. + // + + UI->Index = SAM_USER_ACCOUNT; + + return(NtStatus); +} + + + +NTSTATUS +SampInitializeMachineInfo( + PSAMP_ACCOUNT_DISPLAY_INFO AccountInfo, + PDOMAIN_DISPLAY_MACHINE *MachineInfo, + BOOLEAN CopyData + ) + +/*++ + +Routine Description: + + This routines initializes the passed machine info structure from the + AccountInfo parameter. + +Parameters: + + AccountInfo - The account information + + MachineInfo - Pointer to the pointer to the Machine structure to initialize. + If CopyData is TRUE, then a pointer to the structure will be + returned to this argument. + + CopyData - FALSE - the MachineInfo structure points to the same data as + the AccountInfo structure + TRUE - space for the data is allocated and all data copied + out of the AccountInfo structure into it. + +Return Values: + + STATUS_SUCCESS - UserInfo initialized successfully. + +--*/ +{ + NTSTATUS + NtStatus; + + PDOMAIN_DISPLAY_MACHINE + MI; + + if (CopyData) { + (*MachineInfo) = MIDL_user_allocate( sizeof(DOMAIN_DISPLAY_MACHINE) ); + if ((*MachineInfo) == NULL) { + SampDiagPrint(DISPLAY_CACHE_ERRORS, + ("SAM: Init Mach Info: failed to allocate %d bytes\n", + sizeof(DOMAIN_DISPLAY_MACHINE)) ); + return(STATUS_NO_MEMORY); + } + } + + MI = (*MachineInfo); + + MI->Rid = AccountInfo->Rid; + MI->AccountControl = AccountInfo->AccountControl; + + if (CopyData) { + + // + // Set all strings to NULL initially + // + + RtlInitUnicodeString(&MI->Machine, NULL); + RtlInitUnicodeString(&MI->Comment, NULL); + + // + // Copy source data into destination + // + + NtStatus = SampDuplicateUnicodeString(&MI->Machine, + &AccountInfo->Name); + if (NT_SUCCESS(NtStatus)) { + NtStatus = SampDuplicateUnicodeString(&MI->Comment, + &AccountInfo->Comment); + } + + // + // Clean up on failure + // + + if (!NT_SUCCESS(NtStatus)) { + SampDiagPrint(DISPLAY_CACHE_ERRORS, + ("SAM: SampInitializeMachineInfo failed, status = 0x%lx\n", NtStatus)); + SampFreeMachineInfo(MI); + } + + } else { + + // + // Refer to source data directly + // + + MI->Machine = AccountInfo->Name; + MI->Comment = AccountInfo->Comment; + + NtStatus = STATUS_SUCCESS; + } + + // + // In the Generic Table, the Index field is used to tag the type + // of account so we can filter enumerations. + // + + MI->Index = SAM_USER_ACCOUNT; + + return(NtStatus); +} + + +NTSTATUS +SampInitializeGroupInfo( + PSAMP_ACCOUNT_DISPLAY_INFO AccountInfo, + PDOMAIN_DISPLAY_GROUP *GroupInfo, + BOOLEAN CopyData + ) + +/*++ + +Routine Description: + + This routines initializes the passed Group info structure from the + AccountInfo parameter. + +Parameters: + + AccountInfo - The account information + + GroupInfo - Pointer to the pointer to the Group structure to initialize. + If CopyData is TRUE, then a pointer to the structure will be + returned to this argument. + + CopyData - FALSE - the GroupInfo structure points to the same data as + the AccountInfo structure + TRUE - space for the data is allocated and all data copied + out of the AccountInfo structure into it. + +Return Values: + + STATUS_SUCCESS - GroupInfo initialized successfully. + +--*/ +{ + NTSTATUS + NtStatus; + + PDOMAIN_DISPLAY_GROUP + GI; + + if (CopyData) { + (*GroupInfo) = MIDL_user_allocate( sizeof(DOMAIN_DISPLAY_GROUP) ); + if ((*GroupInfo) == NULL) { + SampDiagPrint(DISPLAY_CACHE_ERRORS, + ("SAM: Init Group Info: failed to allocate %d bytes\n", + sizeof(DOMAIN_DISPLAY_GROUP)) ); + return(STATUS_NO_MEMORY); + } + } + + GI = (*GroupInfo); + + + GI->Rid = AccountInfo->Rid; + GI->Attributes = AccountInfo->AccountControl; + + if (CopyData) { + + // + // Set all strings to NULL initially + // + + RtlInitUnicodeString(&GI->Group, NULL); + RtlInitUnicodeString(&GI->Comment, NULL); + + // + // Copy source data into destination + // + + NtStatus = SampDuplicateUnicodeString(&GI->Group, + &AccountInfo->Name); + if (NT_SUCCESS(NtStatus)) { + NtStatus = SampDuplicateUnicodeString(&GI->Comment, + &AccountInfo->Comment); + } + + // + // Clean up on failure + // + + if (!NT_SUCCESS(NtStatus)) { + SampDiagPrint(DISPLAY_CACHE_ERRORS, + ("SAM: SampInitializeGroupInfo failed, status = 0x%lx\n", NtStatus)); + SampFreeGroupInfo(GI); + } + + } else { + + // + // Refer to source data directly + // + + GI->Group = AccountInfo->Name; + GI->Comment = AccountInfo->Comment; + + NtStatus = STATUS_SUCCESS; + } + + // + // In the Generic Table, the Index field is used to tag the type + // of account so we can filter enumerations. + // + + GI->Index = SAM_GLOBAL_GROUP_ACCOUNT; + + return(NtStatus); +} + + + +NTSTATUS +SampDuplicateUserInfo( + PDOMAIN_DISPLAY_USER Destination, + PDOMAIN_DISPLAY_USER Source, + ULONG Index + ) + +/*++ + +Routine Description: + + This routine allocates space in the destination and copies over the + data from the source into it. + +Parameters: + + Destination - The structure to copy data into + + Source - The structure containing the data to copy + + Index - This value will be placed in the destination's Index + field. + +Return Values: + + STATUS_SUCCESS - Destination contains a duplicate of the source data. + +--*/ +{ + NTSTATUS NtStatus; + + Destination->Index = Index; + Destination->Rid = Source->Rid; + Destination->AccountControl = Source->AccountControl; + + // + // Set all strings to NULL initially + // + + RtlInitUnicodeString(&Destination->LogonName, NULL); + RtlInitUnicodeString(&Destination->AdminComment, NULL); + RtlInitUnicodeString(&Destination->FullName, NULL); + + // + // Copy source data into destination + // + + NtStatus = SampDuplicateUnicodeString(&Destination->LogonName, + &Source->LogonName); + if (NT_SUCCESS(NtStatus)) { + NtStatus = SampDuplicateUnicodeString(&Destination->AdminComment, + &Source->AdminComment); + if (NT_SUCCESS(NtStatus)) { + NtStatus = SampDuplicateUnicodeString(&Destination->FullName, + &Source->FullName); + } + } + + // + // Clean up on failure + // + + if (!NT_SUCCESS(NtStatus)) { + SampDiagPrint(DISPLAY_CACHE_ERRORS, + ("SAM: SampDuplicateUserInfo failed, status = 0x%lx\n", NtStatus)); + SampFreeUserInfo(Destination); + } + + return(NtStatus); +} + + + +NTSTATUS +SampDuplicateMachineInfo( + PDOMAIN_DISPLAY_MACHINE Destination, + PDOMAIN_DISPLAY_MACHINE Source, + ULONG Index + ) + +/*++ + +Routine Description: + + This routine allocates space in the destination and copies over the + data from the source into it. + +Parameters: + + Destination - The structure to copy data into + + Source - The structure containing the data to copy + + Index - This value will be placed in the destination's Index + field. + +Return Values: + + STATUS_SUCCESS - Destination contains a duplicate of the source data. + +--*/ +{ + NTSTATUS NtStatus; + + Destination->Index = Index; + Destination->Rid = Source->Rid; + Destination->AccountControl = Source->AccountControl; + + // + // Set all strings to NULL initially + // + + RtlInitUnicodeString(&Destination->Machine, NULL); + RtlInitUnicodeString(&Destination->Comment, NULL); + + // + // Copy source data into destination + // + + NtStatus = SampDuplicateUnicodeString(&Destination->Machine, + &Source->Machine); + if (NT_SUCCESS(NtStatus)) { + NtStatus = SampDuplicateUnicodeString(&Destination->Comment, + &Source->Comment); + } + + // + // Clean up on failure + // + + if (!NT_SUCCESS(NtStatus)) { + SampDiagPrint(DISPLAY_CACHE_ERRORS, + ("SAM: SampDuplicateMachineInfo failed, status = 0x%lx\n", NtStatus)); + SampFreeMachineInfo(Destination); + } + + return(NtStatus); +} + + +NTSTATUS +SampDuplicateGroupInfo( + PDOMAIN_DISPLAY_GROUP Destination, + PDOMAIN_DISPLAY_GROUP Source, + ULONG Index + ) + +/*++ + +Routine Description: + + This routine allocates space in the destination and copies over the + data from the source into it. + +Parameters: + + Destination - The structure to copy data into + + Source - The structure containing the data to copy + + Index - This value will be placed in the destination's Index + field. + +Return Values: + + STATUS_SUCCESS - Destination contains a duplicate of the source data. + +--*/ +{ + NTSTATUS NtStatus; + + Destination->Index = Index; + Destination->Rid = Source->Rid; + Destination->Attributes = Source->Attributes; + + // + // Set all strings to NULL initially + // + + RtlInitUnicodeString(&Destination->Group, NULL); + RtlInitUnicodeString(&Destination->Comment, NULL); + + // + // Copy source data into destination + // + + NtStatus = SampDuplicateUnicodeString(&Destination->Group, + &Source->Group); + if (NT_SUCCESS(NtStatus)) { + NtStatus = SampDuplicateUnicodeString(&Destination->Comment, + &Source->Comment); + } + + // + // Clean up on failure + // + + if (!NT_SUCCESS(NtStatus)) { + SampDiagPrint(DISPLAY_CACHE_ERRORS, + ("SAM: SampDuplicateGroupInfo failed, status = 0x%lx\n", NtStatus)); + SampFreeGroupInfo(Destination); + } + + return(NtStatus); +} + + + +NTSTATUS +SampDuplicateOemUserInfo( + PDOMAIN_DISPLAY_OEM_USER Destination, + PDOMAIN_DISPLAY_USER Source, + ULONG Index + ) + +/*++ + +Routine Description: + + This routine allocates space in the destination and copies over the + data from the source into it. + +Parameters: + + Destination - The structure to copy data into + + Source - The structure containing the data to copy + + Index - This value will be placed in the destination's Index + field. + +Return Values: + + STATUS_SUCCESS - Destination contains a duplicate of a subset of + the source data. + +--*/ +{ + NTSTATUS NtStatus; + + Destination->Index = Index; + + // + // Set all strings to NULL initially + // + + RtlInitString(&Destination->User, NULL); + + + // + // Copy source data into destination + // + + NtStatus = SampUnicodeToOemString(&Destination->User, + &Source->LogonName); + + // + // Clean up on failure + // + + if (!NT_SUCCESS(NtStatus)) { + SampDiagPrint(DISPLAY_CACHE_ERRORS, + ("SAM: SampDuplicateOemUser failed, status = 0x%lx\n", NtStatus)); + RtlInitString(&Destination->User, NULL); + } + + return(NtStatus); +} + + + +NTSTATUS +SampDuplicateOemGroupInfo( + PDOMAIN_DISPLAY_OEM_GROUP Destination, + PDOMAIN_DISPLAY_GROUP Source, + ULONG Index + ) + +/*++ + +Routine Description: + + This routine allocates space in the destination and copies over the + data from the source into it. + +Parameters: + + Destination - The structure to copy data into + + Source - The structure containing the data to copy + + Index - This value will be placed in the destination's Index + field. + +Return Values: + + STATUS_SUCCESS - Destination contains a duplicate of a subset of + the source data. + +--*/ +{ + NTSTATUS NtStatus; + + Destination->Index = Index; + + // + // Set all strings to NULL initially + // + + RtlInitString(&Destination->Group, NULL); + + + // + // Copy source data into destination + // + + NtStatus = SampUnicodeToOemString(&Destination->Group, + &Source->Group); + + // + // Clean up on failure + // + + if (!NT_SUCCESS(NtStatus)) { + SampDiagPrint(DISPLAY_CACHE_ERRORS, + ("SAM: SampDuplicateOemGroup failed, status = 0x%lx\n", NtStatus)); + RtlInitString(&Destination->Group, NULL); + } + + return(NtStatus); +} + + + +VOID +SampSwapUserInfo( + PDOMAIN_DISPLAY_USER Info1, + PDOMAIN_DISPLAY_USER Info2 + ) + +/*++ + +Routine Description: + + Swap the field contents of Info1 and Info2. + +Parameters: + + Info1 & Info2 - The structures whose contents are to be swapped. + + +Return Values: + + None + +--*/ +{ + + DOMAIN_DISPLAY_USER + Tmp; + + Tmp.LogonName = Info1->LogonName; + Tmp.AdminComment = Info1->AdminComment; + Tmp.FullName = Info1->FullName; + Tmp.AccountControl = Info1->AccountControl; + + Info1->LogonName = Info2->LogonName; + Info1->AdminComment = Info2->AdminComment; + Info1->FullName = Info2->FullName; + Info1->AccountControl = Info2->AccountControl; + + Info2->LogonName = Tmp.LogonName; + Info2->AdminComment = Tmp.AdminComment; + Info2->FullName = Tmp.FullName; + Info2->AccountControl = Tmp.AccountControl; + + return; +} + + +VOID +SampSwapMachineInfo( + PDOMAIN_DISPLAY_MACHINE Info1, + PDOMAIN_DISPLAY_MACHINE Info2 + ) + +/*++ + +Routine Description: + + Swap the field contents of Info1 and Info2. + +Parameters: + + Info1 & Info2 - The structures whose contents are to be swapped. + + +Return Values: + + None + +--*/ +{ + + DOMAIN_DISPLAY_MACHINE + Tmp; + + Tmp.Machine = Info1->Machine; + Tmp.Comment = Info1->Comment; + Tmp.AccountControl = Info1->AccountControl; + + Info1->Machine = Info2->Machine; + Info1->Comment = Info2->Comment; + Info1->AccountControl = Info2->AccountControl; + + Info2->Machine = Tmp.Machine; + Info2->Comment = Tmp.Comment; + Info2->AccountControl = Tmp.AccountControl; + + return; +} + + +VOID +SampSwapGroupInfo( + PDOMAIN_DISPLAY_GROUP Info1, + PDOMAIN_DISPLAY_GROUP Info2 + ) + +/*++ + +Routine Description: + + Swap the field contents of Info1 and Info2. + +Parameters: + + Info1 & Info2 - The structures whose contents are to be swapped. + + +Return Values: + + None + +--*/ +{ + + DOMAIN_DISPLAY_GROUP + Tmp; + + Tmp.Group = Info1->Group; + Tmp.Comment = Info1->Comment; + Tmp.Attributes = Info1->Attributes; + + Info1->Group = Info2->Group; + Info1->Comment = Info2->Comment; + Info1->Attributes = Info2->Attributes; + + Info2->Group = Tmp.Group; + Info2->Comment = Tmp.Comment; + Info2->Attributes = Tmp.Attributes; + + return; +} + + +VOID +SampFreeUserInfo( + PDOMAIN_DISPLAY_USER UserInfo + ) + +/*++ + +Routine Description: + + Frees data associated with a userinfo structure. + +Parameters: + + UserInfo - User structure to free + + +Return Values: + + None + +--*/ +{ + SampFreeUnicodeString(&UserInfo->LogonName); + SampFreeUnicodeString(&UserInfo->AdminComment); + SampFreeUnicodeString(&UserInfo->FullName); + + MIDL_user_free( UserInfo ); + return; +} + + + +VOID +SampFreeMachineInfo( + PDOMAIN_DISPLAY_MACHINE MachineInfo + ) + +/*++ + +Routine Description: + + Frees data associated with a machineinfo structure. + +Parameters: + + UserInfo - User structure to free + +Return Values: + + None + +--*/ +{ + SampFreeUnicodeString(&MachineInfo->Machine); + SampFreeUnicodeString(&MachineInfo->Comment); + + MIDL_user_free( MachineInfo ); + return; +} + + +VOID +SampFreeGroupInfo( + PDOMAIN_DISPLAY_GROUP GroupInfo + ) + +/*++ + +Routine Description: + + Frees data associated with a Groupinfo structure. + +Parameters: + + UserInfo - User structure to free + +Return Values: + + None + +--*/ +{ + SampFreeUnicodeString(&GroupInfo->Group); + SampFreeUnicodeString(&GroupInfo->Comment); + + MIDL_user_free( GroupInfo ); + return; +} + + + +VOID +SampFreeOemUserInfo( + PDOMAIN_DISPLAY_OEM_USER UserInfo + ) + +/*++ + +Routine Description: + + Frees data associated with a UserInfo structure. + +Parameters: + + UserInfo - User structure to free + + +Return Values: + + None + +--*/ +{ + SampFreeOemString(&UserInfo->User); + + MIDL_user_free( UserInfo ); + return; +} + + + +VOID +SampFreeOemGroupInfo( + PDOMAIN_DISPLAY_OEM_GROUP GroupInfo + ) + +/*++ + +Routine Description: + + Frees data associated with a GroupInfo structure. + +Parameters: + + GroupInfo - Group structure to free + + +Return Values: + + None + +--*/ +{ + SampFreeOemString(&GroupInfo->Group); + + MIDL_user_free( GroupInfo ); + return; +} + + + +ULONG +SampBytesRequiredUserNode ( + PDOMAIN_DISPLAY_USER Node + ) + +/*++ + +Routine Description: + + This routines returns the total number of bytes required to store all + the elements of the the specified node. + +Parameters: + + Node - The node whose size we will return. + +Return Values: + + Bytes required by node + +--*/ +{ + return( sizeof(*Node) + + Node->LogonName.Length + + Node->AdminComment.Length + + Node->FullName.Length + ); +} + + + +ULONG +SampBytesRequiredMachineNode ( + PDOMAIN_DISPLAY_MACHINE Node + ) + +/*++ + +Routine Description: + + This routines returns the total number of bytes required to store all + the elements of the the specified node. + +Parameters: + + Node - The node whose size we will return. + +Return Values: + + Bytes required by node + +--*/ +{ + return( sizeof(*Node) + + Node->Machine.Length + + Node->Comment.Length + ); +} + + +ULONG +SampBytesRequiredGroupNode ( + PDOMAIN_DISPLAY_GROUP Node + ) + +/*++ + +Routine Description: + + This routines returns the total number of bytes required to store all + the elements of the the specified node. + +Parameters: + + Node - The node whose size we will return. + +Return Values: + + Bytes required by node + +--*/ +{ + return( sizeof(*Node) + Node->Group.Length + Node->Comment.Length ); +} + + +ULONG +SampBytesRequiredOemUserNode ( + PDOMAIN_DISPLAY_OEM_USER Node + ) + +/*++ + +Routine Description: + + This routines returns the total number of bytes required to store all + the elements of the the specified node. + +Parameters: + + Node - The node whose size we will return. + +Return Values: + + Bytes required by node + +--*/ +{ + return( sizeof(*Node) + Node->User.Length ); +} + + +ULONG +SampBytesRequiredOemGroupNode ( + PDOMAIN_DISPLAY_OEM_GROUP Node + ) + +/*++ + +Routine Description: + + This routines returns the total number of bytes required to store all + the elements of the the specified node. + +Parameters: + + Node - The node whose size we will return. + +Return Values: + + Bytes required by node + +--*/ +{ + return( sizeof(*Node) + Node->Group.Length ); +} + + + +PVOID +SampGenericTable2Allocate ( + CLONG BufferSize + ) + +/*++ + +Routine Description: + + This routine is used by the generic table2 package to allocate + memory. + +Parameters: + + BufferSize - the number of bytes needed. + +Return Values: + + Pointer to the allocated memory + +--*/ +{ + PVOID + Buffer; + + Buffer = MIDL_user_allocate(BufferSize); +#if DBG + if (Buffer == NULL) { + SampDiagPrint( DISPLAY_CACHE_ERRORS, + ("SAM: GenTab alloc of %d bytes failed.\n", + BufferSize) ); + } +#endif //DBG + return(Buffer); +} + + + +VOID +SampGenericTable2Free ( + PVOID Buffer + ) + +/*++ + +Routine Description: + + This routines frees memory previously allocated using + SampGenericTable2Allocate(). + +Parameters: + + Node - the memory to free. + +Return Values: + + None. + +--*/ +{ + // + // Free up the base structure + // + + MIDL_user_free(Buffer); + + return; +} + + + +RTL_GENERIC_COMPARE_RESULTS +SampCompareUserNodeByName ( + PVOID Node1, + PVOID Node2 + ) + +/*++ + +Routine Description: + + This routines compares account name fields of two user nodes. + +Parameters: + + Node1, Node2, the nodes to compare + +Return Values: + + GenericLessThan - Node1 < Node2 + GenericGreaterThan - Node1 > Node2 + GenericEqual - Node1 == Node2 + +--*/ +{ + PUNICODE_STRING + NodeName1, + NodeName2; + + LONG + NameComparison; + + NodeName1 = &((PDOMAIN_DISPLAY_USER)Node1)->LogonName; + NodeName2 = &((PDOMAIN_DISPLAY_USER)Node2)->LogonName; + + // + // Do a case-insensitive comparison of the node names + // + + NameComparison = SampCompareDisplayStrings(NodeName1, NodeName2, TRUE); + + if (NameComparison > 0) { + return(GenericGreaterThan); + } + + if (NameComparison < 0) { + return(GenericLessThan); + } + + return(GenericEqual); +} + + +RTL_GENERIC_COMPARE_RESULTS +SampCompareMachineNodeByName ( + PVOID Node1, + PVOID Node2 + ) + +/*++ + +Routine Description: + + This routines compares account name fields of two machine nodes. + +Parameters: + + Node1, Node2, the nodes to compare + +Return Values: + + GenericLessThan - Node1 < Node2 + GenericGreaterThan - Node1 > Node2 + GenericEqual - Node1 == Node2 + +--*/ +{ + PUNICODE_STRING + NodeName1, + NodeName2; + + LONG + NameComparison; + + + + NodeName1 = &((PDOMAIN_DISPLAY_MACHINE)Node1)->Machine; + NodeName2 = &((PDOMAIN_DISPLAY_MACHINE)Node2)->Machine; + + + // + // Do a case-insensitive comparison of the node names + // + + NameComparison = SampCompareDisplayStrings(NodeName1, NodeName2, TRUE); + + if (NameComparison > 0) { + return(GenericGreaterThan); + } + + if (NameComparison < 0) { + return(GenericLessThan); + } + + return(GenericEqual); +} + + +RTL_GENERIC_COMPARE_RESULTS +SampCompareGroupNodeByName ( + PVOID Node1, + PVOID Node2 + ) + +/*++ + +Routine Description: + + This routines compares account name fields of two group nodes. + +Parameters: + + Node1, Node2, the nodes to compare + +Return Values: + + GenericLessThan - Node1 < Node2 + GenericGreaterThan - Node1 > Node2 + GenericEqual - Node1 == Node2 + +--*/ +{ + PUNICODE_STRING + NodeName1, + NodeName2; + + LONG + NameComparison; + + + + NodeName1 = &((PDOMAIN_DISPLAY_GROUP)Node1)->Group; + NodeName2 = &((PDOMAIN_DISPLAY_GROUP)Node2)->Group; + + // + // Do a case-insensitive comparison of the node names + // + + NameComparison = SampCompareDisplayStrings(NodeName1, NodeName2, TRUE); + + if (NameComparison > 0) { + return(GenericGreaterThan); + } + + if (NameComparison < 0) { + return(GenericLessThan); + } + + return(GenericEqual); +} + + +RTL_GENERIC_COMPARE_RESULTS +SampCompareNodeByRid ( + PVOID Node1, + PVOID Node2 + ) + +/*++ + +Routine Description: + + This routines compares the RID of two nodes. + +Parameters: + + Node1, Node2, the nodes to compare + +Return Values: + + GenericLessThan - Node1 < Node2 + GenericGreaterThan - Node1 > Node2 + GenericEqual - Node1 == Node2 + +--*/ +{ + + PDOMAIN_DISPLAY_USER + N1, + N2; + + // + // This routine assumes that all nodes have RIDs in the same + // place, regardless of node type. + // + + ASSERT(FIELD_OFFSET(DOMAIN_DISPLAY_USER, Rid) == + FIELD_OFFSET(DOMAIN_DISPLAY_MACHINE, Rid)); + ASSERT(FIELD_OFFSET(DOMAIN_DISPLAY_USER, Rid) == + FIELD_OFFSET(DOMAIN_DISPLAY_GROUP, Rid)); + + N1 = Node1; + N2 = Node2; + + + if (N1->Rid < N2->Rid) { + return(GenericLessThan); + } + + if (N1->Rid > N2->Rid) { + return(GenericGreaterThan); + } + + return(GenericEqual); +} + + +LONG +SampCompareDisplayStrings( + IN PUNICODE_STRING String1, + IN PUNICODE_STRING String2, + IN BOOLEAN IgnoreCase + ) +/*++ + +Routine Description: + + This routine is a replacement for RtlCompareUnicodeString(). + The difference between RtlCompareUnicodeString() and this routine + is that this routine takes into account various customer selected + sort criteria (like, how is "A-MarilF" sorted in comparison to + "Alfred"). This routine uses CompareStringW() for its comparison + function. + + +Parameters: + + String1 - Points to a unicode string to compare. + + String2 - Points to a unicode string to compare. + + IgnoreCase - indicates whether the comparison is to be case + sensitive (FALSE) or case insensitive (TRUE). + +Return Values: + + + -1 - String1 is lexically less than string 2. That is, String1 + preceeds String2 in an ordered list. + + 0 - String1 and String2 are lexically equivalent. + + -1 - String1 is lexically greater than string 2. That is, String1 + follows String2 in an ordered list. + + +--*/ + + +{ + + INT + CompareResult; + + DWORD + Options = 0; + + if (IgnoreCase) { + Options = NORM_IGNORECASE; + } + + CompareResult = CompareStringW( SampSystemDefaultLCID, + Options, + String1->Buffer, + (String1->Length / sizeof(WCHAR)), + String2->Buffer, + (String2->Length / sizeof(WCHAR)) + ); + + // + // Note that CompareStringW() returns values 1, 2, and 3 for + // string1 less than, equal, or greater than string2 (respectively) + // So, to obtain the RtlCompareUnicodeString() return values of + // -1, 0, and 1 for the same meaning, we simply have to subtract 2. + // + + CompareResult -= 2; + + // + // CompareStringW has the property that alternate spellings may + // produce strings that compare identically while the rest of SAM + // treats the strings as different. To get around this, if the + // strings are the same we call RtlCompareUnicodeString to make + // sure the strings really are the same. + // + + if (CompareResult == 0) { + CompareResult = RtlCompareUnicodeString( + String1, + String2, + IgnoreCase + ); + + } + return(CompareResult); +} + + +#if SAMP_DIAGNOSTICS + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Internal diagnostics // +// // +/////////////////////////////////////////////////////////////////////////////// + +#define SAMP_DISPLAY_DIAG_ENUM_RIDS (0x00000001) + +VOID +SampDisplayDiagnosticSuccess( + IN NTSTATUS s, + IN BOOLEAN Eol + ) + +/*++ + +Routine Description: + + This routine prints "Success" or "Failure" depending upon the + the passed in status value. + + If failure, it also prints the status code. + + +Parameters: + + s - the status value. + + Eol - if TRUE, causes an end of line to also be printed. + + +Return Values: + + +--*/ +{ + if (NT_SUCCESS(s)) { + SampDiagPrint(DISPLAY_CACHE, ("Success")); + } else { + SampDiagPrint(DISPLAY_CACHE, ("Failure - Status: 0x%lx", s)); + } + + if (Eol) { + SampDiagPrint(DISPLAY_CACHE, ("\n")); + } + return; +} + + +VOID +SampDisplayDiagnostic( + VOID + ) + +/*++ + +Routine Description: + + This routine provides internal diagnostics and test capabilities. + + This routine is called whenever an account called SAMP_DIAG is + modified such that the display cache requires updating. + + This routine breakpoints, allowing diagnostic parameters to be set. + + +Parameters: + + None. + +Return Values: + + +--*/ +{ + + ULONG + DiagnosticRunCount = 0, + DiagnosticsToRun = 0; + + SampDiagPrint(DISPLAY_CACHE, + ("SAM: SampDisplayDiagnostic() called.\n" + " Breakpointing to allow diagnostic parameters to be set.\n" + " Diagnostic Flag Word: 0x%lx\n" + " Diagnostic values: \n" + " SamIEnumerateAccountRids: 0x%lx\n" + "\n", + &DiagnosticsToRun, + SAMP_DISPLAY_DIAG_ENUM_RIDS + ) ); + DbgBreakPoint(); + + if ((DiagnosticsToRun & SAMP_DISPLAY_DIAG_ENUM_RIDS) != 0) { + SampDisplayDiagEnumRids(); + DiagnosticRunCount++; + } + + + SampDiagPrint(DISPLAY_CACHE, + ("SAM: SampDisplayDiagnostic() - %d diagnostics run.\n", + DiagnosticRunCount) ); + + + return; +} + + +VOID +SampDisplayDiagEnumRids( + VOID + ) + +/*++ + +Routine Description: + + This routine tests the RID table enumeration api + (SamIEnumerateAccountRids()). + + +Parameters: + + None. + +Return Values: + + +--*/ +{ + NTSTATUS + NtStatus; + + ULONG + i, + ReturnCount, + LastRid; + + PULONG + AccountRids; + + SAMPR_HANDLE + Server, + Domain; + + SampDiagPrint(DISPLAY_CACHE, + ("SAM: Testing SamIEnumerateAccountRids...\n")); + + + NtStatus = SamIConnect( L"", //ServerName + &Server, //ServerHandle + 0, //DesiredAccess + TRUE //TrustedClient + ); + ASSERT(NT_SUCCESS(NtStatus)); + + NtStatus = SamrOpenDomain( Server, + 0, //DesiredAccess + SampDefinedDomains[1].Sid, //DomainId + &Domain + ); + ASSERT(NT_SUCCESS(NtStatus)); + + + + + /////////////////////////////////////////////////////////////////// + // // + // Enumerate both USERs and GLOBAL GROUPs // + // // + /////////////////////////////////////////////////////////////////// + + + SampDiagPrint(DISPLAY_CACHE, + (" Enumerating first three users and global groups...") ); + NtStatus = SamIEnumerateAccountRids( Domain, + SAM_USER_ACCOUNT | SAM_GLOBAL_GROUP_ACCOUNT, + 0, //StartingRid + 3*sizeof(ULONG), //PreferedMaximumLength + &ReturnCount, + &AccountRids + ); + + SampDisplayDiagnosticSuccess( NtStatus, TRUE ); + if (NT_SUCCESS(NtStatus)) { + SampDiagPrint(DISPLAY_CACHE, + (" %d entries returned.\n", ReturnCount)); + if (ReturnCount > 0) { + ASSERT(AccountRids != NULL); + for (i=0; i<ReturnCount; i++) { + SampDiagPrint(DISPLAY_CACHE, + (" 0x%lx\n", AccountRids[i])); + } + LastRid = AccountRids[ReturnCount-1]; + MIDL_user_free(AccountRids); + } else { + ASSERT(AccountRids == NULL); + } + } + + + // + // Now try to continue for another 100 accounts + // + + + SampDiagPrint(DISPLAY_CACHE, + (" Enumerating next 100 users and global groups...") ); + NtStatus = SamIEnumerateAccountRids( Domain, + SAM_USER_ACCOUNT | SAM_GLOBAL_GROUP_ACCOUNT, + LastRid, //StartingRid + 100*sizeof(ULONG), //PreferedMaximumLength + &ReturnCount, + &AccountRids + ); + + SampDisplayDiagnosticSuccess( NtStatus, TRUE ); + if (NT_SUCCESS(NtStatus)) { + SampDiagPrint(DISPLAY_CACHE, + (" %d entries returned.\n", ReturnCount)); + if (ReturnCount > 0) { + ASSERT(AccountRids != NULL); + for (i=0; i<ReturnCount; i++) { + SampDiagPrint(DISPLAY_CACHE, + (" 0x%lx\n", AccountRids[i])); + } + LastRid = AccountRids[ReturnCount-1]; + MIDL_user_free(AccountRids); + } else { + ASSERT(AccountRids == NULL); + } + } + + + // + // Now try to continue for another 4000 accounts + // + + + if (NtStatus == STATUS_MORE_ENTRIES) { + SampDiagPrint(DISPLAY_CACHE, + (" Enumerating next 4000 users and global groups...") ); + NtStatus = SamIEnumerateAccountRids( Domain, + SAM_USER_ACCOUNT | SAM_GLOBAL_GROUP_ACCOUNT, + LastRid, //StartingRid + 400*sizeof(ULONG), //PreferedMaximumLength + &ReturnCount, + &AccountRids + ); + + SampDisplayDiagnosticSuccess( NtStatus, TRUE ); + if (NT_SUCCESS(NtStatus)) { + SampDiagPrint(DISPLAY_CACHE, + (" %d entries returned.\n", ReturnCount)); + if (ReturnCount > 0) { + ASSERT(AccountRids != NULL); + i=0; + if (ReturnCount > 8) { + for (i=0; i<ReturnCount-8; i=i+8) { + SampDiagPrint(DISPLAY_CACHE, + (" 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx\n", + AccountRids[i+0], AccountRids[i+1], + AccountRids[i+2], AccountRids[i+3], + AccountRids[i+4], AccountRids[i+5], + AccountRids[i+6], AccountRids[i+7] )); + } + } + for (i=i; i<ReturnCount; i++) { + SampDiagPrint(DISPLAY_CACHE, + (" 0x%lx ", AccountRids[i])); + } + SampDiagPrint(DISPLAY_CACHE, ("\n")); + LastRid = AccountRids[i]; + MIDL_user_free(AccountRids); + } else { + ASSERT(AccountRids == NULL); + } + } + } + + + + /////////////////////////////////////////////////////////////////// + // // + // Now try just USER accounts // + // // + /////////////////////////////////////////////////////////////////// + + SampDiagPrint(DISPLAY_CACHE, + (" Enumerating first three users ...") ); + NtStatus = SamIEnumerateAccountRids( Domain, + SAM_USER_ACCOUNT, + 0, //StartingRid + 3*sizeof(ULONG), //PreferedMaximumLength + &ReturnCount, + &AccountRids + ); + + SampDisplayDiagnosticSuccess( NtStatus, TRUE ); + if (NT_SUCCESS(NtStatus)) { + SampDiagPrint(DISPLAY_CACHE, + (" %d entries returned.\n", ReturnCount)); + if (ReturnCount > 0) { + ASSERT(AccountRids != NULL); + for (i=0; i<ReturnCount; i++) { + SampDiagPrint(DISPLAY_CACHE, + (" 0x%lx\n", AccountRids[i])); + } + LastRid = AccountRids[ReturnCount-1]; + MIDL_user_free(AccountRids); + } else { + ASSERT(AccountRids == NULL); + } + } + + + // + // Now try to continue for another 100 accounts + // + + + SampDiagPrint(DISPLAY_CACHE, + (" Enumerating next 100 users...") ); + NtStatus = SamIEnumerateAccountRids( Domain, + SAM_USER_ACCOUNT, + LastRid, //StartingRid + 100*sizeof(ULONG), //PreferedMaximumLength + &ReturnCount, + &AccountRids + ); + + SampDisplayDiagnosticSuccess( NtStatus, TRUE ); + if (NT_SUCCESS(NtStatus)) { + SampDiagPrint(DISPLAY_CACHE, + (" %d entries returned.\n", ReturnCount)); + if (ReturnCount > 0) { + ASSERT(AccountRids != NULL); + for (i=0; i<ReturnCount; i++) { + SampDiagPrint(DISPLAY_CACHE, + (" 0x%lx\n", AccountRids[i])); + } + LastRid = AccountRids[ReturnCount-1]; + MIDL_user_free(AccountRids); + } else { + ASSERT(AccountRids == NULL); + } + } + + + // + // Now try to continue for another 4000 accounts + // + + + if (NtStatus == STATUS_MORE_ENTRIES) { + SampDiagPrint(DISPLAY_CACHE, + (" Enumerating next 4000 users...") ); + NtStatus = SamIEnumerateAccountRids( Domain, + SAM_USER_ACCOUNT, + LastRid, //StartingRid + 400*sizeof(ULONG), //PreferedMaximumLength + &ReturnCount, + &AccountRids + ); + + SampDisplayDiagnosticSuccess( NtStatus, TRUE ); + if (NT_SUCCESS(NtStatus)) { + SampDiagPrint(DISPLAY_CACHE, + (" %d entries returned.\n", ReturnCount)); + if (ReturnCount > 0) { + ASSERT(AccountRids != NULL); + i=0; + if (ReturnCount > 8) { + for (i=0; i<ReturnCount-8; i=i+8) { + SampDiagPrint(DISPLAY_CACHE, + (" 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx\n", + AccountRids[i+0], AccountRids[i+1], + AccountRids[i+2], AccountRids[i+3], + AccountRids[i+4], AccountRids[i+5], + AccountRids[i+6], AccountRids[i+7] )); + } + } + for (i=i; i<ReturnCount; i++) { + SampDiagPrint(DISPLAY_CACHE, + (" 0x%lx ", AccountRids[i])); + } + SampDiagPrint(DISPLAY_CACHE, ("\n")); + LastRid = AccountRids[i]; + MIDL_user_free(AccountRids); + } else { + ASSERT(AccountRids == NULL); + } + } + } + + + /////////////////////////////////////////////////////////////////// + // // + // Now just try GLOBAL GROUPs // + // // + /////////////////////////////////////////////////////////////////// + + + SampDiagPrint(DISPLAY_CACHE, + (" Enumerating first three global groups...") ); + NtStatus = SamIEnumerateAccountRids( Domain, + SAM_GLOBAL_GROUP_ACCOUNT, + 0, //StartingRid + 3*sizeof(ULONG), //PreferedMaximumLength + &ReturnCount, + &AccountRids + ); + + SampDisplayDiagnosticSuccess( NtStatus, TRUE ); + if (NT_SUCCESS(NtStatus)) { + SampDiagPrint(DISPLAY_CACHE, + (" %d entries returned.\n", ReturnCount)); + if (ReturnCount > 0) { + ASSERT(AccountRids != NULL); + for (i=0; i<ReturnCount; i++) { + SampDiagPrint(DISPLAY_CACHE, + (" 0x%lx\n", AccountRids[i])); + } + LastRid = AccountRids[ReturnCount-1]; + MIDL_user_free(AccountRids); + } else { + ASSERT(AccountRids == NULL); + } + } + + + // + // Now try to continue for another 100 accounts + // + + + SampDiagPrint(DISPLAY_CACHE, + (" Enumerating next 100 global groups...") ); + NtStatus = SamIEnumerateAccountRids( Domain, + SAM_GLOBAL_GROUP_ACCOUNT, + LastRid, //StartingRid + 100*sizeof(ULONG), //PreferedMaximumLength + &ReturnCount, + &AccountRids + ); + + SampDisplayDiagnosticSuccess( NtStatus, TRUE ); + if (NT_SUCCESS(NtStatus)) { + SampDiagPrint(DISPLAY_CACHE, + (" %d entries returned.\n", ReturnCount)); + if (ReturnCount > 0) { + ASSERT(AccountRids != NULL); + for (i=0; i<ReturnCount; i++) { + SampDiagPrint(DISPLAY_CACHE, + (" 0x%lx\n", AccountRids[i])); + } + LastRid = AccountRids[ReturnCount-1]; + MIDL_user_free(AccountRids); + } else { + ASSERT(AccountRids == NULL); + } + } + + + // + // Now try to continue for another 4000 accounts + // + + + if (NtStatus == STATUS_MORE_ENTRIES) { + SampDiagPrint(DISPLAY_CACHE, + (" Enumerating next 4000 global groups...") ); + NtStatus = SamIEnumerateAccountRids( Domain, + SAM_GLOBAL_GROUP_ACCOUNT, + LastRid, //StartingRid + 4000*sizeof(ULONG), //PreferedMaximumLength + &ReturnCount, + &AccountRids + ); + + SampDisplayDiagnosticSuccess( NtStatus, TRUE ); + if (NT_SUCCESS(NtStatus)) { + SampDiagPrint(DISPLAY_CACHE, + (" %d entries returned.\n", ReturnCount)); + if (ReturnCount > 0) { + ASSERT(AccountRids != NULL); + i=0; + if (ReturnCount > 8) { + for (i=0; i<ReturnCount-8; i=i+8) { + SampDiagPrint(DISPLAY_CACHE, + (" 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx\n", + AccountRids[i+0], AccountRids[i+1], + AccountRids[i+2], AccountRids[i+3], + AccountRids[i+4], AccountRids[i+5], + AccountRids[i+6], AccountRids[i+7] )); + } + } + for (i=i; i<ReturnCount; i++) { + SampDiagPrint(DISPLAY_CACHE, + (" 0x%lx ", AccountRids[i])); + } + SampDiagPrint(DISPLAY_CACHE, ("\n")); + LastRid = AccountRids[i]; + MIDL_user_free(AccountRids); + } else { + ASSERT(AccountRids == NULL); + } + } + } + + + + // + // Now try to continue an enumeration from the very last RID. + // At one point in time, this use to restart the enumeration + // (which was not correct behaviour). It should indicate that + // there are no more entries. + // + + SampDiagPrint(DISPLAY_CACHE, + (" Enumerating next 5 global groups.." + " (should be none to enumerate) ...") ); + NtStatus = SamIEnumerateAccountRids( Domain, + SAM_GLOBAL_GROUP_ACCOUNT, + LastRid, //StartingRid + 5*sizeof(ULONG), //PreferedMaximumLength + &ReturnCount, + &AccountRids + ); + + SampDisplayDiagnosticSuccess( NtStatus, TRUE ); + if (NT_SUCCESS(NtStatus)) { + SampDiagPrint(DISPLAY_CACHE, + (" %d entries returned.\n", ReturnCount)); + if (ReturnCount > 0) { + SampDiagPrint(DISPLAY_CACHE, + (" ERROR - there should be no RIDs returned ! !\n")); + ASSERT(AccountRids != NULL); + i=0; + if (ReturnCount > 8) { + for (i=0; i<ReturnCount-8; i=i+8) { + SampDiagPrint(DISPLAY_CACHE, + (" 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx\n", + AccountRids[i+0], AccountRids[i+1], + AccountRids[i+2], AccountRids[i+3], + AccountRids[i+4], AccountRids[i+5], + AccountRids[i+6], AccountRids[i+7] )); + } + } + for (i=i; i<ReturnCount; i++) { + SampDiagPrint(DISPLAY_CACHE, + (" 0x%lx ", AccountRids[i])); + } + SampDiagPrint(DISPLAY_CACHE, ("\n")); + LastRid = AccountRids[i]; + MIDL_user_free(AccountRids); + } else { + ASSERT(AccountRids == NULL); + } + } + + + + + + /////////////////////////////////////////////////////////////////// + // // + // Hmmm, can't close handles because it will conflict // + // with the rxact state we already have going. Bummer. // + // // + /////////////////////////////////////////////////////////////////// + + //NtStatus = SamrCloseHandle( &Domain ); ASSERT(NT_SUCCESS(NtStatus)); + //NtStatus = SamrCloseHandle( &Server ); ASSERT(NT_SUCCESS(NtStatus)); + + return; +} + +#endif //SAMP_DIAGNOSTICS diff --git a/private/newsam2/server/dll/makefile b/private/newsam2/server/dll/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/newsam2/server/dll/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/newsam2/server/dll/makefile.inc b/private/newsam2/server/dll/makefile.inc new file mode 100644 index 000000000..4cedea558 --- /dev/null +++ b/private/newsam2/server/dll/makefile.inc @@ -0,0 +1,2 @@ +sampmsgs.h msg00001.bin: ..\sampmsgs.mc + mc -v ..\sampmsgs.mc diff --git a/private/newsam2/server/dll/samsrv.def b/private/newsam2/server/dll/samsrv.def new file mode 100644 index 000000000..73d0792e4 --- /dev/null +++ b/private/newsam2/server/dll/samsrv.def @@ -0,0 +1,127 @@ +LIBRARY SAMSRV + +DESCRIPTION 'Security Account Manager Server' + +EXPORTS + +; +; Initialization +; + + SamIInitialize + + +; +; Internal routines to support NetLogon +; + + SamIAccountRestrictions + SamIConnect + +; +; Internal routines to support clients in the Security Process. +; + + SamIFree_SAMPR_SR_SECURITY_DESCRIPTOR + SamIFree_SAMPR_DOMAIN_INFO_BUFFER + SamIFree_SAMPR_DISPLAY_INFO_BUFFER + SamIFree_SAMPR_ENUMERATION_BUFFER + SamIFree_SAMPR_PSID_ARRAY + SamIFree_SAMPR_ULONG_ARRAY + SamIFree_SAMPR_RETURNED_USTRING_ARRAY + SamIFree_SAMPR_GROUP_INFO_BUFFER + SamIFree_SAMPR_ALIAS_INFO_BUFFER + SamIFree_SAMPR_GET_MEMBERS_BUFFER + SamIFree_SAMPR_USER_INFO_BUFFER + SamIFree_SAMPR_GET_GROUPS_BUFFER + + SamIGetPrivateData + SamISetPrivateData + SamICreateAccountByRid + SamIGetSerialNumberDomain + SamISetSerialNumberDomain + SamINotifyDelta + SamISetAuditingInformation + SamIEnumerateAccountRids + + + +; +; Exported RPC Services +; + + SamrConnect + SamrCloseHandle + SamrShutdownSamServer + + SamrSetSecurityObject + SamrQuerySecurityObject + + SamrLookupDomainInSamServer + SamrEnumerateDomainsInSamServer + + SamrOpenAlias + SamrDeleteAlias + SamrQueryInformationAlias + SamrSetInformationAlias + SamrAddMemberToAlias + SamrAddMultipleMembersToAlias + SamrRemoveMemberFromAlias + SamrRemoveMultipleMembersFromAlias + SamrGetMembersInAlias + + SamrOpenDomain + SamrQueryInformationDomain + SamrSetInformationDomain + SamrCreateGroupInDomain + SamrEnumerateGroupsInDomain + SamrCreateUserInDomain + SamrEnumerateUsersInDomain + SamrCreateAliasInDomain + SamrEnumerateAliasesInDomain + SamrGetAliasMembership + SamrRemoveMemberFromForeignDomain + + SamrLookupNamesInDomain + SamrLookupIdsInDomain + + SamrQueryDisplayInformation + + SamrOpenGroup + SamrQueryInformationGroup + SamrSetInformationGroup + SamrAddMemberToGroup + SamrDeleteGroup + SamrRemoveMemberFromGroup + SamrGetMembersInGroup + SamrSetMemberAttributesOfGroup + + SamrOpenUser + SamrDeleteUser + SamrQueryInformationUser + SamrSetInformationUser + SamrChangePasswordUser + SamrGetGroupsForUser + +; +; Routines used by the client side, but not directly by the user (these +; can not return information that is not already obtainable from the client +; side). +; + + SamrGetUserDomainPasswordInformation + +; +; Routines used for testing; these only work on special builds. +; + + SamrTestPrivateFunctionsDomain + SamrTestPrivateFunctionsUser + +; +; Interfaces used only by bldsam2 +; + + SampInitializeRegistry + SampRtlConvertUlongToUnicodeString + diff --git a/private/newsam2/server/dll/samsrv.prf b/private/newsam2/server/dll/samsrv.prf new file mode 100644 index 000000000..c83a7a0a9 --- /dev/null +++ b/private/newsam2/server/dll/samsrv.prf @@ -0,0 +1,699 @@ +SamrGetAliasMembership@12 +SampAlQueryAliasMembership@12 +SampAlLookupMemberAccount@12 +SampAlLookupMemberDomain@12 +SampAlInfoIsValidForDomain@4 +SampLookupContext@16 +SampDeReferenceContext@8 +SampValidateContextAddress@4 +SampAcquireReadLock@0 +SampReleaseReadLock@0 +SampSetTransactionDomain@4 +SampSplitSid@12 +SampGetFixedAttributes@12 +SampGetUnicodeStringAttribute@16 +SampGetAccessAttribute@20 +SampGetLargeIntArrayAttribute@20 +SampGetLogonHoursAttribute@16 +SampValidateAttributes@8 +SampObjectAttributeAddress@8 +SampObjectAttributeLength@8 +SampObjectAttributeQualifier@8 +SampGetAttributeBufferReadInfo@20 +SampExtendAttributeBuffer@8 +SampReadRegistryAttribute@20 +SampFreeAttributeBuffer@4 +SamrCloseHandle@4 +SampCreateContext@8 +SampDeleteContext@4 +SampReferenceContext@4 +SampInvalidateContextAddress@4 +SampRtlConvertUlongToUnicodeString@20 +SampAddNewValidContextAddress@4 +SampGetPrivateUserData@12 +SamIFree_SAMPR_ULONG_ARRAY@4 +SamIFree_SAMPR_USER_INFO_BUFFER@8 +SamIFree_SAMPR_GET_GROUPS_BUFFER@4 +_fgs__RPC_UNICODE_STRING@4 +_fgs__SAMPR_SR_SECURITY_DESCRIPTOR@4 +_fgs__SAMPR_GET_GROUPS_BUFFER@4 +_fgs__SAMPR_LOGON_HOURS@4 +_fgs__SAMPR_ULONG_ARRAY@4 +_fgs__SAMPR_USER_ALL_INFORMATION@4 +_fgu__SAMPR_USER_INFO_BUFFER@8 +SampGetObjectSD@12 +SampValidateObjectAccess@12 +SamrOpenUser@16 +SamrQueryInformationUser@12 +SamrGetGroupsForUser@8 +SamIAccountRestrictions@24 +SampGetPasswordMustChange@20 +SampRetrieveUserPasswords@24 +SampRetrieveUserMembership@16 +SampRetrieveUserLogonHours@8 +SampRetrieveUserV1aFixed@8 +SampMatchworkstation@8 +SampInitUnicodeString@8 +SampAppendUnicodeString@8 +SampFreeUnicodeString@4 +SampBuildAccountSubKeyName@16 +SampOpenAccount@24 +SampCreateAccountContext@20 +SamrLookupNamesInDomain@20 +SampBuildAccountKeyName@12 +SamrQueryDisplayInformation@32 +SamrGetDisplayEnumerationIndex@16 +SampInitializeDisplayInformation@4 +SampDeleteDisplayInformation@8 +SampCreateDisplayInformation@4 +SampUpdateDisplayInformation@12 +SampDeleteDisplayAccount@12 +SampAddDisplayAccount@12 +SampUpdateDisplayAccount@12 +SampInitializeUserInfo@12 +SampInitializeMachineInfo@12 +SampDuplicateUserInfo@12 +SampDuplicateMachineInfo@12 +SampFreeUserInfo@4 +SampFreeMachineInfo@4 +SampBytesRequiredUserNode@4 +SampBytesRequiredMachineNode@4 +SamrOpenAlias@16 +SamrQueryInformationAlias@12 +SamrSetInformationAlias@12 +SamrDeleteAlias@4 +SamrAddMemberToAlias@8 +SamrRemoveMemberFromAlias@8 +SamrGetMembersInAlias@8 +SampRemoveAccountFromAllAliases@20 +SampRetrieveAliasMembership@16 +SampAddAccountToAlias@8 +SampRemoveAccountFromAlias@8 +SampAddAliasToAccountMembership@8 +SampRemoveAliasFromAccountMembership@8 +SampRemoveAliasFromAllAccounts@4 +SampRetrieveAliasMembers@12 +SampDeleteAliasKeys@4 +SampDeleteAliasMembershipKeysForAccount@4 +SampAdjustAliasDomainsCount@4 +SampValidateNewAliasMember@4 +SampChangeAliasAccountName@12 +SampAlSlowQueryAliasMembership@12 +SampAlQueryMembersOfAlias@8 +SampAlAddMembersToAlias@12 +SampAlRemoveMembersFromAlias@12 +SampAlLookupMembersInAlias@16 +SampAlDeleteAlias@4 +SampAlRemoveAccountFromAllAliases@20 +SampAlBuildAliasInformation@0 +SampAlCreateMemberAccount@20 +SampAlAllocateMemberAccount@16 +SampAlGrowMemberAccount@16 +SampAlAddAliasesToMemberAccount@20 +SampAlLookupAliasesInMemberAccount@12 +SampAlRemoveAliasesFromMemberAccount@20 +SampAlDeleteMemberAccount@12 +SampAlCreateMemberDomain@12 +SampAlAllocateMemberDomain@12 +SampAlGrowMemberDomain@12 +SampAlDeleteMemberDomain@8 +SampAlCreateMemberAliasList@12 +SampAlGrowMemberAliasList@8 +SampAlBuildAliasInformationKeyNames@4 +SampAlBuildMemberAliasList@4 +SampAlInfoIsValidForAlias@4 +SampAlFastRestoreList@20 +SampInitObjectInfoAttributes@0 +SampStoreObjectAttributes@8 +SampDeleteAttributeKeys@4 +SampSetFixedAttributes@8 +SampSetUnicodeStringAttribute@12 +SampGetSidAttribute@16 +SampSetSidAttribute@12 +SampSetAccessAttribute@16 +SampGetUlongArrayAttribute@24 +SampSetUlongArrayAttribute@20 +SampSetLargeIntArrayAttribute@16 +SampGetSidArrayAttribute@24 +SampSetSidArrayAttribute@20 +SampSetLogonHoursAttribute@12 +SampSetVariableAttribute@20 +Usage@0 +UnexpectedProblem@0 +Initialize@0 +InitializeSecurityDescriptors@0 +TmppBuildSamProtection@24 +SampGetDomainPolicy@0 +SetCurrentDomain@4 +InitializeSam@0 +SampCreateDatabaseProtection@4 +CreateBuiltinDomain@0 +CreateAccountDomain@0 +PrepDomain@4 +CreateAlias@20 +CreateGroup@20 +CreateUser@32 +BuildPrimaryDomainSid@4 +BuildAccountSid@8 +UpdateAliasXReference@8 +OpenAliasMember@8 +OpenOrCreateAccountRidKey@12 +OpenOrCreateAliasDomainKey@8 +AppendAliasDomainNameToUnicodeString@8 +TmppGetAccountDomainInfo@4 +SampGetServerRole@0 +SampGetPrimaryDomainInfo@0 +SampRestoreDefaultDacl@0 +SampDetermineSetupEnvironment@0 +SampInitializeRegistry@0 +SampGetMessageStrings@20 +SampInvalidateGroupContexts@4 +SampInvalidateAliasContexts@4 +SampInvalidateUserContexts@4 +SampInvalidateContextListKeys@8 +SamrOpenDomain@16 +SamrQueryInformationDomain@12 +SamrSetInformationDomain@12 +SampCreateGroupInDomain@24 +SamrCreateGroupInDomain@20 +SamrEnumerateGroupsInDomain@20 +SampCreateAliasInDomain@24 +SamrCreateAliasInDomain@20 +SamrEnumerateAliasesInDomain@20 +SamrRemoveMemberFromForeignDomain@8 +SamrCreateUserInDomain@20 +SamrEnumerateUsersInDomain@24 +SamrLookupIdsInDomain@20 +SampOpenDomainKey@8 +SampInitializeDomainObject@0 +SampInitializeSingleDomain@4 +SampSetDomainPolicy@0 +SampReInitializeSingleDomain@4 +SampGetAccountDomainInfo@4 +SampCollisionError@4 +SamICreateAccountByRid@28 +SamIGetSerialNumberDomain@12 +SamISetSerialNumberDomain@16 +SamIGetPrivateData@20 +SampSetPrivateUserData@12 +SamISetPrivateData@12 +SamrTestPrivateFunctionsDomain@4 +SamrTestPrivateFunctionsUser@4 +SampBuildDomainKeyName@8 +SamrOpenGroup@16 +SamrQueryInformationGroup@12 +SamrSetInformationGroup@12 +SamrAddMemberToGroup@12 +SamrDeleteGroup@4 +SamrRemoveMemberFromGroup@8 +SamrGetMembersInGroup@8 +SamrSetMemberAttributesOfGroup@12 +SampAddUserToGroup@8 +SampRemoveUserFromGroup@8 +SampRetrieveGroupV1Fixed@8 +SampReplaceGroupV1Fixed@8 +SampRetrieveGroupMembers@12 +SampReplaceGroupMembers@12 +SampDeleteGroupKeys@4 +SampChangeGroupAccountName@12 +SampAddAccountToGroupMembers@8 +SampRemoveAccountFromGroupMembers@8 +SAMPR_HANDLE_rundown@4 +SamIFree_SAMPR_SR_SECURITY_DESCRIPTOR@4 +SamIFree_SAMPR_DOMAIN_INFO_BUFFER@8 +SamIFree_SAMPR_ENUMERATION_BUFFER@4 +SamIFree_SAMPR_PSID_ARRAY@4 +SamIFree_SAMPR_RETURNED_USTRING_ARRAY@4 +SamIFree_SAMPR_GROUP_INFO_BUFFER@8 +SamIFree_SAMPR_ALIAS_INFO_BUFFER@8 +SamIFree_SAMPR_GET_MEMBERS_BUFFER@4 +SamIFree_SAMPR_DISPLAY_INFO_BUFFER@8 +samr_SamrConnect@4 +samr_SamrCloseHandle@4 +samr_SamrSetSecurityObject@4 +samr_SamrQuerySecurityObject@4 +samr_SamrShutdownSamServer@4 +samr_SamrLookupDomainInSamServer@4 +samr_SamrEnumerateDomainsInSamServer@4 +samr_SamrOpenDomain@4 +samr_SamrQueryInformationDomain@4 +samr_SamrSetInformationDomain@4 +samr_SamrCreateGroupInDomain@4 +samr_SamrEnumerateGroupsInDomain@4 +samr_SamrCreateUserInDomain@4 +samr_SamrEnumerateUsersInDomain@4 +samr_SamrCreateAliasInDomain@4 +samr_SamrEnumerateAliasesInDomain@4 +samr_SamrGetAliasMembership@4 +samr_SamrLookupNamesInDomain@4 +samr_SamrLookupIdsInDomain@4 +samr_SamrOpenGroup@4 +samr_SamrQueryInformationGroup@4 +samr_SamrSetInformationGroup@4 +samr_SamrAddMemberToGroup@4 +samr_SamrDeleteGroup@4 +samr_SamrRemoveMemberFromGroup@4 +samr_SamrGetMembersInGroup@4 +samr_SamrSetMemberAttributesOfGroup@4 +samr_SamrOpenAlias@4 +samr_SamrQueryInformationAlias@4 +samr_SamrSetInformationAlias@4 +samr_SamrDeleteAlias@4 +samr_SamrAddMemberToAlias@4 +samr_SamrRemoveMemberFromAlias@4 +samr_SamrGetMembersInAlias@4 +samr_SamrOpenUser@4 +samr_SamrDeleteUser@4 +samr_SamrQueryInformationUser@4 +samr_SamrSetInformationUser@4 +samr_SamrChangePasswordUser@4 +samr_SamrGetGroupsForUser@4 +samr_SamrQueryDisplayInformation@4 +samr_SamrGetDisplayEnumerationIndex@4 +samr_SamrTestPrivateFunctionsDomain@4 +samr_SamrTestPrivateFunctionsUser@4 +samr_SamrGetUserDomainPasswordInformation@4 +samr_SamrRemoveMemberFromForeignDomain@4 +_gns__SID_IDENTIFIER_AUTHORITY@8 +_pns__DOMAIN_SERVER_ROLE_INFORMATION@8 +_gns__DOMAIN_SERVER_ROLE_INFORMATION@8 +_pns__DOMAIN_STATE_INFORMATION@8 +_gns__DOMAIN_STATE_INFORMATION@8 +_gns__CYPHER_BLOCK@8 +_gns__ENCRYPTED_LM_OWF_PASSWORD@8 +_sgs__RPC_UNICODE_STRING@8 +_pgs__RPC_UNICODE_STRING@8 +_ggs__RPC_UNICODE_STRING@12 +_sns__RPC_SID@8 +_pns__RPC_SID@8 +_gns__RPC_SID@12 +_ggs__RPC_SID@16 +_ans__RPC_SID@8 +_ags__RPC_SID@12 +_sgs__SAMPR_RID_ENUMERATION@8 +_pgs__SAMPR_RID_ENUMERATION@8 +_fgs__SAMPR_RID_ENUMERATION@4 +_sgs__SAMPR_ENUMERATION_BUFFER@8 +_pgs__SAMPR_ENUMERATION_BUFFER@8 +_fgs__SAMPR_ENUMERATION_BUFFER@4 +_sgs__SAMPR_SR_SECURITY_DESCRIPTOR@8 +_pgs__SAMPR_SR_SECURITY_DESCRIPTOR@8 +_ggs__SAMPR_SR_SECURITY_DESCRIPTOR@12 +_sgs__SAMPR_GET_GROUPS_BUFFER@8 +_pgs__SAMPR_GET_GROUPS_BUFFER@8 +_sgs__SAMPR_GET_MEMBERS_BUFFER@8 +_pgs__SAMPR_GET_MEMBERS_BUFFER@8 +_fgs__SAMPR_GET_MEMBERS_BUFFER@4 +_sgs__SAMPR_LOGON_HOURS@8 +_pgs__SAMPR_LOGON_HOURS@8 +_ggs__SAMPR_LOGON_HOURS@12 +_sgs__SAMPR_ULONG_ARRAY@8 +_pgs__SAMPR_ULONG_ARRAY@8 +_sgs__SAMPR_SID_INFORMATION@8 +_pgs__SAMPR_SID_INFORMATION@8 +_ggs__SAMPR_SID_INFORMATION@16 +_ags__SAMPR_SID_INFORMATION@8 +_fgs__SAMPR_SID_INFORMATION@4 +_sgs__SAMPR_PSID_ARRAY@8 +_pgs__SAMPR_PSID_ARRAY@8 +_ggs__SAMPR_PSID_ARRAY@12 +_fgs__SAMPR_PSID_ARRAY@4 +_sgs__SAMPR_RETURNED_USTRING_ARRAY@8 +_pgs__SAMPR_RETURNED_USTRING_ARRAY@8 +_fgs__SAMPR_RETURNED_USTRING_ARRAY@4 +_sgs__SAMPR_DOMAIN_GENERAL_INFORMATION@8 +_pgs__SAMPR_DOMAIN_GENERAL_INFORMATION@8 +_ggs__SAMPR_DOMAIN_GENERAL_INFORMATION@12 +_fgs__SAMPR_DOMAIN_GENERAL_INFORMATION@4 +_sgs__SAMPR_DOMAIN_OEM_INFORMATION@8 +_pgs__SAMPR_DOMAIN_OEM_INFORMATION@8 +_ggs__SAMPR_DOMAIN_OEM_INFORMATION@12 +_fgs__SAMPR_DOMAIN_OEM_INFORMATION@4 +_sgs__SAMPR_DOMAIN_NAME_INFORMATION@8 +_pgs__SAMPR_DOMAIN_NAME_INFORMATION@8 +_ggs__SAMPR_DOMAIN_NAME_INFORMATION@12 +_fgs__SAMPR_DOMAIN_NAME_INFORMATION@4 +_sgs_SAMPR_DOMAIN_REPLICATION_INFORMATION@8 +_pgs_SAMPR_DOMAIN_REPLICATION_INFORMATION@8 +_ggs_SAMPR_DOMAIN_REPLICATION_INFORMATION@12 +_fgs_SAMPR_DOMAIN_REPLICATION_INFORMATION@4 +_snu__SAMPR_DOMAIN_INFO_BUFFER@12 +_sgu__SAMPR_DOMAIN_INFO_BUFFER@12 +_pnu__SAMPR_DOMAIN_INFO_BUFFER@12 +_pgu__SAMPR_DOMAIN_INFO_BUFFER@12 +_gnu__SAMPR_DOMAIN_INFO_BUFFER@12 +_ggu__SAMPR_DOMAIN_INFO_BUFFER@16 +_fgu__SAMPR_DOMAIN_INFO_BUFFER@8 +_sgs__SAMPR_GROUP_GENERAL_INFORMATION@8 +_pgs__SAMPR_GROUP_GENERAL_INFORMATION@8 +_ggs__SAMPR_GROUP_GENERAL_INFORMATION@12 +_fgs__SAMPR_GROUP_GENERAL_INFORMATION@4 +_sgs__SAMPR_GROUP_NAME_INFORMATION@8 +_pgs__SAMPR_GROUP_NAME_INFORMATION@8 +_ggs__SAMPR_GROUP_NAME_INFORMATION@12 +_fgs__SAMPR_GROUP_NAME_INFORMATION@4 +_sgs__SAMPR_GROUP_ADM_COMMENT_INFORMATION@8 +_pgs__SAMPR_GROUP_ADM_COMMENT_INFORMATION@8 +_ggs__SAMPR_GROUP_ADM_COMMENT_INFORMATION@12 +_fgs__SAMPR_GROUP_ADM_COMMENT_INFORMATION@4 +_snu__SAMPR_GROUP_INFO_BUFFER@12 +_sgu__SAMPR_GROUP_INFO_BUFFER@12 +_pnu__SAMPR_GROUP_INFO_BUFFER@12 +_pgu__SAMPR_GROUP_INFO_BUFFER@12 +_gnu__SAMPR_GROUP_INFO_BUFFER@12 +_ggu__SAMPR_GROUP_INFO_BUFFER@16 +_fgu__SAMPR_GROUP_INFO_BUFFER@8 +_sgs__SAMPR_ALIAS_GENERAL_INFORMATION@8 +_pgs__SAMPR_ALIAS_GENERAL_INFORMATION@8 +_ggs__SAMPR_ALIAS_GENERAL_INFORMATION@12 +_fgs__SAMPR_ALIAS_GENERAL_INFORMATION@4 +_sgs__SAMPR_ALIAS_NAME_INFORMATION@8 +_pgs__SAMPR_ALIAS_NAME_INFORMATION@8 +_ggs__SAMPR_ALIAS_NAME_INFORMATION@12 +_fgs__SAMPR_ALIAS_NAME_INFORMATION@4 +_sgs__SAMPR_ALIAS_ADM_COMMENT_INFORMATION@8 +_pgs__SAMPR_ALIAS_ADM_COMMENT_INFORMATION@8 +_ggs__SAMPR_ALIAS_ADM_COMMENT_INFORMATION@12 +_fgs__SAMPR_ALIAS_ADM_COMMENT_INFORMATION@4 +_snu__SAMPR_ALIAS_INFO_BUFFER@12 +_sgu__SAMPR_ALIAS_INFO_BUFFER@12 +_pnu__SAMPR_ALIAS_INFO_BUFFER@12 +_pgu__SAMPR_ALIAS_INFO_BUFFER@12 +_gnu__SAMPR_ALIAS_INFO_BUFFER@12 +_ggu__SAMPR_ALIAS_INFO_BUFFER@16 +_fgu__SAMPR_ALIAS_INFO_BUFFER@8 +_sgs__SAMPR_USER_ALL_INFORMATION@8 +_pgs__SAMPR_USER_ALL_INFORMATION@8 +_ggs__SAMPR_USER_ALL_INFORMATION@12 +_sgs__SAMPR_USER_GENERAL_INFORMATION@8 +_pgs__SAMPR_USER_GENERAL_INFORMATION@8 +_ggs__SAMPR_USER_GENERAL_INFORMATION@12 +_fgs__SAMPR_USER_GENERAL_INFORMATION@4 +_sgs__SAMPR_USER_PREFERENCES_INFORMATION@8 +_pgs__SAMPR_USER_PREFERENCES_INFORMATION@8 +_ggs__SAMPR_USER_PREFERENCES_INFORMATION@12 +_fgs__SAMPR_USER_PREFERENCES_INFORMATION@4 +_sgs__SAMPR_USER_PARAMETERS_INFORMATION@8 +_pgs__SAMPR_USER_PARAMETERS_INFORMATION@8 +_ggs__SAMPR_USER_PARAMETERS_INFORMATION@12 +_fgs__SAMPR_USER_PARAMETERS_INFORMATION@4 +_sgs__SAMPR_USER_LOGON_INFORMATION@8 +_pgs__SAMPR_USER_LOGON_INFORMATION@8 +_ggs__SAMPR_USER_LOGON_INFORMATION@12 +_fgs__SAMPR_USER_LOGON_INFORMATION@4 +_sgs__SAMPR_USER_ACCOUNT_INFORMATION@8 +_pgs__SAMPR_USER_ACCOUNT_INFORMATION@8 +_ggs__SAMPR_USER_ACCOUNT_INFORMATION@12 +_fgs__SAMPR_USER_ACCOUNT_INFORMATION@4 +_sgs__SAMPR_USER_A_NAME_INFORMATION@8 +_pgs__SAMPR_USER_A_NAME_INFORMATION@8 +_ggs__SAMPR_USER_A_NAME_INFORMATION@12 +_fgs__SAMPR_USER_A_NAME_INFORMATION@4 +_sgs__SAMPR_USER_F_NAME_INFORMATION@8 +_pgs__SAMPR_USER_F_NAME_INFORMATION@8 +_ggs__SAMPR_USER_F_NAME_INFORMATION@12 +_fgs__SAMPR_USER_F_NAME_INFORMATION@4 +_sgs__SAMPR_USER_NAME_INFORMATION@8 +_pgs__SAMPR_USER_NAME_INFORMATION@8 +_ggs__SAMPR_USER_NAME_INFORMATION@12 +_fgs__SAMPR_USER_NAME_INFORMATION@4 +_sgs__SAMPR_USER_HOME_INFORMATION@8 +_pgs__SAMPR_USER_HOME_INFORMATION@8 +_ggs__SAMPR_USER_HOME_INFORMATION@12 +_fgs__SAMPR_USER_HOME_INFORMATION@4 +_sgs__SAMPR_USER_SCRIPT_INFORMATION@8 +_pgs__SAMPR_USER_SCRIPT_INFORMATION@8 +_ggs__SAMPR_USER_SCRIPT_INFORMATION@12 +_fgs__SAMPR_USER_SCRIPT_INFORMATION@4 +_sgs__SAMPR_USER_PROFILE_INFORMATION@8 +_pgs__SAMPR_USER_PROFILE_INFORMATION@8 +_ggs__SAMPR_USER_PROFILE_INFORMATION@12 +_fgs__SAMPR_USER_PROFILE_INFORMATION@4 +_sgs__SAMPR_USER_ADMIN_COMMENT_INFORMATION@8 +_pgs__SAMPR_USER_ADMIN_COMMENT_INFORMATION@8 +_ggs__SAMPR_USER_ADMIN_COMMENT_INFORMATION@12 +_fgs__SAMPR_USER_ADMIN_COMMENT_INFORMATION@4 +_sgs__SAMPR_USER_WORKSTATIONS_INFORMATION@8 +_pgs__SAMPR_USER_WORKSTATIONS_INFORMATION@8 +_ggs__SAMPR_USER_WORKSTATIONS_INFORMATION@12 +_fgs__SAMPR_USER_WORKSTATIONS_INFORMATION@4 +_sgs__SAMPR_USER_LOGON_HOURS_INFORMATION@8 +_pgs__SAMPR_USER_LOGON_HOURS_INFORMATION@8 +_ggs__SAMPR_USER_LOGON_HOURS_INFORMATION@12 +_fgs__SAMPR_USER_LOGON_HOURS_INFORMATION@4 +_gns__SAMPR_USER_INTERNAL1_INFORMATION@8 +_snu__SAMPR_USER_INFO_BUFFER@12 +_sgu__SAMPR_USER_INFO_BUFFER@12 +_pnu__SAMPR_USER_INFO_BUFFER@12 +_pgu__SAMPR_USER_INFO_BUFFER@12 +_gnu__SAMPR_USER_INFO_BUFFER@12 +_ggu__SAMPR_USER_INFO_BUFFER@16 +_sgs__SAMPR_DOMAIN_DISPLAY_USER@8 +_pgs__SAMPR_DOMAIN_DISPLAY_USER@8 +_fgs__SAMPR_DOMAIN_DISPLAY_USER@4 +_sgs__SAMPR_DOMAIN_DISPLAY_MACHINE@8 +_pgs__SAMPR_DOMAIN_DISPLAY_MACHINE@8 +_fgs__SAMPR_DOMAIN_DISPLAY_MACHINE@4 +_sgs__SAMPR_DOMAIN_DISPLAY_USER_BUFFER@8 +_pgs__SAMPR_DOMAIN_DISPLAY_USER_BUFFER@8 +_fgs__SAMPR_DOMAIN_DISPLAY_USER_BUFFER@4 +_sgs__SAMPR_DOMAIN_DISPLAY_MACHINE_BUFFER@8 +_pgs__SAMPR_DOMAIN_DISPLAY_MACHINE_BUFFER@8 +_fgs__SAMPR_DOMAIN_DISPLAY_MACHINE_BUFFER@4 +_snu__SAMPR_DISPLAY_INFO_BUFFER@12 +_sgu__SAMPR_DISPLAY_INFO_BUFFER@12 +_pnu__SAMPR_DISPLAY_INFO_BUFFER@12 +_pgu__SAMPR_DISPLAY_INFO_BUFFER@12 +_fgu__SAMPR_DISPLAY_INFO_BUFFER@8 +SamIInitialize@0 +SampInitialize@0 +SampLoadPasswordFilterDll@0 +SampEnableAuditPrivilege@0 +SampInitializeDomainDescriptors@4 +SampBuildSamProtection@40 +SamrSetSecurityObject@12 +SampValidatePassedSD@8 +SamrQuerySecurityObject@12 +SampAuditOnClose@4 +SamrConnect@12 +SamIConnect@16 +SamrShutdownSamServer@4 +SamrLookupDomainInSamServer@12 +SamrEnumerateDomainsInSamServer@20 +SampGetUnicodeStringField@12 +SamrDeleteUser@4 +SampIsUserAccountControlValid@8 +SamrSetInformationUser@12 +SamrChangePasswordUser@44 +SamrGetUserDomainPasswordInformation@8 +SampReplaceUserV1aFixed@8 +SampComputePasswordExpired@8 +SampStorePasswordExpired@8 +SampStoreUserPasswords@24 +SampReplaceUserMembership@12 +SampReplaceUserLogonHours@8 +SampRetrieveUserGroupAttribute@12 +SampAddGroupToUserMembership@16 +SampRemoveMembershipUser@12 +SampSetGroupAttributesOfUser@12 +SampDeleteUserKeys@4 +SampAddPasswordHistory@24 +SampCheckPasswordHistory@28 +SampAddDeltaTime@16 +SampChangeUserAccountName@12 +SampAcquireWriteLock@0 +SampFlushThread@4 +SampCommitChanges@0 +SampRefreshRegistry@0 +SampReleaseWriteLock@4 +SampCommitAndRetainWriteLock@0 +SampRetrieveStringFromRegistry@12 +SampPutStringToRegistry@12 +SampBuildDomainSubKeyName@8 +SampBuildAliasMembersKeyName@12 +SampValidateNewAccountName@4 +SampValidateAccountNameChange@8 +SampRetrieveAccountCounts@12 +SampAdjustAccountCount@8 +SampEnumerateAccountNamesCommon@28 +SampEnumerateAccountNames@28 +SampLookupAccountRid@20 +SampLookupAccountName@12 +SampIsAccountBuiltIn@4 +SampCreateFullSid@12 +SampCreateAccountSid@8 +SampNotifyNetlogonOfDelta@24 +SampDuplicateUnicodeString@8 +SampChangeAccountOperatorAccessToMember@8 +SampChangeAccountOperatorAccessToUser@8 +SamINotifyDelta@28 +SamISetAuditingInformation@4 +SampRtlWellKnownPrivilegeCheck@12 +SampWriteEventLog@32 +SampShutdownNotification@4 +CloseHandle@4 +CreateThread@24 +DbgPrint +ElfDeregisterEventSource@4 +ElfRegisterEventSourceW@12 +ElfReportEventW@48 +FormatMessageW@28 +GetLastError@0 +GetTimeZoneInformation@4 +NlLoadNetlogonDll@0 +I_NetNotifyDelta@40 +I_NetNotifyRole@4 +I_NetNotifyMachineAccount@20 +I_NetGetAnyDCName@8 +FreeLibrary@4 +GetProcAddress@8 +I_RpcMapWin32Status@4 +LoadLibraryA@4 +LoadLibraryW@4 +LocalFree@4 +LsaClose@4 +LsaFreeMemory@4 +LsaIAuditSamEvent@32 +LsaIFree_LSAPR_POLICY_INFORMATION@8 +LsaIOpenPolicyTrusted@4 +LsaIQueryInformationPolicyTrusted@8 +LsaOpenPolicy@16 +LsaQueryInformationPolicy@12 +LsarClose@4 +LsarQueryInformationPolicy@12 +MIDL_user_allocate@4 +MIDL_user_free@4 +MIDL_user_reallocate@8 +MIDL_user_size@4 +LocalAlloc@8 +LocalHandle@4 +LocalReAlloc@12 +LocalSize@4 +NDRSContextMarshall@12 +NDRSContextUnmarshall@8 +NDRcopy@12 +NtAccessCheckAndAuditAlarm@44 +NtAdjustPrivilegesToken@24 +NtClose@4 +NtCloseObjectAuditAlarm@12 +NtDelayExecution@8 +NtFlushKey@4 +NtOpenEvent@12 +NtOpenProcess@16 +NtOpenProcessToken@12 +NtOpenThread@16 +NtOpenThreadToken@16 +NtPrivilegeCheck@12 +NtQueryInformationToken@20 +NtQuerySystemTime@4 +NtQueryValueKey@24 +NtRestoreKey@12 +NtSetInformationToken@16 +RpcImpersonateClient@4 +RpcMgmtStopServerListening@4 +RpcRaiseException@4 +RpcRevertToSelf@0 +RpcpInitRpcServer@0 +RpcpAddInterface@8 +RpcpStartRpcServer@8 +RpcpDeleteInterface@4 +RpcpStopRpcServer@4 +EnterCriticalSection@4 +InitializeCriticalSection@4 +InitializeSecurityDescriptor@8 +LeaveCriticalSection@4 +RpcServerListen@12 +RpcServerRegisterIf@12 +RpcServerUnregisterIf@12 +RpcServerUseProtseqEpW@16 +RtlAbortRXact@4 +RtlAbsoluteToSelfRelativeSD@12 +RtlAcquireResourceExclusive@8 +RtlAddAccessAllowedAce@16 +RtlAddAce@20 +RtlAddActionToRXact@24 +RtlAddAttributeActionToRXact@32 +RtlAddAuditAccessAce@24 +RtlAdjustPrivilege@16 +RtlAllocateHeap@12 +RtlAppendUnicodeStringToString@8 +RtlAppendUnicodeToString@8 +RtlApplyRXactNoFlush@4 +RtlAreAllAccessesGranted@8 +RtlAreAnyAccessesGranted@8 +RtlAssert@16 +RtlCompareMemory@12 +RtlCompareUnicodeString@12 +RtlConvertSidToUnicodeString@12 +RtlConvertUiListToApiList@12 +RtlCopySid@12 +RtlCopyUnicodeString@8 +RtlCreateAcl@12 +RtlCreateSecurityDescriptor@8 +RtlEqualComputerName@8 +RtlEqualDomainName@8 +RtlEqualSid@8 +RtlEqualUnicodeString@12 +RtlExtendedIntegerMultiply@12 +RtlExtendedLargeIntegerDivide@16 +RtlFreeHeap@12 +RtlFreeUnicodeString@4 +RtlGetAce@12 +RtlGetDaclSecurityDescriptor@16 +RtlGetGroupSecurityDescriptor@12 +RtlGetNtProductType@4 +RtlGetOwnerSecurityDescriptor@12 +RtlGetSaclSecurityDescriptor@16 +RtlInitUnicodeString@8 +RtlInitializeRXact@12 +RtlInitializeResource@4 +RtlInitializeSid@12 +RtlIntegerToUnicodeString@12 +RtlLengthRequiredSid@4 +RtlLengthSecurityDescriptor@4 +RtlLengthSid@4 +RtlMakeSelfRelativeSD@12 +RtlMapGenericMask@8 +RtlRandom@4 +RtlReleaseResource@4 +RtlSetDaclSecurityDescriptor@16 +RtlSetGroupSecurityDescriptor@12 +RtlSetOwnerSecurityDescriptor@12 +RtlSetSaclSecurityDescriptor@16 +RtlSetSecurityObject@20 +RtlStartRXact@4 +RtlSubAuthorityCountSid@4 +RtlSubAuthoritySid@8 +RtlTimeToTimeFields@8 +RtlValidAcl@4 +RtlValidSid@4 +RtlpNtCreateKey@24 +RtlpNtEnumerateSubKey@16 +RtlpNtOpenKey@16 +RtlpNtQueryValueKey@20 +RtlpNtSetValueKey@16 +SetConsoleCtrlHandler@8 +SetProcessShutdownParameters@8 +SetSecurityDescriptorDacl@16 +SystemFunction006@8 +SystemFunction007@8 +SystemFunction013@12 +SystemFunction015@12 +SystemFunction021@12 +SystemFunction023@12 +SystemFunction024@12 +SystemFunction025@12 +SystemFunction026@12 +SystemFunction027@12 +SystemFunction029@8 +SystemFunction030@8 +SystemFunction031@8 +char_array_from_ndr@16 +char_from_ndr@8 +data_from_ndr@16 +long_array_from_ndr@16 +long_from_ndr@8 +long_from_ndr_temp@12 +midl_allocate@4 +short_from_ndr@8 +short_from_ndr_temp@12 diff --git a/private/newsam2/server/dll/sources b/private/newsam2/server/dll/sources new file mode 100644 index 000000000..c6bd73cb4 --- /dev/null +++ b/private/newsam2/server/dll/sources @@ -0,0 +1,97 @@ +!IF 0 + +Copyright (c) 1989 - 1996 Microsoft Corporation + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + +Author: + + Chris Mayhall (ChrisMay) 03-Jul-1996 + +NOTE: Commented description of this file is in \nt\oak\bin\sources.tpl + +!ENDIF + +DS_BUILD=1 + +MAJORCOMP=sam + +MINORCOMP=server + +DLLDEF=samsrv.def + +TARGETNAME=samsrv + +TARGETPATH=$(BASEDIR)\public\sdk\lib2 + +TARGETTYPE=DYNLINK + +TARGETLIBS= \ + $(BASEDIR)\public\sdk\lib\*\nlrepl.lib \ + $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \ + $(BASEDIR)\public\sdk\lib\*\rpcndr.lib \ + $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \ + $(BASEDIR)\public\sdk\lib\*\kernel32.lib \ + $(BASEDIR)\public\sdk\lib\*\advapi32.lib \ + $(BASEDIR)\public\sdk\lib\*\lsasrv.lib \ + ..\..\..\lsa\crypt\engine\obj\*\rc4c.obj \ + ..\..\..\ds\src\dsamain\dll\obj\*\dsa.lib + +INCLUDES=.;..;..\..\inc;..\..\..\inc;..\..\..\lsa\crypt\engine;..\..\..\ds\src\inc;..\..\..\ds\src\dsamain\include;..\..\..\ds\src\xinc; + +NTPROFILEINPUT=yes + +SOURCES= \ + ..\display.c \ + ..\alias.c \ + ..\almember.c \ + ..\attr.c \ + ..\bldsam3.c \ + ..\close.c \ + ..\context.c \ + ..\dbgutil.c \ + ..\domain.c \ + ..\dsmember.c \ + ..\dslayer.c \ + ..\dsutil.c \ + ..\enum.c \ + ..\gentab2.c \ + ..\global.c \ + ..\group.c \ + ..\notify.c \ + ..\oldstub.c \ + ..\rundown.c \ + ..\samifree.c \ + ..\samrpc_s.c \ + ..\sam_rev.rc \ + ..\samss.c \ + ..\secdescr.c \ + ..\security.c \ + ..\server.c \ + ..\user.c \ + ..\utility.c \ + ..\upgrade.c + +C_DEFINES=-DRPC_NO_WINDOWS_H + +USE_CRTDLL=1 + +# +# Defining the NTTARGETFILES variable causes MAKEFILE.DEF to attempt to +# include .\makefile.inc immediately after it specifies the top +# level targets (all, clean and loc) and their dependencies. MAKEFILE.DEF +# also expands the value of the NTTARGETFILES variable at the end of the +# list of dependencies for the all target. Useful for specifying additional +# targets and dependencies that don't fit the general case covered by +# MAKEFILE.DEF +# + +UMRES=obj\*\sam_rev.res + +NTTARGETFILE0=sampmsgs.h + diff --git a/private/newsam2/server/domain.c b/private/newsam2/server/domain.c new file mode 100644 index 000000000..25f59d36b --- /dev/null +++ b/private/newsam2/server/domain.c @@ -0,0 +1,8151 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + domain.c + +Abstract: + + This file contains services related to the SAM "domain" object. + + +Author: + + Jim Kelly (JimK) 4-July-1991 + +Environment: + + User Mode - Win32 + +Revision History: + + +--*/ + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include <samsrvp.h> +#include "ntlsa.h" +#include "lmcons.h" // LM20_PWLEN +#include "msaudite.h" +#include <nlrepl.h> // I_NetNotifyMachineAccount prototype + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// private service prototypes // +// // +/////////////////////////////////////////////////////////////////////////////// + + +NTSTATUS +SampInitializeSingleDomain( + ULONG Index + ); + +NTSTATUS +SampOpenDomainKey( + IN PSAMP_OBJECT DomainContext, + IN PRPC_SID DomainId + ); + +NTSTATUS +SampSetDomainPolicy( VOID ); + + +NTSTATUS +SampBuildDomainKeyName( + OUT PUNICODE_STRING DomainKeyName, + IN PUNICODE_STRING DomainName OPTIONAL + ); + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// RPC Dispatch routines // +// // +/////////////////////////////////////////////////////////////////////////////// + + + +NTSTATUS +SamrOpenDomain( + IN SAMPR_HANDLE ServerHandle, + IN ACCESS_MASK DesiredAccess, + IN PRPC_SID DomainId, + OUT SAMPR_HANDLE *DomainHandle + ) + +/*++ + +Routine Description: + + This service is the RPC dispatch routine for SamrOpenDomain(). + +Arguments: + + ServerHandle - An active context handle to a Server object. + + Access desired to the domain. + + DomainId - The SID of the domain to open. + + DomainHandle - If successful, will receive the context handle value + for the newly opened domain. Otherwise, NULL is returned. + +Return Value: + + STATUS_SUCCESS - The object has been successfully openned. + + STATUS_INSUFFICIENT_RESOURCES - The SAM server processes doesn't + have sufficient resources to process or accept another connection + at this time. + + Other values as may be returned from: + + NtAccessCheckAndAuditAlarm() + + +--*/ +{ + NTSTATUS NtStatus, IgnoreStatus; + PSAMP_OBJECT ServerContext, DomainContext; + SAMP_OBJECT_TYPE FoundType; + + SAMTRACE("SamrOpenDomain"); + + // + // Grab a read lock + // + + SampAcquireReadLock(); + + + // + // Validate type of, and access to server object. + // + + ServerContext = (PSAMP_OBJECT)ServerHandle; + NtStatus = SampLookupContext( + ServerContext, + SAM_SERVER_LOOKUP_DOMAIN, // DesiredAccess + SampServerObjectType, // ExpectedType + &FoundType + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Try to create a context for the domain. + // + + DomainContext = SampCreateContext( + SampDomainObjectType, + ServerContext->TrustedClient + ); + + if (DomainContext != NULL) { + + // + // Open the specified domain's registry key. + // + + NtStatus = SampOpenDomainKey( + DomainContext, + DomainId + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Reference the object for the validation + // + + SampReferenceContext(DomainContext); + + // + // Validate the caller's access. + // + + NtStatus = SampValidateObjectAccess( + DomainContext, //Context + DesiredAccess, //DesiredAccess + FALSE //ObjectCreation + ); + + // + // Dereference object, discarding any changes + // + + IgnoreStatus = SampDeReferenceContext(DomainContext, FALSE); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + // + // if we didn't pass the access test, then free up the context + // block and return the error status returned from the access + // validation routine. Otherwise, return the context handle + // value. + // + + if (!NT_SUCCESS(NtStatus)) { + SampDeleteContext( DomainContext ); + } else { + (*DomainHandle) = DomainContext; + } + } + + } else { + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // De-reference the server object + // + + IgnoreStatus = SampDeReferenceContext( ServerContext, FALSE ); + } + + // + // Free the read lock + // + + SampReleaseReadLock(); + + return(NtStatus); + + +} + + +NTSTATUS +SamrQueryInformationDomain2( + IN SAMPR_HANDLE DomainHandle, + IN DOMAIN_INFORMATION_CLASS DomainInformationClass, + OUT PSAMPR_DOMAIN_INFO_BUFFER *Buffer + ) +{ + // + // This is a thin veil to SamrQueryInformationDomain(). + // This is needed so that new-release systems can call + // this routine without the danger of passing an info + // level that release 1.0 systems didn't understand. + // + + return( SamrQueryInformationDomain(DomainHandle, DomainInformationClass, Buffer ) ); +} + +NTSTATUS +SamrQueryInformationDomain( + IN SAMPR_HANDLE DomainHandle, + IN DOMAIN_INFORMATION_CLASS DomainInformationClass, + OUT PSAMPR_DOMAIN_INFO_BUFFER *Buffer + ) + +/*++ + +Routine Description: + + This service retrieves information about a domain object. + +Arguments: + + DomainHandle - A handle obtained via a previous call to SamrOpenDomain(). + + DomainInformationClass - Indicates the type of information to retrieve. + + Buffer - Receives the requested information. Several blocks of memory + will be returned: (one) containing a pointer to the (second) which + contains the requested information structure. This block may contain + pointers, which will point to other blocks of allocated memory, such + as string buffers. All of these blocks of memory must be + (independently) deallocated using MIDL_user_free(). + +Return Value: + + + STATUS_SUCCESS - Indicates the service completed successfully. + + STATUS_ACCESS_DENIED - Caller's handle does not have the appropriate + access to the object. + + +--*/ +{ + + NTSTATUS NtStatus, IgnoreStatus; + PSAMP_OBJECT DomainContext; + SAMP_OBJECT_TYPE FoundType; + ACCESS_MASK DesiredAccess; + PSAMP_DEFINED_DOMAINS Domain; + ULONG i; + + // + // Used for tracking allocated blocks of memory - so we can deallocate + // them in case of error. Don't exceed this number of allocated buffers. + // || + // vv + PVOID AllocatedBuffer[10]; + ULONG AllocatedBufferCount = 0; + + SAMTRACE("SamrQueryInformationDomain"); + +#define RegisterBuffer(Buffer) \ + if ((Buffer) != NULL) { \ + ASSERT(AllocatedBufferCount < sizeof(AllocatedBuffer)/sizeof(*AllocatedBuffer)); \ + AllocatedBuffer[AllocatedBufferCount++] = (Buffer); \ + } + +#define AllocateBuffer(BufferPointer, Size) \ + (BufferPointer) = MIDL_user_allocate((Size)); \ + RegisterBuffer((BufferPointer)); + + + // + // Make sure we understand what RPC is doing for (to) us. + // + + ASSERT (Buffer != NULL); + ASSERT ((*Buffer) == NULL); + + + + // + // Set the desired access based upon the Info class + // + + switch (DomainInformationClass) { + + case DomainPasswordInformation: + case DomainLockoutInformation: + + DesiredAccess = DOMAIN_READ_PASSWORD_PARAMETERS; + break; + + + case DomainGeneralInformation: + case DomainLogoffInformation: + case DomainOemInformation: + case DomainNameInformation: + case DomainServerRoleInformation: + case DomainReplicationInformation: + case DomainModifiedInformation: + case DomainStateInformation: + case DomainUasInformation: + case DomainModifiedInformation2: + + DesiredAccess = DOMAIN_READ_OTHER_PARAMETERS; + break; + + + case DomainGeneralInformation2: + + DesiredAccess = DOMAIN_READ_PASSWORD_PARAMETERS | + DOMAIN_READ_OTHER_PARAMETERS; + break; + + default: + return(STATUS_INVALID_INFO_CLASS); + } // end_switch + + + + // + // Allocate the info structure + // + + AllocateBuffer(*Buffer, sizeof(SAMPR_DOMAIN_INFO_BUFFER) ); + if ((*Buffer) == NULL) { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + SampAcquireReadLock(); + + + // + // Validate type of, and access to object. + // + + DomainContext = (PSAMP_OBJECT)DomainHandle; + NtStatus = SampLookupContext( + DomainContext, + DesiredAccess, + SampDomainObjectType, // ExpectedType + &FoundType + ); + + + if (NT_SUCCESS(NtStatus)) { + + Domain = &SampDefinedDomains[ DomainContext->DomainIndex ]; + + + // + // case on the type information requested + // + + switch (DomainInformationClass) { + + case DomainUasInformation: + + (*Buffer)->General.UasCompatibilityRequired = + Domain->UnmodifiedFixed.UasCompatibilityRequired; + + break; + + + case DomainGeneralInformation2: + + + (*Buffer)->General2.LockoutDuration = + Domain->UnmodifiedFixed.LockoutDuration; + + (*Buffer)->General2.LockoutObservationWindow = + Domain->UnmodifiedFixed.LockoutObservationWindow; + + (*Buffer)->General2.LockoutThreshold = + Domain->UnmodifiedFixed.LockoutThreshold; + + + + // + // WARNING - GeneralInformation2 falls into the + // GeneralInformation code for the rest of its processing. + // This action assumes that the beginning of a GeneralInformation2 + // structure is a GeneralInformation structure !!! + // + + // don't break; + + case DomainGeneralInformation: + + /////////////////////////////////////////////////////// + // // + // Warning, the previous case falls into this case. // + // Be aware of this when working on this code. // + // // + /////////////////////////////////////////////////////// + + (*Buffer)->General.ForceLogoff = + *((POLD_LARGE_INTEGER)&Domain->UnmodifiedFixed.ForceLogoff); + + (*Buffer)->General.DomainModifiedCount = + *((POLD_LARGE_INTEGER)&Domain->UnmodifiedFixed.ModifiedCount); + + (*Buffer)->General.DomainServerState = + Domain->UnmodifiedFixed.ServerState; + + (*Buffer)->General.DomainServerRole = + Domain->UnmodifiedFixed.ServerRole; + + (*Buffer)->General.UasCompatibilityRequired = + Domain->UnmodifiedFixed.UasCompatibilityRequired; + + + // + // Copy the domain name from our in-memory structure. + // + + NtStatus = STATUS_INSUFFICIENT_RESOURCES; // Default status if the allocate fails + + AllocateBuffer((*Buffer)->General.DomainName.Buffer, + Domain->ExternalName.MaximumLength ); + + if ((*Buffer)->General.DomainName.Buffer != NULL) { + + NtStatus = STATUS_SUCCESS; + + (*Buffer)->General.DomainName.Length = Domain->ExternalName.Length; + (*Buffer)->General.DomainName.MaximumLength = Domain->ExternalName.MaximumLength; + + RtlCopyMemory((*Buffer)->General.DomainName.Buffer, + Domain->ExternalName.Buffer, + Domain->ExternalName.MaximumLength + ); + + // + // Now get copies of the strings we must retrieve from + // the registry. + // + + NtStatus = SampGetUnicodeStringAttribute( + DomainContext, + SAMP_DOMAIN_OEM_INFORMATION, + TRUE, + (PUNICODE_STRING)&((*Buffer)->General.OemInformation) + ); + + if (NT_SUCCESS(NtStatus)) { + RegisterBuffer((*Buffer)->General.OemInformation.Buffer); + + NtStatus = SampGetUnicodeStringAttribute( + DomainContext, + SAMP_DOMAIN_REPLICA, + TRUE, + (PUNICODE_STRING)&((*Buffer)->General.ReplicaSourceNodeName) // Body + ); + + if (NT_SUCCESS(NtStatus)) { + RegisterBuffer((*Buffer)->General.ReplicaSourceNodeName.Buffer); + } + } + } + + + // + // Get the count of users and groups + // + + if (NT_SUCCESS(NtStatus)) { + NtStatus = SampRetrieveAccountCounts( + &(*Buffer)->General.UserCount, + &(*Buffer)->General.GroupCount, + &(*Buffer)->General.AliasCount ); + } + + + + break; + + + case DomainPasswordInformation: + + (*Buffer)->Password.MinPasswordLength = + Domain->UnmodifiedFixed.MinPasswordLength; + (*Buffer)->Password.PasswordHistoryLength = + Domain->UnmodifiedFixed.PasswordHistoryLength; + (*Buffer)->Password.PasswordProperties = + Domain->UnmodifiedFixed.PasswordProperties; + (*Buffer)->Password.MaxPasswordAge = + Domain->UnmodifiedFixed.MaxPasswordAge; + (*Buffer)->Password.MinPasswordAge = + Domain->UnmodifiedFixed.MinPasswordAge; + + break; + + + case DomainLogoffInformation: + + (*Buffer)->Logoff.ForceLogoff = + Domain->UnmodifiedFixed.ForceLogoff; + + break; + + case DomainOemInformation: + + NtStatus = SampGetUnicodeStringAttribute( + DomainContext, + SAMP_DOMAIN_OEM_INFORMATION, + TRUE, + (PUNICODE_STRING)&((*Buffer)->Oem.OemInformation) + ); + + if (!NT_SUCCESS(NtStatus)) { + RegisterBuffer((*Buffer)->Oem.OemInformation.Buffer); + } + + break; + + case DomainNameInformation: + + // + // Copy the domain name from our in-memory structure. + // + + NtStatus = STATUS_INSUFFICIENT_RESOURCES; // Default status if the allocate fails + + AllocateBuffer((*Buffer)->Name.DomainName.Buffer, + Domain->ExternalName.MaximumLength); + + if ((*Buffer)->Name.DomainName.Buffer != NULL) { + + NtStatus = STATUS_SUCCESS; + + (*Buffer)->Name.DomainName.Length = Domain->ExternalName.Length; + (*Buffer)->Name.DomainName.MaximumLength = Domain->ExternalName.MaximumLength; + + RtlCopyMemory((*Buffer)->Name.DomainName.Buffer, + Domain->ExternalName.Buffer, + Domain->ExternalName.MaximumLength + ); + } + + break; + + case DomainServerRoleInformation: + + (*Buffer)->Role.DomainServerRole = + Domain->UnmodifiedFixed.ServerRole; + + break; + + case DomainReplicationInformation: + + NtStatus = SampGetUnicodeStringAttribute( + DomainContext, + SAMP_DOMAIN_REPLICA, + TRUE, + (PUNICODE_STRING)&((*Buffer)->Replication.ReplicaSourceNodeName) // Body + ); + + if (NT_SUCCESS(NtStatus)) { + RegisterBuffer((*Buffer)->Replication.ReplicaSourceNodeName.Buffer); + } + + break; + + case DomainModifiedInformation2: + + (*Buffer)->Modified2.ModifiedCountAtLastPromotion = + Domain->UnmodifiedFixed.ModifiedCountAtLastPromotion; + + // + // This case falls through to DomainModifiedInformation + // + + + case DomainModifiedInformation: + + ///////////////////////////////// + // // + // WARNING // + // // + // The previous case falls // + // into this one. // + // // + ///////////////////////////////// + + (*Buffer)->Modified.DomainModifiedCount = + Domain->UnmodifiedFixed.ModifiedCount; + (*Buffer)->Modified.CreationTime = + Domain->UnmodifiedFixed.CreationTime; + + break; + + case DomainStateInformation: + + (*Buffer)->State.DomainServerState = + Domain->UnmodifiedFixed.ServerState; + + break; + + + case DomainLockoutInformation: + + (*Buffer)->Lockout.LockoutDuration = + Domain->UnmodifiedFixed.LockoutDuration; + (*Buffer)->Lockout.LockoutObservationWindow = + Domain->UnmodifiedFixed.LockoutObservationWindow; + (*Buffer)->Lockout.LockoutThreshold = + Domain->UnmodifiedFixed.LockoutThreshold; + + break; + + } + + + + + + + // + // De-reference the object + // + + IgnoreStatus = SampDeReferenceContext( DomainContext, FALSE ); + } + + // + // Free the read lock + // + + SampReleaseReadLock(); + + + + // + // If we didn't succeed, free any allocated memory + // + + if (!NT_SUCCESS(NtStatus)) { + for ( i=0; i<AllocatedBufferCount ; i++ ) { + MIDL_user_free( AllocatedBuffer[i] ); + } + *Buffer = NULL; + } + + return(NtStatus); +} + + + +NTSTATUS SamrSetInformationDomain( + IN SAMPR_HANDLE DomainHandle, + IN DOMAIN_INFORMATION_CLASS DomainInformationClass, + IN PSAMPR_DOMAIN_INFO_BUFFER DomainInformation + ) + +/*++ + +Routine Description: + + This API sets the domain information to the values passed in the + buffer. + + +Arguments: + + DomainHandle - A domain handle returned from a previous call to + SamOpenDomain. + + DomainInformationClass - Class of information desired. The + accesses required for each class is shown below: + + Info Level Required Access Type + ------------------------- ---------------------------- + + DomainPasswordInformation DOMAIN_WRITE_PASSWORD_PARAMS + + DomainGeneralInformation (not setable) + + DomainLogoffInformation DOMAIN_WRITE_OTHER_PARAMETERS + + DomainOemInformation DOMAIN_WRITE_OTHER_PARAMETERS + + DomainNameInformation (Not valid for set operations.) + + DomainServerRoleInformation DOMAIN_ADMINISTER_SERVER + + DomainReplicationInformation DOMAIN_ADMINISTER_SERVER + + DomainModifiedInformation (not valid for set operations) + + DomainStateInformation DOMAIN_ADMINISTER_SERVER + + DomainUasInformation DOMAIN_WRITE_OTHER_PARAMETERS + + DomainGeneralInformation2 (not setable) + + DomainLockoutInformation DOMAIN_WRITE_PASSWORD_PARAMS + + + DomainInformation - Buffer where the domain information can be + found. + + +Return Value: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_INVALID_INFO_CLASS - The class provided was invalid. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be disabled before role + changes can be made. + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. + + + +--*/ +{ + NTSTATUS NtStatus, IgnoreStatus; + PSAMP_OBJECT DomainContext; + SAMP_OBJECT_TYPE FoundType; + ACCESS_MASK DesiredAccess; + PSAMP_DEFINED_DOMAINS Domain; + BOOLEAN ReplicateImmediately = FALSE; + ULONG DomainIndex; + LARGE_INTEGER PromotionIncrement = DOMAIN_PROMOTION_INCREMENT; + + +#if DBG + + LARGE_INTEGER + TmpTime; + + TIME_FIELDS + DT1, DT2, DT3, DT4; + +#endif //DBG + + SAMTRACE("SamrSetInformationDomain"); + + // + // Make sure we understand what RPC is doing for (to) us. + // + + ASSERT (DomainInformation != NULL); + + + + // + // Set the desired access based upon the Info class + // + + switch (DomainInformationClass) { + + case DomainPasswordInformation: + case DomainLockoutInformation: + + ReplicateImmediately = TRUE; + DesiredAccess = DOMAIN_WRITE_PASSWORD_PARAMS; + break; + + + case DomainLogoffInformation: + case DomainOemInformation: + + DesiredAccess = DOMAIN_WRITE_OTHER_PARAMETERS; + break; + + + case DomainReplicationInformation: + case DomainStateInformation: + case DomainServerRoleInformation: + + DesiredAccess = DOMAIN_ADMINISTER_SERVER; + break; + + + case DomainModifiedInformation: + case DomainNameInformation: + case DomainGeneralInformation: + case DomainGeneralInformation2: + default: + + return(STATUS_INVALID_INFO_CLASS); + + } // end_switch + + + NtStatus = SampAcquireWriteLock(); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + // + // Validate type of, and access to object. + // + + DomainContext = (PSAMP_OBJECT)DomainHandle; + NtStatus = SampLookupContext( + DomainContext, + DesiredAccess, + SampDomainObjectType, // ExpectedType + &FoundType + ); + + + if ( ( NtStatus == STATUS_INVALID_DOMAIN_ROLE ) && + ( DomainInformationClass == DomainServerRoleInformation ) ) { + + + // + // Non-trusted client isn't being allowed to write to backup + // server. But admin MUST be able to set the server role back + // to primary. So temporarily pretend that administering the + // server isn't a write operation, just long enough for the + // LookupContext to succeed. + // + // Note that before returning INVALID_DOMAIN_ROLE, LookupContext + // verified that the caller otherwise has proper access - if the + // caller didn't, then we would have gotten a different error. + // + + SampObjectInformation[ SampDomainObjectType ].WriteOperations &= + ~DOMAIN_ADMINISTER_SERVER; + + SampTransactionWithinDomain = FALSE; + + NtStatus = SampLookupContext( + DomainContext, + DesiredAccess, + SampDomainObjectType, // ExpectedType + &FoundType + ); + + SampObjectInformation[ SampDomainObjectType ].WriteOperations |= + DOMAIN_ADMINISTER_SERVER; + } + + + if (NT_SUCCESS(NtStatus)) { + + + DomainIndex = DomainContext->DomainIndex; + Domain = &SampDefinedDomains[ DomainIndex ]; + + // + // case on the type information provided + // + + switch (DomainInformationClass) { + + case DomainPasswordInformation: + + if ( + ( DomainInformation->Password.PasswordHistoryLength > + SAMP_MAXIMUM_PASSWORD_HISTORY_LENGTH ) || + + ( DomainInformation->Password.MinPasswordAge.QuadPart > 0) || + + ( DomainInformation->Password.MaxPasswordAge.QuadPart > 0) || + + ( DomainInformation->Password.MaxPasswordAge.QuadPart >= + DomainInformation->Password.MinPasswordAge.QuadPart) || + + ( ( Domain->UnmodifiedFixed.UasCompatibilityRequired ) && + ( DomainInformation->Password.MinPasswordLength > LM20_PWLEN ) ) + ) { + + // + // One of the following is wrong: + // + // 1. The history length is larger than we can allow (and + // still ensure everything will fit in a string) + // 2. The MinPasswordAge isn't a delta time + // 3. The MaxPasswordAge isn't a delta time + // 4. The MaxPasswordAge isn't greater than the + // MinPasswordAge (they're negative delta times) + // 5. UAS compatibility is required, but MinPasswordLength + // is greater than LM's max password length. + // + + NtStatus = STATUS_INVALID_PARAMETER; + + } else { + + Domain->CurrentFixed.MinPasswordLength = + DomainInformation->Password.MinPasswordLength; + + Domain->CurrentFixed.PasswordHistoryLength = + DomainInformation->Password.PasswordHistoryLength; + + Domain->CurrentFixed.PasswordProperties = + DomainInformation->Password.PasswordProperties; + + Domain->CurrentFixed.MaxPasswordAge = + DomainInformation->Password.MaxPasswordAge; + + Domain->CurrentFixed.MinPasswordAge = + DomainInformation->Password.MinPasswordAge; + } + + break; + + + case DomainLogoffInformation: + + Domain->CurrentFixed.ForceLogoff = + DomainInformation->Logoff.ForceLogoff; + + break; + + case DomainOemInformation: + + NtStatus = SampSetUnicodeStringAttribute( + DomainContext, + SAMP_DOMAIN_OEM_INFORMATION, + (PUNICODE_STRING)&(DomainInformation->Oem.OemInformation) + ); + break; + + case DomainServerRoleInformation: + + // + // Only NTAS systems can be demoted. + // + + if (SampProductType != NtProductLanManNt) { + + if ( (DomainInformation->Role.DomainServerRole == + DomainServerRoleBackup) //Trying to demote + ) { + + NtStatus = STATUS_INVALID_DOMAIN_ROLE; + break; + + } + } + + // + // Are we being promoted to primary? + // + + if ( (Domain->UnmodifiedFixed.ServerRole == DomainServerRoleBackup) + && + (DomainInformation->Role.DomainServerRole == DomainServerRolePrimary) + ) { + + + // + // We are being promoted. Increment the ModifiedCount + // by the promotion increment. + // + + SampDefinedDomains[SampTransactionDomainIndex].CurrentFixed.ModifiedCount.QuadPart = + SampDefinedDomains[SampTransactionDomainIndex].CurrentFixed.ModifiedCount.QuadPart + + PromotionIncrement.QuadPart; + + Domain->CurrentFixed.ModifiedCountAtLastPromotion = + Domain->CurrentFixed.ModifiedCount; + + Domain->CurrentFixed.ModifiedCountAtLastPromotion.QuadPart += 1; + + SampDiagPrint( DISPLAY_ROLE_CHANGES, + ("SAM: Role Change: Promoting to primary\n" + " Old ModifiedId: [0x%lx, 0x%lx]\n" + " New ModifiedId: [0x%lx, 0x%lx]\n", + Domain->UnmodifiedFixed.ModifiedCount.HighPart, + Domain->UnmodifiedFixed.ModifiedCount.LowPart, + Domain->CurrentFixed.ModifiedCount.HighPart, + Domain->CurrentFixed.ModifiedCount.LowPart) + ); +#if DBG + } else { + + + SampDiagPrint( DISPLAY_ROLE_CHANGES, + ("SAM: Role Change: Demoting to backup\n" + " ModifiedId: [0x%lx, 0x%lx]\n", + Domain->CurrentFixed.ModifiedCount.HighPart, + Domain->CurrentFixed.ModifiedCount.LowPart ) + ); + +#endif //DBG + } + + + Domain->CurrentFixed.ServerRole = + DomainInformation->Role.DomainServerRole; + + + break; + + case DomainReplicationInformation: + + NtStatus = SampSetUnicodeStringAttribute( + DomainContext, + SAMP_DOMAIN_REPLICA, + (PUNICODE_STRING)&(DomainInformation->Replication.ReplicaSourceNodeName) // Body + ); + break; + + case DomainStateInformation: + + Domain->CurrentFixed.ServerState = + DomainInformation->State.DomainServerState; + + break; + + case DomainLockoutInformation: + + if ( + ( DomainInformation->Lockout.LockoutDuration.QuadPart > 0) || + + ( DomainInformation->Lockout.LockoutObservationWindow.QuadPart > 0 ) + + ) { + + // + // One of the following is wrong: + // + // 1. The LockoutDuration isn't a delta time (or zero). + // 2. The LockoutObservationWindow isn't a delta time (or zero). + // + + NtStatus = STATUS_INVALID_PARAMETER; + + } else { + +#if DBG + TmpTime.QuadPart = -Domain->CurrentFixed.LockoutObservationWindow.QuadPart; + RtlTimeToElapsedTimeFields( &TmpTime, &DT1 ); + TmpTime.QuadPart = -Domain->CurrentFixed.LockoutDuration.QuadPart; + RtlTimeToElapsedTimeFields( &TmpTime, &DT2 ); + TmpTime.QuadPart = -DomainInformation->Lockout.LockoutObservationWindow.QuadPart; + RtlTimeToElapsedTimeFields( &TmpTime, &DT3 ); + TmpTime.QuadPart = -DomainInformation->Lockout.LockoutDuration.QuadPart; + RtlTimeToElapsedTimeFields( &TmpTime, &DT4 ); + + SampDiagPrint( DISPLAY_LOCKOUT, + ("SAM: SetInformationDomain: Changing Lockout values.\n" + " Old:\n" + " Window : [0x%lx, 0x%lx] %d:%d:%d\n" + " Duration : [0x%lx, 0x%lx] %d:%d:%d\n" + " Threshold: %ld\n" + " New:\n" + " Window : [0x%lx, 0x%lx] %d:%d:%d\n" + " Duration : [0x%lx, 0x%lx] %d:%d:%d\n" + " Threshold: %ld\n", + Domain->CurrentFixed.LockoutObservationWindow.HighPart, + Domain->CurrentFixed.LockoutObservationWindow.LowPart, + DT1.Hour, DT1.Minute, DT1.Second, + Domain->CurrentFixed.LockoutDuration.HighPart, + Domain->CurrentFixed.LockoutDuration.LowPart, + DT2.Hour, DT2.Minute, DT2.Second, + Domain->CurrentFixed.LockoutThreshold, + DomainInformation->Lockout.LockoutObservationWindow.HighPart, + DomainInformation->Lockout.LockoutObservationWindow.LowPart, + DT3.Hour, DT3.Minute, DT3.Second, + DomainInformation->Lockout.LockoutDuration.HighPart, + DomainInformation->Lockout.LockoutDuration.LowPart, + DT4.Hour, DT4.Minute, DT4.Second, + DomainInformation->Lockout.LockoutThreshold) + ); +#endif //DBG + + Domain->CurrentFixed.LockoutDuration = + DomainInformation->Lockout.LockoutDuration; + + Domain->CurrentFixed.LockoutObservationWindow = + DomainInformation->Lockout.LockoutObservationWindow; + + Domain->CurrentFixed.LockoutThreshold = + DomainInformation->Lockout.LockoutThreshold; + + + + } + + break; + } + + + // + // Generate an audit if necessary + // + + if (NT_SUCCESS(NtStatus) && + SampDoAccountAuditing(DomainIndex)) { + + LsaIAuditSamEvent( + STATUS_SUCCESS, + SE_AUDITID_DOMAIN_POLICY_CHANGE, // AuditId + Domain->Sid, // Domain SID + NULL, // Member Rid (not used) + NULL, // Member Sid (not used) + NULL, // Account Name (not used) + &Domain->ExternalName, // Domain + NULL, // Account Rid (not used) + NULL // Privileges used + ); + + } + + + + + // + // Have the changes written out to the RXACT, and + // de-reference the object. + // + + if ( NT_SUCCESS( NtStatus ) ) { + + NtStatus = SampDeReferenceContext( DomainContext, TRUE ); + + } else { + + IgnoreStatus = SampDeReferenceContext( DomainContext, FALSE ); + } + } + + + + + + // + // Commit changes, if successful, and notify Netlogon of changes. + // + + if ( NT_SUCCESS(NtStatus) ) { + + NtStatus = SampCommitAndRetainWriteLock(); + + if ( NT_SUCCESS( NtStatus ) ) { + + SampNotifyNetlogonOfDelta( + SecurityDbChange, + SecurityDbObjectSamDomain, + 0L, + (PUNICODE_STRING) NULL, + (DWORD) ReplicateImmediately, + NULL // Delta data + ); + } + } + + IgnoreStatus = SampReleaseWriteLock( FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + + return(NtStatus); +} + + +NTSTATUS +SampCreateGroupInDomain( + IN SAMPR_HANDLE DomainHandle, + IN PRPC_UNICODE_STRING AccountName, + IN ACCESS_MASK DesiredAccess, + IN BOOLEAN WriteLockHeld, + OUT SAMPR_HANDLE *GroupHandle, + IN OUT PULONG RelativeId + ) + +/*++ + +Routine Description: + + This API creates a new group in the account database. Initially, + this group does not contain any users. Note that creating a group + is a protected operation, and requires the DOMAIN_CREATE_GROUP + access type. + + This call returns a handle to the newly created group that may be + used for successive operations on the group. This handle may be + closed with the SamCloseHandle API. + + A newly created group will have the following initial field value + settings. If another value is desired, it must be explicitly + changed using the group object manipulation services. + + + Name - The name of the group will be as specified in the + creation API. + + Attributes - The following attributes will be set: + + Mandatory + EnabledByDefault + + MemberCount - Zero. Initially the group has no members. + + RelativeId - will be a uniquelly allocated ID. + + + +Arguments: + + + DomainHandle - A domain handle returned from a previous call to + SamOpenDomain. + + + AccountName - Points to the name of the new account. A case-insensitive + comparison must not find a group or user with this name already defined. + + + DesiredAccess - Is an access mask indicating which access types + are desired to the group. + + GroupHandle - Receives a handle referencing the newly created + group. This handle will be required in successive calls to + operate on the group. + + RelativeId - Receives the relative ID of the newly created group + account. The SID of the new group account is this relative ID + value prefixed with the domain's SID value. This RID will be a + new, uniquely allocated value - unless a non-zero RID was passed + in, in which case that RID is used (nothing is done if a group + with that RID already exists). + + +Return Value: + + STATUS_SUCCESS - The group was added successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_INVALID_ACCOUNT_NAME - The name was poorly formed, e.g. + contains non-printable characters. + + STATUS_GROUP_EXISTS - The name is already in use as a group. + + STATUS_USER_EXISTS - The name is already in use as a user. + + STATUS_ALIAS_EXISTS - The name is already in use as an alias. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled before groups + can be created in it. + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. The domain server must be a primary server to + create group accounts. + + + + +--*/ + +{ + NTSTATUS NtStatus = STATUS_SUCCESS, IgnoreStatus; + PSAMP_OBJECT DomainContext, GroupContext; + SAMP_OBJECT_TYPE FoundType; + PSAMP_DEFINED_DOMAINS Domain; + + ULONG NewAccountRid, NewSecurityDescriptorLength; + UNICODE_STRING KeyName; + PSECURITY_DESCRIPTOR NewSecurityDescriptor; + SAMP_V1_0A_FIXED_LENGTH_GROUP V1Fixed; + PRIVILEGE_SET PrivilegeSet; + + SAMTRACE("SampCreateGroupInDomain"); + + if (GroupHandle == NULL) { + return(STATUS_INVALID_PARAMETER); + } + + // + // Initialize the privilege set. + // + + PrivilegeSet.PrivilegeCount = 0; + PrivilegeSet.Control = PRIVILEGE_SET_ALL_NECESSARY; + PrivilegeSet.Privilege[0].Luid = RtlConvertLongToLuid(0L); + PrivilegeSet.Privilege[0].Attributes = 0; + + // + // Make sure a name was provided + // + + if (AccountName == NULL) { + return(STATUS_INVALID_ACCOUNT_NAME); + } + if (AccountName->Length > AccountName->MaximumLength) { + return(STATUS_INVALID_ACCOUNT_NAME); + } + if (AccountName->Buffer == NULL) { + return(STATUS_INVALID_ACCOUNT_NAME); + } + + + + if ( !WriteLockHeld ) { + + NtStatus = SampAcquireWriteLock(); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + } + + + // + // Validate type of, and access to domain object. + // + + DomainContext = (PSAMP_OBJECT)DomainHandle; + NtStatus = SampLookupContext( + DomainContext, + DOMAIN_CREATE_GROUP, // DesiredAccess + SampDomainObjectType, // ExpectedType + &FoundType + ); + + GroupContext = NULL; + + if (NT_SUCCESS(NtStatus)) { + + Domain = &SampDefinedDomains[ DomainContext->DomainIndex ]; + + // + // Make sure the name is valid and not already in use before we + // use it to create the new group. + // + + NtStatus = SampValidateNewAccountName((PUNICODE_STRING)AccountName); + + if ( NT_SUCCESS(NtStatus) ) { + + + if ( (*RelativeId) == 0 ) { + + // + // No RID specified, so allocate a new account RID + // + + NewAccountRid = Domain->CurrentFixed.NextRid; + + Domain->CurrentFixed.NextRid += 1; + (*RelativeId) = NewAccountRid; + + } else { + + // + // A RID was passed in, so we want to use that rather than + // selecting a new one. + // + + NewAccountRid = (*RelativeId); + } + + // + // Increment the group count + // + + NtStatus = SampAdjustAccountCount(SampGroupObjectType, TRUE ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Create the registry key that has the group's name. + // This simply serves as a name to RID mapping. Save + // the name when done; we'll put it in the context. + // + + NtStatus = SampBuildAccountKeyName( + SampGroupObjectType, + &KeyName, + (PUNICODE_STRING)AccountName + ); + + + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = RtlAddActionToRXact( + SampRXactContext, + RtlRXactOperationSetValue, + &KeyName, + NewAccountRid, + NULL, + 0 + ); + + SampFreeUnicodeString(&KeyName); + } + } + + + + if (NT_SUCCESS(NtStatus)) { + + // + // Now create a group context block + // + + NtStatus = SampCreateAccountContext( + SampGroupObjectType, + NewAccountRid, + DomainContext->TrustedClient, + FALSE, // Account exists + &GroupContext + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // The existing reference count of 1 is for RPC. + // Reference the context again for the writes we're + // about to do to initialize it. + // + + SampReferenceContext( GroupContext ); + + // + // If MAXIMUM_ALLOWED is requested, add GENERIC_ALL + // + + if (DesiredAccess & MAXIMUM_ALLOWED) { + + DesiredAccess |= GENERIC_ALL; + } + + // + // If ACCESS_SYSTEM_SECURITY is requested and we are + // a non-trusted client, check that we have + // SE_SECURITY_PRIVILEGE. + // + + if ((DesiredAccess & ACCESS_SYSTEM_SECURITY) && + (!DomainContext->TrustedClient)) { + + NtStatus = SampRtlWellKnownPrivilegeCheck( + TRUE, + SE_SECURITY_PRIVILEGE, + NULL + ); + + if (!NT_SUCCESS(NtStatus)) { + + if (NtStatus == STATUS_PRIVILEGE_NOT_HELD) { + + NtStatus = STATUS_ACCESS_DENIED; + } + + } else { + + PrivilegeSet.PrivilegeCount = 1; + PrivilegeSet.Control = PRIVILEGE_SET_ALL_NECESSARY; + PrivilegeSet.Privilege[0].Luid = RtlConvertLongToLuid(SE_SECURITY_PRIVILEGE); + PrivilegeSet.Privilege[0].Attributes = 0; + } + } + + // + // Make sure the caller can be given the requested access + // to the new object + // + + if (NT_SUCCESS(NtStatus)) { + + GroupContext->GrantedAccess = DesiredAccess; + + RtlMapGenericMask( + &GroupContext->GrantedAccess, + &SampObjectInformation[SampGroupObjectType].GenericMapping + ); + + if ((SampObjectInformation[SampGroupObjectType].InvalidMappedAccess + & GroupContext->GrantedAccess) != 0) { + + NtStatus = STATUS_ACCESS_DENIED; + } + } + + } else { + GroupContext = NULL; + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + } + } + + + + // + // Set the V1_fixed attribute + // + + if (NT_SUCCESS(NtStatus)) { + + // + // Create the V1_Fixed key + // + + V1Fixed.RelativeId = NewAccountRid; + V1Fixed.Attributes = (SE_GROUP_MANDATORY | + SE_GROUP_ENABLED_BY_DEFAULT); + V1Fixed.AdminCount = 0; + V1Fixed.OperatorCount = 0; + V1Fixed.Revision = SAMP_REVISION; + + NtStatus = SampSetFixedAttributes( + GroupContext, + (PVOID *)&V1Fixed + ); + } + + + // + // Set the SecurityDescriptor attribute + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampGetNewAccountSecurity( + SampGroupObjectType, + FALSE, // Not member of ADMINISTRATORS alias + DomainContext->TrustedClient, + FALSE, //RestrictCreatorAccess + NewAccountRid, + &NewSecurityDescriptor, + &NewSecurityDescriptorLength + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampSetAccessAttribute( + GroupContext, + SAMP_GROUP_SECURITY_DESCRIPTOR, + NewSecurityDescriptor, + NewSecurityDescriptorLength + ); + + MIDL_user_free( NewSecurityDescriptor ); + } + } + + + // + // Set the NAME attribute + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampSetUnicodeStringAttribute( + GroupContext, + SAMP_GROUP_NAME, + (PUNICODE_STRING)AccountName + ); + } + + + + // + // Set the AdminComment attribute + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampSetUnicodeStringAttribute( + GroupContext, + SAMP_GROUP_ADMIN_COMMENT, + &SampNullString + ); + } + + + // + // Set the MEMBERS attribute (with no members) + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampSetUlongArrayAttribute( + GroupContext, + SAMP_GROUP_MEMBERS, + NULL, + 0, + 0 + ); + } + } + + IgnoreStatus = SampDeReferenceContext( DomainContext, FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + // + // If we created an object, dereference it. Write out its attributes + // if everything was created OK. + // + + if (NT_SUCCESS(NtStatus)) { + + // + // De-reference the object, write out any change to current xaction. + // + + ASSERT(GroupContext != NULL); + NtStatus = SampDeReferenceContext( GroupContext, TRUE ); + + } else { + + if (GroupContext != NULL) { + + // + // De-reference the object, ignore changes + // + + IgnoreStatus = SampDeReferenceContext( GroupContext, FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + } + + + // + // Commit changes and notify netlogon + // + + if ( NT_SUCCESS(NtStatus) ) { + + NtStatus = SampCommitAndRetainWriteLock(); + + if (NT_SUCCESS(NtStatus)) { + + SAMP_ACCOUNT_DISPLAY_INFO AccountInfo; + + // + // Update the display information + // + + AccountInfo.Name = *((PUNICODE_STRING)AccountName); + AccountInfo.Rid = NewAccountRid; + AccountInfo.AccountControl = V1Fixed.Attributes; + RtlInitUnicodeString(&AccountInfo.Comment, NULL); + RtlInitUnicodeString(&AccountInfo.FullName, NULL); + + IgnoreStatus = SampUpdateDisplayInformation(NULL, + &AccountInfo, + SampGroupObjectType); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + SampNotifyNetlogonOfDelta( + SecurityDbNew, + SecurityDbObjectSamGroup, + *RelativeId, + (PUNICODE_STRING) NULL, + (DWORD) FALSE, // Replicate immediately + NULL // Delta data + ); + + // + // Generate Audit + // + + if (SampDoAccountAuditing(DomainContext->DomainIndex)) { + + LsaIAuditSamEvent( + STATUS_SUCCESS, + SE_AUDITID_GLOBAL_GROUP_CREATED, // AuditId + Domain->Sid, // Domain SID + NULL, // Member Rid (not used) + NULL, // Member Sid (not used) + (PUNICODE_STRING) AccountName, // Account Name + &Domain->ExternalName, // Domain + &GroupContext->TypeBody.User.Rid, // Account Rid + &PrivilegeSet // Privileges used + ); + } + } + } + + // + // Return the context handle on success + // Delete the context block and return a NULL handle on failure + // + + if (NT_SUCCESS(NtStatus)) { + + ASSERT(GroupContext != NULL); + (*GroupHandle) = GroupContext; + + } else { + + if (GroupContext != NULL) { + SampDeleteContext(GroupContext); + } + + (*GroupHandle) = (SAMPR_HANDLE)0; + } + + + + // + // Release the lock + // + + if ( !WriteLockHeld ) { + IgnoreStatus = SampReleaseWriteLock( FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + return(NtStatus); +} + + +NTSTATUS +SamrCreateGroupInDomain( + IN SAMPR_HANDLE DomainHandle, + IN PRPC_UNICODE_STRING AccountName, + IN ACCESS_MASK DesiredAccess, + OUT SAMPR_HANDLE *GroupHandle, + OUT PULONG RelativeId + ) + +/*++ + +Routine Description: + + This is just a wrapper for SampCreateGroupInDomain() that ensures that + RelativeId points to a RID of zero first. + + A non-zero RID means that SampCreateGroupInDomain() was called by + SamICreateAccountByRid(), which specifies a RID to be used. + +Parameters: + + Same as SampCreateGroupInDomain(). + +Return Values: + + Same as SampCreateGroupInDomain(). + +--*/ + +{ + NTSTATUS NtStatus; + + SAMTRACE("SamrCreateGroupInDomain"); + + (*RelativeId) = 0; + + NtStatus = SampCreateGroupInDomain( + DomainHandle, + AccountName, + DesiredAccess, + FALSE, + GroupHandle, + RelativeId + ); + + return( NtStatus ); +} + + + +NTSTATUS SamrEnumerateGroupsInDomain( + IN SAMPR_HANDLE DomainHandle, + IN OUT PSAM_ENUMERATE_HANDLE EnumerationContext, + OUT PSAMPR_ENUMERATION_BUFFER *Buffer, + IN ULONG PreferedMaximumLength, + OUT PULONG CountReturned + ) + +/*++ + +Routine Description: + + This API lists all the groups defined in the account database. + Since there may be more groups than can fit into a buffer, the + caller is provided with a handle that can be used across calls to + the API. On the initial call, EnumerationContext should point to a + SAM_ENUMERATE_HANDLE variable that is set to 0. + + If the API returns STATUS_MORE_ENTRIES, then the API should be + called again with EnumerationContext. When the API returns + STATUS_SUCCESS or any error return, the context becomes invalid for + future use. + + This API requires DOMAIN_LIST_GROUPS access to the Domain object. + +Arguments: + + DomainHandle - A domain handle returned from a previous call to + SamOpenDomain. + + EnumerationContext - API specific handle to allow multiple calls + (see below). This is a zero based index. + + Buffer - Receives a pointer to the buffer containing the + requested information. The information returned is + structured as an array of SAM_RID_ENUMERATION data + structures. When this information is no longer needed, the + buffer must be freed using SamFreeMemory(). + + PreferedMaximumLength - Prefered maximum length of returned data + (in 8-bit bytes). This is not a hard upper limit, but serves + as a guide to the server. Due to data conversion between + systems with different natural data sizes, the actual amount + of data returned may be greater than this value. + + CountReturned - Number of entries returned. + + +Return Value: + + STATUS_SUCCESS - The Service completed successfully, and there + are no addition entries. + + STATUS_MORE_ENTRIES - There are more entries, so call again. + This is a successful return. + + STATUS_ACCESS_DENIED - Caller does not have privilege required to + request that data. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + +--*/ +{ + + NTSTATUS NtStatus; + + SAMTRACE("SamrEnumerateGroupsInDomain"); + + NtStatus = SampEnumerateAccountNamesCommon( + DomainHandle, + SampGroupObjectType, + EnumerationContext, + Buffer, + PreferedMaximumLength, + 0L, // no filter + CountReturned + ); + + return(NtStatus); + +} + + + +NTSTATUS +SampCreateAliasInDomain( + IN SAMPR_HANDLE DomainHandle, + IN PRPC_UNICODE_STRING AccountName, + IN ACCESS_MASK DesiredAccess, + IN BOOLEAN WriteLockHeld, + OUT SAMPR_HANDLE *AliasHandle, + IN OUT PULONG RelativeId + ) + +/*++ + +Routine Description: + + This API creates a new alias in the account database. Initially, + this alias does not contain any users. Note that creating a alias + is a protected operation, and requires the DOMAIN_CREATE_ALIAS + access type. + + This call returns a handle to the newly created alias that may be + used for successive operations on the alias. This handle may be + closed with the SamCloseHandle API. + + A newly created alias will have the following initial field value + settings. If another value is desired, it must be explicitly + changed using the alias object manipulation services. + + + Name - The name of the alias will be as specified in the + creation API. + + MemberCount - Zero. Initially the alias has no members. + + RelativeId - will be a uniquelly allocated ID. + + + +Arguments: + + + DomainHandle - A domain handle returned from a previous call to + SamOpenDomain. + + + AccountName - Points to the name of the new account. A case-insensitive + comparison must not find an alias or user with this name already defined. + + + DesiredAccess - Is an access mask indicating which access types + are desired to the alias. + + AliasHandle - Receives a handle referencing the newly created + alias. This handle will be required in successive calls to + operate on the alias. + + RelativeId - Receives the relative ID of the newly created alias + account. The SID of the new alias account is this relative + ID value prefixed with the domain's SID value. + + + +Return Value: + + STATUS_SUCCESS - The alias was added successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_INVALID_ACCOUNT_NAME - The name was poorly formed, e.g. + contains non-printable characters. + + STATUS_GROUP_EXISTS - The name is already in use as a group. + + STATUS_USER_EXISTS - The name is already in use as a user. + + STATUS_ALIAS_EXISTS - The name is already in use as an alias. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled before aliases + can be created in it. + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. The domain server must be a primary server to + create alias accounts. + + + +--*/ +{ + NTSTATUS NtStatus = STATUS_SUCCESS, IgnoreStatus; + PSAMP_OBJECT DomainContext, AliasContext; + SAMP_OBJECT_TYPE FoundType; + PSAMP_DEFINED_DOMAINS Domain; + ULONG NewAccountRid, NewSecurityDescriptorLength; + UNICODE_STRING KeyName; + PSECURITY_DESCRIPTOR NewSecurityDescriptor; + SAMP_V1_FIXED_LENGTH_ALIAS V1Fixed; + PRIVILEGE_SET Privileges; + + SAMTRACE("SampCreateAliasInDomain"); + + if (AliasHandle == NULL) { + return(STATUS_INVALID_PARAMETER); + } + + // + // Initialize the privilege set. + // + + Privileges.PrivilegeCount = 0; + Privileges.Control = PRIVILEGE_SET_ALL_NECESSARY; + Privileges.Privilege[0].Luid = RtlConvertLongToLuid(0L); + Privileges.Privilege[0].Attributes = 0; + + // + // Make sure a name was provided + // + + if (AccountName == NULL) { + return(STATUS_INVALID_ACCOUNT_NAME); + } + if (AccountName->Length > AccountName->MaximumLength) { + return(STATUS_INVALID_ACCOUNT_NAME); + } + if (AccountName->Buffer == NULL) { + return(STATUS_INVALID_ACCOUNT_NAME); + } + + + if ( !WriteLockHeld ) { + + NtStatus = SampAcquireWriteLock(); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + } + + + // + // Validate type of, and access to domain object. + // + + DomainContext = (PSAMP_OBJECT)DomainHandle; + NtStatus = SampLookupContext( + DomainContext, + DOMAIN_CREATE_ALIAS, // DesiredAccess + SampDomainObjectType, // ExpectedType + &FoundType + ); + + AliasContext = NULL; + + if (NT_SUCCESS(NtStatus)) { + + + Domain = &SampDefinedDomains[ DomainContext->DomainIndex ]; + + // + // Make sure the name is valid and not already in use before we + // use it to create the new alias. + // + + NtStatus = SampValidateNewAccountName((PUNICODE_STRING)AccountName); + + if ( NT_SUCCESS(NtStatus) ) { + + + if ( (*RelativeId) == 0 ) { + + // + // Allocate a new account RID + // + + NewAccountRid = Domain->CurrentFixed.NextRid; + + Domain->CurrentFixed.NextRid += 1; + (*RelativeId) = NewAccountRid; + + } else { + + // + // Use the RID that was passed in. + // + + NewAccountRid = (*RelativeId); + } + + // + // Increment the alias count + // + + NtStatus = SampAdjustAccountCount(SampAliasObjectType, TRUE ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Create the registry key that has the alias's name. + // This simply serves as a name to RID mapping. Save + // the name when done; we'll put it in the context. + // + + NtStatus = SampBuildAccountKeyName( + SampAliasObjectType, + &KeyName, + (PUNICODE_STRING)AccountName + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = RtlAddActionToRXact( + SampRXactContext, + RtlRXactOperationSetValue, + &KeyName, + NewAccountRid, + NULL, + 0 + ); + + SampFreeUnicodeString(&KeyName); + } + } + + if (NT_SUCCESS(NtStatus)) { + + // + // Now create an alias context block + // + + NtStatus = SampCreateAccountContext( + SampAliasObjectType, + NewAccountRid, + DomainContext->TrustedClient, + FALSE, // Account exists + &AliasContext + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // The existing reference count of 1 is for RPC. + // Reference the context again for the writes we're + // about to do to initialize it. + // + + SampReferenceContext( AliasContext ); + + // + // If MAXIMUM_ALLOWED is requested, add GENERIC_ALL + // + + if (DesiredAccess & MAXIMUM_ALLOWED) { + + DesiredAccess |= GENERIC_ALL; + } + + // + // If ACCESS_SYSTEM_SECURITY is requested and we are + // a non-trusted client, check that we have + // SE_SECURITY_PRIVILEGE. + // + + if ((DesiredAccess & ACCESS_SYSTEM_SECURITY) && + (!DomainContext->TrustedClient)) { + + NtStatus = SampRtlWellKnownPrivilegeCheck( + TRUE, + SE_SECURITY_PRIVILEGE, + NULL + ); + + if (!NT_SUCCESS(NtStatus)) { + + if (NtStatus == STATUS_PRIVILEGE_NOT_HELD) { + + NtStatus = STATUS_ACCESS_DENIED; + } + + } else { + + Privileges.PrivilegeCount = 1; + Privileges.Control = PRIVILEGE_SET_ALL_NECESSARY; + Privileges.Privilege[0].Luid = RtlConvertLongToLuid(SE_SECURITY_PRIVILEGE); + Privileges.Privilege[0].Attributes = 0; + } + } + + // + // Make sure the caller can be given the requested access + // to the new object + // + + if (NT_SUCCESS(NtStatus)) { + + AliasContext->GrantedAccess = DesiredAccess; + + RtlMapGenericMask( + &AliasContext->GrantedAccess, + &SampObjectInformation[SampAliasObjectType].GenericMapping + ); + + if ((SampObjectInformation[SampAliasObjectType].InvalidMappedAccess & + AliasContext->GrantedAccess) != 0) { + NtStatus = STATUS_ACCESS_DENIED; + } + } + + } else { + AliasContext = NULL; + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + } + } + + + // + // Set the V1_fixed attribute + // + + if (NT_SUCCESS(NtStatus)) { + + V1Fixed.RelativeId = NewAccountRid; + + NtStatus = SampSetFixedAttributes( + AliasContext, + (PVOID *)&V1Fixed + ); + } + + // + // Set the SECURITY DESCRIPTOR attribute + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampGetNewAccountSecurity( + SampAliasObjectType, + FALSE, // Not member of ADMINISTRATORS alias + DomainContext->TrustedClient, + FALSE, //RestrictCreatorAccess + NewAccountRid, + &NewSecurityDescriptor, + &NewSecurityDescriptorLength + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampSetAccessAttribute( + AliasContext, + SAMP_ALIAS_SECURITY_DESCRIPTOR, + NewSecurityDescriptor, + NewSecurityDescriptorLength + ); + + MIDL_user_free( NewSecurityDescriptor ); + } + } + + + // + // Set the NAME attribute + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampSetUnicodeStringAttribute( + AliasContext, + SAMP_ALIAS_NAME, + (PUNICODE_STRING)AccountName + ); + } + + + // + // Set the AdminComment attribute + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampSetUnicodeStringAttribute( + AliasContext, + SAMP_ALIAS_ADMIN_COMMENT, + &SampNullString + ); + } + + + // + // Set the MEMBERS attribute (with no members) + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampSetUlongArrayAttribute( + AliasContext, + SAMP_ALIAS_MEMBERS, + NULL, + 0, + 0 + ); + } + } + + + IgnoreStatus = SampDeReferenceContext( DomainContext, FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + // + // If we created an object, dereference it. Write out its attributes + // if everything was created OK. + // + + if (NT_SUCCESS(NtStatus)) { + + // + // De-reference the object, write out any change to current xaction. + // + + ASSERT(AliasContext != NULL); + NtStatus = SampDeReferenceContext( AliasContext, TRUE ); + + } else { + + if (AliasContext != NULL) { + + // + // De-reference the object, ignore changes + // + + IgnoreStatus = SampDeReferenceContext( AliasContext, FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + } + + + + // + // Commit changes and notify netlogon + // + + if ( NT_SUCCESS(NtStatus) ) { + + NtStatus = SampCommitAndRetainWriteLock(); + + if (NT_SUCCESS(NtStatus)) { + + SampNotifyNetlogonOfDelta( + SecurityDbNew, + SecurityDbObjectSamAlias, + *RelativeId, + (PUNICODE_STRING) NULL, + (DWORD) FALSE, // Replicate immediately + NULL // Delta data + ); + + // + // Generate audit here for local group creation + // here. + // + + if (SampDoAccountAuditing(DomainContext->DomainIndex)) { + + LsaIAuditSamEvent( + STATUS_SUCCESS, + SE_AUDITID_LOCAL_GROUP_CREATED, // AuditId + Domain->Sid, // Domain SID + NULL, // Member Rid (not used) + NULL, // Member Sid (not used) + (PUNICODE_STRING)AccountName, // Account Name + &Domain->ExternalName, // Domain + &AliasContext->TypeBody.User.Rid, // Account Rid + &Privileges // Privileges used + ); + } + } + } + + // + // Return the context handle on success + // Delete the context block and return a NULL handle on failure + // + + if (NT_SUCCESS(NtStatus)) { + + ASSERT(AliasContext != NULL); + (*AliasHandle) = AliasContext; + + } else { + + if (AliasContext != NULL) { + SampDeleteContext(AliasContext); + } + + (*AliasHandle) = (SAMPR_HANDLE)0; + } + + // + // Release the lock + // + + if ( !WriteLockHeld ) { + IgnoreStatus = SampReleaseWriteLock( FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + return(NtStatus); +} + + + +NTSTATUS +SamrCreateAliasInDomain( + IN SAMPR_HANDLE DomainHandle, + IN PRPC_UNICODE_STRING AccountName, + IN ACCESS_MASK DesiredAccess, + OUT SAMPR_HANDLE *AliasHandle, + OUT PULONG RelativeId + ) + +/*++ + +Routine Description: + + This is just a wrapper for SampCreateAliasInDomain() that ensures that + RelativeId points to a RID of zero first. + + A non-zero RID means that SampCreateAliasInDomain() was called by + SamICreateAccountByRid(), which specifies a RID to be used. + +Parameters: + + Same as SampCreateAliasInDomain(). + +Return Values: + + Same as SampCreateAliasInDomain(). + +--*/ + +{ + NTSTATUS NtStatus; + + SAMTRACE("SamrCreateAliasInDomain"); + + (*RelativeId) = 0; + + NtStatus = SampCreateAliasInDomain( + DomainHandle, + AccountName, + DesiredAccess, + FALSE, + AliasHandle, + RelativeId + ); + + return( NtStatus ); +} + + + +NTSTATUS SamrEnumerateAliasesInDomain( + IN SAMPR_HANDLE DomainHandle, + IN OUT PSAM_ENUMERATE_HANDLE EnumerationContext, + OUT PSAMPR_ENUMERATION_BUFFER *Buffer, + IN ULONG PreferedMaximumLength, + OUT PULONG CountReturned + ) + +/*++ + +Routine Description: + + This API lists all the aliases defined in the account database. + Since there may be more aliass than can fit into a buffer, the + caller is provided with a handle that can be used across calls to + the API. On the initial call, EnumerationContext should point to a + SAM_ENUMERATE_HANDLE variable that is set to 0. + + If the API returns STATUS_MORE_ENTRIES, then the API should be + called again with EnumerationContext. When the API returns + STATUS_SUCCESS or any error return, the context becomes invalid for + future use. + + This API requires DOMAIN_LIST_ALIASES access to the Domain object. + +Arguments: + + DomainHandle - A domain handle returned from a previous call to + SamOpenDomain. + + EnumerationContext - API specific handle to allow multiple calls + (see below). This is a zero based index. + + Buffer - Receives a pointer to the buffer containing the + requested information. The information returned is + structured as an array of SAM_RID_ENUMERATION data + structures. When this information is no longer needed, the + buffer must be freed using SamFreeMemory(). + + PreferedMaximumLength - Prefered maximum length of returned data + (in 8-bit bytes). This is not a hard upper limit, but serves + as a guide to the server. Due to data conversion between + systems with different natural data sizes, the actual amount + of data returned may be greater than this value. + + CountReturned - Number of entries returned. + + +Return Value: + + STATUS_SUCCESS - The Service completed successfully, and there + are no addition entries. + + STATUS_MORE_ENTRIES - There are more entries, so call again. + This is a successful return. + + STATUS_ACCESS_DENIED - Caller does not have privilege required to + request that data. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + +--*/ +{ + + NTSTATUS NtStatus; + + SAMTRACE("SamrEnumerateAliasesInDomain"); + + NtStatus = SampEnumerateAccountNamesCommon( + DomainHandle, + SampAliasObjectType, + EnumerationContext, + Buffer, + PreferedMaximumLength, + 0L, // no filter + CountReturned + ); + + return(NtStatus); + +} + + + +NTSTATUS SamrRemoveMemberFromForeignDomain( + IN SAMPR_HANDLE DomainHandle, + IN PRPC_SID MemberId + ) + +/*++ + +Routine Description: + + This routine removes an account (group or user) from all aliases in + the given domain. It is meant to be called in domains OTHER than + domain in which the account was created. + + This is typically called just before deleting the account from the + domain in which it was created. + + +Parameters: + + DomainHandle - A domain handle returned from a previous call to + SamOpenDomain. + + MemberId - The SID of the account being removed. + + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_SPECIAL_ACCOUNT - This operation may not be performed on + builtin accounts. + + +--*/ + +{ + NTSTATUS NtStatus, IgnoreStatus; + SAMP_OBJECT_TYPE FoundType; + PSAMP_OBJECT DomainContext = NULL; + PULONG Membership = NULL; + PSID DomainSid = NULL; + ULONG MembershipCount, MemberRid, i; + + SAMTRACE("SamrRemoveMemberFromForiegnDomain"); + NtStatus = SampAcquireWriteLock(); + + if ( !NT_SUCCESS( NtStatus ) ) { + + return(NtStatus); + } + + // + // Validate type of, and access to domain object. + // + + DomainContext = (PSAMP_OBJECT)DomainHandle; + + NtStatus = SampLookupContext( + DomainContext, + DOMAIN_LOOKUP, // DesiredAccess + SampDomainObjectType, // ExpectedType + &FoundType + ); + + if (NT_SUCCESS(NtStatus)) { + + if ( !DomainContext->TrustedClient ) { + + // + // Return error if the SID passed in is for a builtin account. + // This may seem overly restrictive, but this API is meant to + // be called before deleting a user, and since deleting + // builtin accounts isn't allowed, it makes sense for this to + // fail too. + // + + NtStatus = SampSplitSid( + MemberId, + &DomainSid, + &MemberRid ); + + if ( NT_SUCCESS( NtStatus ) ) { + + MIDL_user_free( DomainSid ); + + NtStatus = SampIsAccountBuiltIn( MemberRid ); + } + } + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampRemoveAccountFromAllAliases( + MemberId, + TRUE, // verify caller is allowed to do this + DomainHandle, + &MembershipCount, + &Membership + ); + + } + + IgnoreStatus = SampDeReferenceContext( DomainContext, FALSE ); + } + + if (NT_SUCCESS(NtStatus)) { + + IgnoreStatus = STATUS_SUCCESS; + + for ( i = 0; + ( ( i < MembershipCount ) && ( NT_SUCCESS( IgnoreStatus ) ) ); + i++ ) { + + // + // Notify netlogon once for each alias that the account was + // removed from. Netlogon requires that ModifiedCount be + // incremented each time; Commit increments ModifiedCount, + // so we do each notification after a commit. + // + + IgnoreStatus = SampCommitAndRetainWriteLock(); + + if ( i == 0 ) { + + // + // The first commit is the one that commits all the + // important changes, so we'll save it's status to return + // to the caller. + // + + NtStatus = IgnoreStatus; + + // + // Update the Cached Alias Information if necessary. + // + + IgnoreStatus = SampAlRemoveAccountFromAllAliases( + MemberId, + FALSE, + DomainHandle, + NULL, + NULL + ); + } + + if ( NT_SUCCESS( IgnoreStatus ) ) { + + // + // Notify if we were able to increment the modified count + // (which is done by SampCommitAndRetainWriteLock()). + // + + SAM_DELTA_DATA DeltaData; + + // + // Fill in id of member being removed + // + + DeltaData.AliasMemberId.MemberSid = MemberId; + + SampNotifyNetlogonOfDelta( + SecurityDbChangeMemberDel, + SecurityDbObjectSamAlias, + Membership[i], + (PUNICODE_STRING) NULL, + (DWORD) FALSE, // Replicate immediately + &DeltaData + ); + } + } + } + + if ( Membership != NULL ) { + + MIDL_user_free( Membership ); + } + + IgnoreStatus = SampReleaseWriteLock( FALSE ); + + return( NtStatus ); +} + + + +NTSTATUS +SampCreateUserInDomain( + IN SAMPR_HANDLE DomainHandle, + IN PRPC_UNICODE_STRING AccountName, + IN ULONG AccountType, + IN ACCESS_MASK DesiredAccess, + IN BOOLEAN WriteLockHeld, + OUT SAMPR_HANDLE *UserHandle, + OUT PULONG GrantedAccess, + IN OUT PULONG RelativeId + ) + +/*++ + +Routine Description: + + This API adds a new user to the account database. The account is + created in a disabled state. Default information is assigned to all + fields except the account name. A password must be provided before + the account may be enabled, unless the PasswordNotRequired control + field is set. + + This api may be used in either of two ways: + + 1) An administrative utility may use this api to create + any type of user account. In this case, the DomainHandle + is expected to be open for DOMAIN_CREATE_USER access. + + 2) A non-administrative user may use this api to create + a machine account. In this case, the caller is expected + to have the SE_CREATE_MACHINE_ACCOUNT_PRIV privilege + and the DomainHandle is expected to be open for DOMAIN_LOOKUP + access. + + + For the normal administrative model ( #1 above), the creator will + be assigned as the owner of the created user account. Furthermore, + the new account will be give USER_WRITE access to itself. + + For the special machine-account creation model (#2 above), the + "Administrators" will be assigned as the owner of the account. + Furthermore, the new account will be given NO access to itself. + Instead, the creator of the account will be give USER_WRITE and + DELETE access to the account. + + + This call returns a handle to the newly created user that may be + used for successive operations on the user. This handle may be + closed with the SamCloseHandle() API. If a machine account is + being created using model #2 above, then this handle will have + only USER_WRITE and DELETE access. Otherwise, it will be open + for USER_ALL_ACCESS. + + + A newly created user will automatically be made a member of the + DOMAIN_USERS group. + + A newly created user will have the following initial field value + settings. If another value is desired, it must be explicitly + changed using the user object manipulation services. + + UserName - the name of the account will be as specified in the + creation API. + + FullName - will be null. + + UserComment - will be null. + + Parameters - will be null. + + CountryCode - will be zero. + + UserId - will be a uniquelly allocated ID. + + PrimaryGroupId - Will be DOMAIN_USERS. + + PasswordLastSet - will be the time the account was created. + + HomeDirectory - will be null. + + HomeDirectoryDrive - will be null. + + UserAccountControl - will have the following flags set: + + UserAccountDisable, + UserPasswordNotRequired, + and the passed account type. + + + ScriptPath - will be null. + + WorkStations - will be null. + + CaseInsensitiveDbcs - will be null. + + CaseSensitiveUnicode - will be null. + + LastLogon - will be zero delta time. + + LastLogoff - will be zero delta time + + AccountExpires - will be very far into the future. + + BadPasswordCount - will be negative 1 (-1). + + LastBadPasswordTime - will be SampHasNeverTime ( [High,Low] = [0,0] ). + + LogonCount - will be negative 1 (-1). + + AdminCount - will be zero. + + AdminComment - will be null. + + Password - will be "". + + +Parameters: + + DomainHandle - A domain handle returned from a previous call to + SamOpenDomain. + + AccountName - Points to the name of the new account. A case-insensitive + comparison must not find a group or user with this name already defined. + + AccountType - Indicates what type of account is being created. + Exactly one account type must be provided: + + USER_INTERDOMAIN_TRUST_ACCOUNT + USER_WORKSTATION_TRUST_ACCOUNT + USER_SERVER_TRUST_ACCOUNT + USER_TEMP_DUPLICATE_ACCOUNT + USER_NORMAL_ACCOUNT + USER_MACHINE_ACCOUNT_MASK + + + DesiredAccess - Is an access mask indicating which access types + are desired to the user. + + UserHandle - Receives a handle referencing the newly created + user. This handle will be required in successive calls to + operate on the user. + + GrantedAccess - Receives the accesses actually granted to via + the UserHandle. + + RelativeId - Receives the relative ID of the newly created user + account. The SID of the new user account is this relative ID + value prefixed with the domain's SID value. + + + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_GROUP_EXISTS - The name is already in use as a group. + + STATUS_USER_EXISTS - The name is already in use as a user. + + STATUS_ALIAS_EXISTS - The name is already in use as an alias. + + STATUS_INVALID_ACCOUNT_NAME - The name was poorly formed, e.g. + contains non-printable characters. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled before users + can be created in it. + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. The domain server must be a primary server to + create user accounts. + + + +--*/ + +{ + NTSTATUS + NtStatus, + IgnoreStatus; + + PSAMP_OBJECT + DomainContext, + UserContext, + GroupContext; + + SAMP_OBJECT_TYPE + FoundType; + + PSAMP_DEFINED_DOMAINS + Domain; + + SAMP_V1_0A_FIXED_LENGTH_GROUP + GroupV1Fixed; + + ULONG + NewAccountRid, + NewSecurityDescriptorLength; + + UNICODE_STRING + KeyName; + + PSECURITY_DESCRIPTOR + NewSecurityDescriptor; + + SAMP_V1_0A_FIXED_LENGTH_USER + V1aFixed; + + GROUP_MEMBERSHIP + DomainUsersMember; + + BOOLEAN + DomainPasswordInformationAccessible = FALSE, + PrivilegedMachineAccountCreate = FALSE; + + PRIVILEGE_SET + Privileges; + + PPRIVILEGE_SET + PPrivileges = NULL; // No privileges in audit by default + + + ACCESS_MASK + AccessRestriction = USER_ALL_ACCESS | + ACCESS_SYSTEM_SECURITY; // No access restrictions by default + + + SAMTRACE("SampCreateUserInDomain"); + + DomainUsersMember.RelativeId = DOMAIN_GROUP_RID_USERS; + DomainUsersMember.Attributes = (SE_GROUP_MANDATORY | + SE_GROUP_ENABLED | + SE_GROUP_ENABLED_BY_DEFAULT); + + + if (UserHandle == NULL) { + return(STATUS_INVALID_PARAMETER); + } + + + + // + // Make sure a name was provided + // + + if (AccountName == NULL) { + return(STATUS_INVALID_ACCOUNT_NAME); + } + if (AccountName->Length > AccountName->MaximumLength) { + return(STATUS_INVALID_ACCOUNT_NAME); + } + if (AccountName->Buffer == NULL) { + return(STATUS_INVALID_ACCOUNT_NAME); + } + + + if ( !WriteLockHeld ) { + + NtStatus = SampAcquireWriteLock(); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + } + + + // + // Validate type of, and access to domain object. + // + + DomainContext = (PSAMP_OBJECT)DomainHandle; + NtStatus = SampLookupContext( + DomainContext, + DOMAIN_CREATE_USER, // DesiredAccess + SampDomainObjectType, // ExpectedType + &FoundType + ); + + // + // if we don't have DOMAIN_CREATE_USER access, then see + // if we are creating a machine account and try for DOMAIN_LOOKUP. + // If this works, then we can see if the client has + // SE_CREATE_MACHINE_ACCOUNT_PRIVILEGE. + // + + if ( (NtStatus == STATUS_ACCESS_DENIED) && + (AccountType == USER_WORKSTATION_TRUST_ACCOUNT) ) { + + SampTransactionWithinDomain = FALSE; + NtStatus = SampLookupContext( + DomainContext, + DOMAIN_LOOKUP, // DesiredAccess + SampDomainObjectType, // ExpectedType + &FoundType + ); + if (NT_SUCCESS(NtStatus)) { + NtStatus = SampRtlWellKnownPrivilegeCheck( + TRUE, // ImpersonateClient + SE_MACHINE_ACCOUNT_PRIVILEGE, + NULL); // ClientId - optional + if (NtStatus == STATUS_PRIVILEGE_NOT_HELD) { + NtStatus = STATUS_ACCESS_DENIED; + } + + if (NT_SUCCESS(NtStatus)) { + + // + // Clients creating accounts in this fashion are limited + // in what they can do to the account. + // + + AccessRestriction = DELETE | + USER_WRITE | + USER_FORCE_PASSWORD_CHANGE; + PrivilegedMachineAccountCreate = TRUE; + + + + } else { + IgnoreStatus = SampDeReferenceContext( DomainContext, FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + } + } + + UserContext = NULL; + + if (NT_SUCCESS(NtStatus)) { + + // + // If the domain handle allows reading the password parameters, + // note that now (best to call LookupContext early because of + // side effects; data will be copied to the user's context later) + // to make life easy for SampGetUserDomainPasswordInformation(). + // + + SampTransactionWithinDomain = FALSE; + + IgnoreStatus = SampLookupContext( + DomainHandle, + DOMAIN_READ_PASSWORD_PARAMETERS, // DesiredAccess + SampDomainObjectType, // ExpectedType + &FoundType + ); + + if ( NT_SUCCESS( IgnoreStatus ) ) { + + DomainPasswordInformationAccessible = TRUE; + + IgnoreStatus = SampDeReferenceContext( DomainHandle, FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + + Domain = &SampDefinedDomains[ DomainContext->DomainIndex ]; + + // + // Make sure the name is valid and not already in use before we + // use it to create the new alias. + // + + NtStatus = SampValidateNewAccountName((PUNICODE_STRING)AccountName); + + if ( NT_SUCCESS(NtStatus) ) { + + + + if ( (*RelativeId) == 0 ) { + + // + // Allocate a new account RID + // + + NewAccountRid = Domain->CurrentFixed.NextRid; + + Domain->CurrentFixed.NextRid += 1; + (*RelativeId) = NewAccountRid; + + } else { + + // + // A RID was passed in, so we want to use that rather than + // select a new one. + // + + NewAccountRid = (*RelativeId); + } + + + // + // Increment the User count + // + + NtStatus = SampAdjustAccountCount(SampUserObjectType, TRUE ); + + if (NT_SUCCESS(NtStatus)) { + + + // + // Create the registry key that has the User's name. + // This simply serves as a name to RID mapping. Save + // the name when finished; we'll put it in the context. + // + + NtStatus = SampBuildAccountKeyName( + SampUserObjectType, + &KeyName, + (PUNICODE_STRING)AccountName + ); + + + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = RtlAddActionToRXact( + SampRXactContext, + RtlRXactOperationSetValue, + &KeyName, + NewAccountRid, + NULL, + 0 + ); + + SampFreeUnicodeString(&KeyName); + } + } + + + if (NT_SUCCESS(NtStatus)) { + + // + // Now create a User context block + // + + NtStatus = SampCreateAccountContext( + SampUserObjectType, + NewAccountRid, + DomainContext->TrustedClient, + FALSE, // Account exists + &UserContext + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // The existing reference count of 1 is for RPC. + // Reference the context again for the writes we're + // about to do to initialize it. + // + + SampReferenceContext( UserContext ); + + // + // Stash away the password info accessible flag + // + + UserContext->TypeBody.User.DomainPasswordInformationAccessible = + DomainPasswordInformationAccessible; + + // + // If MAXIMUM_ALLOWED is requested, add GENERIC_ALL + // + + if (DesiredAccess & MAXIMUM_ALLOWED) { + + DesiredAccess |= GENERIC_ALL; + } + + // + // If ACCESS_SYSTEM_SECURITY is requested and we are + // a non-trusted client, check that we have + // SE_SECURITY_PRIVILEGE. + // + + if ((DesiredAccess & ACCESS_SYSTEM_SECURITY) && + (!DomainContext->TrustedClient)) { + + NtStatus = SampRtlWellKnownPrivilegeCheck( + TRUE, + SE_SECURITY_PRIVILEGE, + NULL + ); + + if (!NT_SUCCESS(NtStatus)) { + + if (NtStatus == STATUS_PRIVILEGE_NOT_HELD) { + + NtStatus = STATUS_ACCESS_DENIED; + } + } + } + + // + // Make sure the caller can be given the requested access + // to the new object + // + + if (NT_SUCCESS(NtStatus)) { + + UserContext->GrantedAccess = DesiredAccess; + + RtlMapGenericMask( + &UserContext->GrantedAccess, + &SampObjectInformation[SampUserObjectType].GenericMapping + ); + + + if ((SampObjectInformation[SampUserObjectType].InvalidMappedAccess + & UserContext->GrantedAccess) != 0) { + + NtStatus = STATUS_ACCESS_DENIED; + } else { + + // + // Restrict access if necessary + // + + UserContext->GrantedAccess &= AccessRestriction; + (*GrantedAccess) = UserContext->GrantedAccess; + } + } + + } else { + UserContext = NULL; + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + } + } + + + + + // + // If the GROUP we're going to put this user in + // is part of an ADMIN alias, we must set the ACL + // on this user account to disallow access by + // account operators. Get group info to determine + // whether it's in an ADMIN alias or not. + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampCreateAccountContext( + SampGroupObjectType, + DOMAIN_GROUP_RID_USERS, + TRUE, // TrustedClient, + TRUE, // Account exists + &GroupContext + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + NtStatus = SampRetrieveGroupV1Fixed( + GroupContext, + &GroupV1Fixed + ); + + SampDeleteContext(GroupContext); + } + } + + + // + // Set the V1_fixed attribute + // + + if (NT_SUCCESS(NtStatus)) { + + V1aFixed.Revision = SAMP_REVISION; + + V1aFixed.CountryCode = 0; + V1aFixed.CodePage = 0; + V1aFixed.BadPasswordCount = 0; + V1aFixed.LogonCount = 0; + V1aFixed.AdminCount = 0; + V1aFixed.OperatorCount = 0; + V1aFixed.Unused1 = 0; + V1aFixed.Unused2 = 0; + V1aFixed.UserAccountControl = (USER_PASSWORD_NOT_REQUIRED | + AccountType); + // + // Disable the account unless this is a special creation + // in which the creator won't be able to enable the account. + // + + if (!PrivilegedMachineAccountCreate) { + V1aFixed.UserAccountControl |= USER_ACCOUNT_DISABLED; + } + + V1aFixed.UserId = NewAccountRid; + V1aFixed.PrimaryGroupId = DOMAIN_GROUP_RID_USERS; + V1aFixed.LastLogon = SampHasNeverTime; + V1aFixed.LastLogoff = SampHasNeverTime; + V1aFixed.PasswordLastSet = SampHasNeverTime; + V1aFixed.AccountExpires = SampWillNeverTime; + V1aFixed.LastBadPasswordTime = SampHasNeverTime; + + NtStatus = SampSetFixedAttributes( + UserContext, + (PVOID *)&V1aFixed + ); + } + + // + // Set the SECURITY_DESCRIPTOR attribute + // + + if (NT_SUCCESS(NtStatus)) { + + // + // Build a security descriptor to protect the User. + // + + NtStatus = SampGetNewAccountSecurity( + SampUserObjectType, + (BOOLEAN) ((GroupV1Fixed.AdminCount == 0) ? FALSE : TRUE), + DomainContext->TrustedClient, + PrivilegedMachineAccountCreate, + NewAccountRid, + &NewSecurityDescriptor, + &NewSecurityDescriptorLength + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampSetAccessAttribute( + UserContext, + SAMP_USER_SECURITY_DESCRIPTOR, + NewSecurityDescriptor, + NewSecurityDescriptorLength + ); + + MIDL_user_free( NewSecurityDescriptor ); + } + } + + + // + // Set the ACCOUNT_NAME attribute + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampSetUnicodeStringAttribute( + UserContext, + SAMP_USER_ACCOUNT_NAME, + (PUNICODE_STRING)AccountName + ); + } + + + // + // Set the FULL_NAME attribute + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampSetUnicodeStringAttribute( + UserContext, + SAMP_USER_FULL_NAME, + &SampNullString + ); + } + + + // + // Set the AdminComment attribute + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampSetUnicodeStringAttribute( + UserContext, + SAMP_USER_ADMIN_COMMENT, + &SampNullString + ); + } + + + // + // Set the UserComment attribute + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampSetUnicodeStringAttribute( + UserContext, + SAMP_USER_USER_COMMENT, + &SampNullString + ); + } + + + // + // Set the Parameters attribute + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampSetUnicodeStringAttribute( + UserContext, + SAMP_USER_PARAMETERS, + &SampNullString + ); + } + + + // + // Set the HomeDirectory attribute + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampSetUnicodeStringAttribute( + UserContext, + SAMP_USER_HOME_DIRECTORY, + &SampNullString + ); + } + + + // + // Set the HomeDirectoryDrive attribute + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampSetUnicodeStringAttribute( + UserContext, + SAMP_USER_HOME_DIRECTORY_DRIVE, + &SampNullString + ); + } + + + // + // Set the ScriptPath attribute + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampSetUnicodeStringAttribute( + UserContext, + SAMP_USER_SCRIPT_PATH, + &SampNullString + ); + } + + + // + // Set the ProfilePath attribute + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampSetUnicodeStringAttribute( + UserContext, + SAMP_USER_PROFILE_PATH, + &SampNullString + ); + } + + + // + // Set the WorkStations attribute + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampSetUnicodeStringAttribute( + UserContext, + SAMP_USER_WORKSTATIONS, + &SampNullString + ); + } + + + // + // Set the LogonHours attribute + // + + if (NT_SUCCESS(NtStatus)) { + + LOGON_HOURS LogonHours; + + LogonHours.UnitsPerWeek = 0; + LogonHours.LogonHours = NULL; + + NtStatus = SampSetLogonHoursAttribute( + UserContext, + SAMP_USER_LOGON_HOURS, + &LogonHours + ); + } + + + // + // Set the Groups attribute (with membership in DomainUsers only) + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampSetLargeIntArrayAttribute( + UserContext, + SAMP_USER_GROUPS, + (PLARGE_INTEGER)&DomainUsersMember, + 1 + ); + } + + + // + // Set the CaseInsensitiveDbcs attribute + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampSetUnicodeStringAttribute( + UserContext, + SAMP_USER_DBCS_PWD, + &SampNullString + ); + } + + + // + // Create the CaseSensitiveUnicode key + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampSetUnicodeStringAttribute( + UserContext, + SAMP_USER_UNICODE_PWD, + &SampNullString + ); + } + + + // + // Set the NtPasswordHistory attribute + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampSetUnicodeStringAttribute( + UserContext, + SAMP_USER_NT_PWD_HISTORY, + &SampNullString + ); + } + + + // + // Set the LmPasswordHistory attribute + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampSetUnicodeStringAttribute( + UserContext, + SAMP_USER_LM_PWD_HISTORY, + &SampNullString + ); + } + + + // + // Add this new user to the DomainUsers group + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampAddUserToGroup( DOMAIN_GROUP_RID_USERS, NewAccountRid ); + } + + + + } + + + IgnoreStatus = SampDeReferenceContext( DomainContext, FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + } + + + + // + // If we created an object, dereference it. Write out its attributes + // if everything was created OK. + // + + if (NT_SUCCESS(NtStatus)) { + + // + // De-reference the object, write out any change to current xaction. + // + + ASSERT(UserContext != NULL); + NtStatus = SampDeReferenceContext( UserContext, TRUE ); + + } else { + + if (UserContext != NULL) { + + // + // De-reference the object, ignore changes + // + + IgnoreStatus = SampDeReferenceContext( UserContext, FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + } + + + + + // + // Commit changes and notify netlogon + // + + if ( NT_SUCCESS(NtStatus) ) { + + // + // Commit the changes; hold on to the write lock for now. + // + + NtStatus = SampCommitAndRetainWriteLock(); + + // + // If we can't commit the mess for some reason, then delete + // the new context block and return null for the context handle. + // + + if (NT_SUCCESS(NtStatus)) { + + + SAMP_ACCOUNT_DISPLAY_INFO AccountInfo; + + // + // Update the display information + // + + AccountInfo.Name = *((PUNICODE_STRING)AccountName); + AccountInfo.Rid = NewAccountRid; + AccountInfo.AccountControl = V1aFixed.UserAccountControl; + RtlInitUnicodeString(&AccountInfo.Comment, NULL); + RtlInitUnicodeString(&AccountInfo.FullName, NULL); + + IgnoreStatus = SampUpdateDisplayInformation(NULL, + &AccountInfo, + SampUserObjectType); + ASSERT(NT_SUCCESS(IgnoreStatus)); + // + // Audit the creation before we free the write lock + // so that we have access to the context block. + // + + if (SampDoAccountAuditing(UserContext->DomainIndex)) { + + if (PrivilegedMachineAccountCreate) { + + // + // Set up the privilege set for auditing + // + + + Privileges.PrivilegeCount = 1; + Privileges.Control = 0; + ASSERT(ANYSIZE_ARRAY >= 1); + Privileges.Privilege[0].Attributes = SE_PRIVILEGE_USED_FOR_ACCESS; + Privileges.Privilege[0].Luid = RtlConvertUlongToLuid( SE_MACHINE_ACCOUNT_PRIVILEGE); + PPrivileges = &Privileges; + } + + + LsaIAuditSamEvent( + STATUS_SUCCESS, + SE_AUDITID_USER_CREATED, // AuditId + Domain->Sid, // Domain SID + NULL, // Member Rid (not used) + NULL, // Member Sid (not used) + (PUNICODE_STRING)AccountName, // Account Name + &Domain->ExternalName, // Domain + &NewAccountRid, // Account Rid + PPrivileges // Privileges used + ); + } + + // + // Notify netlogon if a machine account was created. + // + + if ( ( V1aFixed.UserAccountControl & + USER_MACHINE_ACCOUNT_MASK ) != 0 ) { + + // + // This was a machine account. Let + // NetLogon know of the change. + // + + IgnoreStatus = I_NetNotifyMachineAccount( + NewAccountRid, + SampDefinedDomains[SampTransactionDomainIndex].Sid, + 0, + V1aFixed.UserAccountControl, + (PUNICODE_STRING)AccountName + ); + } + + // + // Notify netlogon of changes + // + + SampNotifyNetlogonOfDelta( + SecurityDbNew, + SecurityDbObjectSamUser, + *RelativeId, + (PUNICODE_STRING) NULL, + (DWORD) FALSE, // Replicate immediately + NULL // Delta data + ); + + } + + } + + + + // + // Return the context handle on success + // Delete the context block and return a NULL handle on failure + // + + if (NT_SUCCESS(NtStatus)) { + + ASSERT(UserContext != NULL); + (*UserHandle) = UserContext; + + } else { + + if (UserContext != NULL) { + SampDeleteContext(UserContext); + } + + (*UserHandle) = (SAMPR_HANDLE)0; + } + + + + // + // Release the lock + // + + if ( !WriteLockHeld ) { + IgnoreStatus = SampReleaseWriteLock( FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + + return(NtStatus); +} + + +NTSTATUS SamrCreateUser2InDomain( + IN SAMPR_HANDLE DomainHandle, + IN PRPC_UNICODE_STRING AccountName, + IN ULONG AccountType, + IN ACCESS_MASK DesiredAccess, + OUT SAMPR_HANDLE *UserHandle, + OUT PULONG GrantedAccess, + OUT PULONG RelativeId + ) + +/*++ + +Routine Description: + + This is just a wrapper for SampCreateUserInDomain() that ensures + RelativeId points to a RID of zero first. It also guarantees + that AccountType is valid. + + A non-zero RID means that SampCreateUserInDomain() was called by + SamICreateAccountByRid(), which specifies a RID to be used. + +Parameters: + + Same as SampCreateUserInDomain() except AccountType maps to + AccountControl. + +Return Values: + + Same as SampCreateUserInDomain(). + +--*/ + +{ + NTSTATUS NtStatus; + + SAMTRACE("SamrCreateUser2InDomain"); + + (*RelativeId) = 0; + + // + // Make sure one, and only one, account type flag is set. + // + + switch (AccountType) { + + case USER_NORMAL_ACCOUNT : + case USER_WORKSTATION_TRUST_ACCOUNT : + case USER_INTERDOMAIN_TRUST_ACCOUNT : + case USER_SERVER_TRUST_ACCOUNT : + case USER_TEMP_DUPLICATE_ACCOUNT : + + // + // AccountType is valid + // + + break; + + default : + + // + // Bad account type value specified + // + + return( STATUS_INVALID_PARAMETER ); + } + + + + + NtStatus = SampCreateUserInDomain( + DomainHandle, + AccountName, + AccountType, + DesiredAccess, + FALSE, + UserHandle, + GrantedAccess, + RelativeId + ); + + return( NtStatus ); +} + + +NTSTATUS SamrCreateUserInDomain( + IN SAMPR_HANDLE DomainHandle, + IN PRPC_UNICODE_STRING AccountName, + IN ACCESS_MASK DesiredAccess, + OUT SAMPR_HANDLE *UserHandle, + OUT PULONG RelativeId + ) + +/*++ + +Routine Description: + + This is just a wrapper for SampCreateUserInDomain() that ensures that + RelativeId points to a RID of zero first. + + A non-zero RID means that SampCreateUserInDomain() was called by + SamICreateAccountByRid(), which specifies a RID to be used. + +Parameters: + + Same as SampCreateUserInDomain() except AccountType is NORMAL_USER. + +Return Values: + + Same as SampCreateUserInDomain(). + +--*/ + +{ + NTSTATUS + NtStatus; + + ULONG + GrantedAccess; + + SAMTRACE("SamrCreateUserInDomain"); + + (*RelativeId) = 0; + + NtStatus = SampCreateUserInDomain( + DomainHandle, + AccountName, + USER_NORMAL_ACCOUNT, + DesiredAccess, + FALSE, + UserHandle, + &GrantedAccess, + RelativeId + ); + + return( NtStatus ); +} + + + +NTSTATUS SamrEnumerateUsersInDomain( + IN SAMPR_HANDLE DomainHandle, + IN OUT PSAM_ENUMERATE_HANDLE EnumerationContext, + IN ULONG UserAccountControl, + OUT PSAMPR_ENUMERATION_BUFFER *Buffer, + IN ULONG PreferedMaximumLength, + OUT PULONG CountReturned + ) + +/*++ + +Routine Description: + + This API lists all the users defined in the account database. Since + there may be more users than can fit into a buffer, the caller is + provided with a handle that can be used across calls to the API. On + the initial call, EnumerationContext should point to a + SAM_ENUMERATE_HANDLE variable that is set to 0. + + If the API returns STATUS_MORE_ENTRIES, then the API should be + called again with EnumerationContext. When the API returns + STATUS_SUCCESS or any error return, the handle becomes invalid for + future use. + + This API requires DOMAIN_LIST_USERS access to the Domain object. + + +Parameters: + + DomainHandle - A domain handle returned from a previous call to + SamOpenDomain. + + EnumerationContext - API specific handle to allow multiple calls. + This is a zero based index. + + UserAccountControl - Provides enumeration filtering information. Any + characteristics specified here will cause that type of User account + to be included in the enumeration process. + + Buffer - Receives a pointer to the buffer containing the + requested information. The information returned is + structured as an array of SAM_RID_ENUMERATION data + structures. When this information is no longer needed, the + buffer must be freed using SamFreeMemory(). + + PreferedMaximumLength - Prefered maximum length of returned data + (in 8-bit bytes). This is not a hard upper limit, but serves + as a guide to the server. Due to data conversion between + systems with different natural data sizes, the actual amount + of data returned may be greater than this value. + + CountReturned - Number of entries returned. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully, and there + are no additional entries. + + STATUS_MORE_ENTRIES - There are more entries, so call again. + This is a successful return. + + STATUS_ACCESS_DENIED - Caller does not have privilege required to + request that data. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + +--*/ + +{ + + NTSTATUS NtStatus; + + SAMTRACE("SamrEnumerateUsersInDomain"); + + NtStatus = SampEnumerateAccountNamesCommon( + DomainHandle, + SampUserObjectType, + EnumerationContext, + Buffer, + PreferedMaximumLength, + UserAccountControl, + CountReturned + ); + + return(NtStatus); +} + + + + + +NTSTATUS SamrLookupNamesInDomain( + IN SAMPR_HANDLE DomainHandle, + IN ULONG Count, + IN RPC_UNICODE_STRING Names[], + OUT PSAMPR_ULONG_ARRAY RelativeIds, + OUT PSAMPR_ULONG_ARRAY Use + ) + +/*++ + +Routine Description: + + This API attempts to find relative IDs corresponding to name + strings. If a name can not be mapped to a relative ID, a zero is + placed in the corresponding relative ID array entry, and translation + continues. + + DOMAIN_LOOKUP access to the domain is needed to use this service. + + +Parameters: + + DomainHandle - A domain handle returned from a previous call to + SamOpenDomain. + + Count - Number of names to translate. + + Names - Pointer to an array of Count UNICODE_STRINGs that contain + the names to map to relative IDs. Case-insensitive + comparisons of these names will be performed for the lookup + operation. + + RelativeIds - Receives an array of Count Relative IDs. + The relative ID of the nth name will be the nth entry in this + array. Any names that could not be translated will have a + zero relative ID. + + RelativeIds - Receives a pointer to a SAMPR_RETURNED_ULONG_ARRAY structure. + The nth entry in the array associated with this structure + contains the RID of the nth name looked up. + When this information is no longer needed, the caller is responsible + for deallocating each returned block (including the + SAMPR_ULONG_ARRAY structure itself) using SamFreeMemory(). + + Use - Receives a pointer to a SAMPR_RETURNED_ULONG_ARRAY structure. + The nth entry in the array associated with this structure + contains the SID_NAME_USE of the nth name looked up. + When this information is no longer needed, the caller is responsible + for deallocating each returned block (including the + SAMPR_ULONG_ARRAY structure itself) using SamFreeMemory(). + + + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The domain handle passed is invalid. + + STATUS_SOME_NOT_MAPPED - Some of the names provided could not be + mapped. This is a successful return. + + STATUS_NONE_MAPPED - No names could be mapped. This is an error + return. + +--*/ +{ + NTSTATUS NtStatus, IgnoreStatus; + UNICODE_STRING KeyName; + OBJECT_ATTRIBUTES ObjectAttributes; + PSAMP_OBJECT DomainContext; + SAMP_OBJECT_TYPE FoundType; + HANDLE TempHandle; + LARGE_INTEGER IgnoreTimeStamp; + ULONG i, KeyValueLength, UnMappedCount; + ULONG ApproximateTotalLength; + + + SAMTRACE("SamrLookupNamesInDomain"); + + + // + // Make sure we understand what RPC is doing for (to) us. + // + + ASSERT (Use != NULL); + ASSERT (Use->Element == NULL); + ASSERT (RelativeIds != NULL); + ASSERT (RelativeIds->Element == NULL); + + Use->Count = 0; + RelativeIds->Count = 0; + + + if (Count == 0) { + return(STATUS_SUCCESS); + } + + // + // Make sure the parameter values are within reasonable bounds + // + + if (Count > SAM_MAXIMUM_LOOKUP_COUNT) { + return(STATUS_INSUFFICIENT_RESOURCES); + } + ApproximateTotalLength = (Count*(sizeof(ULONG) + sizeof(SID_NAME_USE))); + + // + // Do the return test inside this loop to avoid overflow problems + // summing up the name lengths. + // + + for ( i=0; i<Count; i++) { + ApproximateTotalLength += (ULONG)Names[i].MaximumLength; + if ( ApproximateTotalLength > SAMP_MAXIMUM_MEMORY_TO_USE ) { + return(STATUS_INSUFFICIENT_RESOURCES); + } + } + + + + // + // Allocate the return buffers + // + + Use->Element = MIDL_user_allocate( Count * sizeof(ULONG) ); + if (Use->Element == NULL) { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + RelativeIds->Element = MIDL_user_allocate( Count * sizeof(ULONG) ); + if (RelativeIds->Element == NULL) { + MIDL_user_free( Use->Element); + Use->Element = NULL; // required to RPC doesn't free it again. + return(STATUS_INSUFFICIENT_RESOURCES); + } + + + Use->Count = Count; + RelativeIds->Count = Count; + + SampAcquireReadLock(); + + + // + // Validate type of, and access to object. + // + + DomainContext = (PSAMP_OBJECT)DomainHandle; + NtStatus = SampLookupContext( + DomainContext, + DOMAIN_LOOKUP, + SampDomainObjectType, // ExpectedType + &FoundType + ); + + + if (NT_SUCCESS(NtStatus)) { + + UnMappedCount = Count; + for ( i=0; i<Count; i++) { + + + // + // Search the groups for a match + // + + NtStatus = SampBuildAccountKeyName( + SampGroupObjectType, + &KeyName, + (PUNICODE_STRING)&Names[i] + ); + if (NT_SUCCESS(NtStatus)) { + + InitializeObjectAttributes( + &ObjectAttributes, + &KeyName, + OBJ_CASE_INSENSITIVE, + SampKey, + NULL + ); + + SampDumpNtOpenKey((KEY_READ), &ObjectAttributes, 0); + + NtStatus = RtlpNtOpenKey( + &TempHandle, + (KEY_READ), + &ObjectAttributes, + 0 + ); + SampFreeUnicodeString( &KeyName ); + + if (NT_SUCCESS(NtStatus)) { + + UnMappedCount -= 1; + Use->Element[i] = SidTypeGroup; + KeyValueLength = 0; + NtStatus = RtlpNtQueryValueKey( + TempHandle, + &RelativeIds->Element[i], + NULL, + &KeyValueLength, + &IgnoreTimeStamp + ); + + SampDumpRtlpNtQueryValueKey(&RelativeIds->Element[i], + NULL, + &KeyValueLength, + &IgnoreTimeStamp); + + IgnoreStatus = NtClose( TempHandle ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + if (!NT_SUCCESS(NtStatus)) { + goto unexpected_error; + } + ASSERT(KeyValueLength == 0); + + + } else { + + // + // Search the aliases for a match + // + + NtStatus = SampBuildAccountKeyName( + SampAliasObjectType, + &KeyName, + (PUNICODE_STRING)&Names[i] + ); + if (NT_SUCCESS(NtStatus)) { + + InitializeObjectAttributes( + &ObjectAttributes, + &KeyName, + OBJ_CASE_INSENSITIVE, + SampKey, + NULL + ); + + SampDumpNtOpenKey((KEY_READ), &ObjectAttributes, 0); + + NtStatus = RtlpNtOpenKey( + &TempHandle, + (KEY_READ), + &ObjectAttributes, + 0 + ); + SampFreeUnicodeString( &KeyName ); + + if (NT_SUCCESS(NtStatus)) { + + UnMappedCount -= 1; + Use->Element[i] = SidTypeAlias; + KeyValueLength = 0; + NtStatus = RtlpNtQueryValueKey( + TempHandle, + &RelativeIds->Element[i], + NULL, + &KeyValueLength, + &IgnoreTimeStamp + ); + + SampDumpRtlpNtQueryValueKey(&RelativeIds->Element[i], + NULL, + &KeyValueLength, + &IgnoreTimeStamp); + + IgnoreStatus = NtClose( TempHandle ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + if (!NT_SUCCESS(NtStatus)) { + goto unexpected_error; + } + ASSERT(KeyValueLength == 0); + + + } else { + + // + // Search the user for a match + // + + NtStatus = SampBuildAccountKeyName( + SampUserObjectType, + &KeyName, + (PUNICODE_STRING)&Names[i] + ); + if (NT_SUCCESS(NtStatus)) { + + InitializeObjectAttributes( + &ObjectAttributes, + &KeyName, + OBJ_CASE_INSENSITIVE, + SampKey, + NULL + ); + + + SampDumpNtOpenKey((KEY_READ), + &ObjectAttributes, + 0); + + NtStatus = RtlpNtOpenKey( + &TempHandle, + (KEY_READ), + &ObjectAttributes, + 0 + ); + SampFreeUnicodeString( &KeyName ); + + if (NT_SUCCESS(NtStatus)) { + + UnMappedCount -= 1; + Use->Element[i] = SidTypeUser; + KeyValueLength = 0; + NtStatus = RtlpNtQueryValueKey( + TempHandle, + &RelativeIds->Element[i], + NULL, + &KeyValueLength, + &IgnoreTimeStamp + ); + + SampDumpRtlpNtQueryValueKey(&RelativeIds->Element[i], + NULL, + &KeyValueLength, + &IgnoreTimeStamp); + + IgnoreStatus = NtClose( TempHandle ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + if (!NT_SUCCESS(NtStatus)) { + goto unexpected_error; + } + ASSERT(KeyValueLength == 0); + + } else if(NtStatus == STATUS_OBJECT_NAME_NOT_FOUND) { + + // + // This is fine. It just means that we don't + // have an account with the name being looked up. + // + + Use->Element[i] = SidTypeUnknown; + RelativeIds->Element[i] = 0; + NtStatus = STATUS_SUCCESS; + + } + + } + } + } + } + } + + if (!NT_SUCCESS(NtStatus) && + NtStatus != STATUS_INVALID_ACCOUNT_NAME) { + goto unexpected_error; + } + + } // end_for + + + // + // De-reference the object + // + + IgnoreStatus = SampDeReferenceContext( DomainContext, FALSE ); + + if (UnMappedCount == Count) { + NtStatus = STATUS_NONE_MAPPED; + } else { + if (UnMappedCount > 0) { + NtStatus = STATUS_SOME_NOT_MAPPED; + } else { + NtStatus = STATUS_SUCCESS; + } + } + } + + // + // Free the read lock + // + + SampReleaseReadLock(); + + + // + // If the status isn't one of the expected return values, + // then deallocate the return memory block + // + + if ( ( NtStatus != STATUS_SUCCESS ) && + ( NtStatus != STATUS_SOME_NOT_MAPPED ) ) { + + Use->Count = 0; + MIDL_user_free( Use->Element ); + Use->Element = NULL; + RelativeIds->Count = 0; + MIDL_user_free( RelativeIds->Element ); + RelativeIds->Element = NULL; + } + + + return( NtStatus ); + + +unexpected_error: + + // + // De-reference the object + // + + IgnoreStatus = SampDeReferenceContext( DomainContext, FALSE ); + + // + // Free the read lock + // + + SampReleaseReadLock(); + + + // + // Don't return any memory + // + + Use->Count = 0; + MIDL_user_free( Use->Element ); + Use->Element = NULL; // Required so RPC doesn't try to free the element + RelativeIds->Count = 0; + MIDL_user_free( RelativeIds->Element ); + RelativeIds->Element = NULL; // Required so RPC doesn't try to free the element + + + return( NtStatus ); + +} + + + +NTSTATUS SamrLookupIdsInDomain( + IN SAMPR_HANDLE DomainHandle, + IN ULONG Count, + IN PULONG RelativeIds, + OUT PSAMPR_RETURNED_USTRING_ARRAY Names, + OUT PSAMPR_ULONG_ARRAY Use + ) + + +/*++ + +Routine Description: + + This API maps a number of relative IDs to their corresponding names. + If a relative ID can not be mapped, a NULL value is placed in the slot + for the UNICODE_STRING, and STATUS_SOME_NOT_MAPPED is returned. + If none of the IDs can be mapped, then all array entries will contain + NULL values and STATUS_NONE_MAPPED is returned. + + DOMAIN_LOOKUP access to the domain is needed to use this service. + + + +Parameters: + + DomainHandle - A domain handle returned from a previous call to + SamOpenDomain. + + Count - Provides the number of relative IDs to translate. + + RelativeIds - Array of Count relative IDs to be mapped. + + Names - Receives a pointer to an allocated SAMPR_UNICODE_STRING_ARRAY. + The nth entry in the array of names pointed to by this structure + corresonds to the nth relative id looked up. + Each name string buffer will be in a separate block of memory + allocated by this routine. When these names are no longer + needed, the caller is responsible for deallocating each + returned block (including the SAMPR_RETURNED_USTRING_ARRAY structure + itself) using SamFreeMemory(). + + Use - Receives a pointer to a SAMPR_ULONG_ARRAY structure. + The nth entry in the array associated with this structure + contains the SID_NAME_USE of the nth relative ID looked up. + When this information is no longer needed, the caller is responsible + for deallocating this memory using SamFreeMemory(). + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The domain handle passed is invalid. + + STATUS_SOME_NOT_MAPPED - Some of the names provided could not be + mapped. This is a successful return. + + STATUS_NONE_MAPPED - No names could be mapped. This is an error + return. + + +--*/ +{ + + NTSTATUS NtStatus, IgnoreStatus; + SAMP_OBJECT_TYPE ObjectType; + PSAMP_OBJECT DomainContext; + PSAMP_DEFINED_DOMAINS Domain; + SAMP_OBJECT_TYPE FoundType; + ULONG i, UnMappedCount; + ULONG TotalLength; + PSAMP_MEMORY NextMemory; + SAMP_MEMORY MemoryHead; + + BOOLEAN LengthLimitReached = FALSE; + + + SAMTRACE("SamrLookupIdsInDomain"); + + // + // Used for tracking allocated memory so we can deallocate it on + // error + // + + MemoryHead.Memory = NULL; + MemoryHead.Next = NULL; + + + // + // Make sure we understand what RPC is doing for (to) us. + // + + ASSERT (RelativeIds != NULL); + ASSERT (Use != NULL); + ASSERT (Use->Element == NULL); + ASSERT (Names != NULL); + ASSERT (Names->Element == NULL); + + Use->Count = 0; + Names->Count = 0; + + if (Count == 0) { + return(STATUS_SUCCESS); + } + + TotalLength = (Count*(sizeof(ULONG) + sizeof(UNICODE_STRING))); + + if ( TotalLength > SAMP_MAXIMUM_MEMORY_TO_USE ) { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + if (Count == 0) { + return(STATUS_SUCCESS); + } + + + // + // Allocate the return buffers + // + + Use->Element = MIDL_user_allocate( Count * sizeof(ULONG) ); + if (Use->Element == NULL) { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + + Names->Element = MIDL_user_allocate( Count * sizeof(UNICODE_STRING) ); + if (Names->Element == NULL) { + MIDL_user_free( Use->Element); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + Use->Count = Count; + Names->Count = Count; + + SampAcquireReadLock(); + + + // + // Validate type of, and access to object. + // + + DomainContext = (PSAMP_OBJECT)DomainHandle; + NtStatus = SampLookupContext( + DomainContext, + DOMAIN_LOOKUP, + SampDomainObjectType, // ExpectedType + &FoundType + ); + + + if (NT_SUCCESS(NtStatus)) { + + UnMappedCount = Count; + for ( i=0; i<Count; i++) { + + // + // allocate a block to track a name allocated for this mapping + // + + NextMemory = MIDL_user_allocate( sizeof(SAMP_MEMORY) ); + if (NextMemory == NULL) { + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + goto unexpected_error; + } + + + NtStatus = SampLookupAccountName( + RelativeIds[i], + (PUNICODE_STRING)&Names->Element[i], + &ObjectType + ); + + if (!NT_SUCCESS(NtStatus)) { + goto unexpected_error; + } + + + switch (ObjectType) { + + case SampUserObjectType: + case SampGroupObjectType: + case SampAliasObjectType: + + // + // We found the account + // + + UnMappedCount -= 1; + + NextMemory->Memory = (PVOID)&Names->Element[i].Buffer; + NextMemory->Next = MemoryHead.Next; + MemoryHead.Next = NextMemory; + + switch (ObjectType) { + + case SampUserObjectType: + Use->Element[i] = SidTypeUser; + break; + + case SampGroupObjectType: + Use->Element[i] = SidTypeGroup; + break; + + case SampAliasObjectType: + Use->Element[i] = SidTypeAlias; + break; + } + + break; + + + case SampUnknownObjectType: + + // + // Hmmm - don't know what this rid is. It's either been + // deleted, or a bogus RID. + // + + Domain = &SampDefinedDomains[ DomainContext->DomainIndex ]; + + if ( ( RelativeIds[i] >= SAMP_RESTRICTED_ACCOUNT_COUNT ) && + ( RelativeIds[i] < Domain->CurrentFixed.NextRid ) ) { + + Use->Element[i] = SidTypeDeletedAccount; + + } else { + + Use->Element[i] = SidTypeUnknown; + } + + Names->Element[i].Length = 0; + Names->Element[i].MaximumLength = 0; + Names->Element[i].Buffer = NULL; + MIDL_user_free( NextMemory ); + + break; + + default: + + ASSERT(FALSE); // unexpected object type returned + break; + } + + } // end_for + + + // + // De-reference the object + // + + IgnoreStatus = SampDeReferenceContext( DomainContext, FALSE ); + + if (UnMappedCount == Count) { + NtStatus = STATUS_NONE_MAPPED; + } else { + if (UnMappedCount > 0) { + NtStatus = STATUS_SOME_NOT_MAPPED; + } else { + NtStatus = STATUS_SUCCESS; + } + } + } + + // + // Free the read lock + // + + SampReleaseReadLock(); + + + // + // Free all the memory tracking blocks + // + + NextMemory = MemoryHead.Next; + while ( NextMemory != NULL ) { + MemoryHead.Next = NextMemory->Next; + MIDL_user_free( NextMemory ); + NextMemory = MemoryHead.Next; + } + + + + return( NtStatus ); + + +unexpected_error: + + // + // De-reference the object + // + + IgnoreStatus = SampDeReferenceContext( DomainContext, FALSE ); + + // + // Free the read lock + // + + SampReleaseReadLock(); + + + // + // Free all the memory tracking blocks - and the memory they point to. + // + + Use->Count = 0; + Names->Count = 0; + MIDL_user_free( Use->Element ); + MIDL_user_free( Names->Element ); + NextMemory = MemoryHead.Next; + while ( NextMemory != NULL ) { + if (NextMemory->Memory != NULL) { + MIDL_user_free( NextMemory->Memory ); + } + MemoryHead.Next = NextMemory->Next; + MIDL_user_free( NextMemory ); + NextMemory = MemoryHead.Next; + } + + return( NtStatus ); +} + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// private services // +// // +/////////////////////////////////////////////////////////////////////////////// + + +NTSTATUS +SampOpenDomainKey( + IN PSAMP_OBJECT DomainContext, + IN PRPC_SID DomainId + ) + +/*++ + +Routine Description: + + This service attempts to open the root registry key of the domain with + the specified SID. The root name and key handle are put in the + passed domain context. + + + If successful, and the domain key is opened, then the opened domain is + established as the transaction domain (using SampSetTransactionDomain()). + + THIS SERVICE MUST BE CALLED WITH THE SampLock() HELD FOR READ OR + WRITE ACCESS. + +Arguments: + + DomainContext - Context in which root namd and handle are stored. + + DomainId - Specifies the SID of the domain to open. + +Return Value: + + STATUS_SUCCESS - The domain has been openned. + + STATUS_NO_SUCH_DOMAIN - The domain object could not be found. + + STATUS_INSUFFICIENT_RESOURCES - The domain object could not be openned + due to the lack of some resource (probably memory). + + STATUS_INVALID_SID - The sid provided as the domain identifier is not + a valid SID structure. + + Other errors that might be returned are values returned by: + +--*/ +{ + NTSTATUS NtStatus; + ULONG i; + + + + SAMTRACE("SampOpenDomainKey"); + + + // + // Make sure the SID provided is legitimate... + // + + if ( !RtlValidSid(DomainId)) { + NtStatus = STATUS_INVALID_SID; + } else { + + // + // Set our default completion status + // + + NtStatus = STATUS_NO_SUCH_DOMAIN; + + + // + // Search the list of defined domains for a match. + // + + for (i = 0; i<SampDefinedDomainsCount; i++ ) { + + if (RtlEqualSid( DomainId, SampDefinedDomains[i].Sid)) { + + // + // Copy the found name and handle into the context + // Note we reference the key handle in the defined_domains + // structure directly since it is not closed + // when the context is deleted. + // + + DomainContext->RootKey = SampDefinedDomains[i].Context->RootKey; + DomainContext->RootName = SampDefinedDomains[i].Context->RootName; + DomainContext->DomainIndex = i; + + // + // Set the transaction domain to the one found + // + + SampSetTransactionDomain( i ); + + NtStatus = STATUS_SUCCESS; + break; // out of for + } + } + } + + + return(NtStatus); +} + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Routines available to other SAM modules // +// // +/////////////////////////////////////////////////////////////////////////////// + + +BOOLEAN +SampInitializeDomainObject( VOID ) + +/*++ + +Routine Description: + + This service performs initialization functions related to the Domain + object class. + + This involves: + + 1) Openning the DOMAINS registry key. + + 2) Obtaining the name of each domain (builtin and account) + and opening that domain. + +Arguments: + + None. + +Return Value: + + TRUE - Indicates initialization was performed successfully. + + FALSE - Indicates initialization was not performed successfully. + + +--*/ + +{ + + NTSTATUS NtStatus; + ULONG DefinedDomainsSize, i, j, k; + BOOLEAN ReturnStatus = TRUE; + + + SAMTRACE("SampInitializeDomainObject"); + + // + // Open all domains and keep information about each in memory for + // somewhat fast processing and less complicated code strewn throughout. + // + // This concept will work in the future + // but we will have to allow dynamic re-sizing of this array + // when domains can be added and/or deleted. For the first + // revision, there exactly 2 domains and they can't be deleted. + // + + SampDefinedDomainsCount = 2; + DefinedDomainsSize = SampDefinedDomainsCount * sizeof(SAMP_DEFINED_DOMAINS); + SampDefinedDomains = MIDL_user_allocate( DefinedDomainsSize ); + + // + // Get the BUILTIN and ACCOUNT domain information from the LSA + // + + NtStatus = SampSetDomainPolicy(); + if (!NT_SUCCESS(NtStatus)) { + return(FALSE); + } + + + // + // Now prepare each of these domains + // + + i = 0; // Index into DefinedDomains array + k = SampDefinedDomainsCount; + for (j=0; j<k; j++) { + NtStatus = SampInitializeSingleDomain( i ); + + if (NT_SUCCESS(NtStatus)) { + + i++; + + } else { + + // + // If a domain didn't initialize, shift the last + // domain into its slot (assuming this isn't the last + // domain). Don't try to free the name buffers on error. + // The builtin domain's name is not in an allocated buffer. + // + // + + if (i != (SampDefinedDomainsCount-1)) { + + SampDefinedDomains[i] = + SampDefinedDomains[SampDefinedDomainsCount-1]; + + SampDefinedDomains[SampDefinedDomainsCount-1].ExternalName.Buffer = NULL; + SampDefinedDomains[SampDefinedDomainsCount-1].InternalName.Buffer = NULL; + SampDefinedDomains[SampDefinedDomainsCount-1].Sid = NULL; + } + + // + // And reduce the number of defined domains we have + // + + SampDefinedDomainsCount --; + } + } + + + + // + // We might not have successfully initialized all domains, + // so set the final tally accordingly. + // + +#if DBG + if (SampDefinedDomainsCount != 2) { + NTSTATUS IgnoreStatus; + if (SampDefinedDomainsCount == 0) { + + + DbgPrint("\n\n"); + DbgPrint("SAMSS: Neither of the two SAM domains has initialized.\n"); + DbgPrint(" This will not prevent the system from booting,\n"); + DbgPrint(" but logons will be prohibited and the system will\n"); + DbgPrint(" not be usable.\n"); + DbgPrint("\n\n"); + + } else { + + DbgPrint("\n\n"); + DbgPrint("SAMSS: Only one of the two SAM domains has initialized.\n"); + DbgPrint(" This will not prevent the system from booting,\n"); + DbgPrint(" but may result in abnormal logon or user security\n"); + DbgPrint(" context behaviour.\n\n"); + DbgPrint(" One typical cause of this is changing your machine\n"); + DbgPrint(" from a WinNt system to a LanManNT system (or vica versa).\n"); + DbgPrint(" You will need to delete (or rename) your existing\n"); + DbgPrint(" SAM database and generate a new one.\n"); + DbgPrint("\n\n"); + + IgnoreStatus = NtClose(SampDefinedDomains[0].Context->RootKey); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + SampFreeUnicodeString(&SampDefinedDomains[0].Context->RootName); + } + DbgPrint(" NOTE: in the end-product this will prevent booting.\n"); + DbgPrint(" For development, the System account is still\n"); + DbgPrint(" usable.\n"); + DbgPrint("\n\n"); + DbgPrint(" You probably want to logon as SYSTEM and run BLDSAM2.EXE\n\n"); + + // + // Allow the existing SAM database to be moved or deleted + // by closing handles that are still open within it. + // + + IgnoreStatus = NtClose( SampKey ); + } +#endif //DBG + + + return(TRUE); + +} + + +NTSTATUS +SampInitializeSingleDomain( + ULONG Index + ) + +/*++ + +Routine Description: + + This service opens a single domain that is expected to be in the + SAM database. + + The name and SID of the DefinedDomains array entry are expected + to be filled in by the caller. + +Arguments: + + Index - An index into the DefinedDomains array. This array + contains information about the domain being openned, + including its name. The remainder of this array entry + is filled in by this routine. + + +Return Value: + + + +--*/ +{ + NTSTATUS NtStatus, IgnoreStatus; + PSAMP_OBJECT DomainContext; + OBJECT_ATTRIBUTES ObjectAttributes; + PSID Sid; + PSAMP_V1_0A_FIXED_LENGTH_DOMAIN V1aFixed; + +#if DBG + SID *Sid1, *Sid2; +#endif + + SAMTRACE("SampInitializeSingleDomain"); + + + // + // Initialize everything we might have to cleanup on error + // + + DomainContext = NULL; + + // + // Initialize the user & group context block list heads + // + + InitializeListHead( &SampDefinedDomains[Index].UserContextHead ); + InitializeListHead( &SampDefinedDomains[Index].GroupContextHead ); + InitializeListHead( &SampDefinedDomains[Index].AliasContextHead ); + + + // + // Create a context for this domain object. + // We'll keep this context around until SAM is shutdown + // We store the context handle in the defined_domains structure. + // + + DomainContext = SampCreateContext( SampDomainObjectType, TRUE ); + + if ( DomainContext == NULL ) { + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + goto error_cleanup; + } + + DomainContext->DomainIndex = Index; + + // + // Create the name of the root key name of this domain in the registry. + // + + NtStatus = SampBuildDomainKeyName( + &DomainContext->RootName, + &SampDefinedDomains[Index].InternalName + ); + + if (!NT_SUCCESS(NtStatus)) { + DomainContext->RootName.Buffer = NULL; + goto error_cleanup; + } + + + // + // Open the root key and store the handle in the context + // + + InitializeObjectAttributes( + &ObjectAttributes, + &DomainContext->RootName, + OBJ_CASE_INSENSITIVE, + SampKey, + NULL + ); + + SampDumpNtOpenKey((KEY_READ | KEY_WRITE), &ObjectAttributes, 0); + + NtStatus = RtlpNtOpenKey( + &DomainContext->RootKey, + (KEY_READ | KEY_WRITE), + &ObjectAttributes, + 0 + ); + + if (!NT_SUCCESS(NtStatus)) { +#if DBG + DbgPrint("SAMSS: Failed to open %Z Domain.\n", + &SampDefinedDomains[Index].ExternalName); +#endif //DBG + DomainContext->RootKey = INVALID_HANDLE_VALUE; + return(NtStatus); + } + + + // + // Get the fixed length data for the domain and store in + // the defined_domain structure + // + + NtStatus = SampGetFixedAttributes( + DomainContext, + FALSE, // Don't make copy + (PVOID *)&V1aFixed + ); + + if (!NT_SUCCESS(NtStatus)) { +#if DBG + DbgPrint("SAMSS: Failed to get fixed attributes for %Z Domain.\n", + &SampDefinedDomains[Index].ExternalName); +#endif //DBG + + goto error_cleanup; + } + + + RtlMoveMemory( + &SampDefinedDomains[Index].UnmodifiedFixed, + V1aFixed, + sizeof(*V1aFixed) + ); + + + // + // Get the sid attribute of the domain + // + + NtStatus = SampGetSidAttribute( + DomainContext, + SAMP_DOMAIN_SID, + FALSE, + &Sid + ); + + if (!NT_SUCCESS(NtStatus)) { +#if DBG + DbgPrint("SAMSS: Failed to get SID attribute for %Z Domain.\n", + &SampDefinedDomains[Index].ExternalName); +#endif //DBG + goto error_cleanup; + } + + + // + // Make sure this sid agrees with the one we were passed + // + + if (RtlEqualSid(Sid, SampDefinedDomains[Index].Sid) != TRUE) { + +#if DBG + DbgPrint("SAMSS: Database corruption for %Z Domain.\n", + &SampDefinedDomains[Index].ExternalName); + + Sid1 = Sid; Sid2 = SampDefinedDomains[Index].Sid; + + DbgPrint("Sid1 Revision = %d\n", Sid1->Revision); + DbgPrint("Sid1 SubAuthorityCount = %d\n", Sid1->SubAuthorityCount); + DbgPrint("Sid1 IdentifierAuthority = %d\n", Sid1->IdentifierAuthority.Value[0]); + DbgPrint("Sid1 IdentifierAuthority = %d\n", Sid1->IdentifierAuthority.Value[1]); + DbgPrint("Sid1 IdentifierAuthority = %d\n", Sid1->IdentifierAuthority.Value[2]); + DbgPrint("Sid1 IdentifierAuthority = %d\n", Sid1->IdentifierAuthority.Value[3]); + DbgPrint("Sid1 IdentifierAuthority = %d\n", Sid1->IdentifierAuthority.Value[4]); + DbgPrint("Sid1 IdentifierAuthority = %d\n", Sid1->IdentifierAuthority.Value[5]); + DbgPrint("Sid1 SubAuthority = %lu\n", Sid1->SubAuthority[0]); + + DbgPrint("Sid2 Revision = %d\n", Sid2->Revision); + DbgPrint("Sid2 SubAuthorityCount = %d\n", Sid2->SubAuthorityCount); + DbgPrint("Sid2 IdentifierAuthority = %d\n", Sid2->IdentifierAuthority.Value[0]); + DbgPrint("Sid2 IdentifierAuthority = %d\n", Sid2->IdentifierAuthority.Value[1]); + DbgPrint("Sid2 IdentifierAuthority = %d\n", Sid2->IdentifierAuthority.Value[2]); + DbgPrint("Sid2 IdentifierAuthority = %d\n", Sid2->IdentifierAuthority.Value[3]); + DbgPrint("Sid2 IdentifierAuthority = %d\n", Sid2->IdentifierAuthority.Value[4]); + DbgPrint("Sid2 IdentifierAuthority = %d\n", Sid2->IdentifierAuthority.Value[5]); + DbgPrint("Sid2 SubAuthority = %lu\n", Sid2->SubAuthority[0]); +#endif //DBG + + NtStatus = STATUS_INVALID_ID_AUTHORITY; + goto error_cleanup; + } + + + // + // Build security descriptors for use in user and group account creations + // in this domain. + // + + NtStatus = SampInitializeDomainDescriptors( Index ); + if (!NT_SUCCESS(NtStatus)) { + goto error_cleanup; + } + + // + // Intialize the cached display information + // + + // MURLIS 6/11/96 -- Store the Context handle in the + // defined domains structure, before calling Initialize Display + // information as Enumerate Account Names will need this information + // to make the decision wether the domain is in the DS or in + // Registry. + + SampDefinedDomains[Index].Context = DomainContext; + + NtStatus = SampInitializeDisplayInformation( Index ); + + + + if (!NT_SUCCESS(NtStatus)) { + + // + // NULL out the context handle in the defined_domain structure + // + + SampDefinedDomains[Index].Context = NULL; + goto error_cleanup; + } + + + + return(NtStatus); + + +error_cleanup: + +#if DBG + DbgPrint(" Status is 0x%lx \n", NtStatus); +#endif //DBG + + + if (DomainContext != 0) { + + SampFreeUnicodeString(&DomainContext->RootName); + + if (DomainContext->RootKey != INVALID_HANDLE_VALUE) { + + IgnoreStatus = NtClose(DomainContext->RootKey); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + } + + return(NtStatus); + +} + + + +NTSTATUS +SampSetDomainPolicy( + ) +/*++ + + +Routine Description: + + This routine sets the names and SIDs for the builtin and account domains. + The builtin account domain has a well known name and SID. + The account domain has these stored in the Policy database. + + + It places the information for the builtin domain in + SampDefinedDomains[0] and the information for the account + domain in SampDefinedDomains[1]. + + +Arguments: + + None. + +Return Value: + + + +--*/ +{ + NTSTATUS NtStatus; + PPOLICY_ACCOUNT_DOMAIN_INFO PolicyAccountDomainInfo; + SID_IDENTIFIER_AUTHORITY BuiltinAuthority = SECURITY_NT_AUTHORITY; + + SAMTRACE("SampSetDomainPolicy"); + + // + // Builtin domain - Well-known External Name and SID + // Constant Internal Name + + RtlInitUnicodeString( &SampDefinedDomains[0].InternalName, L"Builtin"); + RtlInitUnicodeString( &SampDefinedDomains[0].ExternalName, L"Builtin"); + + SampDefinedDomains[0].Sid = RtlAllocateHeap(RtlProcessHeap(), 0,RtlLengthRequiredSid( 1 )); + ASSERT( SampDefinedDomains[0].Sid != NULL ); + RtlInitializeSid( + SampDefinedDomains[0].Sid, &BuiltinAuthority, 1 ); + *(RtlSubAuthoritySid( SampDefinedDomains[0].Sid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID; + + // + // Account domain - Configurable External Name and Sid + // The External Name is held in the LSA Policy + // Database. It is equal to the Domain Name for DC's + // or the Computer Name for Workstations. + // Constant Internal Name + // + + NtStatus = SampGetAccountDomainInfo( &PolicyAccountDomainInfo ); + + if (NT_SUCCESS(NtStatus)) { + + SampDefinedDomains[1].Sid = PolicyAccountDomainInfo->DomainSid; + SampDefinedDomains[1].ExternalName = PolicyAccountDomainInfo->DomainName; + RtlInitUnicodeString( &SampDefinedDomains[1].InternalName, L"Account"); + } + + return(NtStatus);; +} + + +NTSTATUS +SampReInitializeSingleDomain( + ULONG Index + ) + +/*++ + +Routine Description: + + This service reinitializes a single domain after a registry hive refresh. + +Arguments: + + Index - An index into the DefinedDomains array. + +Return Value: + + STATUS_SUCCESS : The domain was re-initialized successfully. + + Other failure codes. + +--*/ +{ + NTSTATUS NtStatus; + PSAMP_DEFINED_DOMAINS Domain; + PSAMP_OBJECT DomainContext; + OBJECT_ATTRIBUTES ObjectAttributes; + PSAMP_V1_0A_FIXED_LENGTH_DOMAIN V1aFixed; + + SAMTRACE("SampReinitializeSingleDomain"); + + Domain = &SampDefinedDomains[Index]; + + // + // Create a context for this domain object. + // We'll keep this context around until SAM is shutdown + // We store the context handle in the defined_domains structure. + // + + DomainContext = SampCreateContext( SampDomainObjectType, TRUE ); + + if ( DomainContext == NULL ) { + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + goto error_cleanup; + } + + DomainContext->DomainIndex = Index; + + // + // Create the name of the root key name of this domain in the registry. + // + + NtStatus = SampBuildDomainKeyName( + &DomainContext->RootName, + &SampDefinedDomains[Index].InternalName + ); + + if (!NT_SUCCESS(NtStatus)) { + RtlInitUnicodeString(&DomainContext->RootName, NULL); + goto error_cleanup; + } + + + // + // Open the root key and store the handle in the context + // + + InitializeObjectAttributes( + &ObjectAttributes, + &DomainContext->RootName, + OBJ_CASE_INSENSITIVE, + SampKey, + NULL + ); + + SampDumpNtOpenKey((KEY_READ | KEY_WRITE), &ObjectAttributes, 0); + + NtStatus = RtlpNtOpenKey( + &DomainContext->RootKey, + (KEY_READ | KEY_WRITE), + &ObjectAttributes, + 0 + ); + + if (!NT_SUCCESS(NtStatus)) { + KdPrint(("SAMSS: Failed to open %Z Domain.\n", &SampDefinedDomains[Index].ExternalName)); + DomainContext->RootKey = INVALID_HANDLE_VALUE; + goto error_cleanup; + } + + KdPrint(("SAM New domain %d key : 0x%lx\n", Index, DomainContext->RootKey)); + + // + // Get the fixed length data for the domain and store in + // the defined_domain structure + // + + NtStatus = SampGetFixedAttributes( + DomainContext, + FALSE, // Don't make copy + (PVOID *)&V1aFixed + ); + + if (!NT_SUCCESS(NtStatus)) { + KdPrint(("SAMSS: Failed to get fixed attributes for %Z Domain.\n", &SampDefinedDomains[Index].ExternalName)); + goto error_cleanup; + } + + // + // Copy the fixed-length data into our in-memory data area for this domain. + // + + RtlMoveMemory( + &SampDefinedDomains[Index].UnmodifiedFixed, + V1aFixed, + sizeof(*V1aFixed) + ); + + + // + // Delete any cached display information + // + + { + ULONG OldTransactionDomainIndex = SampTransactionDomainIndex; + SampTransactionDomainIndex = Index; + + NtStatus = SampMarkDisplayInformationInvalid(SampUserObjectType); + NtStatus = SampMarkDisplayInformationInvalid(SampGroupObjectType); + + SampTransactionDomainIndex = OldTransactionDomainIndex; + } + + + + if (NT_SUCCESS(NtStatus)) { + + // + // Store the context handle in the defined_domain structure + // + + SampDeleteContext(Domain->Context); + Domain->Context = DomainContext; + + + // + // Close all account context registry handles for existing + // open contexts + // + + SampInvalidateContextListKeys(&Domain->UserContextHead, TRUE); + SampInvalidateContextListKeys(&Domain->GroupContextHead, TRUE); + SampInvalidateContextListKeys(&Domain->AliasContextHead, TRUE); + } + + + return(NtStatus); + + +error_cleanup: + + KdPrint((" Status is 0x%lx \n", NtStatus)); + + if (DomainContext != NULL) { + SampDeleteContext(DomainContext); + } + + return(NtStatus); + +} + + +NTSTATUS +SampCollisionError( + IN SAMP_OBJECT_TYPE ObjectType + ) + +/*++ + +Routine Description: + + This routine is called by SamICreateAccountByRid when there is a + name or RID collision. It accepts the type of account which caused + the collision, and returns the appropriate error status. + +Arguments: + + ObjectType - The type of account that has the same Rid or Name (but + not both) as the account that was to be created. + +Return Value: + + STATUS_USER_EXISTS - An object with the specified name could not be + created because a User account with that name or RID already exists. + + STATUS_GROUP_EXISTS - An object with the specified name could not be + created because a Group account with that name or RID already exists. + + STATUS_ALIAS_EXISTS - An object with the specified name could not be + created because an Alias account with that name or RID already exists. + +--*/ +{ + + SAMTRACE("SampCollisionError"); + + // + // Name collision. Return offending RID and appropriate + // error code. + // + + switch ( ObjectType ) { + + case SampAliasObjectType: { + + return STATUS_ALIAS_EXISTS; + } + + case SampGroupObjectType: { + + return STATUS_GROUP_EXISTS; + } + + case SampUserObjectType: { + + return STATUS_USER_EXISTS; + } + } +} + + + +NTSTATUS +SamICreateAccountByRid( + IN SAMPR_HANDLE DomainHandle, + IN SAM_ACCOUNT_TYPE AccountType, + IN ULONG RelativeId, + IN PRPC_UNICODE_STRING AccountName, + IN ACCESS_MASK DesiredAccess, + OUT SAMPR_HANDLE *AccountHandle, + OUT ULONG *ConflictingAccountRid + ) + +/*++ + +Routine Description: + + This service creates a user, group or alias account with a specific + RID value. + + +Arguments: + + DomainHandle - A handle to an open domain. + + AccountType - Specifies which type of account is being created. + + RelativeId - The relative ID to be assigned to the account. If an + account of the specified type and specified RID value and + specified name already exists, then it will be opened. If an + account exists with any of this information in conflict, then an + error will be returned indicating what the problem is. + + AccountName - The name to assign to the account. If an account of + the specified type and specified RID value and specified name + already exists, then it will be opened. If an account exists with + any of this information in conflict, then an error will be returned + indicating what the problem is. + + DesiredAccess - Specifies the accesses desired to the account object. + + AccountHandle - Recieves a handle to the account object. + + ConflictingAccountRid - If another account with the same name or RID + prevents this account from being created, then this will receive + the RID of the conflicting account (in the case of conflicting + RIDs, this means that we return the RID that was passed in). + The error value indicates the type of the account. + + +Return Value: + + STATUS_SUCCESS - The object has been successfully opened or created. + + STATUS_OBJECT_TYPE_MISMATCH - The specified object type did not match + the type of the object found with the specified RID. + + STATUS_USER_EXISTS - An object with the specified name could not be + created because a User account with that name already exists. + + STATUS_GROUP_EXISTS - An object with the specified name could not be + created because a Group account with that name already exists. + + STATUS_ALIAS_EXISTS - An object with the specified name could not be + created because an Alias account with that name already exists. + + +--*/ +{ + PSAMP_OBJECT DomainContext; + SAMP_OBJECT_TYPE FoundType; + UNICODE_STRING KeyName; + OBJECT_ATTRIBUTES ObjectAttributes; + SAM_ACCOUNT_TYPE ObjectType, SecondObjectType, ThirdObjectType; + SID_NAME_USE SidNameUse; + HANDLE KeyHandle; + NTSTATUS NtStatus, IgnoreStatus, + NotFoundStatus, FoundButWrongStatus; + ACCESS_MASK GrantedAccess; + + SAMTRACE("SamICreateAccountByRid"); + + ASSERT( RelativeId != 0 ); + + switch ( AccountType ) { + + case SamObjectUser: { + + ObjectType = SampUserObjectType; + SecondObjectType = SampAliasObjectType; + ThirdObjectType = SampGroupObjectType; + NotFoundStatus = STATUS_NO_SUCH_USER; + FoundButWrongStatus = STATUS_USER_EXISTS; + break; + } + + case SamObjectGroup: { + + ObjectType = SampGroupObjectType; + SecondObjectType = SampAliasObjectType; + ThirdObjectType = SampUserObjectType; + NotFoundStatus = STATUS_NO_SUCH_GROUP; + FoundButWrongStatus = STATUS_GROUP_EXISTS; + break; + } + + case SamObjectAlias: { + + ObjectType = SampAliasObjectType; + SecondObjectType = SampGroupObjectType; + ThirdObjectType = SampUserObjectType; + NotFoundStatus = STATUS_NO_SUCH_ALIAS; + FoundButWrongStatus = STATUS_ALIAS_EXISTS; + break; + } + + default: { + + return( STATUS_INVALID_PARAMETER ); + } + } + + // + // See if the account specified already exists. + // + + NtStatus = SampAcquireWriteLock(); + + if ( !NT_SUCCESS( NtStatus ) ) { + + return( NtStatus ); + } + + // + // Validate type of, and access to object. + // + + DomainContext = (PSAMP_OBJECT)DomainHandle; + NtStatus = SampLookupContext( + DomainContext, + 0, + SampDomainObjectType, // ExpectedType + &FoundType + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampLookupAccountRid( + DomainContext, + ObjectType, + (PUNICODE_STRING)AccountName, + NotFoundStatus, + ConflictingAccountRid, + &SidNameUse + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // The NAME exists; now we have to check the RID. + // + + if ( (*ConflictingAccountRid) == RelativeId ) { + + // + // The correct account already exists, so just open it. + // + + SampTransactionWithinDomain = FALSE; + + NtStatus = SampOpenAccount( + ObjectType, + DomainHandle, + DesiredAccess, + RelativeId, + TRUE, //we already have the lock + AccountHandle + ); + + goto Done; + + } else { + + // + // An account with the given name, but a different RID, exists. + // Return error. + // + + NtStatus = FoundButWrongStatus; + } + + } else { + + if ( NtStatus == NotFoundStatus ) { + + // + // Account doesn't exist, that's good + // + + NtStatus = STATUS_SUCCESS; + } + } + + if (NT_SUCCESS(NtStatus)) { + + // + // Check for name collision with 2nd object type + // + + NtStatus = SampLookupAccountRid( + DomainContext, + SecondObjectType, + (PUNICODE_STRING)AccountName, + STATUS_UNSUCCESSFUL, + ConflictingAccountRid, + &SidNameUse + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // The name was found; return an error. + // + + NtStatus = SampCollisionError( SecondObjectType ); + + } else { + + if ( NtStatus == STATUS_UNSUCCESSFUL ) { + + // + // Account doesn't exist, that's good + // + + NtStatus = STATUS_SUCCESS; + } + } + } + + + if (NT_SUCCESS(NtStatus)) { + + // + // Check for name collision with 3rd object type + // + + NtStatus = SampLookupAccountRid( + DomainContext, + ThirdObjectType, + (PUNICODE_STRING)AccountName, + STATUS_UNSUCCESSFUL, + ConflictingAccountRid, + &SidNameUse + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + NtStatus = SampCollisionError( ThirdObjectType ); + + } else { + + if ( NtStatus == STATUS_UNSUCCESSFUL ) { + + // + // Account doesn't exist, that's good + // + + NtStatus = STATUS_SUCCESS; + } + } + } + + + + if (NT_SUCCESS(NtStatus)) { + + // + // We didn't find the name as an alias, group or user. + // Now, check to see if the RID is already in use. First, + // check to see if the specified RID is already in use as + // the specified account type, but with a different name. + // + + SampTransactionWithinDomain = FALSE; + + NtStatus = SampOpenAccount( + ObjectType, + DomainHandle, + DesiredAccess, + RelativeId, + TRUE, //we already have the lock + AccountHandle + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // The RID is in use, but the name is wrong. + // + + SampTransactionWithinDomain = FALSE; + IgnoreStatus = SamrCloseHandle( AccountHandle ); + ASSERT( NT_SUCCESS( IgnoreStatus ) ); + + *ConflictingAccountRid = RelativeId; + + NtStatus = FoundButWrongStatus; + + } else { + + // + // RID not in use, that's good + // + + NtStatus = STATUS_SUCCESS; + } + } + + + + + if (NT_SUCCESS(NtStatus)) { + + // + // Now check to see if the RID is already in use, but in + // an account type other than the one specified. (type2) + // + + NtStatus = SampBuildAccountSubKeyName( + SecondObjectType, + &KeyName, + RelativeId, + NULL // Don't give a sub-key name + ); + + if (NT_SUCCESS(NtStatus)) { + + InitializeObjectAttributes( + &ObjectAttributes, + &KeyName, + OBJ_CASE_INSENSITIVE, + SampKey, + NULL + ); + + SampDumpNtOpenKey((KEY_READ | KEY_WRITE), &ObjectAttributes, 0); + + NtStatus = RtlpNtOpenKey( + &KeyHandle, + (KEY_READ | KEY_WRITE), + &ObjectAttributes, + 0 + ); + + SampFreeUnicodeString( &KeyName ); + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // There is an account with the specified RID. + // Return error. + // + + IgnoreStatus = NtClose( KeyHandle ); + ASSERT( NT_SUCCESS( IgnoreStatus ) ); + + *ConflictingAccountRid = RelativeId; + + NtStatus = SampCollisionError( SecondObjectType ); + + } else { + NtStatus = STATUS_SUCCESS; + } + } + } + + + + if (NT_SUCCESS(NtStatus)) { + + // + // Now check to see if the RID is already in use, but in + // an account type other than the one specified. (type3) + // + + NtStatus = SampBuildAccountSubKeyName( + ThirdObjectType, + &KeyName, + RelativeId, + NULL // Don't give a sub-key name + ); + + if (NT_SUCCESS(NtStatus)) { + + InitializeObjectAttributes( + &ObjectAttributes, + &KeyName, + OBJ_CASE_INSENSITIVE, + SampKey, + NULL + ); + + SampDumpNtOpenKey((KEY_READ | KEY_WRITE), &ObjectAttributes, 0); + + NtStatus = RtlpNtOpenKey( + &KeyHandle, + (KEY_READ | KEY_WRITE), + &ObjectAttributes, + 0 + ); + + SampFreeUnicodeString( &KeyName ); + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // There is an account with the specified + // RID. Return error. + // + + IgnoreStatus = NtClose( KeyHandle ); + ASSERT( NT_SUCCESS( IgnoreStatus ) ); + + *ConflictingAccountRid = RelativeId; + + NtStatus = SampCollisionError( ThirdObjectType ); + + } else { + + NtStatus = STATUS_SUCCESS; + } + } + } + + + + + + if (NT_SUCCESS(NtStatus)) { + + // + // We haven't found a conflicting account, so go ahead + // and create this one with the name and RID specified. + // + + switch ( AccountType ) { + + case SamObjectUser: { + + SampTransactionWithinDomain = FALSE; + + NtStatus = SampCreateUserInDomain( + DomainHandle, + AccountName, + USER_NORMAL_ACCOUNT, + DesiredAccess, + TRUE, + AccountHandle, + &GrantedAccess, + &RelativeId + ); + + break; + } + + case SamObjectGroup: { + + SampTransactionWithinDomain = FALSE; + + NtStatus = SampCreateGroupInDomain( + DomainHandle, + AccountName, + DesiredAccess, + TRUE, + AccountHandle, + &RelativeId + ); + break; + } + + case SamObjectAlias: { + + SampTransactionWithinDomain = FALSE; + + NtStatus = SampCreateAliasInDomain( + DomainHandle, + AccountName, + DesiredAccess, + TRUE, + AccountHandle, + &RelativeId + ); + break; + } + } + + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // We may have created a new account. Set the domain's RID marker, + // if necessary, to make sure we don't reuse the RID we just created. + // + + PSAMP_DEFINED_DOMAINS Domain = &SampDefinedDomains[ DomainContext->DomainIndex ]; + + if ( RelativeId >= Domain->CurrentFixed.NextRid ) { + Domain->CurrentFixed.NextRid = RelativeId + 1; + } + } + } + + +Done: + // + // De-reference the domain object + // + + IgnoreStatus = SampDeReferenceContext( DomainContext, FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + + if ( NT_SUCCESS( NtStatus ) ) { + + NtStatus = SampReleaseWriteLock( TRUE ); + + } else { + + IgnoreStatus = SampReleaseWriteLock( FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + return( NtStatus ); +} + + + +NTSTATUS +SamIGetSerialNumberDomain( + IN SAMPR_HANDLE DomainHandle, + OUT PLARGE_INTEGER ModifiedCount, + OUT PLARGE_INTEGER CreationTime + ) + +/*++ + +Routine Description: + + This routine retrieves the creation time and modified count of the + domain. This information is used as a serial number for the domain. + +Arguments: + + DomainHandle - Handle to the domain being replicated. + + ModifiedCount - Retrieves the current count of modifications to the + domain. + + CreationTime - Receives the date/time the domain was created. + +Return Value: + + STATUS_SUCCESS - The service has completed successfully. + +--*/ +{ + PSAMP_DEFINED_DOMAINS Domain; + PSAMP_OBJECT DomainContext; + SAMP_OBJECT_TYPE FoundType; + NTSTATUS NtStatus; + NTSTATUS IgnoreStatus; + + SAMTRACE("SamIGetSerialNumberDomain"); + + SampAcquireReadLock(); + + // + // Validate type of, and access to object. + // + + DomainContext = (PSAMP_OBJECT)DomainHandle; + + NtStatus = SampLookupContext( + DomainContext, + 0L, + SampDomainObjectType, // ExpectedType + &FoundType + ); + + if (NT_SUCCESS(NtStatus)) { + + Domain = &SampDefinedDomains[ DomainContext->DomainIndex ]; + + (*ModifiedCount) = Domain->UnmodifiedFixed.ModifiedCount; + (*CreationTime) = Domain->UnmodifiedFixed.CreationTime; + + // + // De-reference the domain object + // + + IgnoreStatus = SampDeReferenceContext( DomainContext, FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + SampReleaseReadLock(); + + return( NtStatus ); +} + + + +NTSTATUS +SamISetSerialNumberDomain( + IN SAMPR_HANDLE DomainHandle, + IN PLARGE_INTEGER ModifiedCount, + IN PLARGE_INTEGER CreationTime, + IN BOOLEAN StartOfFullSync + ) + +/*++ + +Routine Description: + + This routine causes the creation time and modified count of the + domain to be replaced. This information is used as a serial number + for the domain. + +Arguments: + + DomainHandle - Handle to the domain being replicated. + + ModifiedCount - Provides the current count of modifications to the + domain. + + CreationTime - Provides the date/time the domain was created. + + StartOfFullSync - This boolean indicates whether a full sync is being + initiated. If this is TRUE, then a full sync is to follow and + all existing domain information may be discarded. If this is + FALSE, then only specific domain information is to follow and all + changes must not violate statndard SAM operation behaviour. + +Return Value: + + STATUS_SUCCESS - The service has completed successfully. + + Other failures may be returned from SampReleaseWriteLock(). + +--*/ +{ + LARGE_INTEGER LargeOne, AdjustedModifiedCount; + NTSTATUS NtStatus, TmpStatus, IgnoreStatus; + PSAMP_DEFINED_DOMAINS Domain; + PSAMP_OBJECT DomainContext; + SAMP_OBJECT_TYPE FoundType; + + UNREFERENCED_PARAMETER( StartOfFullSync ); + + SAMTRACE("SamISetSerialNumberDomain"); + + NtStatus = SampAcquireWriteLock(); + + if ( !NT_SUCCESS( NtStatus ) ) { + + return(NtStatus); + } + + // + // Validate type of, and access to object. + // + + DomainContext = (PSAMP_OBJECT)DomainHandle; + + NtStatus = SampLookupContext( + DomainContext, + 0L, + SampDomainObjectType, // ExpectedType + &FoundType + ); + + if (NT_SUCCESS(NtStatus)) { + + Domain = &SampDefinedDomains[ DomainContext->DomainIndex ]; + + // + // Now set the Domain's ModifiedCount and CreationTime to the values + // specified. + // + + Domain->CurrentFixed.CreationTime = (*CreationTime); + + if ( SampDefinedDomains[SampTransactionDomainIndex].CurrentFixed.ServerRole + == DomainServerRoleBackup ) { + + // + // Go ahead and use the ModifiedCount that was passed in. + // Since this is a BDC, the commit code will not increment + // the ModifiedCount. + // + + Domain->CurrentFixed.ModifiedCount = (*ModifiedCount); + + } else { + + // + // This is a PDC, so the commit code will increment the + // ModifiedCount before writing it out to disk. So decrement + // it here so that it ends up at the right value. + // + + + AdjustedModifiedCount.QuadPart = ModifiedCount->QuadPart - 1 ; + + Domain->CurrentFixed.ModifiedCount = AdjustedModifiedCount; + } + + if ( !( ModifiedCount->QuadPart == 0) || + !StartOfFullSync ) { + + // + // If ModifiedCount is non-zero, we must be ending a full + // or partial replication of the database...or perhaps we've + // just finished a 128k chunk over a WAN or somesuch. Let's + // ask to flush this stuff out to disk right away, rather + // than waiting for the flush thread to get around to it. + // + + FlushImmediately = TRUE; + } + + + + + SampDiagPrint( DISPLAY_ROLE_CHANGES, + ("SAM: SamISetSerialNumberDomain\n" + " Old ModifiedId: [0x%lx, 0x%lx]\n" + " New ModifiedId: [0x%lx, 0x%lx]\n", + Domain->UnmodifiedFixed.ModifiedCount.HighPart, + Domain->UnmodifiedFixed.ModifiedCount.LowPart, + Domain->CurrentFixed.ModifiedCount.HighPart, + Domain->CurrentFixed.ModifiedCount.LowPart ) + ); + + + // + // De-reference the domain object + // Don't save changes - the domain fixed info will be written + // out when the write lock is released. + // + + IgnoreStatus = SampDeReferenceContext( DomainContext, FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + + NtStatus = SampReleaseWriteLock( TRUE ); + + } else { + + TmpStatus = SampReleaseWriteLock( FALSE ); + } + + return( NtStatus ); +} + + + +NTSTATUS +SampGetPrivateUserData( + PSAMP_OBJECT UserContext, + OUT PULONG DataLength, + OUT PVOID *Data + ) + +/*++ + +Routine Description: + + This service is used during replication of private user + type-specific information. It reads the private user information from + the registry, and adjusts it if necessary (ie if the password history + value is smaller than it used to be). + +Arguments: + + UserContext - A handle to a User. + + DataLength - The length of the data returned. + + Data - Receives a pointer to a buffer of length DataLength allocated + and returned by SAM. The buffer must be freed to the process + heap when it is no longer needed. + +Return Value: + + STATUS_SUCCESS - Indicates the service completed successfully. + + STATUS_INVALID_PARAMETER_1 - The object type of the provided handle + does not support this operation. + + +--*/ +{ + NTSTATUS NtStatus; + UNICODE_STRING TempString; + PSAMI_PRIVATE_DATA_PASSWORD_TYPE PasswordData; + PSAMP_DEFINED_DOMAINS Domain; + PVOID BufferPointer; + + + SAMTRACE("SampGetPrivateUserData"); + + Domain = &SampDefinedDomains[ UserContext->DomainIndex ]; + + // + // Return data length as the maximum possible for this domain + // - the size of the structure, plus the maximum size of the + // NT and LM password histories. + // + + *DataLength = ( ( Domain->UnmodifiedFixed.PasswordHistoryLength ) + * ENCRYPTED_NT_OWF_PASSWORD_LENGTH ) + + ( ( Domain->UnmodifiedFixed.PasswordHistoryLength ) * + ENCRYPTED_LM_OWF_PASSWORD_LENGTH ) + + sizeof( SAMI_PRIVATE_DATA_PASSWORD_TYPE ); + + *Data = MIDL_user_allocate( *DataLength ); + + if ( *Data == NULL ) { + + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + + } else { + + PasswordData = (PSAMI_PRIVATE_DATA_PASSWORD_TYPE)*Data; + PasswordData->DataType = 0; // set correctly when we're done + + NtStatus = SampGetUnicodeStringAttribute( + UserContext, + SAMP_USER_DBCS_PWD, + FALSE, + &TempString + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + PasswordData->CaseInsensitiveDbcs = TempString; + + RtlCopyMemory( + &(PasswordData->CaseInsensitiveDbcsBuffer), + PasswordData->CaseInsensitiveDbcs.Buffer, + PasswordData->CaseInsensitiveDbcs.Length ); + + NtStatus = SampGetUnicodeStringAttribute( + UserContext, + SAMP_USER_UNICODE_PWD, + FALSE, + &TempString + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + PasswordData->CaseSensitiveUnicode = TempString; + + RtlCopyMemory( + &(PasswordData->CaseSensitiveUnicodeBuffer), + PasswordData->CaseSensitiveUnicode.Buffer, + PasswordData->CaseSensitiveUnicode.Length ); + + NtStatus = SampGetUnicodeStringAttribute( + UserContext, + SAMP_USER_NT_PWD_HISTORY, + FALSE, + &TempString + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // If history is too long, must shorten here + // + + PasswordData->NtPasswordHistory = TempString; + + if ( PasswordData->NtPasswordHistory.Length > (USHORT) + ( Domain->UnmodifiedFixed.PasswordHistoryLength + * ENCRYPTED_NT_OWF_PASSWORD_LENGTH ) ) { + + PasswordData->NtPasswordHistory.Length = (USHORT) + ( Domain->UnmodifiedFixed.PasswordHistoryLength + * ENCRYPTED_NT_OWF_PASSWORD_LENGTH ); + } + + // + // Put the body of the Nt password history + // immediately following the structure. + // + + BufferPointer = (PVOID)(((PCHAR)PasswordData) + + sizeof( SAMI_PRIVATE_DATA_PASSWORD_TYPE ) ); + + RtlCopyMemory( + BufferPointer, + PasswordData->NtPasswordHistory.Buffer, + PasswordData->NtPasswordHistory.Length ); + + NtStatus = SampGetUnicodeStringAttribute( + UserContext, + SAMP_USER_LM_PWD_HISTORY, + FALSE, + &TempString + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + PasswordData->LmPasswordHistory = TempString; + + if ( PasswordData->LmPasswordHistory.Length > (USHORT) + ( Domain->UnmodifiedFixed.PasswordHistoryLength + * ENCRYPTED_LM_OWF_PASSWORD_LENGTH ) ) { + + PasswordData->LmPasswordHistory.Length = (USHORT) + ( Domain->UnmodifiedFixed.PasswordHistoryLength + * ENCRYPTED_LM_OWF_PASSWORD_LENGTH ); + } + + // + // Put the body of the Lm password history + // immediately following the Nt password + // history. + // + + BufferPointer = (PVOID)(((PCHAR)(BufferPointer)) + + PasswordData->NtPasswordHistory.Length ); + + RtlCopyMemory( + BufferPointer, + PasswordData->LmPasswordHistory.Buffer, + PasswordData->LmPasswordHistory.Length ); + + PasswordData->DataType = SamPrivateDataPassword; + } + } + } + } + } + + return( NtStatus ); +} + + + +NTSTATUS +SamIGetPrivateData( + IN SAMPR_HANDLE SamHandle, + IN PSAMI_PRIVATE_DATA_TYPE PrivateDataType, + OUT PBOOLEAN SensitiveData, + OUT PULONG DataLength, + OUT PVOID *Data + ) + +/*++ + +Routine Description: + + This service is used to replicate private object type-specific + information. This information must be replicated for each instance + of the object type that is replicated. + +Arguments: + + SamHandle - A handle to a Domain, User, Group or Alias. + + PrivateDataType - Indicates which private data is being retrieved. + The data type must correspond to the type of object that the + handle is to. + + SensitiveData - Indicates that the data returned must be encrypted + before being sent anywhere. + + DataLength - The length of the data returned. + + Data - Receives a pointer to a buffer of length DataLength allocated + and returned by SAM. The buffer must be freed to the process + heap when it is no longer needed. + +Return Value: + + STATUS_SUCCESS - Indicates the service completed successfully. + + STATUS_INVALID_PARAMETER_1 - The object type of the provided handle + does not support this operation. + + +--*/ +{ + + NTSTATUS NtStatus, IgnoreStatus; + SAMP_OBJECT_TYPE FoundType; + PSAMP_DEFINED_DOMAINS Domain; + + SAMTRACE("SamIGetPrivateData"); + + SampAcquireReadLock(); + + switch ( *PrivateDataType ) { + + case SamPrivateDataNextRid: { + + PSAMP_OBJECT DomainContext; + + // + // Validate type of, and access to object. + // + + DomainContext = (PSAMP_OBJECT)SamHandle; + NtStatus = SampLookupContext( + DomainContext, + 0L, + SampDomainObjectType, // ExpectedType + &FoundType + ); + + if (NT_SUCCESS(NtStatus)) { + + PSAMI_PRIVATE_DATA_NEXTRID_TYPE NextRidData; + + // + // Return the domain's NextRid. + // + + Domain = &SampDefinedDomains[ DomainContext->DomainIndex ]; + + *Data = MIDL_user_allocate( sizeof( SAMI_PRIVATE_DATA_NEXTRID_TYPE ) ); + + if ( *Data == NULL ) { + + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + + } else { + + NextRidData = (PSAMI_PRIVATE_DATA_NEXTRID_TYPE)*Data; + NextRidData->NextRid = Domain->CurrentFixed.NextRid; + NextRidData->DataType = SamPrivateDataNextRid; + } + + *DataLength = sizeof( SAMI_PRIVATE_DATA_NEXTRID_TYPE ); + + *SensitiveData = FALSE; + + // + // De-reference the object + // + + IgnoreStatus = SampDeReferenceContext( DomainContext, FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + break; + } + + case SamPrivateDataPassword: { + + PSAMP_OBJECT UserContext; + + // + // Validate type of, and access to object. + // + + UserContext = (PSAMP_OBJECT)SamHandle; + NtStatus = SampLookupContext( + UserContext, + 0L, + SampUserObjectType, //ExpectedType + &FoundType + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampGetPrivateUserData( + UserContext, + DataLength, + Data + ); + + *SensitiveData = TRUE; + + // + // De-reference the object + // + + IgnoreStatus = SampDeReferenceContext( UserContext, FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + break; + } + + default: { + + // + // Since caller is trusted, assume we've got a version mismatch + // or somesuch. + // + + NtStatus = STATUS_NOT_IMPLEMENTED; + + break; + } + } + + // + // Free the read lock + // + + SampReleaseReadLock(); + + return( NtStatus ); +} + + + +NTSTATUS +SampSetPrivateUserData( + PSAMP_OBJECT UserContext, + IN ULONG DataLength, + IN PVOID Data + ) + +/*++ + +Routine Description: + + This service is used to replicate private user type-specific + information. It writes the private data (passwords and password + histories) to the registry. + + +Arguments: + + UserContext - Handle to a User object. + + DataLength - The length of the data being set. + + Data - A pointer to a buffer of length DataLength containing the + private data. + + +Return Value: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_INVALID_PARAMETER_1 - The object type of the provided handle + does not support this operation. + + +--*/ +{ + NTSTATUS NtStatus; + PSAMI_PRIVATE_DATA_PASSWORD_TYPE PasswordData; + BOOLEAN ReplicateImmediately = FALSE; + + SAMTRACE("SampSetPrivateUserData"); + + ASSERT( Data != NULL ); + + if ( ( Data != NULL ) && + ( DataLength >= sizeof(SAMI_PRIVATE_DATA_PASSWORD_TYPE) ) ) { + + PasswordData = (PSAMI_PRIVATE_DATA_PASSWORD_TYPE)Data; + + PasswordData->CaseInsensitiveDbcs.Buffer = (PWSTR) + (&(PasswordData->CaseInsensitiveDbcsBuffer)); + + NtStatus = SampSetUnicodeStringAttribute( + UserContext, + SAMP_USER_DBCS_PWD, + &(PasswordData->CaseInsensitiveDbcs) + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + PasswordData->CaseSensitiveUnicode.Buffer = (PWSTR) + (&(PasswordData->CaseSensitiveUnicodeBuffer)); + + NtStatus = SampSetUnicodeStringAttribute( + UserContext, + SAMP_USER_UNICODE_PWD, + &(PasswordData->CaseSensitiveUnicode) + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + PasswordData->NtPasswordHistory.Buffer = + (PWSTR)(((PCHAR)PasswordData) + + sizeof( SAMI_PRIVATE_DATA_PASSWORD_TYPE ) ); + + NtStatus = SampSetUnicodeStringAttribute( + UserContext, + SAMP_USER_NT_PWD_HISTORY, + &(PasswordData->NtPasswordHistory) + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + PasswordData->LmPasswordHistory.Buffer = + (PWSTR)(((PCHAR)(PasswordData->NtPasswordHistory.Buffer)) + + PasswordData->NtPasswordHistory.Length ); + + NtStatus = SampSetUnicodeStringAttribute( + UserContext, + SAMP_USER_LM_PWD_HISTORY, + &(PasswordData->LmPasswordHistory) + ); + } + } + } + + } else { + + NtStatus = STATUS_INVALID_PARAMETER; + } + + return(NtStatus); +} + + +NTSTATUS +SamISetPrivateData( + IN SAMPR_HANDLE SamHandle, + IN ULONG DataLength, + IN PVOID Data + ) + +/*++ + +Routine Description: + + This service is used to replicate private object type-specific + information. This information must be replicated for each instance + of the object type that is replicated. + + +Arguments: + + SamHandle - Handle to a Domain, User, Group or Alias object. See + SamIGetPrivateInformation() for a list of supported object + types. + + DataLength - The length of the data being set. + + Data - A pointer to a buffer of length DataLength containing the + private data. + + +Return Value: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_INVALID_PARAMETER_1 - The object type of the provided handle + does not support this operation. + + +--*/ +{ + NTSTATUS NtStatus, IgnoreStatus; + SAMP_OBJECT_TYPE FoundType; + BOOLEAN ReplicateImmediately = FALSE; + + SAMTRACE("SamISetPrivateData"); + + ASSERT( Data != NULL ); + + NtStatus = SampAcquireWriteLock(); + + if ( !NT_SUCCESS( NtStatus ) ) { + + return( NtStatus ); + } + + switch ( *((PSAMI_PRIVATE_DATA_TYPE)(Data)) ) { + + case SamPrivateDataNextRid: { + + PSAMP_OBJECT DomainContext; + PSAMP_DEFINED_DOMAINS Domain; + + // + // Validate type of, and access to object. + // + + DomainContext = (PSAMP_OBJECT)SamHandle; + NtStatus = SampLookupContext( + DomainContext, + 0L, + SampDomainObjectType, // ExpectedType + &FoundType + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Set the domain's NextRid. + // + + Domain = &SampDefinedDomains[ DomainContext->DomainIndex ]; + + if ( ( Data != NULL ) && + ( DataLength == sizeof(SAMI_PRIVATE_DATA_NEXTRID_TYPE) ) ) { + + PSAMI_PRIVATE_DATA_NEXTRID_TYPE NextRidData; + + // + // We can trust Data to be a valid pointer; since our + // caller is trusted. + // + + NextRidData = (PSAMI_PRIVATE_DATA_NEXTRID_TYPE)Data; + + // + // We used to set the domain's NextRid here. But we've + // decided that, rather than trying to replicate an exact + // copy of the database, we're going to try to patch any + // problems as we replicate. To ensure that we don't + // create any problems on the way, we want to make sure + // that the NextRid value on a BDC is NEVER decreased. + // Not that it matters; nobody calls this anyway. So the + // Get/SetPrivateData code for domains could be removed. + // + + // Domain->CurrentFixed.NextRid = NextRidData->NextRid; + + } else { + + NtStatus = STATUS_INVALID_PARAMETER; + } + + // + // De-reference the object + // + + if ( NT_SUCCESS( NtStatus ) ) { + + NtStatus = SampDeReferenceContext( + DomainContext, + TRUE + ); + } else { + + IgnoreStatus = SampDeReferenceContext( + DomainContext, + FALSE + ); + } + } + + break; + } + + case SamPrivateDataPassword: { + + PSAMP_OBJECT UserContext; + + // + // Validate type of, and access to object. + // + + UserContext = (PSAMP_OBJECT)SamHandle; + NtStatus = SampLookupContext( + UserContext, + 0L, + SampUserObjectType, // ExpectedType + &FoundType + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + NtStatus = SampSetPrivateUserData( + UserContext, + DataLength, + Data + ); + // + // De-reference the object, adding attribute changes to the + // RXACT if everything went OK. + // + + if ( NT_SUCCESS( NtStatus ) ) { + + NtStatus = SampDeReferenceContext( + UserContext, + TRUE + ); + + } else { + + IgnoreStatus = SampDeReferenceContext( + UserContext, + FALSE + ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + } + + break; + } + + default: { + + // + // We've either got a version mismatch, or the caller passed us + // bad data, or SamIGetPrivateData() never finished getting the + // data into the buffer. + // + + NtStatus = STATUS_INVALID_PARAMETER; + + break; + } + } + + + // + // Release the write lock - commit only if successful. + // + + if ( NT_SUCCESS(NtStatus) ) { + + NtStatus = SampReleaseWriteLock( TRUE ); + + // + // No need to call SampNotifyNetlogonOfDelta, since the replicator + // is the one that made this call. + // + + } else { + + IgnoreStatus = SampReleaseWriteLock( FALSE ); + } + + return(NtStatus); +} + + +NTSTATUS +SamrTestPrivateFunctionsDomain( + IN SAMPR_HANDLE DomainHandle + ) + +/*++ + +Routine Description: + + This service is called to test functions that are normally only + accessible inside the security process. + + +Arguments: + + DomainHandle - Handle to the domain being tested. + +Return Value: + + STATUS_SUCCESS - The tests completed successfully. + + Any errors are as propogated from the tests. + + +--*/ +{ +#if SAM_SERVER_TESTS + + LARGE_INTEGER ModifiedCount1; + LARGE_INTEGER CreationTime1; + PSAMP_DEFINED_DOMAINS Domain; + NTSTATUS NtStatus, TmpStatus; + SAMI_PRIVATE_DATA_TYPE DataType = SamPrivateDataNextRid; + SAMI_PRIVATE_DATA_NEXTRID_TYPE LocalNextRidData; + PSAMI_PRIVATE_DATA_NEXTRID_TYPE NextRidData1 = NULL; + PSAMI_PRIVATE_DATA_NEXTRID_TYPE NextRidData2 = NULL; + PVOID NextRidDataPointer = NULL; + ULONG DataLength = 0; + BOOLEAN SensitiveData = TRUE; + + SAMTRACE("SamrTestPrivateFunctionsDomain"); + + Domain = &SampDefinedDomains[ ((PSAMP_OBJECT)DomainHandle)->DomainIndex ]; + + // + // Test SamIGetSerialNumberDomain(). Just do a GET to make sure we + // don't blow up. + // + + NtStatus = SamIGetSerialNumberDomain( + DomainHandle, + &ModifiedCount1, + &CreationTime1 ); + + // + // Test SamISetSerialNumberDomain(). + // + + if ( NT_SUCCESS( NtStatus ) ) { + + LARGE_INTEGER ModifiedCount2; + LARGE_INTEGER ModifiedCount3; + LARGE_INTEGER CreationTime2; + LARGE_INTEGER CreationTime3; + + // + // Try a simple SET to make sure we don't blow up. + // + + ModifiedCount2.HighPart = 7; + ModifiedCount2.LowPart = 4; + CreationTime2.HighPart = 6; + CreationTime2.LowPart = 9; + + NtStatus = SamISetSerialNumberDomain( + DomainHandle, + &ModifiedCount2, + &CreationTime2, + FALSE ); + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // Now do a GET to see if our SET worked. + // + + NtStatus = SamIGetSerialNumberDomain( + DomainHandle, + &ModifiedCount3, + &CreationTime3 ); + + if ( ( CreationTime2.HighPart != CreationTime3.HighPart ) || + ( CreationTime2.LowPart != CreationTime3.LowPart ) ) { + + NtStatus = STATUS_DATA_ERROR; + } + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // Do another SET to put CreationTime back where it should + // be. ModifiedCount will be 1 too big, so what. + // + + NtStatus = SamISetSerialNumberDomain( + DomainHandle, + &ModifiedCount1, + &CreationTime1, + FALSE ); + } + } + } + + // + // Test SamIGetPrivateData(). + // + + if ( NT_SUCCESS( NtStatus ) ) { + + NtStatus = SamIGetPrivateData( + DomainHandle, + &DataType, + &SensitiveData, + &DataLength, + &NextRidDataPointer ); + + if ( NT_SUCCESS( NtStatus ) ) { + + NextRidData1 = (PSAMI_PRIVATE_DATA_NEXTRID_TYPE)NextRidDataPointer; + + if ( ( DataLength != sizeof( SAMI_PRIVATE_DATA_NEXTRID_TYPE ) ) || + ( SensitiveData != FALSE ) || + ( NextRidData1->DataType != SamPrivateDataNextRid ) || + ( NextRidData1->NextRid != Domain->CurrentFixed.NextRid ) ) { + + NtStatus = STATUS_DATA_ERROR; + } + } + } + +// // +// // Test SamISetPrivateData(). +// // +// // NO, don't test it, since it no longer does anything. We don't +// // ever want NextRid to get set, because we never want it to get +// // smaller. +// +// if ( NT_SUCCESS( NtStatus ) ) { +// +// // +// // First do a random domain set to make sure we don't blow up. +// // +// +// LocalNextRidData.DataType = SamPrivateDataNextRid; +// LocalNextRidData.NextRid = 34567; +// +// NtStatus = SamISetPrivateData( +// DomainHandle, +// sizeof( SAMI_PRIVATE_DATA_NEXTRID_TYPE ), +// &LocalNextRidData +// ); +// +// if ( NT_SUCCESS( NtStatus ) ) { +// +// // +// // Now do a domain get to make sure our set worked. +// // +// +// NtStatus = SamIGetPrivateData( +// DomainHandle, +// &DataType, +// &SensitiveData, +// &DataLength, +// &NextRidDataPointer ); +// +// if ( NT_SUCCESS( NtStatus ) ) { +// +// // +// // Verify the data is as we set it. +// // +// +// NextRidData2 = (PSAMI_PRIVATE_DATA_NEXTRID_TYPE)NextRidDataPointer; +// +// if ( NextRidData2->NextRid != LocalNextRidData.NextRid ) { +// +// NtStatus = STATUS_DATA_ERROR; +// } +// +// // +// // Now do a domain set to restore things to their original state. +// // +// +// TmpStatus = SamISetPrivateData( +// DomainHandle, +// sizeof( SAMI_PRIVATE_DATA_NEXTRID_TYPE ), +// NextRidData1 +// ); +// +// if ( NT_SUCCESS( NtStatus ) ) { +// +// NtStatus = TmpStatus; +// } +// } +// } +// +// if ( NextRidData1 != NULL ) { +// +// MIDL_user_free( NextRidData1 ); +// } +// +// if ( NextRidData2 != NULL ) { +// +// MIDL_user_free( NextRidData2 ); +// } +// } + + // + // Test SamICreateAccountByRid(). + // + + if ( NT_SUCCESS( NtStatus ) ) { + + RPC_UNICODE_STRING AccountNameU; + RPC_UNICODE_STRING AccountName2U; + SAMPR_HANDLE UserAccountHandle; + SAMPR_HANDLE BadAccountHandle; + SAMPR_HANDLE GroupAccountHandle; + NTSTATUS TmpStatus; + ULONG RelativeId = 1111; + ULONG ConflictingAccountRid; + BOOLEAN AllTestsCompleted = FALSE; + + // + // Create a unique account - a user with a known name and RID. + // + + RtlInitUnicodeString( &AccountNameU, L"USER1SRV" ); + RtlInitUnicodeString( &AccountName2U, L"USER2SRV" ); + + NtStatus = SamICreateAccountByRid( + DomainHandle, + SamObjectUser, + RelativeId, + &AccountNameU, + USER_ALL_ACCESS, + &UserAccountHandle, + &ConflictingAccountRid ); + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // User is open. Close it, and make the same call as above to + // make sure that the user gets opened. We'll need it open + // later to delete it anyway. + // + + TmpStatus = SamrCloseHandle( &UserAccountHandle ); + ASSERT( NT_SUCCESS( TmpStatus ) ); + + NtStatus = SamICreateAccountByRid( + DomainHandle, + SamObjectUser, + RelativeId, + &AccountName, + USER_ALL_ACCESS, + &UserAccountHandle, + &ConflictingAccountRid ); + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // Make same call as above, but with a different RID. + // Should get an error because of the name collision. + // + + NtStatus = SamICreateAccountByRid( + DomainHandle, + SamObjectUser, + RelativeId + 1, + &AccountName, + 0L, + &BadAccountHandle, + &ConflictingAccountRid ); + + if ( NtStatus == STATUS_USER_EXISTS ) { + + // + // Make same call as above, but with a different name. Should + // get an error because of the RID collision. + // + + NtStatus = SamICreateAccountByRid( + DomainHandle, + SamObjectUser, + RelativeId, + &AccountName2, + 0L, + &BadAccountHandle, + &ConflictingAccountRid ); + + if ( NtStatus == STATUS_USER_EXISTS ) { + + // + // Create a different type - a group - with the + // user's RID. Should get an error because of + // the RID collision. + // + + NtStatus = SamICreateAccountByRid( + DomainHandle, + SamObjectGroup, + RelativeId, + &AccountName, + 0L, + &BadAccountHandle, + &ConflictingAccountRid ); + + if ( NtStatus == STATUS_USER_EXISTS ) { + + // + // Try a different type - a group - with a + // different name, but still the same RID. + // This should still fail due to the RID + // collision. + // + + NtStatus = SamICreateAccountByRid( + DomainHandle, + SamObjectGroup, + RelativeId, + &AccountName2, + 0L, + &BadAccountHandle, + &ConflictingAccountRid ); + + if ( NtStatus == STATUS_USER_EXISTS ) { + + // + // Create a group with the user's name, but + // a different RID. This should fail + // because of the name collision. + // + + NtStatus = SamICreateAccountByRid( + DomainHandle, + SamObjectGroup, + RelativeId + 1, + &AccountName, + GROUP_ALL_ACCESS, + &GroupAccountHandle, + &ConflictingAccountRid ); + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // Ack! This shouldn't have happened. + // Close and delete the group we just created. + // + + TmpStatus = SamrDeleteGroup( &GroupAccountHandle ); + ASSERT( NT_SUCCESS( TmpStatus ) ); + NtStatus = STATUS_UNSUCCESSFUL; + + } else { + + if ( NtStatus == STATUS_USER_EXISTS ) { + + NtStatus = STATUS_SUCCESS; + AllTestsCompleted = TRUE; + } + } + } + } + } + } + } + + // + // Now delete the user. + // + + TmpStatus = SamrDeleteUser( &UserAccountHandle ); + ASSERT( NT_SUCCESS( TmpStatus ) ); + } + + if ( ( !AllTestsCompleted ) && ( NtStatus == STATUS_SUCCESS ) ) { + + // + // STATUS_SUCCESS means everything succeeded (it was set after + // the last one succeeded) or a test that was supposed to fail + // didn't. If the former, set an error. + // + + NtStatus = STATUS_UNSUCCESSFUL; + } + } + + return( NtStatus ); + +#else + + return( STATUS_NOT_IMPLEMENTED ); + +#endif // SAM_SERVER_TESTS + +} + + + +NTSTATUS +SamrTestPrivateFunctionsUser( + IN SAMPR_HANDLE UserHandle + ) + +/*++ + +Routine Description: + + This service is called to test functions that are normally only + accessible inside the security process. + + +Arguments: + + UserHandle - Handle to the user being tested. + +Return Value: + + STATUS_SUCCESS - The tests completed successfully. + + Any errors are as propogated from the tests. + + +--*/ +{ + +#if SAM_SERVER_TESTS + + UNICODE_STRING WorkstationsU, LogonWorkstationU; + LOGON_HOURS LogonHours; + PVOID LogonHoursPointer, WorkstationsPointer; + LARGE_INTEGER LogoffTime, KickoffTime; + NTSTATUS NtStatus, TmpStatus; + SAMI_PRIVATE_DATA_TYPE DataType = SamPrivateDataPassword; + PVOID PasswordDataPointer = NULL; + PCHAR BufferPointer; + ULONG OriginalDataLength = 0; + ULONG DataLength = 0; + USHORT i; + BOOLEAN SensitiveData = FALSE; + SAMI_PRIVATE_DATA_PASSWORD_TYPE LocalPasswordData; + PSAMI_PRIVATE_DATA_PASSWORD_TYPE PasswordData1; + PSAMI_PRIVATE_DATA_PASSWORD_TYPE PasswordData2; + PUSER_ALL_INFORMATION All = NULL; + PUSER_ALL_INFORMATION All2 = NULL; + + SAMTRACE("SamrTestPrivateFunctionsUser"); + + // -------------------------------------------------------------- + // Test Query and SetInformationUser for UserAllInformation level + // + // The handle is passed to us from user space. Make it look like + // a trusted handle so we can test the trusted stuff. + // + + ((PSAMP_OBJECT)(UserHandle))->TrustedClient = TRUE; + + NtStatus = SamrQueryInformationUser( + UserHandle, + UserAllInformation, + (PSAMPR_USER_INFO_BUFFER *)&All + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // Now change some of the data, and set it + // + + RtlInitUnicodeString( (PUNICODE_STRING)(&All->FullName), L"FullName ); + + RtlInitUnicodeString( (PUNICODE_STRING)(&All->HomeDirectory), L"HomeDirectory" ); + + RtlInitUnicodeString( + (PUNICODE_STRING)(&All->HomeDirectoryDrive), + L"HomeDirectoryDrive" + ); + + RtlInitUnicodeString( + (PUNICODE_STRING)(&All->ScriptPath), + L"ScriptPath" + ); + + RtlInitUnicodeString( + (PUNICODE_STRING)(&All->ProfilePath), + L"ProfilePath" + ); + + RtlInitUnicodeString( + (PUNICODE_STRING)(&All->AdminComment), + L"AdminComment" + ); + + RtlInitUnicodeString( + (PUNICODE_STRING)(&All->WorkStations), + L"WorkStations" + ); + + RtlInitUnicodeString( + (PUNICODE_STRING)(&All->UserComment), + L"UserComment" + ); + + RtlInitUnicodeString( + (PUNICODE_STRING)(&All->Parameters), + L"Parameters" + ); + + RtlInitUnicodeString( + (PUNICODE_STRING)(&All->NtPassword), + L"12345678" + ); + + RtlInitUnicodeString( + (PUNICODE_STRING)(&All->LmPassword), + L"87654321" + ); + + All->BadPasswordCount = 5; + All->LogonCount = 6; + All->CountryCode = 7; + All->CodePage = 8; + + All->PasswordExpired = TRUE; + All->NtPasswordPresent = TRUE; + All->LmPasswordPresent = TRUE; + + All->LogonHours.UnitsPerWeek = 7; + + All->WhichFields = + USER_ALL_FULLNAME | + USER_ALL_HOMEDIRECTORY | + USER_ALL_HOMEDIRECTORYDRIVE | + USER_ALL_SCRIPTPATH | + USER_ALL_PROFILEPATH | + USER_ALL_ADMINCOMMENT | + USER_ALL_WORKSTATIONS | + USER_ALL_USERCOMMENT | + USER_ALL_PARAMETERS | + USER_ALL_BADPASSWORDCOUNT | + USER_ALL_LOGONCOUNT | + USER_ALL_COUNTRYCODE | + USER_ALL_CODEPAGE | + USER_ALL_PASSWORDEXPIRED | + USER_ALL_LMPASSWORDPRESENT | + USER_ALL_NTPASSWORDPRESENT | + USER_ALL_LOGONHOURS; + + NtStatus = SamrSetInformationUser( + UserHandle, + UserAllInformation, + (PSAMPR_USER_INFO_BUFFER)All + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + NtStatus = SamrQueryInformationUser( + UserHandle, + UserAllInformation, + (PSAMPR_USER_INFO_BUFFER *)&All2 + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // Verify that queried info is as we set it + // + + if ( + + // + // Fields that we didn't touch. Note that private + // data and PasswordMustChange will change anyway + // due to password changes. + // + + ( All2->WhichFields != (USER_ALL_READ_GENERAL_MASK | + USER_ALL_READ_LOGON_MASK | + USER_ALL_READ_ACCOUNT_MASK | + USER_ALL_READ_PREFERENCES_MASK | + USER_ALL_READ_TRUSTED_MASK) ) || + ( !(All->LastLogon.QuadPart == All2->LastLogon.QuadPart) ) || + ( !(All->LastLogoff.QuadPart == All2->LastLogoff.QuadPart) ) || + ( !(All->PasswordLastSet.QuadPart == All2->PasswordLastSet.QuadPart) ) || + ( !(All->AccountExpires.QuadPart == All2->AccountExpires.QuadPart) ) || + ( !(All->PasswordCanChange.QuadPart == All2->PasswordCanChange.QuadPart) ) || + ( (All->PasswordMustChange.QuadPart == All2->PasswordMustChange.QuadPart) ) || + (RtlCompareUnicodeString( + &(All->UserName), + &(All2->UserName), + FALSE) != 0) || + (RtlCompareUnicodeString( + &(All->PrivateData), + &(All2->PrivateData), + FALSE) == 0) || + ( All->SecurityDescriptor.Length != + All2->SecurityDescriptor.Length ) || + ( All->UserId != All2->UserId ) || + ( All->PrimaryGroupId != All2->PrimaryGroupId ) || + ( All->UserAccountControl != All2->UserAccountControl ) || + ( All->PrivateDataSensitive != + All2->PrivateDataSensitive ) || + + // Fields that we changed + + (RtlCompareUnicodeString( + &(All->FullName), + &(All2->FullName), + FALSE) != 0) || + (RtlCompareUnicodeString( + &(All->HomeDirectory), + &(All2->HomeDirectory), + FALSE) != 0) || + (RtlCompareUnicodeString( + &(All->HomeDirectoryDrive), + &(All2->HomeDirectoryDrive), + FALSE) != 0) || + (RtlCompareUnicodeString( + &(All->ScriptPath), + &(All2->ScriptPath), + FALSE) != 0) || + (RtlCompareUnicodeString( + &(All->ProfilePath), + &(All2->ProfilePath), + FALSE) != 0) || + (RtlCompareUnicodeString( + &(All->AdminComment), + &(All2->AdminComment), + FALSE) != 0) || + (RtlCompareUnicodeString( + &(All->WorkStations), + &(All2->WorkStations), + FALSE) != 0) || + (RtlCompareUnicodeString( + &(All->UserComment), + &(All2->UserComment), + FALSE) != 0) || + (RtlCompareUnicodeString( + &(All->Parameters), + &(All2->Parameters), + FALSE) != 0) || + ( All->BadPasswordCount != All2->BadPasswordCount ) || + ( All->LogonCount != All2->LogonCount ) || + ( All->CountryCode != All2->CountryCode ) || + ( All->CodePage != All2->CodePage ) || + ( All->PasswordExpired != All2->PasswordExpired ) || + ( All->LmPasswordPresent != All2->LmPasswordPresent ) || + ( All->NtPasswordPresent != All2->NtPasswordPresent ) || + ( All->LogonHours.UnitsPerWeek != + All2->LogonHours.UnitsPerWeek ) + ) { + + NtStatus = STATUS_DATA_ERROR; + } + + MIDL_user_free( All2 ); + } + } + + MIDL_user_free( All ); + } + + if ( !NT_SUCCESS( NtStatus ) ) { + + return( NtStatus ); + } + + // -------------------------------------------------------------- + // Test SamIAccountRestrictions + // NOTE: We really should have more tests for this + // + + RtlInitUnicodeString( &WorkstationsU, L"machine1,CHADS2 chads1" ); + + NtStatus = SamrSetInformationUser( + UserHandle, + UserWorkStationsInformation, + (PSAMPR_USER_INFO_BUFFER) &WorkstationsU + ); + ASSERT( NT_SUCCESS( NtStatus ) ) ; + + LogonHours.UnitsPerWeek = 168; + LogonHours.LogonHours = MIDL_user_allocate( 21 ); + ASSERT( LogonHours.LogonHours != NULL ); + + for ( i = 0; i < 21; i++ ) { + + LogonHours.LogonHours[i] = 0xa1; + } + + NtStatus = SamrSetInformationUser( + UserHandle, + UserLogonHoursInformation, + (PSAMPR_USER_INFO_BUFFER)&LogonHours + ); + ASSERT( NT_SUCCESS( NtStatus ) ) ; + + LogonHoursPointer = NULL; + + NtStatus = SamrQueryInformationUser( + UserHandle, + UserLogonHoursInformation, + (PSAMPR_USER_INFO_BUFFER *)&LogonHoursPointer + ); + ASSERT( NT_SUCCESS( NtStatus ) ) ; + + WorkstationsPointer = NULL; + + NtStatus = SamrQueryInformationUser( + UserHandle, + UserWorkStationsInformation, + (PSAMPR_USER_INFO_BUFFER *)&WorkstationsPointer + ); + ASSERT( NT_SUCCESS( NtStatus ) ) ; + + RtlInitUnicodeString( &WorkstationsU, L"ChadS2" ); + + NtStatus = SamIAccountRestrictions( + UserHandle, + &LogonWorkstation, + WorkstationsPointer, + LogonHoursPointer, + &LogoffTime, + &KickoffTime + ); + + if ( NtStatus == STATUS_INVALID_LOGON_HOURS ) { + + // + // We hate to use 0xff all the time as a test value, but using + // 0xA1 as a test value means that this test may fail depending + // on when it runs. So only IF we get this error, will we try + // again with 0xff as the logon hours. + // + + LogonHours.UnitsPerWeek = 168; + + for ( i = 0; i < 21; i++ ) { + + LogonHours.LogonHours[i] = 0xff; + } + + NtStatus = SamrSetInformationUser( + UserHandle, + UserLogonHoursInformation, + (PSAMPR_USER_INFO_BUFFER)&LogonHours + ); + ASSERT( NT_SUCCESS( NtStatus ) ) ; + + MIDL_user_free( LogonHoursPointer ); + LogonHoursPointer = NULL; + + NtStatus = SamrQueryInformationUser( + UserHandle, + UserLogonHoursInformation, + (PSAMPR_USER_INFO_BUFFER *)&LogonHoursPointer + ); + ASSERT( NT_SUCCESS( NtStatus ) ) ; + + NtStatus = SamIAccountRestrictions( + UserHandle, + &LogonWorkstationU, + WorkstationsPointer, + LogonHoursPointer, + &LogoffTime, + &KickoffTime + ); + } + + MIDL_user_free( LogonHours.LogonHours ); + + MIDL_user_free( LogonHoursPointer ); + MIDL_user_free( WorkstationsPointer ); + + if ( !NT_SUCCESS( NtStatus ) ) { + + return( NtStatus ); + } + + // -------------------------------------------------------------- + // Test SamIGetPrivateData + // + + NtStatus = SamIGetPrivateData( + UserHandle, + &DataType, + &SensitiveData, + &OriginalDataLength, + &PasswordDataPointer ); + + if ( NT_SUCCESS( NtStatus ) ) { + + PasswordData1 = (PSAMI_PRIVATE_DATA_PASSWORD_TYPE)PasswordDataPointer; + + if ( ( !( OriginalDataLength >= sizeof( SAMI_PRIVATE_DATA_PASSWORD_TYPE ) ) ) || + ( SensitiveData != TRUE ) || + ( PasswordData1->DataType != SamPrivateDataPassword ) ) { + + NtStatus = STATUS_DATA_ERROR; + } + } + + // -------------------------------------------------------------- + // Now test SamISetPrivateData() for user objects. + // + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // First do a random user set to make sure we don't blow up. + // + + LocalPasswordData.DataType = SamPrivateDataPassword; + + LocalPasswordData.CaseInsensitiveDbcs.Length = ENCRYPTED_LM_OWF_PASSWORD_LENGTH; + LocalPasswordData.CaseInsensitiveDbcs.MaximumLength = ENCRYPTED_LM_OWF_PASSWORD_LENGTH; + LocalPasswordData.CaseInsensitiveDbcs.Buffer = (PWSTR)&(LocalPasswordData.CaseInsensitiveDbcsBuffer); + + BufferPointer = (PCHAR)&(LocalPasswordData.CaseInsensitiveDbcsBuffer); + + for ( i = 0; i < ENCRYPTED_LM_OWF_PASSWORD_LENGTH; i++ ) { + + *BufferPointer++ = (CHAR)(i + 12); + } + + LocalPasswordData.CaseSensitiveUnicode.Length = ENCRYPTED_NT_OWF_PASSWORD_LENGTH; + LocalPasswordData.CaseSensitiveUnicode.MaximumLength = ENCRYPTED_NT_OWF_PASSWORD_LENGTH; + LocalPasswordData.CaseSensitiveUnicode.Buffer = (PWSTR)&(LocalPasswordData.CaseSensitiveUnicodeBuffer); + + BufferPointer = (PCHAR)(&LocalPasswordData.CaseSensitiveUnicodeBuffer); + + for ( i = 0; i < ENCRYPTED_NT_OWF_PASSWORD_LENGTH; i++ ) { + + *BufferPointer++ = (CHAR)(i + 47); + } + + LocalPasswordData.LmPasswordHistory.Length = 0; + LocalPasswordData.LmPasswordHistory.MaximumLength = 0; + LocalPasswordData.LmPasswordHistory.Buffer = (PWSTR) + ( &LocalPasswordData + sizeof( SAMI_PRIVATE_DATA_PASSWORD_TYPE ) ); + + LocalPasswordData.NtPasswordHistory.Length = 0; + LocalPasswordData.NtPasswordHistory.MaximumLength = 0; + LocalPasswordData.NtPasswordHistory.Buffer = (PWSTR) + ( &LocalPasswordData + sizeof( SAMI_PRIVATE_DATA_PASSWORD_TYPE ) ); + + NtStatus = SamISetPrivateData( + UserHandle, + sizeof( LocalPasswordData ), + &LocalPasswordData + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // Now do a user get to make sure our set worked. + // + + NtStatus = SamIGetPrivateData( + UserHandle, + &DataType, + &SensitiveData, + &DataLength, + &PasswordDataPointer ); + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // Verify the data is as we set it. + // + + PasswordData2 = (PSAMI_PRIVATE_DATA_PASSWORD_TYPE)PasswordDataPointer; + + if ( ( PasswordData2->DataType != LocalPasswordData.DataType ) || + + ( PasswordData2->CaseInsensitiveDbcs.Length != LocalPasswordData.CaseInsensitiveDbcs.Length ) || + + ( PasswordData2->CaseSensitiveUnicode.Length != LocalPasswordData.CaseSensitiveUnicode.Length ) || + + ( PasswordData2->LmPasswordHistory.Length != LocalPasswordData.LmPasswordHistory.Length ) || + + ( PasswordData2->NtPasswordHistory.Length != LocalPasswordData.NtPasswordHistory.Length ) || + + ( RtlCompareMemory( + &LocalPasswordData.CaseInsensitiveDbcsBuffer, + &(PasswordData2->CaseInsensitiveDbcsBuffer), + ENCRYPTED_LM_OWF_PASSWORD_LENGTH) != ENCRYPTED_LM_OWF_PASSWORD_LENGTH ) || + + ( RtlCompareMemory( + &LocalPasswordData.CaseSensitiveUnicodeBuffer, + &(PasswordData2->CaseSensitiveUnicodeBuffer), + ENCRYPTED_NT_OWF_PASSWORD_LENGTH) != ENCRYPTED_NT_OWF_PASSWORD_LENGTH ) + + ) { + + NtStatus = STATUS_DATA_ERROR; + } + + // + // Now do a user set to restore things to their original state. + // + + TmpStatus = SamISetPrivateData( + UserHandle, + OriginalDataLength, + PasswordData1 + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + NtStatus = TmpStatus; + } + } + } + + if ( PasswordData1 != NULL ) { + + MIDL_user_free( PasswordData1 ); + } + + if ( PasswordData2 != NULL ) { + + MIDL_user_free( PasswordData2 ); + } + } + + return( NtStatus ); + +#else + + return( STATUS_NOT_IMPLEMENTED ); + +#endif // SAM_SERVER_TESTS + +} + + + +NTSTATUS +SampBuildDomainKeyName( + OUT PUNICODE_STRING DomainKeyName, + IN PUNICODE_STRING DomainName OPTIONAL + ) + +/*++ + +Routine Description: + + This routine builds the name of a domain registry key. + The name produced is relative to the SAM root and will be the name of + key whose name is the name of the domain. + + The name built up is comprized of the following components: + + 1) The constant named domain parent key name ("DOMAINS"). + + 2) A backslash + + 3) The name of the domain. + + + For example, given a DomainName of "ABC_DOMAIN" this would + yield a resultant DomainKeyName of "DOMAINS\ABC_DOMAIN" + + + + All allocation for this string will be done using MIDL_user_allocate. + Any deallocations will be done using MIDL_user_free. + + + +Arguments: + + DomainKeyName - The address of a unicode string whose buffer is to be + filled in with the full name of the registry key. If successfully + created, this string must be released with SampFreeUnicodeString() + when no longer needed. + + + DomainName - The name of the domain. This string is not modified. + + +Return Value: + + STATUS_SUCCESS - DomainKeyName points at the full key name. + +--*/ +{ + NTSTATUS NtStatus; + USHORT TotalLength, DomainNameLength; + + SAMTRACE("SampBuildDomainKeyName"); + + // + // Allocate a buffer large enough to hold the entire name. + // Only count the domain name if it is passed. + // + + DomainNameLength = 0; + if (ARGUMENT_PRESENT(DomainName)) { + DomainNameLength = DomainName->Length + SampBackSlash.Length; + } + + TotalLength = SampNameDomains.Length + + DomainNameLength + + (USHORT)(sizeof(UNICODE_NULL)); // for null terminator + + NtStatus = SampInitUnicodeString( DomainKeyName, TotalLength ); + if (NT_SUCCESS(NtStatus)) { + + // + // "DOMAINS" + // + + NtStatus = SampAppendUnicodeString( DomainKeyName, &SampNameDomains); + if (NT_SUCCESS(NtStatus)) { + + if (ARGUMENT_PRESENT(DomainName)) { + + // + // "DOMAINS\" + // + + NtStatus = SampAppendUnicodeString( DomainKeyName, &SampBackSlash ); + if (NT_SUCCESS(NtStatus)) { + + // + // "DOMAINS\(domain name)" + // + + NtStatus = SampAppendUnicodeString( + DomainKeyName, + DomainName + ); + } + } + } + } + + + // + // Clean-up on failure + // + + if (!NT_SUCCESS(NtStatus)) { + SampFreeUnicodeString( DomainKeyName ); + } + + return(NtStatus); + +} diff --git a/private/newsam2/server/dslayer.c b/private/newsam2/server/dslayer.c new file mode 100644 index 000000000..811a0c270 --- /dev/null +++ b/private/newsam2/server/dslayer.c @@ -0,0 +1,2188 @@ +/*++ + +Copyright (C) 1996 Microsoft Corporation + +Module Name: + + dslayer.c + +Abstract: + + Contains SAM Private API Routines to access the DS + These provide a simplified API, and hide most of the + underlying complexity to set up the parameters to a DS call + and parse the resulting result. + +Author: + MURLIS + +Revision History + + 5-14-96 Murlis Created + +--*/ + + +#include <samsrvp.h> +#include <duapi.h> +#include <dslayer.h> +#include <mappings.h> +#include <objids.h> +#include <filtypes.h> +#include <fileno.h> + +// +// Define FILENO for SAM +// + + +#define FILENO FILENO_SAM + +//++ +//++ +//++ IMPORTANT NOTE REGARDING SID's and RID's +//++ +//++ The DS can choose to either store the entire SID's or only +//++ the Rid's for account objects. In case Entire SID's are stored +//++ the DS layer handles the Mapping between the attribute type and +//++ and value of SID and those of Rid for account objects. This is +//++ done within SampDsToSamAttrBlock and SampSamToDsAttrBlock. +//++ +//++ +//++ Irrespective of which way we go the Rid and Sid are both +//++ attributes defined in the schema. +//++ +//++ If we go the way the of storing Sid's then the DS functions +//++ should call the Convert AttrBlock functions using the MAP_SID_RID +//++ conversion Flag, and Lookup Object By Rid, should actually use +//++ the Sid Attribute. +//++ +//++ + + + + +// +// Forward declarations of Private Samp Routines used in this file only +// + + +PVOID +DSAlloc( + IN ULONG Length + ); + +NTSTATUS +SampMapDsErrorToNTStatus( + ULONG RetValue + ); + + +void +BuildStdCommArg( + IN OUT COMMARG * pCommArg + ); + + + +NTSTATUS +SampDsSetNewSidAttribute( + IN PSID DomainSid, + IN ULONG ConversionFlags, + IN ATTR *RidAttr, + IN OUT ATTR *SidAttr + ); + +NTSTATUS +SampDsCopyAttributeValue( + IN ATTR * Src, + IN OUT ATTR * Dst + ); + + + +VOID +BuildStdCommArg( + IN OUT COMMARG * pCommArg + ) +/*++ + + Routine Description: + + Fills a COMMARG structue with the standard set of options + + Arguments: + pCommArg - Pointer to the COMMARG structure + + Return Values: + None + +--*/ +{ + SVCCNTL * pSvcCntl; + + SAMTRACE("BuildStdCommonArg"); + + // Get the address of the service control structure + pSvcCntl = &(pCommArg->Svccntl); + + + // Setup the service control structure + pSvcCntl->preferChaining = FALSE; + pSvcCntl->chainingProhibited = TRUE; + pSvcCntl->localScope = TRUE; + pSvcCntl->dontUseCopy = FALSE; + pSvcCntl->dontDerefAlias = TRUE; + pSvcCntl->makeDeletionsAvail = FALSE; + pSvcCntl->fUnicodeSupport = TRUE; + + // Setup the CommArgs part of the ReadArg Strucure + pCommArg->Opstate.nameRes = OP_NAMERES_NOT_STARTED; + pCommArg->aliasRDN = 0; + pCommArg->pReserved = NULL; + pCommArg->PagedResult.fPresent = FALSE; + pCommArg->PagedResult.pRestart = NULL; + pCommArg->ulSizeLimit = SAMP_DS_SIZE_LIMIT; + pCommArg->SortAttr = 0; + pCommArg->Delta = 0; + pCommArg->fForwardSeek = TRUE; +} + + + +NTSTATUS +SampDsInitialize() + +/*++ + +Routine Description: + + Initializes the DS system + starts up DS. + +Arguments: + None + +Return Values: + Any values from DsInitialize +--*/ +{ + NTSTATUS Status; + + SAMTRACE("SampDsInitialize"); + + // Start up the DS + Status = DsInitialize(); + + return Status; +} + + +NTSTATUS +SampDsUninitialize() + +/*++ + +Routine Description + + Initiates a clean shut down of the DS + +Arguments: + None +Return codes: + Any returned by DSUninitialize + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + + SAMTRACE("SampDsUninitialize"); + + Status = DsUninitialize(); + return Status; +} + +NTSTATUS +SampDsRead( + IN DSNAME * Object, + IN ULONG Flags, + IN SAMP_OBJECT_TYPE ObjectType, + IN ATTRBLOCK * AttributesToRead, + OUT ATTRBLOCK * AttributeValues +) + +/*++ + +Routine Description: + + Read attributes of an object from the DS + +Argumants: + Object -- Pointer to Dist Name, which sepcifies object to read + Flags -- To control operation of routine + ObjectType -- Specifies the type of the object + AttributesToRead -- Specfies the attributes to read + AttributeValues -- Returned value of the attributes + + Return Values: + + STATUS_SUCCESS on successful completion + DS return codes mapped to NT_STATUS as in SampMapDSErrorToNTStatus + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + ENTINFSEL EntInf; + READARG ReadArg; + COMMARG *pCommArg; + ULONG RetValue; + READRES * pReadRes; + ATTRBLOCK *AttrBlockForDs, * ConvertedAttrBlock; + + SAMTRACE("SampDsRead"); + + // + // Asserts and parameter validation + // + + ASSERT(Object!=NULL); + ASSERT(AttributesToRead!=NULL); + ASSERT(AttributeValues != NULL); + ASSERT(AttributesToRead->attrCount > 0); + + + // Perform lazy thread and transaction initialization. + Status = SampMaybeBeginDsTransaction(SampWriteLock); + + if (Status!= STATUS_SUCCESS) + goto Error; + + // + // Translate the attribute types in Attrblock to map between + // SAM and DS attributes + // + + Status = SampSamToDsAttrBlock( + ObjectType, // Object Type + AttributesToRead, // Attributes To Convert + ( MAP_RID_TO_SID + | MAP_ATTRIBUTE_TYPES + | REALLOC_IN_DSMEMORY ), // Conversion Flags + NULL, // Domain Sid + &(EntInf.AttrTypBlock) + ); + + if (!NT_SUCCESS(Status)) + { + goto Error; + } + + // + // Setup up the ENTINFSEL structure + // + + EntInf.attSel = EN_ATTSET_LIST; + EntInf.infoTypes = EN_INFOTYPES_TYPES_VALS; + + // + // Build the commarg structure + // + + pCommArg = &(ReadArg.CommArg); + BuildStdCommArg(pCommArg); + + // + // Setup the Read Arg Structure + // + + ReadArg.pObject = Object; + ReadArg.pSel = & EntInf; + + // + // Make the DS call + // + + RetValue = DirRead(& ReadArg, & pReadRes); + + // + // Map the RetValue to a NT Status code + // + + Status = SampMapDsErrorToNTStatus(RetValue); + + if (Status!= STATUS_SUCCESS) + goto Error; + + // + // Translate attribute types back from DS to SAM + // + + Status = SampDsToSamAttrBlock( + ObjectType, + &(pReadRes->entry.AttrBlock), + ( MAP_ATTRIBUTE_TYPES | MAP_SID_TO_RID ), + AttributeValues + ); + + if (Status != STATUS_SUCCESS) + goto Error; + + + + + +Error: + + return Status; +} + + + +NTSTATUS +SampDsSetAttributes( + IN DSNAME * Object, + IN ULONG Operation, + IN SAMP_OBJECT_TYPE ObjectType, + IN ATTRBLOCK * AttributeList +) + +/*++ + +Routine Description: + + Set an Object's attributes + +Arguments: + + Object Specifies the DS Object + Operation Controls operation of routine + ObjectType SAM Object Type for attribute Type conversion + AttributeList Specifies the attributes to Modify + +Return Values: + STATUS_SUCCESS on succesful completion + STATUS_NO_MEMORY - if failed to allocate memory + DS return codes mapped to NT_STATUS as in SampMapDSErrorToNTStatus + + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + ATTRMODLIST * AttrModList = NULL; + MODIFYARG ModifyArg; + ATTRMODLIST * CurrentMod, * NextMod, *LastMod; + ULONG Index; + COMMARG *pCommArg; + ULONG RetValue; + UCHAR Choice; + ULONG ModCount = 0; + + + SAMTRACE("SampDsSetAttributes"); + + // + // Asserts and parameter validation + // + + ASSERT(Object!=NULL); + ASSERT(AttributeList != NULL); + ASSERT(AttributeList->attrCount > 0); + + // Perform lazy thread and transaction initialization. + Status = SampMaybeBeginDsTransaction(SampWriteLock); + + if (Status!= STATUS_SUCCESS) + goto Error; + + // + // Setup our Choice + // + + if (Operation == ADD_VALUE) + Choice = AT_CHOICE_ADD_VALUES; + + else if (Operation == REMOVE_VALUE) + Choice = AT_CHOICE_REMOVE_VALUES; + + else if (Operation == REMOVE_ATT) + Choice = AT_CHOICE_REMOVE_ATT; + + else if (Operation == REPLACE_ATT) + Choice = AT_CHOICE_REPLACE_ATT; + + else + Choice = AT_CHOICE_REPLACE_ATT; + + + // + // Allocate enough memory in AttrModList to hold the contents of + // AttrBlock. First such structure is specified in ModifyArg itself + // + + AttrModList = (ATTRMODLIST *) DSAlloc( + (AttributeList->attrCount -1) + * sizeof(ATTRMODLIST) + ); + if (AttrModList==NULL) + { + Status = STATUS_NO_MEMORY; + goto Error; + + } + + // + // Initialize the Linked Attribute Modification List + // required for the DS call + // + + CurrentMod = &(ModifyArg.FirstMod); + NextMod = AttrModList; + LastMod = NULL; + + for (Index = 0; Index < AttributeList->attrCount; Index++) + { + ULONG DsAttrTyp; + + // + // MAP the attribute Type from DS to SAM + // + DsAttrTyp = SampDsAttrFromSamAttr( + ObjectType, + AttributeList->pAttr[Index].attrTyp + ); + + // + // Skip over any Rid Attribute + // + if (DsAttrTyp == SampDsAttrFromSamAttr( + SampUnknownObjectType, + SAMP_UNKNOWN_OBJECTRID + )) + { + + // + // We will not allow modifications of Rid's + // + + continue; + } + + + // + // Setup the Choice + // + + CurrentMod->choice = Choice; + + // + // Copy over the ATTR Type + // + CurrentMod->AttrInf.attrTyp = DsAttrTyp; + + // + // Copy Over the Attribute Value + // + + Status = SampDsCopyAttributeValue( + &(AttributeList->pAttr[Index]), + &(CurrentMod->AttrInf) + ); + + if (Status != STATUS_SUCCESS) + goto Error; + + // + // Setup the chaining. AttrModList is suposed to be a linked list, though + // for effciency purposes we allocated a single block + // + + LastMod = CurrentMod; + CurrentMod->pNextMod = NextMod; + CurrentMod = CurrentMod->pNextMod; + NextMod = NextMod +1 ; + + // + // Keep track of Count of Modifications we pass to DS, as we skip over RId etc + // + ModCount++; + + } + + // + // Initialize the last pointer in the chain to NULL + // + + if (LastMod) + LastMod->pNextMod = NULL; + else + + { + // + // This Means we have nothing to modify + // + + Status = STATUS_SUCCESS; + goto Error; + } + + + + // + // Setup the Common Args structure + // + + pCommArg = &(ModifyArg.CommArg); + BuildStdCommArg(pCommArg); + + // + // Setup the MODIFY ARG structure + // + + ModifyArg.pObject = Object; + ModifyArg.count = (USHORT) ModCount; + + // + // Make the DS call + // + + RetValue = DirModifyEntry(&ModifyArg); + + // + // Map the return code to an NT status + // + + Status = SampMapDsErrorToNTStatus(RetValue); + + +Error: + + + + return Status; +} + + + +NTSTATUS +SampDsCreateObject( + IN DSNAME *Object, + SAMP_OBJECT_TYPE ObjectType, + IN ATTRBLOCK *AttributesToSet, + IN OPTIONAL PSID DomainSid + ) +/*++ + + Routine Description: + + Create Object in the DS + + Arguments: + + + Object -- DSNAME of the object to be created + + ObjectType -- one of + + SampServerObjectType + SampDomainObjectType + SampGroupObjectType + SampUserObjectType + SampAliasObjectType + + + AttributesToSet -- Allows the caller to pass in an + attribute block to to Set at Object creation time + itself. Useful as this allows one to save a JET + write. Also the attributes are set in the same + transaction as the write. + NULL can be passed in if caller does + not want any attribute to be set + + DomainSid -- Optional Parameter, used in creating the Full + SID for the account, from the specified Rid + + + Return values: + + STATUS_SUCCESS on successful completion + DS return codes mapped to NT_STATUS as in SampMapDSErrorToNTStatus + + + BUG: Need to do something about DS requiring Allocs from the Thread + heap. We anyway alloc memory in the bunch of cases. Alloc this using the + thread heap. + +--*/ +{ + + + NTSTATUS Status = STATUS_SUCCESS; + ULONG RetCode; + ADDARG AddArg; + COMMARG * pCommArg; + + + SAMTRACE("SampDsCreateObject"); + + // + // Parameter validation + // + + ASSERT(Object); + ASSERT(AttributesToSet); + ASSERT(AttributesToSet->attrCount > 0); + + // Perform lazy thread and transaction initialization. + Status = SampMaybeBeginDsTransaction(SampWriteLock); + + if (Status!= STATUS_SUCCESS) + goto Error; + + // + // MAP the AttrBlock to get the final attributes to Set + // + + Status = SampSamToDsAttrBlock( + ObjectType, + AttributesToSet, + ( MAP_RID_TO_SID | MAP_ATTRIBUTE_TYPES + | REALLOC_IN_DSMEMORY |ADD_OBJECT_CLASS_ATTRIBUTE), + DomainSid, + &AddArg.AttrBlock + ); + + if (Status != STATUS_SUCCESS) + goto Error; + + // + // Setup the Common Args structure + // + + pCommArg = &(AddArg.CommArg); + BuildStdCommArg(pCommArg); + + // + // Setup the AddArg structure + // + + AddArg.pObject = Object; + + // + // Make the DS call + // + + RetCode = DirAddEntry(&AddArg); + + // + // Map the return code to an NT status + // + + Status = SampMapDsErrorToNTStatus(RetCode); + +Error: + + + return Status; + +} + + +NTSTATUS +SampDsDeleteObject( +IN DSNAME * Object +) +/*++ + + Routine Description: + + Delete an Object in the DS + + Arguments: + Object -- specifies the Object to delete + + Return Values: + STATUS_SUCCESS on succesful completion + DS return codes mapped to NT_STATUS as in SampMapDSErrorToNTStatus + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + REMOVEARG RemoveArg; + COMMARG *pCommArg; + ULONG RetValue; + + + SAMTRACE("SampDsDeleteObject"); + + // + // Asserts and parameter validation + // + + ASSERT(Object!=NULL); + + // Perform lazy thread and transaction initialization. + Status = SampMaybeBeginDsTransaction(SampWriteLock); + + if (Status!= STATUS_SUCCESS) + goto Error; + + // + // Setup the Common Args structure + // + + pCommArg = &(RemoveArg.CommArg); + BuildStdCommArg(pCommArg); + + // + // Setup the RemoveArgs structure + // + + RemoveArg.pObject = Object; + + // + // Make the directory call + // + + RetValue = DirRemoveEntry(&RemoveArg); + + // + // Map to corresponding NT status code + // + + Status = SampMapDsErrorToNTStatus(RetValue); + +Error: + + return Status; +} + + +NTSTATUS +SampDsDoUniqueSearch( + IN ULONG Flags, + IN DSNAME * ContainerObject, + IN ATTR * AttributeToMatch, + OUT DSNAME **Object + ) +/*++ + + Routine Description: + + Searches for the object with the given attribute + NOTE - SampDsDoUniqueSearch expects that the search result is unique. + It is typically used in Rid to Object, Sid To Object, Name To Object Mappings, + This is a simplified search, so that simple searches on a single attribute + can be easily set up. + + Arguments + Flags -- Flags, control searching + + ContainerObject -- specifies the DSNAME of the container in which to search + + AttributeToMatch -- specifies the type and value of the attribute that must match. + The attribute Type is the DS attribute Type. Caller must do + the translation. This is acceptable as this is not a function that + is called from outside dslayer.c + + Object -- Pointer to a DSNAME specifying the object is returned in here. + This object is allocated using SAM's memory allocation routines + + Return Values: + STATUS_SUCCESS -- on successful completion + STATUS_NOT_FOUND -- if object not found + STATUS_UNSUCCESSFUL -- if more than one match + DS return codes mapped to NT_STATUS as in SampMapDSErrorToNTStatus +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + SEARCHARG SearchArg; + SEARCHRES * SearchRes; + FILTER Filter; + ULONG RetCode; + COMMARG * pCommArg; + SVCCNTL * pSvcCntl; + ENTINFSEL EntInfSel; + + + SAMTRACE("SampDsDoSearch"); + + // + // Asserts and parameter validation + // + + ASSERT(AttributeToMatch); + ASSERT(AttributeToMatch->AttrVal.pAVal); + ASSERT(ContainerObject); + ASSERT(Object); + + // + // Set Object To NULL for sake of error returns + // + + *Object = NULL; + + // Perform lazy thread and transaction initialization. + Status = SampMaybeBeginDsTransaction(SampWriteLock); + + if (Status!= STATUS_SUCCESS) + goto Error; + + // + // Build the filter + // + + Filter.choice = FILTER_CHOICE_ITEM; + Filter.FilTypes.Item.choice = FI_CHOICE_EQUALITY; + Filter.FilTypes.Item.FilTypes.ava.type = AttributeToMatch->attrTyp; + Filter.FilTypes.Item.FilTypes.ava.Value.valLen = AttributeToMatch->AttrVal.pAVal->valLen; + Filter.FilTypes.Item.FilTypes.ava.Value.pVal = AttributeToMatch->AttrVal.pAVal->pVal; + + // + // Build the SearchArg Structure + // + + SearchArg.pObject = ContainerObject; + SearchArg.choice = SE_CHOICE_WHOLE_SUBTREE; + SearchArg.pFilter = & Filter; + SearchArg.searchAliases = FALSE; + SearchArg.pSelection = & EntInfSel; + + EntInfSel.attSel = EN_ATTSET_LIST; + EntInfSel.AttrTypBlock.attrCount = 1; + EntInfSel.AttrTypBlock.pAttr = AttributeToMatch; + EntInfSel.infoTypes = EN_INFOTYPES_TYPES_ONLY; + + // + // Build the Commarg structure + // Get the address of the service control structure + // + + pCommArg = &(SearchArg.CommArg); + BuildStdCommArg(pCommArg); + + if (Flags & MAKE_DEL_AVAILABLE) + { + pSvcCntl = &(pCommArg->Svccntl); + pSvcCntl->makeDeletionsAvail = TRUE; + } + + // + // Make the Directory call + // + + RetCode = DirSearch(&SearchArg, & SearchRes); + + // + // check for errors + // + + Status = SampMapDsErrorToNTStatus(RetCode); + if (Status != STATUS_SUCCESS) + goto Error; + + // + // If more data exists then error out. Under normal memory + // conditions we should not ever need to hit size limits. + // + + if ((SearchRes->pPartialOutcomeQualifier) + && (SearchRes->pPartialOutcomeQualifier->problem == PA_PROBLEM_SIZE_LIMIT)) + { + // Partial outcome, error out saying no mmeory + Status = STATUS_NO_MEMORY; + goto Error; + } + + + // + // Check if no match exists or more than one match exists. + // + + if (SearchRes->count == 0) + { + // + // No Match Exists + // + + Status = STATUS_NOT_FOUND; + goto Error; + } + else + if (SearchRes->count > 1) + { + // + // More than one match exists, + // BUG: for now fail the call saying STATUS_UNSUCCESSFUL. Later we + // may want to return a different error code. + // + + Status = STATUS_UNSUCCESSFUL; + goto Error; + } + + // + // Allocate Memory to hold that object, and copy in its Value + // + + *Object = MIDL_user_allocate(SearchRes->FirstEntInf.Entinf.pName->structLen); + if (NULL==Object) + { + Status = STATUS_NO_MEMORY; + goto Error; + } + RtlCopyMemory(*Object, + SearchRes->FirstEntInf.Entinf.pName, + SearchRes->FirstEntInf.Entinf.pName->structLen + ); + +Error: + return Status; +} + + +NTSTATUS +SampDsDoSearch( + RESTART *Restart, + DSNAME *DomainObject, + FILTER *DsFilter, + SAMP_OBJECT_TYPE ObjectTypeForConversion, + ATTRBLOCK * AttrsToRead, + ULONG MaxMemoryToUse, + SEARCHRES **SearchRes + ) +/*++ + + Routine Description: + + This routine calls a DS Search to list a set of Objects with + the given Filter. The user passes in a Filter Structure. PagedResults + are always requested. + + NOTE - This routine expects all attribute types to be DS attribute + Types. No Translation is Done, as otherwise it requires walking through + Messy and Complex Filter Structures, and Lists of AttrBlocks, as + returned by the Search. + + Arguments: + + Restart - Pointer to Restart Structure to contine an + old search + ContainerObject - The place to Search in + DsFilter - A Ds Filter Structure that is passed in + ObjectTypeForConversion - Sam Object Type to be used in + AttrBlock Conversion + AttrsToRead - Attributes to be read back, and returned with + every object that matched the search criteria. + MaxMemoryToUse - The Maximum Memory to Use. + SearchRes - Pointer to Search Results is passed back + in this + + Return Values + DS error codes Mapped to NT Status + +--*/ +{ + SEARCHARG SearchArg; + ENTINFSEL EntInfSel; + ULONG RetCode; + COMMARG *pCommArg; + NTSTATUS Status = STATUS_SUCCESS; + + // Perform lazy thread and transaction initialization. + Status = SampMaybeBeginDsTransaction(SampWriteLock); + + if (Status!= STATUS_SUCCESS) + return(Status); + + // + // Build the SearchArg Structure + // + + SearchArg.pObject = DomainObject; + SearchArg.choice = SE_CHOICE_WHOLE_SUBTREE; + SearchArg.pFilter = DsFilter; + SearchArg.searchAliases = FALSE; + SearchArg.pSelection = & EntInfSel; + + // + // Fill the ENTINF Structure + // + + EntInfSel.attSel = EN_ATTSET_LIST; + EntInfSel.infoTypes = EN_INFOTYPES_TYPES_VALS; + + + Status = SampSamToDsAttrBlock( + ObjectTypeForConversion, + AttrsToRead, + ( MAP_RID_TO_SID | MAP_ATTRIBUTE_TYPES| REALLOC_IN_DSMEMORY), + NULL, + & EntInfSel.AttrTypBlock + ); + + // + // Build the CommArg Structure + // Build the Commarg structure + // Get the address of the service control structure + // + + pCommArg = &(SearchArg.CommArg); + BuildStdCommArg(pCommArg); + + // + // Request For Paged Results + // + + pCommArg->PagedResult.fPresent = TRUE; + pCommArg->PagedResult.pRestart = Restart; + + // + // Set our memory size + // + + pCommArg->ulSizeLimit = MaxMemoryToUse; + + // + // Make the Directory call + // + + RetCode = DirSearch(&SearchArg, SearchRes); + + // + // Map Errors + // + + Status = SampMapDsErrorToNTStatus(RetCode); + + // + // Return error code + // + + return Status; +} + +NTSTATUS +SampDsLookupObjectByName( +IN DSNAME * DomainObject, +IN SAMP_OBJECT_TYPE ObjectType, +IN PUNICODE_STRING ObjectName, +OUT DSNAME ** Object +) +/*++ + +Routine Description: + + Does a Name to Object Mapping. + +Arguments: + ContainerObject -- The container in which to search the object + ObjectType -- The type of the Object. + ObjectName -- Unicode name of the Object to be located + Object -- DSNAME structure specifying the object +Return Values: + + STATUS_UNSUCCESSFUL + Returned Status from SampDoDsSearch + +--*/ + +{ + + NTSTATUS Status = STATUS_SUCCESS; + ATTRVAL NameVal; + ATTR NameAttr; + + + SAMTRACE("SampLookpObjectByName"); + + // + // The Name is a property stored in the object + // and we search for it. + // + + // + // setup the attribute field for the search + // + NameVal.valLen = (ObjectName->Length+1) * sizeof(WCHAR); + NameVal.pVal = (UCHAR *) ObjectName->Buffer; + NameAttr.AttrVal.valCount = 1; + NameAttr.AttrVal.pAVal = & NameVal; + + switch (ObjectType) + { + case SampGroupObjectType: + NameAttr.attrTyp = + SampDsAttrFromSamAttr(ObjectType,SAMP_GROUP_NAME); + break; + + case SampUserObjectType: + NameAttr.attrTyp = + SampDsAttrFromSamAttr(ObjectType,SAMP_USER_ACCOUNT_NAME); + break; + + case SampAliasObjectType: + NameAttr.attrTyp = + SampDsAttrFromSamAttr(ObjectType,SAMP_ALIAS_NAME); + break; + + default: + ASSERT(FALSE); + Status = STATUS_UNSUCCESSFUL; + goto Error; + } + + + Status = SampDsDoUniqueSearch(0,DomainObject,&NameAttr,Object); + + +Error: + + return(Status); +} + +NTSTATUS +SampDsObjectFromSid( + IN PSID Sid, + OUT DSNAME ** Object + ) +/*++ + + This Routine Searches the DS for specified SID. + PRE M1 -- This is just a DS search using the SID index + Post M1 -- This object may not exist in the current index, + in which case you go to the GC Server + + Arguments: + + Sid -- SID of the object + DsName -- DS NAME of the located object. + + + Return Values: + + STATUS_SUCCESS + STATUS_NOT_FOUND +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + ATTR SidAttr; + ATTRVAL SidVal; + DSNAME RootObject; + + SAMTRACE("SampDsObjectFromSid"); + + + // + // Set up the Sid Attribute + // + + SidAttr.attrTyp = SampDsAttrFromSamAttr( + SampUnknownObjectType, + SAMP_UNKNOWN_OBJECTSID + ); + + SidAttr.AttrVal.valCount = 1; + SidAttr.AttrVal.pAVal = &SidVal; + SidVal.valLen = RtlLengthSid(Sid); + SidVal.pVal = (UCHAR *)Sid; + + + // + // Specify Root as base of Search. This + // Translates to a zeroing out a DSName + // + + + Status = SampDsDoUniqueSearch( + 0, // Flags + ROOT_OBJECT, // Search Base + &SidAttr, // Sid + Object // Get Results in Here. + ); + + +Error: + + return Status; + +} + + +PSID +SampDsGetObjectSid( + IN DSNAME * Object + ) +/*++ +Routine Description: + + Given the DSNAME of the Object this routine returns the Sid + of the Object. + + Arguments: + + Object: + Object whose Sid needs returning + + Return Values: + Sid of the object. + NULL if no Sid exists +--*/ +{ + + ATTR SidAttr; + ATTRBLOCK SidAttrBlock; + ATTRBLOCK Result; + NTSTATUS Status; + + + SidAttrBlock.attrCount =1; + SidAttrBlock.pAttr = &(SidAttr); + + SidAttr.AttrVal.valCount =0; + SidAttr.AttrVal.pAVal = NULL; + SidAttr.attrTyp = SAMP_UNKNOWN_OBJECTSID; + + Status = SampDsRead( + Object, + 0, + SampUnknownObjectType, + & SidAttrBlock, + & Result + ); + + if (Status != STATUS_SUCCESS) + return NULL; + + return Result.pAttr[0].AttrVal.pAVal->pVal; +} + + +NTSTATUS +SampDsLookupObjectByRid( +IN DSNAME * DomainObject, +ULONG ObjectRid, +DSNAME **Object +) +/*++ + +Routine Description: + + RID to Object Mapping + +Arguments: + + ContainerObject -- The container in which to locate this object + ObjectRid -- RID of the object to be located + Object -- returns pointer to DSNAME structure specifying the object + + Return Values: + STATUS_SUCCESS on successful completion + Any returned by SampDsDoSearch + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + ATTRVAL RidVal = {sizeof(ULONG), (UCHAR *)&ObjectRid}; + ATTR RidAttr = {SAMP_UNKNOWN_OBJECTRID, {1, &RidVal}}; + PSID DomainSid; + ATTR SidAttr; + + SAMTRACE("SampDsLookupObjectByRid"); + + DomainSid = SampDsGetObjectSid(DomainObject); + + if (DomainSid == NULL) + { + Status = STATUS_UNSUCCESSFUL; + goto Error; + } + + SidAttr.attrTyp = SampDsAttrFromSamAttr( + SampUnknownObjectType, + SAMP_UNKNOWN_OBJECTSID + ); + + Status = SampDsSetNewSidAttribute( + DomainSid, + REALLOC_IN_DSMEMORY, + &RidAttr, + &SidAttr + ); + + if (Status != STATUS_SUCCESS) + goto Error; + + + Status = SampDsDoUniqueSearch(0,DomainObject,&SidAttr,Object); + + +Error: + + return Status; + +} + + +NTSTATUS +SampMapDsErrorToNTStatus(ULONG DsRetVal) +/*++ + +Routine Description: + + Maps a DS error to NTSTATUS + +Arguments: + None + +Return Values: + See the switch statement below + +--*/ +{ + NTSTATUS Status; + + switch (DsRetVal) + { + case 0L: + Status = STATUS_SUCCESS; + break; + + case attributeError: + case nameError: + case referralError: + case securityError: + case serviceError: + case updError: + case systemError: + default: + Status = STATUS_UNSUCCESSFUL; + break; + } + return Status; +} + + + +NTSTATUS +SampSamToDsAttrBlock( + IN SAMP_OBJECT_TYPE ObjectType, + IN ATTRBLOCK *AttrBlockToConvert, + IN ULONG ConversionFlags, + IN PSID DomainSid, + OUT ATTRBLOCK * ConvertedAttrBlock + ) +/*++ + +Routine Description: + + Converts the Attribute types in an Attrblock + from SAM to DS Types + +Arguments: + + ObjectType -- specifies type of SAM object + AttrBlockToConvert -- pointer to Attrblock to be converted + ConversionFlags -- The Type of conversion Desired. Currently + defined values are + + MAP_ATTRIBUTE_TYPES + REALLOC_IN_DSMEMORY + ADD_OBJECT_CLASS_ATTRIBUTE + MAP_RID_TO_SID + + DomainSid -- Used to Compose the Sid of the Object + when the MAP sid to Rid is specified + + ConvertedAttrBlock -- The Converted DS AttrBlock. + + +Return Values: + None + +--*/ +{ + ULONG Index; + NTSTATUS Status; + ULONG DsSidAttr = SampDsAttrFromSamAttr( + SampUnknownObjectType, + SAMP_UNKNOWN_OBJECTSID + ); + + ULONG DsRidAttr = SampDsAttrFromSamAttr( + SampUnknownObjectType, + SAMP_UNKNOWN_OBJECTRID + ); + + + // + // Copy the Fixed Portion + // + + *ConvertedAttrBlock = *AttrBlockToConvert; + + if (ConversionFlags & REALLOC_IN_DSMEMORY) + { + + ULONG AttrsToAllocate = AttrBlockToConvert->attrCount; + + if (ConversionFlags & ADD_OBJECT_CLASS_ATTRIBUTE) + { + // + // Caller requested that an object class attribute + // be added, alloc one more attr for object class + // + + AttrsToAllocate++ ; + } + + + // + // Realloc and Copy the pAttr portion of it. + // + + ConvertedAttrBlock->pAttr = DSAlloc( + AttrsToAllocate + * sizeof(ATTR) + ); + + if (NULL==ConvertedAttrBlock->pAttr) + { + Status = STATUS_NO_MEMORY; + goto Error; + } + + RtlCopyMemory( + ConvertedAttrBlock->pAttr, + AttrBlockToConvert->pAttr, + AttrBlockToConvert->attrCount * sizeof(ATTR) + ); + + } + + for (Index=0; Index<AttrBlockToConvert->attrCount;Index++) + { + + + // + // MAP Sam Attribute Types to DS Types if that was requested + // + + if (ConversionFlags & MAP_ATTRIBUTE_TYPES) + { + ConvertedAttrBlock->pAttr[Index].attrTyp = + SampDsAttrFromSamAttr( + ObjectType, + AttrBlockToConvert->pAttr[Index].attrTyp + ); + } + else + { + ConvertedAttrBlock->pAttr[Index].attrTyp = + AttrBlockToConvert->pAttr[Index].attrTyp; + } + + + // + // MAP Rid To Sid, if Attribute is Rid + // + + if ( (ConversionFlags & MAP_RID_TO_SID) + &&(ConvertedAttrBlock->pAttr[Index].attrTyp == DsRidAttr) + ) + + { + + + ConvertedAttrBlock->pAttr[Index].attrTyp = DsSidAttr; + Status = SampDsSetNewSidAttribute( + DomainSid, + ConversionFlags, + & (AttrBlockToConvert->pAttr[Index]), + & (ConvertedAttrBlock->pAttr[Index]) + ); + + if (!(NT_SUCCESS(Status))) + goto Error; + } + + // + // Else Just simply copy the Attribute Value + // + + else + { + Status = SampDsCopyAttributeValue( + & (AttrBlockToConvert->pAttr[Index]), + & (ConvertedAttrBlock->pAttr[Index]) + ); + } + } + + // + // If Addition of Object Class attribute was requested then + // Add this attribute. + // + + if (ConversionFlags & ADD_OBJECT_CLASS_ATTRIBUTE) + { + ULONG DsClass; + ATTR * LastAttr; + + DsClass = SampDsClassFromSamObjectType(ObjectType); + LastAttr = + &(ConvertedAttrBlock->pAttr[AttrBlockToConvert->attrCount]); + LastAttr->attrTyp = SampDsAttrFromSamAttr( + SampUnknownObjectType, + SAMP_UNKNOWN_OBJECTCLASS + ); + LastAttr->AttrVal.valCount = 1; + LastAttr->AttrVal.pAVal = DSAlloc(sizeof(ATTRVAL)); + if (NULL== LastAttr->AttrVal.pAVal) + { + Status = STATUS_NO_MEMORY; + goto Error; + } + + LastAttr->AttrVal.pAVal->valLen = sizeof(ULONG); + LastAttr->AttrVal.pAVal->pVal = DSAlloc(sizeof(ULONG)); + if (NULL== LastAttr->AttrVal.pAVal->pVal) + { + Status = STATUS_NO_MEMORY; + goto Error; + } + *((ULONG *) LastAttr->AttrVal.pAVal->pVal) = DsClass; + + // + // Increment the Attr Count + // + + ConvertedAttrBlock->attrCount++; + + + } + + +Error: + + return Status; +} + + + + +NTSTATUS +SampDsNewAccountSid( + PSID DomainSid, + ULONG AccountRid, + PSID *NewSid + ) +/* + Routine Description + + Composes an Account Sid from the given Domain Sid and Rid. + Uses DS thread memory. THis is the main difference between + the function in utility.c + + Arguments: + + DomainSid The Domain Sid + AccountRid The Rid + NewSid The final account Sid + + Return Values + + STATUS_SUCCESS + STATUS_NO_MEMORY +*/ + +{ + ULONG DomainSidLength = RtlLengthSid(DomainSid); + NTSTATUS Status = STATUS_SUCCESS; + + + // + // Alloc Memory to hold the account Sid + // + + *NewSid = DSAlloc(DomainSidLength + sizeof(ULONG)); + + if (NULL==*NewSid) + { + Status = STATUS_NO_MEMORY; + goto Error; + } + + // + // Copy the Domain Sid Part + // + + RtlCopyMemory(*NewSid,DomainSid,DomainSidLength); + + // + // Increment the SubAuthority Count + // + + ((UCHAR *) *NewSid)[1]++; + + // + // Add the RID as a sub authority + // + + *((ULONG *) (((UCHAR *) *NewSid ) + DomainSidLength)) = + AccountRid; + +Error: + + return Status; +} + + +NTSTATUS +SampDsSetNewSidAttribute( + PSID DomainSid, + ULONG ConversionFlags, + ATTR *RidAttr, + ATTR *SidAttr + ) +/* + Routine Description + + Composes a DS Sid Attr , given a DS Rid Attr + + Arguments: + + Conversion Flags + + Currently only valid value is REALLOC_IN_DS_MEMORY + + RidAttr + + Rid Attribute + SidAttr + The Sid Attribute that is composed +*/ +{ + + PSID NewSid = NULL; + ULONG AccountRid; + NTSTATUS Status = STATUS_SUCCESS; + + if ( + (RidAttr->AttrVal.valCount) + && (RidAttr->AttrVal.pAVal) + && (RidAttr->AttrVal.pAVal->pVal) + && (RidAttr->AttrVal.pAVal->valLen) + ) + { + // + // Values are Present, assert that REALLOC is also + // specified + // + + ASSERT(ConversionFlags & REALLOC_IN_DSMEMORY); + + if (!(ConversionFlags & REALLOC_IN_DSMEMORY)) + { + // + // Realloc in DS memory is not specified + // + + Status = STATUS_NOT_IMPLEMENTED; + goto Error; + } + + // + // Compose New Sid + // + + AccountRid = * ((ULONG *)RidAttr->AttrVal.pAVal->pVal); + Status = SampDsNewAccountSid(DomainSid,AccountRid, &NewSid); + if (!(NT_SUCCESS(Status))) + goto Error; + + // + // Alloc Memory for ATTRVAL structure + // + + SidAttr->AttrVal.pAVal = + DSAlloc(sizeof(ATTRVAL)); + + if (NULL== SidAttr->AttrVal.pAVal) + { + Status = STATUS_NO_MEMORY; + goto Error; + } + + // + // Set the Value to the New Sid + // + + SidAttr->AttrVal.valCount = 1; + SidAttr->AttrVal.pAVal->valLen = RtlLengthSid(NewSid); + SidAttr->AttrVal.pAVal->pVal = NewSid; + } + else + { + SidAttr->AttrVal.valCount = 0; + SidAttr->AttrVal.pAVal = NULL; + } + +Error: + + + return Status; +} + + +NTSTATUS +SampDsCopyAttributeValue( + ATTR * Src, + ATTR * Dst + ) +/* + Routine Description + + Copies a DS Attributes Value + + Arguments: + + Src - Source Attribute + Dst - Destination Attribute + + Return Values + + STATUS_SUCCESS + STATUS_NO_MEMORY + +*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + ULONG Index; + + if ( + (Src->AttrVal.valCount) + && (Src->AttrVal.pAVal) + ) + { + // + // Values are Present, Copy Them + // + + Dst->AttrVal.pAVal = DSAlloc( + Src->AttrVal.valCount * + sizeof(ATTRVAL) + ); + + if (NULL== Dst->AttrVal.pAVal) + { + Status = STATUS_NO_MEMORY; + goto Error; + } + + Dst->AttrVal.valCount = Src->AttrVal.valCount; + + for (Index=0;Index<Src->AttrVal.valCount;Index++) + { + if (Src->AttrVal.pAVal[Index].valLen) + Dst->AttrVal.pAVal[Index].valLen = + Src->AttrVal.pAVal[Index].valLen; + + if ((Src->AttrVal.pAVal[Index].valLen) + && (Src->AttrVal.pAVal[Index].pVal)) + { + Dst->AttrVal.pAVal[Index].pVal = + DSAlloc(Src->AttrVal.pAVal[Index].valLen); + if (NULL== Dst->AttrVal.pAVal[Index].pVal) + { + Status = STATUS_NO_MEMORY; + goto Error; + } + RtlCopyMemory( + Dst->AttrVal.pAVal[Index].pVal, + Src->AttrVal.pAVal[Index].pVal, + Dst->AttrVal.pAVal[Index].valLen + ); + } + else + Dst->AttrVal.pAVal[Index].pVal = NULL; + } + } + else + { + Dst->AttrVal.pAVal = NULL; + Dst->AttrVal.valCount = 0; + } + +Error: + + return Status; +} + + + +NTSTATUS +SampDsToSamAttrBlock( + IN SAMP_OBJECT_TYPE ObjectType, + IN ATTRBLOCK * AttrBlockToConvert, + IN ULONG ConversionFlags, + OUT ATTRBLOCK * ConvertedAttrBlock + ) +/*++ + +Routine Description: + + Converts the Attribute types in an Attrblock + from DS to SAM Types + +Arguments: + + ObjectType -- specifies type of SAM object + AttrBlockToConvert -- pointer to Attrblock to be converted + ConversionFlags -- The Type of Conversion Desired. Currently + defined values are + + MAP_ATTRIBUTE_TYPES + MAP_SID_TO_RID + + ConvertedAttrBlock -- The converted AttrBlock. + +Return Values: + None + + + --*/ + { + ULONG Index; + ULONG DsSidAttr = SampDsAttrFromSamAttr( + SampUnknownObjectType, + SAMP_UNKNOWN_OBJECTSID + ); + + ULONG DsRidAttr = SampDsAttrFromSamAttr( + SampUnknownObjectType, + SAMP_UNKNOWN_OBJECTRID + ); + + + *ConvertedAttrBlock = *AttrBlockToConvert; + + for (Index=0; Index<AttrBlockToConvert->attrCount;Index++) + { + // + // MAP Any Sid Attribute to Rid Attribute + // + + if ((ConversionFlags & MAP_SID_TO_RID) && + (AttrBlockToConvert->pAttr[Index].attrTyp == DsSidAttr)) + + { + ATTR * pSidAttr = &(AttrBlockToConvert->pAttr[Index]); + + switch(ObjectType) + { + case SampGroupObjectType: + case SampAliasObjectType: + case SampUserObjectType: + + // + // Map the Attr Type + // + + pSidAttr->attrTyp = DsRidAttr; + + // + // Map the Attr Value, the Last ULONG in the Sid + // is the Rid, so advance the pointer accordingly + // + + pSidAttr->AttrVal.pAVal->pVal+= + pSidAttr->AttrVal.pAVal->valLen - sizeof(ULONG); + pSidAttr->AttrVal.pAVal->valLen = sizeof(ULONG); + + default: + break; + } + } + + // + // MAP Attribute Types + // + + if (ConversionFlags & MAP_ATTRIBUTE_TYPES) + { + ConvertedAttrBlock->pAttr[Index].attrTyp = + SampSamAttrFromDsAttr( + ObjectType, + AttrBlockToConvert->pAttr[Index].attrTyp + ); + } + + + + } // End of For Loop + + return STATUS_SUCCESS; + +} + + +NTSTATUS +SampDsCreateDsName( + IN DSNAME * DomainObject, + IN ULONG AccountId, + IN OUT DSNAME ** NewObject + ) +/*++ + Routine Description + Builds a DSName given an account Rid and the Domain Object + + Arguments: + + DomainObject -- DSName of the Domain Object + AccountRid -- The Rid of the account + NewObject -- Returns the New DS Name in this object + + Return values: + STATUS_SUCCESS - upon successful completion + STATUS_NO_MEMORY - Memory alloc Failure + +--*/ +{ + + + NTSTATUS Status = STATUS_SUCCESS; + WCHAR LookupTable[]=L"0123456789ABCDEF"; + WCHAR CommonNamePart[] = L"/cn="; + ULONG Mask = 0xF0000000; + ULONG Index = 0; + + // + // Define the size of the Rid Part + // This should be length of the Rid Hex printed out + + // 1 for Null terminator + // + + #define RidPartSize (8+sizeof(CommonNamePart)) + WCHAR NameBuffer[RidPartSize]; + + + // + // Alloc a DS Name capable of holding everything + // + + *NewObject = MIDL_user_allocate(DomainObject->structLen + + RidPartSize * sizeof(WCHAR)); + if (*NewObject == NULL) + { + Status = STATUS_NO_MEMORY; + goto Error; + } + + // + // Copy the comon name part + // + + RtlCopyMemory(NameBuffer,CommonNamePart,sizeof(CommonNamePart)); + + // + // Now Hext print out the Rid. + // + + for (Index=0;Index<(2*sizeof(ULONG));Index++) + { + ULONG HexDigit; + + HexDigit = (AccountId & Mask)>>(4*(2*sizeof(ULONG)-1-Index)); + NameBuffer[Index+sizeof(CommonNamePart)/sizeof(WCHAR)-1] + = LookupTable[HexDigit]; + Mask = Mask>>4; + } + + NameBuffer[RidPartSize-1]=0; + + // + // Build the DS Name + // + + SampInitializeDsName(*NewObject, + DomainObject->StringName, + DomainObject->NameLen*sizeof(WCHAR), + NameBuffer, + RidPartSize*sizeof(WCHAR) + ); +Error: + + return Status; +} + + +void +SampInitializeDsName( + IN DSNAME * pDsName, + IN WCHAR * NamePrefix, + IN ULONG NamePrefixLen, + IN WCHAR * ObjectName, + IN ULONG NameLen + ) +/*++ +Routine Description: + Initializes a DSNAME structure + +Arguments: + pDsName -- A pointer to a buffer large enough to hold everything. This buffer will be + filled with a NULL GUID plus a complete name + + NamePrefix -- pointer to a sequence of NULL terminated + UNICODE chars holding any prefix + to the name. Useful in composing + hierarchial names + + NamePrefixLen -- Length of the Prefix in bytes. Also includes the NULL terminator + + ObjectName -- pointer to a sequence of NULL terminated + UNICODE char the name of the object + + NameLen -- Length of the Object Name in bytes. Also includes the NULL terminator + + + Return Values: + + None + +--*/ +{ + // + // Single NULL string is not allowed for name or Prefix + // + + ASSERT(NamePrefixLen!=sizeof(WCHAR)); + ASSERT(NameLen!=sizeof(WCHAR)); + + // + // Zero the GUID + // + + RtlZeroMemory(&(pDsName->Guid), sizeof(GUID)); + + // + // Compute String Length including Null terminator + // + + if (NamePrefix) + { + + pDsName->NameLen = (NameLen + NamePrefixLen)/sizeof(WCHAR)-1; + + // + // Compute the Struct length + // + + pDsName->structLen = sizeof (DSNAME) + NamePrefixLen + NameLen -2 * sizeof ( WCHAR); + + // + // Copy the name Prefix + // + + RtlCopyMemory(pDsName->StringName, NamePrefix,NamePrefixLen); + + // + // Copy the Object Name + // + + RtlCopyMemory(&(pDsName->StringName[(NamePrefixLen/sizeof(WCHAR))-1]), ObjectName, NameLen); + } + else + { + pDsName->NameLen = NameLen/sizeof(WCHAR); + + // + // Compute the Struct length + // + + pDsName->structLen = sizeof (DSNAME) + NameLen - sizeof(WCHAR); + + // + // Copy the name Prefix + // + + RtlCopyMemory(pDsName->StringName, NamePrefix,NamePrefixLen); + + // + // Copy the Object Name + // + + RtlCopyMemory(&(pDsName->StringName[0]), ObjectName, NameLen); + } + +} + + +// +// DS Memory Allocation Routine +// +// + +PVOID +DSAlloc( + IN ULONG Length + ) +/* + + Routine Description: + + Ds Memory Allocation Routine + + Arguments: + + Length - Amount of memory to be allocated + + Return Values + + NULL if Memory alloc failed + Pointer to memory upon success +*/ +{ + PVOID MemoryToReturn = NULL; + + __try + { + MemoryToReturn = THAllocEx((USHORT) Length); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + MemoryToReturn = NULL; + } + + return MemoryToReturn; +} + + +VOID +SampDsBuildRootObjectName() +/* + + Routine Description: + + Initializes the Global variable that holds the + name of the Root Object + + */ +{ + WCHAR RootObjectStringName[] = L"/o=NT" ; + + // + // BUG for now this is hard coded to /o=NT, + // as searches on root currently fail. + // + + SampInitializeDsName( + ROOT_OBJECT, + NULL, + 0, + RootObjectStringName, + sizeof(RootObjectStringName) + ); +} + + diff --git a/private/newsam2/server/dslayer.h b/private/newsam2/server/dslayer.h new file mode 100644 index 000000000..1ca673538 --- /dev/null +++ b/private/newsam2/server/dslayer.h @@ -0,0 +1,333 @@ +/*++ +Copyright (c) 1996 Microsoft Corporation + +Module Name: + + dslayer.h + +Abstract: + + Header file for SAM Private API Routines to access the DS + These API provide a simplified API, and hide most of the + underlying complexity to set up the parameters to a DS call + and parse the resulting result. They also provide an abstraction + by which we can create a simple layer, to unit test SAM without + actually running the DS. + +Author: + MURLIS + +Revision History + + 5-14-96 Murlis Created + +--*/ + +#ifndef __DSLAYER_H__ +#define __DSLAYER_H__ + +#include <samsrvp.h> +#include <duapi.h> +#include <scache.h> +#include <dbglobal.h> +#include <mdglobal.h> +#include <dsatools.h> + +// Size Limit for DS operations +#define SAMP_DS_SIZE_LIMIT 100000 +#define SAMP_MAX_DSNAME_SIZE 1024 + +/////////////////////////////////////////////////////////////////// +// // +// Macros for defining Local arrays of Attributes // +// // +/////////////////////////////////////////////////////////////////// + +//Need some preprocessor support to do this a variable number of times + + +//***** ATTRBLOCK1 +#define DEFINE_ATTRBLOCK1(_Name_, _AttrTypes_,_AttrValues_)\ +ATTR _AttrList_##_Name_[]=\ +{\ + {_AttrTypes_[0], {1,&_AttrValues_[0]}}\ +};\ +ATTRBLOCK _Name_=\ +{\ + sizeof(_AttrTypes_)/sizeof(_AttrTypes_[0]),\ + _AttrList_##_Name_\ +} + + +//***** ATTRBLOCK2 +#define DEFINE_ATTRBLOCK2(_Name_, _AttrTypes_,_AttrValues_)\ +ATTR _AttrList_##_Name_[]=\ +{\ + {_AttrTypes_[0], {1,&_AttrValues_[0]}},\ + {_AttrTypes_[1], {1,&_AttrValues_[1]}}\ +};\ +ATTRBLOCK _Name_=\ +{\ + sizeof(_AttrTypes_)/sizeof(_AttrTypes_[0]),\ + _AttrList_##_Name_\ +} + + +//***** ATTRBLOCK3 +#define DEFINE_ATTRBLOCK3(_Name_, _AttrTypes_,_AttrValues_)\ +ATTR _AttrList_##_Name_[]=\ +{\ + {_AttrTypes_[0], {1,&_AttrValues_[0]}},\ + {_AttrTypes_[1], {1,&_AttrValues_[1]}},\ + {_AttrTypes_[2], {1,&_AttrValues_[2]}}\ +};\ +ATTRBLOCK _Name_=\ +{\ + sizeof(_AttrTypes_)/sizeof(_AttrTypes_[0]),\ + _AttrList_##_Name_\ +} + + +//***** ATTRBLOCK4 +#define DEFINE_ATTRBLOCK4(_Name_, _AttrTypes_,_AttrValues_)\ +ATTR _AttrList_##_Name_[]=\ +{\ + {_AttrTypes_[0], {1,&_AttrValues_[0]}},\ + {_AttrTypes_[1], {1,&_AttrValues_[1]}},\ + {_AttrTypes_[2], {1,&_AttrValues_[2]}},\ + {_AttrTypes_[3], {1,&_AttrValues_[3]}}\ +};\ +ATTRBLOCK _Name_=\ +{\ + sizeof(_AttrTypes_)/sizeof(_AttrTypes_[0]),\ + _AttrList_##_Name_\ +} + +///////////////////////////////////////////////////////////////////////////// +// // +// DS DLL initialize exports. This is here only temporarily. Should remove // +// this and create a header file that has all the exports together // +// // +///////////////////////////////////////////////////////////////////////////// + + + +NTSTATUS +DsInitialize(void); + +NTSTATUS +DsUninitialize(void); + + +/////////////////////////////////////////////////////////////////////// +// // +// DS Operation Routines implemented in dslayer.c // +// // +/////////////////////////////////////////////////////////////////////// + +NTSTATUS +SampDsInitialize(); + +NTSTATUS +SampDsUninitialize(); + +NTSTATUS +SampDsRead( + IN DSNAME * Object, + IN ULONG Flags, + IN SAMP_OBJECT_TYPE ObjectType, + IN ATTRBLOCK * AttributesToRead, + OUT ATTRBLOCK * AttributeValues + ); + + +// +// Operatin Values for Set Attributes +// + +#define REPLACE_ATT ((ULONG) 0) +#define ADD_ATT ((ULONG) 1) +#define REMOVE_ATT ((ULONG) 2) +#define ADD_VALUE ((ULONG) 3) +#define REMOVE_VALUE ((ULONG) 4) + +NTSTATUS +SampDsSetAttributes( + IN DSNAME * Object, + IN ULONG Operation, + IN SAMP_OBJECT_TYPE ObjectType, + IN ATTRBLOCK * AttributeList + ); + + + + +NTSTATUS +SampDsCreateObject( + IN DSNAME * Object, + IN SAMP_OBJECT_TYPE ObjectType, + IN ATTRBLOCK * AttributesToSet, + IN PSID DomainSid + ); + + +NTSTATUS +SampDsDeleteObject( + IN DSNAME * Object + ); + + +/////////////////////////////////////////////////////////////////// +// // +// // +// DS Search Routines // +// // +// // +// // +/////////////////////////////////////////////////////////////////// + +// +// Flag Values for Unique Search +// + +#define MAKE_DEL_AVAILABLE 0x1 + +NTSTATUS +SampDsDoSearch( + RESTART *Restart, + DSNAME *DomainObject, + FILTER *DsFilter, + SAMP_OBJECT_TYPE ObjectTypeForConversion, + ATTRBLOCK * AttrsToRead, + ULONG MaxMemoryToUse, + SEARCHRES **SearchRes + ); + + +NTSTATUS +SampDsDoUniqueSearch( + ULONG Flags, + IN DSNAME * ContainerObject, + IN ATTR * AttributeToMatch, + OUT DSNAME **Object + ); + + +NTSTATUS +SampDsLookupObjectByName( + IN DSNAME * DomainObject, + IN SAMP_OBJECT_TYPE ObjectType, + IN PUNICODE_STRING ObjectName, + OUT DSNAME ** Object + ); + +NTSTATUS +SampDsLookupObjectByRid( + IN DSNAME * DomainObject, + ULONG ObjectRid, + DSNAME **Object + ); + +//////////////////////////////////////////////////////////////////// +// // +// // +// Object To Sid Mappings // +// // +// // +//////////////////////////////////////////////////////////////////// + +NTSTATUS +SampDsObjectFromSid( + IN PSID Sid, + OUT DSNAME ** DsName + ); + +PSID +SampDsGetObjectSid( + IN DSNAME * Object + ); + + + +///////////////////////////////////////////////////////////////////// +// // +// Some Utility Routines in Dslayer.c // +// // +// // +///////////////////////////////////////////////////////////////////// + +NTSTATUS +SampDsCreateDsName( + IN DSNAME *DomainObject, + IN ULONG AccountId, + IN OUT DSNAME **NewObject + ); + + +VOID +SampInitializeDsName( + DSNAME * pDsName, + WCHAR * NamePrefix, + ULONG NamePrefixLen, + WCHAR * ObjectName, + ULONG NameLen + ); + + + +VOID +SampDsBuildRootObjectName( + VOID + ); + + + +///////////////////////////////////////////////////////////////////// +// // +// ATTRBLOCK conversion routines. These Routines convert back // +// and forth between SAM and DS ATTRBLOCKS. The type of conversion// +// depends upon the Flags Conversion Flags that are passed in. // +// // +// // +///////////////////////////////////////////////////////////////////// + + +// +// Conversion Flag Definitions for SampSamToDsAttrBlock +// + +#define MAP_ATTRIBUTE_TYPES ((ULONG)0x1) +#define REALLOC_IN_DSMEMORY ((ULONG)0x2) +#define ADD_OBJECT_CLASS_ATTRIBUTE ((ULONG)0x4) +#define MAP_RID_TO_SID ((ULONG)0x8) + +// +// Function Declaration +// + +NTSTATUS +SampSamToDsAttrBlock( + IN SAMP_OBJECT_TYPE ObjectType, + IN ATTRBLOCK *AttrBlockToConvert, + IN ULONG ConversionFlags, + IN PSID DomainSid, + OUT ATTRBLOCK * ConvertedAttrBlock + ); + +// +// Conversion Flag Definitions For SampDsToSamAttrBlock +// + +// #define MAP_ATTRIBUTE_TYPES 0x1 +#define MAP_SID_TO_RID 0x2 + +NTSTATUS +SampDsToSamAttrBlock( + IN SAMP_OBJECT_TYPE ObjectType, + IN ATTRBLOCK * AttrBlockToConvert, + IN ULONG ConversionFlags, + OUT ATTRBLOCK * ConvertedAttrBlock + ); + + +#endif diff --git a/private/newsam2/server/dsmember.c b/private/newsam2/server/dsmember.c new file mode 100644 index 000000000..58ace9f62 --- /dev/null +++ b/private/newsam2/server/dsmember.c @@ -0,0 +1,683 @@ +/*++ +Copyright (c) 1996 Microsoft Corporation + +Module Name: + + dsmember.c + +Abstract: + + This file contains SAM private API Routines that manipulate + membership related things in the DS. + +Author: + MURLIS + +Revision History + + 7-2-96 Murlis Created + +--*/ + +#include <samsrvp.h> +#include <dslayer.h> +#include <dsmember.h> + +NTSTATUS +SampDsGetAliasMembershipOfAccount( + IN DSNAME * DomainObjectName, + IN PSID AccountSid, + OUT PULONG MemberCount OPTIONAL, + IN OUT PULONG BufferSize OPTIONAL, + OUT PULONG Buffer OPTIONAL + ) +/* +Routine Description: + + This routine gives the alias membership list of a given + account SID, in the domain speicified by DomainObjectName, + in the DS. This list is used in computing the given user's + Token. + + Arguments: + + DomainObjectName -- DS Name of the Domain Object + AccountSid -- Sid of the Account + MemberCount -- List of Aliases this is a member of + BufferSize -- Passed in by caller if he has alredy allocated + Buffer + Buffer -- Buffer to hold things in, Pointer can hold + NULL, if caller wants us to allocate + + Return Values + + STATUS_SUCCESS + Other Error codes From DS Layer. +*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + + // + // BUG: Need further support in the DS to get the reverse membership. + // Call speced out for now, till required support is there. + // + // + + if (MemberCount) + * MemberCount = 0; + + return STATUS_SUCCESS; +} + + +NTSTATUS +SampDsAddMembershipAttribute( + IN DSNAME * GroupObjectName, + IN SAMP_OBJECT_TYPE SamObjectType, + IN DSNAME * MemberName + ) +/*++ + Routine Description: + + This routine adds a Member To a Group or Alias Object + + Arguments: + GroupObjectName -- DS Name of the Group or Alias + MemberName -- DS Name of the Member to be added + + Return Values: + STATUS_SUCCESS + Other Error codes from DS Layer +--*/ +{ + ATTRVAL MemberVal; + ATTR MemberAttr; + ATTRBLOCK AttrsToAdd; + ULONG MembershipAttrType; + ORNAME *MemberORName = NULL; + NTSTATUS NtStatus = STATUS_SUCCESS; + + + // + // Get the membership attribute for the SAM object in question + // + // + + switch( SamObjectType ) + { + case SampGroupObjectType: + MembershipAttrType = SAMP_GROUP_MEMBERS; + break; + + case SampAliasObjectType: + MembershipAttrType = SAMP_ALIAS_MEMBERS; + break; + + default: + + ASSERT(FALSE); + + NtStatus = STATUS_UNSUCCESSFUL; + goto Error; + } + + // + // Build the Attr Val adding the membership attr + // + + MemberORName = MIDL_user_allocate(sizeof(ORNAME)+ MemberName->structLen); + if (NULL==MemberORName) + { + NtStatus = STATUS_NO_MEMORY; + goto Error; + } + + MemberORName->cbStruct = sizeof(ORNAME)+ MemberName->structLen; + MemberORName->fIsTag = FALSE; + MemberORName->DN.pDN = (DSNAME *) sizeof(ORNAME); // DSBUG: + // The ORNAME structure in + // in DS defines this as a + // pointer to the DSNAME, + // but the DS uses this as + // an offset. + MemberORName->nDesc = 0; + RtlCopyMemory( + ((UCHAR *)(MemberORName)) + (ULONG) MemberORName->DN.pDN, + MemberName, + MemberName->structLen + ); + + + MemberVal.valLen = MemberORName->cbStruct; + MemberVal.pVal = (UCHAR *) MemberORName; + MemberAttr.attrTyp = MembershipAttrType; + MemberAttr.AttrVal.valCount = 1; + MemberAttr.AttrVal.pAVal = & MemberVal; + + + // + // Build the AttrBlock + // + + AttrsToAdd.attrCount = 1; + AttrsToAdd.pAttr = & MemberAttr; + + // + // Add the Value + // + + NtStatus = SampDsSetAttributes( + GroupObjectName, // Object + ADD_VALUE, // Operation + SamObjectType, // ObjectType + &AttrsToAdd // AttrBlock + ); + + +Error: + if (MemberORName) + MIDL_user_free(MemberORName); + + return NtStatus; +} + + +NTSTATUS +SampDsRemoveMembershipAttribute( + IN DSNAME * GroupObjectName, + IN SAMP_OBJECT_TYPE SamObjectType, + IN DSNAME * MemberName + ) +/*++ +Routine Description: + + This Routine Removes a Member from a Group or Alias Object + + BUG: For Now this treats it the membership attribute as an + ORNAME. Later change this to a DS Name + +Arguments: + + GroupObjectName -- DS Name of the Group or Alias + MemberName -- DS Name of the Member to be added + + Return Values: + STATUS_SUCCESS + Other Error codes from DS Layer +--*/ +{ + ATTRVAL MemberVal; + ATTR MemberAttr; + ATTRBLOCK AttrsToRemove; + ULONG MembershipAttrType; + ORNAME *MemberORName = NULL; + NTSTATUS NtStatus = STATUS_SUCCESS; + + + // + // Get the membership attribute for the SAM object in question + // + // + + switch( SamObjectType ) + { + + case SampGroupObjectType: + + MembershipAttrType = SAMP_GROUP_MEMBERS; + break; + + case SampAliasObjectType: + + MembershipAttrType = SAMP_ALIAS_MEMBERS; + break; + + default: + + ASSERT(FALSE); + + NtStatus = STATUS_UNSUCCESSFUL; + goto Error; + } + + // + // Build the Attr Val adding the membership attr + // + + + MemberORName = MIDL_user_allocate(sizeof(ORNAME)+ MemberName->structLen); + if (NULL==MemberORName) + { + NtStatus = STATUS_NO_MEMORY; + goto Error; + } + + MemberORName->cbStruct = sizeof(ORNAME)+ MemberName->structLen; + MemberORName->fIsTag = FALSE; + MemberORName->DN.pDN = (DSNAME *) sizeof(ORNAME); // DSBUG: + // The ORNAME structure in + // in DS defines this as a + // pointer to the DSNAME, + // but the DS uses this as + // an offset. + + MemberORName->nDesc = 0; + RtlCopyMemory( + ((UCHAR *)(MemberORName)) + (ULONG) MemberORName->DN.pDN, + MemberName, + MemberName->structLen + ); + + MemberVal.valLen = MemberORName->cbStruct; + MemberVal.pVal = (UCHAR *) MemberORName; + MemberAttr.attrTyp = MembershipAttrType; + MemberAttr.AttrVal.valCount = 1; + MemberAttr.AttrVal.pAVal = & MemberVal; + + // + // Build the AttrBlock + // + + AttrsToRemove.attrCount = 1; + AttrsToRemove.pAttr = & MemberAttr; + + // + // Remove the Value + // + + NtStatus = SampDsSetAttributes( + GroupObjectName, // Object + REMOVE_VALUE, // Operation + SamObjectType, // ObjectType + &AttrsToRemove // AttrBlock + ); + +Error: + + if (MemberORName) + MIDL_user_free(MemberORName); + return NtStatus; + +} + + + +NTSTATUS +SampDsGetGroupMembershipList( + IN DSNAME * GroupName, + IN PULONG *Members OPTIONAL, + IN PULONG MemberCount + ) +/*++ + + Routine Description: + + This Routine Gets a Group Membership as an array of Rid's as required + by SAM. + + Arguments + + GroupName -- DSNAME of the concerned group object + Members -- Array of Rids will be passed in here + MemberCount -- Count of Rids + + Return Values: + STATUS_SUCCESS + STATUS_NO_MEMORY + Return Codes from DS Layer +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + ATTR MemberAttr; + ATTRBLOCK AttrBlockToRead; + ATTRBLOCK AttrsRead; + + + // + // Asserts + // + + ASSERT(MemberCount); + + // + // Initialize Members field + // + + *MemberCount = 0; + if (Members) + *Members = NULL; + + // + // Setup to Read the Membership Attributes + // + + MemberAttr.AttrVal.valCount = 0; + MemberAttr.attrTyp = SAMP_GROUP_MEMBERS; + MemberAttr.AttrVal.pAVal = NULL; + AttrBlockToRead.attrCount =1; + AttrBlockToRead.pAttr = & MemberAttr; + + + // + // Read from the DS. + // + + Status = SampDsRead( + GroupName, + 0, + SampGroupObjectType, + &AttrBlockToRead, + &AttrsRead + ); + + if (!NT_SUCCESS(Status)) + goto Error; + + // + // Go Along and Map to Rid. + // + + ASSERT(AttrsRead.pAttr); + if (AttrsRead.pAttr) + { + + // + // Get Member Count + // + + *MemberCount = AttrsRead.pAttr->AttrVal.valCount; + + if (Members) + { + // + // If List was asked for + // + + ULONG Index; + ULONG Rid; + PSID MemberSid; + ORNAME * MemberORName; + DSNAME * MemberName; + + + *Members = MIDL_user_allocate(*MemberCount * sizeof(ULONG)); + + if (NULL==*Members) + { + Status = STATUS_NO_MEMORY; + goto Error; + } + + // + // Loop through each entry looking at the Sids + // + + for(Index = 0; Index<*MemberCount;Index ++) + { + + + MemberORName = (ORNAME *) AttrsRead.pAttr->AttrVal.pAVal[Index].pVal; + MemberName = (DSNAME *) ((UCHAR *) MemberORName + + (ULONG) MemberORName->DN.pDN); + + // + // BUG: Zero out the GUID + // + + RtlZeroMemory(&(MemberName->Guid), sizeof(GUID)); + + MemberSid = SampDsGetObjectSid(MemberName); + + if (NULL==MemberSid) + { + ASSERT(FALSE); + Status = STATUS_INTERNAL_ERROR; + goto Error; + } + + // + // Split the SId + // + + Status = SampSplitSid( + MemberSid, + NULL, + &Rid + ); + + + if (!NT_SUCCESS(Status)) + goto Error; + + + // + // Copy the Rid + // + + (*Members)[Index] = Rid; + } + } + } + + +Error: + + if (!NT_SUCCESS(Status)) + { + // + // Set Error Return + // + + if (*Members) + { + MIDL_user_free(*Members); + *Members = NULL; + *MemberCount = 0; + } + } + + return Status; +} + + + +NTSTATUS +SampDsGetAliasMembershipList( + IN DSNAME *AliasName, + IN PULONG MemberCount, + IN PSID **Members OPTIONAL + ) +/*++ + + Routine Description: + + This Routine Gets a Alias Membership as an array of Sid's as required + by SAM. + + Arguments + + AliasName -- DSNAME of the concerned Alias object + Members -- Array of Rids will be passed in here + MemberCount -- Count of Sids + + Return Values: + STATUS_SUCCESS + STATUS_NO_MEMORY + Return Codes from DS Layer +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + ATTR MemberAttr; + ATTRBLOCK AttrBlockToRead; + ATTRBLOCK AttrsRead; + + + // + // Asserts + // + + ASSERT(MemberCount); + + // + // Initialize Members field + // + + *MemberCount = 0; + if (Members) + *Members = NULL; + + // + // Setup to Read the Membership Attributes + // + + MemberAttr.AttrVal.valCount = 0; + MemberAttr.attrTyp = SAMP_ALIAS_MEMBERS; + MemberAttr.AttrVal.pAVal = NULL; + AttrBlockToRead.attrCount =1; + AttrBlockToRead.pAttr = & MemberAttr; + + + // + // Read from the DS. + // + + Status = SampDsRead( + AliasName, + 0, + SampAliasObjectType, + &AttrBlockToRead, + &AttrsRead + ); + + if (!NT_SUCCESS(Status)) + goto Error; + + // + // Go Along and Map to Rid. + // + + ASSERT(AttrsRead.pAttr); + if (AttrsRead.pAttr) + { + + // + // Get Member Count + // + + *MemberCount = AttrsRead.pAttr->AttrVal.valCount; + + if (Members) + { + // + // If List was asked for + // + + ULONG Index; + ULONG Rid; + ORNAME * MemberORName; + DSNAME * MemberName; + PSID MemberSid; + + + + + *Members = MIDL_user_allocate(*MemberCount * sizeof(PSID)); + + if (NULL==*Members) + { + Status = STATUS_NO_MEMORY; + goto Error; + } + + // + // Zero the Array for Error cleanups + // + + RtlZeroMemory(*Members,*MemberCount * sizeof(PSID)); + + // + // Loop through each entry looking at the Sids + // + + for(Index = 0; Index<*MemberCount;Index ++) + { + + MemberORName = (ORNAME *) AttrsRead.pAttr->AttrVal.pAVal[Index].pVal; + MemberName = (DSNAME *) ((UCHAR *) MemberORName + + (ULONG) MemberORName->DN.pDN); + MemberSid = SampDsGetObjectSid(MemberName); + if (NULL==MemberSid) + { + // + // Sid should exist for Member + // + + ASSERT(FALSE); + Status = STATUS_INTERNAL_ERROR; + goto Error; + } + + // + // Alloc Space for the Sid + // + + (*Members)[Index] = MIDL_user_allocate( + RtlLengthSid(MemberSid) + ); + if (NULL==((*Members)[Index])) + { + Status = STATUS_NO_MEMORY; + goto Error; + } + + // + // Copy the Sid + // + + RtlCopyMemory( + (*Members)[Index], + MemberSid, + RtlLengthSid(MemberSid) + ); + + + } + } + } + + +Error: + + if (!NT_SUCCESS(Status)) + { + // + // Set Error Return + // + + if (*Members) + { + ULONG Index; + + for(Index = 0; Index<*MemberCount;Index ++) + { + // + // Free any allocated Sids + // + + if ((*Members)[Index]) + MIDL_user_free((*Members)[Index]); + } + + MIDL_user_free(*Members); + *Members = NULL; + *MemberCount = 0; + } + } + + return Status; +} + + + + diff --git a/private/newsam2/server/dsmember.h b/private/newsam2/server/dsmember.h new file mode 100644 index 000000000..afb5be506 --- /dev/null +++ b/private/newsam2/server/dsmember.h @@ -0,0 +1,65 @@ +/*++ +Copyright (c) 1996 Microsoft Corporation + +Module Name: + + dsmember.h + +Abstract: + + Header File for SAM private API Routines that manipulate + membership related things in the DS. + +Author: + MURLIS + +Revision History + + 7-2-96 Murlis Created + +--*/ + + +NTSTATUS +SampDsGetAliasMembershipOfAccount( + IN DSNAME * DomainObjectName, + IN PSID AccountSid, + OUT PULONG MemberCount OPTIONAL, + IN OUT PULONG BufferSize OPTIONAL, + OUT PULONG Buffer OPTIONAL + ); + + +NTSTATUS +SampDsAddMembershipAttribute( + IN DSNAME * GroupObjectName, + IN SAMP_OBJECT_TYPE SamObjectType, + IN DSNAME * MemberName + ); + +NTSTATUS +SampDsRemoveMembershipAttribute( + IN DSNAME * GroupObjectName, + IN SAMP_OBJECT_TYPE SamObjectType, + IN DSNAME * MemberName + ); + + + +NTSTATUS +SampDsGetGroupMembershipList( + IN DSNAME * GroupName, + IN PULONG *Members OPTIONAL, + IN PULONG MemberCount + ); + +NTSTATUS +SampDsGetAliasMembershipList( + IN DSNAME *AliasName, + IN PULONG MemberCount, + IN PSID **Members OPTIONAL + ); + + + + diff --git a/private/newsam2/server/dsutil.c b/private/newsam2/server/dsutil.c new file mode 100644 index 000000000..d41172aed --- /dev/null +++ b/private/newsam2/server/dsutil.c @@ -0,0 +1,3541 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + dsutil.c + +Abstract: + + This file contains helper routines for accessing and manipulating data + based on the DS backing store. Included, are routines for converting be- + tween the SAM data format (BLOBs) and the DS data format (ATTRBLOCKs). + + NOTE: The routines in this file have direct knowledge of the SAM fixed- + length attribute and variable-length attribute structures, as well as the + DS ATTRBLOCK structure. Any changes to these structures, including: + + -addition/deletion of a structure member + -data type/size change of a structure member + -reordering of the data members + -renaming of the data members + + will break these routines. SAM attributes are accessed via byte buffer + offsets and lengths, rather than by identifier or by explicit structure + data members. Because of this, changes to the structure layout will lead + to failures in SAM operation. + + Several of the routines have been written assuming that the order of the + attributes passed in via an ATTRBLOCK are exactly the order in which SAM + understands its own buffer layout. If the attributes are passed into the + routines (that take ATTRBLOCKs) out of order, the data in the SAM buffers + will be invalid. + +Author: + + Chris Mayhall (ChrisMay) 09-May-1996 + +Environment: + + User Mode - Win32 + +Revision History: + + ChrisMay 09-May-1996 + Created initial file, DS ATTRBLOCK-SAM buffer conversion routines for + variable-length attributes. + ChrisMay 14-May-1996 + DS ATTRBLOCK-SAM buffer conversion routines for fixed-length attri- + butes. + ChrisMay 22-May-1996 + Added DWORD_ALIGN macro to align data on DWORD boundaries. Fixed + alignment problems on MIPS in SampExtractAttributeFromDsAttr routine. + ChrisMay 30-May-1996 + Added routines to convert SAM combined-buffer attributes to/from DS + ATTRBLOCKs. Revised fixed-length routines to do explicit structure + member assignment instead of attempting to compute structure offsets. + ChrisMay 18-Jun-1996 + Updated fixed-attribute tables to reflect recent changes in mappings.c + and mappings.h, and DS schema. Added code to coerce the data sizes of + USHORT and BOOLEAN to DS integer data type (4 bytes) so that the DS + modify entry routines don't AV. Correctly set attribute type for the + variable-length attributes. + ChrisMay 25-Jun-1996 + Added RtlZeroMemory calls where they were missing. + ColinBr 18-Jul-1996 + Fixed array overwrite and assigned type to variable length + attributes when combining fixed and variable length attrs + into one. + + +--*/ + +#include <samsrvp.h> +#include <dsutilp.h> +#include <mappings.h> +#include <objids.h> + +// Private debugging display routine is enabled when DSUTIL_DBG_PRINTF = 1. + +#define DSUTIL_DBG_PRINTF 0 + +#if (DSUTIL_DBG_PRINTF == 1) +#define DebugPrint printf +#else +#define DebugPrint +#endif + +// DWORD_ALIGN is used to adjust pointer offsets up to the next DWORD boundary +// during the construction of SAM blob buffers. + +#define DWORD_ALIGN(value) (((DWORD)(value) + 3) & ~3) + +// Because it is apparently difficult for the DS to support NT data types of +// USHORT, UCHAR, and BOOLEAN (and which are used by SAM), these crappy data +// types have been defined for the SampFixedAttributeInfo table so that four- +// byte quantities are used. These four-byte quantities correspond to the DS +// "integer" data type (for how long?) which is used for storing certain SAM +// attributes. Note that it is important to zero out any memory allocated w/ +// these data sizes, since only the lower couple of bytes actually contain +// data. Enjoy...and refer to the DS schema(.hlp file) for the ultimate word +// on the currently used DS data types. + +#define DS_USHORT ULONG +#define DS_UCHAR ULONG +#define DS_BOOLEAN ULONG + +// This type-information table is used by the routines that convert between +// SAM fixed-length buffers and DS ATTRBLOCKs. The table contains information +// about the data type and size (but may contain any suitable information that +// is needed in the future) of the fixed-length attributes. NOTE: the layout +// of this table corresponds to the data members of the fixed-length struct- +// ures (in samsrvp.h), hence, any changes to those structures must be re- +// flected in the type-information table. + +SAMP_FIXED_ATTRIBUTE_TYPE_INFO + SampFixedAttributeInfo[SAMP_OBJECT_TYPES_MAX][SAMP_FIXED_ATTRIBUTES_MAX] = +{ + // The initialization values of this table must strictly match the set + // and order of the data members in the SAM fixed-attribute structures, + // contained in samsrvp.h. + + // The routines that manipulate this table assume that the fixed-length + // attributes, unlike the variable-length counterparts, are single valued + // attributes (i.e. are not multi-valued attributes). + + // The first column of each element in the table is a type identifier, as + // defined in mappings.c. This is used to map the SAM data type into the + // equivalent DS data type. The second column of each table element is the + // actual (C-defined) size of the element and is used throughout the data + // conversion routines in this file in order to allocate memory or set + // offset information correctly. + + // SampServerObjectType + + { + {SAMP_FIXED_SERVER_REVISION_LEVEL, sizeof(ULONG)} + }, + + // SampDomainObjectType + + { + {SAMP_FIXED_DOMAIN_REVISION_LEVEL, sizeof(ULONG)}, + {SAMP_FIXED_DOMAIN_UNUSED1, sizeof(ULONG)}, + {SAMP_FIXED_DOMAIN_CREATION_TIME, sizeof(LARGE_INTEGER)}, + {SAMP_FIXED_DOMAIN_MODIFIED_COUNT, sizeof(LARGE_INTEGER)}, + {SAMP_FIXED_DOMAIN_MAX_PASSWORD_AGE, sizeof(LARGE_INTEGER)}, + {SAMP_FIXED_DOMAIN_MIN_PASSWORD_AGE, sizeof(LARGE_INTEGER)}, + {SAMP_FIXED_DOMAIN_FORCE_LOGOFF, sizeof(LARGE_INTEGER)}, + {SAMP_FIXED_DOMAIN_LOCKOUT_DURATION, sizeof(LARGE_INTEGER)}, + {SAMP_FIXED_DOMAIN_LOCKOUT_OBSERVATION_WINDOW, sizeof(LARGE_INTEGER)}, + {SAMP_FIXED_DOMAIN_MODCOUNT_LAST_PROMOTION, sizeof(LARGE_INTEGER)}, + {SAMP_FIXED_DOMAIN_NEXT_RID, sizeof(ULONG)}, + {SAMP_FIXED_DOMAIN_PWD_PROPERTIES, sizeof(ULONG)}, + {SAMP_FIXED_DOMAIN_MIN_PASSWORD_LENGTH, sizeof(DS_USHORT)}, + {SAMP_FIXED_DOMAIN_PASSWORD_HISTORY_LENGTH, sizeof(DS_USHORT)}, + {SAMP_FIXED_DOMAIN_LOCKOUT_THRESHOLD, sizeof(DS_USHORT)}, + {SAMP_FIXED_DOMAIN_SERVER_STATE, sizeof(DOMAIN_SERVER_ENABLE_STATE)}, + {SAMP_FIXED_DOMAIN_SERVER_ROLE, sizeof(DOMAIN_SERVER_ROLE)}, + {SAMP_FIXED_DOMAIN_UAS_COMPAT_REQUIRED, sizeof(DS_BOOLEAN)} + }, + + // SampGroupObjectType + + { + {SAMP_FIXED_GROUP_REVISION_LEVEL, sizeof(ULONG)}, + {SAMP_FIXED_GROUP_RID, sizeof(ULONG)}, + {SAMP_FIXED_GROUP_ATTRIBUTES, sizeof(ULONG)}, + {SAMP_FIXED_GROUP_UNUSED1, sizeof(ULONG)}, + {SAMP_FIXED_GROUP_ADMIN_COUNT, sizeof(DS_UCHAR)}, + {SAMP_FIXED_GROUP_OPERATOR_COUNT, sizeof(DS_UCHAR)} + }, + + // SampAliasObjectType + + { + {SAMP_FIXED_ALIAS_RID, sizeof(ULONG)} + }, + + // SampUserObjectType + + { + {SAMP_FIXED_USER_REVISION_LEVEL, sizeof(ULONG)}, + {SAMP_FIXED_USER_UNUSED1, sizeof(ULONG)}, + {SAMP_FIXED_USER_LAST_LOGON, sizeof(LARGE_INTEGER)}, + {SAMP_FIXED_USER_LAST_LOGOFF, sizeof(LARGE_INTEGER)}, + {SAMP_FIXED_USER_PWD_LAST_SET, sizeof(LARGE_INTEGER)}, + {SAMP_FIXED_USER_ACCOUNT_EXPIRES, sizeof(LARGE_INTEGER)}, + {SAMP_FIXED_USER_LAST_BAD_PASSWORD_TIME, sizeof(LARGE_INTEGER)}, + {SAMP_FIXED_USER_USERID, sizeof(ULONG)}, + {SAMP_FIXED_USER_PRIMARY_GROUP_ID, sizeof(ULONG)}, + {SAMP_FIXED_USER_ACCOUNT_CONTROL, sizeof(ULONG)}, + {SAMP_FIXED_USER_COUNTRY_CODE, sizeof(DS_USHORT)}, + {SAMP_FIXED_USER_CODEPAGE, sizeof(DS_USHORT)}, + {SAMP_FIXED_USER_BAD_PWD_COUNT, sizeof(DS_USHORT)}, + {SAMP_FIXED_USER_LOGON_COUNT, sizeof(DS_USHORT)}, + {SAMP_FIXED_USER_ADMIN_COUNT, sizeof(DS_USHORT)}, + {SAMP_FIXED_USER_UNUSED2, sizeof(DS_USHORT)}, + {SAMP_FIXED_USER_OPERATOR_COUNT, sizeof(DS_USHORT)} + } +}; + + + +SAMP_VAR_ATTRIBUTE_TYPE_INFO + SampVarAttributeInfo[SAMP_OBJECT_TYPES_MAX][SAMP_VAR_ATTRIBUTES_MAX] = +{ + // The initialization values of this table must strictly match the set + // and order of the data members in the SAM variable-attributes, defined + // in samsrvp.h. Size is not defined here, because SAM variable-length + // attributes store attribute length explicity. Refer to mappings.c and + // mappings.h for the definitions used for the data types in this table. + + // SampServerObjectType + + { + {SAMP_SERVER_SECURITY_DESCRIPTOR} + }, + + // SampDomainObjectType + + { + {SAMP_DOMAIN_SECURITY_DESCRIPTOR}, + {SAMP_DOMAIN_SID}, + {SAMP_DOMAIN_OEM_INFORMATION}, + {SAMP_DOMAIN_REPLICA} + }, + + // SampGroupObjectType + + { + {SAMP_GROUP_SECURITY_DESCRIPTOR}, + {SAMP_GROUP_NAME}, + {SAMP_GROUP_ADMIN_COMMENT}, + {SAMP_GROUP_MEMBERS} + }, + + // SampAliasObjectType + + { + {SAMP_ALIAS_SECURITY_DESCRIPTOR}, + {SAMP_ALIAS_NAME}, + {SAMP_ALIAS_ADMIN_COMMENT}, + {SAMP_ALIAS_MEMBERS} + }, + + // SampUserObjectType + + { + {SAMP_USER_SECURITY_DESCRIPTOR}, + {SAMP_USER_ACCOUNT_NAME}, + {SAMP_USER_FULL_NAME}, + {SAMP_USER_ADMIN_COMMENT}, + {SAMP_USER_USER_COMMENT}, + {SAMP_USER_PARAMETERS}, + {SAMP_USER_HOME_DIRECTORY}, + {SAMP_USER_HOME_DIRECTORY_DRIVE}, + {SAMP_USER_SCRIPT_PATH}, + {SAMP_USER_PROFILE_PATH}, + {SAMP_USER_WORKSTATIONS}, + {SAMP_USER_LOGON_HOURS}, + {SAMP_USER_GROUPS}, + {SAMP_USER_DBCS_PWD}, + {SAMP_USER_UNICODE_PWD}, + {SAMP_USER_NT_PWD_HISTORY}, + {SAMP_USER_LM_PWD_HISTORY} + } +}; + + + +// +// MISCELLANEOUS HELPER ROUTINES +// + +NTSTATUS +SampFreeSamAttributes( + IN PSAMP_VARIABLE_LENGTH_ATTRIBUTE SamAttributes + ) + +/*++ + +Routine Description: + + (Under development) + +Arguments: + + + +Return Value: + + +--*/ + +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + + SAMTRACE("SampFreeSamAttributes"); + + return(NtStatus); +} + + + +NTSTATUS +SampReallocateBuffer( + IN ULONG OldLength, + IN ULONG NewLength, + IN OUT PVOID *Buffer + ) + +/*++ + +Routine Description: + + This routine resizes an in-memory buffer. The routine can either grow or + shrink the buffer based on specified lengths. Data is preserved from old + to new buffers, truncating if the new buffer is shorter than the actual + data length. The newly allocated buffer is returned as an out parameter, + the passed in buffer is released for the caller. + +Arguments: + + OldLength - Length of the buffer passed into the routine. + + NewLength - Length of the re-allocated buffer. + + Buffer - Pointer, incoming buffer to resize, outgoing new buffer. + +Return Value: + + STATUS_SUCCESS - Buffer header block allocated and initialized. + + Other codes indicating the nature of the failure. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + PVOID BufferTmp = NULL; + + SAMTRACE("SampReallocateBuffer"); + + if ((NULL != Buffer) && + (NULL != *Buffer) && + (0 < OldLength) && + (0 < NewLength)) + { + // Allocate a new buffer and set the temporary variable. Note that + // the routine does not destroy the old buffer if there is any kind + // of failure along the way. + + BufferTmp = RtlAllocateHeap(RtlProcessHeap(), 0, NewLength); + + if (NULL != BufferTmp) + { + RtlZeroMemory(BufferTmp, NewLength); + + // Copy the original buffer into the new one, truncating data if + // the new buffer is shorter than the original data size. + + if (OldLength < NewLength) + { + RtlCopyMemory(BufferTmp, *Buffer, OldLength); + } + else + { + RtlCopyMemory(BufferTmp, *Buffer, NewLength); + } + + // If all has worked, delete the old buffer and set the outgoing + // buffer pointer. + + RtlFreeHeap(RtlProcessHeap(), 0, *Buffer); + *Buffer = BufferTmp; + + NtStatus = STATUS_SUCCESS; + } + else + { + NtStatus = STATUS_NO_MEMORY; + } + } + else + { + NtStatus = STATUS_INVALID_PARAMETER; + } + + return(NtStatus); +} + + + +// +// ATTRBLOCK-TO-VARIABLE LENGTH CONVERSION ROUTINES +// + +NTSTATUS +SampInitializeVarLengthAttributeBuffer( + IN ULONG AttributeCount, + OUT PULONG BufferLength, + OUT PSAMP_VARIABLE_LENGTH_ATTRIBUTE *SamAttributes + ) + +/*++ + +Routine Description: + + This routine sets up the SAM attribute buffer that is the destination for + attributes read from the DS backing store. The buffer contains a header, + followed by variable-length attributes (SAMP_VARIABLE_LENGTH_ATTRIBUTE). + + This routine allocates memory for the buffer header and zeros it out. + +Arguments: + + AttributeCount - Number of variable-length attributes. + + BufferLength - Pointer, buffer size allocated by this routine. + + SamAttributes - Pointer, returned buffer. + +Return Value: + + STATUS_SUCCESS - Buffer header block allocated and initialized. + + Other codes indicating the nature of the failure. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + ULONG Length = 0; + + SAMTRACE("SampInitializeVarLengthAttributeBuffer"); + + if (0 < AttributeCount) + { + // Calculate the space needed for the attribute-offset array. If the + // attribute count is zero, skip the allocation and return an error. + + Length = AttributeCount * sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE); + + if (NULL != SamAttributes) + { + *SamAttributes = RtlAllocateHeap(RtlProcessHeap(), 0, Length); + + if (NULL != *SamAttributes) + { + // Initialize the block and return the updated buffer offset, + // which now points to the last byte of the header block. + + RtlZeroMemory(*SamAttributes, Length); + + if (NULL != BufferLength) + { + *BufferLength = Length; + NtStatus = STATUS_SUCCESS; + } + } + else + { + NtStatus = STATUS_NO_MEMORY; + } + } + } + + return(NtStatus); +} + + + +NTSTATUS +SampExtractAttributeFromDsAttr( + IN PDSATTR Attribute, + OUT PULONG MultiValuedCount, + OUT PULONG Length, + OUT PVOID *Buffer + ) + +/*++ + +Routine Description: + + This routine determines whether or not the current attribute is single- + valued or multi-valued and returns a buffer containing the value(s) of + the attribute. If the attribute is multi-valued, the values are appended + in the buffer. + +Arguments: + + Attribute - Pointer, incoming DS attribute structure. + + MultiValuedCount - Pointer, returned count of the number of values found + for this attribute. + + Length - Pointer, returned buffer length. + + Buffer - Pointer, returned buffer containing one or more values. + +Return Value: + + STATUS_SUCCESS - Buffer header block allocated and initialized. + + Other codes indicating the nature of the failure. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + ULONG ValueCount = 0; + PDSATTRVALBLOCK ValueBlock; + PDSATTRVAL Values = NULL; + ULONG ValueIndex = 0; + ULONG TotalLength = 0; + ULONG Offset = 0; + + SAMTRACE("SampExtractAttributeFromDsAttr"); + + // Get the count of attributes and a pointer to the attribute. Note that + // it is possible to have multi-valued attributes, in which case they are + // appended onto the end of the return buffer. + + if (NULL != Attribute) + { + // DSATTR structure contains: attrTyp, AttrVal + + ValueBlock = &(Attribute->AttrVal); + + // DSATTRVALBLOCK structure contains: valCount, pAVal + + ValueCount = ValueBlock->valCount; + Values = ValueBlock->pAVal; + + if ((0 < ValueCount) && (NULL != Values)) + { + // Multi-valued attribute processing; first determine the total + // buffer length that will be needed. + + for (ValueIndex = 0; ValueIndex < ValueCount; ValueIndex++) + { + // Determine total length needed for this attribute. Because + // the value lengths may not be DWORD size, pad up to the + // next DWORD size. + + TotalLength += DWORD_ALIGN(Values[ValueIndex].valLen); + } + } + + if ((0 < TotalLength) && (NULL != Buffer)) + { + // Allocate the buffer for the attributes. + + *Buffer = RtlAllocateHeap(RtlProcessHeap(), 0, TotalLength); + + if (NULL != *Buffer) + { + RtlZeroMemory(*Buffer, TotalLength); + + for (ValueIndex = 0; + ValueIndex < ValueCount; + ValueIndex++) + { + // DSATTRVAL structure contains: valLen, pVal. Append + // subsequent values onto the end of the buffer, up- + // dating the end-of-buffer offset each time. + + RtlCopyMemory((*(BYTE **)Buffer + Offset), + (PBYTE)(Values[ValueIndex].pVal), + Values[ValueIndex].valLen); + + // Adjust the offset up to the next DWORD boundary. + + Offset += DWORD_ALIGN(Values[ValueIndex].valLen); + } + + if ((NULL != MultiValuedCount) && (NULL != Length)) + { + // Finished, update return values. + + *MultiValuedCount = ValueCount; + *Length = TotalLength; + NtStatus = STATUS_SUCCESS; + } + } + else + { + NtStatus = STATUS_NO_MEMORY; + } + } + } + + return(NtStatus); +} + + + +NTSTATUS +SampVerifyVarLengthAttribute( + IN INT ObjectType, + IN ULONG AttrIndex, + IN ULONG MultiValuedCount, + IN ULONG AttributeLength + ) + +/*++ + +Routine Description: + + This routine is under construction. + +Arguments: + + +Return Value: + + STATUS_SUCCESS - Buffer header block allocated and initialized. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + + SAMTRACE("SampVerifyVarLengthAttribute"); + + // BUG: Define a table of variable-length attribute information. + + switch(ObjectType) + { + + // For each SAM object type, verify that attributes that are supposed to + // be single valued, have a MultiValueCount of 1 (multi-valued attributes + // can have a count greater-than or equal to 1). + + case SampServerObjectType: + + if (1 == MultiValuedCount) + { + NtStatus = STATUS_SUCCESS; + } + + break; + + case SampDomainObjectType: + + if (1 == MultiValuedCount) + { + NtStatus = STATUS_SUCCESS; + } + + break; + + case SampGroupObjectType: + + // Multi-valued attribute + + if ((SAMP_GROUP_MEMBERS != AttrIndex)) + { + if (1 == MultiValuedCount) + { + NtStatus = STATUS_SUCCESS; + } + } + + break; + + case SampAliasObjectType: + + // Multi-valued attribute + + if ((SAMP_ALIAS_MEMBERS != AttrIndex)) + { + if (1 == MultiValuedCount) + { + NtStatus = STATUS_SUCCESS; + } + } + + break; + + case SampUserObjectType: + + // Multi-valued attributes + + if ((SAMP_ALIAS_MEMBERS != AttrIndex) && + (SAMP_USER_LOGON_HOURS != AttrIndex)) + { + if (1 == MultiValuedCount) + { + NtStatus = STATUS_SUCCESS; + } + } + + break; + + default: + + break; + + } + + NtStatus = STATUS_SUCCESS; + + return(NtStatus); +} + + + +NTSTATUS +SampAppendVarLengthAttributeToBuffer( + IN ULONG AttrIndex, + IN PVOID NewAttribute, + IN ULONG MultiValuedCount, + IN ULONG AttributeLength, + IN OUT PULONG BufferLength, + IN OUT PSAMP_VARIABLE_LENGTH_ATTRIBUTE *SamAttributes + ) + +/*++ + +Routine Description: + + This routine appends the current attribute onto the end of the attribute + buffer, and updates the SAMP_VARIABLE_LENGTH_DATA structures in the head- + er of the buffer with new offset, length, and qualifier information. + +Arguments: + + AttrIndex - Index into the array of variable-length offsets. + + NewAttribute - Pointer, the new attribute to be appended to the buffer. + + MultiValuedCount - Number of values for the attribute. + + AttributeLength - Number of bytes of the attribute. + + BufferLength - Pointer, incoming contains the current length of the buf- + fer; outgoing contains the updated length after appending the latest + attribute. + + SamAttributes - Pointer, SAMP_VARIABLE_LENGTH_ATTRIBUTE buffer. + +Return Value: + + STATUS_SUCCESS - Buffer header block allocated and initialized. + + Other codes indicating the nature of the failure. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + ULONG NewLength = 0; + + SAMTRACE("SampAppendVarLengthAttributeToBuffer"); + + if ((NULL != BufferLength) && (0 < AttributeLength)) + { + // Compute the required buffer length needed to append the attribute. + + NewLength = *BufferLength + AttributeLength; + + if (0 < NewLength) + { + // Adjust buffer size for the attribute. + + NtStatus = SampReallocateBuffer(*BufferLength, + NewLength, + SamAttributes); + } + + if (NT_SUCCESS(NtStatus)) + { + // Append the attribute onto the return buffer. + + RtlCopyMemory((((PBYTE)(*SamAttributes)) + *BufferLength), + NewAttribute, + AttributeLength); + + // Update the variable-length header information for the latest + // attribute. + + (*SamAttributes + AttrIndex)->Offset = *BufferLength; + (*SamAttributes + AttrIndex)->Length = AttributeLength; + + // BUG: Assuming that Qualifier is used for multi-value count? + + (*SamAttributes + AttrIndex)->Qualifier = MultiValuedCount; + + // Pass back the updated buffer length. + + *BufferLength = NewLength; + + DebugPrint("BufferLength = %lu\n", *BufferLength); + DebugPrint("NewLength = %lu\n", NewLength); + DebugPrint("SamAttributes Offset = %lu\n", (*SamAttributes + AttrIndex)->Offset); + DebugPrint("SamAttributes Length = %lu\n", (*SamAttributes + AttrIndex)->Length); + DebugPrint("SamAttributes Qualifier = %lu\n", (*SamAttributes + AttrIndex)->Qualifier); + } + } + + return(NtStatus); +} + + + +NTSTATUS +SampConvertAttrBlockToVarLengthAttributes( + IN INT ObjectType, + IN PDSATTRBLOCK DsAttributes, + OUT PSAMP_VARIABLE_LENGTH_ATTRIBUTE *SamAttributes, + OUT PULONG TotalLength + ) + +/*++ + +Routine Description: + + This routine extracts the DS attributes from a DS READRES structure and + builds a SAMP_VARIABLE_LENGTH_BUFFER with them. This routine allocates + the necessary memory block for the SAM variable-length attribute buffer. + + This routine assumes that the attributes passed in via the READRES struc- + ture are in the correct order (as known to SAM). + +Arguments: + + ObjectType - SAM object type identifier (this parameter is currently un- + used, but will likely be used to set the maximum number of attributes + for any given conversion). + + DsAttributes - Pointer, DS attribute list. + + SamAttributes - Pointer, returned SAM variable-length attribute buffer. + +Return Value: + + STATUS_SUCCESS - The object has been successfully accessed. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + ULONG AttributeCount = 0; + PDSATTR Attributes = NULL; + ULONG BufferLength = 0; + ULONG AttrIndex = 0; + ULONG AttributeLength = 0; + ULONG MultiValuedCount = 0; + PVOID Attribute = NULL; + + SAMTRACE("SampConvertAttrBlockToVarLengthAttributes"); + + if ((NULL != DsAttributes) && (NULL != SamAttributes)) + { + // Get the attribute count and a pointer to the attributes. + + // DSATTRBLOCK contains: attrCount, pAttr + + // BUG: ObjectType can be used to get the compile-time attr count. + // Obtaining the attribute count from DsAttributes may be erroron- + // eous, hence ObjectType could be used instead to set the count to + // the constants that define the maximum number of attributes. + + AttributeCount = DsAttributes->attrCount; + Attributes = DsAttributes->pAttr; + + if ((0 < AttributeCount) && (NULL != Attributes)) + { + // Set up the variable-length attribute buffer header based on the + // number of attributes. Allocate and initialize the SamAttributes + // buffer. Update BufferLength to reflect the new size. + + NtStatus = SampInitializeVarLengthAttributeBuffer( + AttributeCount, + &BufferLength, + SamAttributes); + } + } + else + { + NtStatus = STATUS_INVALID_PARAMETER; + } + + if (NT_SUCCESS(NtStatus)) + { + // For each attribute, get its value (or values in the case of multi- + // valued attributes). + + for (AttrIndex = 0; AttrIndex < AttributeCount; AttrIndex++) + { + // A given attribute may be multi-valued, in which case multiple + // values are simply concatenated together. MultiValuedCount will + // contain the number of values for the attribute. + + NtStatus = SampExtractAttributeFromDsAttr( + &(Attributes[AttrIndex]), + &MultiValuedCount, + &AttributeLength, + &Attribute); + + // Verify that the DS has returned SAM attributes correctly. Check + // such things as attribute length, single vs. multi-value status. + + NtStatus = SampVerifyVarLengthAttribute(ObjectType, + AttrIndex, + MultiValuedCount, + AttributeLength); + + if (NT_SUCCESS(NtStatus)) + { + // Append the current attribute onto the end of the SAM vari- + // able length attribute buffer and update the offset array. + + // AttrIndex is not only the loop counter, but is also the + // index into the proper element of the variable-length attr- + // ibute array. NOTE: This routine assumes that the order in + // which the elements were returned in the READRES buffer is + // in fact the correct order of the SAM attributes as defined + // in samsrvp.h + + NtStatus = SampAppendVarLengthAttributeToBuffer( + AttrIndex, + Attribute, + MultiValuedCount, + AttributeLength, + &BufferLength, + SamAttributes); + + } + + if (!NT_SUCCESS(NtStatus)) + { + // Detect failure of either routine and break for return. Let + // the caller release the memory that is returned. + + break; + } + } + + if (NULL != TotalLength) + { + *TotalLength = BufferLength; + } + else + { + NtStatus = STATUS_INVALID_PARAMETER; + } + } + + return(NtStatus); +} + + + +// +// VARIABLE LENGTH-TO-ATTRBLOCK CONVERSION ROUTINES +// + +BOOLEAN +SampIsQualifierTheCount( + IN INT ObjectType, + IN ULONG AttrIndex + ) +{ + BOOLEAN IsCount = FALSE; + + SAMTRACE("SampIsQualifierTheCount"); + + switch(ObjectType) + { + + case SampServerObjectType: + + IsCount = FALSE; + + break; + + case SampDomainObjectType: + + IsCount = FALSE; + + break; + + case SampGroupObjectType: + + // Multi-valued attribute + + if ((SAMP_GROUP_MEMBERS == AttrIndex)) + { + IsCount = TRUE; + } + + break; + + case SampAliasObjectType: + + // Multi-valued attribute + + if ((SAMP_ALIAS_MEMBERS == AttrIndex)) + { + IsCount = TRUE; + } + + break; + + case SampUserObjectType: + + // Multi-valued attributes + + if ((SAMP_ALIAS_MEMBERS == AttrIndex) || + (SAMP_USER_LOGON_HOURS == AttrIndex)) + { + IsCount = TRUE; + } + + break; + + default: + + // Error + + break; + + } + + return(IsCount); + +} + + + +NTSTATUS +SampConvertAndAppendAttribute( + IN INT ObjectType, + IN ULONG AttrIndex, + IN PSAMP_VARIABLE_LENGTH_ATTRIBUTE SamAttributes, + OUT PDSATTR Attributes + ) + +/*++ + +Routine Description: + + This routine does the work of converting a variable-length attribute from + a SAM buffer into a DS attribute. A DSATTR structure is constructed and + passed back from this routine to the caller. + +Arguments: + + ObjectType - SAM object type identifier (this parameter is currently un- + used, but will likely be used to set the maximum number of attributes + for any given conversion). + + AttrIndex - Index into the array of the variable-length attribute inform- + ation and the DS attribute (i.e. the current attribute). + + SamAttributes - Pointer, returned SAM variable-length attribute buffer. + + Attributes - Pointer, the returned DS attribute structure. + +Return Value: + + STATUS_SUCCESS - The object has been successfully accessed. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + ULONG Offset = SamAttributes[AttrIndex].Offset; + ULONG Length = SamAttributes[AttrIndex].Length; + ULONG MultiValuedCount = SamAttributes[AttrIndex].Qualifier; + ULONG Index = 0; + PDSATTRVAL Attribute = NULL; + PBYTE Value = NULL; + + SAMTRACE("SampConvertAndAppendAttribute"); + + // Set the attribute type to the equivalent DS data type. + + Attributes[AttrIndex].attrTyp = + SampVarAttributeInfo[ObjectType][AttrIndex].Type; + + if (TRUE == SampIsQualifierTheCount(ObjectType, AttrIndex)) + { + // Qualifier contains the attribute's multi-value count. + + Attributes[AttrIndex].AttrVal.valCount = MultiValuedCount; + } + else + { + // BUG: Lost "Qualifier" information (e.g. REVISION) during conversion. + + // Qualifier contains something other than the count, so set valCount + // to 1 because this is a single-valued attribute. + + Attributes[AttrIndex].AttrVal.valCount = 1; + MultiValuedCount = 1; + } + + // Allocate memory for the attribute (array if multi-valued). + + Attribute = RtlAllocateHeap(RtlProcessHeap(), + 0, + (MultiValuedCount * sizeof(DSATTRVAL))); + + if (NULL != Attribute) + { + RtlZeroMemory(Attribute, (MultiValuedCount * sizeof(DSATTRVAL))); + + // Begin construction of the DSATTR structure by setting the pointer + // the to the attribute. + + Attributes[AttrIndex].AttrVal.pAVal = Attribute; + + // SAM does not store per-value length information for multi-valued + // attributes, instead the total length of all of the values of a + // single attribute is stored. + + // Length is the number of bytes in the overall attribute. If the + // attribute is multi-valued, then this length is the total length + // of all of the attribute values. The per-value allocation is equal + // to the Length divided by the number of values (because all values + // of all multi-valued attributes are a fixed size (i.e. ULONG or + // LARGE_INTEGER). + + // Test to make sure that total length is an integral multiple of the + // number of values--a sanity check. + + if (0 == (Length % MultiValuedCount)) + { + Length = (Length / MultiValuedCount); + } + else + { + // The length is erroneous, so artificially reset to zero in order + // to terminate things. + + Length = 0; + } + + for (Index = 0; Index < MultiValuedCount; Index++) + { + // Allocate memory for the attribute data. + + Value = RtlAllocateHeap(RtlProcessHeap(), 0, Length); + + if (NULL != Value) + { + RtlZeroMemory(Value, Length); + + // For each value, in the attribute, store its length and + // copy the value into the destination buffer. + + Attribute[Index].valLen = Length; + Attribute[Index].pVal = Value; + + // Note: SamAttributes is passed in as PSAMP_VARIABLE_LENTGH- + // ATTRIBUTE, hence is explicitly cast to a byte pointer to + // do the byte-offset arithmetic correctly for RtlCopyMemory. + + RtlCopyMemory(Value, (((PBYTE)SamAttributes) + Offset), Length); + + // Adjust the SAM-buffer offset to point at the next value in + // the multi-valued attribute. + + Offset += Length; + + NtStatus = STATUS_SUCCESS; + } + else + { + NtStatus = STATUS_NO_MEMORY; + break; + } + } + } + else + { + NtStatus = STATUS_NO_MEMORY; + } + + return(NtStatus); +} + + + +NTSTATUS +SampConvertVarLengthAttributesToAttrBlock( + IN INT ObjectType, + IN PSAMP_VARIABLE_LENGTH_ATTRIBUTE SamAttributes, + OUT PDSATTRBLOCK *DsAttributes + ) + +/*++ + +Routine Description: + + This routine determines the SAM object type so that the attribute count + can be set, and then performs the attribute conversion. This routine al- + locates the top-level DS structure and then calls a helper routine to + fill in the rest of the data. + +Arguments: + + ObjectType - SAM object type identifier (this parameter is currently un- + used, but will likely be used to set the maximum number of attributes + for any given conversion). + + SamAttributes - Pointer, returned SAM variable-length attribute buffer. + + DsAttributes - Pointer, the returned DS attribute structure. + +Return Value: + + STATUS_SUCCESS - The object has been successfully accessed. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + ULONG AttributeCount = 0; + PDSATTR Attributes = NULL; + PVOID Attribute = NULL; + ULONG AttrIndex = 0; + ULONG Length = 0; + ULONG Qualifier = 0; + + SAMTRACE("SampConvertVarLengthAttributesToAttrBlock"); + + if (NULL != DsAttributes) + { + // Allocate the top-level structure. + + *DsAttributes = RtlAllocateHeap(RtlProcessHeap(), + 0, + sizeof(DSATTRBLOCK)); + + if ((NULL != SamAttributes) && (NULL != *DsAttributes)) + { + RtlZeroMemory(*DsAttributes, sizeof(DSATTRBLOCK)); + + // Determine the object type, and hence set the corresponding + // attribute count. + + switch(ObjectType) + { + + case SampServerObjectType: + + AttributeCount = SAMP_SERVER_VARIABLE_ATTRIBUTES; + break; + + case SampDomainObjectType: + + AttributeCount = SAMP_DOMAIN_VARIABLE_ATTRIBUTES; + break; + + case SampGroupObjectType: + + AttributeCount = SAMP_GROUP_VARIABLE_ATTRIBUTES; + break; + + case SampAliasObjectType: + + AttributeCount = SAMP_ALIAS_VARIABLE_ATTRIBUTES; + break; + + case SampUserObjectType: + + AttributeCount = SAMP_USER_VARIABLE_ATTRIBUTES; + break; + + default: + + AttributeCount = 0; + break; + + } + + DebugPrint("AttributeCount = %lu\n", AttributeCount); + + // Allocate the array of DS attribute-information structs. + + Attributes = RtlAllocateHeap(RtlProcessHeap(), + 0, + (AttributeCount * sizeof(DSATTR))); + + if (NULL != Attributes) + { + RtlZeroMemory(Attributes, (AttributeCount * sizeof(DSATTR))); + + (*DsAttributes)->attrCount = AttributeCount; + (*DsAttributes)->pAttr = Attributes; + + // Walk through the array of attributes, converting each + // SAM variable-length attribute to a DS attribute. Refer to + // the DS header files (core.h, drs.h) for definitions of + // these structures. + + for (AttrIndex = 0; AttrIndex < AttributeCount; AttrIndex++) + { + NtStatus = SampConvertAndAppendAttribute(ObjectType, + AttrIndex, + SamAttributes, + Attributes); + + if (!NT_SUCCESS(NtStatus)) + { + break; + } + + DebugPrint("attrCount = %lu\n", (*DsAttributes)->attrCount); + DebugPrint("attrTyp = %lu\n", (*DsAttributes)->pAttr[AttrIndex].attrTyp); + DebugPrint("valCount = %lu\n", (*DsAttributes)->pAttr[AttrIndex].AttrVal.valCount); + DebugPrint("valLen = %lu\n", (*DsAttributes)->pAttr[AttrIndex].AttrVal.pAVal->valLen); + } + } + } + } + + return(NtStatus); +} + + + +// +// ATTRBLOCK-TO-FIXED LENGTH CONVERSION ROUTINES +// + +NTSTATUS +SampExtractFixedLengthAttributeFromDsAttr( + IN PDSATTR Attribute, + OUT PULONG MultiValuedCount, + OUT PULONG Length, + OUT PVOID *Buffer + ) + +/*++ + +Routine Description: + + This routine determines whether or not the current attribute is single- + valued or multi-valued and returns a buffer containing the value(s) of + the attribute. If the attribute is multi-valued, the values are appended + in the buffer. + +Arguments: + + Attribute - Pointer, incoming DS attribute structure. + + MultiValuedCount - Pointer, returned count of the number of values found + for this attribute. + + Length - Pointer, returned buffer length. + + Buffer - Pointer, returned buffer containing one or more values. + +Return Value: + + STATUS_SUCCESS - Buffer header block allocated and initialized. + + Other codes indicating the nature of the failure. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + ULONG ValueCount = 0; + PDSATTRVALBLOCK ValueBlock; + PDSATTRVAL Values = NULL; + ULONG ValueIndex = 0; + ULONG TotalLength = 0; + ULONG Offset = 0; + + SAMTRACE("SampExtractFixedLengthAttributeFromDsAttr"); + + // Get the count of attributes and a pointer to the attribute. Note that + // it is possible to have multi-valued attributes, in which case they are + // appended onto the end of the return buffer. + + if (NULL != Attribute) + { + // DSATTR structure contains: attrTyp, AttrVal + + ValueBlock = &(Attribute->AttrVal); + + // DSATTRVALBLOCK structure contains: valCount, pAVal + + if (NULL != ValueBlock) + { + ValueCount = ValueBlock->valCount; + Values = ValueBlock->pAVal; + } + + if ((0 < ValueCount) && (NULL != Values)) + { + // Multi-valued attribute processing; first determine the total + // buffer length that will be needed. + + // BUG: Use SampFixedAttrInfo for length instead? + + // BUG: Fixed Attributes are only single valued, remove loop. + + for (ValueIndex = 0; ValueIndex < ValueCount; ValueIndex++) + { + // Determine total length needed for this attribute. + + TotalLength += Values[ValueIndex].valLen; + } + } + + if ((0 < TotalLength) && (NULL != Buffer)) + { + // Allocate the buffer for the attributes. + + *Buffer = RtlAllocateHeap(RtlProcessHeap(), 0, TotalLength); + + if (NULL != *Buffer) + { + RtlZeroMemory(*Buffer, TotalLength); + + for (ValueIndex = 0; + ValueIndex < ValueCount; + ValueIndex++) + { + // DSATTRVAL structure contains: valLen, pVal. Append + // subsequent values onto the end of the buffer, up- + // dating the end-of-buffer offset each time. + + RtlCopyMemory((*(BYTE **)Buffer + Offset), + (PBYTE)(Values[ValueIndex].pVal), + Values[ValueIndex].valLen); + + Offset += Values[ValueIndex].valLen; + } + + if ((NULL != MultiValuedCount) && (NULL != Length)) + { + // Finished, update return values. + + *MultiValuedCount = ValueCount; + *Length = TotalLength; + NtStatus = STATUS_SUCCESS; + } + } + else + { + NtStatus = STATUS_NO_MEMORY; + } + } + } + + return(NtStatus); +} + + + +NTSTATUS +SampVerifyFixedLengthAttribute( + IN INT ObjectType, + IN ULONG AttrIndex, + IN ULONG MultiValuedCount, + IN ULONG AttributeLength + ) + +/*++ + +Routine Description: + + This routine verifies that the length of a given (fixed-length) attribute + obtained from the attribute information in a DSATTRBLOCK is in fact the + correct length. This check is necessary because the underlying data store + and various internal DS layers remap the SAM data types to their internal + data types, which may be a different size (e.g. BOOLEAN is mapped to INT). + Validation of the lenght is accomplished by comparing the passed-in length + to the a prior known lengths stored in the SampFixedAttributeInfo table. + + NOTE: Currently, this routine simply checks for equality, returning an + error if the two lengths are not equal. This test may need to "special + case" certain attributes as the database schema is finalized and more is + known about the underlying data types. + + +Arguments: + + ObjectType - SAM Object identifier (server, domain, etc.) index + + AttrIndex - Index into the array of fixed-length attribute length inform- + ation. + + MultiValuedCount - Number of values in a multi-valued attribute. + + AttributeLength - Attribute length (byte count) to be verified. + +Return Value: + + STATUS_SUCCESS - The object has been successfully accessed. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + + SAMTRACE("SampVerifyFixedLengthAttribute"); + + // Verify that the attribute length is correct. The AttributeLength is + // already rounded up to a DWORD boundary, so do the same for the attri- + // bute information length. + + if (AttributeLength == + (SampFixedAttributeInfo[ObjectType][AttrIndex].Length)) + { + if (1 == MultiValuedCount) + { + // Verify that the fixed-length attribute is single-valued. + + NtStatus = STATUS_SUCCESS; + } + } + else + { + DebugPrint("AttributeLength = %lu Length = %lu\n", + AttributeLength, + SampFixedAttributeInfo[ObjectType][AttrIndex].Length); + } + + + return(NtStatus); +} + + + +NTSTATUS +SampAppendFixedLengthAttributeToBuffer( + IN INT ObjectType, + IN ULONG AttrIndex, + IN PVOID NewAttribute, + IN OUT PVOID SamAttributes + ) + +/*++ + +Routine Description: + + This routine builds a SAM fixed-length attribute buffer from a correspond- + ing DS attribute by copying the data into the SAM fixed-length structure. + + Note that pointer-casts during structure member assignment are not only + needed due to the fact that NewAttribute is a PVOID, but also because the + DS uses different data types than does SAM for certain data types (e.g. + SAM USHORT is stored as a four-byte integer in the DS). Refer to the Samp- + FixedAttributeInfo table for details. The data truncation is benign in + all cases. + +Arguments: + + ObjectType - SAM object type (server, domain, etc.). + + AttrIndex - Index of the attribute to set. This value corresponds to the + elements of the various fixed-length attributes (see samsrvp.h). + + NewAttribute - The incoming attribute, extracted from the DS data. + + SamAttributes - Pointer, updated SAM attribute buffer. + +Return Value: + + STATUS_SUCCESS - The object has been successfully accessed. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_SUCCESS; + PSAMP_V1_FIXED_LENGTH_SERVER ServerAttrs = NULL; + PSAMP_V1_0A_FIXED_LENGTH_DOMAIN DomainAttrs = NULL; + PSAMP_V1_0A_FIXED_LENGTH_GROUP GroupAttrs = NULL; + PSAMP_V1_FIXED_LENGTH_ALIAS AliasAttrs = NULL; + PSAMP_V1_0A_FIXED_LENGTH_USER UserAttrs = NULL; + + SAMTRACE("SampAppendFixedLengthAttributeToBuffer"); + + if ((NULL != NewAttribute) && (NULL != SamAttributes)) + { + // BUG: Define constants for the fixed attributes cases. + + // Determine the object type, and then the attribute for that object + // to copy into the target SAM fixed-length structure. + + switch(ObjectType) + { + + case SampServerObjectType: + + ServerAttrs = SamAttributes; + + switch(AttrIndex) + { + + case 0: + ServerAttrs->RevisionLevel = *(PULONG)NewAttribute; + break; + + default: + NtStatus = STATUS_INTERNAL_ERROR; + break; + + } + + break; + + case SampDomainObjectType: + + DomainAttrs = SamAttributes; + + switch(AttrIndex) + { + + case 0: + DomainAttrs->Revision = *(PULONG)NewAttribute; + break; + + case 1: + DomainAttrs->Unused1 = *(PULONG)NewAttribute; + break; + + case 2: + DomainAttrs->CreationTime = *(PLARGE_INTEGER)NewAttribute; + break; + + case 3: + DomainAttrs->ModifiedCount = *(PLARGE_INTEGER)NewAttribute; + break; + + case 4: + DomainAttrs->MaxPasswordAge = *(PLARGE_INTEGER)NewAttribute; + break; + + case 5: + DomainAttrs->MinPasswordAge = *(PLARGE_INTEGER)NewAttribute; + break; + + case 6: + DomainAttrs->ForceLogoff = *(PLARGE_INTEGER)NewAttribute; + break; + + case 7: + DomainAttrs->LockoutDuration = *(PLARGE_INTEGER)NewAttribute; + break; + + case 8: + DomainAttrs->LockoutObservationWindow = *(PLARGE_INTEGER)NewAttribute; + break; + + case 9: + DomainAttrs->ModifiedCountAtLastPromotion = *(PLARGE_INTEGER)NewAttribute; + break; + + case 10: + DomainAttrs->NextRid = *(PULONG)NewAttribute; + break; + + case 11: + DomainAttrs->PasswordProperties = *(PULONG)NewAttribute; + break; + + case 12: + DomainAttrs->MinPasswordLength = *(PUSHORT)NewAttribute; + break; + + case 13: + DomainAttrs->PasswordHistoryLength = *(PUSHORT)NewAttribute; + break; + + case 14: + DomainAttrs->LockoutThreshold = *(PUSHORT)NewAttribute; + break; + + case 15: + DomainAttrs->ServerState = *(PULONG)NewAttribute; + break; + + case 16: + DomainAttrs->ServerRole = *(PULONG)NewAttribute; + break; + + case 17: + DomainAttrs->UasCompatibilityRequired = *(PBOOLEAN)NewAttribute; + break; + + default: + NtStatus = STATUS_INTERNAL_ERROR; + break; + + } + + break; + + case SampGroupObjectType: + + GroupAttrs = SamAttributes; + + switch(AttrIndex) + { + + case 0: + GroupAttrs->Revision = *(PULONG)NewAttribute; + break; + + case 1: + GroupAttrs->RelativeId = *(PULONG)NewAttribute; + break; + + case 2: + GroupAttrs->Attributes = *(PULONG)NewAttribute; + break; + + case 3: + GroupAttrs->Unused1 = *(PULONG)NewAttribute; + break; + + case 4: + GroupAttrs->AdminCount = *(PUCHAR)NewAttribute; + break; + + case 5: + GroupAttrs->OperatorCount = *(PUCHAR)NewAttribute; + break; + + default: + break; + + } + + break; + + case SampAliasObjectType: + + AliasAttrs = SamAttributes; + + switch(AttrIndex) + { + + case 0: + AliasAttrs->RelativeId = *(PULONG)NewAttribute; + break; + + default: + NtStatus = STATUS_INTERNAL_ERROR; + break; + + } + + break; + + case SampUserObjectType: + + UserAttrs = SamAttributes; + + switch(AttrIndex) + { + + case 0: + UserAttrs->Revision = *(PULONG)NewAttribute; + break; + + case 1: + UserAttrs->Unused1 = *(PULONG)NewAttribute; + break; + + case 2: + UserAttrs->LastLogon = *(PLARGE_INTEGER)NewAttribute; + break; + + case 3: + UserAttrs->LastLogoff = *(PLARGE_INTEGER)NewAttribute; + break; + + case 4: + UserAttrs->PasswordLastSet = *(PLARGE_INTEGER)NewAttribute; + break; + + case 5: + UserAttrs->AccountExpires = *(PLARGE_INTEGER)NewAttribute; + break; + + case 6: + UserAttrs->LastBadPasswordTime = *(PLARGE_INTEGER)NewAttribute; + break; + + case 7: + UserAttrs->UserId = *(PULONG)NewAttribute; + break; + + case 8: + UserAttrs->PrimaryGroupId = *(PULONG)NewAttribute; + break; + + case 9: + UserAttrs->UserAccountControl = *(PULONG)NewAttribute; + break; + + case 10: + UserAttrs->CountryCode = *(PUSHORT)NewAttribute; + break; + + case 11: + UserAttrs->CodePage = *(PUSHORT)NewAttribute; + break; + + case 12: + UserAttrs->BadPasswordCount = *(PUSHORT)NewAttribute; + break; + + case 13: + UserAttrs->LogonCount = *(PUSHORT)NewAttribute; + break; + + case 14: + UserAttrs->AdminCount = *(PUSHORT)NewAttribute; + break; + + case 15: + UserAttrs->Unused2 = *(PUSHORT)NewAttribute; + break; + + case 16: + UserAttrs->OperatorCount = *(PUSHORT)NewAttribute; + break; + + default: + NtStatus = STATUS_INTERNAL_ERROR; + break; + + } + + break; + + default: + NtStatus = STATUS_INTERNAL_ERROR; + break; + + } + } + else + { + NtStatus = STATUS_INVALID_PARAMETER; + } + + return(NtStatus); +} + + + +NTSTATUS +SampConvertAttrBlockToFixedLengthAttributes( + IN INT ObjectType, + IN PDSATTRBLOCK DsAttributes, + OUT PVOID *SamAttributes, + OUT PULONG TotalLength + ) + +/*++ + +Routine Description: + + This routine converts a DS ATTRBLOCK into a SAM fixed-length buffer. The + SAM buffer that is passed back from the routine can be either treated as + a blob or can be cast to one of the SAM fixed-length attribute types for + convenience. + +Arguments: + + ObjectType - Identifies which SAM object type, and hence, which attribute + set to work with. + + DsAttributes - Pointer, incoming DS ATTRBLOCK containing fixed-length + attributes. + + SamAttributes - Pointer, updated SAM attribute buffer. + + TotalLength - Pointer, length of the SAM fixed attribute data returned. + +Return Value: + + STATUS_SUCCESS - The object has been successfully accessed. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + ULONG AttributeCount = 0; + PDSATTR Attributes = NULL; + ULONG Length = 0; + ULONG BufferLength = 0; + ULONG AttributeLength = 0; + ULONG AttrIndex = 0; + ULONG MultiValuedCount = 0; + PVOID Attribute = NULL; + + SAMTRACE("SampConvertAttrBlockToFixedLengthAttributes"); + + if ((NULL != DsAttributes) && (NULL != SamAttributes)) + { + AttributeCount = DsAttributes->attrCount; + Attributes = DsAttributes->pAttr; + + // Using the SAM object type identifer, set the length of the buffer + // to be allocated based on the fixed-length data structure. + + switch(ObjectType) + { + + case SampServerObjectType: + + Length = sizeof(SAMP_V1_FIXED_LENGTH_SERVER); + break; + + case SampDomainObjectType: + + Length = sizeof(SAMP_V1_0A_FIXED_LENGTH_DOMAIN); + break; + + case SampGroupObjectType: + + Length = sizeof(SAMP_V1_0A_FIXED_LENGTH_GROUP); + break; + + case SampAliasObjectType: + + Length = sizeof(SAMP_V1_FIXED_LENGTH_ALIAS); + break; + + case SampUserObjectType: + + Length = sizeof(SAMP_V1_0A_FIXED_LENGTH_USER); + break; + + default: + + Length = 0; + break; + } + + // Allocate space for the fixed-length attributes. + + *SamAttributes = RtlAllocateHeap(RtlProcessHeap(), 0, Length); + + if ((NULL != *SamAttributes) && (NULL != Attributes)) + { + RtlZeroMemory(*SamAttributes, Length); + + // Walk the DSATTRBLOCK, pulling out the attributes and returning + // each one in the Attribute out parameter. + + // BUG: Verify that the attribute count is correct. + + for (AttrIndex = 0; AttrIndex < AttributeCount; AttrIndex++) + { + NtStatus = SampExtractFixedLengthAttributeFromDsAttr( + &(Attributes[AttrIndex]), + &MultiValuedCount, + &AttributeLength, + &Attribute); + + // Always call the verification routine, regardless of an + // error in the extraction routine. + + NtStatus = SampVerifyFixedLengthAttribute(ObjectType, + AttrIndex, + MultiValuedCount, + AttributeLength); + + // Append the attribute onto the end of the SAM buffer (i.e. + // fill in the members of the fixed-length data structure). + + // NOTE: This routine assumes that the order of the attributes + // returned in the DSATTRBLOCK are correct (i.e. correspond + // to the order of the members in the given SAM fixed-length + // structure). It also assumes that SAM fixed-length attri- + // butes are always single-valued attributes. + + if (NT_SUCCESS(NtStatus) && (NULL != Attribute)) + { + NtStatus = SampAppendFixedLengthAttributeToBuffer( + ObjectType, + AttrIndex, + Attribute, + *SamAttributes); + } + else + { + NtStatus = STATUS_INTERNAL_ERROR; + break; + } + } + + if (NULL != TotalLength) + { + *TotalLength = Length; + } + else + { + NtStatus = STATUS_INVALID_PARAMETER; + } + } + } + + return(NtStatus); +} + + + +// +// FIXED LENGTH-TO-ATTRBLOCK CONVERSION ROUTINES +// + +NTSTATUS +SampConvertFixedLengthAttributes( + IN INT ObjectType, + IN PVOID SamAttributes, + IN ULONG AttributeCount, + OUT PDSATTR Attributes + ) + +/*++ + +Routine Description: + + This routine does the work of converting a given SAM fixed-length attri- + bute type (i.e. contains all of the fixed-length attributes pertinent to + the specified ObjectType) into a DSATTR array. Related DS attribute infor- + mation, such as attribute length and type, are also set by this routine. + +Arguments: + + ObjectType - Identifies which SAM object type, and hence, which attribute + set to work with. + + SamAttributes - Pointer, updated SAM attribute buffer. + + AttributeCount - Number of attributes to convert into DSATTRs. + + Attributes - Pointer, outgoing DSATTR, containing fixed-length attributes. + +Return Value: + + STATUS_SUCCESS - The object has been successfully accessed. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_SUCCESS; + ULONG Offset = 0; + ULONG Length = 0; + ULONG Index = 0; + PDSATTRVAL Attribute = NULL; + PBYTE Value = NULL; + ULONG AttrIndex = 0; + PSAMP_V1_FIXED_LENGTH_SERVER ServerAttrs = NULL; + PSAMP_V1_0A_FIXED_LENGTH_DOMAIN DomainAttrs = NULL; + PSAMP_V1_0A_FIXED_LENGTH_GROUP GroupAttrs = NULL; + PSAMP_V1_FIXED_LENGTH_ALIAS AliasAttrs = NULL; + PSAMP_V1_0A_FIXED_LENGTH_USER UserAttrs = NULL; + + SAMTRACE("SampConvertFixedLengthAttributes"); + + for (AttrIndex = 0; AttrIndex < AttributeCount; AttrIndex++) + { + // BUG: Assuming that all fixed-length attributes are single-valued. + + // Set the multi-value count to 1 for the fixed-length attribute, and + // set its type identifier. + + Attributes[AttrIndex].AttrVal.valCount = 1; + + Attributes[AttrIndex].attrTyp = + SampFixedAttributeInfo[ObjectType][AttrIndex].Type; + + // First, allocate a block for the individual DSATTRVAL. + + Attribute = RtlAllocateHeap(RtlProcessHeap(), 0, sizeof(DSATTRVAL)); + + if (NULL != Attribute) + { + RtlZeroMemory(Attribute, sizeof(DSATTRVAL)); + + Attributes[AttrIndex].AttrVal.pAVal = Attribute; + Length = SampFixedAttributeInfo[ObjectType][AttrIndex].Length; + + // Second, allocate a block for the actual value, and make the + // DSATTRVAL point to it. + + Value = RtlAllocateHeap(RtlProcessHeap(), 0, Length); + RtlZeroMemory(Value, Length); + + if (NULL != Value) + { + Attribute->pVal = Value; + Attribute->valLen = Length; + + // Then copy the data into the target DS attribute. + + switch(ObjectType) + { + + case SampServerObjectType: + + ServerAttrs = SamAttributes; + + switch(AttrIndex) + { + + case 0: + RtlCopyMemory(Value, + &(ServerAttrs->RevisionLevel), + Length); + break; + + default: + NtStatus = STATUS_INTERNAL_ERROR; + break; + + } + + break; + + case SampDomainObjectType: + + DomainAttrs = SamAttributes; + + switch(AttrIndex) + { + + case 0: + RtlCopyMemory(Value, + &(DomainAttrs->Revision), + Length); + break; + + case 1: + RtlCopyMemory(Value, + &(DomainAttrs->Unused1), + Length); + break; + + case 2: + RtlCopyMemory(Value, + &(DomainAttrs->CreationTime), + Length); + break; + + case 3: + RtlCopyMemory(Value, + &(DomainAttrs->ModifiedCount), + Length); + break; + + case 4: + RtlCopyMemory(Value, + &(DomainAttrs->MaxPasswordAge), + Length); + break; + + case 5: + RtlCopyMemory(Value, + &(DomainAttrs->MinPasswordAge), + Length); + break; + + case 6: + RtlCopyMemory(Value, + &(DomainAttrs->ForceLogoff), + Length); + break; + + case 7: + RtlCopyMemory(Value, + &(DomainAttrs->LockoutDuration), + Length); + break; + + case 8: + RtlCopyMemory(Value, + &(DomainAttrs->LockoutObservationWindow), + Length); + break; + + case 9: + RtlCopyMemory(Value, + &(DomainAttrs->ModifiedCountAtLastPromotion), + Length); + break; + + case 10: + RtlCopyMemory(Value, + &(DomainAttrs->NextRid), + Length); + break; + + case 11: + RtlCopyMemory(Value, + &(DomainAttrs->PasswordProperties), + Length); + break; + + case 12: + RtlCopyMemory(Value, + &(DomainAttrs->MinPasswordLength), + Length); + break; + + case 13: + RtlCopyMemory(Value, + &(DomainAttrs->PasswordHistoryLength), + Length); + break; + + case 14: + RtlCopyMemory(Value, + &(DomainAttrs->LockoutThreshold), + Length); + break; + + case 15: + RtlCopyMemory(Value, + &(DomainAttrs->ServerState), + Length); + break; + + case 16: + RtlCopyMemory(Value, + &(DomainAttrs->ServerRole), + Length); + break; + + case 17: + RtlCopyMemory(Value, + &(DomainAttrs->UasCompatibilityRequired), + Length); + break; + + default: + NtStatus = STATUS_INTERNAL_ERROR; + break; + + } + + break; + + case SampGroupObjectType: + + GroupAttrs = SamAttributes; + + switch(AttrIndex) + { + + case 0: + RtlCopyMemory(Value, + &(GroupAttrs->Revision), + Length); + break; + + case 1: + RtlCopyMemory(Value, + &(GroupAttrs->RelativeId), + Length); + break; + + case 2: + RtlCopyMemory(Value, + &(GroupAttrs->Attributes), + Length); + break; + + case 3: + RtlCopyMemory(Value, + &(GroupAttrs->Unused1), + Length); + break; + + case 4: + RtlCopyMemory(Value, + &(GroupAttrs->AdminCount), + Length); + break; + + case 5: + RtlCopyMemory(Value, + &(GroupAttrs->OperatorCount), + Length); + break; + + default: + NtStatus = STATUS_INTERNAL_ERROR; + break; + + } + + break; + + case SampAliasObjectType: + + AliasAttrs = SamAttributes; + + switch(AttrIndex) + { + + case 0: + RtlCopyMemory(Value, + &(AliasAttrs->RelativeId), + Length); + break; + + default: + NtStatus = STATUS_INTERNAL_ERROR; + break; + + } + + break; + + case SampUserObjectType: + + UserAttrs = SamAttributes; + + switch(AttrIndex) + { + + case 0: + RtlCopyMemory(Value, + &(UserAttrs->Revision), + Length); + break; + + case 1: + RtlCopyMemory(Value, + &(UserAttrs->Unused1), + Length); + break; + + case 2: + RtlCopyMemory(Value, + &(UserAttrs->LastLogon), + Length); + break; + + case 3: + RtlCopyMemory(Value, + &(UserAttrs->LastLogoff), + Length); + break; + + case 4: + RtlCopyMemory(Value, + &(UserAttrs->PasswordLastSet), + Length); + break; + + case 5: + RtlCopyMemory(Value, + &(UserAttrs->AccountExpires), + Length); + break; + + case 6: + RtlCopyMemory(Value, + &(UserAttrs->LastBadPasswordTime), + Length); + break; + + case 7: + RtlCopyMemory(Value, + &(UserAttrs->UserId), + Length); + break; + + case 8: + RtlCopyMemory(Value, + &(UserAttrs->PrimaryGroupId), + Length); + break; + + case 9: + RtlCopyMemory(Value, + &(UserAttrs->UserAccountControl), + Length); + break; + + case 10: + RtlCopyMemory(Value, + &(UserAttrs->CountryCode), + Length); + break; + + case 11: + RtlCopyMemory(Value, + &(UserAttrs->CodePage), + Length); + break; + + case 12: + RtlCopyMemory(Value, + &(UserAttrs->BadPasswordCount), + Length); + break; + + case 13: + RtlCopyMemory(Value, + &(UserAttrs->LogonCount), + Length); + break; + + case 14: + RtlCopyMemory(Value, + &(UserAttrs->AdminCount), + Length); + break; + + case 15: + RtlCopyMemory(Value, + &(UserAttrs->Unused2), + Length); + break; + + case 16: + RtlCopyMemory(Value, + &(UserAttrs->OperatorCount), + Length); + break; + + default: + NtStatus = STATUS_INTERNAL_ERROR; + break; + + } + + break; + + default: + NtStatus = STATUS_INTERNAL_ERROR; + break; + + } + } + else + { + NtStatus = STATUS_NO_MEMORY; + break; + } + } + else + { + NtStatus = STATUS_NO_MEMORY; + break; + } + } + + return(NtStatus); +} + + + +NTSTATUS +SampConvertFixedLengthAttributesToAttrBlock( + IN INT ObjectType, + IN PVOID SamAttributes, + OUT PDSATTRBLOCK *DsAttributes + ) + +/*++ + +Routine Description: + + This routine is the top-level routine for converting a SAM fixed-length + attribute into a DSATTRBLOCK. Based on the SAM object type, the attribute + count is set, and subsequently used to allocate memory for the DS attri- + butes. + +Arguments: + + ObjectType - Identifies which SAM object type, and hence, which attribute + set to work with. + + SamAttributes - Pointer, updated SAM attribute buffer. + + DsAttributes - Pointer, outgoing DSATTRBLOCK, containing fixed-length + attributes. + +Return Value: + + STATUS_SUCCESS - The object has been successfully accessed. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + ULONG AttributeCount = 0; + ULONG Length = 0; + PDSATTR Attributes = NULL; + + SAMTRACE("SampConvertFixedLengthAttributesToAttrBlock"); + + if (NULL != DsAttributes) + { + // Allocate the top-level DS structure, DSATTRBLOCK. + + *DsAttributes = RtlAllocateHeap(RtlProcessHeap(), + 0, + sizeof(DSATTRBLOCK)); + + // From the SAM object type, set the attribute count. + + if ((NULL != SamAttributes) && (NULL != *DsAttributes)) + { + RtlZeroMemory(*DsAttributes, sizeof(DSATTRBLOCK)); + + switch(ObjectType) + { + + case SampServerObjectType: + + AttributeCount = SAMP_SERVER_FIXED_ATTR_COUNT; + break; + + case SampDomainObjectType: + + AttributeCount = SAMP_DOMAIN_FIXED_ATTR_COUNT; + break; + + case SampGroupObjectType: + + AttributeCount = SAMP_GROUP_FIXED_ATTR_COUNT; + break; + + case SampAliasObjectType: + + AttributeCount = SAMP_ALIAS_FIXED_ATTR_COUNT; + break; + + case SampUserObjectType: + + AttributeCount = SAMP_USER_FIXED_ATTR_COUNT; + break; + + default: + + break; + + } + + // Allocate a block for the DSATTR array, then convert the SAM + // fixed-length attributes into the DSATTRBLOCK. + + Length = AttributeCount * sizeof(DSATTR); + Attributes = RtlAllocateHeap(RtlProcessHeap(), 0, Length); + + if (NULL != Attributes) + { + RtlZeroMemory(Attributes, Length); + + (*DsAttributes)->attrCount = AttributeCount; + (*DsAttributes)->pAttr = Attributes; + + NtStatus = SampConvertFixedLengthAttributes(ObjectType, + SamAttributes, + AttributeCount, + Attributes); + } + } + } + + return(NtStatus); +} + + + +// +// ATTRBLOCK-TO-COMBINED BUFFER CONVERSION ROUTINES +// + +NTSTATUS +SampWalkAttrBlock( + IN ULONG FixedLengthAttributeCount, + IN ULONG VarLengthAttributeCount, + IN PDSATTRBLOCK DsAttributes, + OUT PDSATTRBLOCK *FixedLengthAttributes, + OUT PDSATTRBLOCK *VarLengthAttributes + ) + +/*++ + +Routine Description: + + This routine scans the DSATTRBLOCK containing the fixed and variable- + length attributes, identifying where each starts. Two new DSATTRBLOCK are + allocated, one that points to the fixed-length data, while the second + points at the variable-length data. + +Arguments: + + FixedLengthAttributeCount - Number of fixed-length attributes for this + object. + + VarLengthAttributeCount - Number of variable-length attributes for this + object. + + DsAttributes - Pointer, incoming DSATTRBLOCK, containing all of the + attributes. + + FixedLengthAttributes - Pointer, returned pointer to the first fixed- + length attribute. + + VarLengthAttributes - Pointer, returned pointer to the first variable- + length attribute. + +Return Value: + + STATUS_SUCCESS - The object has been successfully accessed. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + ULONG AttributeCount = FixedLengthAttributeCount + VarLengthAttributeCount; + + if ((0 < FixedLengthAttributeCount) && + (0 < VarLengthAttributeCount) && + (NULL != DsAttributes)) + { + ASSERT(DsAttributes->attrCount == AttributeCount); + + if ((NULL != FixedLengthAttributes) && + (NULL != VarLengthAttributes)) + { + // Allocate a new DSATTRBLOCK structure that will point to the + // first N DSATTR elements, representing the fixed-length attri- + // butes for this SAM object. + + *FixedLengthAttributes = RtlAllocateHeap(RtlProcessHeap(), + 0, + sizeof(DSATTRBLOCK)); + + if (NULL != *FixedLengthAttributes) + { + RtlZeroMemory(*FixedLengthAttributes, sizeof(DSATTRBLOCK)); + + // Set the pointer, and attribute count to the number of fixed + // length attributes. + + if (NULL != DsAttributes->pAttr) + { + (*FixedLengthAttributes)->pAttr = DsAttributes->pAttr; + + (*FixedLengthAttributes)->attrCount = + FixedLengthAttributeCount; + + // Now, allocate a second DSATTRBLOCK that will point + // to the variable-length attributes. + + *VarLengthAttributes = RtlAllocateHeap(RtlProcessHeap(), + 0, + sizeof(DSATTRBLOCK)); + + if (NULL != *VarLengthAttributes) + { + RtlZeroMemory(*VarLengthAttributes, + sizeof(DSATTRBLOCK)); + + // The remaining M DSATTR elements represent the var- + // iable length attributes. Set the pointer, and the + // attribute count to the number of variable attrs. + + (*VarLengthAttributes)->pAttr = + DsAttributes->pAttr + FixedLengthAttributeCount; + + (*VarLengthAttributes)->attrCount = + VarLengthAttributeCount; + + NtStatus = STATUS_SUCCESS; + } + else + { + NtStatus = STATUS_NO_MEMORY; + } + } + else + { + NtStatus = STATUS_INTERNAL_ERROR; + } + } + else + { + NtStatus = STATUS_NO_MEMORY; + } + } + else + { + NtStatus = STATUS_INVALID_PARAMETER; + } + } + else + { + NtStatus = STATUS_INVALID_PARAMETER; + } + + return(NtStatus); +} + + + +NTSTATUS +SampLocateAttributesInAttrBlock( + IN INT ObjectType, + IN PDSATTRBLOCK DsAttributes, + OUT PDSATTRBLOCK *FixedLengthAttributes, + OUT PDSATTRBLOCK *VarLengthAttributes + ) + +/*++ + +Routine Description: + + This routine determines the number of attributes based on object type, + then calls a worker routine to obtain pointers to the fixed-length and + variable-length portions of the DSATTRBLOCK. + +Arguments: + + ObjectType - Identifies which SAM object type, and hence, which attribute + set to work with. + + DsAttributes - Pointer, incoming DSATTRBLOCK. + + FixedLengthAttributes - Pointer, returned pointer to the fixed data. + + VarLengthAttributes - Pointer, returned pointer to the variable data. + +Return Value: + + STATUS_SUCCESS - The object has been successfully accessed. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + ULONG FixedLengthAttributeCount = 0; + ULONG VarLengthAttributeCount = 0; + ULONG AttributeCount = 0; + + SAMTRACE("SampLocateAttributesInAttrBlock"); + + // Set the fixed-length, variable-length attribute counts based upon + // the object type. + + switch(ObjectType) + { + + case SampServerObjectType: + + FixedLengthAttributeCount = SAMP_SERVER_FIXED_ATTR_COUNT; + VarLengthAttributeCount = SAMP_SERVER_VARIABLE_ATTRIBUTES; + break; + + case SampDomainObjectType: + + FixedLengthAttributeCount = SAMP_DOMAIN_FIXED_ATTR_COUNT; + VarLengthAttributeCount = SAMP_DOMAIN_VARIABLE_ATTRIBUTES; + break; + + case SampGroupObjectType: + + FixedLengthAttributeCount = SAMP_GROUP_FIXED_ATTR_COUNT; + VarLengthAttributeCount = SAMP_GROUP_VARIABLE_ATTRIBUTES; + break; + + case SampAliasObjectType: + + FixedLengthAttributeCount = SAMP_ALIAS_FIXED_ATTR_COUNT; + VarLengthAttributeCount = SAMP_ALIAS_VARIABLE_ATTRIBUTES; + break; + + case SampUserObjectType: + + FixedLengthAttributeCount = SAMP_USER_FIXED_ATTR_COUNT; + VarLengthAttributeCount = SAMP_USER_VARIABLE_ATTRIBUTES; + break; + + default: + break; + + } + + AttributeCount = FixedLengthAttributeCount + VarLengthAttributeCount; + + if (0 < AttributeCount) + { + NtStatus = SampWalkAttrBlock(FixedLengthAttributeCount, + VarLengthAttributeCount, + DsAttributes, + FixedLengthAttributes, + VarLengthAttributes); + } + + return(NtStatus); +} + + + +NTSTATUS +SampCombineSamAttributes( + IN PVOID SamFixedLengthAttributes, + IN ULONG FixedLength, + IN PSAMP_VARIABLE_LENGTH_ATTRIBUTE SamVarLengthAttributes, + IN ULONG VarLength, + OUT PVOID *SamAttributes + ) + +/*++ + +Routine Description: + + This routine combines the SAM fixed and variable-length buffers into a + single SAM combined-attribute buffer. + +Arguments: + + SamFixedLengthAttributes - Pointer, fixed attributes. + + FixedLength - Number of bytes. + + SamVarLengthAttributes - Pointer, variable attributes. + + VarLength - Number of bytes. + + SamAttributes - Pointer, returned combined-attribute buffer. + +Return Value: + + STATUS_SUCCESS - The object has been successfully accessed. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + ULONG CombinedLength = 0; + + SAMTRACE("SampCombineSamAttributes"); + + if ((0 < FixedLength) && (0 < VarLength)) + { + // Adjust the length so that the appended variable attributes start + // on a DWORD boundary. + + FixedLength = DWORD_ALIGN(FixedLength); + CombinedLength = FixedLength + VarLength; + + if (NULL != SamAttributes) + { + // Allocate a new buffer for the combined attributes. + + *SamAttributes = RtlAllocateHeap(RtlProcessHeap(), + 0, + CombinedLength); + + if (NULL != *SamAttributes) + { + RtlZeroMemory(*SamAttributes, CombinedLength); + + if ((NULL != SamFixedLengthAttributes) && + (NULL != SamVarLengthAttributes)) + { + // BUG: Check return value from RtlCopyMemory. + + // Copy the fixed-length attributes first... + + RtlCopyMemory(*SamAttributes, + SamFixedLengthAttributes, + FixedLength); + + RtlFreeHeap(RtlProcessHeap(), 0, SamFixedLengthAttributes); + + // then the variable ones. + + RtlCopyMemory(((PBYTE)(*SamAttributes)) + FixedLength, + SamVarLengthAttributes, + VarLength); + + RtlFreeHeap(RtlProcessHeap(), 0, SamVarLengthAttributes); + + // BUG: Need to set Object->VariableArrayOffset, etc. + + NtStatus = STATUS_SUCCESS; + } + else + { + NtStatus = STATUS_INVALID_PARAMETER; + } + } + else + { + NtStatus = STATUS_NO_MEMORY; + } + } + else + { + NtStatus = STATUS_INVALID_PARAMETER; + } + } + else + { + NtStatus = STATUS_INVALID_PARAMETER; + } + + return(NtStatus); +} + + + +NTSTATUS +SampConvertAttrBlockToCombinedAttributes( + IN INT ObjectType, + IN PDSATTRBLOCK DsAttributes, + OUT PVOID *SamAttributes, + OUT PULONG FixedLength, + OUT PULONG VariableLength + ) + +/*++ + +Routine Description: + + This routine produces a SAM combined-attribute buffer from a DSATTRBLOCK. + It is assumed that the order of the attributes in the DSATTRBLOCK is cor- + rect. This means that the fixed-length attributes come first, followed by + the variable-length attributes. The attributes within each of these parts + must also be correctly ordered, as per samsrvp.h definitions. + +Arguments: + + ObjectType - Identifies which SAM object type, and hence, which attribute + set to work with. + + DsAttributes - Pointer, incoming DSATTRBLOCK. + + SamAttributes - Pointer, returned SAM combined-attribute buffer. + + FixedLength - Pointer, returned byte count of the fixed-length portion. + + VariableLength - Pointer, returned byte count of the variable-length + portion. + +Return Value: + + STATUS_SUCCESS - The object has been successfully accessed. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + PDSATTRBLOCK DsFixedLengthAttributes = NULL; + PDSATTRBLOCK DsVarLengthAttributes = NULL; + PVOID SamFixedLengthAttributes = NULL; + PSAMP_VARIABLE_LENGTH_ATTRIBUTE SamVarLengthAttributes = NULL; + ULONG TotalFixedLength = 0; + ULONG TotalVarLength = 0; + + SAMTRACE("SampConvertAttrBlockToCombinedAttributes"); + + // This routine assumes that the incoming attributes, in DsAttributes, + // are correctly ordered. This means that the fixed-length attributes + // come first, followed by the variable-length attributes. Within the + // fixed or variable sections, the individual attributes must also be + // arranged in the same order in which SAM expects the attributes to + // appear. Essentially, the order of the attributes must match the order + // in which they are arranged in the output SAM buffer (see samsrvp.h + // for details). + + if (NULL != DsAttributes) + { + // The incoming DSATTRBLOCK is pointing to a set of both fixed-length + // and variable-length attributes. SampLocateAttributesInAttrBlock + // will return a new DSATTRBLOCK pointer for the fixed-length attri- + // butes and a new DSATTRBLOCK pointer for the variable-length attri- + // butes, so that these can be subsequently passed onto the conversion + // routines. + + NtStatus = SampLocateAttributesInAttrBlock(ObjectType, + DsAttributes, + &DsFixedLengthAttributes, + &DsVarLengthAttributes); + + if (NT_SUCCESS(NtStatus) && (NULL != DsFixedLengthAttributes)) + { + // First, convert the fixed-length attributes... + + NtStatus = SampConvertAttrBlockToFixedLengthAttributes( + ObjectType, + DsFixedLengthAttributes, + &SamFixedLengthAttributes, + &TotalFixedLength); + + if (NT_SUCCESS(NtStatus) && (NULL != DsVarLengthAttributes)) + { + // then convert the variable-length attributes. + + NtStatus = SampConvertAttrBlockToVarLengthAttributes( + ObjectType, + DsVarLengthAttributes, + &SamVarLengthAttributes, + &TotalVarLength); + + if (NT_SUCCESS(NtStatus) && + (NULL != SamFixedLengthAttributes) && + (NULL != SamVarLengthAttributes)) + { + // Finally, concatenate the the variable-length attribute + // buffer onto the end of the fixed-length buffer, passing + // the result back as the SAM combined-attribute buffer. + + if ((0 < TotalFixedLength) && + (0 < TotalVarLength) && + (NULL != SamAttributes)) + { + NtStatus = SampCombineSamAttributes( + SamFixedLengthAttributes, + TotalFixedLength, + SamVarLengthAttributes, + TotalVarLength, + SamAttributes); + + ASSERT(NULL != SamAttributes); + + if (NT_SUCCESS(NtStatus)) + { + if ((NULL != FixedLength) && + (NULL != VariableLength)) + { + *FixedLength = TotalFixedLength; + *VariableLength = TotalVarLength; + } + else + { + NtStatus = STATUS_INVALID_PARAMETER; + } + } + else + { + NtStatus = STATUS_INTERNAL_ERROR; + } + } + else + { + NtStatus = STATUS_INTERNAL_ERROR; + } + } + else + { + NtStatus = STATUS_INTERNAL_ERROR; + } + } + else + { + NtStatus = STATUS_INTERNAL_ERROR; + } + } + else + { + NtStatus = STATUS_INTERNAL_ERROR; + } + } + else + { + NtStatus = STATUS_INVALID_PARAMETER; + } + + return(NtStatus); +} + + + +// +// COMBINED BUFFER-TO-ATTRBLOCK CONVERSION ROUTINES +// + +NTSTATUS +SampLocateAttributesInSamBuffer( + IN INT ObjectType, + IN PVOID SamAttributes, + IN ULONG FixedLength, + IN ULONG VariableLength, + OUT PVOID *FixedLengthAttributes, + OUT PSAMP_VARIABLE_LENGTH_ATTRIBUTE *VarLengthAttributes + ) + +/*++ + +Routine Description: + + This routine finds the start of the fixed-length and variable-length + attributes, returning a pointer to each. + +Arguments: + + ObjectType - Identifies which SAM object type, and hence, which attribute + set to work with. + + SamAttributes - Pointer, SAM attribute buffer. + + FixedLength - Number of bytes of fixed-length attributes. + + VariableLength - Number of bytes of variable-length attributes. + + FixedLengthAttributes - Pointer, returned pointer to the fixed data. + + VarLengthAttributes - Pointer, returned pointer to the variable data. + +Return Value: + + STATUS_SUCCESS - The object has been successfully accessed. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + + SAMTRACE("SampLocateAttributesInSamBuffer"); + + // BUG: ObjectType and VariableLength are not used in this routine. + // These parameters could be used in the future for validation checks. + + if ((NULL != SamAttributes) && (NULL != FixedLengthAttributes)) + { + // The fixed-length attributes are in the first part of the overall + // buffer. + + *FixedLengthAttributes = SamAttributes; + + if (NULL != VarLengthAttributes) + { + // The variable-length attributes come after the fixed ones. + + *VarLengthAttributes = + (PSAMP_VARIABLE_LENGTH_ATTRIBUTE)(((PBYTE)SamAttributes) + + FixedLength); + + NtStatus = STATUS_SUCCESS; + } + } + + return(NtStatus); +} + + + +NTSTATUS +SampCreateDsAttributes( + IN INT ObjectType, + IN PDSATTRBLOCK DsFixedLengthAttributes, + IN ULONG FixedLengthAttributeCount, + IN PDSATTRBLOCK DsVarLengthAttributes, + IN ULONG VarLengthAttributeCount, + OUT PDSATTRBLOCK *DsAttributes + ) + +/*++ + +Routine Description: + + This routine does the work of combining two DSATTRBLOCKs into a single + DSATTRBLOCK by "concatenating" them together. The routine allocates a + new top-level DSATTR array, and then fixes up the pointers to the real + attributes, finally releasing the old DSATTR array. + +Arguments: + + AttributeCount - Total number of attributes, fixed and variable. + + DsFixedLengthAttributes - Pointer, the DSATTRBLOCK containing the fixed- + length attributes. + + DsVarLengthAttributes - Pointer, the DSATTRBLOCK containing the variable- + length attributes. + + DsAttributes - Pointer, the outgoing DSATTRBLOCK containing both sets of + attributes. + +Return Value: + + STATUS_SUCCESS - The object has been successfully accessed. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + PDSATTR Attributes = NULL; + PDSATTR FixedAttributes = NULL; + PDSATTR VarAttributes = NULL; + ULONG AttrIndex = 0; + ULONG AttrIndexTmp = 0; + ULONG AttributeCount = FixedLengthAttributeCount + VarLengthAttributeCount; + + if (NULL != DsAttributes) + { + // Allocate a new top-level DSATTRBLOCK for DsAttributes. + + *DsAttributes = RtlAllocateHeap(RtlProcessHeap(), + 0, + sizeof(DSATTRBLOCK)); + + if (NULL != *DsAttributes) + { + RtlZeroMemory(*DsAttributes, sizeof(DSATTRBLOCK)); + + // Allocate the DSATTR array for the attributes. + + Attributes = RtlAllocateHeap(RtlProcessHeap(), + 0, + (AttributeCount * sizeof(DSATTR))); + + if (NULL != Attributes) + { + RtlZeroMemory(Attributes, (AttributeCount * sizeof(DSATTR))); + + // Set the return DsAttributes members. + + (*DsAttributes)->attrCount = AttributeCount; + (*DsAttributes)->pAttr = Attributes; + + if ((NULL != DsFixedLengthAttributes) && + (NULL != DsVarLengthAttributes)) + { + FixedAttributes = DsFixedLengthAttributes->pAttr; + VarAttributes = DsVarLengthAttributes->pAttr; + + if ((NULL != FixedAttributes) && + (NULL != VarAttributes)) + { + // Reset the attribute pointers so that DsAttributes + // points to the fixed-length attributes and counts. + + for (AttrIndex = 0; + AttrIndex < FixedLengthAttributeCount; + AttrIndex++) + { + Attributes[AttrIndex].attrTyp = + SampFixedAttributeInfo[ObjectType][AttrIndex].Type; + + Attributes[AttrIndex].AttrVal.valCount = + FixedAttributes[AttrIndex].AttrVal.valCount; + + Attributes[AttrIndex].AttrVal.pAVal = + FixedAttributes[AttrIndex].AttrVal.pAVal; + } + + // Save the current attribute index so that the + // variable-length attributes can be appended next. + + AttrIndexTmp = AttrIndex; + + // Now fix up the variable-length attribute pointers. + + for (AttrIndex = 0; + AttrIndex < VarLengthAttributeCount; + AttrIndex++) + { + Attributes[AttrIndex + AttrIndexTmp].attrTyp = + VarAttributes[AttrIndex].attrTyp; + + Attributes[AttrIndex + AttrIndexTmp].AttrVal.valCount = + VarAttributes[AttrIndex].AttrVal.valCount; + + Attributes[AttrIndex + AttrIndexTmp].AttrVal.pAVal = + VarAttributes[AttrIndex].AttrVal.pAVal; + } + + ASSERT(AttrIndex == (AttributeCount-1)); + + NtStatus = STATUS_SUCCESS; + } + } + + // BUG: Need to free FixedAttributes, VarAttributes arrays. + } + else + { + NtStatus = STATUS_NO_MEMORY; + } + } + else + { + NtStatus = STATUS_NO_MEMORY; + } + } + else + { + NtStatus = STATUS_INVALID_PARAMETER; + } + + return(NtStatus); +} + + + +NTSTATUS +SampCombineDsAttributes( + IN INT ObjectType, + IN PDSATTRBLOCK DsFixedLengthAttributes, + IN PDSATTRBLOCK DsVarLengthAttributes, + OUT PDSATTRBLOCK *DsAttributes + ) + +/*++ + +Routine Description: + + This routine does the work of combining two DSATTRBLOCKs into a single + DSATTRBLOCK by "concatenating" them together. The routine allocates a + new top-level DSATTR array, and then fixes up the pointers to the real + attributes, finally releasing the old DSATTR array. + +Arguments: + + ObjectType - Identifies which SAM object type, and hence, which attribute + set to work with. + + DsFixedLengthAttributes - Pointer, the DSATTRBLOCK containing the fixed- + length attributes. + + DsVarLengthAttributes - Pointer, the DSATTRBLOCK containing the variable- + length attributes. + + DsAttributes - Pointer, the outgoing DSATTRBLOCK containing both sets of + attributes. + +Return Value: + + STATUS_SUCCESS - The object has been successfully accessed. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + ULONG FixedLengthAttributeCount = 0; + ULONG VarLengthAttributeCount = 0; + ULONG AttributeCount = 0; + + SAMTRACE("SampCombineDsAttributes"); + + // Set the fixed-length, variable-length attribute counts based upon + // the object type. + + switch(ObjectType) + { + + case SampServerObjectType: + + FixedLengthAttributeCount = SAMP_SERVER_FIXED_ATTR_COUNT; + VarLengthAttributeCount = SAMP_SERVER_VARIABLE_ATTRIBUTES; + break; + + case SampDomainObjectType: + + FixedLengthAttributeCount = SAMP_DOMAIN_FIXED_ATTR_COUNT; + VarLengthAttributeCount = SAMP_DOMAIN_VARIABLE_ATTRIBUTES; + break; + + case SampGroupObjectType: + + FixedLengthAttributeCount = SAMP_GROUP_FIXED_ATTR_COUNT; + VarLengthAttributeCount = SAMP_GROUP_VARIABLE_ATTRIBUTES; + break; + + case SampAliasObjectType: + + FixedLengthAttributeCount = SAMP_ALIAS_FIXED_ATTR_COUNT; + VarLengthAttributeCount = SAMP_ALIAS_VARIABLE_ATTRIBUTES; + break; + + case SampUserObjectType: + + FixedLengthAttributeCount = SAMP_USER_FIXED_ATTR_COUNT; + VarLengthAttributeCount = SAMP_USER_VARIABLE_ATTRIBUTES; + break; + + default: + // Error case, NtStatus, counts, etc. already set. + break; + + } + + AttributeCount = FixedLengthAttributeCount + VarLengthAttributeCount; + + if (0 < AttributeCount) + { + NtStatus = SampCreateDsAttributes(ObjectType, + DsFixedLengthAttributes, + FixedLengthAttributeCount, + DsVarLengthAttributes, + VarLengthAttributeCount, + DsAttributes); + } + + return(NtStatus); +} + + + +NTSTATUS +SampConvertCombinedAttributesToAttrBlock( + IN INT ObjectType, + IN PVOID SamAttributes, + IN ULONG FixedLength, + IN ULONG VariableLength, + OUT PDSATTRBLOCK *DsAttributes + ) + +/*++ + +Routine Description: + + This routine converts a SAM combined-attribute buffer into a DSATTRBLOCK + containing all of the attributes. A SAM combined buffer contains fixed- + length attributes, followed by variable-length attributes (see attr.c for + the layout). + + The resultant DSATTRBLOCK contains the SAM attributes in exactly the + order in which they appeared in the input SAM buffer. + +Arguments: + + ObjectType - Identifies which SAM object type, and hence, which attribute + set to work with. + + SamAttributes - Pointer, input SAM combined attribute buffer. + + FixedLength - Number of bytes of the buffer containing the fixed-length + attributes. + + VariableLength - Number of bytes of the buffer containing the variable- + length attributes. + + DsAttributes - Pointer, the returned DSATTRBLOCK containing the SAM attri- + butes. + +Return Value: + + STATUS_SUCCESS - The object has been successfully accessed. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + PVOID SamFixedLengthAttributes = NULL; + PSAMP_VARIABLE_LENGTH_ATTRIBUTE SamVarLengthAttributes = NULL; + PDSATTRBLOCK DsFixedLengthAttributes = NULL; + PDSATTRBLOCK DsVarLengthAttributes = NULL; + + SAMTRACE("SampConvertCombinedAttributesToAttrBlock"); + + + if ((NULL != SamAttributes) && (0 < FixedLength) && (0 < VariableLength)) + { + // Begin by obtaining a two pointers: a pointer to the fixed-length + // attributes and a pointer to the variable-length attributes within + // the SAM buffer. + + NtStatus = SampLocateAttributesInSamBuffer(ObjectType, + SamAttributes, + FixedLength, + VariableLength, + &SamFixedLengthAttributes, + &SamVarLengthAttributes); + + + if (NT_SUCCESS(NtStatus) && + (NULL != SamFixedLengthAttributes) && + (NULL != SamVarLengthAttributes)) + { + // First, convert the fixed-length attributes into a DSATTRBLOCK. + + NtStatus = SampConvertFixedLengthAttributesToAttrBlock( + ObjectType, + SamFixedLengthAttributes, + &DsFixedLengthAttributes); + + + if (NT_SUCCESS(NtStatus) && (NULL != DsFixedLengthAttributes)) + { + // Then convert the variable-length attributes. + + NtStatus = SampConvertVarLengthAttributesToAttrBlock( + ObjectType, + SamVarLengthAttributes, + &DsVarLengthAttributes); + + + if (NT_SUCCESS(NtStatus) && (NULL != DsVarLengthAttributes)) + { + if (NULL != DsAttributes) + { + // Finally, combine the two DSATTRBLOCKs into a single + // DSATTRBLOCK, containing all of the attributes. + + NtStatus = SampCombineDsAttributes( + ObjectType, + DsFixedLengthAttributes, + DsVarLengthAttributes, + DsAttributes); + + + ASSERT(NULL != DsAttributes); + } + else + { + NtStatus = STATUS_INVALID_PARAMETER; + } + } + else + { + NtStatus = STATUS_INTERNAL_ERROR; + } + } + else + { + NtStatus = STATUS_INTERNAL_ERROR; + } + } + else + { + NtStatus = STATUS_INTERNAL_ERROR; + } + } + else + { + NtStatus = STATUS_INVALID_PARAMETER; + } + + return(NtStatus); +} diff --git a/private/newsam2/server/dsutilp.h b/private/newsam2/server/dsutilp.h new file mode 100644 index 000000000..18479f37e --- /dev/null +++ b/private/newsam2/server/dsutilp.h @@ -0,0 +1,173 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + dsutilp.h + +Abstract: + + This file contains definitions private to the SAM server program. + +Author: + + Chris Mayhall (ChrisMay) 09-May-1996 + +Environment: + + User Mode - Win32 + +Revision History: + + ChrisMay 09-May-1996 + Created initial file. + +--*/ + +#ifndef _DSUTILP_H_ +#define _DSUTILP_H_ + +// Include DSA header files to resolve READARG, READRES, etc. + +#include <duapi.h> // DS data types, READRES +#include <drs.h> // DS data types, ATTRBLOCK, ATTR, ATTRVAL +#include <core.h> // DS data types, ENTINF, COMMRES + +// Wrap the DS typedefs with private typedefs to insulate the SAM code from +// onging changes to the DS structure names. + +typedef READRES DSDATA, *PDSDATA; +typedef ATTR DSATTR, *PDSATTR; +typedef ATTRVAL DSATTRVAL, *PDSATTRVAL; +typedef ATTRBLOCK DSATTRBLOCK, *PDSATTRBLOCK; +typedef ATTRVALBLOCK DSATTRVALBLOCK, *PDSATTRVALBLOCK; +typedef ATTRMODLIST DSATTRMODLIST, *PDSATTRMODLIST; + +// +// The following type is used to identify which grouping of attribute +// (fixed or variable-length) are being refered to in a number of api. +// + +#define SAMP_FIXED_ATTRIBUTES (0L) +#define SAMP_VARIABLE_ATTRIBUTES (1L) + +// BUG: Defining BOGUS_TYPE. This type is used to indicate a missing or +// erroneous data type in the various AttributeMappingTables, found in +// mappings.c. + +#define BOGUS_TYPE 0 + +// SAM does not explicity store type or length information for its fixed- +// length attributes, but the DS storage routines require this information. +// This structure is intended to store any "patch" information needed for +// the DS backing store, as regards fixed attributes. + +typedef struct _SAMP_FIXED_ATTRIBUTE_TYPE_INFO +{ + // Type of the fixed-length attribute. + + ULONG Type; + + // Byte count of the fixed-length attribute. + + ULONG Length; + +} SAMP_FIXED_ATTRIBUTE_TYPE_INFO, PSAMP_FIXED_ATTRIBUTE_TYPE_INFO; + +// These constants are used to allocate a table of fixed-attribute informa- +// tion structures. If elements are added or removed from SAMP_OBJECT_TYPE, +// or if structure members in any of the SAM fixed-attribute strucutes are +// added or removed, then these constants must be updated to reflect the new +// members. SAMP_ATTRIBUTE_TYPES_MAX is the maximum number of attributes in +// any single SAM object. + +#define SAMP_OBJECT_TYPES_MAX 5 +#define SAMP_FIXED_ATTRIBUTES_MAX 18 +#define SAMP_VAR_ATTRIBUTES_MAX 18 + +// These values of these constants are equal to the number of data members in +// the SAM fixed-length attribute structures for each object type. These con- +// stants must be updated whenever data members are added or removed from the +// fixed-length attributes structures. + +#define SAMP_SERVER_FIXED_ATTR_COUNT 1 +#define SAMP_DOMAIN_FIXED_ATTR_COUNT 18 +#define SAMP_GROUP_FIXED_ATTR_COUNT 6 +#define SAMP_ALIAS_FIXED_ATTR_COUNT 1 +#define SAMP_USER_FIXED_ATTR_COUNT 17 + +// These type-information arrays are used by the routines in this file. They +// contain data type/size information that is needed by the DS routines for +// reading/writing data. Changes to the fixed-length attribute structures re- +// quire corresponding updates to these arrays. + +extern SAMP_FIXED_ATTRIBUTE_TYPE_INFO + SampFixedAttributeInfo[SAMP_OBJECT_TYPES_MAX][SAMP_FIXED_ATTRIBUTES_MAX]; + +// SAM variable-length attributes explicitly store length and the number +// of attributes for each object is defined in samsrvp.h. No type information, +// however is stored with these attributes, so define this table. + +typedef struct _SAMP_VAR_ATTRIBUTE_TYPE_INFO +{ + // Type of the variable-length attribute. + + ULONG Type; + +} SAMP_VAR_ATTRIBUTE_TYPE_INFO, PSAMP_VAR_ATTRIBUTE_TYPE_INFO; + +extern SAMP_VAR_ATTRIBUTE_TYPE_INFO + SampVarAttributeInfo[SAMP_OBJECT_TYPES_MAX][SAMP_VAR_ATTRIBUTES_MAX]; + +// Routine forward declarations. + +NTSTATUS +SampConvertAttrBlockToVarLengthAttributes( + IN INT ObjectType, + IN PDSATTRBLOCK DsAttributes, + OUT PSAMP_VARIABLE_LENGTH_ATTRIBUTE *SamAttributes, + OUT PULONG TotalLength + ); + +NTSTATUS +SampConvertVarLengthAttributesToAttrBlock( + IN INT ObjectType, + IN PSAMP_VARIABLE_LENGTH_ATTRIBUTE SamAttributes, + OUT PDSATTRBLOCK *DsAttributes + ); + +NTSTATUS +SampConvertAttrBlockToFixedLengthAttributes( + IN INT ObjectType, + IN PDSATTRBLOCK DsAttributes, + OUT PVOID *SamAttributes, + OUT PULONG TotalLength + ); + +NTSTATUS +SampConvertFixedLengthAttributesToAttrBlock( + IN INT ObjectType, + IN PVOID SamAttributes, + OUT PDSATTRBLOCK *DsAttributes + ); + +NTSTATUS +SampConvertAttrBlockToCombinedAttributes( + IN INT ObjectType, + IN PDSATTRBLOCK DsAttributes, + OUT PVOID *SamAttributes, + OUT PULONG FixedLength, + OUT PULONG VariableLength + ); + +NTSTATUS +SampConvertCombinedAttributesToAttrBlock( + IN INT ObjectType, + IN PVOID SamAttributes, + IN ULONG FixedLength, + IN ULONG VariableLength, + OUT PDSATTRBLOCK *DsAttributes + ); + +#endif // _DSUTIL_H_ diff --git a/private/newsam2/server/enum.c b/private/newsam2/server/enum.c new file mode 100644 index 000000000..a6b703377 --- /dev/null +++ b/private/newsam2/server/enum.c @@ -0,0 +1,2288 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + enum.c + +Abstract: + + This file contains the core account enumeration services + +Author: + + Jim Kelly (JimK) 4-July-1991 + +Environment: + + User Mode - Win32 + +Revision History: + + 6-19-96: MURLIS Created. + + +--*/ + + +///////////////////////////////////////////////////////////////////////////// +/* + + ENUMERATION ROUTINES IMPLEMENTATION + + The Entry Points for the core Enumeration routines are + + SampEnumerateAcountNamesCommon -- + + Called By the Samr RPC routines + + SampEnumerateAccountNames -- + + Called by the above SampEnumerateAccountNamesCommon + and internal routines that need enumeration. + + SampEnumerateAccountNames does the actual work of enumerating account + names. the transaction domain to be set . SampEnumerateAccountNames + looks at the current current transaction domain and makes the decision + wether it is DS or Registry and then Calls either DS or Registry version. + While the way enumeration is done from the registry is unaltered the + way it is done from the DS is as follows: + + Enumerating Accounts in DS uses the DS Search mechanism along with + the Paged Results extension. The First time the client calls the Enumerate + accounts routine, the value of EnumerationHandle is set to NULL. + This results in the code building a DS Filter structure and set up a + new search. If More entries are turned up the search, than memory + restrictions will warrant, then the DS will turn return a PagedResults + Structure. This paged results structure containes a pointer to a restart + structure. This restart structure represents the state information, the + DS requires in order to continue the search. The DS expects that this + structure to be passed back in subsequent searches. + + There are two cases of the enumeration logic: + + + + 1. Enumeration is called by clients. + + While the XDS head of the DS passes back the entire restart + structure SAM needs to pass back a handle in order to maintain + backwards compatiblity with older releases of NT. Therefore + SAM copies the restart structure returned by the DS ( the DS + allocates using its thread alloc scheme, whose scope is limited + to the current RPC call ) and keeps it around in server memory, + and returns a handle to the client that identifies this restart + strucure in server memory. The mechanism of correlating the handle + to the restart structure is as follows + + The Handle holds a pointer to an enumeration context structure. + The value of the pointer is also stored in the Enumeration Context + Structure. The Domain Context maintains a linked list of Enumeration + Context's, which represent the Enumerations initiated by this client + on this domain and which have not yet computed. + SampValidateEnumerationContext is used to validate an enumeration + handle passed in by the client. This routine uses the Domain + Context that is passed in and traverses the List to find a context + block with a pointer value that matches the passed in Enumeration + Handle. If it finds such a pointer then it returns STATUS_SUCCESS + and the value of the handle is cast into the pointer to the + enumeration context block. Else the routine returns STATUS_INVALID_ + HANDLE. Upon rundown, a SampDeleteContext will be generated on + the Domain Context block, which will also completely free the linked + list of enumeration context blocks. + + + 2. Called by code within this DLL. + + The value of the Enumeration Handle is cast to a pointer to an + Enumeration Context. The validation involved is only to check the + pointer value field. It is assumed that bad handles will not be + passed by the code in this DLL. + + +*/ +//////////////////////////////////////////////////////////////////////////// + +// +// Include all those includes +// +#include <samsrvp.h> +#include <mappings.h> +#include <dslayer.h> +#include <filtypes.h> + +// +// +// The Maximum Number of Enumerations a Client can simultaneously do. Since +// we keep around some state in memory per enumeration operation and since +// we are the security system, we cannot alow a malicious client from running +// us out of memory. So limit on a per client basis. Our state info is size is +// qpprox 1K byte. +// + +#define SAMP_MAX_CLIENT_ENUMERATIONS 16 + +// +// DS limits the number of items that a given search can find. While in the +// SAM API, the approximate amount of memory is specified. This factor is +// is used in computing the number of entries required fro memory specified +// + +#define AVERAGE_MEMORY_PER_ENTRY 32 + +// Prototypes of Private Functions +// + +NTSTATUS +SampEnumerateAccountNamesDs( + IN SAMP_OBJECT_TYPE ObjectType, + IN OUT PSAMP_DS_ENUMERATION_CONTEXT *EnumerationContext, + OUT PSAMPR_ENUMERATION_BUFFER *Buffer, + IN ULONG PreferedMaximumLength, + IN ULONG Filter, + OUT PULONG CountReturned, + IN BOOLEAN TrustedClient + ); + +NTSTATUS +SampBuildDsEnumerationFilter( + IN SAMP_OBJECT_TYPE ObjectType, + IN ULONG UserAccountControlFilter, + OUT FILTER * DsFilter + ); + +VOID +SampFreeDsEnumerationFilter( + FILTER * DsFilter + ); + + +NTSTATUS +SampEnumerateAccountNamesRegistry( + IN SAMP_OBJECT_TYPE ObjectType, + IN OUT PSAM_ENUMERATE_HANDLE EnumerationContext, + OUT PSAMPR_ENUMERATION_BUFFER *Buffer, + IN ULONG PreferedMaximumLength, + IN ULONG Filter, + OUT PULONG CountReturned, + IN BOOLEAN TrustedClient + ); + + +NTSTATUS +SampPackDsEnumerationResults( + SEARCHRES *SearchRes, + IN SAMP_OBJECT_TYPE ObjectType, + IN ULONG ExpectedAttrCount, + IN ULONG Filter, + ULONG * Count, + PSAMPR_RID_ENUMERATION *RidEnumerationList + ); + +NTSTATUS +SampDoDsSearchContinuation( + IN SEARCHRES * SearchRes, + IN OUT PSAMP_DS_ENUMERATION_CONTEXT * EnumerationContext, + OUT BOOLEAN * MoreEntries + ); + +NTSTATUS +SampValidateEnumerationContext( + IN PSAMP_OBJECT DomainContext, + IN SAM_ENUMERATE_HANDLE EnumerationHandle, + IN ULONG MaxEnumerationContexts + ); + +ULONG +Ownstrlen( + CHAR * Sz + ); + + + +NTSTATUS +SampCopyRestart( + IN PRESTART OldRestart, + OUT PRESTART *NewRestart + ); + + + +NTSTATUS +SampEnumerateAccountNamesCommon( + IN SAMPR_HANDLE DomainHandle, + IN SAMP_OBJECT_TYPE ObjectType, + IN OUT PSAM_ENUMERATE_HANDLE EnumerationHandle, + OUT PSAMPR_ENUMERATION_BUFFER *Buffer, + IN ULONG PreferedMaximumLength, + IN ULONG Filter, + OUT PULONG CountReturned + ) + +/*++ + +Routine Description: + + This routine enumerates names of either user, group or alias accounts. + This routine is intended to directly support + + SamrEnumerateGroupsInDomain(), + SamrEnumerateAliasesInDomain() and + SamrEnumerateUsersInDomain(). + + This routine performs database locking, and context lookup (including + access validation). + + + + + All allocation for OUT parameters will be done using MIDL_user_allocate. + + + +Arguments: + + DomainHandle - The domain handle whose users or groups are to be enumerated. + + ObjectType - Indicates whether users or groups are to be enumerated. + + EnumerationHandle - API specific handle to allow multiple calls. The + caller should return this value in successive calls to retrieve + additional information. + + Buffer - Receives a pointer to the buffer containing the + requested information. The information returned is + structured as an array of SAM_ENUMERATION_INFORMATION data + structures. When this information is no longer needed, the + buffer must be freed using SamFreeMemory(). + + PreferedMaximumLength - Prefered maximum length of returned data + (in 8-bit bytes). This is not a hard upper limit, but serves + as a guide to the server. Due to data conversion between + systems with different natural data sizes, the actual amount + of data returned may be greater than this value. + + Filter - if ObjectType is users, the users can optionally be filtered + by setting this field with bits from the AccountControlField that + must match. Otherwise ignored. + + CountReturned - Receives the number of entries returned. + + +Return Value: + + STATUS_SUCCESS - The Service completed successfully, and there + are no additional entries. Entries may or may not have been + returned from this call. The CountReturned parameter indicates + whether any were. + + STATUS_MORE_ENTRIES - There are more entries which may be obtained + using successive calls to this API. This is a successful return. + + STATUS_ACCESS_DENIED - Caller does not have access to request the data. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + +--*/ +{ + NTSTATUS NtStatus; + NTSTATUS IgnoreStatus; + PSAMP_OBJECT Context; + SAMP_OBJECT_TYPE FoundType; + ACCESS_MASK DesiredAccess; + + SAMTRACE("SampEnumerateAccountNamesCommon"); + + + ASSERT( (ObjectType == SampGroupObjectType) || + (ObjectType == SampAliasObjectType) || + (ObjectType == SampUserObjectType) ); + + // + // Make sure we understand what RPC is doing for (to) us. + // + + ASSERT (DomainHandle != NULL); + ASSERT (EnumerationHandle != NULL); + ASSERT ( Buffer != NULL); + ASSERT ((*Buffer) == NULL); + ASSERT (CountReturned != NULL); + + + // + // Establish type-specific information + // + + DesiredAccess = DOMAIN_LIST_ACCOUNTS; + + + SampAcquireReadLock(); + + + // + // Validate type of, and access to object. + // + + Context = (PSAMP_OBJECT)DomainHandle; + NtStatus = SampLookupContext( + Context, + DesiredAccess, + SampDomainObjectType, + &FoundType + ); + + + if (NT_SUCCESS(NtStatus)) { + + + // + // If DS Object then Validate the Enumeration + // Context, as above. + // + + NtStatus = SampValidateEnumerationContext( + Context, + *EnumerationHandle, + SAMP_MAX_CLIENT_ENUMERATIONS + ); + + if ( NT_SUCCESS(NtStatus)) + { + // + // If Domain Context was a DS Object + // Remove the Enumeration Context, + // given by this handle + // From the list of enumeration Context's + // hanging out from the DS Object. + // + if ((0!=*EnumerationHandle) && (IsDsObject(Context))) + RemoveEntryList((LIST_ENTRY *)(*EnumerationHandle)); + + // + // Call our private worker routine + // + + NtStatus = SampEnumerateAccountNames( + ObjectType, + EnumerationHandle, + Buffer, + PreferedMaximumLength, + Filter, + CountReturned, + Context->TrustedClient + ); + + // + // Insert enumeration context blob into list + // + if ( // Operation Succeeded + NT_SUCCESS(NtStatus) + // Search is not over. More Entries remain to read + && *EnumerationHandle + // We are Talking of DS objects + && IsDsObject(Context) + ) + // + // Add this EnumerationContext to the list of enumeration + // context's maintained in the domain context block + // + InsertTailList(&(Context->TypeBody.Domain.DsEnumerationContext), + ((LIST_ENTRY *)*EnumerationHandle)); + + + } + + // + // De-reference the object, discarding changes + // + + IgnoreStatus = SampDeReferenceContext( Context, FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + // + // Free the read lock + // + + SampReleaseReadLock(); + + return(NtStatus); +} + + +NTSTATUS +SampEnumerateAccountNames( + IN SAMP_OBJECT_TYPE ObjectType, + IN OUT PSAM_ENUMERATE_HANDLE EnumerationContext, + OUT PSAMPR_ENUMERATION_BUFFER *Buffer, + IN ULONG PreferedMaximumLength, + IN ULONG Filter, + OUT PULONG CountReturned, + IN BOOLEAN TrustedClient + ) +/*++ + +Routine Description: + + This is the wrapper around the worker routine used to enumerate user, + group or alias accounts. This determines wether the domain is in the + DS or Registry, and then depending upon the outcome calls the + appropriate flavour of the routine + + + Note: THIS ROUTINE REFERENCES THE CURRENT TRANSACTION DOMAIN + (ESTABLISHED USING SampSetTransactioDomain()). THIS + SERVICE MAY ONLY BE CALLED AFTER SampSetTransactionDomain() + AND BEFORE SampReleaseReadLock(). + + + + All allocation for OUT parameters will be done using MIDL_user_allocate. + + + +Arguments: + + ObjectType - Indicates whether users or groups are to be enumerated. + + EnumerationContext - API specific handle to allow multiple calls. The + caller should return this value in successive calls to retrieve + additional information. + + Buffer - Receives a pointer to the buffer containing the + requested information. The information returned is + structured as an array of SAM_ENUMERATION_INFORMATION data + structures. When this information is no longer needed, the + buffer must be freed using SamFreeMemory(). + + PreferedMaximumLength - Prefered maximum length of returned data + (in 8-bit bytes). This is not a hard upper limit, but serves + as a guide to the server. Due to data conversion between + systems with different natural data sizes, the actual amount + of data returned may be greater than this value. + + Filter - if ObjectType is users, the users can optionally be filtered + by setting this field with bits from the AccountControlField that + must match. Otherwise ignored. + + CountReturned - Receives the number of entries returned. + + TrustedClient - says whether the caller is trusted or not. If so, + we'll ignore the SAMP_MAXIMUM_MEMORY_TO_USE restriction on data + returns. + + +Return Value: + + STATUS_SUCCESS - The Service completed successfully, and there + are no additional entries. Entries may or may not have been + returned from this call. The CountReturned parameter indicates + whether any were. + + STATUS_MORE_ENTRIES - There are more entries which may be obtained + using successive calls to this API. This is a successful return. + + STATUS_ACCESS_DENIED - Caller does not have access to request the data. + + +--*/ + +{ + NTSTATUS NtStatus = STATUS_SUCCESS; + + + if (IsDsObject(SampDefinedDomains[SampTransactionDomainIndex].Context)) + { + // + // DS Object - Do the DS thing + // + NtStatus = SampEnumerateAccountNamesDs( + ObjectType, + (PSAMP_DS_ENUMERATION_CONTEXT *) + EnumerationContext, + Buffer, + PreferedMaximumLength, + Filter, + CountReturned, + TrustedClient + ); + } + else + { + // + // Registry Object - Do the Registry thing + // + NtStatus = SampEnumerateAccountNamesRegistry( + ObjectType, + EnumerationContext, + Buffer, + PreferedMaximumLength, + Filter, + CountReturned, + TrustedClient + ); + } + + return NtStatus; + +} + + +NTSTATUS +SampEnumerateAccountNamesDs( + IN SAMP_OBJECT_TYPE ObjectType, + IN OUT PSAMP_DS_ENUMERATION_CONTEXT *EnumerationContext, + OUT PSAMPR_ENUMERATION_BUFFER *Buffer, + IN ULONG PreferedMaximumLength, + IN ULONG Filter, + OUT PULONG CountReturned, + IN BOOLEAN TrustedClient + ) +/*++ + +Routine Description: + + This routine does the work of enumeration for the DS case. + + + Note: THIS ROUTINE REFERENCES THE CURRENT TRANSACTION DOMAIN + (ESTABLISHED USING SampSetTransactioDomain()). THIS + SERVICE MAY ONLY BE CALLED AFTER SampSetTransactionDomain() + AND BEFORE SampReleaseReadLock(). + + + + All allocation for OUT parameters will be done using MIDL_user_allocate. + + + +Arguments: + + ObjectType - Indicates whether users or groups are to be enumerated. + + EnumerationContext - API specific handle to allow multiple calls. The + caller should return this value in successive calls to retrieve + additional information. + + Buffer - Receives a pointer to the buffer containing the + requested information. The information returned is + structured as an array of SAM_ENUMERATION_INFORMATION data + structures. When this information is no longer needed, the + buffer must be freed using SamFreeMemory(). + + PreferedMaximumLength - Prefered maximum length of returned data + (in 8-bit bytes). This is not a hard upper limit, but serves + as a guide to the server. Due to data conversion between + systems with different natural data sizes, the actual amount + of data returned may be greater than this value. + + Filter - if ObjectType is users, the users can optionally be filtered + by setting this field with bits from the AccountControlField that + must match. Otherwise ignored. + + CountReturned - Receives the number of entries returned. + + TrustedClient - says whether the caller is trusted or not. If so, + we'll ignore the SAMP_MAXIMUM_MEMORY_TO_USE restriction on data + returns. + + +Return Value: + + STATUS_SUCCESS - The Service completed successfully, and there + are no additional entries. Entries may or may not have been + returned from this call. The CountReturned parameter indicates + whether any were. + + STATUS_MORE_ENTRIES - There are more entries which may be obtained + using successive calls to this API. This is a successful return. + + STATUS_ACCESS_DENIED - Caller does not have access to request the data. + + +--*/ +{ + // + // Amount of memory that we may use. + // + + ULONG MemoryToUse = PreferedMaximumLength; + + // + // Specify the attributes that we want to read as part of the search. + // The Attributes specified in GenericReadAttrTypes are read from the DS, + // except for user objects ( due to filter on account control bits ) + // account control bits. + // + // NOTE + // The Ordering of the Rid and the Name + // must be the same for both User and Generic Attr Types. + // Further they should be the First two attributes. + // + + ATTRTYP GenericReadAttrTypes[]= + { + SAMP_UNKNOWN_OBJECTRID, + SAMP_UNKNOWN_OBJECTNAME, + }; + ATTRVAL GenericReadAttrVals[]= + { + {0,NULL}, + {0,NULL} + }; + + DEFINE_ATTRBLOCK2( + GenericReadAttrs, + GenericReadAttrTypes, + GenericReadAttrVals + ); + + ATTRTYP UserReadAttrTypes[]= + { + SAMP_FIXED_USER_USERID, + SAMP_USER_ACCOUNT_NAME, + SAMP_FIXED_USER_ACCOUNT_CONTROL, + }; + ATTRVAL UserReadAttrVals[]= + { + {0,NULL}, + {0,NULL}, + {0,NULL} + }; + + DEFINE_ATTRBLOCK3( + UserReadAttrs, + UserReadAttrTypes, + UserReadAttrVals + ); + + // + // Specify other local variables that we need + // + ATTRBLOCK *AttrsToRead; + NTSTATUS Status = STATUS_SUCCESS; + DSNAME *DomainObjectName; + PSAMPR_RID_ENUMERATION RidEnumerationList = NULL; + SEARCHRES *SearchRes; + BOOLEAN MoreEntries = FALSE; + ULONG MaximumNumberOfEntries; + SAMP_OBJECT_TYPE ObjectTypeForConversion; + + // + // Allocate memory to hold the result + // + + *Buffer = MIDL_user_allocate(sizeof(SAMPR_ENUMERATION_BUFFER)); + if (NULL==*Buffer) + { + Status = STATUS_NO_MEMORY; + goto Error; + } + + // + // Get The Domain Object Name + // + + DomainObjectName = + SampDefinedDomains[SampTransactionDomainIndex].Context->ObjectNameInDs; + + // + // Check for Memory Restrictions + // + + if ( (!TrustedClient) && + (PreferedMaximumLength > SAMP_MAXIMUM_MEMORY_TO_USE)) + { + MemoryToUse = SAMP_MAXIMUM_MEMORY_TO_USE; + } + + // + // Compute the maximim number of entries we want based on + // memory restrictions. Add plus 1 , so that at least 1 entry + // will be returned. + // + + MaximumNumberOfEntries = MemoryToUse/AVERAGE_MEMORY_PER_ENTRY + 1; + + // + // Specify the Apropriate Attributes to Read + // + + if (ObjectType == SampUserObjectType) + { + AttrsToRead = &UserReadAttrs; + ObjectTypeForConversion = SampUserObjectType; + } + else + { + AttrsToRead = &GenericReadAttrs; + ObjectTypeForConversion = SampUnknownObjectType; + } + + // + // Check if New Search. Accordingly Pass Arguments to SampDsDoSearch + // + + if (NULL == *EnumerationContext) + { + FILTER DsFilter; + + // + // Build the correct filter + // + + Status = SampBuildDsEnumerationFilter( + ObjectType, + Filter, + &DsFilter + ); + + if (!NT_SUCCESS(Status)) + goto Error; + + // + // Do the DS Search using the Filter, pass in NULL for restart + // + + Status = SampDsDoSearch( + NULL, + DomainObjectName, + &DsFilter, + ObjectTypeForConversion, + AttrsToRead, + MaximumNumberOfEntries, + &SearchRes + ); + // + // First Free the Filter Structure , irrespective of any + // Error returns + // + + SampFreeDsEnumerationFilter(&DsFilter); + + if (!NT_SUCCESS(Status)) + goto Error; + + + } + else + { + // + // Get the Restart Structure returned from previous search + // and do the search using it. + // + + PRESTART TmpRestart; + + TmpRestart = (*EnumerationContext)->Restart; + ASSERT(TmpRestart); + + Status = SampDsDoSearch( + TmpRestart, + DomainObjectName, + NULL, + ObjectTypeForConversion, + AttrsToRead, + MaximumNumberOfEntries, + &SearchRes + ); + if (!NT_SUCCESS(Status)) + goto Error; + } + + // + // Handle any paged results returned by the DS. + // + + Status = SampDoDsSearchContinuation( + SearchRes, + EnumerationContext, + &MoreEntries + ); + + if (!NT_SUCCESS(Status)) + goto Error; + + + // + // Search Succeeded. Pack the results into appropriate + // Rid Enumeration Buffers. + // + + Status = SampPackDsEnumerationResults( + SearchRes, + ObjectType, + AttrsToRead->attrCount, + Filter, + CountReturned, + &RidEnumerationList + ); + + +Error: + + if (!NT_SUCCESS(Status)) + { + // + // Error return, do the cleanup work. + // + + if (*EnumerationContext) + { + SampFreeRestart((*EnumerationContext)->Restart); + *EnumerationContext = NULL; + } + + if (*Buffer) + MIDL_user_free(*Buffer); + + } + else + { + if (MoreEntries) + Status = STATUS_MORE_ENTRIES; + (*Buffer)->EntriesRead = *CountReturned; + (*Buffer)->Buffer = RidEnumerationList; + } + + return Status; +} + + + + +NTSTATUS +SampPackDsEnumerationResults( + IN SEARCHRES *SearchRes, + IN SAMP_OBJECT_TYPE ObjectType, + IN ULONG ExpectedAttrCount, + IN ULONG Filter, + OUT ULONG * Count, + OUT PSAMPR_RID_ENUMERATION *RidEnumerationList + ) +/*++ + + Routine Description: + + This routine Packs the complex structures + returned by the core DS, into the Rid Enumeration + Structures required by SAM. + + Arguments: + + SearchRes SearchRes strucure as obtained from the DS. + + ExpectedAttrCount -- Passed by the caller. This is the count + of Attrs which the caller expects from the SearchRes + on a per search entry basis. Used to validate results + from the DS. + + Filter For User Accounts bits of the AccountControlId. + + Count Returned Count of Structures. + + RidEnumerationList - Array of structures of type + SAMP_RID_ENUMERATION passed back in this. + + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + PSAMPR_RID_ENUMERATION RidEnumerationListToReturn = NULL; + + // + // Initialize what we plan to return. + // + *RidEnumerationList = NULL; + *Count = 0; + + // + // Look if search turned up any results. + // If so stuff them in Rid Enumeration Array ( or whatever ) + // + if (SearchRes->count) + { + // + // Search Did Turn up Results + // + + ULONG Index; + ENTINFLIST * CurrentEntInf = &(SearchRes->FirstEntInf); + + // + // Allocate memory for an array of Rid Enumerations + // + RidEnumerationListToReturn = MIDL_user_allocate( + SearchRes->count + * sizeof(SAMPR_RID_ENUMERATION) + ); + if (NULL==RidEnumerationListToReturn) + { + Status = STATUS_NO_MEMORY; + goto Error; + } + + // + // Zero Memory just what we alloced. Useful for freeing up stuff + // in case we error'd out + // + RtlZeroMemory(RidEnumerationListToReturn,SearchRes->count + * sizeof(SAMPR_RID_ENUMERATION) + ); + + // + // Walk through the List turned up by the search and + // build the RidEnumeration Buffer + // + for (Index=0;Index<SearchRes->count;Index++) + { + + + // + // Assert the count of Attrs is normal. If Not + // Fail the Call if the returned count is not the + // Same as Expected Count + // + // + + ASSERT(CurrentEntInf->Entinf.AttrBlock.attrCount== + ExpectedAttrCount); + + if (CurrentEntInf->Entinf.AttrBlock.attrCount!= + ExpectedAttrCount) + { + Status = STATUS_UNSUCCESSFUL; + goto Error; + } + + // + // Assert that the Rid is in the right place + // + + ASSERT(CurrentEntInf->Entinf.AttrBlock.pAttr[0].attrTyp == + SampDsAttrFromSamAttr(SampUnknownObjectType, + SAMP_UNKNOWN_OBJECTRID)); + // + // Assert that the Name is in the right place + // + + ASSERT(CurrentEntInf->Entinf.AttrBlock.pAttr[1].attrTyp == + SampDsAttrFromSamAttr(SampUnknownObjectType, + SAMP_UNKNOWN_OBJECTNAME)); + + if (ObjectType == SampUserObjectType) + { + + // + // For User objects we need to filter based on account-control + // field + // + + ULONG AccountControlValue; + + // + // Assert that the Account control is in the right place + // + + ASSERT(CurrentEntInf->Entinf.AttrBlock.pAttr[2].attrTyp == + SampDsAttrFromSamAttr(SampUserObjectType, + SAMP_FIXED_USER_ACCOUNT_CONTROL)); + + // + // Get account control value and skip past if does + // not match the filter criteria + // + AccountControlValue = + *(CurrentEntInf->Entinf.AttrBlock. + pAttr[2].AttrVal.pAVal[0].pVal); + + if ((Filter!=0) && + ((Filter & AccountControlValue) != Filter)) + // + // Fails the Filter Test, skip this one + // + continue; + } + + // + // Stuff this entry in the buffer to be returned. + // + + // + // Copy the RID + // + + RtlCopyMemory( + &(RidEnumerationListToReturn[Index].RelativeId), + CurrentEntInf->Entinf.AttrBlock.pAttr[0].AttrVal.pAVal[0].pVal, + sizeof(ULONG) + ); + + // + // Copy the Name + // + + RidEnumerationListToReturn[Index].Name.Length = (USHORT) + (CurrentEntInf->Entinf.AttrBlock.pAttr[1].AttrVal. + pAVal[0].valLen)/2 -1; + RidEnumerationListToReturn[Index].Name.MaximumLength = (USHORT) + (CurrentEntInf->Entinf.AttrBlock.pAttr[1].AttrVal. + pAVal[0].valLen)/2 -1; + + + RidEnumerationListToReturn[Index].Name.Buffer = + MIDL_user_allocate(CurrentEntInf->Entinf.AttrBlock.pAttr[1]. + AttrVal.pAVal[0].valLen); + + if (NULL== (RidEnumerationListToReturn[Index]).Name.Buffer) + { + Status = STATUS_NO_MEMORY; + goto Error; + } + + RtlCopyMemory( RidEnumerationListToReturn[Index].Name.Buffer, + CurrentEntInf->Entinf.AttrBlock.pAttr[1].AttrVal. + pAVal[0].pVal, + CurrentEntInf->Entinf.AttrBlock.pAttr[1].AttrVal. + pAVal[0].valLen + ); + + // + // Go to the Next Entry + // + + CurrentEntInf = CurrentEntInf->pNextEntInf; + } + + // + // End of For Loop + // + + } + // + // Fill in the count and return buffer correctly + // + + *Count = SearchRes->count; + *RidEnumerationList = RidEnumerationListToReturn; + + +Error: + + if (!NT_SUCCESS(Status)) + { + // + // We Errored out, need to free all that we allocated + // + + if (NULL!=RidEnumerationListToReturn) + { + // + // We did allocate something + // + + ULONG Index; + + // + // First free all possible Names that we alloc'ed. + // + + for (Index=0;Index<SearchRes->count;Index++) + { + if (RidEnumerationListToReturn[Index].Name.Buffer) + MIDL_user_free( + RidEnumerationListToReturn[Index].Name.Buffer); + } + + // + // Free the buffer that we alloc'ed + // + + MIDL_user_free(RidEnumerationListToReturn); + RidEnumerationListToReturn = NULL; + *RidEnumerationList = NULL; + } + } + + return Status; + +} + + +NTSTATUS +SampDoDsSearchContinuation( + IN SEARCHRES * SearchRes, + IN OUT PSAMP_DS_ENUMERATION_CONTEXT * EnumerationContext, + OUT BOOLEAN * MoreEntries + ) +/*++ + Routine Description + + This routine will look if a PagedResults is present in + the Search Res argument that is passed in. If so, then it + will Try creating and EnumerationContext if NULL was passed + in the handle. Else it will free the old restart structure + from the Enumeration Context and copy in the new one passed + by the DS. + + Arguments: + SearchRes - Pointer to Search Results structure returned by + the DS. + + EnumerationContext - Holds a pointer to the enumeration Context + Structure + + MoreEntries - Inidicates that more entries are present. + + Return Values: + + STATUS_SUCCESS + STATUS_NO_MEMORY + + +-*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + PRESTART Restart = NULL; + + // + // Initialize this to False + // + + *MoreEntries = FALSE; + + // + // Now look at the Paged Results part of Search Results + // And create enumeration contexts as necessary. + // + + if ((SearchRes->PagedResult.fPresent) + && (SearchRes->PagedResult.pRestart)) + { + + // + // Search has more entries to it and therefore retrned + // a restart structure + // + + // + // If this was new search then we need to allocate a new + // Enumeration Context Structure + // + + if (NULL== *EnumerationContext) + { + // + // New search , allocate a new enumeration context structure + // + + PSAMP_DS_ENUMERATION_CONTEXT NewEnumerationContext; + + // + // Allocate Memory + // + NewEnumerationContext = MIDL_user_allocate( + sizeof(SAMP_DS_ENUMERATION_CONTEXT) + ); + if (NULL==NewEnumerationContext) + { + Status = STATUS_NO_MEMORY; + goto Error; + } + + // + // Initialize the List Head fields + // + + InitializeListHead((LIST_ENTRY *)(NewEnumerationContext)); + + // + // Initialize the Handle fields + // + + NewEnumerationContext->EnumerateHandle = + (SAM_ENUMERATE_HANDLE) NewEnumerationContext; + + // + // Initialize the Restart Pointer to NULL + // + + NewEnumerationContext->Restart = NULL; + + *EnumerationContext = NewEnumerationContext; + + } + + // + // Copy over the returned Enumeration handle. This Copying over + // is necessary because the DS allocs using the thread heap. , + // while this structure has to stay around for longer as client + // comes around again and again. + // + + Status = SampCopyRestart(SearchRes->PagedResult.pRestart, &Restart); + if (!NT_SUCCESS(Status)) + goto Error; + + // + // Free any old restart Structures, hanging from the enumeration + // context structures ( No op for NULL Restart Pointers) + // + + SampFreeRestart((*EnumerationContext)->Restart); + + // + // Keep the restart structure returned by the DS + // with the enumeration context + // + + (*EnumerationContext)->Restart = Restart; + Restart = NULL; + *MoreEntries = TRUE; + + } + else + { + // + // Search is Over, DS did not indicate that we have to come + // back for more entries. Free any state information that we + // created for this search + // + + if (NULL!= *EnumerationContext) + { + // + // We did allocate State Information for this + // Search + // + + SampFreeRestart((*EnumerationContext)->Restart); + MIDL_user_free(*EnumerationContext); + *EnumerationContext = NULL; + } + } + + +Error: + + // + // Do all the error cleanup. + // + + if (!NT_SUCCESS(Status)) + { + // + // Error ocurred, free all state information, NULL + // out the handle + // + + SampFreeRestart(Restart); + if (NULL!=*EnumerationContext) + { + SampFreeRestart((*EnumerationContext)->Restart); + MIDL_user_free(*EnumerationContext); + *EnumerationContext = NULL; + } + } + + return Status; + +} + +NTSTATUS +SampBuildDsEnumerationFilter( + IN SAMP_OBJECT_TYPE ObjectType, + IN ULONG UserAccountControlFilter, + OUT FILTER * DsFilter + ) +/*++ + + Routine Description: + + Builds a Filter structure for use in enumeration operations. + + Arguments: + + ObjectType - Type of SAM objects we want enumerated + UserAcountControlFilter - Bitmaks of bits to be set in Account Control field + when enumerating user objects + DsFilter -- Filter structure is built in here. + + NOTE This routine must be kept in sync with + SampFreeDsEnumerationFilter + + Return Values + + STATUS_SUCCESS + STATUS_NO_MEMORY + +--*/ +{ + + NTSTATUS Status = STATUS_SUCCESS; + + // Build the Appropriate Filter + switch(ObjectType) + { + case SampUserObjectType: + + if (UserAccountControlFilter!=0) + { + // + // Filtering on Account control field is Specified + // + + // We need a number which when bitwise anded + // with the filter gives a non zero result. + // It can be easily seen that only numbers having + // a value greater than or equal to the given filter + // can satisfy this criterion. + + // We will filter out only on the account control field + // rather than on the object class field. The assumption is that + // since this field will exist only for user objects, + // and we are filtering on objects having a value greater than something + // automatically we will get only user objects. Since the DS maintains only + // a limited set of inidices, and walks through the list and see if they match + // the given filter, a tradeoff between filter complexity and filter accuracy + // exisits. In case an Index is maintained on the user Account Field, then using + // this method should be quite O.K + + DsFilter->choice = FILTER_CHOICE_ITEM; + DsFilter->FilTypes.Item.choice = FI_CHOICE_EQUALITY; + DsFilter->FilTypes. + Item.FilTypes.ava.type = SampDsAttrFromSamAttr( + SampUserObjectType, + SAMP_FIXED_USER_ACCOUNT_CONTROL + ); + + DsFilter->FilTypes.Item.FilTypes.ava.Value.valLen = sizeof(ULONG); + DsFilter->FilTypes.Item.FilTypes.ava.Value.pVal = + MIDL_user_allocate(sizeof(ULONG)); + if (NULL==DsFilter->FilTypes.Item.FilTypes.ava.Value.pVal) + { + Status = STATUS_NO_MEMORY; + goto Error; + } + *((ULONG *)DsFilter->FilTypes.Item.FilTypes.ava.Value.pVal) + = UserAccountControlFilter; + break; + } + + // + // For the non User Account Control filter case we just + // fall through to the next case. + // + + default: + + + // + // Build our default Filter. + // + + DsFilter->choice = FILTER_CHOICE_ITEM; + DsFilter->FilTypes.Item.choice = FI_CHOICE_EQUALITY; + DsFilter->FilTypes.Item.FilTypes.ava.type = SampDsAttrFromSamAttr( + SampUnknownObjectType, + SAMP_UNKNOWN_OBJECTCLASS + ); + + DsFilter->FilTypes.Item.FilTypes.ava.Value.valLen = sizeof(ULONG); + DsFilter->FilTypes.Item.FilTypes.ava.Value.pVal = + MIDL_user_allocate(sizeof(ULONG)); + if (NULL==DsFilter->FilTypes.Item.FilTypes.ava.Value.pVal) + { + Status = STATUS_NO_MEMORY; + goto Error; + } + *((ULONG *)DsFilter->FilTypes.Item.FilTypes.ava.Value.pVal)= + SampDsClassFromSamObjectType(ObjectType); + + break; + + } + +Error: + return Status; + +} + + +VOID +SampFreeDsEnumerationFilter( + FILTER * DsFilter + ) +/*++ + + Routine Description: + + This routine frees a DS Filter as built by SampBuildDsEnumerationFilter + + NOTE: This routine must be kept in sync with SampBuildDsEnumerationFilter + + Argumements: + + DsFilter -- Pointer to a DS Filter Structure + + --*/ +{ + // + // For Now, Hopefully forever, our filters do not have anything hanging + // of them + // + + MIDL_user_free(DsFilter->FilTypes.Item.FilTypes.ava.Value.pVal); + +} + + + +NTSTATUS +SampEnumerateAccountNamesRegistry( + IN SAMP_OBJECT_TYPE ObjectType, + IN OUT PSAM_ENUMERATE_HANDLE EnumerationContext, + OUT PSAMPR_ENUMERATION_BUFFER *Buffer, + IN ULONG PreferedMaximumLength, + IN ULONG Filter, + OUT PULONG CountReturned, + IN BOOLEAN TrustedClient + ) + +/*++ + +Routine Description: + + This is the worker routine used to enumerate user, group or alias accounts + + + Note: THIS ROUTINE REFERENCES THE CURRENT TRANSACTION DOMAIN + (ESTABLISHED USING SampSetTransactioDomain()). THIS + SERVICE MAY ONLY BE CALLED AFTER SampSetTransactionDomain() + AND BEFORE SampReleaseReadLock(). + + + + All allocation for OUT parameters will be done using MIDL_user_allocate. + + + +Arguments: + + ObjectType - Indicates whether users or groups are to be enumerated. + + EnumerationContext - API specific handle to allow multiple calls. The + caller should return this value in successive calls to retrieve + additional information. + + Buffer - Receives a pointer to the buffer containing the + requested information. The information returned is + structured as an array of SAM_ENUMERATION_INFORMATION data + structures. When this information is no longer needed, the + buffer must be freed using SamFreeMemory(). + + PreferedMaximumLength - Prefered maximum length of returned data + (in 8-bit bytes). This is not a hard upper limit, but serves + as a guide to the server. Due to data conversion between + systems with different natural data sizes, the actual amount + of data returned may be greater than this value. + + Filter - if ObjectType is users, the users can optionally be filtered + by setting this field with bits from the AccountControlField that + must match. Otherwise ignored. + + CountReturned - Receives the number of entries returned. + + TrustedClient - says whether the caller is trusted or not. If so, + we'll ignore the SAMP_MAXIMUM_MEMORY_TO_USE restriction on data + returns. + + +Return Value: + + STATUS_SUCCESS - The Service completed successfully, and there + are no additional entries. Entries may or may not have been + returned from this call. The CountReturned parameter indicates + whether any were. + + STATUS_MORE_ENTRIES - There are more entries which may be obtained + using successive calls to this API. This is a successful return. + + STATUS_ACCESS_DENIED - Caller does not have access to request the data. + + +--*/ +{ + SAMP_V1_0A_FIXED_LENGTH_USER UserV1aFixed; + NTSTATUS NtStatus, TmpStatus; + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE TempHandle = NULL; + ULONG i, NamesToReturn, MaxMemoryToUse; + ULONG TotalLength,NewTotalLength; + PSAMP_OBJECT UserContext = NULL; + PSAMP_ENUMERATION_ELEMENT SampHead = NULL, + NextEntry = NULL, + NewEntry = NULL, + SampTail = NULL; + BOOLEAN MoreNames; + BOOLEAN LengthLimitReached = FALSE; + BOOLEAN FilteredName; + PSAMPR_RID_ENUMERATION ArrayBuffer = NULL; + ULONG ArrayBufferLength; + LARGE_INTEGER IgnoreLastWriteTime; + UNICODE_STRING AccountNamesKey; + SID_NAME_USE IgnoreUse; + + SAMTRACE("SampEnumerateAccountNames"); + + + // + // Open the registry key containing the account names + // + + NtStatus = SampBuildAccountKeyName( + ObjectType, + &AccountNamesKey, + NULL + ); + + if ( NT_SUCCESS(NtStatus) ) { + + // + // Now try to open this registry key so we can enumerate its + // sub-keys + // + + + InitializeObjectAttributes( + &ObjectAttributes, + &AccountNamesKey, + OBJ_CASE_INSENSITIVE, + SampKey, + NULL + ); + + SampDumpNtOpenKey((KEY_READ), &ObjectAttributes, 0); + + NtStatus = RtlpNtOpenKey( + &TempHandle, + (KEY_READ), + &ObjectAttributes, + 0 + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Read names until we have exceeded the preferred maximum + // length or we run out of names. + // + + NamesToReturn = 0; + SampHead = NULL; + SampTail = NULL; + MoreNames = TRUE; + + NewTotalLength = 0; + TotalLength = 0; + + if ( TrustedClient ) { + + // + // We place no restrictions on the amount of memory used + // by a trusted client. Rely on their + // PreferedMaximumLength to limit us instead. + // + + MaxMemoryToUse = 0xffffffff; + + } else { + + MaxMemoryToUse = SAMP_MAXIMUM_MEMORY_TO_USE; + } + + while (MoreNames) { + + UNICODE_STRING SubKeyName; + USHORT LengthRequired; + + // + // Try reading with a DEFAULT length buffer first. + // + + LengthRequired = 32; + + NewTotalLength = TotalLength + + sizeof(UNICODE_STRING) + + LengthRequired; + + // + // Stop if SAM or user specified length limit reached + // + + if ( ( (TotalLength != 0) && + (NewTotalLength >= PreferedMaximumLength) ) || + ( NewTotalLength > MaxMemoryToUse ) + ) { + + NtStatus = STATUS_SUCCESS; + break; // Out of while loop, MoreNames = TRUE + } + + NtStatus = SampInitUnicodeString(&SubKeyName, LengthRequired); + if (!NT_SUCCESS(NtStatus)) { + break; // Out of while loop + } + + NtStatus = RtlpNtEnumerateSubKey( + TempHandle, + &SubKeyName, + *EnumerationContext, + &IgnoreLastWriteTime + ); + + SampDumpRtlpNtEnumerateSubKey(&SubKeyName, + EnumerationContext, + IgnoreLastWriteTime); + + if (NtStatus == STATUS_BUFFER_OVERFLOW) { + + // + // The subkey name is longer than our default size, + // Free the old buffer. + // Allocate the correct size buffer and read it again. + // + + SampFreeUnicodeString(&SubKeyName); + + LengthRequired = SubKeyName.Length; + + NewTotalLength = TotalLength + + sizeof(UNICODE_STRING) + + LengthRequired; + + // + // Stop if SAM or user specified length limit reached + // + + if ( ( (TotalLength != 0) && + (NewTotalLength >= PreferedMaximumLength) ) || + ( NewTotalLength > MaxMemoryToUse ) + ) { + + NtStatus = STATUS_SUCCESS; + break; // Out of while loop, MoreNames = TRUE + } + + // + // Try reading the name again, we should be successful. + // + + NtStatus = SampInitUnicodeString(&SubKeyName, LengthRequired); + if (!NT_SUCCESS(NtStatus)) { + break; // Out of while loop + } + + NtStatus = RtlpNtEnumerateSubKey( + TempHandle, + &SubKeyName, + *EnumerationContext, + &IgnoreLastWriteTime + ); + + SampDumpRtlpNtEnumerateSubKey(&SubKeyName, + EnumerationContext, + IgnoreLastWriteTime); + + } + + + // + // Free up our buffer if we failed to read the key data + // + + if (!NT_SUCCESS(NtStatus)) { + + SampFreeUnicodeString(&SubKeyName); + + // + // Map a no-more-entries status to success + // + + if (NtStatus == STATUS_NO_MORE_ENTRIES) { + + MoreNames = FALSE; + NtStatus = STATUS_SUCCESS; + } + + break; // Out of while loop + } + + // + // We've allocated the subkey and read the data into it + // Stuff it in an enumeration element. + // + + NewEntry = MIDL_user_allocate(sizeof(SAMP_ENUMERATION_ELEMENT)); + if (NewEntry == NULL) { + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + } else { + + *(PUNICODE_STRING)&NewEntry->Entry.Name = SubKeyName; + + // + // Now get the Rid value of this named + // account. We must be able to get the + // name or we have an internal database + // corruption. + // + + NtStatus = SampLookupAccountRidRegistry( + ObjectType, + (PUNICODE_STRING)&NewEntry->Entry.Name, + STATUS_INTERNAL_DB_CORRUPTION, + &NewEntry->Entry.RelativeId, + &IgnoreUse + ); + + ASSERT(NtStatus != STATUS_INTERNAL_DB_CORRUPTION); + + if (NT_SUCCESS(NtStatus)) { + + FilteredName = TRUE; + + if ( ( ObjectType == SampUserObjectType ) && + ( Filter != 0 ) ) { + + // + // We only want to return users with a + // UserAccountControl field that matches + // the filter passed in. Check here. + // + + NtStatus = SampCreateAccountContext( + SampUserObjectType, + NewEntry->Entry.RelativeId, + TRUE, // Trusted client + TRUE, // Account exists + &UserContext + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + NtStatus = SampRetrieveUserV1aFixed( + UserContext, + &UserV1aFixed + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + if ( ( UserV1aFixed.UserAccountControl & + Filter ) == 0 ) { + + FilteredName = FALSE; + SampFreeUnicodeString( &SubKeyName ); + } + } + + SampDeleteContext( UserContext ); + } + } + + *EnumerationContext += 1; + + if ( NT_SUCCESS( NtStatus ) && ( FilteredName ) ) { + + NamesToReturn += 1; + + TotalLength = TotalLength + (ULONG) + NewEntry->Entry.Name.MaximumLength; + + NewEntry->Next = NULL; + + if( SampHead == NULL ) { + + ASSERT( SampTail == NULL ); + + SampHead = SampTail = NewEntry; + } + else { + + // + // add this new entry to the list end. + // + + SampTail->Next = NewEntry; + SampTail = NewEntry; + } + + } else { + + // + // Entry was filtered out, or error getting + // filter information. + // + + MIDL_user_free( NewEntry ); + } + + } else { + + // + // Error looking up the RID + // + + MIDL_user_free( NewEntry ); + } + } + + + // + // Free up our subkey name + // + + if (!NT_SUCCESS(NtStatus)) { + + SampFreeUnicodeString(&SubKeyName); + break; // Out of whle loop + } + + } // while + + + + TmpStatus = NtClose( TempHandle ); + ASSERT( NT_SUCCESS(TmpStatus) ); + + } + + + SampFreeUnicodeString( &AccountNamesKey ); + } + + + + + if ( NT_SUCCESS(NtStatus) ) { + + + + + // + // If we are returning the last of the names, then change our + // enumeration context so that it starts at the beginning again. + // + + if (!( (NtStatus == STATUS_SUCCESS) && (MoreNames == FALSE))) { + + NtStatus = STATUS_MORE_ENTRIES; + } + + + + // + // Set the number of names being returned + // + + (*CountReturned) = NamesToReturn; + + + // + // Build a return buffer containing an array of the + // SAM_ENUMERATION_INFORMATIONs pointed to by another + // buffer containing the number of elements in that + // array. + // + + (*Buffer) = MIDL_user_allocate( sizeof(SAMPR_ENUMERATION_BUFFER) ); + + if ( (*Buffer) == NULL) { + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + } else { + + (*Buffer)->EntriesRead = (*CountReturned); + + ArrayBufferLength = sizeof( SAM_RID_ENUMERATION ) * + (*CountReturned); + ArrayBuffer = MIDL_user_allocate( ArrayBufferLength ); + (*Buffer)->Buffer = ArrayBuffer; + + if ( ArrayBuffer == NULL) { + + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + MIDL_user_free( (*Buffer) ); + + } else { + + // + // Walk the list of return entries, copying + // them into the return buffer + // + + NextEntry = SampHead; + i = 0; + while (NextEntry != NULL) { + + NewEntry = NextEntry; + NextEntry = NewEntry->Next; + + ArrayBuffer[i] = NewEntry->Entry; + i += 1; + + MIDL_user_free( NewEntry ); + } + + } + + } + + + + } + + + + + + if ( !NT_SUCCESS(NtStatus) ) { + + // + // Free the memory we've allocated + // + + NextEntry = SampHead; + while (NextEntry != NULL) { + + NewEntry = NextEntry; + NextEntry = NewEntry->Next; + + if (NewEntry->Entry.Name.Buffer != NULL ) MIDL_user_free( NewEntry->Entry.Name.Buffer ); + MIDL_user_free( NewEntry ); + } + + (*EnumerationContext) = 0; + (*CountReturned) = 0; + (*Buffer) = NULL; + + } + + return(NtStatus); + +} + +NTSTATUS +SampValidateEnumerationContext( + IN PSAMP_OBJECT DomainContext, + IN SAM_ENUMERATE_HANDLE EnumerationHandle, + IN ULONG MaxClientEnumerations + ) +/*++ + + Routine Description + + This routine Traverses the linked list of enumeration + context's that are kept with the Domain Context that is + passed in and tries to find a enumeration context, whose + handle value matches the passed in Handle. + + Arguments: + DomainContext - The DomainContext pointer + EnumerationHandle -The enumeration Handle + MaxClientEnumerations - Max Limit of simultaneous enumerations + from single non trusted client + + Return Values: + + STATUS_SUCCESS -- The Enumeration Handle is safe to be case + as a pointer and used as a Enumeration Context + STATUS_ACCESS_DENIED -- A New Search was requested and the number + of simultaneous enumerations from this client is + already at the limit. + STATUS_INVALID_HANDLE -- The walk through the enumeration context + list did not turn up any matches. Therefore + the handle must be invalid. + +--*/ +{ + + NTSTATUS Status = STATUS_SUCCESS; + + ASSERT(DomainContext->ObjectType == SampDomainObjectType); + + if (SampDomainObjectType!= DomainContext->ObjectType) + { + Status = STATUS_INVALID_HANDLE; + } + else if (IsDsObject(DomainContext)) + { + LIST_ENTRY *HeadOfList= + &(DomainContext->TypeBody.Domain.DsEnumerationContext); + LIST_ENTRY *CurrentItem ; + ULONG Count = 0; + + CurrentItem = HeadOfList->Flink; + + // + // Our Default Return + // + + Status = STATUS_INVALID_HANDLE; + + // + // Traverse the Circular List for a match. + // If EnumerationHandle is a NULL, this will just walk the + // loop taking a count of enumeration's that are proceeding + // on this client. + // + + while (CurrentItem!=HeadOfList) + { + + if ((0!=EnumerationHandle) + && (EnumerationHandle==((SAMP_DS_ENUMERATION_CONTEXT *) + CurrentItem)->EnumerateHandle)) + { + // + // Existing Search handle Matches + // + + + // + // Assert that the contents of Enumeration Handle is same + // as the pointer to the context block that matches. + // + ASSERT(CurrentItem==(LIST_ENTRY *) + EnumerationHandle); + Status = STATUS_SUCCESS; + break; + } + + // + // Increment the count of enumeration context's we have so far visited + // + + Count++; + + // + // Traverse to the Next Element in List + // + + CurrentItem = CurrentItem->Flink; + } + + // + // New Search was requested + // verify that count of enumeration contexts is within limits + // for non trusted clients. For trusted clients override limit check + // + + if ((0==EnumerationHandle) + && (!(DomainContext->TrustedClient) + || (Count < MaxClientEnumerations))) + { + // + // Either trusted client or count of enumerations is within limits + // + + Status = STATUS_SUCCESS; + } + } + + return Status; +} + + + + +ULONG +Ownstrlen( + CHAR * Sz + ) +/*++ + + Routine Description + + String Length function for ASCII Null terminated strings. Own version + as we are not yet inclined to use C-Runtime + + Arguments + + Sz - NULL terminated String Whose lenght we eant to count + + Return Values + + Length of String + +--*/ +{ + ULONG Count = 0; + + ASSERT(Sz); + + while (*Sz) + { + Sz++; + Count++; + } + + return Count; +} + + + + +VOID +SampFreeRestart( + IN PRESTART Restart + ) +/*++ + + Routine Description: + + This Routine frees a Restart structure as defined by the DS. + + Arguments + + Restart Pointer to the Restart funcion + + Return values: + + None +--*/ +{ + + if (Restart!= NULL) + { + if (Restart->szIndexName) + MIDL_user_free(Restart->szIndexName); + if (Restart->rgbDBKeyUpper) + MIDL_user_free(Restart->rgbDBKeyUpper); + if (Restart->rgbDBKeyCurrent) + MIDL_user_free(Restart->rgbDBKeyCurrent); + if (Restart->rgbDBKeyLower) + MIDL_user_free(Restart->rgbDBKeyLower); + if (Restart->rgbSubString) + MIDL_user_free(Restart->rgbSubString); + + MIDL_user_free(Restart); + } +} + + +NTSTATUS +SampCopyRestart( + IN PRESTART OldRestart, + OUT PRESTART *NewRestart + ) +/*++ + + Routine Description: + + This Routine Copies a Restart Structure + + Arguments: + + OldRestart - Old Structure + NewRestart - New Structure + + Return Values: + + STATUS_SUCCESS + STATUS_NO_MEMORY + + --*/ +{ + +////////////////////////////////////////////////////////////// +// Define This Macro so that we may save some typing +// And also make the code a little more readable + +#define ALLOC_AND_COPY(ToAlloc,ToCopy,Len)\ + if (ToCopy !=NULL)\ + {\ + ToAlloc = MIDL_user_allocate(Len);\ + if (NULL==ToAlloc)\ + {\ + Status = STATUS_NO_MEMORY;\ + goto Error;\ + }\ + RtlCopyMemory(ToAlloc,ToCopy,Len);\ + } + +////////////////////////////////////////////////////////////// + + + NTSTATUS Status = STATUS_SUCCESS; + + *NewRestart = NULL; + if (OldRestart!=NULL) + { + // Alloc memory for 1 restart structure + *NewRestart = MIDL_user_allocate(sizeof(RESTART)); + if (NULL == *NewRestart) + { + Status = STATUS_NO_MEMORY; + goto Error; + } + + // Zero the Memory + RtlZeroMemory(*NewRestart,sizeof(RESTART)); + + // Alloc and Copy Strlen + ALLOC_AND_COPY((*NewRestart)->szIndexName, + OldRestart->szIndexName, + // Not yet prepared to use C Runtine + Ownstrlen(OldRestart->szIndexName)+1 + ); + ALLOC_AND_COPY((*NewRestart)->rgbDBKeyLower, + OldRestart->rgbDBKeyLower, + OldRestart->cbDBKeyLower + ); + ALLOC_AND_COPY((*NewRestart)->rgbDBKeyCurrent, + OldRestart->rgbDBKeyCurrent, + OldRestart->cbDBKeyCurrent + ); + ALLOC_AND_COPY((*NewRestart)->rgbDBKeyUpper, + OldRestart->rgbDBKeyUpper, + OldRestart->cbDBKeyUpper + ); + ALLOC_AND_COPY((*NewRestart)->rgbSubString, + OldRestart->rgbSubString, + OldRestart->cbSubString + ); + // Copy the remaining simple Data Types. + (*NewRestart)->StartDNT = OldRestart->StartDNT; + (*NewRestart)->cbDBKeyLower = OldRestart->cbDBKeyLower; + (*NewRestart)->cbDBKeyCurrent = OldRestart->cbDBKeyCurrent; + (*NewRestart)->cbDBKeyUpper = OldRestart->cbDBKeyUpper; + (*NewRestart)->ulSearchType = OldRestart->ulSearchType; + (*NewRestart)->ulSearchRootDnt = OldRestart-> ulSearchRootDnt; + (*NewRestart)->ulSearchRootDnt = OldRestart-> ulSearchRootPDNT; + (*NewRestart)->iSearchRoot = OldRestart->iSearchRoot; + (*NewRestart)->fCursored = OldRestart->fCursored; + (*NewRestart)->attToReadFromKey = OldRestart->attToReadFromKey; + (*NewRestart)->ulComparisonToApply = OldRestart->ulComparisonToApply; + (*NewRestart)->cbSubString = OldRestart->cbSubString; + } +Error: + + // If we were not successful free everything + if (!NT_SUCCESS(Status)) + { + SampFreeRestart(*NewRestart); + *NewRestart = NULL; + } + + return Status; +} + + + + + + + + + diff --git a/private/newsam2/server/exe/attrtest.c b/private/newsam2/server/exe/attrtest.c new file mode 100644 index 000000000..c50ae7a36 --- /dev/null +++ b/private/newsam2/server/exe/attrtest.c @@ -0,0 +1,750 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + attrtest.c + +Abstract: + + This file contains various test routines that call internal SAM server + routines for a unit test. + + THE ROUTINES IN THIS FILE ARE FOR SAM SEVER TEST PURPOSES ONLY. + + There are a number of server-side unit tests aimed at private routines + within the SAM server. These tests are defined in this file. This file + should only be compiled into the SAM server when it is built as a stand- + alone executable (built in the \um subdir). Therefore, it is only listed + in the sources file in the \um subdir. + + Because the SAM code relies on a fairly large amount of state information + being in place from the initialization of the server, it is difficult to + write a unit test that "plugs in" to the server from the outside. + + Tests: + + BasicAttributeTest - This test creates a full set of SAM user-object + attributes and context, writes this to the DS, reads it back into a + SAM context buffer, and compares the before-and-after buffers for byte + equivalence. + + AdvancedAttributeTest - This test calls the attribute Get/Set routines + in attr.c, with SAM objects from the DS backing store. + +Author: + + Chris Mayhall (ChrisMay) 19-Jun-1996 + +Environment: + + User Mode - Win32 + +Revision History: + + ChrisMay 19-Jun-1996 + Created initial file. + ChrisMay 25-Jun-1996 + Fleshed out more of the basic attribute test--now compares input/out- + put contexts and their attribute buffers (OnDisk member) for errors + during test, in the form of non-matching bytes. Added variable-attr- + ibute tests and verification. + ChrisMay 02-Jul-1996 + Added Unicode-string test case and verification. Variable attributes. + ChrisMay 03-Jul-1996 + Added access attribute test case and verification. Added test for + SampSetVariableAttributes with buffer resizing. + +--*/ + +#include <samsrvp.h> +#include <dsutilp.h> +#include <dslayer.h> +#include <mappings.h> +#include <testutil.h> + +// Forward Declarations + +NTSTATUS +SampValidateAttributes( + IN PSAMP_OBJECT Context, + IN ULONG AttributeGroup + ); + +NTSTATUS +SampSetVariableAttribute( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex, + IN ULONG Qualifier, + IN PUCHAR Buffer, + IN ULONG Length + ); + +// Private debugging display routine is enabled when ATTRTEST_DBG_PRINTF = 1. + +#define ATTRTEST_DBG_PRINTF 0 + +#if (ATTRTEST_DBG_PRINTF == 1) +#define DebugPrint printf +#else +#define DebugPrint +#endif + +//===========================BASIC ATTRIBUTE TEST============================= + +NTSTATUS +BasicAttributeTest( + VOID *Parameter + ) +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + NTSTATUS NtStatus2 = STATUS_INTERNAL_ERROR; + PDSNAME ObjectDsName = NULL; + WCHAR Buffer1[128]; + WCHAR *NamePrefix = NULL; + ULONG NamePrefixLength = 0; + ULONG Rid = 1001; + BYTE AttributeBuffer1[BUF_SIZE]; + BYTE AttributeBuffer2[BUF_SIZE]; + SAMP_OBJECT WriteObjectContext; + SAMP_OBJECT ReadObjectContext; + BOOLEAN UseKeyHandle = FALSE; + ULONG AttributeGroup = SAMP_FIXED_ATTRIBUTES; + BOOLEAN Identical = FALSE; + UCHAR DomainSid[] = {1,4,1,2,3,4,5,6,1,0,0,0,2,0,0,0,3,0,0,0,4,0,0,0}; + + // BUG: Hard-coded ObjectName used in test data. + + WCHAR ObjectName[] = L"/o=NT/ou=DS/cn=Configuration/cn=User1"; + + // Set up the DS attribute blocks used to initialize the test. These + // attributes are not actually part of the test, but only serve to set + // up the initial environment for the attribute test. + + ATTRVAL SecurityDescriptorVal[] = + { + {sizeof(SECURITY_DESCRIPTOR), NULL}, + {sizeof(ULONG),(UCHAR *) &Rid} + }; + + ATTRTYP SecurityDescriptorType[] = + { + SAMP_USER_SECURITY_DESCRIPTOR, + SAMP_FIXED_USER_USERID + }; + + DEFINE_ATTRBLOCK2(SdBlock, SecurityDescriptorType, SecurityDescriptorVal); + + // The DS requires a default security descriptor for object creation, so + // put one together (in self-relative format). + + NtStatus = BuildDefaultSecurityDescriptor( + &(SecurityDescriptorVal[0].pVal), + &(SecurityDescriptorVal[0].valLen)); + + if (!NT_SUCCESS(NtStatus)) + { + return(NtStatus); + } + + // Set up the object's DS name. + + ObjectDsName = (PDSNAME)Buffer1; + + SampInitializeDsName(ObjectDsName, + NamePrefix, + NamePrefixLength, + ObjectName, + sizeof(ObjectName)); + + RtlZeroMemory(AttributeBuffer1, BUF_SIZE); + + NtStatus = BuildObjectContext(SampUserObjectType, + ObjectDsName, + AttributeBuffer1, + &WriteObjectContext); + + if (!NT_SUCCESS(NtStatus)) + { + return(NtStatus); + } + + RtlZeroMemory(AttributeBuffer2, BUF_SIZE); + + NtStatus = BuildObjectContext(SampUserObjectType, + ObjectDsName, + AttributeBuffer2, + &ReadObjectContext); + + if (NT_SUCCESS(NtStatus)) + { + DebugPrint("ATTRTEST: Built Object(%d) Context Successfully\n", + SampUserObjectType); + + // Object is a DSNAME data type; create a default user object + // for the test. + + NtStatus = SampDsCreateObject(ObjectDsName, + SampUserObjectType, + &SdBlock, + (PSID)DomainSid + ); + + if (NT_SUCCESS(NtStatus)) + { + // Attempt to set ALL of the SAM user attributes on the newly + // created object. Note that UseKeyHandle is only used for + // SAM-registry operations, so is NULL here. + + + NtStatus = SampStoreObjectAttributes(&WriteObjectContext, + UseKeyHandle); + + + if (NT_SUCCESS(NtStatus)) + { + DebugPrint("SampStoreObjectAttributes status = 0x%lx\n", + NtStatus); + + // Reset the dirty flags on the written attributes so + // that the later context comparison will pass (note + // that SampStoreObjectAttributes sets these flags to + // FALSE. + + WriteObjectContext.FixedDirty = TRUE; + WriteObjectContext.VariableDirty = TRUE; + + // Reset the "FixedValid" context flag to FALSE so that + // the validation routine will re-read the attributes + // from the DS backing store. + + ReadObjectContext.FixedValid = FALSE; + + // Validate the fixed-length attributes. + + NtStatus = SampValidateAttributes(&ReadObjectContext, + AttributeGroup); + + if (NT_SUCCESS(NtStatus)) + { + // Compare the input/output contexts to make sure + // the data hasn't been changed. + + Identical = CompareContexts(&WriteObjectContext, + &ReadObjectContext); + + if (TRUE == Identical) + { + DebugPrint("Contexts are identical\n"); + } + else + { + DebugPrint("Contexts are different\n"); + } + } + else + { + DebugPrint("SampValidateAttributes error = 0x%lx\n", + NtStatus); + } + + if (NT_SUCCESS(NtStatus) && (TRUE == Identical)) + { + // Reset the "VariableValid" context flag to FALSE so + // that the validation routine will re-read the attr- + // ibutes from the DS backing store. + + ReadObjectContext.VariableValid = FALSE; + AttributeGroup = SAMP_VARIABLE_ATTRIBUTES; + + // Validate the variable-length attributes. + + NtStatus = SampValidateAttributes(&ReadObjectContext, + AttributeGroup); + + if (NT_SUCCESS(NtStatus)) + { + // Compare the input/output contexts to make sure + // the data hasn't been changed. + + Identical = CompareContexts(&WriteObjectContext, + &ReadObjectContext); + + if (TRUE == Identical) + { + DebugPrint("Contexts are identical\n"); + } + else + { + DebugPrint("Contexts are different\n"); + } + } + else + { + DebugPrint( + "SampValidateAttributes error = 0x%lx\n", + NtStatus); + } + } + } + else + { + DebugPrint("SampStoreObjectAttributes error = 0x%lx\n", + NtStatus); + } + + // Clean up so that the test can be re-run. + + NtStatus = SampDsDeleteObject(ObjectDsName); + + if (!NT_SUCCESS(NtStatus)) + { + DebugPrint("SampDsDeleteObject error = 0x%lx\n", NtStatus); + } + } + else + { + DebugPrint("SampDsCreateObject error = 0x%lx\n", NtStatus); + } + } + + // Commit the DS transaction. + + NtStatus2 = SampMaybeEndDsTransaction(FALSE); + + ASSERT(!SampExistsDsTransaction()); + + if (!NT_SUCCESS(NtStatus2) ) + { + DebugPrint("SampMaybeEndDsTransaction error = 0x%lx\n", NtStatus2); + } + + if ((NT_SUCCESS(NtStatus)) && NT_SUCCESS(NtStatus2) && (TRUE == Identical)) + { + printf("\nBasicAttributeTest PASSED\n"); + } + else + { + printf("\nBasicAttributeTest FAILED\n"); + } + + return(NtStatus); +} + + + +NTSTATUS +AdvancedAttributeTest( + VOID *Parameter + ) +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + NTSTATUS NtStatus2 = STATUS_INTERNAL_ERROR; + PDSNAME ObjectDsName = NULL; + WCHAR Buffer1[128]; + WCHAR *NamePrefix = NULL; + ULONG NamePrefixLength = 0; + ULONG Rid = 1001; + BYTE AttributeBuffer1[BUF_SIZE]; + PBYTE AttributeBuffer2 = NULL; + SAMP_OBJECT WriteObjectContext; + SAMP_OBJECT ReadObjectContext; + BOOLEAN UseKeyHandle = FALSE; + ULONG AttributeGroup = SAMP_FIXED_ATTRIBUTES; + BOOLEAN Identical = FALSE; + BOOLEAN MakeCopy = TRUE; + PVOID FixedData = NULL; + UNICODE_STRING UnicodeString; + ULONG Revision = 0; + PSECURITY_DESCRIPTOR SecurityDescriptor = NULL; + PLARGE_INTEGER LargeIntArray = NULL; + ULONG Count = 0; + LOGON_HOURS LogonHours; + ULONG Qualifier = 1; + WCHAR NewAccountName[] = L"NewAccountName"; + ULONG Length = sizeof(NewAccountName); + + // BUG: Hard-coded ObjectName used in test data. + + WCHAR ObjectName[] = L"/o=NT/ou=DS/cn=Configuration/cn=User1"; + + // Set up the DS attribute blocks used to initialize the test. These + // attributes are not actually part of the test, but only serve to set + // up the initial environment for the attribute test. + + ATTRVAL SecurityDescriptorVal[] = + { + {sizeof(SECURITY_DESCRIPTOR), NULL}, + {sizeof(ULONG),(UCHAR *) &Rid} + }; + + ATTRTYP SecurityDescriptorType[] = + { + SAMP_USER_SECURITY_DESCRIPTOR, + SAMP_FIXED_USER_USERID + }; + + DEFINE_ATTRBLOCK2(SdBlock, SecurityDescriptorType, SecurityDescriptorVal); + + UCHAR DomainSid[] = {1,4,1,2,3,4,5,6,1,0,0,0,2,0,0,0,3,0,0,0,4,0,0,0}; + + // The DS requires a default security descriptor for object creation, so + // put one together (in self-relative format). + + NtStatus = BuildDefaultSecurityDescriptor( + &(SecurityDescriptorVal[0].pVal), + &(SecurityDescriptorVal[0].valLen)); + + if (!NT_SUCCESS(NtStatus)) + { + return(NtStatus); + } + + // Set up the object's DS name. + + ObjectDsName = (PDSNAME)Buffer1; + + SampInitializeDsName(ObjectDsName, + NamePrefix, + NamePrefixLength, + ObjectName, + sizeof(ObjectName)); + + RtlZeroMemory(AttributeBuffer1, BUF_SIZE); + + NtStatus = BuildObjectContext(SampUserObjectType, + ObjectDsName, + AttributeBuffer1, + &WriteObjectContext); + + if (!NT_SUCCESS(NtStatus)) + { + return(NtStatus); + } + + // The second attribute buffer must be allocated from the heap because + // SampSetVariableAttribute may grow the buffer, releasing the original + // buffer. + + AttributeBuffer2 = RtlAllocateHeap(RtlProcessHeap(), 0, BUF_SIZE); + + if (NULL != AttributeBuffer2) + { + RtlZeroMemory(AttributeBuffer2, BUF_SIZE); + + NtStatus = BuildObjectContext(SampUserObjectType, + ObjectDsName, + AttributeBuffer2, + &ReadObjectContext); + } + else + { + NtStatus = STATUS_NO_MEMORY; + } + + if (NT_SUCCESS(NtStatus)) + { + DebugPrint("ATTRTEST: Built Object(%d) Context Successfully\n", + SampUserObjectType); + + // Object is a DSNAME data type; create a default user object + // for the test. + + NtStatus = SampDsCreateObject(ObjectDsName, + SampUserObjectType, + &SdBlock, + (PSID) DomainSid + ); + + if (NT_SUCCESS(NtStatus)) + { + // Attempt to set ALL of the SAM user attributes on the newly + // created object. Note that UseKeyHandle is only used for + // SAM-registry operations, so is NULL here. + + + NtStatus = SampStoreObjectAttributes(&WriteObjectContext, + UseKeyHandle); + + + if (NT_SUCCESS(NtStatus)) + { + DebugPrint("SampStoreObjectAttributes status = 0x%lx\n", + NtStatus); + + // Reset the dirty flags on the written attributes so + // that the later context comparison will pass (note + // that SampStoreObjectAttributes sets these flags to + // FALSE. + + WriteObjectContext.FixedDirty = TRUE; + WriteObjectContext.VariableDirty = TRUE; + + // Reset the "FixedValid" context flag to FALSE so that + // the validation routine will re-read the attributes + // from the DS backing store. + + ReadObjectContext.FixedValid = FALSE; + + NtStatus = SampGetFixedAttributes(&ReadObjectContext, + MakeCopy, + &FixedData); + + if (NT_SUCCESS(NtStatus)) + { + DebugPrint("Comparing fixed attributes...\n"); + + // Compare the input/output attributes to make sure + // the data hasn't been changed. + + Identical = CompareFixedAttributes( + &WriteObjectContext, + FixedData); + + if (TRUE == Identical) + { + DebugPrint("Fixed attributes are identical\n"); + } + else + { + DebugPrint("Fixed attributes are different\n"); + } + + // The copy of the fixed attributes was allocated via + // MIDL_user_allocate from SAM. + + MIDL_user_free(FixedData); + } + else + { + DebugPrint("SampGetFixedAttributes error = 0x%lx\n", + NtStatus); + } + + if (NT_SUCCESS(NtStatus) && (TRUE == Identical)) + { + // BUG: Work in progress. + + // NtStatus = SampSetFixedAttributes(); + } + + // The following tests are for variable-length attrs. + + if (NT_SUCCESS(NtStatus) && (TRUE == Identical)) + { + RtlZeroMemory(&UnicodeString, + sizeof(UNICODE_STRING)); + + NtStatus = SampGetUnicodeStringAttribute( + &ReadObjectContext, + SAMP_USER_ACCOUNT_NAME, + MakeCopy, + &UnicodeString); + + if (NT_SUCCESS(NtStatus)) + { + DebugPrint("Comparing unicode string attribute...\n"); + + Identical = CompareVariableAttributes( + &WriteObjectContext, + SAMP_USER_ACCOUNT_NAME, + UnicodeString.Buffer); + + if (TRUE == Identical) + { + DebugPrint("Variable attributes are identical\n"); + } + else + { + DebugPrint("Variable attributes are different\n"); + } + + MIDL_user_free(UnicodeString.Buffer); + } + else + { + DebugPrint("SampGetUnicodeStringAttribute error = 0x%lx\n", + NtStatus); + } + } + + if (NT_SUCCESS(NtStatus) && (TRUE == Identical)) + { + // BUG: Sid is an attribute of the domain object, not the user object. + + // NtStatus = SampGetSidAttribute(); + } + + if (NT_SUCCESS(NtStatus) && (TRUE == Identical)) + { + NtStatus = SampGetAccessAttribute( + &ReadObjectContext, + SAMP_USER_SECURITY_DESCRIPTOR, + MakeCopy, + &Revision, + &SecurityDescriptor); + + if (NT_SUCCESS(NtStatus)) + { + DebugPrint("Comparing access attribute...\n"); + + // BUG: Need to compare revision levels also. + + Identical = CompareVariableAttributes( + &WriteObjectContext, + SAMP_USER_SECURITY_DESCRIPTOR, + SecurityDescriptor); + + if (TRUE == Identical) + { + DebugPrint("Variable attributes are identical\n"); + } + else + { + DebugPrint("Variable attributes are different\n"); + } + + MIDL_user_free(SecurityDescriptor); + } + else + { + DebugPrint("SampGetAccessAttribute error = 0x%lx\n", + NtStatus); + } + } + + if (NT_SUCCESS(NtStatus) && (TRUE == Identical)) + { + // BUG: This routine is not used in the DS version? + + // NtStatus = SampGetUlongArrayAttribute(); + } + + if (NT_SUCCESS(NtStatus) && (TRUE == Identical)) + { + // BUG: This routine is not used in the DS version? + + // NtStatus = SampGetLargeIntArrayAttribute(); + } + + if (NT_SUCCESS(NtStatus) && (TRUE == Identical)) + { + // BUG: This routine is not used in the DS version? + + // NtStatus = SampGetSidArrayAttribute(); + } + + if (NT_SUCCESS(NtStatus) && (TRUE == Identical)) + { + RtlZeroMemory(&LogonHours, sizeof(LOGON_HOURS)); + + NtStatus = SampGetLogonHoursAttribute( + &ReadObjectContext, + SAMP_USER_LOGON_HOURS, + MakeCopy, + &LogonHours); + + if (NT_SUCCESS(NtStatus)) + { + DebugPrint("Comparing logon hours attribute...\n"); + + // The SAM attribute buffer contains the data + // that is the LogonHours.LogonHours member and + // not the entire LogonHours Structure. + + Identical = CompareVariableAttributes( + &WriteObjectContext, + SAMP_USER_LOGON_HOURS, + LogonHours.LogonHours); + + if (TRUE == Identical) + { + DebugPrint("Variable attributes are identical\n"); + } + else + { + DebugPrint("Variable attributes are different\n"); + } + + MIDL_user_free(LogonHours.LogonHours); + } + else + { + DebugPrint("SampGetAccessAttribute error = 0x%lx\n", + NtStatus); + } + } + + if (NT_SUCCESS(NtStatus) && (TRUE == Identical)) + { + // Attempt to update the user's full name attribute + // with a longer string, forcing this routine to grow + // the attribute buffer. + + NtStatus = SampSetVariableAttribute( + &ReadObjectContext, + SAMP_USER_ACCOUNT_NAME, + Qualifier, + (PUCHAR)NewAccountName, + Length); + + if (NT_SUCCESS(NtStatus)) + { + // BUG: Need to verify that the update was correct. + + // For now, verified in the debugger by dumping + // the attribute buffer... + + DebugPrint("SampSetVariableAttribute succeeded\n"); + } + else + { + DebugPrint("SampSetVariableAttribute error = 0x%lx\n", + NtStatus); + } + } + } + else + { + DebugPrint("SampStoreObjectAttributes error = 0x%lx\n", + NtStatus); + } + + // Clean up so that the test can be re-run. + + NtStatus = SampDsDeleteObject(ObjectDsName); + + if (!NT_SUCCESS(NtStatus)) + { + DebugPrint("SampDsDeleteObject error = 0x%lx\n", NtStatus); + } + } + else + { + DebugPrint("SampDsCreateObject error = 0x%lx\n", NtStatus); + } + } + + // Commit the DS transaction. + + NtStatus2 = SampMaybeEndDsTransaction(FALSE); + + ASSERT(!SampExistsDsTransaction()); + + if (!NT_SUCCESS(NtStatus2) ) + { + DebugPrint("SampMaybeEndDsTransaction error = 0x%lx\n", NtStatus2); + } + + if ((NT_SUCCESS(NtStatus)) && NT_SUCCESS(NtStatus2) && (TRUE == Identical)) + { + printf("\nAdvancedAttributeTest PASSED\n"); + } + else + { + printf("\nAdvancedAttributeTest FAILED\n"); + } + + return(NtStatus); +} diff --git a/private/newsam2/server/exe/cnvrtdat.c b/private/newsam2/server/exe/cnvrtdat.c new file mode 100644 index 000000000..eb5afc681 --- /dev/null +++ b/private/newsam2/server/exe/cnvrtdat.c @@ -0,0 +1,2323 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + main.c + +Abstract: + + This file contains unit-test routines for the data conversion routines, + used to convert between SAM and DS in-memory data structures, located in + dsutil.c. + + +Author: + + Chris Mayhall (ChrisMay) 09-May-1996 + +Environment: + + User Mode - Win32 + +Revision History: + + ChrisMay 09-May-1996 + Created initial file, added tests for variable-length attributes. + ChrisMay 14-May-1996 + Added tests for fixed-length attributes. + ChrisMay 14-May-1996 + Added tests for multi-value attributes. + +--*/ + +#include <stdio.h> +#include <stdlib.h> +#include <samsrvp.h> +#include <dsutilp.h> +#include <mappings.h> + +// Constants + +#define DBG_BUFFER_SIZE 128 +#define MULTI_VALUE_COUNT_MAXIMUM 16 + +// Debugging Macros + +// Set CNVRTDAT_DBG_PRINTF to zero to turn off debugging trace output; set it +// to one to enable output. + +#define CNVRTDAT_DBG_PRINTF 1 + +#if (CNVRTDAT_DBG_PRINTF == 1) +#define DebugPrint printf +#else +#define DebugPrint +#endif + +// Global Test Data (for fixed and variable length attributes) + +DSATTR Attributes[20]; + +DSATTRVAL Attr1Val[MULTI_VALUE_COUNT_MAXIMUM]; +DSATTRVAL Attr2Val[MULTI_VALUE_COUNT_MAXIMUM]; +DSATTRVAL Attr3Val[MULTI_VALUE_COUNT_MAXIMUM]; +DSATTRVAL Attr4Val[MULTI_VALUE_COUNT_MAXIMUM]; +DSATTRVAL Attr5Val[MULTI_VALUE_COUNT_MAXIMUM]; +DSATTRVAL Attr6Val[MULTI_VALUE_COUNT_MAXIMUM]; +DSATTRVAL Attr7Val[MULTI_VALUE_COUNT_MAXIMUM]; +DSATTRVAL Attr8Val[MULTI_VALUE_COUNT_MAXIMUM]; +DSATTRVAL Attr9Val[MULTI_VALUE_COUNT_MAXIMUM]; +DSATTRVAL Attr10Val[MULTI_VALUE_COUNT_MAXIMUM]; +DSATTRVAL Attr11Val[MULTI_VALUE_COUNT_MAXIMUM]; +DSATTRVAL Attr12Val[MULTI_VALUE_COUNT_MAXIMUM]; +DSATTRVAL Attr13Val[MULTI_VALUE_COUNT_MAXIMUM]; +DSATTRVAL Attr14Val[MULTI_VALUE_COUNT_MAXIMUM]; +DSATTRVAL Attr15Val[MULTI_VALUE_COUNT_MAXIMUM]; +DSATTRVAL Attr16Val[MULTI_VALUE_COUNT_MAXIMUM]; +DSATTRVAL Attr17Val[MULTI_VALUE_COUNT_MAXIMUM]; +DSATTRVAL Attr18Val[MULTI_VALUE_COUNT_MAXIMUM]; +DSATTRVAL Attr19Val[MULTI_VALUE_COUNT_MAXIMUM]; +DSATTRVAL Attr20Val[MULTI_VALUE_COUNT_MAXIMUM]; + +SECURITY_DESCRIPTOR SecurityDescriptor; + +SID Sid; +SID OwnerSid; +SID GroupSid; + +ACL Sacl; +ACL Dacl; + +WCHAR OemString[] = {L"Oem Domain Information"}; +UNICODE_STRING OemInfo; +WCHAR ReplicaString[] = {L"Replica Domain Information"}; +UNICODE_STRING ReplicaInfo; +WCHAR GroupString[] = {L"GroupName"}; +UNICODE_STRING GroupInfo; +WCHAR CommentString[] = {L"Group Comment String"}; +UNICODE_STRING CommentInfo; + +LARGE_INTEGER CreationTime = {1, 1}; +LARGE_INTEGER ModifiedCount = {2, 2}; +LARGE_INTEGER MaxPasswordAge = {3, 3}; +LARGE_INTEGER MinPasswordAge = {4, 4}; +LARGE_INTEGER ForceLogoff = {5, 5}; +LARGE_INTEGER LockoutDuration = {6, 6}; +LARGE_INTEGER LockoutObservationWindow = {7, 7}; +LARGE_INTEGER ModifiedCountAtLastPromotion = {8, 8}; + +ULONG Revision = 100; +ULONG Unused1 = 200; +ULONG NextRid = 300; +USHORT MinPasswordLength = 4; +USHORT PasswordHistoryLength = 8; +USHORT LockoutThreshold = 3; + +// ServerState and ServerRole are enums (see samsrvp.h, ntsam.h). + +USHORT ServerState = SampServiceInitializing; +USHORT ServerRole = DomainServerRolePrimary; + +ULONG Member1 = 0x12345678; +ULONG Member2 = 0x1234ABCD; +ULONG Member3 = 0xABCD1234; +ULONG RelativeId = 0x1111AAAA; +ULONG AdminCount = 2; +ULONG OperatorCount = 4; + +BOOLEAN UasCompatibilityRequired = TRUE; +ULONG PasswordProperties = 0x1234FFFF; +ULONG AttributeProperties = 0xFFFF1234; + +VOID +DumpBinaryData( + PBYTE pData, + DWORD cbData + ) +{ + DWORD i; + BYTE AsciiLine[16]; + BYTE BinaryLine[16]; + CHAR Buffer[DBG_BUFFER_SIZE]; + + if (0 == cbData) + { + DebugPrint("Zero-Length Data\n"); + return; + } + + if (cbData > DBG_BUFFER_SIZE) + { + DebugPrint("ShowBinaryData - truncating display to 256 bytes\n"); + cbData = 256; + } + + for (; cbData > 0 ;) + { + for (i = 0; i < 16 && cbData > 0 ; i++, cbData--) + { + BinaryLine[i] = *pData; + (isprint(*pData)) ? (AsciiLine[i] = *pData) : (AsciiLine[i] = '.'); + pData++; + } + + if (i < 15) + { + for (; i < 16 ; i++) + { + BinaryLine[i] = ' '; + AsciiLine[i] = ' '; + } + } + + sprintf(Buffer, + "%02x %02x %02x %02x %02x %02x %02x %02x - %02x %02x %02x %02x %02x %02x %02x %02x\t", + BinaryLine[0], + BinaryLine[1], + BinaryLine[2], + BinaryLine[3], + BinaryLine[4], + BinaryLine[5], + BinaryLine[6], + BinaryLine[7], + BinaryLine[8], + BinaryLine[9], + BinaryLine[10], + BinaryLine[11], + BinaryLine[12], + BinaryLine[13], + BinaryLine[14], + BinaryLine[15]); + + DebugPrint(Buffer); + + sprintf(Buffer, + "%c%c%c%c%c%c%c%c - %c%c%c%c%c%c%c%c\n", + AsciiLine[0], + AsciiLine[1], + AsciiLine[2], + AsciiLine[3], + AsciiLine[4], + AsciiLine[5], + AsciiLine[6], + AsciiLine[7], + AsciiLine[8], + AsciiLine[9], + AsciiLine[10], + AsciiLine[11], + AsciiLine[12], + AsciiLine[13], + AsciiLine[14], + AsciiLine[15]); + + DebugPrint(Buffer); + } +} + + + +VOID +DumpNtSid( + IN SID *Sid + ) +{ + // Note: PSID is defined as a PVOID, hence, use SID * explicitly. + + DebugPrint(" Sid.Revision = %d\n", Sid->Revision); + DebugPrint(" Sid.SubAuthorityCount = %d\n", Sid->SubAuthorityCount); + DebugPrint(" Sid.IdentifierAuthority.Value[0] = %d\n", Sid->IdentifierAuthority.Value[0]); + DebugPrint(" Sid.IdentifierAuthority.Value[1] = %d\n", Sid->IdentifierAuthority.Value[1]); + DebugPrint(" Sid.IdentifierAuthority.Value[2] = %d\n", Sid->IdentifierAuthority.Value[2]); + DebugPrint(" Sid.IdentifierAuthority.Value[3] = %d\n", Sid->IdentifierAuthority.Value[3]); + DebugPrint(" Sid.IdentifierAuthority.Value[4] = %d\n", Sid->IdentifierAuthority.Value[4]); + DebugPrint(" Sid.IdentifierAuthority.Value[5] = %d\n", Sid->IdentifierAuthority.Value[5]); + DebugPrint(" Sid.SubAuthority[0] = %d\n", Sid->SubAuthority[0]); +} + + + +VOID +DumpNtAcl( + IN ACL *Acl + ) +{ + DebugPrint(" Acl.AclRevision = %d\n", Acl->AclRevision); + DebugPrint(" Acl.Sbz1 = %d\n", Acl->Sbz1); + DebugPrint(" Acl.AclSize = %d\n", Acl->AclSize); + DebugPrint(" Acl.AceCount = %d\n", Acl->AceCount); + DebugPrint(" Acl.Sbz2 = %d\n", Acl->Sbz2); +} + + + +VOID +DumpNtSecurityDescriptor( + IN SECURITY_DESCRIPTOR *SecDesc + ) +{ + // Note: PSSECURITY_DESCRIPTOR is defined as a PVOID, hence, use + // SECURITY_DESCRIPTOR * explicitly. + + DebugPrint(" SecurityDescriptor.Revision = %d\n", SecDesc->Revision); + DebugPrint(" SecurityDescriptor.Sbz1 = %d\n", SecDesc->Sbz1); + DebugPrint(" SecurityDescriptor.Control = %d\n", SecDesc->Control); + DumpNtSid(SecDesc->Owner); + DumpNtSid(SecDesc->Group); + DumpNtAcl(SecDesc->Sacl); + DumpNtAcl(SecDesc->Dacl); +} + + + +NTSTATUS +DumpSamSecurityDescriptor( + IN PSAMP_VARIABLE_LENGTH_ATTRIBUTE SamAttributes, + IN ULONG AttrIndex + ) +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + ULONG Offset = 0; + ULONG Length = 0; + ULONG Qualifier = 0; + PSECURITY_DESCRIPTOR SecurityDescriptor = NULL; + + if (NULL != SamAttributes) + { + Offset = (ULONG)((SamAttributes + AttrIndex)->Offset); + Length = (ULONG)((SamAttributes + AttrIndex)->Length); + Qualifier = (ULONG)((SamAttributes + AttrIndex)->Qualifier); + + SecurityDescriptor = (SECURITY_DESCRIPTOR*)((((PBYTE)SamAttributes) + Offset)); + + DebugPrint(" BufAddr = 0x%lx Offset = %lu Length = %lu Qualifier = %lu\n", + SamAttributes + Offset, + Offset, + Length, + Qualifier); + + DumpNtSecurityDescriptor(SecurityDescriptor); + + // BUG: SecurityDescriptor may be NULL. + + NtStatus = STATUS_SUCCESS; + } + + return(NtStatus); +} + + + +NTSTATUS +DumpSamSid( + IN PSAMP_VARIABLE_LENGTH_ATTRIBUTE SamAttributes, + IN ULONG AttrIndex + ) +{ + NTSTATUS NtStatus = STATUS_SEVERITY_ERROR; + ULONG Offset = 0; + ULONG Length = 0; + ULONG Qualifier = 0; + PSID Sid = NULL; + + if (NULL != SamAttributes) + { + Offset = (ULONG)((SamAttributes + AttrIndex)->Offset); + Length = (ULONG)((SamAttributes + AttrIndex)->Length); + Qualifier = (ULONG)((SamAttributes + AttrIndex)->Qualifier); + + Sid = (SID *)((((PBYTE)SamAttributes) + Offset)); + + DebugPrint(" BufAddr = 0x%lx Offset = %lu Length = %lu Qualifier = %lu\n", + SamAttributes + Offset, + Offset, + Length, + Qualifier); + + DumpNtSid(Sid); + + // BUG: Sid may be NULL. + + NtStatus = STATUS_SUCCESS; + } + + return(NtStatus); +} + + + +NTSTATUS +DumpSamOemInformation( + IN PSAMP_VARIABLE_LENGTH_ATTRIBUTE SamAttributes, + IN ULONG AttrIndex + ) +{ + NTSTATUS NtStatus = STATUS_SEVERITY_ERROR; + ULONG Offset = 0; + ULONG Length = 0; + ULONG Qualifier = 0; + PUNICODE_STRING OemString = NULL; + + if (NULL != SamAttributes) + { + Offset = (ULONG)((SamAttributes + AttrIndex)->Offset); + Length = (ULONG)((SamAttributes + AttrIndex)->Length); + Qualifier = (ULONG)((SamAttributes + AttrIndex)->Qualifier); + + OemString = (PUNICODE_STRING)((((PBYTE)SamAttributes) + Offset)); + + DebugPrint(" BufAddr = 0x%lx Offset = %lu Length = %lu Qualifier = %lu\n", + SamAttributes + Offset, + Offset, + Length, + Qualifier); + + DebugPrint(" Oem Data = %ws\n", OemString->Buffer); + + // BUG: OemString may be NULL. + + NtStatus = STATUS_SUCCESS; + } + + return(NtStatus); +} + + + +NTSTATUS +DumpSamReplica( + IN PSAMP_VARIABLE_LENGTH_ATTRIBUTE SamAttributes, + IN ULONG AttrIndex + ) +{ + NTSTATUS NtStatus = STATUS_SEVERITY_ERROR; + ULONG Offset = 0; + ULONG Length = 0; + ULONG Qualifier = 0; + PUNICODE_STRING ReplicaString = NULL; + + if (NULL != SamAttributes) + { + Offset = (ULONG)((SamAttributes + AttrIndex)->Offset); + Length = (ULONG)((SamAttributes + AttrIndex)->Length); + Qualifier = (ULONG)((SamAttributes + AttrIndex)->Qualifier); + + ReplicaString = (PUNICODE_STRING)((((PBYTE)SamAttributes) + Offset)); + + DebugPrint(" BufAddr = 0x%lx Offset = %lu Length = %lu Qualifier = %lu\n", + SamAttributes + Offset, + Offset, + Length, + Qualifier); + + DebugPrint(" Replica Data = %ws\n", ReplicaString->Buffer); + + // BUG: ReplicaString may be NULL. + + NtStatus = STATUS_SUCCESS; + } + + return(NtStatus); +} + + + +NTSTATUS +DumpSamUnicodeStringAttribute( + IN PSAMP_VARIABLE_LENGTH_ATTRIBUTE SamAttributes, + IN ULONG AttrIndex + ) +{ + NTSTATUS NtStatus = STATUS_SEVERITY_ERROR; + ULONG Offset = 0; + ULONG Length = 0; + ULONG Qualifier = 0; + PUNICODE_STRING String = NULL; + + if (NULL != SamAttributes) + { + Offset = (ULONG)((SamAttributes + AttrIndex)->Offset); + Length = (ULONG)((SamAttributes + AttrIndex)->Length); + Qualifier = (ULONG)((SamAttributes + AttrIndex)->Qualifier); + + String = (PUNICODE_STRING)((((PBYTE)SamAttributes) + Offset)); + + DebugPrint(" BufAddr = 0x%lx Offset = %lu Length = %lu Qualifier = %lu\n", + SamAttributes + Offset, + Offset, + Length, + Qualifier); + + DebugPrint(" String Data = %ws\n", String->Buffer); + + // BUG: String may be NULL. + + NtStatus = STATUS_SUCCESS; + } + + return(NtStatus); +} + + + +VOID +DumpNtGroupMembers( + IN PULONG Identifiers, + IN ULONG ValueCount + ) +{ + ULONG ValueIndex = 0; + + for (ValueIndex = 0; ValueIndex < ValueCount; ValueIndex++) + { + DebugPrint(" Group Member %lu ID = 0x%lx\n", + ValueIndex, + *(Identifiers + ValueIndex)); + } +} + + + +NTSTATUS +DumpSamGroupMembers( + IN PSAMP_VARIABLE_LENGTH_ATTRIBUTE SamAttributes, + IN ULONG AttrIndex + ) +{ + NTSTATUS NtStatus = STATUS_SEVERITY_ERROR; + ULONG Offset = 0; + ULONG Length = 0; + ULONG Qualifier = 0; + PULONG Identifiers = NULL; + + if (NULL != SamAttributes) + { + Offset = (ULONG)((SamAttributes + AttrIndex)->Offset); + Length = (ULONG)((SamAttributes + AttrIndex)->Length); + Qualifier = (ULONG)((SamAttributes + AttrIndex)->Qualifier); + + Identifiers = (PULONG)((((PBYTE)SamAttributes) + Offset)); + + DebugPrint(" BufAddr = 0x%lx Offset = %lu Length = %lu Qualifier = %lu\n", + SamAttributes + Offset, + Offset, + Length, + Qualifier); + + DumpNtGroupMembers(Identifiers, Qualifier); + + // BUG: Identifiers may be NULL. + + NtStatus = STATUS_SUCCESS; + } + + return(NtStatus); +} + + + +NTSTATUS +DumpDsSecurityDescriptor( + IN PDSATTR DsAttributes, + IN ULONG AttrIndex + ) +{ + NTSTATUS NtStatus = STATUS_SEVERITY_ERROR; + ULONG Type = 0; + ULONG Length = 0; + ULONG ValueCount = 0; + ULONG Index = 0; + PSECURITY_DESCRIPTOR SecurityDescriptor = NULL; + + if (NULL != DsAttributes) + { + Type = DsAttributes[AttrIndex].attrTyp; + ValueCount = DsAttributes[AttrIndex].AttrVal.valCount; + + for (Index = 0; Index < ValueCount; Index++) + { + Length = DsAttributes[AttrIndex].AttrVal.pAVal[Index].valLen; + SecurityDescriptor = (PSECURITY_DESCRIPTOR)DsAttributes[AttrIndex].AttrVal.pAVal[Index].pVal; + DebugPrint(" AttrType = %lu ValueCount = %lu ValueLength = %lu\n", Type, ValueCount, Length); + DumpNtSecurityDescriptor(SecurityDescriptor); + } + + // BUG: SecurityDescriptor may be NULL. + + NtStatus = STATUS_SUCCESS; + } + + return(NtStatus); +} + + + +NTSTATUS +DumpDsSid( + IN PDSATTR DsAttributes, + IN ULONG AttrIndex + ) +{ + NTSTATUS NtStatus = STATUS_SEVERITY_ERROR; + ULONG Type = 0; + ULONG Length = 0; + ULONG ValueCount = 0; + ULONG Index = 0; + PSID Sid = NULL; + + if (NULL != DsAttributes) + { + Type = DsAttributes[AttrIndex].attrTyp; + ValueCount = DsAttributes[AttrIndex].AttrVal.valCount; + + for (Index = 0; Index < ValueCount; Index++) + { + Length = DsAttributes[AttrIndex].AttrVal.pAVal[Index].valLen; + Sid = (PSID)DsAttributes[AttrIndex].AttrVal.pAVal[Index].pVal; + DebugPrint(" AttrType = %lu ValueCount = %lu ValueLength = %lu\n", Type, ValueCount, Length); + DumpNtSid(Sid); + } + + // BUG: Sid may be NULL. + + NtStatus = STATUS_SUCCESS; + } + + return(NtStatus); +} + + + +NTSTATUS +DumpDsOemInformation( + IN PDSATTR DsAttributes, + IN ULONG AttrIndex + ) +{ + NTSTATUS NtStatus = STATUS_SEVERITY_ERROR; + ULONG Type = 0; + ULONG Length = 0; + ULONG ValueCount = 0; + ULONG Index = 0; + PUNICODE_STRING OemString = NULL; + + if (NULL != DsAttributes) + { + Type = DsAttributes[AttrIndex].attrTyp; + ValueCount = DsAttributes[AttrIndex].AttrVal.valCount; + + for (Index = 0; Index < ValueCount; Index++) + { + Length = DsAttributes[AttrIndex].AttrVal.pAVal[Index].valLen; + OemString = (PUNICODE_STRING)DsAttributes[AttrIndex].AttrVal.pAVal[Index].pVal; + DebugPrint(" AttrType = %lu ValueCount = %lu ValueLength = %lu\n", Type, ValueCount, Length); + DebugPrint(" Oem Data = %ws\n", OemString->Buffer); + } + + // BUG: OemString may be NULL. + + NtStatus = STATUS_SUCCESS; + } + + return(NtStatus); +} + + + +NTSTATUS +DumpDsReplica( + IN PDSATTR DsAttributes, + IN ULONG AttrIndex + ) +{ + NTSTATUS NtStatus = STATUS_SEVERITY_ERROR; + ULONG Type = 0; + ULONG Length = 0; + ULONG ValueCount = 0; + ULONG Index = 0; + PUNICODE_STRING ReplicaString = NULL; + + if (NULL != DsAttributes) + { + Type = DsAttributes[AttrIndex].attrTyp; + ValueCount = DsAttributes[AttrIndex].AttrVal.valCount; + + for (Index = 0; Index < ValueCount; Index++) + { + Length = DsAttributes[AttrIndex].AttrVal.pAVal[Index].valLen; + ReplicaString = (PUNICODE_STRING)DsAttributes[AttrIndex].AttrVal.pAVal[Index].pVal; + DebugPrint(" AttrType = %lu ValueCount = %lu ValueLength = %lu\n", Type, ValueCount, Length); + DebugPrint(" Replica Data = %ws\n", ReplicaString->Buffer); + } + + // BUG: ReplicaString may be NULL. + + NtStatus = STATUS_SUCCESS; + } + + return(NtStatus); +} + + + +NTSTATUS +DumpDsUnicodeStringAttribute( + IN PDSATTR DsAttributes, + IN ULONG AttrIndex + ) +{ + NTSTATUS NtStatus = STATUS_SEVERITY_ERROR; + ULONG Type = 0; + ULONG Length = 0; + ULONG ValueCount = 0; + ULONG Index = 0; + PUNICODE_STRING String = NULL; + + if (NULL != DsAttributes) + { + Type = DsAttributes[AttrIndex].attrTyp; + ValueCount = DsAttributes[AttrIndex].AttrVal.valCount; + + for (Index = 0; Index < ValueCount; Index++) + { + Length = DsAttributes[AttrIndex].AttrVal.pAVal[Index].valLen; + String = (PUNICODE_STRING)DsAttributes[AttrIndex].AttrVal.pAVal[Index].pVal; + DebugPrint(" AttrType = %lu ValueCount = %lu ValueLength = %lu\n", Type, ValueCount, Length); + DebugPrint(" String Data = %ws\n", String->Buffer); + } + + // BUG: String may be NULL. + + NtStatus = STATUS_SUCCESS; + } + + return(NtStatus); +} + + + +NTSTATUS +DumpDsGroupMembers( + IN PDSATTR DsAttributes, + IN ULONG AttrIndex + ) +{ + NTSTATUS NtStatus = STATUS_SEVERITY_ERROR; + ULONG Type = 0; + ULONG Length = 0; + ULONG ValueCount = 0; + ULONG Index = 0; + ULONG Identifier = 0; + + if (NULL != DsAttributes) + { + Type = DsAttributes[AttrIndex].attrTyp; + ValueCount = DsAttributes[AttrIndex].AttrVal.valCount; + + for (Index = 0; Index < ValueCount; Index++) + { + Length = DsAttributes[AttrIndex].AttrVal.pAVal[Index].valLen; + Identifier = *(PULONG)DsAttributes[AttrIndex].AttrVal.pAVal[Index].pVal; + DebugPrint(" AttrType = %lu ValueCount = %lu ValueLength = %lu\n", Type, ValueCount, Length); + DebugPrint(" Member Identifier = 0x%lx\n", Identifier); + } + + // BUG: Identifiers may be NULL. + + NtStatus = STATUS_SUCCESS; + } + + return(NtStatus); +} + + + +NTSTATUS +CompareDsAttrs( + DSATTR DsAttr1, + DSATTR DsAttr2 + ) +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + ULONG ValueCount = 0; + ULONG ValueCount1 = 0; + ULONG ValueCount2 = 0; + PDSATTRVAL AttrValue1 = NULL; + PDSATTRVAL AttrValue2 = NULL; + ULONG ValueIndex = 0; + ULONG ValueLength = 0; + ULONG ValueLength1 = 0; + ULONG ValueLength2 = 0; + PUCHAR Value1 = NULL; + PUCHAR Value2 = NULL; + INT Status = -1; + + if (DsAttr1.attrTyp == DsAttr2.attrTyp) + { + ValueCount1 = DsAttr1.AttrVal.valCount; + ValueCount2 = DsAttr2.AttrVal.valCount; + + if (ValueCount1 == ValueCount2) + { + ValueCount = ValueCount1; + + AttrValue1 = DsAttr1.AttrVal.pAVal; + AttrValue2 = DsAttr2.AttrVal.pAVal; + + if ((NULL != AttrValue1) && + (NULL != AttrValue2) && + (AttrValue1 != AttrValue2)) // This may be okay + { + for (ValueIndex = 0; ValueIndex < ValueCount; ValueIndex++) + { + ValueLength1 = AttrValue1[ValueIndex].valLen; + ValueLength2 = AttrValue2[ValueIndex].valLen; + + // BUG: Value length test doesn't know about alignment. + // Variable-length attributes are DWORD aligned if needed + // by SampExtractAttributeFromDsAttr, but this test does + // not know about any sort of alignment! + + if (((ValueLength1 + 1) == ValueLength2) || // DWORD align case + ((ValueLength1 + 2) == ValueLength2) || // DWORD align case + ((ValueLength1 + 3) == ValueLength2)) // DWORD align case + { + DebugPrint("ASSUMING DWORD ALIGNMENT: ValueLength1 = %lu ValueLength2 = %lu\n", + ValueLength1, + ValueLength2); + } + + if ((ValueLength1 == ValueLength2) || + ((ValueLength1 + 1) == ValueLength2) || // DWORD align case + ((ValueLength1 + 2) == ValueLength2) || // DWORD align case + ((ValueLength1 + 3) == ValueLength2)) // DWORD align case + { + ValueLength = ValueLength2; + + Value1 = AttrValue1[ValueIndex].pVal; + Value2 = AttrValue2[ValueIndex].pVal; + + if ((NULL != Value1) && + (NULL != Value2) && + (Value1 != Value2)) // This may be okay + { + Status = memcmp(Value1, Value2, ValueLength); + + if (0 == Status) + { + NtStatus = STATUS_SUCCESS; + } + else + { + DebugPrint("RtlCompareMemory returned %d\n", + Status); + } + } + else + { + DebugPrint("Value1 Address = 0x%lx Value2 Address = 0x%lx\n", + Value1, + Value2); + } + } + else + { + DebugPrint("ValueLength1 = %lu ValueLength2 = %lu\n", + ValueLength1, + ValueLength2); + } + } + } + else + { + DebugPrint("AttrValue1 Address = 0x%lx AttrValue2 Address = 0x%lx\n", + AttrValue1, + AttrValue2); + } + } + else + { + DebugPrint("ValueCount1 = %lu ValueCount2 = %lu\n", + ValueCount1, + ValueCount2); + } + } + else + { + DebugPrint("attrTyp1 = %lu attrTyp2 = %lu\n", + DsAttr1.attrTyp, + DsAttr2.attrTyp); + } + + return(NtStatus); +} + + + +NTSTATUS +CompareDsAttrBlocks( + PDSATTRBLOCK DsAttrBlock1, + PDSATTRBLOCK DsAttrBlock2 + ) +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + ULONG AttrIndex = 0; + ULONG AttrCount = 0; + ULONG AttrCount1 = 0; + ULONG AttrCount2 = 0; + PDSATTR Attributes1 = NULL; + PDSATTR Attributes2 = NULL; + + if ((NULL != DsAttrBlock1) && + (NULL != DsAttrBlock2) && + (DsAttrBlock1 != DsAttrBlock2)) + { + AttrCount1 = DsAttrBlock1->attrCount; + AttrCount2 = DsAttrBlock2->attrCount; + + if (AttrCount1 == AttrCount2) + { + AttrCount = AttrCount1; + + Attributes1 = DsAttrBlock1->pAttr; + Attributes2 = DsAttrBlock2->pAttr; + + if ((NULL != Attributes1) && + (NULL != Attributes2) && + (Attributes1 != Attributes2)) + { + for (AttrIndex = 0; AttrIndex < AttrCount; AttrIndex++) + { + NtStatus = CompareDsAttrs(Attributes1[AttrIndex], + Attributes2[AttrIndex]); + + if (!NT_SUCCESS(NtStatus)) + { + DebugPrint("CompareDsAttrs returned 0x%lx\n", + NtStatus); + break; + } + } + } + else + { + DebugPrint("Attributes1 Address = 0x%lx Attributes2 Address = 0x%lx\n", + Attributes1, + Attributes2); + } + } + else + { + DebugPrint("AttrCount1 = %lu AttrCount2 = %lu\n", + AttrCount1, + AttrCount2); + } + } + else + { + DebugPrint("DsAttrBlock1 Address = 0x%lx DsAttrBlock2 Address = 0x%lx\n", + DsAttrBlock1, + DsAttrBlock2); + + NtStatus = STATUS_INVALID_PARAMETER; + } + + return(NtStatus); +} + + + +VOID +SetupVariableLengthDomainAttributes( + PDSATTRBLOCK DsAttributesIn + ) +{ + ULONG AttrIndex = 0; + ULONG ValueIndex = 0; + + // Set up domain security descriptor + + ValueIndex = 0; + + // Set up the SIDs and ACLs for the descriptor + + OwnerSid.Revision = 1; // UCHAR + OwnerSid.SubAuthorityCount = 1; // UCHAR + OwnerSid.IdentifierAuthority.Value[0] = 1; // UCHAR[6] + OwnerSid.IdentifierAuthority.Value[1] = 2; + OwnerSid.IdentifierAuthority.Value[2] = 3; + OwnerSid.IdentifierAuthority.Value[3] = 4; + OwnerSid.IdentifierAuthority.Value[4] = 5; + OwnerSid.IdentifierAuthority.Value[5] = 6; + OwnerSid.SubAuthority[0] = 111; // ULONG + + GroupSid.Revision = 1; // UCHAR + GroupSid.SubAuthorityCount = 1; // UCHAR + GroupSid.IdentifierAuthority.Value[0] = 1; // UCHAR[6] + GroupSid.IdentifierAuthority.Value[1] = 2; + GroupSid.IdentifierAuthority.Value[2] = 3; + GroupSid.IdentifierAuthority.Value[3] = 4; + GroupSid.IdentifierAuthority.Value[4] = 5; + GroupSid.IdentifierAuthority.Value[5] = 6; + GroupSid.SubAuthority[0] = 222; // ULONG + + Sacl.AclRevision = 1; // UCHAR + Sacl.Sbz1 = 1; // UCHAR + Sacl.AclSize = 1; // USHORT + Sacl.AceCount = 1; // USHORT + Sacl.Sbz2 = 1; // USHORT + + Dacl.AclRevision = 1; // UCHAR + Dacl.Sbz1 = 1; // UCHAR + Dacl.AclSize = 1; // USHORT + Dacl.AceCount = 1; // USHORT + Dacl.Sbz2 = 1; // USHORT + + SecurityDescriptor.Revision = 1; // UCHAR + SecurityDescriptor.Sbz1 = 1; // UCHAR + SecurityDescriptor.Control = 1; // USHORT + SecurityDescriptor.Owner = &OwnerSid; // PSID + SecurityDescriptor.Group = &GroupSid; // PSID + SecurityDescriptor.Sacl = &Sacl; // PACL + SecurityDescriptor.Dacl = &Dacl; // PACL + + Attr1Val[ValueIndex].valLen = sizeof(SECURITY_DESCRIPTOR) + + (2 * sizeof(SID)) + + (2 * sizeof(ACL)); + + Attr1Val[ValueIndex].pVal = (PUCHAR)&SecurityDescriptor; + + AttrIndex = SAMP_DOMAIN_SECURITY_DESCRIPTOR; + + // BUG: What should type be set to? + + Attributes[AttrIndex].attrTyp = 0; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr1Val[ValueIndex]; + + // Set up domain SID + + ValueIndex = 0; + + Sid.Revision = 1; // UCHAR + Sid.SubAuthorityCount = 1; // UCHAR + Sid.IdentifierAuthority.Value[0] = 1; // UCHAR[6] + Sid.IdentifierAuthority.Value[1] = 2; + Sid.IdentifierAuthority.Value[2] = 3; + Sid.IdentifierAuthority.Value[3] = 4; + Sid.IdentifierAuthority.Value[4] = 5; + Sid.IdentifierAuthority.Value[5] = 6; + Sid.SubAuthority[0] = 333; // ULONG + + Attr2Val[ValueIndex].valLen = sizeof(SID); + Attr2Val[ValueIndex].pVal = (PUCHAR)&Sid; + + AttrIndex = SAMP_DOMAIN_SID; + + // BUG: What should type be set to? + + Attributes[AttrIndex].attrTyp = 0; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr2Val[ValueIndex]; + + // Set up domain OEM information + + ValueIndex = 0; + + RtlInitUnicodeString(&OemInfo, OemString); + + // NOTE: valLen should not include space for the null character because it + // is dropped by the RTL routines that manipulate UNICODE_STRINGs, hence + // will lead to an erroneous buffer length when tested during the call to + // CompareDsAttrBlocks. + + Attr3Val[ValueIndex].valLen = sizeof(UNICODE_STRING) + + (sizeof(WCHAR) * wcslen(OemString)); + + Attr3Val[ValueIndex].pVal = (PUCHAR)&OemInfo; + + AttrIndex = SAMP_DOMAIN_OEM_INFORMATION; + + // BUG: What should type be set to? + + Attributes[AttrIndex].attrTyp = 0; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr3Val[ValueIndex]; + + // Set up domain replica + + ValueIndex = 0; + + RtlInitUnicodeString(&ReplicaInfo, ReplicaString); + + Attr4Val[ValueIndex].valLen = sizeof(UNICODE_STRING) + + (sizeof(WCHAR) * wcslen(ReplicaString)); + + Attr4Val[ValueIndex].pVal = (PUCHAR)&ReplicaInfo; + + AttrIndex = SAMP_DOMAIN_REPLICA; + + // BUG: What should type be set to? + + Attributes[AttrIndex].attrTyp = 0; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr4Val[ValueIndex]; + + DsAttributesIn->attrCount = SAMP_DOMAIN_VARIABLE_ATTRIBUTES; + DsAttributesIn->pAttr = Attributes; +} + + + +NTSTATUS +BasicVariableLengthAttrTest( + VOID + ) +{ + NTSTATUS NtStatus = STATUS_SEVERITY_ERROR; + INT ObjectType = SampDomainObjectType; + DSATTRBLOCK DsAttributesIn; + PDSATTRBLOCK DsAttributesOut; + PSAMP_VARIABLE_LENGTH_ATTRIBUTE SamAttributes; + ULONG AttrIndex = 0; + ULONG Length = 0; + + SetupVariableLengthDomainAttributes(&DsAttributesIn); + + NtStatus = SampConvertAttrBlockToVarLengthAttributes(ObjectType, + &DsAttributesIn, + &SamAttributes, + &Length); + + if (NT_SUCCESS(NtStatus)) + { + DebugPrint("\nSAM Variable-Length Domain Attributes:\n"); + + for (AttrIndex = 0; + AttrIndex < SAMP_DOMAIN_VARIABLE_ATTRIBUTES; + AttrIndex++) + { + switch(AttrIndex) + { + + case SAMP_DOMAIN_SECURITY_DESCRIPTOR: + DumpSamSecurityDescriptor(SamAttributes, AttrIndex); + break; + + case SAMP_DOMAIN_SID: + DumpSamSid(SamAttributes, AttrIndex); + break; + + case SAMP_DOMAIN_OEM_INFORMATION: + DumpSamOemInformation(SamAttributes, AttrIndex); + break; + + case SAMP_DOMAIN_REPLICA: + DumpSamReplica(SamAttributes, AttrIndex); + break; + + default: + DebugPrint("ERROR - UNRECOGNIZED ATTRIBUTE\n"); + break; + + } + } + } + + if (NT_SUCCESS(NtStatus)) + { + NtStatus = SampConvertVarLengthAttributesToAttrBlock(ObjectType, + SamAttributes, + &DsAttributesOut); + } + + if (NT_SUCCESS(NtStatus)) + { + DebugPrint("\nDS Domain Attributes:\n"); + + DebugPrint(" DsAttributes.attrCount = %lu\n", DsAttributesOut->attrCount); + + for (AttrIndex = 0; + AttrIndex < SAMP_DOMAIN_VARIABLE_ATTRIBUTES; + AttrIndex++) + { + switch(AttrIndex) + { + + case SAMP_DOMAIN_SECURITY_DESCRIPTOR: + DumpDsSecurityDescriptor(DsAttributesOut->pAttr, AttrIndex); + break; + + case SAMP_DOMAIN_SID: + DumpDsSid(DsAttributesOut->pAttr, AttrIndex); + break; + + case SAMP_DOMAIN_OEM_INFORMATION: + DumpDsOemInformation(DsAttributesOut->pAttr, AttrIndex); + break; + + case SAMP_DOMAIN_REPLICA: + DumpDsReplica(DsAttributesOut->pAttr, AttrIndex); + break; + + default: + DebugPrint("ERROR - UNRECOGNIZED ATTRIBUTE\n"); + break; + + } + } + } + + // Make sure that the input data is the same as the output. + + NtStatus = CompareDsAttrBlocks(&DsAttributesIn, DsAttributesOut); + + return(NtStatus); +} + + + +VOID +SetupFixedLengthDomainAttributes( + PDSATTRBLOCK DsAttributesIn + ) +{ + // ValueIndex is always zero because fixed-length attributess are not + // multi-valued. + + ULONG ValueIndex = 0; + ULONG AttrIndex = 0; + + // Set up revision + + AttrIndex = 0; + + Attr1Val[ValueIndex].valLen = sizeof(ULONG); + Attr1Val[ValueIndex].pVal = (PUCHAR)&Revision; + Attributes[AttrIndex].attrTyp = SAMP_FIXED_DOMAIN_REVISION_LEVEL; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr1Val[ValueIndex]; + + // Set up unused1 + + AttrIndex = 1; + + // BUG: Setting attrTyp to BOGUS_TYPE due to missing types. + + Attr2Val[ValueIndex].valLen = sizeof(ULONG); + Attr2Val[ValueIndex].pVal = (PUCHAR)&Unused1; + Attributes[AttrIndex].attrTyp = BOGUS_TYPE; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr2Val[ValueIndex]; + + // Set up CreationTime + + AttrIndex = 2; + + Attr3Val[ValueIndex].valLen = sizeof(LARGE_INTEGER); + Attr3Val[ValueIndex].pVal = (PUCHAR)&CreationTime; + Attributes[AttrIndex].attrTyp = SAMP_FIXED_DOMAIN_CREATION_TIME; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr3Val[ValueIndex]; + + // Set up ModifiedCount + + AttrIndex = 3; + + Attr4Val[ValueIndex].valLen = sizeof(LARGE_INTEGER); + Attr4Val[ValueIndex].pVal = (PUCHAR)&ModifiedCount; + Attributes[AttrIndex].attrTyp = SAMP_FIXED_DOMAIN_MODIFIED_COUNT; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr4Val[ValueIndex]; + + // Set up MaxPasswordAge + + AttrIndex = 4; + + Attr5Val[ValueIndex].valLen = sizeof(LARGE_INTEGER); + Attr5Val[ValueIndex].pVal = (PUCHAR)&MaxPasswordAge; + Attributes[AttrIndex].attrTyp = SAMP_FIXED_DOMAIN_MAX_PASSWORD_AGE; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr5Val[ValueIndex]; + + // Set up MinPasswordAge + + AttrIndex = 5; + + Attr6Val[ValueIndex].valLen = sizeof(LARGE_INTEGER); + Attr6Val[ValueIndex].pVal = (PUCHAR)&MinPasswordAge; + Attributes[AttrIndex].attrTyp = SAMP_FIXED_DOMAIN_MIN_PASSWORD_AGE; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr6Val[ValueIndex]; + + // Set up ForceLogoff + + AttrIndex = 6; + + Attr7Val[ValueIndex].valLen = sizeof(LARGE_INTEGER); + Attr7Val[ValueIndex].pVal = (PUCHAR)&ForceLogoff; + Attributes[AttrIndex].attrTyp = SAMP_FIXED_DOMAIN_FORCE_LOGOFF; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr7Val[ValueIndex]; + + // Set up LockoutDuration + + AttrIndex = 7; + + Attr8Val[ValueIndex].valLen = sizeof(LARGE_INTEGER); + Attr8Val[ValueIndex].pVal = (PUCHAR)&LockoutDuration; + Attributes[AttrIndex].attrTyp = SAMP_FIXED_DOMAIN_LOCKOUT_DURATION; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr8Val[ValueIndex]; + + // Set up LockoutObservationWindow + + AttrIndex = 8; + + // BUG: Setting attrTyp to BOGUS_TYPE due to missing types. + + Attr9Val[ValueIndex].valLen = sizeof(LARGE_INTEGER); + Attr9Val[ValueIndex].pVal = (PUCHAR)&LockoutObservationWindow; + Attributes[AttrIndex].attrTyp = BOGUS_TYPE; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr9Val[ValueIndex]; + + // Set up ModifiedCountAtLastPromotion + + AttrIndex = 9; + + Attr10Val[ValueIndex].valLen = sizeof(LARGE_INTEGER); + Attr10Val[ValueIndex].pVal = (PUCHAR)&ModifiedCountAtLastPromotion; + Attributes[AttrIndex].attrTyp = SAMP_FIXED_DOMAIN_MODCOUNT_LAST_PROMOTION; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr10Val[ValueIndex]; + + // Set up NextRid + + AttrIndex = 10; + + Attr11Val[ValueIndex].valLen = sizeof(ULONG); + Attr11Val[ValueIndex].pVal = (PUCHAR)&NextRid; + Attributes[AttrIndex].attrTyp = SAMP_FIXED_DOMAIN_NEXT_RID; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr11Val[ValueIndex]; + + // Set up PasswordProperties + + AttrIndex = 11; + + Attr12Val[ValueIndex].valLen = sizeof(ULONG); + Attr12Val[ValueIndex].pVal = (PUCHAR)&PasswordProperties; + Attributes[AttrIndex].attrTyp = SAMP_FIXED_DOMAIN_PWD_PROPERTIES; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr12Val[ValueIndex]; + + // Set up MinPasswordLength + + AttrIndex = 12; + + Attr13Val[ValueIndex].valLen = sizeof(USHORT); + Attr13Val[ValueIndex].pVal = (PUCHAR)&MinPasswordLength; + Attributes[AttrIndex].attrTyp = SAMP_FIXED_DOMAIN_MIN_PASSWORD_LENGTH; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr13Val[ValueIndex]; + + // Set up PasswordHistoryLength + + AttrIndex = 13; + + Attr14Val[ValueIndex].valLen = sizeof(USHORT); + Attr14Val[ValueIndex].pVal = (PUCHAR)&PasswordHistoryLength; + Attributes[AttrIndex].attrTyp = SAMP_FIXED_DOMAIN_PASSWORD_HISTORY_LENGTH; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr14Val[ValueIndex]; + + // Set up LockoutThreshold + + AttrIndex = 14; + + Attr15Val[ValueIndex].valLen = sizeof(USHORT); + Attr15Val[ValueIndex].pVal = (PUCHAR)&LockoutThreshold; + Attributes[AttrIndex].attrTyp = SAMP_FIXED_DOMAIN_LOCKOUT_THRESHOLD; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr15Val[ValueIndex]; + + // Set up ServerState + + AttrIndex = 15; + + Attr16Val[ValueIndex].valLen = sizeof(DOMAIN_SERVER_ENABLE_STATE); + Attr16Val[ValueIndex].pVal = (PUCHAR)&ServerState; + Attributes[AttrIndex].attrTyp = SAMP_FIXED_DOMAIN_SERVER_STATE; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr16Val[ValueIndex]; + + // Set up ServerRole + + AttrIndex = 16; + + Attr17Val[ValueIndex].valLen = sizeof(DOMAIN_SERVER_ROLE); + Attr17Val[ValueIndex].pVal = (PUCHAR)&ServerRole; + Attributes[AttrIndex].attrTyp = SAMP_FIXED_DOMAIN_SERVER_ROLE; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr17Val[ValueIndex]; + + // Set up UasCompatibilityRequired + + AttrIndex = 17; + + Attr18Val[ValueIndex].valLen = sizeof(BOOLEAN); + Attr18Val[ValueIndex].pVal = (PUCHAR)&UasCompatibilityRequired; + Attributes[AttrIndex].attrTyp = SAMP_FIXED_DOMAIN_UAS_COMPAT_REQUIRED; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr18Val[ValueIndex]; + + DsAttributesIn->attrCount = SAMP_DOMAIN_FIXED_ATTR_COUNT; + DsAttributesIn->pAttr = Attributes; +} + + + +NTSTATUS +BasicFixedLengthAttrTest( + VOID + ) +{ + NTSTATUS NtStatus = STATUS_SEVERITY_ERROR; + INT ObjectType = SampDomainObjectType; + DSATTRBLOCK DsAttributesIn; + PDSATTRBLOCK DsAttributesOut; + PSAMP_V1_0A_FIXED_LENGTH_DOMAIN SamAttributes; + ULONG AttrIndex = 0; + ULONG Index = 0; + ULONG AttrType = 0; + ULONG ValueCount = 0; + ULONG ValueLength = 0; + PUCHAR Value = NULL; + ULONG Length = 0; + + SetupFixedLengthDomainAttributes(&DsAttributesIn); + + NtStatus = SampConvertAttrBlockToFixedLengthAttributes(ObjectType, + &DsAttributesIn, + &SamAttributes, + &Length); + + if (NT_SUCCESS(NtStatus)) + { + DebugPrint("\nSAM Fixed-Length Domain Attributes:\n"); + + DebugPrint(" Revision = %lu\n", SamAttributes->Revision); + DebugPrint(" Unused1 = %lu\n", SamAttributes->Unused1); + DebugPrint(" CreationTime = %lu:%lu\n", + SamAttributes->CreationTime.HighPart, + SamAttributes->CreationTime.LowPart); + DebugPrint(" ModifiedCount = %lu:%lu\n", + SamAttributes->ModifiedCount.HighPart, + SamAttributes->ModifiedCount.LowPart); + DebugPrint(" MaxPasswordAge = %lu:%lu\n", + SamAttributes->MaxPasswordAge.HighPart, + SamAttributes->MaxPasswordAge.LowPart); + DebugPrint(" MinPasswordAge = %lu:%lu\n", + SamAttributes->MinPasswordAge.HighPart, + SamAttributes->MinPasswordAge.LowPart); + DebugPrint(" ForceLogoff = %lu:%lu\n", + SamAttributes->ForceLogoff.HighPart, + SamAttributes->ForceLogoff.LowPart); + DebugPrint(" LockoutDuration = %lu:%lu\n", + SamAttributes->LockoutDuration.HighPart, + SamAttributes->LockoutDuration.LowPart); + DebugPrint(" LockoutObservationWindow = %lu:%lu\n", + SamAttributes->LockoutObservationWindow.HighPart, + SamAttributes->LockoutObservationWindow.LowPart); + DebugPrint(" ModifiedCountAtLastPromotion = %lu:%lu\n", + SamAttributes->ModifiedCountAtLastPromotion.HighPart, + SamAttributes->ModifiedCountAtLastPromotion.LowPart); + DebugPrint(" NextRid = %lu\n", SamAttributes->NextRid); + DebugPrint(" PasswordProperties = 0x%lx\n", + SamAttributes->PasswordProperties); + DebugPrint(" MinPasswordLength = %d\n", + SamAttributes->MinPasswordLength); + DebugPrint(" PasswordHistoryLength = %d\n", + SamAttributes->PasswordHistoryLength); + DebugPrint(" LockoutThreshold = %d\n", + SamAttributes->LockoutThreshold); + DebugPrint(" ServerState = %lu\n", SamAttributes->ServerState); + DebugPrint(" ServerRole = %lu\n", SamAttributes->ServerRole); + DebugPrint(" UasCompatibilityRequired = %d\n", + SamAttributes->UasCompatibilityRequired); + } + + if (NT_SUCCESS(NtStatus)) + { + NtStatus = SampConvertFixedLengthAttributesToAttrBlock( + ObjectType, + SamAttributes, + &DsAttributesOut); + } + + if (NT_SUCCESS(NtStatus)) + { + DebugPrint("\nDS Domain Attributes:\n"); + DebugPrint(" AttrCount = %lu\n", DsAttributesOut->attrCount); + + Index = 0; + + for (AttrIndex = 0; + AttrIndex < SAMP_DOMAIN_FIXED_ATTR_COUNT; + AttrIndex++) + { + AttrType = DsAttributesOut->pAttr[AttrIndex].attrTyp; + ValueCount = DsAttributesOut->pAttr[AttrIndex].AttrVal.valCount; + ValueLength = + DsAttributesOut->pAttr[AttrIndex].AttrVal.pAVal[Index].valLen; + + DebugPrint(" AttrType = %lu ValueCount = %lu ValueLength = %lu\n", + AttrType, + ValueCount, + ValueLength); + + Value = + DsAttributesOut->pAttr[AttrIndex].AttrVal.pAVal[Index].pVal; + + switch(AttrIndex) + { + + case 0: + DebugPrint(" Revision = %lu\n", *(PULONG)Value); + break; + + case 1: + DebugPrint(" Unused1 = %lu\n", *(PULONG)Value); + break; + + case 2: + DebugPrint(" CreationTime = %lu:%lu\n", + ((PLARGE_INTEGER)Value)->HighPart, + ((PLARGE_INTEGER)Value)->LowPart); + break; + + case 3: + DebugPrint(" ModifiedCount = %lu:%lu\n", + ((PLARGE_INTEGER)Value)->HighPart, + ((PLARGE_INTEGER)Value)->LowPart); + break; + + case 4: + DebugPrint(" MaxPasswordAge = %lu:%lu\n", + ((PLARGE_INTEGER)Value)->HighPart, + ((PLARGE_INTEGER)Value)->LowPart); + break; + + case 5: + DebugPrint(" MinPasswordAge = %lu:%lu\n", + ((PLARGE_INTEGER)Value)->HighPart, + ((PLARGE_INTEGER)Value)->LowPart); + break; + + case 6: + DebugPrint(" ForceLogoff = %lu:%lu\n", + ((PLARGE_INTEGER)Value)->HighPart, + ((PLARGE_INTEGER)Value)->LowPart); + break; + + case 7: + DebugPrint(" LockoutDuration = %lu:%lu\n", + ((PLARGE_INTEGER)Value)->HighPart, + ((PLARGE_INTEGER)Value)->LowPart); + break; + + case 8: + DebugPrint(" LockoutObservationWindow = %lu:%lu\n", + ((PLARGE_INTEGER)Value)->HighPart, + ((PLARGE_INTEGER)Value)->LowPart); + break; + + case 9: + DebugPrint(" ModifiedCountAtLastPromotion = %lu:%lu\n", + ((PLARGE_INTEGER)Value)->HighPart, + ((PLARGE_INTEGER)Value)->LowPart); + break; + + case 10: + DebugPrint(" NextRid = %lu\n", *(PULONG)Value); + break; + + case 11: + DebugPrint(" PasswordProperties = 0x%lx\n", *(PULONG)Value); + break; + + case 12: + DebugPrint(" MinPasswordLength = %d\n", *(PUSHORT)Value); + break; + + case 13: + DebugPrint(" PasswordHistoryLength = %d\n", *(PUSHORT)Value); + break; + + case 14: + DebugPrint(" LockoutThreshold = %d\n", *(PUSHORT)Value); + break; + + case 15: + DebugPrint(" ServerState = %d\n", *(PUSHORT)Value); + break; + + case 16: + DebugPrint(" ServerRole = %d\n", *(PUSHORT)Value); + break; + + case 17: + DebugPrint(" UasCompatibilityRequired = %d\n", *(BOOLEAN *)Value); + break; + + } + } + } + + // Make sure that the input data is the same as the output. + + NtStatus = CompareDsAttrBlocks(&DsAttributesIn, DsAttributesOut); + + return(NtStatus); +} + + + +VOID +SetupMultiValuedGroupAttributes( + PDSATTRBLOCK DsAttributesIn + ) +{ + ULONG AttrIndex = 0; + ULONG ValueIndex = 0; + + // Set up group security descriptor + + ValueIndex = 0; + + // Set up the SIDs and ACLs for the descriptor + + OwnerSid.Revision = 1; // UCHAR + OwnerSid.SubAuthorityCount = 1; // UCHAR + OwnerSid.IdentifierAuthority.Value[0] = 1; // UCHAR[6] + OwnerSid.IdentifierAuthority.Value[1] = 2; + OwnerSid.IdentifierAuthority.Value[2] = 3; + OwnerSid.IdentifierAuthority.Value[3] = 4; + OwnerSid.IdentifierAuthority.Value[4] = 5; + OwnerSid.IdentifierAuthority.Value[5] = 6; + OwnerSid.SubAuthority[0] = 111; // ULONG + + GroupSid.Revision = 1; // UCHAR + GroupSid.SubAuthorityCount = 1; // UCHAR + GroupSid.IdentifierAuthority.Value[0] = 1; // UCHAR[6] + GroupSid.IdentifierAuthority.Value[1] = 2; + GroupSid.IdentifierAuthority.Value[2] = 3; + GroupSid.IdentifierAuthority.Value[3] = 4; + GroupSid.IdentifierAuthority.Value[4] = 5; + GroupSid.IdentifierAuthority.Value[5] = 6; + GroupSid.SubAuthority[0] = 222; // ULONG + + Sacl.AclRevision = 1; // UCHAR + Sacl.Sbz1 = 1; // UCHAR + Sacl.AclSize = 1; // USHORT + Sacl.AceCount = 1; // USHORT + Sacl.Sbz2 = 1; // USHORT + + Dacl.AclRevision = 1; // UCHAR + Dacl.Sbz1 = 1; // UCHAR + Dacl.AclSize = 1; // USHORT + Dacl.AceCount = 1; // USHORT + Dacl.Sbz2 = 1; // USHORT + + SecurityDescriptor.Revision = 1; // UCHAR + SecurityDescriptor.Sbz1 = 1; // UCHAR + SecurityDescriptor.Control = 1; // USHORT + SecurityDescriptor.Owner = &OwnerSid; // PSID + SecurityDescriptor.Group = &GroupSid; // PSID + SecurityDescriptor.Sacl = &Sacl; // PACL + SecurityDescriptor.Dacl = &Dacl; // PACL + + Attr1Val[ValueIndex].valLen = sizeof(SECURITY_DESCRIPTOR) + + (2 * sizeof(SID)) + + (2 * sizeof(ACL)); + + Attr1Val[ValueIndex].pVal = (PUCHAR)&SecurityDescriptor; + + AttrIndex = SAMP_GROUP_SECURITY_DESCRIPTOR; + + // BUG: What should type be set to? + + Attributes[AttrIndex].attrTyp = 0; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr1Val[ValueIndex]; + + // Set up group name + + ValueIndex = 0; + + RtlInitUnicodeString(&GroupInfo, GroupString); + + Attr2Val[ValueIndex].valLen = sizeof(UNICODE_STRING) + + (sizeof(WCHAR) * wcslen(GroupString)); + + Attr2Val[ValueIndex].pVal = (PUCHAR)&GroupInfo; + + AttrIndex = SAMP_GROUP_NAME; + + // BUG: What should type be set to? + + Attributes[AttrIndex].attrTyp = 0; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr2Val[ValueIndex]; + + // Set up group admin comment + + ValueIndex = 0; + + RtlInitUnicodeString(&CommentInfo, CommentString); + + Attr3Val[ValueIndex].valLen = sizeof(UNICODE_STRING) + + (sizeof(WCHAR) * wcslen(CommentString)); + + Attr3Val[ValueIndex].pVal = (PUCHAR)&CommentInfo; + + AttrIndex = SAMP_GROUP_ADMIN_COMMENT; + + // BUG: What should type be set to? + + Attributes[AttrIndex].attrTyp = 0; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr3Val[ValueIndex]; + + // Set up group members + + ValueIndex = 0; + + Member1 = 0x1234abcd; + Attr4Val[ValueIndex].valLen = sizeof(ULONG); + Attr4Val[ValueIndex].pVal = (PUCHAR)&Member1; + + ValueIndex = 1; + + Member2 = 0x5678abef; + Attr4Val[ValueIndex].valLen = sizeof(ULONG); + Attr4Val[ValueIndex].pVal = (PUCHAR)&Member2; + + ValueIndex = 2; + + Member3 = 0x12345678; + Attr4Val[ValueIndex].valLen = sizeof(ULONG); + Attr4Val[ValueIndex].pVal = (PUCHAR)&Member3; + + AttrIndex = SAMP_GROUP_MEMBERS; + + // BUG: What should type be set to? + + Attributes[AttrIndex].attrTyp = 0; + Attributes[AttrIndex].AttrVal.valCount = 3; + Attributes[AttrIndex].AttrVal.pAVal = Attr4Val; + + DsAttributesIn->attrCount = SAMP_GROUP_VARIABLE_ATTRIBUTES; + DsAttributesIn->pAttr = Attributes; +} + + + +NTSTATUS +MultiValueAttributeTest( + VOID + ) +{ + NTSTATUS NtStatus = STATUS_SEVERITY_ERROR; + INT ObjectType = SampGroupObjectType; + DSATTRBLOCK DsAttributesIn; + PDSATTRBLOCK DsAttributesOut; + PSAMP_VARIABLE_LENGTH_ATTRIBUTE SamAttributes; + ULONG AttrIndex = 0; + ULONG Length = 0; + + SetupMultiValuedGroupAttributes(&DsAttributesIn); + + NtStatus = SampConvertAttrBlockToVarLengthAttributes(ObjectType, + &DsAttributesIn, + &SamAttributes, + &Length); + + if (NT_SUCCESS(NtStatus)) + { + DebugPrint("\nSAM Variable-Length Group Multi-Value Attributes:\n"); + + for (AttrIndex = 0; + AttrIndex < SAMP_GROUP_VARIABLE_ATTRIBUTES; + AttrIndex++) + { + switch(AttrIndex) + { + + case SAMP_GROUP_SECURITY_DESCRIPTOR: + DumpSamSecurityDescriptor(SamAttributes, AttrIndex); + break; + + case SAMP_GROUP_NAME: + DumpSamUnicodeStringAttribute(SamAttributes, AttrIndex); + break; + + case SAMP_GROUP_ADMIN_COMMENT: + DumpSamUnicodeStringAttribute(SamAttributes, AttrIndex); + break; + + case SAMP_GROUP_MEMBERS: + DumpSamGroupMembers(SamAttributes, AttrIndex); + break; + + default: + DebugPrint("ERROR - UNRECOGNIZED ATTRIBUTE\n"); + break; + + } + } + } + + if (NT_SUCCESS(NtStatus)) + { + NtStatus = SampConvertVarLengthAttributesToAttrBlock(ObjectType, + SamAttributes, + &DsAttributesOut); + } + + if (NT_SUCCESS(NtStatus)) + { + DebugPrint("\nDS Group Multi-Value Attributes:\n"); + + DebugPrint(" DsAttributes.attrCount = %lu\n", DsAttributesOut->attrCount); + + for (AttrIndex = 0; + AttrIndex < SAMP_GROUP_VARIABLE_ATTRIBUTES; + AttrIndex++) + { + switch(AttrIndex) + { + + case SAMP_GROUP_SECURITY_DESCRIPTOR: + DumpDsSecurityDescriptor(DsAttributesOut->pAttr, AttrIndex); + break; + + case SAMP_GROUP_NAME: + DumpDsUnicodeStringAttribute(DsAttributesOut->pAttr, AttrIndex); + break; + + case SAMP_GROUP_ADMIN_COMMENT: + DumpDsUnicodeStringAttribute(DsAttributesOut->pAttr, AttrIndex); + break; + + case SAMP_GROUP_MEMBERS: + DumpDsGroupMembers(DsAttributesOut->pAttr, AttrIndex); + break; + + default: + DebugPrint("ERROR - UNRECOGNIZED ATTRIBUTE\n"); + break; + + } + } + } + + // Make sure that the input data is the same as the output. + + NtStatus = CompareDsAttrBlocks(&DsAttributesIn, DsAttributesOut); + + return(NtStatus); +} + + + +VOID +SetupCombinedGroupAttributes( + PDSATTRBLOCK DsAttributesIn + ) +{ + // ValueIndex is always zero because fixed-length attributess are not + // multi-valued. + + ULONG ValueIndex = 0; + ULONG AttrIndex = 0; + + // Set up the fixed-length group attributes. + + // Set up Revision + + AttrIndex = 0; + + Attr1Val[ValueIndex].valLen = sizeof(ULONG); + Attr1Val[ValueIndex].pVal = (PUCHAR)&Revision; + Attributes[AttrIndex].attrTyp = SAMP_FIXED_GROUP_REVISION_LEVEL; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr1Val[ValueIndex]; + + // Set up RelativeId + + AttrIndex = 1; + + Attr2Val[ValueIndex].valLen = sizeof(ULONG); + Attr2Val[ValueIndex].pVal = (PUCHAR)&RelativeId; + Attributes[AttrIndex].attrTyp = SAMP_FIXED_GROUP_RID; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr2Val[ValueIndex]; + + // Set up Attributes + + AttrIndex = 2; + + Attr3Val[ValueIndex].valLen = sizeof(ULONG); + Attr3Val[ValueIndex].pVal = (PUCHAR)&AttributeProperties; + Attributes[AttrIndex].attrTyp = SAMP_FIXED_GROUP_ATTRIBUTES; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr3Val[ValueIndex]; + + // Set up Unused1 + + AttrIndex = 3; + + // BUG: Setting attrTyp to BOGUS_TYPE due to missing types. + + Attr4Val[ValueIndex].valLen = sizeof(ULONG); + Attr4Val[ValueIndex].pVal = (PUCHAR)&Unused1; + Attributes[AttrIndex].attrTyp = BOGUS_TYPE; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr4Val[ValueIndex]; + + // Set up AdminCount + + AttrIndex = 4; + + Attr5Val[ValueIndex].valLen = sizeof(UCHAR); + Attr5Val[ValueIndex].pVal = (PUCHAR)&AdminCount; + Attributes[AttrIndex].attrTyp = SAMP_FIXED_GROUP_ADMIN_COUNT; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr5Val[ValueIndex]; + + // Set up OperatorCount + + AttrIndex = 5; + + Attr6Val[ValueIndex].valLen = sizeof(UCHAR); + Attr6Val[ValueIndex].pVal = (PUCHAR)&OperatorCount; + Attributes[AttrIndex].attrTyp = SAMP_FIXED_GROUP_OPERATOR_COUNT; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr6Val[ValueIndex]; + + // Set up the variable-length group attributes. + + // Set up the SIDs and ACLs for the descriptor + + OwnerSid.Revision = 1; // UCHAR + OwnerSid.SubAuthorityCount = 1; // UCHAR + OwnerSid.IdentifierAuthority.Value[0] = 1; // UCHAR[6] + OwnerSid.IdentifierAuthority.Value[1] = 2; + OwnerSid.IdentifierAuthority.Value[2] = 3; + OwnerSid.IdentifierAuthority.Value[3] = 4; + OwnerSid.IdentifierAuthority.Value[4] = 5; + OwnerSid.IdentifierAuthority.Value[5] = 6; + OwnerSid.SubAuthority[0] = 111; // ULONG + + GroupSid.Revision = 1; // UCHAR + GroupSid.SubAuthorityCount = 1; // UCHAR + GroupSid.IdentifierAuthority.Value[0] = 1; // UCHAR[6] + GroupSid.IdentifierAuthority.Value[1] = 2; + GroupSid.IdentifierAuthority.Value[2] = 3; + GroupSid.IdentifierAuthority.Value[3] = 4; + GroupSid.IdentifierAuthority.Value[4] = 5; + GroupSid.IdentifierAuthority.Value[5] = 6; + GroupSid.SubAuthority[0] = 222; // ULONG + + Sacl.AclRevision = 1; // UCHAR + Sacl.Sbz1 = 1; // UCHAR + Sacl.AclSize = 1; // USHORT + Sacl.AceCount = 1; // USHORT + Sacl.Sbz2 = 1; // USHORT + + Dacl.AclRevision = 1; // UCHAR + Dacl.Sbz1 = 1; // UCHAR + Dacl.AclSize = 1; // USHORT + Dacl.AceCount = 1; // USHORT + Dacl.Sbz2 = 1; // USHORT + + SecurityDescriptor.Revision = 1; // UCHAR + SecurityDescriptor.Sbz1 = 1; // UCHAR + SecurityDescriptor.Control = 1; // USHORT + SecurityDescriptor.Owner = &OwnerSid; // PSID + SecurityDescriptor.Group = &GroupSid; // PSID + SecurityDescriptor.Sacl = &Sacl; // PACL + SecurityDescriptor.Dacl = &Dacl; // PACL + + Attr7Val[ValueIndex].valLen = sizeof(SECURITY_DESCRIPTOR) + + (2 * sizeof(SID)) + + (2 * sizeof(ACL)); + + Attr7Val[ValueIndex].pVal = (PUCHAR)&SecurityDescriptor; + + AttrIndex = 6; + + // BUG: What should type be set to? + + Attributes[AttrIndex].attrTyp = 0; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr7Val[ValueIndex]; + + // Set up group name + + ValueIndex = 0; + + RtlInitUnicodeString(&GroupInfo, GroupString); + + Attr8Val[ValueIndex].valLen = sizeof(UNICODE_STRING) + + (sizeof(WCHAR) * wcslen(GroupString)); + + Attr8Val[ValueIndex].pVal = (PUCHAR)&GroupInfo; + + AttrIndex = 7; + + // BUG: What should type be set to? + + Attributes[AttrIndex].attrTyp = 0; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr8Val[ValueIndex]; + + // Set up group admin comment + + ValueIndex = 0; + + RtlInitUnicodeString(&CommentInfo, CommentString); + + Attr9Val[ValueIndex].valLen = sizeof(UNICODE_STRING) + + (sizeof(WCHAR) * wcslen(CommentString)); + + Attr9Val[ValueIndex].pVal = (PUCHAR)&CommentInfo; + + AttrIndex = 8; + + // BUG: What should type be set to? + + Attributes[AttrIndex].attrTyp = 0; + Attributes[AttrIndex].AttrVal.valCount = 1; + Attributes[AttrIndex].AttrVal.pAVal = &Attr9Val[ValueIndex]; + + // Set up group members + + ValueIndex = 0; + + Member1 = 0x1234abcd; + Attr10Val[ValueIndex].valLen = sizeof(ULONG); + Attr10Val[ValueIndex].pVal = (PUCHAR)&Member1; + + ValueIndex = 1; + + Member2 = 0x5678abef; + Attr10Val[ValueIndex].valLen = sizeof(ULONG); + Attr10Val[ValueIndex].pVal = (PUCHAR)&Member2; + + ValueIndex = 2; + + Member3 = 0x12345678; + Attr10Val[ValueIndex].valLen = sizeof(ULONG); + Attr10Val[ValueIndex].pVal = (PUCHAR)&Member3; + + AttrIndex = 9; + + // BUG: What should type be set to? + + Attributes[AttrIndex].attrTyp = 0; + Attributes[AttrIndex].AttrVal.valCount = 3; + Attributes[AttrIndex].AttrVal.pAVal = Attr10Val; + + DsAttributesIn->attrCount = + SAMP_GROUP_FIXED_ATTR_COUNT + SAMP_GROUP_VARIABLE_ATTRIBUTES; + + DsAttributesIn->pAttr = Attributes; +} + + + +NTSTATUS +CombinedAttributeTest( + VOID + ) +{ + NTSTATUS NtStatus = STATUS_SEVERITY_ERROR; + INT ObjectType = SampGroupObjectType; + DSATTRBLOCK DsAttributesIn; + PDSATTRBLOCK DsAttributesOut = NULL; + DSATTRBLOCK DsAttributesTmp; + PVOID SamAttributes; + ULONG AttrIndex = 0; + ULONG Length = 0; + ULONG FixedLength = 0; + ULONG VarLength = 0; + PSAMP_V1_0A_FIXED_LENGTH_GROUP SamFixedGroupAttrs = NULL; + PSAMP_VARIABLE_LENGTH_ATTRIBUTE SamVarGroupAttrs = NULL; + ULONG AttributeCount = 0; + ULONG AttrType = 0; + ULONG ValueCount = 0; + ULONG ValueLength = 0; + PUCHAR Value = NULL; + ULONG Index = 0; + + PBYTE ValueTmp = NULL; + + SetupCombinedGroupAttributes(&DsAttributesIn); + + NtStatus = SampConvertAttrBlockToCombinedAttributes(ObjectType, + &DsAttributesIn, + &SamAttributes, + &FixedLength, + &VarLength); + + if (NT_SUCCESS(NtStatus)) + { + DebugPrint("\nSAM Combined Group Multi-Value Attributes:\n"); + + SamFixedGroupAttrs = (PSAMP_V1_0A_FIXED_LENGTH_GROUP)SamAttributes; + + DebugPrint(" Fixed Buffer Addr = 0x%lx FixedLength = %lu VarLength = %lu\n", + SamFixedGroupAttrs, + FixedLength, + VarLength); + + DebugPrint(" Revision = %lu\n", + SamFixedGroupAttrs->Revision); + DebugPrint(" RelativeId = 0x%lx\n", + SamFixedGroupAttrs->RelativeId); + DebugPrint(" Attributes = 0x%lx\n", + SamFixedGroupAttrs->Attributes); + DebugPrint(" Unused1 = %lu\n", + SamFixedGroupAttrs->Unused1); + DebugPrint(" AdminCount = %d\n", + SamFixedGroupAttrs->AdminCount); + DebugPrint(" OperatorCount = %d\n", + SamFixedGroupAttrs->OperatorCount); + + SamVarGroupAttrs = + (PSAMP_VARIABLE_LENGTH_ATTRIBUTE)(((PBYTE)SamAttributes) + + FixedLength); + + DebugPrint(" Variable Buffer Addr = 0x%lx VariableLength = %lu\n", + SamVarGroupAttrs, + VarLength); + + for (AttrIndex = 0; + AttrIndex < SAMP_GROUP_VARIABLE_ATTRIBUTES; + AttrIndex++) + { + switch(AttrIndex) + { + + case SAMP_GROUP_SECURITY_DESCRIPTOR: + DumpSamSecurityDescriptor(SamVarGroupAttrs, AttrIndex); + break; + + case SAMP_GROUP_NAME: + DumpSamUnicodeStringAttribute(SamVarGroupAttrs, AttrIndex); + break; + + case SAMP_GROUP_ADMIN_COMMENT: + DumpSamUnicodeStringAttribute(SamVarGroupAttrs, AttrIndex); + break; + + case SAMP_GROUP_MEMBERS: + DumpSamGroupMembers(SamVarGroupAttrs, AttrIndex); + break; + + default: + DebugPrint("ERROR - UNRECOGNIZED ATTRIBUTE\n"); + break; + + } + } + } + + if (NT_SUCCESS(NtStatus)) + { + NtStatus = SampConvertCombinedAttributesToAttrBlock( + ObjectType, + SamAttributes, + FixedLength, + VarLength, + &DsAttributesOut); + } + + if (NT_SUCCESS(NtStatus)) + { + DebugPrint("\nDS Combined Group Multi-Value Attributes:\n"); + + DebugPrint(" DsAttributes.attrCount = %lu\n", DsAttributesOut->attrCount); + + for (AttrIndex = 0; + AttrIndex < DsAttributesOut->attrCount; + AttrIndex++) + { + + AttrType = DsAttributesOut->pAttr[AttrIndex].attrTyp; + ValueCount = DsAttributesOut->pAttr[AttrIndex].AttrVal.valCount; + ValueLength = + DsAttributesOut->pAttr[AttrIndex].AttrVal.pAVal[Index].valLen; + + DebugPrint(" AttrType = %lu ValueCount = %lu ValueLength = %lu\n", + AttrType, + ValueCount, + ValueLength); + + Value = + DsAttributesOut->pAttr[AttrIndex].AttrVal.pAVal[Index].pVal; + + switch(AttrIndex) + { + + case 0: // Revision + DebugPrint(" Revision = %lu\n", *(PULONG)Value); + break; + + case 1: // RelativeId + DebugPrint(" RelativeId = 0x%lx\n", *(PULONG)Value); + break; + + case 2: // Attributes + DebugPrint(" Attributes = 0x%lx\n", *(PULONG)Value); + break; + + case 3: // Unused1 + DebugPrint(" Unused1 = %lu\n", *(PULONG)Value); + break; + + case 4: // AdminCount + DebugPrint(" AdminCount = %u\n", *(PUCHAR)Value); + break; + + case 5: // OperatorCount + DebugPrint(" OperatorCount = %u\n", *(PUCHAR)Value); + break; + + // Because all of the attributes are in one DSATTRBLOCK, the + // remaining elements correspond to the variable-length attri- + // butes. + + case (SAMP_GROUP_SECURITY_DESCRIPTOR + 6): + DumpDsSecurityDescriptor(DsAttributesOut->pAttr, AttrIndex); + break; + + case (SAMP_GROUP_NAME + 6): + DumpDsUnicodeStringAttribute(DsAttributesOut->pAttr, AttrIndex); + break; + + case (SAMP_GROUP_ADMIN_COMMENT + 6): + DumpDsUnicodeStringAttribute(DsAttributesOut->pAttr, AttrIndex); + break; + + case (SAMP_GROUP_MEMBERS + 6): + DumpDsGroupMembers(DsAttributesOut->pAttr, AttrIndex); + break; + + default: + DebugPrint("ERROR - UNRECOGNIZED ATTRIBUTE\n"); + break; + + } + } + } + + // Make sure that the input data is the same as the output. + + NtStatus = CompareDsAttrBlocks(&DsAttributesIn, DsAttributesOut); + + return(NtStatus); +} + + + +#if 0 + +// BUG: Disabled redundant main, using main from ..\server\main.c. + +void +_cdecl +main( + int argc, + char **argv + ) + +/*++ + +Routine Description: + + This test is designed to test the SAM-DS data conversion routines defined + in dsutil.c. The tests are as follows: + + BasicVariableLengthAttrTest - This test constructs a DSATTRBLOCK, calls + the conversion routines to produce a SAM variable-length buffer, and + then converts the buffer back into a DSATTRBLOCK. + + BasicFixedLengthAttrTest - This test constructs a DSATTRBLOCK, calls the + conversion routines to produce a SAM fixed-length buffer, and then + converts the buffer back into a DSATTRBLOCK. + + MultiValueAttributeTest - This test constructs a DSATTRBLOCK with a multi- + valued attribute, calls the conversion routines to produce a SAM + variable-length data buffer, and then converts the buffer back into + a DSATTRBLOCK. + + (Further tests TBD) + +Arguments: + + None. + +Return Value: + + The test always calls exit(0) for successful completion, or exit(1) for + failure during any test. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_SEVERITY_ERROR; + + DebugPrint("Starting CNVRTDAT\n"); + + NtStatus = BasicVariableLengthAttrTest(); + + if (NT_SUCCESS(NtStatus)) + { + NtStatus = BasicFixedLengthAttrTest(); + } + + if (NT_SUCCESS(NtStatus)) + { + NtStatus = MultiValueAttributeTest(); + } + + if (NT_SUCCESS(NtStatus)) + { + NtStatus = CombinedAttributeTest(); + } + + // BUG: Need to perform a memcmp to verfiy correctness. + + // Display test status to stdout--do not change printf to DebugPrint for + // the exit messages. + + if (NT_SUCCESS(NtStatus)) + { + printf("\nPASSED\n"); + exit(0); + } + else + { + printf("\nFAILED\n"); + exit(1); + } +} + + + +#endif diff --git a/private/newsam2/server/exe/cuttest.c b/private/newsam2/server/exe/cuttest.c new file mode 100644 index 000000000..a296d3e2a --- /dev/null +++ b/private/newsam2/server/exe/cuttest.c @@ -0,0 +1,524 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + cuttesu.c + +Abstract: + + This file contains tests that test functions in context.c, utility.c + and enumeration routines. + + THE ROUTINES IN THIS FILE ARE FOR SAM SEVER TEST PURPOSES ONLY. + + There are a number of server-side unit tests aimed at private routines + within the SAM server. These tests are defined in this file. This file + should only be compiled into the SAM server when it is built as a stand- + alone executable (built in the \um subdir). Therefore, it is only listed + in the sources file in the \um subdir. + + Because the SAM code relies on a fairly large amount of state information + being in place from the initialization of the server, it is difficult to + write a unit test that "plugs in" to the server from the outside. + +Author: + + Murli Satagopan (ChrisMay) 24-Jun-1996 + +Environment: + + User Mode - Win32 + +Revision History: + + +--*/ +#include <samsrvp.h> +#include <dsutilp.h> +#include <dslayer.h> +#include <mappings.h> +#include <testutil.h> + + + + + +NTSTATUS +SampDoAccountCountTests( + VOID + ) +/*++ +Routine Description: + + Doing Account Count Tests , which basically gets hold of all account + counts in the Domain and verfies them + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + ULONG GroupCount1, AliasCount1, UserCount1; + ULONG GroupCount2, AliasCount2, UserCount2; + ULONG GroupCount3, AliasCount3, UserCount3; + + // + // Get the Account counts in this Domain , increment and decrement + // them and verify that operations are O.K + // + + + Status = SampRetrieveAccountCounts(&UserCount1,&GroupCount1,&AliasCount1); + if (Status != STATUS_SUCCESS) + { + printf("\t\tRetrieve Account Counts 1 Failed \n"); + goto Error; + } + + Status = SampAdjustAccountCount(SampUserObjectType,TRUE); + if (Status != STATUS_SUCCESS) + { + printf("\t\tAdjust Account Counts 1 Failed \n"); + goto Error; + } + + Status = SampAdjustAccountCount(SampGroupObjectType,TRUE); + if (Status != STATUS_SUCCESS) + { + printf("\t\tAdjust Account Counts 2 Failed \n"); + goto Error; + } + + Status = SampAdjustAccountCount(SampAliasObjectType,TRUE); + if (Status != STATUS_SUCCESS) + { + printf("\t\tAdjust Account Counts 3 Failed \n"); + goto Error; + } + + + Status = SampRetrieveAccountCounts(&UserCount2,&GroupCount2,&AliasCount2); + if (Status != STATUS_SUCCESS) + { + printf("\t\tRetrieve Account Counts 2 Failed \n"); + goto Error; + } + + Status = SampAdjustAccountCount(SampUserObjectType,FALSE); + if (Status != STATUS_SUCCESS) + { + printf("\t\tAdjust Account Counts 4 Failed \n"); + goto Error; + } + + Status = SampAdjustAccountCount(SampGroupObjectType,FALSE); + if (Status != STATUS_SUCCESS) + { + printf("\t\tAdjust Account Counts 5 Failed \n"); + goto Error; + } + + Status = SampAdjustAccountCount(SampAliasObjectType,FALSE); + if (Status != STATUS_SUCCESS) + { + printf("\t\tAdjust Account Counts 6 Failed \n"); + goto Error; + } + + + Status = SampRetrieveAccountCounts(&UserCount3,&GroupCount3,&AliasCount3); + if (Status != STATUS_SUCCESS) + { + printf("Retrieve Account Counts 3 Failed \n"); + goto Error; + } + + // Verify the the results + + if ( + (UserCount1 != UserCount3) + || (GroupCount1 != GroupCount3) + || (AliasCount1 != AliasCount3) + || ((UserCount1+1) != UserCount2) + || ((GroupCount1+1) != GroupCount2) + || ((AliasCount1+1) != AliasCount2) + ) + { + printf("\t\tError Count Operations Mismatch\n"); + Status = STATUS_UNSUCCESSFUL; + goto Error; + } + + +Error: + if (!NT_SUCCESS(Status)) + printf("\t\tTest Failed : Return Code %d\n", Status); + + return Status; +} + + +VOID +SampPrintEnumResults( + PSAMPR_ENUMERATION_BUFFER EnumResults, + ULONG Count + ) +{ + ULONG Index; + + printf("\t\tEnumerating %d Accounts\n",Count); + + for (Index=0;Index<Count;Index++) + { + printf("\t\t\tRid= %d ; Name = %S\n", + EnumResults->Buffer[Index].RelativeId, + EnumResults->Buffer[Index].Name.Buffer + ); + } +} + + + +NTSTATUS +SampDoEnumerationTests( + PSAMP_OBJECT DomainContext, + ULONG NumAccountsToCreate, + ULONG MaxMemorySize + ) +{ + NTSTATUS Status = STATUS_SUCCESS; + PSAMPR_ENUMERATION_BUFFER EnumResults; + ULONG Count; + ULONG Index; + SAM_ENUMERATE_HANDLE handle=0; + DSNAME **NewObject = NULL; + ULONG Rid =1; + PSID DomainSid; + + // + // Define AttrBlocks + // + // + + ATTRVAL CreateAttrsVal[] = + { + { 0,NULL}, // Place Holder for Security Descriptor + { 0,NULL}, // Place HOlder for Object Flat Name + { 0,NULL}, // Place Holder for Account Control field + {sizeof(ULONG), (UCHAR *) &Rid} // Rid Field + }; + + ATTRTYP CreateAttrsTypes[]= + { + SAMP_USER_SECURITY_DESCRIPTOR, + SAMP_USER_ACCOUNT_NAME, + SAMP_FIXED_USER_ACCOUNT_CONTROL, + SAMP_FIXED_USER_USERID + }; + DEFINE_ATTRBLOCK4(CreateAttrs,CreateAttrsTypes,CreateAttrsVal); + + // + // At this point of time there are just 2 accounts. So an enumeration + // will go through the case of No Context continuations. + // + printf("\t\tTesting Simple case with 2 initial account objects\n"); + + Status = SamrEnumerateUsersInDomain( + DomainContext, //DomainHandle, + &handle, // EnumerationContext, + 0, //UserAccountControl, + (PSAMPR_ENUMERATION_BUFFER *) & EnumResults, + 1024, // Max length + &Count + ); + if (Status != STATUS_SUCCESS) + goto Error; + + if (handle != 0) + { + Status = STATUS_UNSUCCESSFUL; + goto Error; + } + + SampPrintEnumResults(EnumResults, Count); + + + printf("\t\tTesting with %d more Accounts\n", NumAccountsToCreate); + Status = BuildDefaultSecurityDescriptor( + &(CreateAttrsVal[0].pVal), + &(CreateAttrsVal[0].valLen) + ); + + // Get the Domain Sid + + DomainSid = SampDsGetObjectSid(DomainContext->ObjectNameInDs); + + // Create the array that will hold the list of DS Names + NewObject = MIDL_user_allocate(NumAccountsToCreate * sizeof(DSNAME *)); + if (NULL==NewObject) + goto Error; + + // Zero the memory + RtlZeroMemory(NewObject,NumAccountsToCreate * sizeof(DSNAME *)); + + // Populate the directory with a large number of users + for (Index=0;Index<NumAccountsToCreate;Index++) + { + WCHAR Buffer[256]; + ULONG Len = 0; + + // + // Build an Account Name + // + swprintf(Buffer,L"User%d",Index); + Len = wcslen(Buffer) + 2; + CreateAttrsVal[1].pVal = (UCHAR *) Buffer; + CreateAttrsVal[1].valLen = Len * sizeof(WCHAR); + + // + // Populate account control field + // + + CreateAttrsVal[2].pVal = (UCHAR *) &Index; + CreateAttrsVal[2].valLen = sizeof(ULONG); + + // + // Create a New object Name + // + Status = SampDsCreateDsName( + DomainContext->ObjectNameInDs, + Index+100, + &(NewObject[Index]) + ); + + if NT_SUCCESS(Status) + { + NTSTATUS IgnoreStatus; + + // Create the new Object in the DS. + + Rid = Index + 100; + + IgnoreStatus = SampDsCreateObject( + NewObject[Index], + SampUserObjectType, + &CreateAttrs, + (PSID) DomainSid + ); + + } + else + NewObject[Index]=NULL; + } + + // + // Now do the work of Enumeration + // + + do + { + Status = SamrEnumerateUsersInDomain( + DomainContext, //DomainHandle, + &handle, // EnumerationContext, + 0, //UserAccountControl, + & EnumResults, + MaxMemorySize, // Max length + &Count + ); + if (!NT_SUCCESS(Status)) + goto Error; + + SampPrintEnumResults(EnumResults, Count); + } while (Status==STATUS_MORE_ENTRIES); + +Error: + + // + // Remove the Host of Accounts that we created. + // + if (NULL!=NewObject) + { + for (Index=0;Index<NumAccountsToCreate;Index++) + { + NTSTATUS IgnoreStatus; + + if (NULL!=NewObject[Index]) + { + IgnoreStatus = SampDsDeleteObject( + NewObject[Index] + ); + MIDL_user_free(NewObject[Index]); + } + + } + + MIDL_user_free(NewObject); + } + + return Status; +} + + + + + +NTSTATUS +SampTestContextAndUtility( + VOID * Param + ) +/*++ +Routine Description: + + Tests the Context and Utility Routines which do various Functions. + This Rouitne does not require the Storage Layer tests to succeed. + DS Layer tests need to pass before this test can pass. + + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + WCHAR Buffer[256]; + DSNAME *pDsName; + WCHAR ObjectName[] = L"/cn=Account"; + ULONG DomainIndex; + PSAMP_OBJECT AccountHandle; + ULONG Rid; + DSNAME *NewObject=NULL; + TESTINFO *TstInfo = (TESTINFO *)Param; + + + + + printf("TESTING CONTEXT AND UTILITY OPERATIONS\n"); + + // + // Create the required Objects in the DS + // + printf("\tSeeding DS\n"); + + SampAcquireWriteLock(); + + Status = SampSeedDS( + TstInfo->EnterpriseName, + TstInfo->EnterpriseNameLen + ); + + + if (Status != STATUS_SUCCESS) + { + printf("SEEDING DS Failed, Reurned Error code %x\n", Status); + goto Error; + } + + + // + // Initialize an account domain in the DS. + // + + printf("\tInitializing a DS Domain\n"); + pDsName = (DSNAME *) Buffer; + + SampInitializeDsName( + pDsName, + TstInfo->EnterpriseName, + TstInfo->EnterpriseNameLen, + ObjectName, + sizeof(ObjectName) + ); + + + Status = InitDsDomain(pDsName); + if (!NT_SUCCESS(Status)) + { + printf("Could not create DS Domain\n"); + SampUnseedDs( + TstInfo->EnterpriseName, + TstInfo->EnterpriseNameLen + ); + return Status; + } + + + DomainIndex = SampDefinedDomainsCount -1; + SampSetTransactionDomain(DomainIndex); + + // + // Do Account count Tests + // + + printf("\tDoing Account Count Test's\n"); + Status = SampDoAccountCountTests(); + if (Status != STATUS_SUCCESS) + { + printf("Account Count Test's Failed\n"); + goto Error; + } + + // + // Open Account Test -- Open a user account + // + printf("\tOpening Existing User Account\n"); + Status = SampOpenAccount( + SampUserObjectType, + SampDefinedDomains[DomainIndex].Context, + 0, + 2, + TRUE, + & AccountHandle + ); + + if (Status != STATUS_SUCCESS) + { + printf("\t\tOpening User Account Failed\n"); + goto Error; + } + + // + // Now Close the Handle + // + SampDeleteContext(AccountHandle); + + // + // Do Account Enumeration Tests + // + printf("\tDoing Enumeration Tests\n"); + Status = SampDoEnumerationTests( + SampDefinedDomains[DomainIndex].Context, + 8, // Num of Users to Create + 64 // Max memory size to use while enumerating + ); + if (Status != STATUS_SUCCESS) + { + printf("\t\tEnumeration test's Failed\n"); + goto Error; + } + + // Commit everything to the DS. + + Status = SampMaybeEndDsTransaction(FALSE); + + if (Status != STATUS_SUCCESS) + { + printf("\tSampMaybeEndDsTransaction error = 0x%lx\n", Status); + } + + printf("CONTEXT AND UTILITY TESTS PASSED\n"); + +Error: + + SampUnseedDs( + TstInfo->EnterpriseName, + TstInfo->EnterpriseNameLen + ); + + + SampReleaseWriteLock(FALSE); + + ASSERT(!SampExistsDsTransaction()); + + return Status; + +} + + diff --git a/private/newsam2/server/exe/grptest.c b/private/newsam2/server/exe/grptest.c new file mode 100644 index 000000000..52371c2a4 --- /dev/null +++ b/private/newsam2/server/exe/grptest.c @@ -0,0 +1,379 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + grptest.c + +Abstract: + + This file contains tests that test group and alias, membership maniplation + functions. + + THE ROUTINES IN THIS FILE ARE FOR SAM SEVER TEST PURPOSES ONLY. + + There are a number of server-side unit tests aimed at private routines + within the SAM server. These tests are defined in this file. This file + should only be compiled into the SAM server when it is built as a stand- + alone executable (built in the \um subdir). Therefore, it is only listed + in the sources file in the \um subdir. + + Because the SAM code relies on a fairly large amount of state information + being in place from the initialization of the server, it is difficult to + write a unit test that "plugs in" to the server from the outside. + +Author: + + Murli Satagopan (MURLIS) 15-July-1996 + +Environment: + + User Mode - Win32 + +Revision History: + + +--*/ + +#include <samsrvp.h> +#include <dsutilp.h> +#include <dslayer.h> +#include <mappings.h> +#include <testutil.h> + +// +// Private declaratins from group.c and alias.c +// + +NTSTATUS +SampAddAccountToAlias( + IN PSAMP_OBJECT AccountContext, + IN PSID AccountSid + ); + +NTSTATUS +SampRemoveAccountFromAlias( + IN PSAMP_OBJECT AccountContext, + IN PSID AccountSid + ); + +NTSTATUS +SampAddAccountToGroupMembers( + IN PSAMP_OBJECT GroupContext, + IN ULONG UserRid + ); + +NTSTATUS +SampRemoveAccountFromGroupMembers( + IN PSAMP_OBJECT GroupContext, + IN ULONG AccountRid + ); + + +NTSTATUS +SampInitMembershipTests( + VOID * Param + ) +{ + + NTSTATUS Status; + TESTINFO * TstInfo = (TESTINFO *) Param; + DSNAME * pDsName; + UCHAR Buffer[256]; + WCHAR ObjectName[] = L"/cn=Account"; + + + // + // Create the required Objects in the DS , + // Create thread state not required because of Lazy transactioning model + // + printf("\tSeeding DS\n"); + + Status = SampSeedDS( + TstInfo->EnterpriseName, + TstInfo->EnterpriseNameLen + ); + + if (Status != STATUS_SUCCESS) + { + printf("SEEDING DS Failed, Reurned Error code %x\n", Status); + goto Error; + } + + // + // Initialize an account domain in the DS. + // + + printf("\tInitializing a DS Domain\n"); + pDsName = (DSNAME *) Buffer; + + SampInitializeDsName( + pDsName, + TstInfo->EnterpriseName, + TstInfo->EnterpriseNameLen, + ObjectName, + sizeof(ObjectName) + ); + + + Status = InitDsDomain(pDsName); + +Error: + + if (!NT_SUCCESS(Status)) + { + printf("Could not create DS Domain\n"); + SampUnseedDs( + TstInfo->EnterpriseName, + TstInfo->EnterpriseNameLen + ); + } + + return Status; +} + + + +VOID +SampGroupTest( + VOID + ) +/* + + Routine Description: + + This routine tests group membership operations. + Functions that are tested are + SamrOpenGroup + SamrAddMemberToGroup + SamrRemoveMemberFromGroup + SamrGetMembersInGroup + + +*/ +{ + + NTSTATUS Status; + PSAMP_OBJECT GroupHandle; + PSAMPR_GET_MEMBERS_BUFFER GetMembersBuffer=NULL; + + printf("TESTING GROUP MEMBERSHIP OPERATIONS\n"); + + + // + // Open the Group Object + // + + printf("\tOpening Group Object\n"); + Status = SamrOpenGroup( + SampDefinedDomains[SampDefinedDomainsCount-1].Context, //DomainHandle, + 0xFFFFFFFF, // DesiredAccess, + 3, // GroupId, + &GroupHandle + ); + if (!NT_SUCCESS(Status)) + goto Error; + + + // + // Add Some members to Group + // + printf("\tAdding Some Members\n"); + Status = SampAddAccountToGroupMembers( + GroupHandle, + 2 // MemberId, + ); + if (!NT_SUCCESS(Status)) + goto Error; + + // + // Get the Group Membership List + // + + printf("\t Retrieving Group Members\n"); + Status = SamrGetMembersInGroup( + GroupHandle, + &GetMembersBuffer + ); + + if (!NT_SUCCESS(Status)) + goto Error; + + // + // Delete the Members From the Group + // + printf("\t Deleting Group Members\n"); + Status = SampRemoveAccountFromGroupMembers( + GroupHandle, + 2 + ); + if (!NT_SUCCESS(Status)) + goto Error; + +Error: + + if (!NT_SUCCESS(Status)) + { + printf("TEST FAILED, Error code is %d\n", Status); + } + else + { + printf("TEST PASSED\n"); + } +} + +VOID +SampAliasTest( + VOID + ) +/* + + Routine Description: + + This routine tests group membership operations. + Functions that are tested are + SamrOpenAlias + SamrAddMemberToAlias + SamrRemoveMemberFromAlias + SamrGetMembersInAlias + + +*/ +{ + + NTSTATUS Status; + PSAMP_OBJECT AliasHandle; + PSID DomainSid = NULL; + PSID AccountSid = NULL; + SAMPR_PSID_ARRAY GetMembersBuffer; + + printf("TESTING ALIAS MEMBERSHIP OPERATIONS\n"); + + + + // + // Open the Alias Object + // + + printf("\tOpening Alias Object\n"); + Status = SamrOpenAlias( + SampDefinedDomains[SampDefinedDomainsCount-1].Context, //DomainHandle, + 0xFFFFFFFF, // DesiredAccess, + 4, // GroupId, + &AliasHandle + ); + if (!NT_SUCCESS(Status)) + goto Error; + + // + // Get the Domain Sid + // + + printf("\tGetting Domain Sid\n"); + DomainSid = SampDsGetObjectSid( + SampDefinedDomains[SampDefinedDomainsCount-1].Context->ObjectNameInDs + ); + + if (NULL==DomainSid) + { + printf("\t Could Not Find Domain Sid ... Test Failed \n"); + return; + } + + // + // Compose the Sid for the Account + // + printf("Creating Full Sid\n"); + Status = SampCreateFullSid( + DomainSid, + 2, + &AccountSid + ); + if (!NT_SUCCESS(Status)) + goto Error; + + // + // Add Some members to Aliases. + // + + printf("\tAdding One Member\n"); + Status = SampAddAccountToAlias( + AliasHandle, + AccountSid + ); + if (!NT_SUCCESS(Status)) + goto Error; + + // + // Get the Group Membership List + // + + printf("\t Retrieving Group Members\n"); + Status = SamrGetMembersInAlias( + AliasHandle, + &GetMembersBuffer + ); + + if (!NT_SUCCESS(Status)) + goto Error; + + // + // Delete the Members From the Group + // + + printf("\t Deleting Group Members\n"); + Status = SampRemoveAccountFromAlias( + AliasHandle, + AccountSid + ); + if (!NT_SUCCESS(Status)) + goto Error; + +Error: + + if (!NT_SUCCESS(Status)) + { + printf("TEST FAILED, Error code is %d\n", Status); + } + else + { + printf("TEST PASSED\n"); + } +} + + + +NTSTATUS +SampMembershipTests( + VOID * Param + ) +{ + NTSTATUS Status; + TESTINFO * TstInfo = (TESTINFO *) Param; + + Status = SampInitMembershipTests(Param); + if (NT_SUCCESS(Status)) + { + SampGroupTest(); + SampAliasTest(); + SampUnseedDs( + TstInfo->EnterpriseName, + TstInfo->EnterpriseNameLen + ); + + } + + return STATUS_SUCCESS; +} + + + + + + + + + + + diff --git a/private/newsam2/server/exe/lsathunk.c b/private/newsam2/server/exe/lsathunk.c new file mode 100644 index 000000000..e91e88c91 --- /dev/null +++ b/private/newsam2/server/exe/lsathunk.c @@ -0,0 +1,242 @@ +/*++ +lsathunk.c + +Source file for the thunk layer for accessing the LSA through the +published NTLSAPI when SAM runs in user mode. User mode SAM is acomplished +by building with USER_MODE_SAM enabled. This causes all the SAM calls to +the LSA be remoted through the published NTLSAPI. + +Author: Murlis 4/30/96 + +Revision History + Murlis 4/30/96 + Created + +--*/ + + +// The Thunk Functions only need to compiled in when user mode operation of +// SAM is desired + +#ifdef USER_MODE_SAM + +#include <nt.h> +#include <ntlsa.h> +#include <samsrvp.h> + + +NTSTATUS LsaThunkIAuditSamEvent( + IN NTSTATUS PassedStatus, + IN ULONG AuditId, + IN PSID DomainSid, + IN PULONG MemberRid OPTIONAL, + IN PSID MemberSid OPTIONAL, + IN PUNICODE_STRING AccountName OPTIONAL, + IN PUNICODE_STRING DomainName, + IN PULONG AccountRid OPTIONAL, + IN PPRIVILEGE_SET Privileges OPTIONAL + ) + +/*++ + Thunk Function For LsaIAuditSamEvent. For now does nothing + + Arguments: + Same as in LsaIAuditSamEvent +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + + // For now do nothing + return Status; +} + + + + +NTSTATUS LsaThunkIOpenPolicyTrusted( + OUT PLSAPR_HANDLE PolicyHandle + ) +/*++ + This is the thunk routine for LsaIOpenPolicyTrusted when SAM + runs as a seperate user Mode App. This does the work of doing the + remote procedure call to the LSA, using LsaOpenPolicy + + Arguments: + Same as in LsaIOpenPolicyTrusted +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + LSA_OBJECT_ATTRIBUTES ObjectAttributes; + SECURITY_QUALITY_OF_SERVICE QualityOfService; + + // Initialize the ObjectAttributes structure + ObjectAttributes.Length = sizeof(ObjectAttributes); + ObjectAttributes.RootDirectory = NULL; + ObjectAttributes.ObjectName = NULL; + ObjectAttributes.SecurityDescriptor = NULL; + ObjectAttributes.SecurityQualityOfService = NULL; + ObjectAttributes.Attributes = 0L; + + // Specify the quality of service SecurityQualityOfService - + + QualityOfService.Length = sizeof(QualityOfService); + QualityOfService.ImpersonationLevel = SecurityImpersonation; + QualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; + + ObjectAttributes.SecurityQualityOfService = (PVOID) &QualityOfService; + + // Call LsaOpenPolicy to establish a session for the LSA. + Status = LsaOpenPolicy( + NULL, + &ObjectAttributes, + POLICY_ALL_ACCESS, + (PLSA_HANDLE) PolicyHandle + ); + return Status; +} + + + +NTSTATUS LsaThunkIFree_LSAPR_POLICY_INFORMATION( + POLICY_INFORMATION_CLASS InformationClass, + PLSAPR_POLICY_INFORMATION PolicyInformation + ) +/*++ + Thunk Function For LsaIFree_LSAPR_POLICY_INFORMATION. + + Arguments: + Same as in LsaIFree_LSAPR_POLICY_INFORMATION +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + + Status = LsaFreeReturnBuffer( (PVOID)PolicyInformation); + + return Status; + +} + + + +NTSTATUS LsaThunkIAuditNotifyPackageLoad( + PUNICODE_STRING PackageFileName + ) +/*++ + Thunk Function For LsaIAuditNotifyPackageLoad. For now does nothing + + Arguments: + Same as in LsaIAuditNotifyPackageLoad +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + + // For now do nothing + return Status; +} + + + +NTSTATUS LsaThunkrQueryInformationPolicy( + IN LSAPR_HANDLE PolicyHandle, + IN POLICY_INFORMATION_CLASS InformationClass, + OUT PLSAPR_POLICY_INFORMATION *Buffer + ) +/*++ + Thunk Function For LsarQueryInformationPolicy . + + Arguments: + Same as in LsarQueryInformationPolicy +--*/ + +{ + NTSTATUS Status; + + // Call LsaQeryInformationPolicy + + // ASSUMPTION: LsaQueryInformation policy returns a buffer + // which is PVOID, while the internal routines use a + // PLSAPR_POLICY_INFORMATION structure pointer . This makes the assumption + // that the returned data is such that the PVOID can be cast into + // the PLSAPR_POLICY_INFORMATION structure. In RISC machines this + // can cause alignment faults so we should SetErrorMode. + + Status = LsaQueryInformationPolicy((LSA_HANDLE) PolicyHandle, + InformationClass, + Buffer + ); + return Status; +} + + +NTSTATUS LsaThunkrClose( + IN OUT LSAPR_HANDLE *ObjectHandle + ) +/*++ + + Thunk Function for LsarClose. Calls LsaClose to close a handle + returned by the LSA. + + Arguments: + Same as in LsarClose +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + + Status = LsaClose((PLSA_HANDLE) ObjectHandle); + return Status; +} + + + +NTSTATUS LsaThunkIQueryInformationPolicyTrusted( + IN POLICY_INFORMATION_CLASS InformationClass, + OUT PLSAPR_POLICY_INFORMATION *Buffer + ) + /*++ + Thunk Function For LsaIQueryInformationPolicyTrusted . For now does nothing + + Arguments: + Same as in LsaIQueryInformationPolicyTrusted +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + NTSTATUS StatusTmp = STATUS_SUCCESS; + LSAPR_HANDLE PolicyHandle; + + // Establish conection to LSA + Status = LsaThunkIOpenPolicyTrusted(&PolicyHandle); + if (Status != STATUS_SUCCESS) + return Status; + + Status = LsaThunkrQueryInformationPolicy(PolicyHandle, + InformationClass, + Buffer + ); + + StatusTmp = LsaThunkrClose(PolicyHandle); + + return Status; +} + + + + +NTSTATUS LsaThunkIHealthCheck( + IN ULONG CallerId + ) +/*++ + + Thunk Function for LsaIHealthCheck. + + Arguments: + Same as in LsaIHealthCheck +--*/ + +{ + // For now this does nothing + return STATUS_SUCCESS; +} +#endif diff --git a/private/newsam2/server/exe/main.c b/private/newsam2/server/exe/main.c new file mode 100644 index 000000000..479bd81b6 --- /dev/null +++ b/private/newsam2/server/exe/main.c @@ -0,0 +1,369 @@ +/*++ + +Copyright (c) 1994 Microsoft Corporation + +Module Name: + + main.c + +Abstract: + + Main Routine for User mode SAM + + +Author: + + Murlis 9-May-1996 + +Environment: + + User Mode - Win32 + +Revision History: + + ChrisMay 11-Jun-96 + Added return code checking, messages, cmd-line parser, -s test. + ChrisMay 21-Jun-96 + Added -s -a tests + ChrisMay 27-Jun-96 + Added -aa test + +--*/ + +#ifdef USER_MODE_SAM + +#include <samsrvp.h> +#include <duapi.h> +#include <dslayer.h> +#include <mappings.h> +#include <process.h> +#include <testutil.h> + +// Private debugging display routine is enabled when DSUTIL_DBG_PRINTF = 1. + +#define SAMAPP_DBG_PRINTF 1 + +#if (SAMAPP_DBG_PRINTF == 1) +#define DebugPrint printf +#else +#define DebugPrint +#endif + +// +// Declaration for SAM startup routine +// + +NTSTATUS +SamIInitialize( + VOID + ); + + +// +// Declaration for SAM test routines +// +NTSTATUS +BasicAttributeTest( + PVOID Parameter + ); + +NTSTATUS +AdvancedAttributeTest( + VOID *Parameter + ); + +NTSTATUS +BasicStorageTest( + PVOID Parameter + ); + +NTSTATUS __stdcall +SampTestDsLayer( + VOID * Param + ); + +NTSTATUS __stdcall +SampTestContextAndUtility( + VOID * Param + ); + +NTSTATUS __stdcall +SampMembershipTests( + VOID * Param + ); + + +VOID +ReadNamePrefix( + TESTINFO * TstInfo + ); + +VOID +SetDefaultEnterpriseName( + TESTINFO * TstInfo + ); + +VOID +Message( + VOID + ) +{ + printf("\nsamapp usage:\n"); + + printf("\t-? Display the usage message\n"); + printf("\t-a Run the attribute test\n"); + printf("\t-d Run the DS-layer test\n"); + printf("\t-s Run the storage-layer test\n"); + printf("\t-c Run Context and utility test\n"); + printf("\t-p Prompts for Org and Org Unit Name\n"); + printf("\t-m Group and Alias Membership Tests\n"); + + printf("\n"); + + return; +} + +VOID _CRTAPI1 +main(int argc, char *argv[]) + +//+ +//+ +//+ Main Routine , Just calls SamIInitialize and then exits the thread +//+ +//+ + +{ + NTSTATUS Status = STATUS_SUCCESS; + INT arg = 1; + WCHAR Parameter[128]; + HANDLE ThreadHandle = NULL; + ULONG ThreadId = 0; + void * Tmp = NULL; + BOOL DirectoryInitialized = FALSE; + unsigned StackSize = 10000; + PVOID Security = NULL; + TESTINFO TestInfo; + PVOID TestParam = (PVOID) &(TestInfo); + unsigned ThreadInitState = 0; // Set running ? + + + // Default Organization and Organizational Unit names (can be overridden + // by command line switch -p ) + + SetDefaultEnterpriseName(&TestInfo); + + // Initialize SAM server. + + Status = SamIInitialize(); + + if (Status != STATUS_SUCCESS) + { + DebugPrint("SamIInitialize error = 0x%lx\n", Status); + goto Error; + } + + // Initialize the Directry Service. + + Status = SampDsInitialize(); + + if (Status != STATUS_SUCCESS) + { + DebugPrint("SampDsInitialize error = 0x%lx\n", Status); + goto Error; + } + + DirectoryInitialized = TRUE; + + // Start the RPC server + + Status = I_RpcMapWin32Status(RpcServerListen(1,1234,1)); + + if (Status != STATUS_SUCCESS) + { + DebugPrint("RpcServerListen status = 0x%lx\n", Status); + + // Continue after benign error. + } + + + // Parse command-line arguments. + + while(arg < argc) + { + // NOTE: Each test should conclude with a display message to stdout + // indicating "PASSED" or "FAILED" status. + + if (0 == _stricmp(argv[arg], "-?")) + { + Message(); + } + else if (0 == _stricmp(argv[arg], "-p")) + { + ReadNamePrefix(&TestInfo); + } + else if (0 == _stricmp(argv[arg], "-s")) + { + // SampStoreObjectAttributes test - see stgtest.c + + // Create a separate thread for the test because it will + // create thread state in order to prepare for DSA calls - + // SampMaybeBegin/EndDsTransaction should not be called + // on the main thread. + + ThreadHandle = (HANDLE) _beginthreadex(Security, + StackSize, + BasicStorageTest, + TestParam, + ThreadInitState, + &ThreadId); + + if (NULL != ThreadHandle) + { + WaitForSingleObject(ThreadHandle, INFINITE); + CloseHandle(ThreadHandle); + } + else + { + DebugPrint("ThreadHandle is NULL - cannot start test\n"); + break; + } + } + else if (0 == _stricmp(argv[arg], "-a")) + { + // BasicAttributeTest test - see attrtest.c. + + ThreadHandle = (HANDLE) _beginthreadex(Security, + StackSize, + BasicAttributeTest, + TestParam, + ThreadInitState, + &ThreadId); + + if (NULL != ThreadHandle) + { + WaitForSingleObject(ThreadHandle, INFINITE); + CloseHandle(ThreadHandle); + } + else + { + DebugPrint("ThreadHandle is NULL - cannot start test\n"); + break; + } + } + else if (0 == _stricmp(argv[arg], "-aa")) + { + // AdvancedAttributeTest test - see attrtest.c. + + ThreadHandle = (HANDLE) _beginthreadex(Security, + StackSize, + AdvancedAttributeTest, + TestParam, + ThreadInitState, + &ThreadId); + + if (NULL != ThreadHandle) + { + WaitForSingleObject(ThreadHandle, INFINITE); + CloseHandle(ThreadHandle); + } + else + { + DebugPrint("ThreadHandle is NULL - cannot start test\n"); + break; + } + } + else if (0 == _stricmp(argv[arg], "-d")) + { + ThreadHandle = (HANDLE) _beginthreadex(Security, + StackSize, + SampTestDsLayer, + TestParam, + ThreadInitState, + &ThreadId); + if (NULL != ThreadHandle) + { + + WaitForSingleObject(ThreadHandle, INFINITE); + CloseHandle(ThreadHandle); + } + else + { + DebugPrint("ThreadHandle is NULL - cannot start test\n"); + break; + } + } + else if (0 == _stricmp(argv[arg], "-c")) + { + ThreadHandle = (HANDLE) _beginthreadex(Security, + StackSize, + SampTestContextAndUtility, + TestParam, + ThreadInitState, + &ThreadId); + if (NULL != ThreadHandle) + { + + WaitForSingleObject(ThreadHandle, INFINITE); + CloseHandle(ThreadHandle); + } + else + { + DebugPrint("ThreadHandle is NULL - cannot start test\n"); + break; + } + } + else if (0 == _stricmp(argv[arg], "-m")) + { + ThreadHandle = (HANDLE) _beginthreadex(Security, + StackSize, + SampMembershipTests, + TestParam, + ThreadInitState, + &ThreadId); + if (NULL != ThreadHandle) + { + + WaitForSingleObject(ThreadHandle, INFINITE); + CloseHandle(ThreadHandle); + } + else + { + DebugPrint("ThreadHandle is NULL - cannot start test\n"); + break; + } + } + + + + + // Add more cases here... + + arg++; + } + + // Wait till user wants to terminate + + printf("\nEnter 'q' and hit return to terminate application\n"); + + scanf("%s",&Tmp); + +Error: + + Status = I_RpcMapWin32Status(RpcMgmtStopServerListening(NULL)); + + if (Status != STATUS_SUCCESS) + { + DebugPrint("RpcMgmtStopServerListening error = 0x%lx\n", Status); + } + + Status = SampDsUninitialize(); + + if (Status != STATUS_SUCCESS) + { + DebugPrint("SampDsUninitialize error = 0x%lx\n", Status); + } + + DebugPrint("Exiting SAM Main\n"); + +} +#endif diff --git a/private/newsam2/server/exe/makefile b/private/newsam2/server/exe/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/newsam2/server/exe/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/newsam2/server/exe/makefile.inc b/private/newsam2/server/exe/makefile.inc new file mode 100644 index 000000000..4cedea558 --- /dev/null +++ b/private/newsam2/server/exe/makefile.inc @@ -0,0 +1,2 @@ +sampmsgs.h msg00001.bin: ..\sampmsgs.mc + mc -v ..\sampmsgs.mc diff --git a/private/newsam2/server/exe/ntds.prf b/private/newsam2/server/exe/ntds.prf new file mode 100644 index 000000000..5c829c4a2 --- /dev/null +++ b/private/newsam2/server/exe/ntds.prf @@ -0,0 +1,700 @@ +SamrGetAliasMembership@12 +SampAlQueryAliasMembership@12 +SampAlLookupMemberAccount@12 +SampAlLookupMemberDomain@12 +SampAlInfoIsValidForDomain@4 +SampLookupContext@16 +SampDeReferenceContext@8 +SampValidateContextAddress@4 +SampAcquireReadLock@0 +SampReleaseReadLock@0 +SampSetTransactionDomain@4 +SampSplitSid@12 +SampGetFixedAttributes@12 +SampGetUnicodeStringAttribute@16 +SampGetAccessAttribute@20 +SampGetLargeIntArrayAttribute@20 +SampGetLogonHoursAttribute@16 +SampValidateAttributes@8 +SampObjectAttributeAddress@8 +SampObjectAttributeLength@8 +SampObjectAttributeQualifier@8 +SampGetAttributeBufferReadInfo@20 +SampExtendAttributeBuffer@8 +SampReadRegistryAttribute@20 +SampFreeAttributeBuffer@4 +SamrCloseHandle@4 +SampCreateContext@8 +SampDeleteContext@4 +SampReferenceContext@4 +SampInvalidateContextAddress@4 +SampRtlConvertUlongToUnicodeString@20 +SampAddNewValidContextAddress@4 +SampGetPrivateUserData@12 +SamIFree_SAMPR_ULONG_ARRAY@4 +SamIFree_SAMPR_USER_INFO_BUFFER@8 +SamIFree_SAMPR_GET_GROUPS_BUFFER@4 +_fgs__RPC_UNICODE_STRING@4 +_fgs__SAMPR_SR_SECURITY_DESCRIPTOR@4 +_fgs__SAMPR_GET_GROUPS_BUFFER@4 +_fgs__SAMPR_LOGON_HOURS@4 +_fgs__SAMPR_ULONG_ARRAY@4 +_fgs__SAMPR_USER_ALL_INFORMATION@4 +_fgu__SAMPR_USER_INFO_BUFFER@8 +SampGetObjectSD@12 +SampValidateObjectAccess@12 +SamrOpenUser@16 +SamrQueryInformationUser@12 +SamrGetGroupsForUser@8 +SamIAccountRestrictions@24 +SampGetPasswordMustChange@20 +SampRetrieveUserPasswords@24 +SampRetrieveUserMembership@16 +SampRetrieveUserLogonHours@8 +SampRetrieveUserV1aFixed@8 +SampMatchworkstation@8 +SampInitUnicodeString@8 +SampAppendUnicodeString@8 +SampFreeUnicodeString@4 +SampBuildAccountSubKeyName@16 +SampOpenAccount@24 +SampCreateAccountContext@20 +SamrLookupNamesInDomain@20 +SampBuildAccountKeyName@12 +SamrQueryDisplayInformation@32 +SamrGetDisplayEnumerationIndex@16 +SampInitializeDisplayInformation@4 +SampDeleteDisplayInformation@8 +SampCreateDisplayInformation@4 +SampUpdateDisplayInformation@12 +SampDeleteDisplayAccount@12 +SampAddDisplayAccount@12 +SampUpdateDisplayAccount@12 +SampInitializeUserInfo@12 +SampInitializeMachineInfo@12 +SampDuplicateUserInfo@12 +SampDuplicateMachineInfo@12 +SampFreeUserInfo@4 +SampFreeMachineInfo@4 +SampBytesRequiredUserNode@4 +SampBytesRequiredMachineNode@4 +SamrOpenAlias@16 +SamrQueryInformationAlias@12 +SamrSetInformationAlias@12 +SamrDeleteAlias@4 +SamrAddMemberToAlias@8 +SamrRemoveMemberFromAlias@8 +SamrGetMembersInAlias@8 +SampRemoveAccountFromAllAliases@20 +SampRetrieveAliasMembership@16 +SampAddAccountToAlias@8 +SampRemoveAccountFromAlias@8 +SampAddAliasToAccountMembership@8 +SampRemoveAliasFromAccountMembership@8 +SampRemoveAliasFromAllAccounts@4 +SampRetrieveAliasMembers@12 +SampDeleteAliasKeys@4 +SampDeleteAliasMembershipKeysForAccount@4 +SampAdjustAliasDomainsCount@4 +SampValidateNewAliasMember@4 +SampChangeAliasAccountName@12 +SampAlSlowQueryAliasMembership@12 +SampAlQueryMembersOfAlias@8 +SampAlAddMembersToAlias@12 +SampAlRemoveMembersFromAlias@12 +SampAlLookupMembersInAlias@16 +SampAlDeleteAlias@4 +SampAlRemoveAccountFromAllAliases@20 +SampAlBuildAliasInformation@0 +SampAlCreateMemberAccount@20 +SampAlAllocateMemberAccount@16 +SampAlGrowMemberAccount@16 +SampAlAddAliasesToMemberAccount@20 +SampAlLookupAliasesInMemberAccount@12 +SampAlRemoveAliasesFromMemberAccount@20 +SampAlDeleteMemberAccount@12 +SampAlCreateMemberDomain@12 +SampAlAllocateMemberDomain@12 +SampAlGrowMemberDomain@12 +SampAlDeleteMemberDomain@8 +SampAlCreateMemberAliasList@12 +SampAlGrowMemberAliasList@8 +SampAlBuildAliasInformationKeyNames@4 +SampAlBuildMemberAliasList@4 +SampAlInfoIsValidForAlias@4 +SampAlFastRestoreList@20 +SampInitObjectInfoAttributes@0 +SampStoreObjectAttributes@8 +SampDeleteAttributeKeys@4 +SampSetFixedAttributes@8 +SampSetUnicodeStringAttribute@12 +SampGetSidAttribute@16 +SampSetSidAttribute@12 +SampSetAccessAttribute@16 +SampGetUlongArrayAttribute@24 +SampSetUlongArrayAttribute@20 +SampSetLargeIntArrayAttribute@16 +SampGetSidArrayAttribute@24 +SampSetSidArrayAttribute@20 +SampSetLogonHoursAttribute@12 +SampSetVariableAttribute@20 +Usage@0 +UnexpectedProblem@0 +Initialize@0 +InitializeSecurityDescriptors@0 +TmppBuildSamProtection@24 +SampGetDomainPolicy@0 +SetCurrentDomain@4 +InitializeSam@0 +SampCreateDatabaseProtection@4 +CreateBuiltinDomain@0 +CreateAccountDomain@0 +PrepDomain@4 +CreateAlias@20 +CreateGroup@20 +CreateUser@32 +BuildPrimaryDomainSid@4 +BuildAccountSid@8 +UpdateAliasXReference@8 +OpenAliasMember@8 +OpenOrCreateAccountRidKey@12 +OpenOrCreateAliasDomainKey@8 +AppendAliasDomainNameToUnicodeString@8 +TmppGetAccountDomainInfo@4 +SampGetServerRole@0 +SampGetPrimaryDomainInfo@0 +SampRestoreDefaultDacl@0 +SampDetermineSetupEnvironment@0 +SampInitializeRegistry@0 +SampGetMessageStrings@20 +SampInvalidateGroupContexts@4 +SampInvalidateAliasContexts@4 +SampInvalidateUserContexts@4 +SampInvalidateContextListKeys@8 +SamrOpenDomain@16 +SamrQueryInformationDomain@12 +SamrSetInformationDomain@12 +SampCreateGroupInDomain@24 +SamrCreateGroupInDomain@20 +SamrEnumerateGroupsInDomain@20 +SampCreateAliasInDomain@24 +SamrCreateAliasInDomain@20 +SamrEnumerateAliasesInDomain@20 +SamrRemoveMemberFromForeignDomain@8 +SamrCreateUserInDomain@20 +SamrEnumerateUsersInDomain@24 +SamrLookupIdsInDomain@20 +SampOpenDomainKey@8 +SampInitializeDomainObject@0 +SampInitializeSingleDomain@4 +SampSetDomainPolicy@0 +SampReInitializeSingleDomain@4 +SampGetAccountDomainInfo@4 +SampCollisionError@4 +SamICreateAccountByRid@28 +SamIGetSerialNumberDomain@12 +SamISetSerialNumberDomain@16 +SamIGetPrivateData@20 +SampSetPrivateUserData@12 +SamISetPrivateData@12 +SamrTestPrivateFunctionsDomain@4 +SamrTestPrivateFunctionsUser@4 +SampBuildDomainKeyName@8 +SamrOpenGroup@16 +SamrQueryInformationGroup@12 +SamrSetInformationGroup@12 +SamrAddMemberToGroup@12 +SamrDeleteGroup@4 +SamrRemoveMemberFromGroup@8 +SamrGetMembersInGroup@8 +SamrSetMemberAttributesOfGroup@12 +SampAddUserToGroup@8 +SampRemoveUserFromGroup@8 +SampRetrieveGroupV1Fixed@8 +SampReplaceGroupV1Fixed@8 +SampRetrieveGroupMembers@12 +SampReplaceGroupMembers@12 +SampDeleteGroupKeys@4 +SampChangeGroupAccountName@12 +SampAddAccountToGroupMembers@8 +SampRemoveAccountFromGroupMembers@8 +SAMPR_HANDLE_rundown@4 +SamIFree_SAMPR_SR_SECURITY_DESCRIPTOR@4 +SamIFree_SAMPR_DOMAIN_INFO_BUFFER@8 +SamIFree_SAMPR_ENUMERATION_BUFFER@4 +SamIFree_SAMPR_PSID_ARRAY@4 +SamIFree_SAMPR_RETURNED_USTRING_ARRAY@4 +SamIFree_SAMPR_GROUP_INFO_BUFFER@8 +SamIFree_SAMPR_ALIAS_INFO_BUFFER@8 +SamIFree_SAMPR_GET_MEMBERS_BUFFER@4 +SamIFree_SAMPR_DISPLAY_INFO_BUFFER@8 +samr_SamrConnect@4 +samr_SamrCloseHandle@4 +samr_SamrSetSecurityObject@4 +samr_SamrQuerySecurityObject@4 +samr_SamrShutdownSamServer@4 +samr_SamrLookupDomainInSamServer@4 +samr_SamrEnumerateDomainsInSamServer@4 +samr_SamrOpenDomain@4 +samr_SamrQueryInformationDomain@4 +samr_SamrSetInformationDomain@4 +samr_SamrCreateGroupInDomain@4 +samr_SamrEnumerateGroupsInDomain@4 +samr_SamrCreateUserInDomain@4 +samr_SamrEnumerateUsersInDomain@4 +samr_SamrCreateAliasInDomain@4 +samr_SamrEnumerateAliasesInDomain@4 +samr_SamrGetAliasMembership@4 +samr_SamrLookupNamesInDomain@4 +samr_SamrLookupIdsInDomain@4 +samr_SamrOpenGroup@4 +samr_SamrQueryInformationGroup@4 +samr_SamrSetInformationGroup@4 +samr_SamrAddMemberToGroup@4 +samr_SamrDeleteGroup@4 +samr_SamrRemoveMemberFromGroup@4 +samr_SamrGetMembersInGroup@4 +samr_SamrSetMemberAttributesOfGroup@4 +samr_SamrOpenAlias@4 +samr_SamrQueryInformationAlias@4 +samr_SamrSetInformationAlias@4 +samr_SamrDeleteAlias@4 +samr_SamrAddMemberToAlias@4 +samr_SamrRemoveMemberFromAlias@4 +samr_SamrGetMembersInAlias@4 +samr_SamrOpenUser@4 +samr_SamrDeleteUser@4 +samr_SamrQueryInformationUser@4 +samr_SamrSetInformationUser@4 +samr_SamrChangePasswordUser@4 +samr_SamrGetGroupsForUser@4 +samr_SamrQueryDisplayInformation@4 +samr_SamrGetDisplayEnumerationIndex@4 +samr_SamrTestPrivateFunctionsDomain@4 +samr_SamrTestPrivateFunctionsUser@4 +samr_SamrGetUserDomainPasswordInformation@4 +samr_SamrRemoveMemberFromForeignDomain@4 +_gns__SID_IDENTIFIER_AUTHORITY@8 +_pns__DOMAIN_SERVER_ROLE_INFORMATION@8 +_gns__DOMAIN_SERVER_ROLE_INFORMATION@8 +_pns__DOMAIN_STATE_INFORMATION@8 +_gns__DOMAIN_STATE_INFORMATION@8 +_gns__CYPHER_BLOCK@8 +_gns__ENCRYPTED_LM_OWF_PASSWORD@8 +_sgs__RPC_UNICODE_STRING@8 +_pgs__RPC_UNICODE_STRING@8 +_ggs__RPC_UNICODE_STRING@12 +_sns__RPC_SID@8 +_pns__RPC_SID@8 +_gns__RPC_SID@12 +_ggs__RPC_SID@16 +_ans__RPC_SID@8 +_ags__RPC_SID@12 +_sgs__SAMPR_RID_ENUMERATION@8 +_pgs__SAMPR_RID_ENUMERATION@8 +_fgs__SAMPR_RID_ENUMERATION@4 +_sgs__SAMPR_ENUMERATION_BUFFER@8 +_pgs__SAMPR_ENUMERATION_BUFFER@8 +_fgs__SAMPR_ENUMERATION_BUFFER@4 +_sgs__SAMPR_SR_SECURITY_DESCRIPTOR@8 +_pgs__SAMPR_SR_SECURITY_DESCRIPTOR@8 +_ggs__SAMPR_SR_SECURITY_DESCRIPTOR@12 +_sgs__SAMPR_GET_GROUPS_BUFFER@8 +_pgs__SAMPR_GET_GROUPS_BUFFER@8 +_sgs__SAMPR_GET_MEMBERS_BUFFER@8 +_pgs__SAMPR_GET_MEMBERS_BUFFER@8 +_fgs__SAMPR_GET_MEMBERS_BUFFER@4 +_sgs__SAMPR_LOGON_HOURS@8 +_pgs__SAMPR_LOGON_HOURS@8 +_ggs__SAMPR_LOGON_HOURS@12 +_sgs__SAMPR_ULONG_ARRAY@8 +_pgs__SAMPR_ULONG_ARRAY@8 +_sgs__SAMPR_SID_INFORMATION@8 +_pgs__SAMPR_SID_INFORMATION@8 +_ggs__SAMPR_SID_INFORMATION@16 +_ags__SAMPR_SID_INFORMATION@8 +_fgs__SAMPR_SID_INFORMATION@4 +_sgs__SAMPR_PSID_ARRAY@8 +_pgs__SAMPR_PSID_ARRAY@8 +_ggs__SAMPR_PSID_ARRAY@12 +_fgs__SAMPR_PSID_ARRAY@4 +_sgs__SAMPR_RETURNED_USTRING_ARRAY@8 +_pgs__SAMPR_RETURNED_USTRING_ARRAY@8 +_fgs__SAMPR_RETURNED_USTRING_ARRAY@4 +_sgs__SAMPR_DOMAIN_GENERAL_INFORMATION@8 +_pgs__SAMPR_DOMAIN_GENERAL_INFORMATION@8 +_ggs__SAMPR_DOMAIN_GENERAL_INFORMATION@12 +_fgs__SAMPR_DOMAIN_GENERAL_INFORMATION@4 +_sgs__SAMPR_DOMAIN_OEM_INFORMATION@8 +_pgs__SAMPR_DOMAIN_OEM_INFORMATION@8 +_ggs__SAMPR_DOMAIN_OEM_INFORMATION@12 +_fgs__SAMPR_DOMAIN_OEM_INFORMATION@4 +_sgs__SAMPR_DOMAIN_NAME_INFORMATION@8 +_pgs__SAMPR_DOMAIN_NAME_INFORMATION@8 +_ggs__SAMPR_DOMAIN_NAME_INFORMATION@12 +_fgs__SAMPR_DOMAIN_NAME_INFORMATION@4 +_sgs_SAMPR_DOMAIN_REPLICATION_INFORMATION@8 +_pgs_SAMPR_DOMAIN_REPLICATION_INFORMATION@8 +_ggs_SAMPR_DOMAIN_REPLICATION_INFORMATION@12 +_fgs_SAMPR_DOMAIN_REPLICATION_INFORMATION@4 +_snu__SAMPR_DOMAIN_INFO_BUFFER@12 +_sgu__SAMPR_DOMAIN_INFO_BUFFER@12 +_pnu__SAMPR_DOMAIN_INFO_BUFFER@12 +_pgu__SAMPR_DOMAIN_INFO_BUFFER@12 +_gnu__SAMPR_DOMAIN_INFO_BUFFER@12 +_ggu__SAMPR_DOMAIN_INFO_BUFFER@16 +_fgu__SAMPR_DOMAIN_INFO_BUFFER@8 +_sgs__SAMPR_GROUP_GENERAL_INFORMATION@8 +_pgs__SAMPR_GROUP_GENERAL_INFORMATION@8 +_ggs__SAMPR_GROUP_GENERAL_INFORMATION@12 +_fgs__SAMPR_GROUP_GENERAL_INFORMATION@4 +_sgs__SAMPR_GROUP_NAME_INFORMATION@8 +_pgs__SAMPR_GROUP_NAME_INFORMATION@8 +_ggs__SAMPR_GROUP_NAME_INFORMATION@12 +_fgs__SAMPR_GROUP_NAME_INFORMATION@4 +_sgs__SAMPR_GROUP_ADM_COMMENT_INFORMATION@8 +_pgs__SAMPR_GROUP_ADM_COMMENT_INFORMATION@8 +_ggs__SAMPR_GROUP_ADM_COMMENT_INFORMATION@12 +_fgs__SAMPR_GROUP_ADM_COMMENT_INFORMATION@4 +_snu__SAMPR_GROUP_INFO_BUFFER@12 +_sgu__SAMPR_GROUP_INFO_BUFFER@12 +_pnu__SAMPR_GROUP_INFO_BUFFER@12 +_pgu__SAMPR_GROUP_INFO_BUFFER@12 +_gnu__SAMPR_GROUP_INFO_BUFFER@12 +_ggu__SAMPR_GROUP_INFO_BUFFER@16 +_fgu__SAMPR_GROUP_INFO_BUFFER@8 +_sgs__SAMPR_ALIAS_GENERAL_INFORMATION@8 +_pgs__SAMPR_ALIAS_GENERAL_INFORMATION@8 +_ggs__SAMPR_ALIAS_GENERAL_INFORMATION@12 +_fgs__SAMPR_ALIAS_GENERAL_INFORMATION@4 +_sgs__SAMPR_ALIAS_NAME_INFORMATION@8 +_pgs__SAMPR_ALIAS_NAME_INFORMATION@8 +_ggs__SAMPR_ALIAS_NAME_INFORMATION@12 +_fgs__SAMPR_ALIAS_NAME_INFORMATION@4 +_sgs__SAMPR_ALIAS_ADM_COMMENT_INFORMATION@8 +_pgs__SAMPR_ALIAS_ADM_COMMENT_INFORMATION@8 +_ggs__SAMPR_ALIAS_ADM_COMMENT_INFORMATION@12 +_fgs__SAMPR_ALIAS_ADM_COMMENT_INFORMATION@4 +_snu__SAMPR_ALIAS_INFO_BUFFER@12 +_sgu__SAMPR_ALIAS_INFO_BUFFER@12 +_pnu__SAMPR_ALIAS_INFO_BUFFER@12 +_pgu__SAMPR_ALIAS_INFO_BUFFER@12 +_gnu__SAMPR_ALIAS_INFO_BUFFER@12 +_ggu__SAMPR_ALIAS_INFO_BUFFER@16 +_fgu__SAMPR_ALIAS_INFO_BUFFER@8 +_sgs__SAMPR_USER_ALL_INFORMATION@8 +_pgs__SAMPR_USER_ALL_INFORMATION@8 +_ggs__SAMPR_USER_ALL_INFORMATION@12 +_sgs__SAMPR_USER_GENERAL_INFORMATION@8 +_pgs__SAMPR_USER_GENERAL_INFORMATION@8 +_ggs__SAMPR_USER_GENERAL_INFORMATION@12 +_fgs__SAMPR_USER_GENERAL_INFORMATION@4 +_sgs__SAMPR_USER_PREFERENCES_INFORMATION@8 +_pgs__SAMPR_USER_PREFERENCES_INFORMATION@8 +_ggs__SAMPR_USER_PREFERENCES_INFORMATION@12 +_fgs__SAMPR_USER_PREFERENCES_INFORMATION@4 +_sgs__SAMPR_USER_PARAMETERS_INFORMATION@8 +_pgs__SAMPR_USER_PARAMETERS_INFORMATION@8 +_ggs__SAMPR_USER_PARAMETERS_INFORMATION@12 +_fgs__SAMPR_USER_PARAMETERS_INFORMATION@4 +_sgs__SAMPR_USER_LOGON_INFORMATION@8 +_pgs__SAMPR_USER_LOGON_INFORMATION@8 +_ggs__SAMPR_USER_LOGON_INFORMATION@12 +_fgs__SAMPR_USER_LOGON_INFORMATION@4 +_sgs__SAMPR_USER_ACCOUNT_INFORMATION@8 +_pgs__SAMPR_USER_ACCOUNT_INFORMATION@8 +_ggs__SAMPR_USER_ACCOUNT_INFORMATION@12 +_fgs__SAMPR_USER_ACCOUNT_INFORMATION@4 +_sgs__SAMPR_USER_A_NAME_INFORMATION@8 +_pgs__SAMPR_USER_A_NAME_INFORMATION@8 +_ggs__SAMPR_USER_A_NAME_INFORMATION@12 +_fgs__SAMPR_USER_A_NAME_INFORMATION@4 +_sgs__SAMPR_USER_F_NAME_INFORMATION@8 +_pgs__SAMPR_USER_F_NAME_INFORMATION@8 +_ggs__SAMPR_USER_F_NAME_INFORMATION@12 +_fgs__SAMPR_USER_F_NAME_INFORMATION@4 +_sgs__SAMPR_USER_NAME_INFORMATION@8 +_pgs__SAMPR_USER_NAME_INFORMATION@8 +_ggs__SAMPR_USER_NAME_INFORMATION@12 +_fgs__SAMPR_USER_NAME_INFORMATION@4 +_sgs__SAMPR_USER_HOME_INFORMATION@8 +_pgs__SAMPR_USER_HOME_INFORMATION@8 +_ggs__SAMPR_USER_HOME_INFORMATION@12 +_fgs__SAMPR_USER_HOME_INFORMATION@4 +_sgs__SAMPR_USER_SCRIPT_INFORMATION@8 +_pgs__SAMPR_USER_SCRIPT_INFORMATION@8 +_ggs__SAMPR_USER_SCRIPT_INFORMATION@12 +_fgs__SAMPR_USER_SCRIPT_INFORMATION@4 +_sgs__SAMPR_USER_PROFILE_INFORMATION@8 +_pgs__SAMPR_USER_PROFILE_INFORMATION@8 +_ggs__SAMPR_USER_PROFILE_INFORMATION@12 +_fgs__SAMPR_USER_PROFILE_INFORMATION@4 +_sgs__SAMPR_USER_ADMIN_COMMENT_INFORMATION@8 +_pgs__SAMPR_USER_ADMIN_COMMENT_INFORMATION@8 +_ggs__SAMPR_USER_ADMIN_COMMENT_INFORMATION@12 +_fgs__SAMPR_USER_ADMIN_COMMENT_INFORMATION@4 +_sgs__SAMPR_USER_WORKSTATIONS_INFORMATION@8 +_pgs__SAMPR_USER_WORKSTATIONS_INFORMATION@8 +_ggs__SAMPR_USER_WORKSTATIONS_INFORMATION@12 +_fgs__SAMPR_USER_WORKSTATIONS_INFORMATION@4 +_sgs__SAMPR_USER_LOGON_HOURS_INFORMATION@8 +_pgs__SAMPR_USER_LOGON_HOURS_INFORMATION@8 +_ggs__SAMPR_USER_LOGON_HOURS_INFORMATION@12 +_fgs__SAMPR_USER_LOGON_HOURS_INFORMATION@4 +_gns__SAMPR_USER_INTERNAL1_INFORMATION@8 +_snu__SAMPR_USER_INFO_BUFFER@12 +_sgu__SAMPR_USER_INFO_BUFFER@12 +_pnu__SAMPR_USER_INFO_BUFFER@12 +_pgu__SAMPR_USER_INFO_BUFFER@12 +_gnu__SAMPR_USER_INFO_BUFFER@12 +_ggu__SAMPR_USER_INFO_BUFFER@16 +_sgs__SAMPR_DOMAIN_DISPLAY_USER@8 +_pgs__SAMPR_DOMAIN_DISPLAY_USER@8 +_fgs__SAMPR_DOMAIN_DISPLAY_USER@4 +_sgs__SAMPR_DOMAIN_DISPLAY_MACHINE@8 +_pgs__SAMPR_DOMAIN_DISPLAY_MACHINE@8 +_fgs__SAMPR_DOMAIN_DISPLAY_MACHINE@4 +_sgs__SAMPR_DOMAIN_DISPLAY_USER_BUFFER@8 +_pgs__SAMPR_DOMAIN_DISPLAY_USER_BUFFER@8 +_fgs__SAMPR_DOMAIN_DISPLAY_USER_BUFFER@4 +_sgs__SAMPR_DOMAIN_DISPLAY_MACHINE_BUFFER@8 +_pgs__SAMPR_DOMAIN_DISPLAY_MACHINE_BUFFER@8 +_fgs__SAMPR_DOMAIN_DISPLAY_MACHINE_BUFFER@4 +_snu__SAMPR_DISPLAY_INFO_BUFFER@12 +_sgu__SAMPR_DISPLAY_INFO_BUFFER@12 +_pnu__SAMPR_DISPLAY_INFO_BUFFER@12 +_pgu__SAMPR_DISPLAY_INFO_BUFFER@12 +_fgu__SAMPR_DISPLAY_INFO_BUFFER@8 +SamIInitialize@0 +SampInitialize@0 +SampLoadPasswordFilterDll@0 +SampEnableAuditPrivilege@0 +SampInitializeDomainDescriptors@4 +SampBuildSamProtection@40 +SamrSetSecurityObject@12 +SampValidatePassedSD@8 +SamrQuerySecurityObject@12 +SampAuditOnClose@4 +SamrConnect@12 +SamIConnect@16 +SamrShutdownSamServer@4 +SamrLookupDomainInSamServer@12 +SamrEnumerateDomainsInSamServer@20 +SampGetUnicodeStringField@12 +SamrDeleteUser@4 +SampIsUserAccountControlValid@8 +SamrSetInformationUser@12 +SamrChangePasswordUser@44 +SamrGetUserDomainPasswordInformation@8 +SampReplaceUserV1aFixed@8 +SampComputePasswordExpired@8 +SampStorePasswordExpired@8 +SampStoreUserPasswords@24 +SampReplaceUserMembership@12 +SampReplaceUserLogonHours@8 +SampRetrieveUserGroupAttribute@12 +SampAddGroupToUserMembership@16 +SampRemoveMembershipUser@12 +SampSetGroupAttributesOfUser@12 +SampDeleteUserKeys@4 +SampAddPasswordHistory@24 +SampCheckPasswordHistory@28 +SampAddDeltaTime@16 +SampChangeUserAccountName@12 +SampAcquireWriteLock@0 +SampFlushThread@4 +SampCommitChanges@0 +SampRefreshRegistry@0 +SampReleaseWriteLock@4 +SampCommitAndRetainWriteLock@0 +SampRetrieveStringFromRegistry@12 +SampPutStringToRegistry@12 +SampBuildDomainSubKeyName@8 +SampBuildAliasMembersKeyName@12 +SampValidateNewAccountName@4 +SampValidateAccountNameChange@8 +SampRetrieveAccountCounts@12 +SampAdjustAccountCount@8 +SampEnumerateAccountNamesCommon@28 +SampEnumerateAccountNames@28 +SampLookupAccountRid@20 +SampLookupAccountName@12 +SampIsAccountBuiltIn@4 +SampCreateFullSid@12 +SampCreateAccountSid@8 +SampNotifyNetlogonOfDelta@24 +SampDuplicateUnicodeString@8 +SampChangeAccountOperatorAccessToMember@8 +SampChangeAccountOperatorAccessToUser@8 +SamINotifyDelta@28 +SamISetAuditingInformation@4 +SampRtlWellKnownPrivilegeCheck@12 +SampWriteEventLog@32 +SampShutdownNotification@4 +CloseHandle@4 +CreateThread@24 +DbgPrint +ElfDeregisterEventSource@4 +ElfRegisterEventSourceW@12 +ElfReportEventW@48 +FormatMessageW@28 +GetLastError@0 +GetTimeZoneInformation@4 +NlLoadNetlogonDll@0 +I_NetNotifyDelta@40 +I_NetNotifyRole@4 +I_NetNotifyMachineAccount@20 +I_NetGetAnyDCName@8 +FreeLibrary@4 +GetProcAddress@8 +I_RpcMapWin32Status@4 +LoadLibraryA@4 +LoadLibraryW@4 +LocalFree@4 +LsaClose@4 +LsaFreeMemory@4 +LsaIAuditSamEvent@32 +LsaIFree_LSAPR_POLICY_INFORMATION@8 +LsaIOpenPolicyTrusted@4 +LsaIQueryInformationPolicyTrusted@8 +LsaOpenPolicy@16 +LsaQueryInformationPolicy@12 +LsarClose@4 +LsarQueryInformationPolicy@12 +MIDL_user_allocate@4 +MIDL_user_free@4 +MIDL_user_reallocate@8 +MIDL_user_size@4 +LocalAlloc@8 +LocalHandle@4 +LocalReAlloc@12 +LocalSize@4 +NDRSContextMarshall@12 +NDRSContextUnmarshall@8 +NDRcopy@12 +NtAccessCheckAndAuditAlarm@44 +NtAdjustPrivilegesToken@24 +NtClose@4 +NtCloseObjectAuditAlarm@12 +NtDelayExecution@8 +NtFlushKey@4 +NtOpenEvent@12 +NtOpenProcess@16 +NtOpenProcessToken@12 +NtOpenThread@16 +NtOpenThreadToken@16 +NtPrivilegeCheck@12 +NtQueryInformationToken@20 +NtQuerySystemTime@4 +NtQueryValueKey@24 +NtRestoreKey@12 +NtSetInformationToken@16 +RpcImpersonateClient@4 +RpcMgmtStopServerListening@4 +RpcRaiseException@4 +RpcRevertToSelf@0 +RpcpInitRpcServer@0 +RpcpAddInterface@8 +RpcpStartRpcServer@8 +RpcpDeleteInterface@4 +RpcpStopRpcServer@4 +EnterCriticalSection@4 +InitializeCriticalSection@4 +InitializeSecurityDescriptor@8 +LeaveCriticalSection@4 +RpcServerListen@12 +RpcServerRegisterIf@12 +RpcServerUnregisterIf@12 +RpcServerUseProtseqEpW@16 +RtlAbortRXact@4 +RtlAbsoluteToSelfRelativeSD@12 +RtlAcquireResourceExclusive@8 +RtlAddAccessAllowedAce@16 +RtlAddAce@20 +RtlAddActionToRXact@24 +RtlAddAttributeActionToRXact@32 +RtlAddAuditAccessAce@24 +RtlAdjustPrivilege@16 +RtlAllocateHeap@12 +RtlAppendUnicodeStringToString@8 +RtlAppendUnicodeToString@8 +RtlApplyRXactNoFlush@4 +RtlAreAllAccessesGranted@8 +RtlAreAnyAccessesGranted@8 +RtlAssert@16 +RtlCompareMemory@12 +RtlCompareUnicodeString@12 +RtlConvertSidToUnicodeString@12 +RtlConvertUiListToApiList@12 +RtlCopySid@12 +RtlCopyUnicodeString@8 +RtlCreateAcl@12 +RtlCreateSecurityDescriptor@8 +RtlEqualComputerName@8 +RtlEqualDomainName@8 +RtlEqualSid@8 +RtlEqualUnicodeString@12 +RtlExtendedIntegerMultiply@12 +RtlExtendedLargeIntegerDivide@16 +RtlFreeHeap@12 +RtlFreeUnicodeString@4 +RtlGetAce@12 +RtlGetDaclSecurityDescriptor@16 +RtlGetGroupSecurityDescriptor@12 +RtlGetNtProductType@4 +RtlGetOwnerSecurityDescriptor@12 +RtlGetSaclSecurityDescriptor@16 +RtlInitUnicodeString@8 +RtlInitializeRXact@12 +RtlInitializeResource@4 +RtlInitializeSid@12 +RtlIntegerToUnicodeString@12 +RtlLengthRequiredSid@4 +RtlLengthSecurityDescriptor@4 +RtlLengthSid@4 +RtlMakeSelfRelativeSD@12 +RtlMapGenericMask@8 +RtlRandom@4 +RtlReleaseResource@4 +RtlSetDaclSecurityDescriptor@16 +RtlSetGroupSecurityDescriptor@12 +RtlSetOwnerSecurityDescriptor@12 +RtlSetSaclSecurityDescriptor@16 +RtlSetSecurityObject@20 +RtlStartRXact@4 +RtlSubAuthorityCountSid@4 +RtlSubAuthoritySid@8 +RtlTimeToTimeFields@8 +RtlValidAcl@4 +RtlValidSid@4 +RtlpNtCreateKey@24 +RtlpNtEnumerateSubKey@16 +RtlpNtOpenKey@16 +RtlpNtQueryValueKey@20 +RtlpNtSetValueKey@16 +SetConsoleCtrlHandler@8 +SetProcessShutdownParameters@8 +SetSecurityDescriptorDacl@16 +SystemFunction006@8 +SystemFunction007@8 +SystemFunction013@12 +SystemFunction015@12 +SystemFunction021@12 +SystemFunction023@12 +SystemFunction024@12 +SystemFunction025@12 +SystemFunction026@12 +SystemFunction027@12 +SystemFunction029@8 +SystemFunction030@8 +SystemFunction031@8 +char_array_from_ndr@16 +char_from_ndr@8 +data_from_ndr@16 +long_array_from_ndr@16 +long_from_ndr@8 +long_from_ndr_temp@12 +midl_allocate@4 +short_from_ndr@8 +short_from_ndr_temp@12 +
\ No newline at end of file diff --git a/private/newsam2/server/exe/seed.c b/private/newsam2/server/exe/seed.c new file mode 100644 index 000000000..5d6616237 --- /dev/null +++ b/private/newsam2/server/exe/seed.c @@ -0,0 +1,882 @@ +/*++ + +Copyright (C) 1996 Microsoft Corporation + +Module Name: + + seed.c + +Abstract: + + Contains all the seeding tables necessary for + initial seeding of the DS + +Author: + MURLIS + +Revision History + + 5-30-96 Murlis Created + 6-16-96 Murlis Added Security Descriptor to objects + Added UnSeedDs call. + +--*/ + +#include <samsrvp.h> +#include <mappings.h> +#include <dslayer.h> + + +#define ARRAY_COUNT(x) (sizeof(x)/sizeof(x[0])) + +///////////////////////////////////////////////////////////////////////////////// +// // +// Structure For Holding the Seeding Information // +// // +///////////////////////////////////////////////////////////////////////////////// + +typedef struct +{ + WCHAR * StringName; + ULONG NameLength; + PSID DomainSid; + SAMP_OBJECT_TYPE SamObjectType; + ATTRBLOCK * AttributesToSet; +} ObjectDefinition; + + +///////////////////////////////////////////////////////////////////////////////// +// // +// Define Some Macros that make the security descriptor stuff more readable // +// // +// // +///////////////////////////////////////////////////////////////////////////////// + + +#define SECURITY_DESCRIPTOR_PLACEHOLDER_LEN 0 +#define SECURITY_DESCRIPTOR_PLACE_HOLDER_VAL NULL +#define SECURITY_DESCRIPTOR_LEN_FIELD(x) (x->pAttr[x->attrCount-1].AttrVal.pAVal[0].valLen) +#define SECURITY_DESCRIPTOR_VAL_FIELD(x) (x->pAttr[x->attrCount-1].AttrVal.pAVal[0].pVal) + + + + +////////////////////////////////////////////////////////////////////////////////////////////////////////// +// // +// Initial DS Setup Seeding Tables // +// These define the SAM Objects and their attributes // +// that are initialy creaed in the DS. // +// // +// IMPORTANT NOTE the Last Attribute should be the security descriptor // +// // +////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Declare all the object Names + +WCHAR AccountDomainName[] = L"/cn=Account"; +WCHAR AdministratorName[] = L"/cn=Account/cn=Administrator"; +WCHAR GuestName[] = L"/cn=Account/cn=Guest"; +WCHAR TestGroupName[] = L"/cn=Account/cn=TestGroup"; +WCHAR TestAliasName[] = L"/cn=Account/cn=TestAlias"; +WCHAR BuiltinDomainName[] = L"/cn=Builtin"; +WCHAR AdminAliasName[] = L"/cn=Builtin/cn=Administrators"; +WCHAR BackupAliasName[] = L"/cn=Builtin/cn=Backup Operators"; +WCHAR PowerAliasName[] = L"/cn=Builtin/cn=Power Users"; +WCHAR UserAliasName[] = L"/cn=Builtin/cn=Users"; +WCHAR GuestAliasName[] = L"/cn=Builtin/cn=Guests"; +WCHAR ReplAliasName[] = L"/cn=Builtin/cn=Replicators"; + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// // +// ACCOUNT DOMAIN SEEDING // +// // +///////////////////////////////////////////////////////////////////////////////////////////////////////// + + +// Domain Fixed Attributes for Account Domain + +SAMP_V1_0A_FIXED_LENGTH_DOMAIN AccountDomainFixedAttributes = +{ + SAMP_DS_REVISION, // Revision + 0, // BUG: Need to set current Creation Time + {0,0}, // Modified count + { 0, - 6L * 7L * 24L * 60L / 7L }, // Max Password Age = 6 weeks + {0,0}, // BUG: Should set to {SampImmediatelyDeltaTime},// Min Password Age + {0,0}, // BUG: Should Set to {SampNeverDeltaTime}, // Force Logoff + {0xCF1DCC00, 0xFFFFFFFB}, // Lockout Durations 30 min + {0xCF1DCC00, 0xFFFFFFFB}, // Lockout Observation wiindow 30 min + {0,0}, // Modified count at last promotion + 1000, // Next Rid + 0L, // Password Properties + 0, // Min Password Length + 0, // Password History Length + 0, // Lockout Threshold ( disabled ) + DomainServerEnabled, // Server State + 0, // BUG: Should set to SampServerRole, // Server Role + TRUE // UAS Compatibility required +}; + +UCHAR AccountDomainSid[] = +{ + 1, // Revision + 4, // Sub Authority Count + 1,2,3,4,5,6, // Identifier Authority + 1,0,0,0, // Sub Authorities + 2,0,0,0, + 3,0,0,0, + 4,0,0,0, +}; + + +// Attribte Block declarations for Account Domains + +ULONG AccountUserCount = 2; +ULONG AccountGroupCount = 0; +ULONG AccountAliasCount = 0; +ATTRVAL AccountDomainVals[] = +{ + {sizeof(AccountDomainFixedAttributes.Revision), (UCHAR *)&(AccountDomainFixedAttributes.Revision)}, + {sizeof(AccountDomainFixedAttributes.CreationTime), (UCHAR *)&(AccountDomainFixedAttributes.CreationTime)}, + {sizeof(AccountDomainFixedAttributes.ModifiedCount), (UCHAR *)&(AccountDomainFixedAttributes.ModifiedCount)}, + {sizeof(AccountDomainFixedAttributes.MaxPasswordAge), (UCHAR *)&(AccountDomainFixedAttributes.MaxPasswordAge)}, + {sizeof(AccountDomainFixedAttributes.MinPasswordAge), (UCHAR *)&(AccountDomainFixedAttributes.MinPasswordAge)}, + {sizeof(AccountDomainFixedAttributes.ForceLogoff), (UCHAR *)&(AccountDomainFixedAttributes.ForceLogoff)}, + {sizeof(AccountDomainFixedAttributes.LockoutDuration), (UCHAR *)&(AccountDomainFixedAttributes.LockoutDuration)}, + // *BUG Mapping Not defined * {sizeof(AccountDomainFixedAttributes.LockoutObservationWindow), (UCHAR *)&(AccountDomainFixedAttributes.LockoutObservationWindow)}, + {sizeof(AccountDomainFixedAttributes.ModifiedCountAtLastPromotion), (UCHAR *)&(AccountDomainFixedAttributes.ModifiedCountAtLastPromotion)}, + {sizeof(AccountDomainFixedAttributes.NextRid),(UCHAR *)&(AccountDomainFixedAttributes.NextRid)}, + {sizeof(AccountDomainFixedAttributes.PasswordProperties), (UCHAR *)&(AccountDomainFixedAttributes.PasswordProperties)}, + //* BUG Incorect length, should be 2 in schema {sizeof(AccountDomainFixedAttributes.PasswordHistoryLength), (UCHAR *)&(AccountDomainFixedAttributes.PasswordHistoryLength)}, + //* BUG Incorrect length should be 2 in schema {sizeof(AccountDomainFixedAttributes.LockoutThreshold), (UCHAR *)&(AccountDomainFixedAttributes.LockoutThreshold)}, + {sizeof(AccountDomainFixedAttributes.ServerState), (UCHAR *)&(AccountDomainFixedAttributes.ServerState)}, + {sizeof(AccountDomainFixedAttributes.ServerRole), (UCHAR *)&(AccountDomainFixedAttributes.ServerRole)}, + {sizeof(ULONG),(UCHAR *) & AccountUserCount}, + {sizeof(ULONG),(UCHAR *) & AccountGroupCount}, + {sizeof(ULONG),(UCHAR *) & AccountAliasCount}, + {sizeof(AccountDomainSid), AccountDomainSid}, + //* BUG Incorrect length in schema * {sizeof(AccountDomainFixedAttributes.UasCompatibilityRequired), (UCHAR *)&(AccountDomainFixedAttributes.UasCompatibilityRequired)} + {SECURITY_DESCRIPTOR_PLACEHOLDER_LEN, SECURITY_DESCRIPTOR_PLACE_HOLDER_VAL}, + +}; + +ATTR AccountDomainAttr [] = +{ + {SAMP_FIXED_DOMAIN_REVISION_LEVEL, {1,&AccountDomainVals[0]}}, + {SAMP_FIXED_DOMAIN_CREATION_TIME, {1,&AccountDomainVals[1]}}, + {SAMP_FIXED_DOMAIN_MODIFIED_COUNT, {1,&AccountDomainVals[2]}}, + {SAMP_FIXED_DOMAIN_MAX_PASSWORD_AGE, {1,&AccountDomainVals[3]}}, + {SAMP_FIXED_DOMAIN_MIN_PASSWORD_AGE, {1,&AccountDomainVals[4]}}, + {SAMP_FIXED_DOMAIN_FORCE_LOGOFF, {1,&AccountDomainVals[5]}}, + {SAMP_FIXED_DOMAIN_LOCKOUT_DURATION, {1,&AccountDomainVals[6]}}, + {SAMP_FIXED_DOMAIN_MODCOUNT_LAST_PROMOTION, {1,&AccountDomainVals[7]}}, + {SAMP_FIXED_DOMAIN_NEXT_RID, {1,&AccountDomainVals[8]}}, + {SAMP_FIXED_DOMAIN_PWD_PROPERTIES, {1,&AccountDomainVals[9]}}, + //* BUG Missed out in the blob above* {SAMP_FIXED_DOMAIN_MIN_PASSWORD_LENGTH, {1,&AccountDomainVals[10]}}, + //* BUG Incorrect length in schema* {SAMP_FIXED_DOMAIN_PASSWORD_HISTORY_LENGTH, {1,&AccountDomainVals[11]}}, + //* BUG Incorrect length in schema* {SAMP_FIXED_DOMAIN_LOCKOUT_THRESHOLD, {1,&AccountDomainVals[10]}}, + {SAMP_FIXED_DOMAIN_SERVER_STATE, {1,&AccountDomainVals[10]}}, + {SAMP_FIXED_DOMAIN_SERVER_ROLE, {1,&AccountDomainVals[11]}}, + // *BUG Incorrect length in schema * {SAMP_FIXED_DOMAIN_UAS_COMPAT_REQUIRED, {1,&AccountDomainVals[15]}} + {SAMP_DOMAIN_USERCOUNT, {1,&AccountDomainVals[12]}}, + {SAMP_DOMAIN_GROUPCOUNT, {1,&AccountDomainVals[13]}}, + {SAMP_DOMAIN_ALIASCOUNT, {1,&AccountDomainVals[14]}}, + {SAMP_DOMAIN_SID, {1,&AccountDomainVals[15]}}, + {SAMP_DOMAIN_SECURITY_DESCRIPTOR, {1,&AccountDomainVals[16]}}, +}; + +ATTRBLOCK AccountDomainAttrBlock = { ARRAY_COUNT(AccountDomainAttr), AccountDomainAttr}; + + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// // +// BUILTIN DOMAIN SEEDING // +// // +///////////////////////////////////////////////////////////////////////////////////////////////////////// + + // Domain Fixed Attributes for builtin Domain +SAMP_V1_0A_FIXED_LENGTH_DOMAIN BuiltInDomainFixedAttributes = +{ + SAMP_DS_REVISION, // Revision + 0, // Creation Time BUG: Need to set to current time + {0,0}, // Modified count + { 0, - 6L * 7L * 24L * 60L / 7L }, // Max Password Age = 6 weeks + {0,0}, // BUG: Should set to {SampImmediatelyDeltaTime},// Min Password Age + {0,0}, // BUG: Should set to {SampNeverDeltaTime}, // Force Logoff + {0xCF1DCC00, 0xFFFFFFFB}, // Lockout Durations 30 min + {0xCF1DCC00, 0xFFFFFFFB}, // Lockout Observation wiindow 30 min + {0,0}, // Modified count at last promotion + 1000, // Next Rid + 0L, // Password Properties + 0, // Min Password Length + 0, // Password History Length + 0, // Lockout Threshold ( disabled ) + DomainServerEnabled, // Server State + 0, // BUG: Should set to SampServerRole, // Server Role + TRUE // UAS Compatibility required +}; + +UCHAR BuiltinDomainSid[] = +{ + 1, // Revision + 4, // Sub Authority Count + 1,2,3,4,5,6, // Identifier Authority + 11,0,0,0, // Sub Authorities + 12,0,0,0, + 13,0,0,0, + 14,0,0,0, +}; + +// Attribute Type Block for Builtin and Account Domains + +ATTRVAL BuiltInDomainVals[] = +{ + {sizeof(BuiltInDomainFixedAttributes.Revision), (UCHAR *)&(BuiltInDomainFixedAttributes.Revision)}, + {sizeof(BuiltInDomainFixedAttributes.CreationTime), (UCHAR *)&(BuiltInDomainFixedAttributes.CreationTime)}, + {sizeof(BuiltInDomainFixedAttributes.ModifiedCount), (UCHAR *)&(BuiltInDomainFixedAttributes.ModifiedCount)}, + {sizeof(BuiltInDomainFixedAttributes.MaxPasswordAge), (UCHAR *)&(BuiltInDomainFixedAttributes.MaxPasswordAge)}, + {sizeof(BuiltInDomainFixedAttributes.MinPasswordAge), (UCHAR *)&(BuiltInDomainFixedAttributes.MinPasswordAge)}, + {sizeof(BuiltInDomainFixedAttributes.ForceLogoff), (UCHAR *)&(BuiltInDomainFixedAttributes.ForceLogoff)}, + {sizeof(BuiltInDomainFixedAttributes.LockoutDuration), (UCHAR *)&(BuiltInDomainFixedAttributes.LockoutDuration)}, + //BUG Mapping not defined {sizeof(BuiltInDomainFixedAttributes.LockoutObservationWindow), (UCHAR *)&(BuiltInDomainFixedAttributes.LockoutObservationWindow)}, + {sizeof(BuiltInDomainFixedAttributes.ModifiedCountAtLastPromotion), (UCHAR *)&(BuiltInDomainFixedAttributes.ModifiedCountAtLastPromotion)}, + {sizeof(BuiltInDomainFixedAttributes.NextRid),(UCHAR *)&(BuiltInDomainFixedAttributes.NextRid)}, + {sizeof(BuiltInDomainFixedAttributes.PasswordProperties), (UCHAR *)&(BuiltInDomainFixedAttributes.PasswordProperties)}, + //BUG incorrect length in schema {sizeof(BuiltInDomainFixedAttributes.PasswordHistoryLength), (UCHAR *)&(BuiltInDomainFixedAttributes.PasswordHistoryLength)}, + //BUG Incorrect length {sizeof(BuiltInDomainFixedAttributes.LockoutThreshold), (UCHAR *)&(BuiltInDomainFixedAttributes.LockoutThreshold)}, + {sizeof(BuiltInDomainFixedAttributes.ServerState), (UCHAR *)&(BuiltInDomainFixedAttributes.ServerState)}, + {sizeof(BuiltInDomainFixedAttributes.ServerRole), (UCHAR *)&(BuiltInDomainFixedAttributes.ServerRole)}, + {sizeof(BuiltinDomainSid),BuiltinDomainSid}, + //BUG Incorrect length in schema {sizeof(BuiltInDomainFixedAttributes.UasCompatibilityRequired), (UCHAR *)&(BuiltInDomainFixedAttributes.UasCompatibilityRequired)} + {SECURITY_DESCRIPTOR_PLACEHOLDER_LEN, SECURITY_DESCRIPTOR_PLACE_HOLDER_VAL}, +}; + +ATTR BuiltInDomainAttr [] = +{ + {SAMP_FIXED_DOMAIN_REVISION_LEVEL, {1,&BuiltInDomainVals[0]}}, + {SAMP_FIXED_DOMAIN_CREATION_TIME, {1,&BuiltInDomainVals[1]}}, + {SAMP_FIXED_DOMAIN_MODIFIED_COUNT, {1,&BuiltInDomainVals[2]}}, + {SAMP_FIXED_DOMAIN_MAX_PASSWORD_AGE, {1,&BuiltInDomainVals[3]}}, + {SAMP_FIXED_DOMAIN_MIN_PASSWORD_AGE, {1,&BuiltInDomainVals[4]}}, + {SAMP_FIXED_DOMAIN_FORCE_LOGOFF, {1,&BuiltInDomainVals[5]}}, + {SAMP_FIXED_DOMAIN_LOCKOUT_DURATION, {1,&BuiltInDomainVals[6]}}, + {SAMP_FIXED_DOMAIN_MODCOUNT_LAST_PROMOTION, {1,&BuiltInDomainVals[7]}}, + {SAMP_FIXED_DOMAIN_NEXT_RID, {1,&BuiltInDomainVals[8]}}, + {SAMP_FIXED_DOMAIN_PWD_PROPERTIES, {1,&BuiltInDomainVals[9]}}, + //BUG Missed out above {SAMP_FIXED_DOMAIN_MIN_PASSWORD_LENGTH, {1,&BuiltInDomainVals[10]}}, + //BUG Incorrect length in schema {SAMP_FIXED_DOMAIN_PASSWORD_HISTORY_LENGTH, {1,&BuiltInDomainVals[11]}}, + //BUG Incorrect length in schema {SAMP_FIXED_DOMAIN_LOCKOUT_THRESHOLD, {1,&BuiltInDomainVals[10]}}, + {SAMP_FIXED_DOMAIN_SERVER_STATE, {1,&BuiltInDomainVals[10]}}, + {SAMP_FIXED_DOMAIN_SERVER_ROLE, {1,&BuiltInDomainVals[11]}}, + {SAMP_DOMAIN_SID, {1,&BuiltInDomainVals[12]}}, + //BUG Incorrect length in schema {SAMP_FIXED_DOMAIN_UAS_COMPAT_REQUIRED, {1,&BuiltInDomainVals[15]}} + {SAMP_DOMAIN_SECURITY_DESCRIPTOR, {1,&BuiltInDomainVals[13]}}, +}; + +ATTRBLOCK BuiltInDomainAttrBlock = { ARRAY_COUNT(BuiltInDomainAttr), BuiltInDomainAttr}; + + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// // +// GUEST USER SEEDING // +// // +///////////////////////////////////////////////////////////////////////////////////////////////////////// + +// User Fixed Attributes for Guest User +SAMP_V1_0A_FIXED_LENGTH_USER GuestFixedAttributes = +{ + SAMP_DS_REVISION, // Revision + 0, // Unused 1 + {0,0}, // BUG: Should set to SampHasNeverTime, // Last Logon + {0,0}, // BUG: Should set to SampHasNeverTime, // Last Logoff + {0,0}, // BUG: Should set to SampHasNeverTime, // Password Set Last + {0,0}, // BUG: Should set to SampWillNeverTime, // Account Expires + {0,0}, // BUG: Should set to SampHasNeverTime, // Last Bad Password Time + 1, // Rid is set to 1 + DOMAIN_GROUP_RID_GUESTS, // Primary group Id + 0, // User Control + 0, // Country code + 0, // Code Page + 0, // Bad Password Count + 0, // Logon Count + 0, // Admin Count + 0, // Unused 2 + 0 // Operator Count +}; + +WCHAR GuestUserAccountName[] = L"Guest"; + +// Attrblock declaration for guest users +ATTRVAL GuestUserVals[] = +{ + {sizeof(GuestFixedAttributes.Revision) , (UCHAR *)&(GuestFixedAttributes.Revision)}, + {sizeof(GuestFixedAttributes.LastLogon), (UCHAR *)&(GuestFixedAttributes.LastLogon)}, + {sizeof(GuestFixedAttributes.LastLogoff), (UCHAR *)&(GuestFixedAttributes.LastLogoff)}, + {sizeof(GuestFixedAttributes.PasswordLastSet), (UCHAR *)&(GuestFixedAttributes.PasswordLastSet)}, + {sizeof(GuestFixedAttributes.AccountExpires), (UCHAR *)&(GuestFixedAttributes.AccountExpires)}, + {sizeof(GuestFixedAttributes.LastBadPasswordTime), (UCHAR *)&(GuestFixedAttributes.LastBadPasswordTime)}, + {sizeof(GuestFixedAttributes.UserId), (UCHAR *)&(GuestFixedAttributes.UserId)}, + {sizeof(GuestFixedAttributes.PrimaryGroupId), (UCHAR *)&(GuestFixedAttributes.PrimaryGroupId)}, + {sizeof(GuestFixedAttributes.UserAccountControl), (UCHAR *)&(GuestFixedAttributes.UserAccountControl)}, + // BUG: {sizeof(GuestFixedAttributes.CountryCode), (UCHAR *)&(GuestFixedAttributes.CountryCode)}, + // BUG: {sizeof(GuestFixedAttributes.CodePage), (UCHAR *)&(GuestFixedAttributes.CodePage)}, + // BUG: {sizeof(GuestFixedAttributes.BadPasswordCount), (UCHAR *)&(GuestFixedAttributes.BadPasswordCount)}, + // BUG: Mapping Not defined {sizeof(GuestFixedAttributes.LogonCount), (UCHAR *)&(GuestFixedAttributes.LogonCount)), + // BUG: Incorrect length in schema {sizeof(GuestFixedAttributes.AdminCount), (UCHAR *)&(GuestFixedAttributes.AdminCount)}, + // BUG: Incorrect length in schema {sizeof(GuestFixedAttributes.OperatorCount), (UCHAR *)&(GuestFixedAttributes.OperatorCount)} + {sizeof(GuestUserAccountName), (UCHAR *)GuestUserAccountName}, + {SECURITY_DESCRIPTOR_PLACEHOLDER_LEN, SECURITY_DESCRIPTOR_PLACE_HOLDER_VAL}, +}; + +ATTR GuestUserAttr [] = +{ + {SAMP_FIXED_USER_REVISION_LEVEL, {1,&GuestUserVals[0]}}, + {SAMP_FIXED_USER_LAST_LOGON, {1,&GuestUserVals[1]}}, + {SAMP_FIXED_USER_LAST_LOGOFF, {1,&GuestUserVals[2]}}, + {SAMP_FIXED_USER_PWD_LAST_SET, {1,&GuestUserVals[3]}}, + {SAMP_FIXED_USER_ACCOUNT_EXPIRES, {1,&GuestUserVals[4]}}, + {SAMP_FIXED_USER_LAST_BAD_PASSWORD_TIME, {1,&GuestUserVals[5]}}, + {SAMP_FIXED_USER_USERID, {1,&GuestUserVals[6]}}, + {SAMP_FIXED_USER_PRIMARY_GROUP_ID, {1,&GuestUserVals[7]}}, + {SAMP_FIXED_USER_ACCOUNT_CONTROL, {1,&GuestUserVals[8]}}, + //{SAMP_FIXED_USER_COUNTRY_CODE, {1,&GuestUserVals[9]}}, + //{SAMP_FIXED_USER_CODEPAGE, {1,&GuestUserVals[10]}}, + //{SAMP_FIXED_USER_BAD_PWD_COUNT, {1,&GuestUserVals[11]}} + //{SAMP_FIXED_USER_ADMIN_COUNT, {1,&GuestUserVals[12]}}, + //{SAMP_FIXED_USER_OPERATOR_COUNT, {1,&GuestUserVals[13]}}, + {SAMP_USER_ACCOUNT_NAME, {1,&GuestUserVals[9]}}, + {SAMP_USER_SECURITY_DESCRIPTOR, {1,&GuestUserVals[10]}}, +}; + +ATTRBLOCK GuestUserAttrBlock = { ARRAY_COUNT(GuestUserAttr), GuestUserAttr}; + + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +// // +// ADMIN USER SEEDING // +// // +///////////////////////////////////////////////////////////////////////////////////////////////////////// + + +WCHAR AdminUserAccountName[] = L"Administrator"; + +// User Fixed Attributes for Administrator +SAMP_V1_0A_FIXED_LENGTH_USER AdminFixedAttributes = +{ + SAMP_DS_REVISION, // Revision + 0, // Unused 1 + {0,0}, // BUG: Need to set to SampHasNeverTime, // Last Logon + {0,0}, // BUG: Need to set to SampHasNeverTime, // Last Logoff + {0,0}, // BUG: Need to set to SampHasNeverTime, // Password Set Last + {0,0}, // BUG: Need to set to SampWillNeverTime, // Account Expires + {0,0}, // BUG: Need to set to SampHasNeverTime, // Last Bad Password Time + 2, // Rid is set to 2 + DOMAIN_GROUP_RID_USERS, // Primary group Id + 0, // User Control + 0, // Country code + 0, // Code Page + 0, // Bad Password Count + 0, // Logon Count + 1, // Admin Count + 0, // Unused 2 + 0 // Operator Count +}; + +// Attrblock declaration for Admin users +ATTRVAL AdminUserVals[] = +{ + {sizeof(AdminFixedAttributes.Revision) , (UCHAR *)&(AdminFixedAttributes.Revision)}, + {sizeof(AdminFixedAttributes.LastLogon), (UCHAR *)&(AdminFixedAttributes.LastLogon)}, + {sizeof(AdminFixedAttributes.LastLogoff), (UCHAR *)&(AdminFixedAttributes.LastLogoff)}, + {sizeof(AdminFixedAttributes.PasswordLastSet), (UCHAR *)&(AdminFixedAttributes.PasswordLastSet)}, + {sizeof(AdminFixedAttributes.AccountExpires), (UCHAR *)&(AdminFixedAttributes.AccountExpires)}, + {sizeof(AdminFixedAttributes.LastBadPasswordTime), (UCHAR *)&(AdminFixedAttributes.LastBadPasswordTime)}, + {sizeof(AdminFixedAttributes.UserId), (UCHAR *)&(AdminFixedAttributes.UserId)}, + {sizeof(AdminFixedAttributes.PrimaryGroupId), (UCHAR *)&(AdminFixedAttributes.PrimaryGroupId)}, + {sizeof(AdminFixedAttributes.UserAccountControl), (UCHAR *)&(AdminFixedAttributes.UserAccountControl)}, + // BUG: Incorrect size in schema {sizeof(AdminFixedAttributes.CountryCode), (UCHAR *)&(AdminFixedAttributes.CountryCode)}, + // BUG: Incorrect size in schema {sizeof(AdminFixedAttributes.CodePage), (UCHAR *)&(AdminFixedAttributes.CodePage)}, + // BUG: Incorrect size in schema {sizeof(AdminFixedAttributes.BadPasswordCount), (UCHAR *)&(AdminFixedAttributes.BadPasswordCount)}, + // BUG: mapping not defined {sizeof(AdminFixedAttributes.LogonCount), (UCHAR *)&(AdminFixedAttributes.LogonCount)), + // BUG: Incorrect length in schema {sizeof(AdminFixedAttributes.AdminCount), (UCHAR *)&(AdminFixedAttributes.AdminCount)}, + // BUG: Incorrect length in schema {sizeof(AdminFixedAttributes.OperatorCount), (UCHAR *)&(AdminFixedAttributes.OperatorCount)} + {sizeof(AdminUserAccountName), (UCHAR *)AdminUserAccountName}, + {SECURITY_DESCRIPTOR_PLACEHOLDER_LEN, SECURITY_DESCRIPTOR_PLACE_HOLDER_VAL}, +}; + +ATTR AdminUserAttr [] = +{ + {SAMP_FIXED_USER_REVISION_LEVEL, {1,&AdminUserVals[0]}}, + {SAMP_FIXED_USER_LAST_LOGON, {1,&AdminUserVals[1]}}, + {SAMP_FIXED_USER_LAST_LOGOFF, {1,&AdminUserVals[2]}}, + {SAMP_FIXED_USER_PWD_LAST_SET, {1,&AdminUserVals[3]}}, + {SAMP_FIXED_USER_ACCOUNT_EXPIRES, {1,&AdminUserVals[4]}}, + {SAMP_FIXED_USER_LAST_BAD_PASSWORD_TIME, {1,&AdminUserVals[5]}}, + {SAMP_FIXED_USER_USERID, {1,&AdminUserVals[6]}}, + {SAMP_FIXED_USER_PRIMARY_GROUP_ID, {1,&AdminUserVals[7]}}, + {SAMP_FIXED_USER_ACCOUNT_CONTROL, {1,&AdminUserVals[8]}}, + //BUG: Incorrect size in schema {SAMP_FIXED_USER_COUNTRY_CODE, {1,&AdminUserVals[9]}}, + //BUG: Incorrect size in schema {SAMP_FIXED_USER_CODEPAGE, {1,&AdminUserVals[10]}}, + //BUG: Incorrect size in schema {SAMP_FIXED_USER_BAD_PWD_COUNT, {1,&AdminUserVals[11]}} + //BUG: Incorrect size in schema {SAMP_FIXED_USER_ADMIN_COUNT, {1,&AdminUserVals[12]}}, + //BUG: Incorrect size in schema {SAMP_FIXED_USER_OPERATOR_COUNT, {1,&AdminUserVals[13]}}, + {SAMP_USER_ACCOUNT_NAME, {1,&AdminUserVals[9]}}, + {SAMP_USER_SECURITY_DESCRIPTOR, {1,&AdminUserVals[10]}}, +}; + +ATTRBLOCK AdminUserAttrBlock = { ARRAY_COUNT(AdminUserAttr), AdminUserAttr}; + + +///////////////////////////////////////////////////////////////////////////////////////// +// // +// Test Group Seeding // +// // +///////////////////////////////////////////////////////////////////////////////////////// + +WCHAR TestGroupAccountName[] = L"TestGroup"; +ULONG TestGroupRid = 3; +ULONG TestGroupAttributes = 0; +ULONG TestGroupAdminCount = 0; +ULONG TestGroupOperatorCount = 0; +ULONG TestGroupUnused1 = 0; + +ATTRVAL TestGroupVals[]= +{ + {sizeof(TestGroupAccountName), (UCHAR *) TestGroupAccountName}, + {sizeof(ULONG),(UCHAR *) &TestGroupRid}, + {sizeof(ULONG),(UCHAR *) &TestGroupAttributes}, + {sizeof(ULONG),(UCHAR *) &TestGroupUnused1}, + {sizeof(ULONG),(UCHAR *) &TestGroupAdminCount}, + {sizeof(ULONG),(UCHAR *) &TestGroupOperatorCount}, + {SECURITY_DESCRIPTOR_PLACEHOLDER_LEN, SECURITY_DESCRIPTOR_PLACE_HOLDER_VAL} +}; + + + +ATTR TestGroupAttr[]= +{ + {SAMP_GROUP_NAME, {1,&TestGroupVals[0]}}, + {SAMP_FIXED_GROUP_RID, {1,&TestGroupVals[1]}}, + {SAMP_FIXED_GROUP_ATTRIBUTES, {1,&TestGroupVals[2]}}, + {SAMP_FIXED_GROUP_UNUSED1, {1,&TestGroupVals[3]}}, + {SAMP_FIXED_GROUP_ADMIN_COUNT, {1,&TestGroupVals[4]}}, + {SAMP_FIXED_GROUP_OPERATOR_COUNT, {1,&TestGroupVals[5]}}, + {SAMP_GROUP_SECURITY_DESCRIPTOR, {1,&TestGroupVals[6]}} +}; + +ATTRBLOCK TestGroupAttrBlock = { ARRAY_COUNT(TestGroupAttr),TestGroupAttr}; + +///////////////////////////////////////////////////////////////////////////////////////// +// // +// Test Alias Seeding // +// // +///////////////////////////////////////////////////////////////////////////////////////// + +WCHAR TestAliasAccountName[] = L"TestAlias"; +ULONG TestAliasRid = 4; + +ATTRVAL TestAliasVals[]= +{ + {sizeof(TestAliasAccountName), (UCHAR *) TestAliasAccountName}, + {sizeof(ULONG),(UCHAR *) &TestAliasRid}, + {SECURITY_DESCRIPTOR_PLACEHOLDER_LEN, SECURITY_DESCRIPTOR_PLACE_HOLDER_VAL} +}; + + +ATTR TestAliasAttr[]= +{ + {SAMP_ALIAS_NAME, {1,&TestAliasVals[0]}}, + {SAMP_FIXED_ALIAS_RID, {1,&TestAliasVals[1]}}, + {SAMP_ALIAS_SECURITY_DESCRIPTOR, {1,&TestAliasVals[2]}} +}; + +ATTRBLOCK TestAliasAttrBlock = { ARRAY_COUNT(TestAliasAttr),TestAliasAttr}; + + +/////////////////////////////////////////////////////////////////////////////////////////////// +// // +// Seeding Object Table -- This lists all the objects and their attributes // +// that need to be added // +// // +/////////////////////////////////////////////////////////////////////////////////////////////// + + +ObjectDefinition SeedObjectTable[] = +{ + {AccountDomainName, sizeof(AccountDomainName), (PSID) AccountDomainSid, SampDomainObjectType, &AccountDomainAttrBlock}, + {AdministratorName, sizeof(AdministratorName), (PSID) AccountDomainSid, SampUserObjectType, &AdminUserAttrBlock}, + {GuestName, sizeof(GuestName), (PSID) AccountDomainSid, SampUserObjectType, &GuestUserAttrBlock}, + {BuiltinDomainName, sizeof(BuiltinDomainName), (PSID) BuiltinDomainSid, SampDomainObjectType, &BuiltInDomainAttrBlock}, + + {TestGroupName, sizeof(TestGroupName), (PSID) AccountDomainSid, SampGroupObjectType, &TestGroupAttrBlock}, + {TestAliasName, sizeof(TestAliasName), (PSID) AccountDomainSid, SampAliasObjectType, &TestAliasAttrBlock}, + // BUG: Commented out for the time being , till exact attributes to set are decided + + //{AdminAliasName, sizeof(AdminAliasName), (PSID) BuiltinDomainSid, SampAliasObjectType, NULL}, + //{BackupAliasName, sizeof(BackupAliasName), (PSID) BuiltinDomainSid, SampAliasObjectType, NULL}, + //{PowerAliasName, sizeof(PowerAliasName), (PSID) BuiltinDomainSid, SampAliasObjectType, NULL}, + //{UserAliasName, sizeof(UserAliasName), (PSID) BuiltinDomainSid, SampAliasObjectType, NULL}, + //{GuestAliasName, sizeof(GuestAliasName), (PSID) BuiltinDomainSid, SampAliasObjectType, NULL}, + //{ReplAliasName, sizeof(ReplAliasName), (PSID) BuiltinDomainSid, SampAliasObjectType, NULL} + + }; + + + + + +///////////////////////////////////////////////////////////////////////////////////////////////// +// // +// // +// Function Prototypes for locallly used Functions // +// // +// // +///////////////////////////////////////////////////////////////////////////////////////////////// + + + + +NTSTATUS +SampBuildSecurityDescriptor( + SAMP_OBJECT_TYPE SamObjectType, + ULONG *Size, + PSECURITY_DESCRIPTOR *pSD + ); + + + + +NTSTATUS +SampSeedDS( + WCHAR * NamePrefix, + ULONG NamePrefixLen + ) +/*++ +Routine Description: + + Creates the initial set of Objects in the DS. This includes the builtin and + account domains plus initial aliases and users + +Arguments: + + NamePrefix + NamePrefixLen + + Specifies the length and string to be prefixed to every object name in the + seeding table. Useful to create the database in a particular container + +Return Values: + + STATUS_SUCCESS upon successful completion + Any error codes form SampCreateObject +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + ULONG NumObjects = ARRAY_COUNT(SeedObjectTable); + ULONG Index; + ULONG DsNameBuffer[SAMP_MAX_DSNAME_SIZE]; + DSNAME *pDsName; + + + // Create the required Objects + for (Index =0; Index< NumObjects; Index++) + { + + // Initialize Object Name + SampInitializeDsName( + (DSNAME *) DsNameBuffer, + NamePrefix, + NamePrefixLen, + SeedObjectTable[Index].StringName, + SeedObjectTable[Index].NameLength + ); + pDsName = (DSNAME *) DsNameBuffer; + + // Build an appropriate Security Descriptor + + Status = SampBuildSecurityDescriptor( + SeedObjectTable[Index].SamObjectType, + &SECURITY_DESCRIPTOR_LEN_FIELD(SeedObjectTable[Index].AttributesToSet), + &SECURITY_DESCRIPTOR_VAL_FIELD(SeedObjectTable[Index].AttributesToSet) + ); + + // Create the object with the required attributes + Status = SampDsCreateObject( + pDsName, + SeedObjectTable[Index].SamObjectType, + SeedObjectTable[Index].AttributesToSet, + SeedObjectTable[Index].DomainSid + ); + // If error Bail out + if (Status != STATUS_SUCCESS) + goto Error; + } + +Error: + + return Status; +} + + +NTSTATUS +SampBuildSecurityDescriptor( + SAMP_OBJECT_TYPE SamObjectType, + ULONG *Size, + PSECURITY_DESCRIPTOR *pSD + ) +/*++ + + +Routine Description: + + This routine builds a self-relative security descriptor ready + to be applied to one of the SAM objects. Currently only a single + Standard Security descriptor is being built. But in future the ACLs + etc will match the object type specified. + + Arguments: + pSD the security descriptor in self relative form + Size The size of it in bytes + +Return Value: + + TBS. + +--*/ +{ + + + + SECURITY_DESCRIPTOR Absolute; + PSECURITY_DESCRIPTOR Relative; + PACL TmpAcl; + PACCESS_ALLOWED_ACE TmpAce; + PSID TmpSid; + ULONG Length, i; + PULONG RidLocation; + BOOLEAN IgnoreBoolean; + ACCESS_MASK MappedMask; + PSID AceSid[10]; // Don't expect more than 10 ACEs in any of these. + ACCESS_MASK AceMask[10]; + NTSTATUS Status; + GENERIC_MAPPING GenericMap = {USER_READ, + USER_WRITE, + USER_EXECUTE, + USER_ALL_ACCESS + }; + + + // + // The approach is to set up an absolute security descriptor that + // looks like what we want and then copy it to make a self-relative + // security descriptor. + // + + + Status = RtlCreateSecurityDescriptor( + &Absolute, + SECURITY_DESCRIPTOR_REVISION1 + ); + ASSERT( NT_SUCCESS(Status) ); + + + + // + // Owner + // + + Status = RtlSetOwnerSecurityDescriptor (&Absolute, SampAdministratorsAliasSid, FALSE ); + ASSERT(NT_SUCCESS(Status)); + + + + // + // Group + // + + Status = RtlSetGroupSecurityDescriptor (&Absolute, SampAdministratorsAliasSid, FALSE ); + ASSERT(NT_SUCCESS(Status)); + + + AceSid[0] = SampWorldSid; + AceMask[0] = (USER_ALL_ACCESS); + + AceSid[1] = SampAdministratorsAliasSid; + AceMask[1] = (USER_ALL_ACCESS); + + + + + // + // Discretionary ACL + // + // Calculate its length, + // Allocate it, + // Initialize it, + // Add each ACE + // Add it to the security descriptor + // + + Length = (ULONG)sizeof(ACL); + for (i=0; i<2; i++) { + + Length += RtlLengthSid( AceSid[i] ) + + (ULONG)sizeof(ACCESS_ALLOWED_ACE) - + (ULONG)sizeof(ULONG); //Subtract out SidStart field length + } + + TmpAcl = RtlAllocateHeap( RtlProcessHeap(), 0, Length ); + ASSERT(TmpAcl != NULL); + + + Status = RtlCreateAcl( TmpAcl, Length, ACL_REVISION2); + ASSERT( NT_SUCCESS(Status) ); + + for (i=0; i<2; i++) { + MappedMask = AceMask[i]; + RtlMapGenericMask( &MappedMask, &GenericMap ); + Status = RtlAddAccessAllowedAce ( + TmpAcl, + ACL_REVISION2, + MappedMask, + AceSid[i] + ); + ASSERT( NT_SUCCESS(Status) ); + } + + Status = RtlSetDaclSecurityDescriptor (&Absolute, TRUE, TmpAcl, FALSE ); + ASSERT(NT_SUCCESS(Status)); + + + + + // + // Sacl + // + + + Length = (ULONG)sizeof(ACL) + + RtlLengthSid( SampWorldSid ) + + (ULONG)sizeof(SYSTEM_AUDIT_ACE) - + (ULONG)sizeof(ULONG); //Subtract out SidStart field length + TmpAcl = RtlAllocateHeap( RtlProcessHeap(), 0, Length ); + ASSERT(TmpAcl != NULL); + + Status = RtlCreateAcl( TmpAcl, Length, ACL_REVISION2); + ASSERT( NT_SUCCESS(Status) ); + + Status = RtlAddAuditAccessAce ( + TmpAcl, + ACL_REVISION2, + (GenericMap.GenericWrite | DELETE | WRITE_DAC | ACCESS_SYSTEM_SECURITY) & ~READ_CONTROL, + SampWorldSid, + TRUE, //AuditSuccess, + TRUE //AuditFailure + ); + ASSERT( NT_SUCCESS(Status) ); + + Status = RtlSetSaclSecurityDescriptor (&Absolute, TRUE, TmpAcl, FALSE ); + ASSERT(NT_SUCCESS(Status)); + + + + + + + // + // Convert the Security Descriptor to Self-Relative + // + // Get the length needed + // Allocate that much memory + // Copy it + // Free the generated absolute ACLs + // + + Length = 0; + Status = RtlAbsoluteToSelfRelativeSD( &Absolute, NULL, &Length ); + ASSERT(Status == STATUS_BUFFER_TOO_SMALL); + + Relative = RtlAllocateHeap( RtlProcessHeap(), 0, Length ); + ASSERT(Relative != NULL); + Status = RtlAbsoluteToSelfRelativeSD(&Absolute, Relative, &Length ); + ASSERT(NT_SUCCESS(Status)); + + + RtlFreeHeap( RtlProcessHeap(), 0, Absolute.Dacl ); + RtlFreeHeap( RtlProcessHeap(), 0, Absolute.Sacl ); + + *pSD = Relative; + *Size = Length; + + return(Status); + +} + + + + +VOID +SampUnseedDs( + WCHAR * NamePrefix, + ULONG NamePrefixLen + ) +/*++ + +Routine Description: + + Removes the set of objects specified in the seed object table. Useful for + Unit test scenarios. + +Arguments: + + NamePrefix + NamePrefixLen + Specifies the length and string to be prefixed to every object name in the + seeding table. Useful to create the database in a particular container + +Return Values: + + None + +--*/ + +{ + + NTSTATUS IgnoreStatus = STATUS_SUCCESS; + ULONG NumObjects = ARRAY_COUNT(SeedObjectTable); + ULONG Index; + ULONG DsNameBuffer[SAMP_MAX_DSNAME_SIZE]; + DSNAME *pDsName; + + + // Remove the Objects. Start from the Last object, so that + // we remove leaf objects before removing container Objects + // the contain them + + for (Index =0; Index< NumObjects; Index++) + { + + // Initialize Object Name + SampInitializeDsName( + (DSNAME *) DsNameBuffer, + NamePrefix, + NamePrefixLen, + SeedObjectTable[NumObjects-Index-1].StringName, + SeedObjectTable[NumObjects-Index-1].NameLength + ); + pDsName = (DSNAME *) DsNameBuffer; + + + + // Delete the object with the required attributes + // Ignore errors + IgnoreStatus = SampDsDeleteObject( + pDsName + ); + + } + +} + + + diff --git a/private/newsam2/server/exe/sources b/private/newsam2/server/exe/sources new file mode 100644 index 000000000..74c4420f3 --- /dev/null +++ b/private/newsam2/server/exe/sources @@ -0,0 +1,136 @@ +!IF 0 + +Copyright (c) 1989 - 1996 Microsoft Corporation + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + +Author: + + Chris Mayhall (ChrisMay) 03-Jul-1996 + +NOTE: Commented description of this file is in \nt\oak\bin\sources.tpl + +>>>NOTE: The target name has changed from "samapp.exe" to "ntds.exe".<<< + +!ENDIF + +DS_BUILD=1 + +MAJORCOMP=sam + +MINORCOMP=server + +USE_LIBCMT=1 + +TARGETNAME=ntds + +TARGETPATH=obj + +TARGETTYPE=PROGRAM + +UMTYPE=console + +TARGETLIBS= \ + $(BASEDIR)\public\sdk\lib\*\nlrepl.lib \ + $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \ + $(BASEDIR)\public\sdk\lib\*\rpcndr.lib \ + $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \ + $(BASEDIR)\public\sdk\lib\*\kernel32.lib \ + $(BASEDIR)\public\sdk\lib\*\advapi32.lib \ + $(BASEDIR)\public\sdk\lib\*\lsasrv.lib \ + $(BASEDIR)\public\sdk\lib\*\lsadll.lib \ + $(BASEDIR)\public\sdk\lib\*\ntdll.lib \ + ..\..\..\lsa\crypt\engine\obj\*\rc4c.obj \ + ..\..\..\ds\src\dsamain\dll\obj\*\dsa.lib + +INCLUDES=.;..;..\..\inc;..\..\..\inc;..\..\..\lsa\crypt\engine;..\..\..\ds\src\inc;..\..\..\ds\src\dsamain\include;..\..\..\ds\src\xinc; + +NTPROFILEINPUT=yes + +SOURCES= \ + ..\display.c \ + ..\alias.c \ + ..\almember.c \ + ..\attr.c \ + ..\bldsam3.c \ + ..\close.c \ + ..\context.c \ + ..\dbgutil.c \ + ..\domain.c \ + ..\dslayer.c \ + ..\dsmember.c \ + ..\dsutil.c \ + ..\enum.c \ + ..\gentab2.c \ + ..\global.c \ + ..\group.c \ + ..\notify.c \ + ..\oldstub.c \ + ..\rundown.c \ + ..\samifree.c \ + ..\samrpc_s.c \ + ..\sam_rev.rc \ + ..\samss.c \ + ..\secdescr.c \ + ..\security.c \ + ..\server.c \ + ..\user.c \ + ..\utility.c \ + ..\upgrade.c \ + ..\dsmember.c \ + main.c \ + lsathunk.c \ + seed.c \ + cnvrtdat.c \ + srvtest.c \ + stgtest.c \ + attrtest.c \ + testutil.c \ + cuttest.c \ + grptest.c + +C_DEFINES=-DRPC_NO_WINDOWS_H -DUSER_MODE_SAM -D_MT -DMT + +USE_CRTDLL=1 + +# +# IDL Files - The stand-alone version (ntds.exe) produced by this file uses +# a different interface UUID (see samsrv.idl) than that of the samsrv.dll +# for testing purposes. +# + +IDL_NAME=samrpc + +SERVER_ACF=..\..\samsrv.acf + +SERVER_INC_FILE=samrpc.h + +SDKINC=$(BASEDIR)\public\sdk\inc + +SDKCRTINC=$(BASEDIR)\public\sdk\inc\crt + +PRIVATEINC=..\..\..\inc + +SERVER_FLAGS=-acf $(SERVER_ACF) -header $(SERVER_INC_FILE) -DUSER_MODE_SAM + +INCS=-I$(SDKINC) -I$(SDKCRTINC) -I..\.. -I$(PRIVATEINC) + +SERVER_CPP=-cpp_cmd "$(MIDL_CPP)" $(MIDL_FLAGS) + +# +# Defining the NTTARGETFILES variable causes MAKEFILE.DEF to attempt to +# include .\makefile.inc immediately after it specifies the top +# level targets (all, clean and loc) and their dependencies. MAKEFILE.DEF +# also expands the value of the NTTARGETFILES variable at the end of the +# list of dependencies for the all target. Useful for specifying additional +# targets and dependencies that don't fit the general case covered by +# MAKEFILE.DEF +# + +NTTARGETFILE0=sampmsgs.h + diff --git a/private/newsam2/server/exe/srvtest.c b/private/newsam2/server/exe/srvtest.c new file mode 100644 index 000000000..50191e5fc --- /dev/null +++ b/private/newsam2/server/exe/srvtest.c @@ -0,0 +1,722 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + srvtest.c + +Abstract: + + This file contains various test routines that call internal SAM server + routines for a unit test. + + THE ROUTINES IN THIS FILE ARE FOR SAM SEVER TEST PURPOSES ONLY. + + There are a number of server-side unit tests aimed at private routines + within the SAM server. These tests are defined in this file. This file + should only be compiled into the SAM server when it is built as a stand- + alone executable (built in the \um subdir). Therefore, it is only listed + in the sources file in the \um subdir. + + Because the SAM code relies on a fairly large amount of state information + being in place from the initialization of the server, it is difficult to + write a unit test that "plugs in" to the server from the outside. + +Author: + + Chris Mayhall (ChrisMay) 07-Jun-1996 + +Environment: + + User Mode - Win32 + +Revision History: + + ChrisMay 09-May-1996 + Created initial file, SampStoreObjectAttributes test. + ChrisMay 12-May-1996 + Moved BasicStorageTest to stgtest.c to reduce the number of merge + conflicts (from everyone partying on a single file). + +--*/ + +#include <samsrvp.h> +#include <dsutilp.h> +#include <dslayer.h> +#include <mappings.h> +#include <stdio.h> +#include <stdlib.h> +#include <testutil.h> + + +// Private debugging display routine is enabled when DSUTIL_DBG_PRINTF = 1. + +#define SRVTEST_DBG_PRINTF 1 + +#if (SRVTEST_DBG_PRINTF == 1) +#define DebugPrint printf +#else +#define DebugPrint +#endif + + + + +BOOL +CompareAttrBlocks( + ATTRBLOCK * attr1, + ATTRBLOCK * attr2 + ) +/*++ + +Routine Description: + + Compares two attrblocks, typically used to test the results + of a modify -read loop + +Arguments + + attr1 - first AttrBlock + attr2 - Second attrBlock + +Return Values + + TRUE - Attrblocks are same + FALSE - Attrblocks are different + +--*/ +{ + ULONG Index; + ULONG Index2; + + if (attr1->attrCount != attr2->attrCount) + goto False; + + for (Index=0;Index<attr1->attrCount;Index++) + { + // compare Types + if (attr1->pAttr[Index].attrTyp != attr2->pAttr[Index].attrTyp) + goto False; + // Comapre val Counts + if (attr1->pAttr[Index].AttrVal.valCount != + attr2->pAttr[Index].AttrVal.valCount + ) + goto False; + // compare Values + for (Index2=0;Index2< attr1->pAttr[Index].AttrVal.valCount;Index2++) + { + // Compare Length of each value + if ( attr1->pAttr[Index].AttrVal.pAVal[Index2].valLen != + attr2->pAttr[Index].AttrVal.pAVal[Index2].valLen + ) + goto False; + // Compare Memory of each value + if (memcmp( (void *) attr1->pAttr[Index].AttrVal.pAVal[Index2].pVal, + (void *) attr2->pAttr[Index].AttrVal.pAVal[Index2].pVal, + attr1->pAttr[Index].AttrVal.pAVal[Index2].valLen + ) !=0) + goto False; + } + } + return TRUE; + +False: + return FALSE; +} + +VOID +SetDefaultEnterpriseName( + TESTINFO *TstInfo + ) +/*++ + + Routine Description + + Sets the Default Enterprise Name to /o=NT/ou=DS + + +--*/ +{ + + WCHAR DefaultDomainName[] = L"/o=NT/ou=DS"; + + RtlCopyMemory( + TstInfo->EnterpriseName, + DefaultDomainName, + sizeof(DefaultDomainName) + ); + + TstInfo->EnterpriseNameLen = sizeof(DefaultDomainName); +} + + +VOID +ReadNamePrefix( + TESTINFO *TstInfo + ) +/*++ + + Routine Description + + Reads the o=, and ou= fields for test purposes + + Arguments + + NamePrefixBuffer - Holds the pointer to the string + NamePrefixLen - Holds the total length in bytes of the string. + Length includes the terminating NULL character. + +--*/ + +{ + ULONG CharCount = 0; + + // First Obtain the Name Prefix + + printf("Please Enter the Name Prefix:\n\n"); + printf("The Name Prefix is in the following Format\n"); + printf("/o=<enterprise name>/ou=<site name>\n"); + wscanf(L"%s",TstInfo->EnterpriseName); + CharCount = wcslen(TstInfo->EnterpriseName); + TstInfo->EnterpriseNameLen = sizeof(WCHAR) * ( CharCount + 1); +} + + +NTSTATUS +BuildDefaultSecurityDescriptor( + OUT PSECURITY_DESCRIPTOR *pSD, + OUT ULONG * Size + ) + +/*++ + + +Routine Description: + + This routine builds a self-relative security descriptor ready + to be applied to one of the SAM objects. + + Arguments: + pSD the security descriptor in self relative form + Size The size of it in bytes + +Return Value: + + TBS. + +--*/ +{ + + + + SECURITY_DESCRIPTOR Absolute; + PSECURITY_DESCRIPTOR Relative; + PACL TmpAcl; + PACCESS_ALLOWED_ACE TmpAce; + PSID TmpSid; + ULONG Length, i; + PULONG RidLocation; + BOOLEAN IgnoreBoolean; + ACCESS_MASK MappedMask; + PSID AceSid[10]; // Don't expect more than 10 ACEs in any of these. + ACCESS_MASK AceMask[10]; + NTSTATUS Status; + GENERIC_MAPPING GenericMap = + { + USER_READ, + USER_WRITE, + USER_EXECUTE, + USER_ALL_ACCESS + }; + + + // + // The approach is to set up an absolute security descriptor that + // looks like what we want and then copy it to make a self-relative + // security descriptor. + // + + + Status = RtlCreateSecurityDescriptor( + &Absolute, + SECURITY_DESCRIPTOR_REVISION1 + ); + ASSERT( NT_SUCCESS(Status) ); + + + + // + // Owner + // + + Status = RtlSetOwnerSecurityDescriptor (&Absolute, SampAdministratorsAliasSid, FALSE ); + ASSERT(NT_SUCCESS(Status)); + + + + // + // Group + // + + Status = RtlSetGroupSecurityDescriptor (&Absolute, SampAdministratorsAliasSid, FALSE ); + ASSERT(NT_SUCCESS(Status)); + + + AceSid[0] = SampWorldSid; + AceMask[0] = (USER_ALL_ACCESS); + + AceSid[1] = SampAdministratorsAliasSid; + AceMask[1] = (USER_ALL_ACCESS); + + + + + // + // Discretionary ACL + // + // Calculate its length, + // Allocate it, + // Initialize it, + // Add each ACE + // Add it to the security descriptor + // + + Length = (ULONG)sizeof(ACL); + for (i=0; i<2; i++) { + + Length += RtlLengthSid( AceSid[i] ) + + (ULONG)sizeof(ACCESS_ALLOWED_ACE) - + (ULONG)sizeof(ULONG); //Subtract out SidStart field length + } + + TmpAcl = RtlAllocateHeap( RtlProcessHeap(), 0, Length ); + ASSERT(TmpAcl != NULL); + + + Status = RtlCreateAcl( TmpAcl, Length, ACL_REVISION2); + ASSERT( NT_SUCCESS(Status) ); + + for (i=0; i<2; i++) { + MappedMask = AceMask[i]; + RtlMapGenericMask( &MappedMask, &GenericMap ); + Status = RtlAddAccessAllowedAce ( + TmpAcl, + ACL_REVISION2, + MappedMask, + AceSid[i] + ); + ASSERT( NT_SUCCESS(Status) ); + } + + Status = RtlSetDaclSecurityDescriptor (&Absolute, TRUE, TmpAcl, FALSE ); + ASSERT(NT_SUCCESS(Status)); + + + + + // + // Sacl + // + + + Length = (ULONG)sizeof(ACL) + + RtlLengthSid( SampWorldSid ) + + (ULONG)sizeof(SYSTEM_AUDIT_ACE) - + (ULONG)sizeof(ULONG); //Subtract out SidStart field length + TmpAcl = RtlAllocateHeap( RtlProcessHeap(), 0, Length ); + ASSERT(TmpAcl != NULL); + + Status = RtlCreateAcl( TmpAcl, Length, ACL_REVISION2); + ASSERT( NT_SUCCESS(Status) ); + + Status = RtlAddAuditAccessAce ( + TmpAcl, + ACL_REVISION2, + (GenericMap.GenericWrite | DELETE | WRITE_DAC | ACCESS_SYSTEM_SECURITY) & ~READ_CONTROL, + SampWorldSid, + TRUE, //AuditSuccess, + TRUE //AuditFailure + ); + ASSERT( NT_SUCCESS(Status) ); + + Status = RtlSetSaclSecurityDescriptor (&Absolute, TRUE, TmpAcl, FALSE ); + ASSERT(NT_SUCCESS(Status)); + + + + + + + // + // Convert the Security Descriptor to Self-Relative + // + // Get the length needed + // Allocate that much memory + // Copy it + // Free the generated absolute ACLs + // + + Length = 0; + Status = RtlAbsoluteToSelfRelativeSD( &Absolute, NULL, &Length ); + ASSERT(Status == STATUS_BUFFER_TOO_SMALL); + + Relative = RtlAllocateHeap( RtlProcessHeap(), 0, Length ); + ASSERT(Relative != NULL); + Status = RtlAbsoluteToSelfRelativeSD(&Absolute, Relative, &Length ); + ASSERT(NT_SUCCESS(Status)); + + + RtlFreeHeap( RtlProcessHeap(), 0, Absolute.Dacl ); + RtlFreeHeap( RtlProcessHeap(), 0, Absolute.Sacl ); + + *pSD = Relative; + *Size = Length; + + return(Status); + +} + + + + +NTSTATUS __stdcall +SampTestDsLayer( + VOID * Param + ) +/*++ + Executes a simple set of tests to unit test + the DS interface Layer + +--*/ +{ + NTSTATUS Status; + TESTINFO *TstInfo = (TESTINFO *) Param; + + // + // Declare Buffers to hold all our DsNames + // + + WCHAR Buffer[256]; + WCHAR Buffer2[256]; + + // + // Declare the names of the Domain and User Objects that + // we will be creating + // + + WCHAR ObjectName[] = L"/cn=NewContainer/cn=user2"; + WCHAR ContainerName[] = L"/cn=NewContainer"; + + // + // Variables to hold our DsNames + // + + DSNAME *pDsName; + DSNAME *pContainer; + DSNAME *ReturnedObject = NULL; + + BOOLEAN ObjectCreated = FALSE; + BOOLEAN ContainerCreated = FALSE; + + SECURITY_DESCRIPTOR SdAbsolute; + UNICODE_STRING NameToLookup; + + ATTRBLOCK AttrsRead; + + // + // declare the attribute values to set + // + + WCHAR UserAccountName[] = L"User2"; + CHAR UnicodePwd[] = {0,1,2,3,4,5,6,7,8,9}; + ULONG RevisionLevel = 4; + LARGE_INTEGER LastLogoff = {123456789, 123456789}; + + ATTRVAL AttributeValuesToSet []= + { + {sizeof(UserAccountName), (UCHAR *) UserAccountName}, // UNICODE Str + {sizeof(UnicodePwd), (UCHAR *) UnicodePwd }, // Binary + {sizeof(ULONG), (UCHAR *) & RevisionLevel }, // Integer + {sizeof(LARGE_INTEGER), (UCHAR *) &LastLogoff} // Large Integer + }; + + // + // declare attribute types + // + + ATTRTYP AttributeTypesToSet[]= + { + SAMP_USER_ACCOUNT_NAME, + SAMP_USER_UNICODE_PWD, + SAMP_FIXED_USER_REVISION_LEVEL, + SAMP_FIXED_USER_LAST_LOGOFF + }; + + ULONG Rid =1; + UCHAR DomainSid[] = {1,4,1,2,3,4,5,6,1,0,0,0,2,0,0,0,3,0,0,0,4,0,0,0}; + + ATTRTYP UserCreateType[]= + { + SAMP_USER_SECURITY_DESCRIPTOR, + SAMP_FIXED_USER_USERID + }; + + ATTRVAL UserCreateVal [] = + { + {sizeof(SECURITY_DESCRIPTOR),NULL}, + {sizeof(ULONG), (UCHAR *) & Rid} + }; + + ATTRTYP DomainCreateType []= + { + SAMP_DOMAIN_SECURITY_DESCRIPTOR , + SAMP_DOMAIN_SID + }; + + ATTRVAL DomainCreateVal []= + { + { sizeof(SECURITY_DESCRIPTOR),NULL}, + { sizeof(DomainSid), (UCHAR *) DomainSid } + }; + + + // + // declare Attrblock's + // + + DEFINE_ATTRBLOCK4(AttrBlock, AttributeTypesToSet, AttributeValuesToSet); + DEFINE_ATTRBLOCK2(DomainCreate,DomainCreateType,DomainCreateVal); + DEFINE_ATTRBLOCK2(UserCreate,UserCreateType,UserCreateVal); + + // + // Log Test Name + // + + printf("TESTING DS LAYER\n"); + + // + // Initialize an Object to Create + // + + pDsName = (DSNAME *) Buffer; + SampInitializeDsName( + pDsName, + TstInfo->EnterpriseName, + TstInfo->EnterpriseNameLen, + ObjectName, + sizeof(ObjectName) + ); + + // + // Initialize Name of Container Object + // + + pContainer = (DSNAME *) Buffer2; + + SampInitializeDsName( + pContainer, + TstInfo->EnterpriseName, + TstInfo->EnterpriseNameLen, + ContainerName, + sizeof(ContainerName) + ); + + // + // Build a default secuirty Descriptor for Domain Object + // + + Status = BuildDefaultSecurityDescriptor( + &(DomainCreateVal[0].pVal), + &(DomainCreateVal[0].valLen) + ); + + + // + // Build Default Security Descriptor for User Object + // + + Status = BuildDefaultSecurityDescriptor( + &(UserCreateVal[0].pVal), + &(UserCreateVal[0].valLen) + ); + + + + // + // Create the container Object + // + + Status = SampDsCreateObject( + pContainer, + SampDomainObjectType, + &DomainCreate, + (PSID) DomainSid + ); + if (Status != STATUS_SUCCESS) + { + printf("Container Creation Failed\n"); + goto Error; + } + + ContainerCreated = TRUE; + + // + // Create the user Object + // + + Status = SampDsCreateObject( + pDsName, + SampUserObjectType, + &UserCreate, + (PSID) DomainSid + ); + + if (Status != STATUS_SUCCESS) + { + printf("Object Creation Failed\n"); + goto Error; + } + + ObjectCreated = TRUE; + + // + // Set some attributes on the object + // + + Status = SampDsSetAttributes( + pDsName, + 0, + SampUserObjectType, + &AttrBlock + ); + if (Status != STATUS_SUCCESS) + { + printf("Set Attributes Failed\n"); + goto Error; + } + + // + // Read back the same attributes + // + + Status = SampDsRead( + pDsName, + 0, + SampUserObjectType, + &AttrBlock, + &AttrsRead + ); + if (Status != STATUS_SUCCESS) + { + printf("Read Attributes Failed\n"); + goto Error; + } + + // + // verify that they match + // + + if ( !CompareAttrBlocks(&AttrBlock,&AttrsRead)) + { + printf("ATTRBLOCK COMPARISON FAILED\n"); + goto Error; + } + + // + // Find the Object using the RID + // + + Status = SampDsLookupObjectByRid(pContainer, Rid, &ReturnedObject); + if (Status != STATUS_SUCCESS) + { + printf("Lookup Object By Rid Failed\n"); + goto Error; + } + + // + // Free the returned Object + // + + MIDL_user_free(ReturnedObject); + + NameToLookup.Buffer = UserAccountName; + NameToLookup.Length = wcslen(UserAccountName); + NameToLookup.MaximumLength = wcslen(UserAccountName); + + // Find the Object using Name + Status = SampDsLookupObjectByName( + pContainer, + SampUserObjectType, + &NameToLookup, + &ReturnedObject + ); + + if (Status != STATUS_SUCCESS) + { + printf("Lookup Object By Name Failed\n"); + goto Error; + } + + MIDL_user_free(ReturnedObject); + + // delete the object + Status = SampDsDeleteObject(pDsName); + if (Status != STATUS_SUCCESS) + { + printf("Delete Object Failed\n"); + goto Error; + } + + + ObjectCreated = FALSE; + + + Status = SampDsDeleteObject(pContainer); + if (Status != STATUS_SUCCESS) + { + printf("Delete Container Failed\n"); + goto Error; + } + + Status = SampMaybeEndDsTransaction(FALSE); + + if (Status != STATUS_SUCCESS) + { + printf("SampMaybeEndDsTransaction error = 0x%lx\n", Status); + goto Error; + } + + ContainerCreated = FALSE; + + printf("DSLAYER TEST PASSED\n"); + + +Cleanup: + + return Status; + +Error: + + if (ObjectCreated) + SampDsDeleteObject(pDsName); + + if (ContainerCreated) + SampDsDeleteObject(pContainer); + + SampMaybeEndDsTransaction(TRUE); + + printf(" ******** TEST FAILED **********\n"); + goto Cleanup; +} + + + + + + + + + + + diff --git a/private/newsam2/server/exe/stgtest.c b/private/newsam2/server/exe/stgtest.c new file mode 100644 index 000000000..0d470cf67 --- /dev/null +++ b/private/newsam2/server/exe/stgtest.c @@ -0,0 +1,227 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + srvtest.c + +Abstract: + + This file contains various test routines that call internal SAM server + routines for a unit test. + + THE ROUTINES IN THIS FILE ARE FOR SAM SEVER TEST PURPOSES ONLY. + + There are a number of server-side unit tests aimed at private routines + within the SAM server. These tests are defined in this file. This file + should only be compiled into the SAM server when it is built as a stand- + alone executable (built in the \um subdir). Therefore, it is only listed + in the sources file in the \um subdir. + + Because the SAM code relies on a fairly large amount of state information + being in place from the initialization of the server, it is difficult to + write a unit test that "plugs in" to the server from the outside. + + Tests: + + BasicStorageTest - This test creates a user object in the DS database, + and then attempts to set every SAM-user attribute defined. Once set, the + test then attempts to read back all of the attributes, thereby exercis- + ing the read operation (read operations are in progress...). + +Author: + + Chris Mayhall (ChrisMay) 07-Jun-1996 + +Environment: + + User Mode - Win32 + +Revision History: + + ChrisMay 12-May-1996 + Moved BasicStorageTest to stgtest.c to reduce the number of merge + conflicts (from everyone partying on a srvtest.c). + ChrisMay 18-Jun-1996 + Added user object to the test. + ChrisMay 21-Jun-1996 + Moved all of the "test environment" setup code to testutil.c/.h. + ChrisMay 25-Jun-1996 + Parameterized attribute buffer for test environment. + +--*/ + +#include <samsrvp.h> +#include <dsutilp.h> +#include <dslayer.h> +#include <mappings.h> +#include <stdio.h> +#include <stdlib.h> +#include <testutil.h> + +// Private debugging display routine is enabled when DSUTIL_DBG_PRINTF = 1. + +#define SRVTEST_DBG_PRINTF 0 + +#if (SRVTEST_DBG_PRINTF == 1) +#define DebugPrint printf +#else +#define DebugPrint +#endif + +//=============================BASIC STORAGE TEST============================= + +NTSTATUS +BasicStorageTest( + VOID *Parameter + ) +{ + NTSTATUS NtStatus = STATUS_INTERNAL_ERROR; + NTSTATUS NtStatus2 = STATUS_INTERNAL_ERROR; + PDSNAME ObjectDsName = NULL; + WCHAR Buffer1[128]; + WCHAR *NamePrefix = NULL; + ULONG Rid = 1001; + BYTE AttributeBuffer[BUF_SIZE]; + ULONG NamePrefixLength = 0; + SAMP_OBJECT ObjectContext; + BOOLEAN UseKeyHandle = FALSE; + UCHAR DomainSid[] = {1,4,1,2,3,4,5,6,1,0,0,0,2,0,0,0,3,0,0,0,4,0,0,0}; + + // BUG: Hard-coded ObjectName used in test data. + + WCHAR ObjectName[] = L"/o=NT/ou=DS/cn=Configuration/cn=User1"; + + // Set up the DS attribute blocks used to initialize the test. These + // attributes are not actually part of the test, but only serve to set + // up the initial environment for the storage test. + + ATTRVAL SecurityDescriptorVal[] = + { + {sizeof(SECURITY_DESCRIPTOR), NULL}, + {sizeof(ULONG), (UCHAR *) &Rid} + }; + + ATTRTYP SecurityDescriptorType[] = + { + SAMP_USER_SECURITY_DESCRIPTOR, + SAMP_FIXED_USER_USERID + }; + + DEFINE_ATTRBLOCK2(SdBlock, SecurityDescriptorType, SecurityDescriptorVal); + + // The DS requires a default security descriptor for object creation, so + // put one together (in self-relative format). + + NtStatus = BuildDefaultSecurityDescriptor( + &(SecurityDescriptorVal[0].pVal), + &(SecurityDescriptorVal[0].valLen)); + + if (!NT_SUCCESS(NtStatus)) + { + return(NtStatus); + } + + // Construct all the bits and pieces needed for exercising a SAM user- + // object in the DS storage. + + // Set up the object's DS name. + + ObjectDsName = (PDSNAME)Buffer1; + + SampInitializeDsName(ObjectDsName, + NamePrefix, + NamePrefixLength, + ObjectName, + sizeof(ObjectName)); + + NtStatus = BuildObjectContext(SampUserObjectType, + ObjectDsName, + AttributeBuffer, + &ObjectContext); + + if (NT_SUCCESS(NtStatus)) + { + DebugPrint("STGTEST: Built Object(%d) Context Successfully\n", + SampUserObjectType); + + // Object is a DSNAME data type; create a default user object + // for the test. + + NtStatus = SampDsCreateObject(ObjectDsName, + SampUserObjectType, + &SdBlock, + (PSID) DomainSid + ); + + if (NT_SUCCESS(NtStatus)) + { + // Attempt to set ALL of the SAM user attributes on the newly + // created object. Note that UseKeyHandle is only used for + // SAM-registry operations, so is NULL here. + + + NtStatus = SampStoreObjectAttributes(&ObjectContext, + UseKeyHandle); + + + if (NT_SUCCESS(NtStatus)) + { + DebugPrint("SampStoreObjectAttributes status = 0x%lx\n", + NtStatus); + + // BUG: Work in progress...verification of the storage. + } + else + { + DebugPrint("SampStoreObjectAttributes error = 0x%lx\n", + NtStatus); + } + + // Clean up so that the test can be re-run. + + NtStatus = SampDsDeleteObject(ObjectDsName); + + if (!NT_SUCCESS(NtStatus)) + { + DebugPrint("SampDsDeleteObject error = 0x%lx\n", NtStatus); + } + + } + else + { + DebugPrint("SampDsCreateObject error = 0x%lx\n", NtStatus); + } + + // BUG: Need to test domain object also. + + // SetupDomainObjectInformation(); + // SetupDomainObjectContext(); + } + else + { + DebugPrint("BuildDefaultSecurityDescriptor error = 0x%lx\n", NtStatus); + } + + NtStatus2 = SampMaybeEndDsTransaction(FALSE); + + ASSERT(!SampExistsDsTransaction()); + + if (!NT_SUCCESS(NtStatus2) ) + { + DebugPrint("SampMaybeEndDsTransaction error = 0x%lx\n", NtStatus2); + } + + if (NT_SUCCESS(NtStatus) && NT_SUCCESS(NtStatus2)) + { + printf("\nBasicStorageTest PASSED\n"); + } + else + { + printf("\nBasicStorageTest FAILED\n"); + } + + return(NtStatus); +} + diff --git a/private/newsam2/server/exe/testutil.c b/private/newsam2/server/exe/testutil.c new file mode 100644 index 000000000..45ae9e8a0 --- /dev/null +++ b/private/newsam2/server/exe/testutil.c @@ -0,0 +1,803 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + testutil.c + +Abstract: + + This file contains various helper routines called by the SAM unit tests. + + THE ROUTINES IN THIS FILE ARE FOR SAM SEVER TEST PURPOSES ONLY. + + The routines in this file are intended to be "helper" functions that are + used by any SAM unit test. + +Author: + + Chris Mayhall (ChrisMay) 21-Jun-1996 + +Environment: + + User Mode - Win32 + +Revision History: + + ChrisMay 21-Jun-1996 + Created initial file, added the "user" routines. + ChrisMay 25-Jun-1996 + Added a blob comparison routine that graphically displays which bytes + differ between two blobs (which allows faster error location). Added + comparison of OnDisk blobs. + ChrisMay 28-Jun-1996 + Added a fixed-length attribute comparison routine. + ChrisMay 02-Jul-1996 + Added a variable-length attribute comparison routine. + +--*/ + +#include <samsrvp.h> +#include <dsutilp.h> +#include <dslayer.h> +#include <mappings.h> +#include <stdio.h> +#include <stdlib.h> +#include <testutil.h> + +#define DWORD_ALIGN(value) (((DWORD)(value) + 3) & ~3) + +// Private debugging display routine is enabled when TESTUTIL_DBG_PRINTF = 1. + +#define TESTUTIL_DBG_PRINTF 0 + +#if (TESTUTIL_DBG_PRINTF == 1) +#define DebugPrint printf +#else +#define DebugPrint +#endif + +//============================GLOBAL TEST DATA================================ + +SAMP_V1_0A_FIXED_LENGTH_USER FixedLengthUserAttributes; // Fixed Attributes +LIST_ENTRY ListEntry; // RTL List +PSECURITY_DESCRIPTOR UserSecurityDescriptor; // Generic SD +ULONG SecurityDescriptorLength; // Global SD size + + + +//============================SETUP SERVER OBJECT============================= + + // work in progress + + + +//============================SETUP DOMAIN OBJECT============================= + + // work in progress + + + +//============================SETUP GROUP OBJECT============================== + + // work in progress + + + +//============================SETUP ALIAS OBJECT============================== + + // work in progress + + + +//============================SETUP USER OBJECT=============================== + +VOID +SetupFixedLengthUserAttributes( + OUT PVOID SamUserAttributes, + OUT PULONG CurrentOffset + ) +{ + ULONG Length = sizeof(SAMP_V1_0A_FIXED_LENGTH_USER); + + // BUG: Setting Revision to SAMP_REVISION breaks schema constraint limits. + + ULONG Revision = 4; + ULONG Unused1 = 200; + LARGE_INTEGER LastLogon = {9, 9}; + LARGE_INTEGER LastLogoff = {0xA, 0xA}; + LARGE_INTEGER PasswordLastSet = {0xB, 0xB}; + LARGE_INTEGER AccountExpires = {0xC, 0xC}; + LARGE_INTEGER LastBadPasswordTime = {0xD, 0xD}; + ULONG UserId = 1001; + USHORT CountryCode = 5; + USHORT CodePage = 5; + USHORT BadPasswordCount = 1; + USHORT LogonCount = 1; + USHORT AdminCount = 1; + USHORT Unused2 = 1; + USHORT OperatorCount = 1; + + DebugPrint("User Fixed-Attr Length = %lu\n", Length); + + // Setup fixed-length domain attributes. + + RtlZeroMemory(&FixedLengthUserAttributes, Length); + + FixedLengthUserAttributes.Revision = Revision; + FixedLengthUserAttributes.Unused1 = Unused1; + FixedLengthUserAttributes.LastLogon = LastLogon; + FixedLengthUserAttributes.LastLogoff = LastLogoff; + FixedLengthUserAttributes.PasswordLastSet = PasswordLastSet; + FixedLengthUserAttributes.AccountExpires = AccountExpires; + FixedLengthUserAttributes.LastBadPasswordTime = LastBadPasswordTime; + FixedLengthUserAttributes.UserId = UserId; + FixedLengthUserAttributes.CountryCode = CountryCode; + FixedLengthUserAttributes.CodePage = CodePage; + FixedLengthUserAttributes.BadPasswordCount = BadPasswordCount; + FixedLengthUserAttributes.LogonCount = LogonCount; + FixedLengthUserAttributes.AdminCount = AdminCount; + FixedLengthUserAttributes.Unused2 = Unused2; + FixedLengthUserAttributes.OperatorCount = OperatorCount; + + // Copy the fixed-length attributes into the first part of the SAM attr- + // ibute buffer and save the current offset for subsequent operations. + + RtlCopyMemory(SamUserAttributes, &FixedLengthUserAttributes, Length); + *CurrentOffset = DWORD_ALIGN(Length); + + DebugPrint("CurrentOffset = %lu\n", *CurrentOffset); + + return; +} + + + +NTSTATUS +SetupVarLengthUserAttributeArray( + OUT PVOID SamUserAttributes, + OUT PULONG CurrentOffset + ) +{ + NTSTATUS NtStatus = STATUS_SEVERITY_ERROR; + ULONG DescriptorLength = 0; + ULONG Offset = 0; + ULONG Length = 0; + ULONG Qualifier = 1; + SAMP_VARIABLE_LENGTH_ATTRIBUTE VarLengthUserArray[SAMP_USER_VARIABLE_ATTRIBUTES]; + ULONG Index = 0; + + // Setup variable-length user attributes, starting with the security + // descriptor. + + NtStatus = BuildDefaultSecurityDescriptor(&UserSecurityDescriptor, + &Length); + + Length = DWORD_ALIGN(Length); + + // Save the descriptor length for late usage. + + SecurityDescriptorLength = Length; + + Offset = SAMP_USER_VARIABLE_ATTRIBUTES * + sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE); + + RtlZeroMemory(VarLengthUserArray, Offset); + + VarLengthUserArray[SAMP_USER_SECURITY_DESCRIPTOR].Offset = Offset; + VarLengthUserArray[SAMP_USER_SECURITY_DESCRIPTOR].Length = Length; + VarLengthUserArray[SAMP_USER_SECURITY_DESCRIPTOR].Qualifier = Qualifier; + + // BUG: Assigning bogus test data for expediency purposes. + + for (Index = 1; Index < SAMP_USER_VARIABLE_ATTRIBUTES; Index++) + { + Offset = Offset + Length; + Length = sizeof(ULONG); + + VarLengthUserArray[Index].Offset = Offset; + VarLengthUserArray[Index].Length = Length; + VarLengthUserArray[Index].Qualifier = Qualifier; + } + + // Append the variable-length attribute array onto the SAM attribute + // buffer and update current offset. + + Length = SAMP_USER_VARIABLE_ATTRIBUTES * + sizeof(SAMP_VARIABLE_LENGTH_ATTRIBUTE); + + + RtlCopyMemory((PBYTE)SamUserAttributes + *CurrentOffset, + VarLengthUserArray, + Length); + + *CurrentOffset += Length; + + *CurrentOffset = DWORD_ALIGN(*CurrentOffset); + + return(NtStatus); +} + + + +VOID +SetupVarLengthUserAttributes( + OUT PVOID SamUserAttributes, + OUT PULONG CurrentOffset, + OUT PULONG FinalBufferLength + ) +{ + BYTE *CurrentAddress = NULL; + ULONG Length = 0; + ULONG Index = 0; + ULONG TestData = 0xAABBCCDD; + + // Set CurrentAddress to point at the SAM attribute-buffer offset where + // the variable-length attributes should start. + + CurrentAddress = (PBYTE)SamUserAttributes + *CurrentOffset; + Length = SecurityDescriptorLength; + + // Append the security descriptor onto the buffer. + + RtlCopyMemory(CurrentAddress, UserSecurityDescriptor, Length); + + // BUG: Append the rest of the user TestData -- need real data. + + for (Index = 1; Index < SAMP_USER_VARIABLE_ATTRIBUTES; Index++) + { + CurrentAddress = CurrentAddress + Length; + Length = sizeof(ULONG); + RtlCopyMemory(CurrentAddress, &TestData, Length); + } + + // Save the total buffer length. + + *FinalBufferLength = (CurrentAddress + Length) - (PBYTE)SamUserAttributes; + + return; +} + + + +NTSTATUS +SetupSamUserAttributes( + OUT PVOID SamUserAttributes, + OUT PULONG FinalBufferLength + ) +{ + NTSTATUS NtStatus = STATUS_SEVERITY_ERROR; + ULONG CurrentOffset = 0; + + SetupFixedLengthUserAttributes(SamUserAttributes, &CurrentOffset); + + // Building a security descriptor is the only thing that can fail, so + // check the status code. + + NtStatus = SetupVarLengthUserAttributeArray(SamUserAttributes, + &CurrentOffset); + + if (NT_SUCCESS(NtStatus)) + { + SetupVarLengthUserAttributes(SamUserAttributes, + &CurrentOffset, + FinalBufferLength); + } + + return(NtStatus); +} + + + +//============================================================================ + +NTSTATUS +BuildObjectContext( + IN INT ObjectType, + IN PDSNAME ObjectDsName, + IN PBYTE AttributeBuffer, + OUT PSAMP_OBJECT ObjectContext + ) + + // This routine can be used to build the context for any SAM object (i.e. + // server, domain, group, alias, user). +{ + NTSTATUS NtStatus = STATUS_SEVERITY_ERROR; + PVOID SamUserAttributes = NULL; + ULONG FinalBufferLength = 0; + UNICODE_STRING RootName = {0, 0, NULL}; + SAMP_USER_OBJECT UserTypeBody = {1001, 0}; + + RtlZeroMemory(AttributeBuffer, BUF_SIZE); + + // Determine which object is to be constructed, create its attributes, + // and fill out the rest of the SAM context structure. + + switch(ObjectType) + { + + case SampServerObjectType: + + // Setup SAM server object here. + + break; + + case SampDomainObjectType: + + // Setup SAM domain object here. + + break; + + case SampGroupObjectType: + + // Setup SAM group object here. + + break; + + case SampAliasObjectType: + + // Setup SAM alias object here. + + break; + + case SampUserObjectType: + + SamUserAttributes = AttributeBuffer; + + // Construct the fixed and variable-length user attributes. + + NtStatus = SetupSamUserAttributes(SamUserAttributes, + &FinalBufferLength); + + if (NT_SUCCESS(NtStatus)) + { + RtlZeroMemory(ObjectContext, sizeof(SAMP_OBJECT)); + + // Set up the user context info. + + ObjectContext->ContextListEntry = ListEntry; + ObjectContext->ObjectType = SampUserObjectType; + ObjectContext->FixedValid = TRUE; + ObjectContext->VariableValid = TRUE; + ObjectContext->FixedDirty = TRUE; + ObjectContext->VariableDirty = TRUE; + ObjectContext->OnDisk = SamUserAttributes; + ObjectContext->OnDiskAllocated = FinalBufferLength; + ObjectContext->OnDiskUsed = FinalBufferLength; + ObjectContext->OnDiskFree = 0; + ObjectContext->ReferenceCount = 1; + ObjectContext->GrantedAccess = 0; + ObjectContext->RootKey = 0; + ObjectContext->RootName = RootName; + ObjectContext->ObjectNameInDs = ObjectDsName; + + // Set the DS-object flag to TRUE. + + SetDsObject(ObjectContext); + + ObjectContext->DomainIndex = 0; + ObjectContext->MarkedForDelete = FALSE; + ObjectContext->TrustedClient = TRUE; + ObjectContext->AuditOnClose = FALSE; + ObjectContext->Valid = TRUE; + + ObjectContext->TypeBody.User = UserTypeBody; + } + + break; + + default: + + DebugPrint("Invalid SAM object type specified\n"); + + break; + + } + + return(NtStatus); +} + + + +BOOLEAN +SampCompareBinaryData( + PBYTE pData1, + PBYTE pData2, + DWORD cbData + ) +{ + BOOLEAN Flag = TRUE; + DWORD i; + BYTE AsciiLine[16]; + BYTE BinaryLine[16]; + CHAR Buffer[DBG_BUFFER_SIZE]; + + // This routine is used to compare two blobs. It returns TRUE if they + // are identical, and also displays (in grid fashion) the content of + // the blob. An adjacent grid is also displayed, which contains zeros + // and ones to quickly show which bytes are different. A "1" indicates + // that the bytes are the same, "0" means that they are different. + + if (0 == cbData) + { + DebugPrint("Zero-Length Data\n"); + return(FALSE); + } + + if (cbData > DBG_BUFFER_SIZE) + { + DebugPrint("ShowBinaryData - truncating display to 512 bytes\n"); + cbData = DBG_BUFFER_SIZE; + } + + for (; cbData > 0 ;) + { + for (i = 0; i < 16 && cbData > 0 ; i++, cbData--) + { + // Determine whether or not the bytes match. + + if (*pData1 == *pData2) + { + BinaryLine[i] = *pData1; + AsciiLine[i] = '1'; + } + else + { + BinaryLine[i] = ' '; + AsciiLine[i] = '0'; + + // At least one byte does not match, so set the flag and + // continue. + + Flag = FALSE; + } + + pData1++; + pData2++; + } + + // Fill out a partial line if necessary. + + if (i < 15) + { + for (; i < 16 ; i++) + { + BinaryLine[i] = ' '; + AsciiLine[i] = ' '; + } + } + + // Display the raw data. + + sprintf(Buffer, + "%02x %02x %02x %02x %02x %02x %02x %02x - %02x %02x %02x %02x %02x %02x %02x %02x\t", + BinaryLine[0], + BinaryLine[1], + BinaryLine[2], + BinaryLine[3], + BinaryLine[4], + BinaryLine[5], + BinaryLine[6], + BinaryLine[7], + BinaryLine[8], + BinaryLine[9], + BinaryLine[10], + BinaryLine[11], + BinaryLine[12], + BinaryLine[13], + BinaryLine[14], + BinaryLine[15]); + + DebugPrint(Buffer); + + // Display the "byte match grid" to show which bytes differ. + + sprintf(Buffer, + "%c%c%c%c%c%c%c%c - %c%c%c%c%c%c%c%c\n", + AsciiLine[0], + AsciiLine[1], + AsciiLine[2], + AsciiLine[3], + AsciiLine[4], + AsciiLine[5], + AsciiLine[6], + AsciiLine[7], + AsciiLine[8], + AsciiLine[9], + AsciiLine[10], + AsciiLine[11], + AsciiLine[12], + AsciiLine[13], + AsciiLine[14], + AsciiLine[15]); + + DebugPrint(Buffer); + } + + return(Flag); +} + + + +BOOLEAN +CompareContexts( + IN PSAMP_OBJECT Context1, + IN PSAMP_OBJECT Context2 + ) +{ + BOOLEAN Identical = FALSE; + ULONG Length = 0; + + + if ((!memcmp(&Context1->ContextListEntry, + &Context2->ContextListEntry, + sizeof(LIST_ENTRY))) && + (Context1->ObjectType == Context2->ObjectType) && + (Context1->FixedValid == Context2->FixedValid) && + (Context1->VariableValid == Context2->VariableValid) && + (Context1->FixedDirty == Context2->FixedDirty) && + (Context1->VariableDirty == Context2->VariableDirty) && + (Context1->OnDiskAllocated == Context2->OnDiskAllocated) && + (Context1->OnDiskUsed == Context2->OnDiskUsed) && + (Context1->OnDiskFree == Context2->OnDiskFree) && + (Context1->ReferenceCount == Context2->ReferenceCount) && + (Context1->GrantedAccess == Context2->GrantedAccess) && + (Context1->RootKey == Context2->RootKey) && + (!memcmp(&Context1->RootName, + &Context2->RootName, + sizeof(UNICODE_STRING))) && + (Context1->ObjectFlags == Context2->ObjectFlags) && + (Context1->DomainIndex == Context2->DomainIndex) && + (Context1->MarkedForDelete == Context2->MarkedForDelete) && + (Context1->TrustedClient == Context2->TrustedClient) && + (Context1->AuditOnClose == Context2->AuditOnClose) && + (Context1->Valid == Context2->Valid)) + + // BUG: Need to verify Context1->TypeBody + + { + Length = Context1->OnDiskUsed; + + // Byte compare the SAM attribute buffers as two blobs. + + if (SampCompareBinaryData(Context1->OnDisk, Context2->OnDisk, Length)) + { + Identical = TRUE; + } + else + { + DebugPrint("Attribute buffers differ\n"); + } + } + else + { + DebugPrint("Object contexts differ\n"); + } + + return(Identical); +} + + + +BOOLEAN +CompareFixedAttributes( + IN PSAMP_OBJECT Context, + IN PVOID FixedAttributes + ) +{ + BOOLEAN Identical = FALSE; + PBYTE FixedAttributesTmp = NULL; + ULONG Length = 0; + + // The fixed-length attributes are stored in the first part of the OnDisk + // buffer. + + FixedAttributesTmp = Context->OnDisk; + + // Determine the buffer length for comparison purposes and compare the + // two sets of fixed attributes. + + switch(Context->ObjectType) + { + + case SampServerObjectType: + + Length = sizeof(SAMP_V1_FIXED_LENGTH_SERVER); + break; + + case SampDomainObjectType: + + Length = sizeof(SAMP_V1_0A_FIXED_LENGTH_DOMAIN); + break; + + case SampGroupObjectType: + + Length = sizeof(SAMP_V1_0A_FIXED_LENGTH_GROUP); + break; + + case SampAliasObjectType: + + Length = sizeof(SAMP_V1_FIXED_LENGTH_ALIAS); + break; + + case SampUserObjectType: + + Length = sizeof(SAMP_V1_0A_FIXED_LENGTH_USER); + break; + + default: + break; + + } + + if (SampCompareBinaryData(FixedAttributesTmp, FixedAttributes, Length)) + { + Identical = TRUE; + } + else + { + DebugPrint("Fixed attribute buffers differ\n"); + } + + return(Identical); +} + + + +BOOLEAN +CompareVariableAttributes( + IN PSAMP_OBJECT Context, + IN ULONG AttributeId, + IN PVOID VarAttribute + ) +{ + BOOLEAN Identical = FALSE; + INT ObjectType = Context->ObjectType; + PSAMP_VARIABLE_LENGTH_ATTRIBUTE VarAttributesArray = NULL; + PBYTE VarAttributeTmp = NULL; + ULONG Length = 0; + ULONG Offset = 0; + + // Get a pointer to the start of the variable-length attributes for + // this object. + + if (IsDsObject(Context)) + { + Offset = SampObjectInformation[ObjectType].VariableDsArrayOffset; + } + else + { + Offset = SampObjectInformation[ObjectType].VariableArrayOffset; + } + + VarAttributesArray = (PSAMP_VARIABLE_LENGTH_ATTRIBUTE) + ((PBYTE)(Context->OnDisk) + Offset); + + // Determine the buffer length for comparison purposes and compare the + // two sets of fixed attributes. + + Length = VarAttributesArray[AttributeId].Length; + Offset = VarAttributesArray[AttributeId].Offset; + + // BUG: Should add a test for the Qualifier field also. + + // Get a pointer to the attribute in question. Note that this works for + // multi-valued attributes as well, since the length is the total length + // of all of the values for a given attribute and the comparison is + // a memcmp style comparison. + + VarAttributeTmp = ((PBYTE)VarAttributesArray) + Offset; + + if (SampCompareBinaryData(VarAttributeTmp, VarAttribute, Length)) + { + Identical = TRUE; + } + else + { + DebugPrint("Variable attribute buffers differ\n"); + } + + return(Identical); +} + + +NTSTATUS +InitDsDomain(DSNAME * pDsName) +/*++ + + Routine Description: + + Initializes a DS Domain in the SampDefinedDomains Strcture + for test purposes + + Arguments: + + pDsName - The DSName of the domain to be initialized + + Return Values: + + STATUS_SUCCESS + STATUS_NO_MEMORY + +--*/ +{ + NTSTATUS NtStatus = STATUS_SUCCESS; + PSAMP_OBJECT DomainContext; + PSAMP_DEFINED_DOMAINS TmpDefinedDomains; + + + // + // Initialize everything we might have to cleanup on error + // + + DomainContext = NULL; + + // + // Create a context for this domain object. + // We'll keep this context around until SAM is shutdown + // We store the context handle in the defined_domains structure. + // + + DomainContext = SampCreateContext( SampDomainObjectType, TRUE ); + + if ( DomainContext == NULL ) { + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + return NtStatus; + } + + DomainContext->DomainIndex = SampDefinedDomainsCount; + + // Set this to DS Object + SetDsObject(DomainContext); + DomainContext->ObjectNameInDs = MIDL_user_allocate( + pDsName->structLen + ); + + if (NULL==DomainContext->ObjectNameInDs) + { + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + return NtStatus; + } + RtlCopyMemory(DomainContext->ObjectNameInDs,pDsName,pDsName->structLen); + + // Reference the Context + SampReferenceContext(DomainContext); + + TmpDefinedDomains = MIDL_user_allocate( + (SampDefinedDomainsCount +1) + * sizeof(SAMP_DEFINED_DOMAINS) + ); + + + if (NULL==TmpDefinedDomains) + { + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + return NtStatus; + } + + RtlCopyMemory( + TmpDefinedDomains, + SampDefinedDomains, + SampDefinedDomainsCount * sizeof(SAMP_DEFINED_DOMAINS) + ); + + MIDL_user_free(SampDefinedDomains); + + SampDefinedDomains = TmpDefinedDomains; + + SampDefinedDomains[SampDefinedDomainsCount].Context = DomainContext; + + // + // Initialize the user & group context block list heads + // + + InitializeListHead( &SampDefinedDomains[SampDefinedDomainsCount].UserContextHead ); + InitializeListHead( &SampDefinedDomains[SampDefinedDomainsCount].GroupContextHead ); + InitializeListHead( &SampDefinedDomains[SampDefinedDomainsCount].AliasContextHead ); + + // Increment the defined Domains Count + SampDefinedDomainsCount++; + + return NtStatus; +} diff --git a/private/newsam2/server/exe/testutil.h b/private/newsam2/server/exe/testutil.h new file mode 100644 index 000000000..03946c7c5 --- /dev/null +++ b/private/newsam2/server/exe/testutil.h @@ -0,0 +1,55 @@ +// Generic buffer sizes. + +#define BUF_SIZE 1024 +#define DBG_BUFFER_SIZE 512 + +// Test info Structure. Put any test parameters +// out here + +typedef struct _TESTINFO +{ + ULONG EnterpriseNameLen; + WCHAR EnterpriseName[256]; +} TESTINFO ; + +// Forward declarations. + +NTSTATUS +BuildDefaultSecurityDescriptor( + OUT PSECURITY_DESCRIPTOR *pSecDesc, + OUT PULONG Size + ); + +NTSTATUS +BuildObjectContext( + IN INT ObjectType, + IN PDSNAME ObjectName, + IN PBYTE AttributeBuffer, + OUT PSAMP_OBJECT ObjectContext + ); + +BOOLEAN +CompareContexts( + IN PSAMP_OBJECT Context1, + IN PSAMP_OBJECT Context2 + ); + +BOOLEAN +CompareFixedAttributes( + IN PSAMP_OBJECT Context, + IN PVOID FixedAttributes + ); + +BOOLEAN +CompareVariableAttributes( + IN PSAMP_OBJECT Context, + IN ULONG AttributeId, + IN PVOID VarAttribute + ); + + +NTSTATUS +InitDsDomain( + DSNAME * pDsName + ); + diff --git a/private/newsam2/server/gentab2.c b/private/newsam2/server/gentab2.c new file mode 100644 index 000000000..fea707673 --- /dev/null +++ b/private/newsam2/server/gentab2.c @@ -0,0 +1,3561 @@ +/*++ + +Copyright (c) 1994 Microsoft Corporation + +Module Name: + + gentab2.c + +Abstract: + + GenericTable2 package + + Generic table services for maintaining data sets. The primary + characteristic of this generic table package is that it maintains + a relatively balanced tree, which provides for good (O(log(N)) + performance. + + The GenericTable2 routines are similar to the original + GenericTable routines provided by Gary Kimure except that the + GenericTable2 routines use a 2-3-tree rather than a splay tree. + 2-3-trees are described in "Data Structures And Algorithms", by + Aho, Hopcroft, and Ullman, published by Addison Wesley Publishing + Company. + + Another difference between this package and the original Generic + Table package is that this one references element buffers that are + inserted rather than copying the data (as in the orignal package). + This characteristic is nice if you have to sort large numbers of + records by multiple keys + + 2-3-trees have better characteristics than splay-trees when the + data being maintained is not random. For example, maintaining a + dictionary, in which the data quite often is provided in an orderly + manner, is an ideal application for 2-3-trees. + + This package does not support the retrieval of elements in inserted + order that is supported in the original Generic Table package. + + Differences between the algorithm outlined in Aho, et al and what + is coded here are: + + 1) I provide an additional means of obtaining the elements + in the tree in sorted order (for enumeration performance). + I keep a linked list of elements in addition to the tree + structure. + + 1) Aho et al point directly to elements in the tree from + nodes in the tree. In order to allow me to keep the linked + list mentioned in (1), I have a separate leaf element pointed + to from nodes which point to the element values. This leaf + component has the LIST_ENTRY structures used to link the + elements together. + + 3) Aho et al's algorithms ignore the fact that they may fail + to allocate memory (that is, they assume the Pascal "new" + function always succeeds). This package assumes that + any memory allocation may fail and will always leave the + tree in a valid form (although an insertion may fail in + this case). + + +Author: + + Jim Kelly (JimK) 20-Jan-1994 + +Environment: + + Run time library, user or kernel mode. + +Revision History: + + +--*/ + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + + +#include <nt.h> +#include <ntrtl.h> +#include <samsrvp.h> + + + + +////////////////////////////////////////////////////////////////////////// +// // +// defines ... // +// // +////////////////////////////////////////////////////////////////////////// + +// +// The following define controls the diagnostic capabilities that +// are built into this package. +// + +#if DBG +#define GTBP_DIAGNOSTICS 1 +#endif // DBG + + +// +// These definitions are useful diagnostics aids +// + +#if GTBP_DIAGNOSTICS + +// +// defining the following symbol causes significant amounts of +// development assistance code to be built +// + +//#define GTBP_DEVELOPER_BUILD 1 + +// +// Global Diagnostics Flags +// + +ULONG GtbpGlobalFlag; + +// +// Test for diagnostics enabled +// + +#define IF_GTBP_GLOBAL( FlagName ) \ + if (GtbpGlobalFlag & (GTBP_DIAG_##FlagName)) + +// +// Diagnostics print statement +// + +#define GtbpDiagPrint( FlagName, _Text_ ) \ + IF_GTBP_GLOBAL( FlagName ) \ + DbgPrint _Text_ + + +#else + +// +// No diagnostics included in build +// + +// +// Test for diagnostics enabled +// + +#define IF_GTBP_GLOBAL( FlagName ) if (FALSE) + + +// +// Diagnostics print statement (nothing) +// + +#define GtbpDiagPrint( FlagName, Text ) ; + + +#endif // GTBP_DIAGNOSTICS + +// +// The following flags enable or disable various diagnostic +// capabilities within SAM. These flags are set in +// GtbpGlobalFlag. +// +// INSERT - print diagnostic messages related to insertion +// operations. +// +// DELETION - print diagnostic messages related to deletion +// operations. +// +// LEAF_AND_NODE_ALLOC - print diagnostic messages related +// to allocation of leaf and node objects for insertion +// operations. +// +// ENUMERATE - print diagnostic messages related to enumeration +// operations. This includes getting restart keys. +// +// LOOKUP - print diagnostic messages related to element lookup +// operations. +// +// COLLISIONS - print diagnostic messages indicating when collisions +// occur on insert. +// +// VALIDATE - print diagnostic messages to be printed during table +// validations. +// + +#define GTBP_DIAG_INSERT ((ULONG) 0x00000001L) +#define GTBP_DIAG_DELETION ((ULONG) 0x00000002L) +#define GTBP_DIAG_LEAF_AND_NODE_ALLOC ((ULONG) 0x00000004L) +#define GTBP_DIAG_ENUMERATE ((ULONG) 0X00000008L) +#define GTBP_DIAG_LOOKUP ((ULONG) 0X00000010L) +#define GTBP_DIAG_COLLISIONS ((ULONG) 0X00000020L) +#define GTBP_DIAG_VALIDATE ((ULONG) 0X00000040L) + + +////////////////////////////////////////////////////////////////////////// +// // +// Macros ... // +// // +////////////////////////////////////////////////////////////////////////// + +// +// GtbpChildrenAreLeaves( +// IN GTB_TWO_THREE_NODE N +// ) +// Returns TRUE if children of N are leaves. +// Otherwise returns FALSE. +// + +#define GtbpChildrenAreLeaves( N ) ((((N)->Control) & GTB_CHILDREN_ARE_LEAVES) != 0) + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Private structures and definitions // +// // +/////////////////////////////////////////////////////////////////////////////// + +// +// GTB_TWO_THREE_NODE.Control field values +// + +#define GTB_CHILDREN_ARE_LEAVES (0x00000001) + + + +////////////////////////////////////////////////////////////////////////// +// // +// Internal Routine Definitions ... // +// // +////////////////////////////////////////////////////////////////////////// + +VOID +GtbpDeleteFromSubTree ( + IN PRTL_GENERIC_TABLE2 Table, + IN PGTB_TWO_THREE_NODE Node, + IN PVOID Element, + OUT PGTB_TWO_THREE_LEAF *LowOfNode, + OUT BOOLEAN *ElementDeleted, + OUT BOOLEAN *OnlyOneChildLeft + ); + +BOOLEAN +GtbpInsertIntoSubTree ( + PRTL_GENERIC_TABLE2 Table, + IN PGTB_TWO_THREE_NODE Node, + IN BOOLEAN NodeIsLeaf, + IN PVOID Element, + IN ULONG SplitCount, + IN PVOID *FoundElement, + OUT PGTB_TWO_THREE_NODE *ExtraNode, + OUT PGTB_TWO_THREE_LEAF *LowLeaf, + OUT PLIST_ENTRY *AllocatedNodes + ); + +ULONG +GtbpNumberOfChildren( + IN PGTB_TWO_THREE_NODE Node + ); + +VOID +GtbpGetSubTreeOfElement( + IN PRTL_GENERIC_TABLE2 Table, + IN PGTB_TWO_THREE_NODE Node, + IN PVOID Element, + OUT PGTB_TWO_THREE_NODE *SubTreeNode, + OUT ULONG *SubTree + ); + +VOID +GtbpCoalesceChildren( + IN PRTL_GENERIC_TABLE2 Table, + IN PGTB_TWO_THREE_NODE Node, + IN ULONG SubTree, + OUT BOOLEAN *OnlyOneChildLeft + ); + +VOID +GtbpSplitNode( + IN PGTB_TWO_THREE_NODE Node, + IN PGTB_TWO_THREE_NODE NodePassedBack, + IN PGTB_TWO_THREE_LEAF LowPassedBack, + IN ULONG SubTree, + IN PLIST_ENTRY AllocatedNodes, + OUT PGTB_TWO_THREE_NODE *NewNode, + OUT PGTB_TWO_THREE_LEAF *LowLeaf + ); + +PGTB_TWO_THREE_LEAF +GtbpAllocateLeafAndNodes( + IN PRTL_GENERIC_TABLE2 Table, + IN ULONG SplitCount, + OUT PLIST_ENTRY *AllocatedNodes + ); + +PGTB_TWO_THREE_NODE +GtbpGetNextAllocatedNode( + IN PLIST_ENTRY AllocatedNodes + ); + + + + +////////////////////////////////////////////////////////////////////////// +// // +// Exported Services ... // +// // +////////////////////////////////////////////////////////////////////////// + + +VOID +RtlInitializeGenericTable2 ( + PRTL_GENERIC_TABLE2 Table, + PRTL_GENERIC_2_COMPARE_ROUTINE CompareRoutine, + PRTL_GENERIC_2_ALLOCATE_ROUTINE AllocateRoutine, + PRTL_GENERIC_2_FREE_ROUTINE FreeRoutine + ) + +/*++ + +Routine Description: + + Initialize the table by initializing the corresponding + (empty) two-three tree and the extra linked-list we have + going through the tree. + + Two-three trees are described in "Data Structures And Algorithms" + by Alfred Aho, John Hopcroft, and Jeffrey Ullman (Addison Wesley + publishing). + +Arguments: + + Table - Pointer to the generic table to be initialized. This gets + typecast internally, but we export this so that we don't have to + invent another type of generic table for users to worry about. + + CompareRoutine - User routine to be used to compare to keys in the + table. + + AllocateRoutine - Used by the table package to allocate memory + when necessary. + + FreeRoutine - Used by the table package to free memory previously + allocated using the AllocateRoutine. + +Return Value: + + None. + +--*/ +{ + + + // + // Tree is empty. + // + + Table->Root = NULL; + Table->ElementCount = 0; + + Table->Compare = CompareRoutine; + Table->Allocate = AllocateRoutine; + Table->Free = FreeRoutine; + + InitializeListHead(&Table->SortOrderHead); + + return; +} + + +PVOID +RtlInsertElementGenericTable2 ( + PRTL_GENERIC_TABLE2 Table, + PVOID Element, + PBOOLEAN NewElement + ) + +/*++ + +Routine Description: + + + This function inserts an element into the table. + + If the element is successfully inserted into the table + then NewElement will be returned as TRUE and the function will + return the value passed via the Element parameter. + + If the element already exists in the table, then NewElement + is returned as FALSE and the function will return the value + of the element already found in the table. + + + The caller is responsible for ensuring that an element referenced by + the table is not modified or deallocated while it is still in the + table. + +Arguments: + + Table - Pointer to the generic table to which the Element is to + be inserted. + + Element - Pointer to the element to be entered into the table. + + NewElement - Receives TRUE if the element was added to the table. + Receives FALSE if the element collided with an element already + in the table (that is, an element with the same comparison + value already exists in the table). + + +Return Value: + + Pointer to the element inserted, or the element that was already + in the table with the same value as the one being inserted. + + If NULL is returned, then memory could not be allocated to add + the new element. + +--*/ +{ + + RTL_GENERIC_COMPARE_RESULTS + CompareResult; + + + PGTB_TWO_THREE_NODE + NodePassedBack, + NewNode, + SubTreeNode, + Node; + + PGTB_TWO_THREE_LEAF + Leaf, + LowLeaf, + LowPassedBack; + + ULONG + SplitCount, + SubTree; + + PVOID + FoundElement; + + PLIST_ENTRY + AllocatedNodes; + + BOOLEAN + NodeIsLeaf; + + + GtbpDiagPrint( INSERT, + ("GTB: Inserting Element 0x%lx into table 0x%lx\n", Element, Table)); + + // + // Except for errors, one of the following will occur: + // + // o There is no root ==> + // 1) Allocate a root and leaf + // 2) put the element in the leaf and make it the + // 3) first child of the root + // + // o There is a root with only one child ==> + // 1) If the elements are equal, return without new entry + // 2) If the new element is less, move child 1 to 2 and + // make new leaf child 1. + // 3) Otherwise element is greater, allocate it a leaf + // and make it child 2. + // + // o There is a root with at least two children ==> + // 1) If there are already 3 children, then set split + // count = 2, otherwise set it to 1. + // 2) Call normal insertion routine to insert into + // appropriate SubTree. + // 3) If there is a split needed, then establish + // a newly allocated node as the root, and make it the + // parent of the current node. Then use the normal + // split routine. + // + + + + + + // + // If empty, then create a root node and add the element. + // + + if (Table->ElementCount == 0) { + + GtbpDiagPrint( INSERT, + ("GTB: Table empty. Creating root node.\n")); + + NewNode = (PGTB_TWO_THREE_NODE) + ((*Table->Allocate)( sizeof(GTB_TWO_THREE_NODE) )); + if (NewNode == NULL) { + GtbpDiagPrint(INSERT, + ("GTB: Couldn't allocate memory for root node.\n")); + (*NewElement) = FALSE; + return( NULL ); + } + GtbpDiagPrint( INSERT, + ("GTB: New root node is: 0x%lx\n", NewNode)); + + + NewNode->ParentNode = NULL; // Doesn't have a parent. Special case. + NewNode->Control = GTB_CHILDREN_ARE_LEAVES; + NewNode->SecondChild = NULL; + NewNode->ThirdChild = NULL; + + // + // Allocate a leaf and put the element in it. + // + + Leaf = (PGTB_TWO_THREE_LEAF) + ((*Table->Allocate)( sizeof(GTB_TWO_THREE_LEAF) )); + + if (Leaf == NULL) { + GtbpDiagPrint(INSERT, + ("GTB: Couldn't allocate memory for leaf.\n")); + ((*Table->Free)( NewNode )); + (*NewElement) = FALSE; + return( NULL ); + } + + + InsertHeadList( &Table->SortOrderHead, &Leaf->SortOrderEntry ); + Leaf->Element = Element; + NewNode->FirstChild = (PGTB_TWO_THREE_NODE)Leaf; + + Table->Root = NewNode; + Table->ElementCount++; + ASSERT(Table->ElementCount == 1); + (*NewElement) = TRUE; + return(Element); + } + + + // + // We have a root with at least one child in it. + // + + if (Table->Root->SecondChild == NULL) { + + // + // The root doesn't have two children. + // If it didn't have any children it would have been + // deallocated. So, it must have a degenerate case of + // only one child. + // + + Leaf = (PGTB_TWO_THREE_LEAF)Table->Root->FirstChild; + CompareResult = (*Table->Compare)( Element, Leaf->Element ); + + if (CompareResult == GenericEqual) { + (*NewElement) = FALSE; + + GtbpDiagPrint( COLLISIONS, + ("GTB: Insertion attempt resulted in collision.\n" + " Element NOT being inserted.\n" + " Elements in table: %d\n", + Table->ElementCount)); + return( Leaf->Element ); + } + + + // + // Need a new leaf + // + + Leaf = (PGTB_TWO_THREE_LEAF) + ((*Table->Allocate)( sizeof(GTB_TWO_THREE_LEAF) )); + + if (Leaf == NULL) { + GtbpDiagPrint(INSERT, + ("GTB: Couldn't allocate memory for leaf.\n")); + (*NewElement) = FALSE; + return( NULL ); + } + Leaf->Element = Element; + + // + // it is either the first child or second + // + + if (CompareResult == GenericLessThan) { + + // + // Move the first child to be the second child and make + // a new first child leaf for the new element. + // + + InsertHeadList( &Table->SortOrderHead, &Leaf->SortOrderEntry ); + + + + Table->Root->SecondChild = Table->Root->FirstChild; + Table->Root->LowOfSecond = (PGTB_TWO_THREE_LEAF) + Table->Root->SecondChild; //it is the leaf + + Table->Root->FirstChild = (PGTB_TWO_THREE_NODE)Leaf; + + + } else { + + // + // new element is greater than existing element. + // make it the second child. + // + + InsertTailList( &Table->SortOrderHead, &Leaf->SortOrderEntry ); + + Table->Root->SecondChild = (PGTB_TWO_THREE_NODE)Leaf; + Table->Root->LowOfSecond = Leaf; + + } + + Table->ElementCount++; + ASSERT(Table->ElementCount == 2); + + (*NewElement) = TRUE; //Set return value + return(Element); + + } + + // + // Normal insertion. + // If we get an ExtraNode coming back, then we may have to + // split the root. Normally for a node with three children + // you would need to allow for one node in a split. However, + // we will need a new root as well, so allow for two new nodes. + // + + if (Table->Root->ThirdChild != NULL) { + SplitCount = 2; + } else { + SplitCount = 0; + } + + GtbpGetSubTreeOfElement( Table, Table->Root, Element, &SubTreeNode, &SubTree); + NodeIsLeaf = GtbpChildrenAreLeaves(Table->Root); + + (*NewElement) = GtbpInsertIntoSubTree ( Table, + SubTreeNode, + NodeIsLeaf, + Element, + SplitCount, + &FoundElement, + &NodePassedBack, + &LowPassedBack, + &AllocatedNodes + ); + + // + // One of several things could have happened: + // + // 1) We didn't have enough memory to add the new element. + // In this case we are done and simply return. + // + // 2) The element was added, and no-rearrangement to this + // node is needed. In this case we are done and simply + // return. + // + // 3) The element was added and caused a node to be pushed + // out of the SubTree. We have some work to do. + // + + + if ( (FoundElement == NULL) || // Insufficient memory, or + (NodePassedBack == NULL) ) { // no work for this node + + return(FoundElement); + } + + + Node = Table->Root; + if (Node->ThirdChild == NULL) { + + // + // Root doesn't yet have a third child, so use it. + // This might require shuffling the second child to the + // be the third child. + // + + if (SubTree == 2) { + + // + // NodePassedBack fell out of second SubTree and root does't + // have a third SubTree. Make that node the third SubTree. + // + + Node->ThirdChild = NodePassedBack; + Node->LowOfThird = LowPassedBack; + + } else { + + // + // Node fell out of first SubTree. + // Make the second SubTree the third SubTree and + // then make the passed back node the second SubTree. + // + + ASSERT(SubTree == 1); + + Node->ThirdChild = Node->SecondChild; + Node->LowOfThird = Node->LowOfSecond; + Node->SecondChild = NodePassedBack; + Node->LowOfSecond = LowPassedBack; + + } + } else { + + // + // Node already has three children - split it. + // Do this by setting a new parent first. + // + + NewNode = GtbpGetNextAllocatedNode( AllocatedNodes ); + ASSERT(NewNode != NULL); + + Table->Root = NewNode; + NewNode->ParentNode = NULL; + NewNode->Control = 0; + NewNode->FirstChild = Node; + NewNode->SecondChild = NULL; + NewNode->ThirdChild = NULL; + + Node->ParentNode = NewNode; + + + GtbpSplitNode( Node, + NodePassedBack, + LowPassedBack, + SubTree, + AllocatedNodes, + &NewNode, + &LowLeaf + ); + + Table->Root->SecondChild = NewNode; + Table->Root->LowOfSecond = LowLeaf; + } + + return(FoundElement); +} + + +BOOLEAN +RtlDeleteElementGenericTable2 ( + PRTL_GENERIC_TABLE2 Table, + PVOID Element + ) + +/*++ + +Routine Description: + + The function DeleteElementGenericTable2 will find and delete an element + from a generic table. If the element is located and deleted the return + value is TRUE, otherwise if the element is not located the return value + is FALSE. The user supplied input buffer is only used as a key in + locating the element in the table. + + The value of the passed element is compared to elements in the table + to determine whether or not the element is in the table. Therefore, + the Element passed in may be the address of the element in the table + to delete, or it may be an element with the same value that is not + in the table. + +Arguments: + + Table - Pointer to the table in which to (possibly) delete the + element referenced by the buffer. + + Element - Passed to the user comparasion routine. Its contents are + up to the user but one could imagine that it contains some + sort of key value. + +Return Value: + + BOOLEAN - If the table contained the Element then TRUE, otherwise FALSE. + +--*/ +{ + + RTL_GENERIC_COMPARE_RESULTS + CompareResult; + + PGTB_TWO_THREE_NODE + Node, + SubTreeNode; + + PGTB_TWO_THREE_LEAF + Leaf, + LowOfNode; + + BOOLEAN + ElementDeleted, + OnlyOneChildLeft; + + ULONG + SubTree; + + GtbpDiagPrint( DELETION, + ("GTB: Request received to delete element 0x%lx\n", Element)); + + + // + // There are the following special cases: + // + // 1) The table is empty. + // 2) The table has only one leaf + // + // Otherwise, all operations work the same. + // + + if (Table->ElementCount == 0) { + GtbpDiagPrint( DELETION, + ("GTB: No elements in table to delete.\n")); + return(FALSE); + } + + if (GtbpChildrenAreLeaves(Table->Root)) { + + + // + // See if any of the elements match the one passed in. + // If so, delete the element and shift larger elements + // to take up the free'd child's spot (unless it is the + // third child). + // + + if (Table->Root->ThirdChild != NULL) { + Leaf = (PGTB_TWO_THREE_LEAF)Table->Root->ThirdChild; + CompareResult = (*Table->Compare)( Element, Leaf->Element ); + + if (CompareResult == GenericEqual) { + + GtbpDiagPrint( DELETION, + ("GTB: Deleting child 3 (0x%lx) from root node.\n" + " Element count before deletion: %d\n", + Leaf, Table->ElementCount)); + + RemoveEntryList( &Leaf->SortOrderEntry ); + (*Table->Free)(Leaf); + Table->Root->ThirdChild = NULL; + + Table->ElementCount--; + ASSERT(Table->ElementCount == 2); + + + return(TRUE); + } + } + + // + // Try second child + // + + if (Table->Root->SecondChild != NULL) { + Leaf = (PGTB_TWO_THREE_LEAF)Table->Root->SecondChild; + CompareResult = (*Table->Compare)( Element, Leaf->Element ); + + if (CompareResult == GenericEqual) { + + GtbpDiagPrint( DELETION, + ("GTB: Deleting child 2 (0x%lx) from root node.\n" + " Element count before deletion: %d\n", + Leaf, Table->ElementCount)); + + RemoveEntryList( &Leaf->SortOrderEntry ); + (*Table->Free)(Leaf); + Table->Root->SecondChild = Table->Root->ThirdChild; + Table->Root->ThirdChild = NULL; + + Table->Root->LowOfSecond = Table->Root->LowOfThird; + + Table->ElementCount--; + ASSERT(Table->ElementCount <= 2); + + return(TRUE); + } + } + + // + // Try first child + // + + ASSERT(Table->Root->FirstChild != NULL); + Leaf = (PGTB_TWO_THREE_LEAF)Table->Root->FirstChild; + CompareResult = (*Table->Compare)( Element, Leaf->Element ); + + if (CompareResult == GenericEqual) { + + GtbpDiagPrint( DELETION, + ("GTB: Deleting child 1 (0x%lx) from root node.\n" + " Element count before deletion: %d\n", + Leaf, Table->ElementCount)); + + RemoveEntryList( &Leaf->SortOrderEntry ); + (*Table->Free)(Leaf); + Table->Root->FirstChild = Table->Root->SecondChild; + Table->Root->SecondChild = Table->Root->ThirdChild; + Table->Root->LowOfSecond = Table->Root->LowOfThird; + Table->Root->ThirdChild = NULL; + + + Table->ElementCount--; + ASSERT(Table->ElementCount <= 2); + + // + // If that was the last element, then free the root as well. + // + + if (Table->ElementCount == 0) { + (*Table->Free)(Table->Root); + Table->Root = NULL; + + GtbpDiagPrint( DELETION, + ("GTB: Deleted last element. Deleting Root node.\n")); + + } + + return(TRUE); + } + + // + // Didn't match any of the leaves + // + + GtbpDiagPrint( DELETION, + ("GTB: No matching element found on DELETE attempt.\n")); + return(FALSE); + + } + + + + + + // + // We have: + // + // - Root with at least two children + // - Root's children are not leaves. + // + + // + // Find which sub-tree the element might be in. + // + + Node = Table->Root; + GtbpGetSubTreeOfElement( Table, Node, Element, &SubTreeNode, &SubTree ); + + GtbpDeleteFromSubTree( Table, + SubTreeNode, + Element, + &LowOfNode, + &ElementDeleted, + &OnlyOneChildLeft + ); + + + // + // If we deleted an entry from either the second or third + // subtree, then we may need to set a new LowOfXxx value. + // If it was the first subtree, then we simply have to return + // the LowLeaf value we received. + // + + if (LowOfNode != 0) { + if (SubTree == 2) { + Node->LowOfSecond = LowOfNode; + } else if (SubTree == 3) { + Node->LowOfThird = LowOfNode; + } + + } + + + // + // If the SubTreeNode has only one child left, then some + // adjustments are going to be necessary. Otherwise, + // we are done. + // + + if (OnlyOneChildLeft) { + + GtbpDiagPrint( DELETION, + ("GTB: Only one child left in 0x%lx\n", SubTreeNode)); + + // + // We are at the root and one of our children has only one + // child. Re-shuffle our kid's kids. + // + + GtbpCoalesceChildren( Table, + Node, + SubTree, + &OnlyOneChildLeft + ); + + // + // After coellescing our children, the root may have only one child + // left. Since we are the root node, we can't pass responsibility + // for fixing this problem to our caller. + // + + if (OnlyOneChildLeft) { + + GtbpDiagPrint( DELETION, + ("GTB: Root has only one child. \n" + " Replacing root with child: 0x%lx\n", Node->FirstChild)); + Table->Root = Table->Root->FirstChild; + Table->Root->ParentNode = NULL; + + (*Table->Free)((PVOID)Node); + } + } + + return(ElementDeleted); + +} + + +PVOID +RtlLookupElementGenericTable2 ( + PRTL_GENERIC_TABLE2 Table, + PVOID Element + ) + +/*++ + +Routine Description: + + + The function LookupElementGenericTable2 will find an element in a + generic table. If the element is located the return value is a + pointer to the user defined structure associated with the element, + otherwise if the element is not located the return value is NULL. + The user supplied input buffer is only used as a key in locating + the element in the table. + + +Arguments: + + Table - Pointer to the users generic table. + + Element - Used for the comparison. + +Return Value: + + PVOID - returns a pointer to the user data if found, otherwise NULL. + +--*/ + +{ + RTL_GENERIC_COMPARE_RESULTS + CompareResult; + + PGTB_TWO_THREE_NODE + Node; + + PGTB_TWO_THREE_LEAF + Leaf; + + ULONG + SubTree; + + + GtbpDiagPrint( LOOKUP, + ("GTB: Looking up element 0x%lx in table 0x%lx\n", + Element, Table)); + // + // If the table is empty, then no possible match. + // + + if (Table->ElementCount == 0) { + GtbpDiagPrint( LOOKUP, + ("GTB: Element not found. No elements in table.\n")); + return(NULL); + } + + Node = Table->Root; + + // + // traverse the tree until we reach a node that has leaves as + // children. + // + // We don't need to use recursion here because there + // is no tree re-structuring necessary. That is, there + // is no need to perform any operations back up the tree + // once we find the element, so it is much more efficient + // not to use recursion (which uses lots of push, call, + // pop, and ret instructions rather than short loop + // termination tests). + // + + while (!GtbpChildrenAreLeaves(Node)) { + GtbpGetSubTreeOfElement( Table, Node, Element, &Node, &SubTree ); + } + + // + // We are at the node which "might" contain the element. + // See if any of the children match. + // + + // + // Try first child + // + + Leaf = (PGTB_TWO_THREE_LEAF)Node->FirstChild; + CompareResult = (*Table->Compare)( Element, Leaf->Element ); + + if (CompareResult == GenericEqual) { + GtbpDiagPrint( LOOKUP, + ("GTB: Element found: 2nd child (0x%lx) of node 0x%lx\n", + Leaf, Node)); + return(Leaf->Element); + } + + // + // Try second child + // + + if (Node->SecondChild != NULL) { // Must allow for Root node case + Leaf = (PGTB_TWO_THREE_LEAF)Node->SecondChild; + CompareResult = (*Table->Compare)( Element, Leaf->Element ); + + if (CompareResult == GenericEqual) { + GtbpDiagPrint( LOOKUP, + ("GTB: Element found: 2nd child (0x%lx) of node 0x%lx\n", + Leaf, Node)); + return(Leaf->Element); + } + } + // + // Try third child + // + + if (Node->ThirdChild != NULL) { + Leaf = (PGTB_TWO_THREE_LEAF)Node->ThirdChild; + CompareResult = (*Table->Compare)( Element, Leaf->Element ); + + if (CompareResult == GenericEqual) { + GtbpDiagPrint( LOOKUP, + ("GTB: Element found: 3rd child (0x%lx) of node 0x%lx\n", + Leaf, Node)); + return(Leaf->Element); + } + } + + + GtbpDiagPrint( LOOKUP, + ("GTB: Element NOT found in node 0x%lx\n", Node)); + + return(NULL); + +} + + +PVOID +RtlEnumerateGenericTable2 ( + PRTL_GENERIC_TABLE2 Table, + PVOID *RestartKey + ) + +/*++ + +Routine Description: + + + The function EnumerateGenericTable2 will return to the + caller, one-by-one, the elements of a table (in sorted order). + The return value is a pointer to the user defined structure + associated with the element. + + The input parameter RestartKey indicates where the enumeration should + proceed from. If there are no more new elements to return the return + value is NULL. + + A RestartKey value of NULL will cause the enumeration to proceed + from the beginning of the list. + + As an example of its use, to enumerate all of the elements in a table + the user would write: + + RestartKey = NULL; + for (ptr = EnumerateGenericTable2(Table, &RestartKey); + ptr != NULL; + ptr = EnumerateGenericTable2(Table, &RestartKey)) { + : + } + + + If you wish to initiate an enumeration at a point other than the first + entry, you may use RestartKeyByIndexGenericTable2() or + RestartKeyByValueGenericTable2(). If a RestartKey + for the I'th entry was obtained via RestartKeyByIndexGenericTable2(), + then passing that RestartKey to this routine will cause the (I+1)th + element to be returned. If a RestartKey was obtained matching + a value passed to RestartKeyByValueGenericTable2(), then passing + that RestartKey to this routine will cause the entry with the + next higher value to be returned. + + ! WARNING ! + A RestartKey value may become invalid and cause an access violation + if any entries have been removed from the table. If enumeration + is to be carried out and it is unknown whether or not the table + contents have changed, it is best to obtain a RestartKey using + RestartKeyByIndexGenericTable2() or + RestartKeyByValueGenericTable2(). + + +Arguments: + + Table - Pointer to the generic table to enumerate. + + RestartKey - Upon call, indicates where the enumeration is to + begin. Upon return, receives context that may be used to + continue enumeration in a successive call. NULL indicates + enumeration should start at the beginning of the table. + A return value of NULL indicates the last entry has been + returned. + +Return Value: + + PVOID - Pointer to the next enumerated element or NULL. + NULL is returned if the entire table has already been + enumerated. + +--*/ + +{ + PLIST_ENTRY + ListEntry; + + PGTB_TWO_THREE_LEAF + Leaf; + + ListEntry = (PLIST_ENTRY)(*RestartKey); + + // + // The restart key is a pointer to our leaf element. + // Since all leaves are linked together in the SortOrderList, + // this makes it really trivial to return the next element. + // + + if (ListEntry == NULL) { + ListEntry = &Table->SortOrderHead; //Point to previous element + } + + // + // RestartKey pointed to the last enumerated leaf. + // Advance to the new one. + // + + ListEntry = ListEntry->Flink; + + // + // See if we have reached the end of the list + // + + if (ListEntry == &Table->SortOrderHead) { + (*RestartKey) = NULL; + return(NULL); + } + + // + // Otherwise, return the address of the element from this leaf. + // + + Leaf = (PGTB_TWO_THREE_LEAF)((PVOID)ListEntry); + + (*RestartKey) = (PVOID)Leaf; + return(Leaf->Element); + +} + + + +PVOID +RtlRestartKeyByIndexGenericTable2( + PRTL_GENERIC_TABLE2 Table, + ULONG I, + PVOID *RestartKey + ) + +/*++ + +Routine Description: + + + The function RestartKeyByIndexGenericTable2 will return a RestartKey + which may then be passed to EnumerateGenericTable2() to perform + an enumeration of sorted elements following the I'th sorted element + (zero based). + + This routine also returns a pointer to the I'th element. + + I = 0 implies restart at the second sorted element. + + I = (RtlNumberGenericTable2Elements(Table)-1) will return the last + sorted element in the generic table. + + Values of I greater than (NumberGenericTableElements(Table)-1) + will return NULL and the returned RestartKey will cause an + enumeration to be performed from the beginning of the sorted list + if passed to EnumerateGenericTable2(). + + WARNING - You may be tempted to use this routine, passing + first 0, then 1, then 2, et cetera, to perform + enumerations. DON'T. This is a very expensive + operation compared to the enumeration call. + +Arguments: + + Table - Pointer to the generic table. + + I - Indicates the point following which you wish to be able + to resume enumeration. For example, if you pass 7 for I, + then a RestartKey will be returned that continues enumeration + at the 8th element (skipping elements 0 through 6). + + RestartKey - Receives context that may be used to continue + enumeration in a successive call. If there is no I'th + element, then NULL is returned. + + Return Value: + + PVOID - Pointer to the I'th Element. If there is no I'th element, + then returns NULL. + +--*/ + +{ + PLIST_ENTRY + ListEntry; + + PGTB_TWO_THREE_LEAF + Leaf; + + ULONG + i; + + if (I >= Table->ElementCount) { + (*RestartKey) = NULL; + return(NULL); + } + + // + // Point to the first entry on the list. + // + + ListEntry = Table->SortOrderHead.Flink; + + // + // Move to the desired index + // + + for (i=0; i<I; i++) { + ListEntry = ListEntry->Flink; + } + + + // + // Found the I'th element . + // + + (*RestartKey) = (PVOID)ListEntry; + Leaf = (PGTB_TWO_THREE_LEAF)((PVOID)ListEntry); + return(Leaf->Element); + +} + + +PVOID +RtlRestartKeyByValueGenericTable2( + PRTL_GENERIC_TABLE2 Table, + PVOID Element, + PVOID *RestartKey + ) + +/*++ + +Routine Description: + + + The function RestartKeyByValueGenericTable2 will return a RestartKey + which may then be passed to EnumerateGenericTable2() to perform + an enumeration of sorted elements. The RestartKey will have a + value that will cause the enumeration to begin starting with + the first element whose value is greater than the passed in element + value. + + There does not have to be an element in the tree whose value + exactly matches the passed in value. A pointer to the element + with the largest value that is less than or equal to the passed + in value will be returned and serve as the continuation point + for the enumeration. + + + +Arguments: + + Table - Pointer to the generic table. + + Value - points to an element whose value indicates where you + wish enumeration to continue. + + RestartKey - Receives context that may be used to continue + enumeration in a successive call. + + Return Value: + + PVOID - Pointer to the element with the largest value less than + or equal to the element value passed in. If there are no + elements in the table less than or equal to the passed value, + then a value of NULL will be returned. + +--*/ + +{ + RTL_GENERIC_COMPARE_RESULTS + CompareResult; + + PGTB_TWO_THREE_NODE + Node; + + PGTB_TWO_THREE_LEAF + Leaf; + + ULONG + Children, + SubTree; + + BOOLEAN + LargestElementPath; + + // + // This routine is real similar to LookupElement + // + + // + // handle the special "table is empty" case. + // + + if (Table->ElementCount == 0) { + (*RestartKey) = NULL; + return(NULL); + } + + + Node = Table->Root; + + // + // traverse the tree until we reach a node that has leaves as + // children. + // + // We don't need to use recursion here because there + // is no tree re-structuring necessary. That is, there + // is no need to perform any operations back up the tree + // once we find the element, so it is much more efficient + // not to use recursion (which uses lots of push, call, + // pop, and ret instructions rather than short loop + // termination tests). + // + + LargestElementPath = TRUE; + while (!GtbpChildrenAreLeaves(Node)) { + Children = GtbpNumberOfChildren( Node ); + GtbpGetSubTreeOfElement( Table, Node, Element, &Node, &SubTree ); + if (Children > SubTree) { //did we take the highest value path? + LargestElementPath = FALSE; + } + } + + Children = GtbpNumberOfChildren(Node); + + // + // We are at the node which "might" contain the element. + // See if any of the children match. + // + // MUST compare 3rd, then 2nd, then 1st child !! + // + + // + // Try third child... + // If we are evaluating the largest element in the + // table, then the RestartKey will be set to continue + // at the beginning of the table. Otherwise, it is + // set to continue from here. + // + + if (Children == 3) { + Leaf = (PGTB_TWO_THREE_LEAF)Node->ThirdChild; + CompareResult = (*Table->Compare)( Leaf->Element, Element ); + + if ( (CompareResult == GenericEqual) || + (CompareResult == GenericLessThan) ) { + if (LargestElementPath && (Children == 3)) { + (*RestartKey) = NULL; // Restart at beginning of list + } else { + (*RestartKey) = (PVOID)(Leaf); // Restart here + } + return(Leaf->Element); + } + } + + // + // Try second child + // + + if (Children >= 2) { // Must allow for Root node case + Leaf = (PGTB_TWO_THREE_LEAF)Node->SecondChild; + CompareResult = (*Table->Compare)( Leaf->Element, Element ); + + if ( (CompareResult == GenericEqual) || + (CompareResult == GenericLessThan) ) { + if (LargestElementPath && (Children == 2)) { + (*RestartKey) = NULL; // Restart at beginning of list + } else { + (*RestartKey) = (PVOID)(Leaf); // Restart here + } + return(Leaf->Element); + } + } + + // + // Try first child + // + + Leaf = (PGTB_TWO_THREE_LEAF)Node->FirstChild; + CompareResult = (*Table->Compare)( Leaf->Element, Element ); + + if ( (CompareResult == GenericEqual) || + (CompareResult == GenericLessThan) ) { + if (LargestElementPath && (Children == 1)) { + (*RestartKey) = NULL; // Restart at beginning of list + } else { + (*RestartKey) = (PVOID)(Leaf); // Restart here + } + return(Leaf->Element); + } + + + + (*RestartKey) = NULL; + return(NULL); +} + + +ULONG +RtlNumberElementsGenericTable2( + PRTL_GENERIC_TABLE2 Table + ) + +/*++ + +Routine Description: + + The function NumberGenericTableElements returns a ULONG value + which is the number of generic table elements currently inserted + in the generic table. + + +Arguments: + + Table - Pointer to the generic table. + + + Return Value: + + ULONG - The number of elements in the table. + +--*/ + +{ + return Table->ElementCount; +} + + +BOOLEAN +RtlIsGenericTable2Empty ( + PRTL_GENERIC_TABLE2 Table + ) +/*++ + +Routine Description: + + The function IsGenericTableEmpty will return to the caller TRUE if + the generic table is empty (i.e., does not contain any elements) + and FALSE otherwise. + + +Arguments: + + Table - Pointer to the generic table. + + + Return Value: + + BOOLEAN - True if table is empty, otherwise FALSE. + +--*/ + +{ + return (Table->ElementCount == 0); +} + + + +////////////////////////////////////////////////////////////////////////// +// // +// Internal (private) Services ... // +// // +////////////////////////////////////////////////////////////////////////// + + +VOID +GtbpDeleteFromSubTree ( + IN PRTL_GENERIC_TABLE2 Table, + IN PGTB_TWO_THREE_NODE Node, + IN PVOID Element, + OUT PGTB_TWO_THREE_LEAF *LowOfNode, + OUT BOOLEAN *ElementDeleted, + OUT BOOLEAN *OnlyOneChildLeft + ) + +/*++ + +Routine Description: + + Delete an element from a SubTree. + + +Arguments: + + Table - Points to the table. This is needed for comparison + and memory-free routine. + + Node - Points to the child node within which the element to + delete resides (if it is in the tree at all). + + Element - points to an element. We are to delete any element + found to be equal to this element. + + LowOfNode - If the first child of Node isn't changed, then + a zero will be returned to this parameter, signifying that + the caller doesn't have to worry about updating LowOfXxx values. + However, if the first child of Node does change, then this + value will point to the new Low Leaf for the node's subtrees. + + ElementDeleted - Receives a boolean value indicating whether or + not an element was actually deleted. TRUE is returned if + an element was deleted. FALSE is returned if no element + was deleted. + + OnlyOneChildLeft - Receives a boolean value indicating whether or + not ChildNode was reduced to having only a single child. + TRUE indicates ChildNode now has only one child. + FALSE indicates ChildNode has at least two children. + +Return Value: + + None. + +--*/ + +{ + RTL_GENERIC_COMPARE_RESULTS + CompareResult; + + PGTB_TWO_THREE_NODE + SubTreeNode; + + PGTB_TWO_THREE_LEAF + Leaf; + + ULONG + SubTree; + + (*LowOfNode) = 0; // Default is no change + (*OnlyOneChildLeft) = FALSE; // Default return value + + + // + // If we are a parent of leaves, then we can look for an + // element to delete. Otherwise, just find the subtree + // to continue or search in and recurse. + // + + if (GtbpChildrenAreLeaves(Node)) { + + (*ElementDeleted) = FALSE; // Default return value + + // + // See if any of the elements match the one passed in. + // If so, delete the element and shift larger elements + // to take up the free'd child's spot (unless it is the + // third child). + // + + if (Node->ThirdChild != NULL) { + Leaf = (PGTB_TWO_THREE_LEAF)Node->ThirdChild; + CompareResult = (*Table->Compare)( Element, Leaf->Element ); + + if (CompareResult == GenericEqual) { + + GtbpDiagPrint( DELETION, + ("GTB: Deleting 3rd child (0x%lx) of node 0x%lx\n" + " ElementCount before deletion: %d\n", + Leaf, Node, Table->ElementCount)); + + RemoveEntryList( &Leaf->SortOrderEntry ); + (*Table->Free)(Leaf); + Node->ThirdChild = NULL; + + Table->ElementCount--; + + (*ElementDeleted) = TRUE; + return; + } + } + + // + // Try second child + // + + Leaf = (PGTB_TWO_THREE_LEAF)Node->SecondChild; + CompareResult = (*Table->Compare)( Element, Leaf->Element ); + + if (CompareResult == GenericEqual) { + + GtbpDiagPrint( DELETION, + ("GTB: Deleting 2nd child (0x%lx) of node 0x%lx\n" + " ElementCount before deletion: %d\n", + Leaf, Node, Table->ElementCount)); + + RemoveEntryList( &Leaf->SortOrderEntry ); + (*Table->Free)(Leaf); + Node->SecondChild = Node->ThirdChild; + Node->LowOfSecond = Node->LowOfThird; + Node->ThirdChild = NULL; + + + // + // If we are down to the last element, let that + // be known. + // + + if (Node->SecondChild == NULL) { + GtbpDiagPrint( DELETION, + ("GTB: Only one child left in node (0x%lx).\n", + Node)); + (*OnlyOneChildLeft) = TRUE; + } + + Table->ElementCount--; + (*ElementDeleted) = TRUE; + return; + } + + // + // Try first child + // + + Leaf = (PGTB_TWO_THREE_LEAF)Node->FirstChild; + CompareResult = (*Table->Compare)( Element, Leaf->Element ); + + if (CompareResult == GenericEqual) { + + GtbpDiagPrint( DELETION, + ("GTB: Deleting 1st child (0x%lx) of node 0x%lx\n" + " ElementCount before deletion: %d\n", + Leaf, Node, Table->ElementCount)); + + RemoveEntryList( &Leaf->SortOrderEntry ); + (*Table->Free)(Leaf); + Node->FirstChild = Node->SecondChild; + (*LowOfNode) = Node->LowOfSecond; + + Node->SecondChild = Node->ThirdChild; + Node->LowOfSecond = Node->LowOfThird; + + Node->ThirdChild = NULL; + + + // + // If we are down to the last element, let that + // be known. + // + + if (Node->SecondChild == NULL) { + GtbpDiagPrint( DELETION, + ("GTB: Only one child left in node (0x%lx).\n", + Node)); + (*OnlyOneChildLeft) = TRUE; + } + + Table->ElementCount--; + (*ElementDeleted) = TRUE; + return; + } + + // + // Didn't match any of the leaves + // + + GtbpDiagPrint( DELETION, + ("GTB: No matching element found on DELETE attempt.\n")); + + return; // Default value already set + } + + // + // Find a subtree to continue our search... + // + + GtbpGetSubTreeOfElement( Table, Node, Element, &SubTreeNode, &SubTree ); + + GtbpDeleteFromSubTree( Table, + SubTreeNode, + Element, + LowOfNode, + ElementDeleted, + OnlyOneChildLeft + ); + + + // + // If we deleted an entry from either the second or third + // subtree, then we may need to set a new LowOfXxx value. + // If it was the first subtree, then we simply have to return + // the LowLeaf value we received. + // + + if ((*LowOfNode) != 0) { + if (SubTree == 2) { + Node->LowOfSecond = (*LowOfNode); + (*LowOfNode) = NULL; + } else if (SubTree == 3) { + Node->LowOfThird = (*LowOfNode); + (*LowOfNode) = NULL; + } + } + + + // + // If the SubTreeNode has only one child left, then some + // adjustments are going to be necessary. Otherwise, + // we are done. + // + + if ((*OnlyOneChildLeft)) { + + GtbpDiagPrint( DELETION, + ("GTB: Only one child left in 0x%lx\n", SubTreeNode)); + + // + // One of our children has only one child. + // Re-shuffle our kid's kids. + // + + GtbpCoalesceChildren( Table, + Node, + SubTree, + OnlyOneChildLeft + ); + } + + return; +} + + +BOOLEAN +GtbpInsertIntoSubTree ( + PRTL_GENERIC_TABLE2 Table, + IN PGTB_TWO_THREE_NODE Node, + IN BOOLEAN NodeIsLeaf, + IN PVOID Element, + IN ULONG SplitCount, + IN PVOID *FoundElement, + OUT PGTB_TWO_THREE_NODE *ExtraNode, + OUT PGTB_TWO_THREE_LEAF *LowLeaf, + OUT PLIST_ENTRY *AllocatedNodes + ) + +/*++ + +Routine Description: + + Insert an element into the SubTree specified by Node. + + Special note: + + if FoundElement is returned as NULL, that means we + couldn't allocate memory to add the new element. + +Arguments: + + Table - Points to the table being inserted into. This is needed + for its allocation routine. + + + Node - Points to the root node of the SubTree into + which the element is to be inserted. + + NodeIsLeaf - TRUE if the node passed in is a leaf. FALSE + if it is an internal node. + + Element - Points to the element to be inserted. + + SplitCount - indicates how many nodes have been traversed since + a node with only two children. When inserting a new element + that causes nodes to be split, this will indicate how many + nodes will split. This allows all memory that will be required + to split nodes to be allocated at the very bottom routine + (before any changes to the tree are made). See the description + of the AllocatedNodes parameter for more information. + + FoundElement - Receives a pointer to the element that + was either inserted, or one already in the table + but found to match the one being inserted. + If null is returned, then not enough memory could be + allocated to insert the new element. + + ExtraNode - If it was necessary to create a new node to + accomodate the insertion, then ExtraNode will receive + a pointer to that node, otherwise ExtraNode will receive + NULL. + + LowLeaf - This value points to the lowest value leaf of the + SubTree starting at Node. + + AllocatedNodes - This is a tricky parameter. We have the problem + that when we insert an element in the tree, we may need to + allocate additional internal nodes further up the tree as + we return out of our recursive calls. We must avoid the + situation where we start making changes to the tree only to + find we can't allocate memory to re-arrange higher levels of + the tree. To accomodate this situation, we always allocate + all the nodes we will need at the very bottom of the call + chain and pass back a linked list of GTB_TWO_THREE_NODEs using + this parameter. We know how many nodes we will need to + allocate because it is the number of nodes between the leaf + and the lowest level node in the path between the leaf and the + root that has only 2 children. That is, all nodes directly + above the leaf that have 3 children will need to be split. + Example: + + 3 + / | \ + +-----+ | +---- + Won't split --> 2 ... ... + / | + +-----+ | + ... 3 <-- Will split + / | \ + +-----+ | +----+ + ... 3 <--- Will split + / | \ + +-----+ | +----+ + Leaf Leaf Leaf <- Add fourth leaf here. + + Adding a fourth leaf where indicated will cause a split at the + two nodes indicated. So, you can see that keeping a count of + the nodes with three children since the last encountered node + with only two children will tell us how many nodes will split. + + + + + + + +Return Value: + + TRUE - if element was added. + FALSE - if element not added (due to collision or out-of-memory) + +--*/ + +{ + RTL_GENERIC_COMPARE_RESULTS + CompareResult; + + ULONG + SubTree; // To track which SubTree an element is being placed in. + + + PGTB_TWO_THREE_NODE + SubTreeNode, + NodePassedBack; + + + PGTB_TWO_THREE_LEAF + NewLeaf, + LowPassedBack; + + BOOLEAN + Inserted, + SubNodeIsLeaf; + + + // + // Don't have an extra node to pass back yet. + // + + (*ExtraNode) = NULL; + + + // + // We are either at a leaf, or an internal node. + // + + if (NodeIsLeaf) { + + // + // Typecast the Node into a leaf + // + + PGTB_TWO_THREE_LEAF Leaf = (PGTB_TWO_THREE_LEAF)((PVOID)Node); + + // + // See if the value matches. + // + + CompareResult = (*Table->Compare)( Element, Leaf->Element ); + + if (CompareResult == GenericEqual) { + (*LowLeaf) = Leaf; + (*FoundElement) = Leaf->Element; + + GtbpDiagPrint( COLLISIONS, + ("GTB: Insertion attempt resulted in collision.\n" + " Element NOT being inserted.\n" + " Elements in table: %d\n", + Table->ElementCount)); + + return(FALSE); + } //end_if equal + + // + // The new element isn't in the tree. + // Allocate a new leaf for it. + // + + NewLeaf = GtbpAllocateLeafAndNodes( Table, SplitCount, AllocatedNodes ); + if (NewLeaf == NULL) { + + // + // The following (unusual) return value indicates we + // couldn't allocate memory to add the entry into the + // tree. + // + + (*FoundElement) = NULL; + return(FALSE); + + } //end_if (NewLeaf == NULL) + + switch (CompareResult) { + + case GenericLessThan: { + + // + // Move the original element into the new leaf. Notice + // that the SortOrderEntry of the existing leaf is + // still in the right place in the linked-list, even + // though the leaf now points at a different element. + // + + NewLeaf->Element = Leaf->Element; + Leaf->Element = Element; + + break; + } //end_case + + case GenericGreaterThan: { + + // + // The new element does not supplant the existing element. + // Put it in the new leaf. + // + + NewLeaf->Element = Element; + break; + } //end_case + + + } //end_switch + + // + // At this point, the lower-value element is in Leaf + // and the higher-value element is in NewLeaf. The + // caller is responsible to putting NewLeaf someplace + // else in the tree. + // + + // + // Now link the new leaf into our sort-order list. + // The new leaf immediately follows our existing leaf, + // regardless of which element is in the new leaf (original + // or new element). + // + + InsertHeadList(&Leaf->SortOrderEntry, &NewLeaf->SortOrderEntry); + Table->ElementCount++; // Increment the element count + + (*ExtraNode) = (PGTB_TWO_THREE_NODE)((PVOID)NewLeaf); + (*LowLeaf) = NewLeaf; + (*FoundElement) = Element; + + return(TRUE); + + } //end_if NodeIsLeaf + + // + // Node is internal (not a leaf) + // + + // + // See if we should re-set or increment the SplitCount. + // + + if (Node->ThirdChild == NULL) { + SplitCount = 0; + } else { + SplitCount += 1; + } + + GtbpGetSubTreeOfElement( Table, Node, Element, &SubTreeNode, &SubTree); + SubNodeIsLeaf = GtbpChildrenAreLeaves(Node); + + Inserted = GtbpInsertIntoSubTree ( Table, + SubTreeNode, + SubNodeIsLeaf, + Element, + SplitCount, + FoundElement, + &NodePassedBack, + &LowPassedBack, + AllocatedNodes + ); + + // + // One of several things could have happened: + // + // 1) We didn't have enough memory to add the new element. + // In this case we are done and simply return. + // + // 2) The element was added, and no-rearrangement to this + // node is needed. In this case we are done and simply + // return. + // + // 3) The element was added and caused a leaf to be pushed + // out of the SubTree. We have some work to do. + // + + if ( (FoundElement == NULL) || // Insufficient memory, or + (NodePassedBack == NULL) ) { // no work for this node + return(Inserted); + } + + if (Node->ThirdChild == NULL) { + + if (!GtbpChildrenAreLeaves(Node)) { + NodePassedBack->ParentNode = Node; + } + + // + // Node doesn't yet have a third child, so use it. + // This might require shuffling the second child to the + // be the third child. + // + + if (SubTree == 2) { + + // + // Node fell out of second SubTree and we don't have a + // third SubTree. Make that node the third SubTree. + // + + Node->ThirdChild = NodePassedBack; + Node->LowOfThird = LowPassedBack; + + } else { + + // + // Node fell out of first SubTree. + // Make the second SubTree the third SubTree and + // then make the passed back node the second SubTree. + // + + ASSERT(SubTree == 1); + + Node->ThirdChild = Node->SecondChild; + Node->LowOfThird = Node->LowOfSecond; + Node->SecondChild = NodePassedBack; + Node->LowOfSecond = LowPassedBack; + + // + // + + } + } else { + + // + // Node already has three children - split it. + // + + GtbpSplitNode( Node, + NodePassedBack, + LowPassedBack, + SubTree, + (*AllocatedNodes), + ExtraNode, + LowLeaf + ); + + } + + return(Inserted); +} + + +ULONG +GtbpNumberOfChildren( + IN PGTB_TWO_THREE_NODE Node + ) + +/*++ + +Routine Description: + + Return the number of children of a specified node. + +Arguments: + + Node - points to the node whose children are to be counted. + +Return Values: + + 0, 1, 2, or 3. + +--*/ +{ + if (Node->ThirdChild != NULL) { + return(3); + } + if (Node->SecondChild != NULL) { + return(2); + } + if (Node->FirstChild != NULL) { + return(1); + } + return(0); + +} + + +VOID +GtbpGetSubTreeOfElement( + IN PRTL_GENERIC_TABLE2 Table, + IN PGTB_TWO_THREE_NODE Node, + IN PVOID Element, + OUT PGTB_TWO_THREE_NODE *SubTreeNode, + OUT ULONG *SubTree + ) + +/*++ + +Routine Description: + + Find which SubTree of Node that Element might be in (or should be + in, if being inserted). + +Arguments: + + Table - Points to the table This is needed for its comparison routine. + + Node - Is the node, one of whose SubTrees is to be chosen as the + subtree in which Element could/should reside. + + Element - is the element we are interested in placing or locating. + + SubTreeNode - Receives a pointer to the node of the SubTree in + which the element could/should reside. + + SubTree - Receives the index (1, 2, or 3) of the subtree of Node + in which the element could/should reside. + +Return Values: + + None. + +--*/ +{ + RTL_GENERIC_COMPARE_RESULTS + CompareResult; + + CompareResult = (*Table->Compare)( Element, Node->LowOfSecond->Element ); + + if (CompareResult == GenericLessThan) { + + (*SubTree) = 1; + (*SubTreeNode) = Node->FirstChild; + + } else { + + // + // default to the second subtree, but + // if there is a subtree we may change it. + // + + (*SubTree) = 2; + (*SubTreeNode) = Node->SecondChild; + + if (Node->ThirdChild != NULL) { + + CompareResult = (*Table->Compare)( Element, Node->LowOfThird->Element ); + if ( (CompareResult == GenericGreaterThan) || + (CompareResult == GenericEqual) ) { + + (*SubTree) = 3; + (*SubTreeNode) = Node->ThirdChild; + } + } + } + + return; + +} + + + +VOID +GtbpCoalesceChildren( + IN PRTL_GENERIC_TABLE2 Table, + IN PGTB_TWO_THREE_NODE Node, + IN ULONG SubTree, + OUT BOOLEAN *OnlyOneChildLeft + ) + +/*++ + +Routine Description: + + This routine is called following a deletion that leaves a child + node with only one child of its own. That is, a child of the + Node parameter has only one child. The SubTree parameter indicates + which child of Node has only one child. + + + + +Arguments: + + Table - Points to the table. + + Node - Is the node, one of whose children has only one child. + + NOTE: The ParentNode field of this node must be valid. + The Low values of ParentNode will be referenced. + + SubTree - Indicates which child of Node (1, 2, or 3) has only one + child. + + OnlyOneChildLeft - Receives a boolean indicating whether or not + Node itself has been left with only one child due to the + coalescing. + +Return Values: + + None. + +--*/ +{ + PGTB_TWO_THREE_NODE + A, + B, + C; + + (*OnlyOneChildLeft) = FALSE; // Default return value + + // + // For the following discussion, using the following: + // + // N is the parent node + // S is the node which has only one child + // (S is a child of N) + // + // A is the first child of N + // B is the second child of N + // C is the third child of N + // + // If S is the first child of N (meaning S is A) + // then: + // + // if B has three children (let A adopt the smallest) + // then: + // + // Move B(1) to A(2) + // Move B(2) to B(1) + // Move B(3) to B(2) + // + // else (B has two children) + // + // (move the orphan into B) + // Move B(2) to B(3) + // Move B(1) to B(2) + // Move A(1) to B(1) + // + // Free A + // Make B the first child of N + // if (C is a real child) + // then: + // Make C the second child of N + // else (N only has one child now) + // (*OnlyOneChildLeft) = TRUE; + // + // else if S is the second child of N (meaning S is B) + // then: + // + // if A has three children + // then: + // Move B(1) to B(2) + // Move A(3) to B(1) + // + // else if C exists and has three children + // then: + // + // Move C(1) to B(2) + // Move C(2) to C(1) + // Move C(3) to C(2) + // + // else: (no other child of N has three children) + // + // (Move the orphan into A) + // Move B(1) to A(3) + // + // Free B + // if (C is a real child) + // then: + // Make C the second child of N + // else: (N only has one child now) + // (*OnlyOneChildLeft) = TRUE; + // + // else: (S is the third child of N (meaning S is C)) + // + // if B has three children + // then: + // (move one into C) + // Move C(1) to C(2) + // Move B(3) to C(1) + // + // else: (B only has two children) + // + // (move the orphan into B) + // Move C(1) to B(3) + // Free C + // Wow! + + + A = Node->FirstChild; + B = Node->SecondChild; + C = Node->ThirdChild; + + + // + // SubTree indicates which child has the orphan. + // + + if (SubTree == 1) { + + // + // First child has the orphan + // + + if (B->ThirdChild != NULL) { + + // (B has three children - let A adopt the smallest) + // + // Move B(1) to A(2) + // Move B(2) to B(1) + // Move B(3) to B(2) + // + + A->SecondChild = B->FirstChild; + A->LowOfSecond = Node->LowOfSecond; + + B->FirstChild = B->SecondChild; + Node->LowOfSecond = B->LowOfSecond; + + B->SecondChild = B->ThirdChild; + B->LowOfSecond = B->LowOfThird; + B->ThirdChild = NULL; + + } else { + + // + // (B has two children) + // + // (move the orphan into B) + // Move B(2) to B(3) + // Move B(1) to B(2) + // Move A(1) to B(1) + // + + B->ThirdChild = B->SecondChild; + B->LowOfThird = B->LowOfSecond; + + B->SecondChild = B->FirstChild; + B->LowOfSecond = Node->LowOfSecond; + + B->FirstChild = A->FirstChild; + //Node->LowOfSecond = Node->LowOfFirst; // This gets moved back in a few steps + + // Free A + // Make B the first child of N + // if (C is a real child) + // then: + // Make C the second child of N + // else (N only has one child now) + // (*OnlyOneChildLeft) = TRUE; + // + + (*Table->Free)(A); + Node->FirstChild = B; + //Node->LowOfFirst = Node->LowOfSecond; // See comment a few lines up + + if (C != NULL) { + Node->SecondChild = C; + Node->LowOfSecond = Node->LowOfThird; + Node->ThirdChild = NULL; + } else { + Node->SecondChild = NULL; + (*OnlyOneChildLeft) = TRUE; + } + } + + + } else if (SubTree == 2) { + + // + // Second child has the orphan + // + + if (A->ThirdChild != NULL) { + + // + // (A has three children) + // + // Move B(1) to B(2) + // Move A(3) to B(1) + // + + B->SecondChild = B->FirstChild; + B->LowOfSecond = Node->LowOfSecond; + + B->FirstChild = A->ThirdChild; + Node->LowOfSecond = A->LowOfThird; + A->ThirdChild = NULL; + + } else { + + if (C != NULL && + C->ThirdChild != NULL) { + + // + // (C exists and has three children) + // (move the smallest into B) + // + // Move C(1) to B(2) + // Move C(2) to C(1) + // Move C(3) to C(2) + // + + B->SecondChild = C->FirstChild; + B->LowOfSecond = Node->LowOfThird; + + C->FirstChild = C->SecondChild; + Node->LowOfThird = C->LowOfSecond; + + C->SecondChild = C->ThirdChild; + C->LowOfSecond = C->LowOfThird; + C->ThirdChild = NULL; + + + + + + } else { + + // + // (no other child of N has three children) + // (Move the orphan into A) + // + // Move B(1) to A(3) + // + // Free B + // if (C is a real child) + // then: + // Make C the second child of N + // else: (N only has one child now) + // (*OnlyOneChildLeft) = TRUE; + // + + A->ThirdChild = B->FirstChild; + A->LowOfThird = Node->LowOfSecond; + + (*Table->Free)(B); + + if (C != NULL) { + Node->SecondChild = C; + Node->LowOfSecond = Node->LowOfThird; + Node->ThirdChild = NULL; + } else { + Node->SecondChild = NULL; + (*OnlyOneChildLeft) = TRUE; + } + } + } + + + + } else { + + // + // Third child has the orphan + // + + ASSERT(SubTree == 3); + ASSERT(C != NULL); + ASSERT(B != NULL); + + if (B->ThirdChild != NULL) { + + // + // (B has three children) + // (move the largest of them into C) + // Move C(1) to C(2) + // Move B(3) to C(1) + // + + C->SecondChild = C->FirstChild; + C->LowOfSecond = Node->LowOfThird; + + C->FirstChild = B->ThirdChild; + Node->LowOfThird = B->LowOfThird; + B->ThirdChild = 0; + } else { + + // + // (B only has two children) + // (move the orphan into B) + // Move C(1) to B(3) + // Free C + // + + B->ThirdChild = C->FirstChild; + B->LowOfThird = Node->LowOfThird; + Node->ThirdChild = NULL; + + (*Table->Free)(C); + + } + } + + return; + +} + + +VOID +GtbpSplitNode( + IN PGTB_TWO_THREE_NODE Node, + IN PGTB_TWO_THREE_NODE NodePassedBack, + IN PGTB_TWO_THREE_LEAF LowPassedBack, + IN ULONG SubTree, + IN PLIST_ENTRY AllocatedNodes, + OUT PGTB_TWO_THREE_NODE *NewNode, + OUT PGTB_TWO_THREE_LEAF *LowLeaf + ) + +/*++ + +Routine Description: + + This routine splits a node that already has three children. + Memory necessary to perform the split is expected to have + already been allocated and available via the AllocatedNodes + parameter. + + +Parameters: + + Node - The node to be split. + + NodePassedBack - The 4th node passed back from an insertion operation + into the SubTree of Node specified by the SubTree parameter. + NOTE: that this may, in fact, be a GTB_TWO_THREE_LEAF !!! + + LowPassedBack - points the the low leaf value passed back by the + insertion operation that is causing the split. + + SubTree - Indicates which SubTree of Node an element was inserted + into which is causing the split. + + AllocatedNodes - Contains a list of allocated GTB_TWO_THREE_NODE + blocks for use in an insertion operation (which this split + is assumed to be part of). + + NewNode - Receives a pointer to the node generated by the split. + + LowLeaf - receives a pointer to the low leaf of the NewNode's SubTree. + + +Return Values: + + None. + +--*/ +{ + + PGTB_TWO_THREE_NODE + LocalNode; + + + + // Make a new node and split things up. + // The node has already been allocated and passed back to + // this routine via the AllocatedNodes parameter. + // + + LocalNode = GtbpGetNextAllocatedNode( AllocatedNodes ); + ASSERT( LocalNode != NULL); + (*NewNode) = LocalNode; + + // + // Set known fields of new node + // + + LocalNode->ParentNode = Node->ParentNode; + LocalNode->Control = Node->Control; + LocalNode->ThirdChild = NULL; //Low of third is left undefined + + // + // Now move around children... + // + + if (SubTree == 3) { + + // + // We were inserting into the 3rd SubTree. This implies: + // + // Node(child 3) moves to new(child 1) + // Back is put in new(child 2) + // + + LocalNode->FirstChild = Node->ThirdChild; + LocalNode->SecondChild = NodePassedBack; + LocalNode->LowOfSecond = LowPassedBack; + (*LowLeaf) = Node->LowOfThird; // low of the new node is low of old third + + Node->ThirdChild = NULL; //Low of third is left undefined + + + + } else { + + // + // We inserted into either SubTree 1 or 2. + // These cases cause: + // + // 1 => Node(child 3) moves to new(child 2) + // Node(child 2) moves to New(child 1) + // Back is put in Node(child 2) + // + // 2 => Node(child 3) moves to new(child 2) + // Back is put in New(child 1) + // + // In both these cases, Node(child 3) moves to New(child 2) + // and there are no third children. So do that. + // + + LocalNode->SecondChild = Node->ThirdChild; + LocalNode->LowOfSecond = Node->LowOfThird; + + + if (SubTree == 2) { + + LocalNode->FirstChild = NodePassedBack; + (*LowLeaf) = LowPassedBack; + + if (!GtbpChildrenAreLeaves(Node)) { + NodePassedBack->ParentNode = LocalNode; + } + + } else { + + // + // SubTree == 1 + // + + LocalNode->FirstChild = Node->SecondChild; + (*LowLeaf) = Node->LowOfSecond; + + Node->SecondChild = NodePassedBack; + Node->LowOfSecond = LowPassedBack; + if (!GtbpChildrenAreLeaves(Node)) { + NodePassedBack->ParentNode = Node; + } + + } + } + + // + // Neither node has a third child anymore + // + + LocalNode->ThirdChild = NULL; //Low of third is left undefined + Node->ThirdChild = NULL; + + // + // Set the ParentNodes only if the children aren't leaves. + // + + if (!GtbpChildrenAreLeaves(Node)) { + + Node->FirstChild->ParentNode = Node; + Node->SecondChild->ParentNode = Node; + + LocalNode->FirstChild->ParentNode = LocalNode; + LocalNode->SecondChild->ParentNode = LocalNode; + } + + + return; +} + + + +PGTB_TWO_THREE_LEAF +GtbpAllocateLeafAndNodes( + IN PRTL_GENERIC_TABLE2 Table, + IN ULONG SplitCount, + OUT PLIST_ENTRY *AllocatedNodes + ) +/*++ + +Routine Description: + + Allocate a leaf and some number of internal nodes. This is + used in conjunction with GtbpGetNextAllocatedNode() when splitting + nodes following an insertion. These two routines allow all necessary + memory to be allocated all at once, rather than trying to deal with + memory allocation failures once changes to the tree have begun. + + +Arguments: + + Table - the table into which the nodes will be added. This + provides the allocation routine. + + SplitCount - indicates how many nodes will need splitting, and, + thus, how many nodes need to be allocated. + + +Return Value: + + Pointer to the leaf if successful. + NULL if unsuccessful. + +--*/ +{ + + PGTB_TWO_THREE_LEAF + Leaf; + + PLIST_ENTRY + NodeRoot, + NextNode; + + ULONG + i; + +#ifdef GTBP_DIAGNOSTICS + PGTB_TWO_THREE_NODE + N; +#endif //GTBP_DIAGNOSTICS + + NodeRoot = NULL; + + // + // Allocate a chain of Nodes, if necessary + // + + if (SplitCount > 0) { + + NodeRoot = (PLIST_ENTRY) + ((*Table->Allocate)( sizeof(GTB_TWO_THREE_NODE))); + if (NodeRoot == NULL) { + goto error_return; + } + + InitializeListHead( NodeRoot ); + +#ifdef GTBP_DIAGNOSTICS + + GtbpDiagPrint(LEAF_AND_NODE_ALLOC, + ("GTB: Allocating %d nodes with leaf, root: 0x%lx\n", + SplitCount, NodeRoot)); + N = (PGTB_TWO_THREE_NODE)NodeRoot; + N->Control = 10000; //Used as a diagnostic allocation counter/index + +#endif //GTBP_DIAGNOSTICS + + for (i=1; i<SplitCount; i++) { + NextNode = (PLIST_ENTRY) + ((*Table->Allocate)( sizeof(GTB_TWO_THREE_NODE))); + if (NextNode == NULL) { + goto error_return; + } + InsertTailList( NodeRoot, NextNode ); + +#ifdef GTBP_DIAGNOSTICS + + N = (PGTB_TWO_THREE_NODE)NextNode; + N->Control = 10000+i; //Used as a diagnostic allocation counter/index + +#endif //GTBP_DIAGNOSTICS + } + } + + + // + // Finally, allocate the leaf + // + + Leaf = (PGTB_TWO_THREE_LEAF) + ((*Table->Allocate)( sizeof(GTB_TWO_THREE_LEAF))); + + if (Leaf == NULL) { + goto error_return; + } + + (*AllocatedNodes) = NodeRoot; + return(Leaf); + +error_return: + + GtbpDiagPrint(LEAF_AND_NODE_ALLOC, + ("GTB: ** error allocating leaf and nodes - insufficient memory.\n")); + // + // Deallocate any nodes that have already been allocated. + // + + if (NodeRoot != NULL) { + + NextNode = NodeRoot->Flink; + while (NextNode != NodeRoot) { + RemoveEntryList( NextNode ); + (*Table->Free)( NextNode ); + + + } + + (*Table->Free)( NodeRoot ); + } + + return(NULL); +} + + +PGTB_TWO_THREE_NODE +GtbpGetNextAllocatedNode( + IN PLIST_ENTRY AllocatedNodes + ) +/*++ + +Routine Description: + + Take the next node off of the list of allocated nodes and + return its address. + + +Arguments: + + AllocatedNodes - Points to the list of nodes allocated using + GtbpAllocateLeafAndNodes(). + + +Return Value: + + Pointer to the node. + any other return value indicates an error in the caller. + +--*/ +{ + PLIST_ENTRY + NextNode; + +#ifdef GTBP_DIAGNOSTICS + PGTB_TWO_THREE_NODE + N; +#endif //GTBP_DIAGNOSTICS + + // + // Remove the first entry on the list. + // This ensures that the passed in root is the last entry + // returned. + // + + NextNode = AllocatedNodes->Flink; + RemoveEntryList( NextNode ); + +#ifdef GTBP_DIAGNOSTICS + + NextNode->Flink = NULL; //Just to prevent accidental re-use + N = (PGTB_TWO_THREE_NODE)NextNode; + ASSERT(N->Control >= 10000); //under 10000 mplies it has already been allocated. + + + GtbpDiagPrint(LEAF_AND_NODE_ALLOC, + ("GTB: Allocating node (index: %d) from root: 0x%lx\n", + (N->Control-10000), AllocatedNodes)); +#endif //GTBP_DIAGNOSTICS + + return( (PGTB_TWO_THREE_NODE)((PVOID)NextNode) ); +} + + + +////////////////////////////////////////////////////////////////////////// +// // +// Diagnostic (Developer) routines ... // +// // +////////////////////////////////////////////////////////////////////////// + +#ifdef GTBP_DEVELOPER_BUILD + +#include <string.h> + +// +// This routine is expected to dump an element's value +// + +typedef +VOID +(NTAPI *PGTBP_DEV_DUMP_ELEMENT_ROUTINE) ( + PVOID Element + ); + + +VOID +GtbpDevIndent( + ULONG Depth + ) +{ + UNICODE_STRING + Indent; + + RtlInitUnicodeString( &Indent, + L" +"); + + Indent.Length = (USHORT)(Depth*4); + if (Indent.Length > Indent.MaximumLength) { + Indent.Length = Indent.MaximumLength; + } + + DbgPrint("\n%wZ%d: ", &Indent, Depth); + return; +} + + +VOID +GtbpDevDumpNode( + PGTB_TWO_THREE_NODE Parent, + PGTB_TWO_THREE_NODE N, + PGTB_TWO_THREE_LEAF Low, + ULONG Depth, + PGTBP_DEV_DUMP_ELEMENT_ROUTINE DumpElement + ) +/*++ + +Routine Description: + + Dump the 2-3 tree starting at the specified node. + +Arguments: + + N - Points to the node to start the dump at. + + Depth - Indicates the depth of the tree prior to this node. + This is used to indent the printing. + +Return Value: + + None. + +--*/ +{ + ASSERT(Parent == N->ParentNode); + + + GtbpDevIndent( Depth ); + DbgPrint("0x%lx ", N); + if (ARGUMENT_PRESENT(Low)) { + DbgPrint("(LowElement): "); + DumpElement( Low->Element ); + } + + Depth++; + + if (GtbpChildrenAreLeaves(N)) { + + GtbpDevIndent( Depth ); + DumpElement( ((PGTB_TWO_THREE_LEAF)N->FirstChild)->Element ); + + if (N->SecondChild != NULL) { + GtbpDevIndent( Depth ); + DumpElement( ((PGTB_TWO_THREE_LEAF)N->SecondChild)->Element ); + + if (N->ThirdChild != NULL) { + GtbpDevIndent( Depth ); + DumpElement( ((PGTB_TWO_THREE_LEAF)N->ThirdChild)->Element ); + } + } + } else { + + GtbpDevDumpNode( N, N->FirstChild, NULL, Depth, DumpElement ); + + if (N->SecondChild != NULL) { + GtbpDevDumpNode( N, N->SecondChild, N->LowOfSecond, Depth, DumpElement ); + + if (N->ThirdChild != NULL) { + GtbpDevDumpNode( N, N->ThirdChild, N->LowOfThird, Depth, DumpElement ); + } + } + } + + return; +} + + +VOID +GtbpDevDumpTwoThreeTree( + PRTL_GENERIC_TABLE2 Table, + PGTBP_DEV_DUMP_ELEMENT_ROUTINE DumpElement + ) +/*++ + +Routine Description: + + This routine causes the entire 2-3 tree to be dumped. + + +Arguments: + + Table - The table containing the 2-3 tree to dump. + + DumpElement - A caller supplied routine that may be called + to dump element values. + +Return Value: + + None. + +--*/ +{ + PLIST_ENTRY + Next; + + + DbgPrint("\n\n\n **** Dump Of Generic Table2 (2-3 tree) **** \n\n"); + + DbgPrint("Table : 0x%lx\n", Table); + DbgPrint("Element Count: %d\n", Table->ElementCount); + + + DbgPrint("\n\nSort Order Of Elements..."); + + Next = Table->SortOrderHead.Flink; + if (Next == &(Table->SortOrderHead)) { + DbgPrint("Sorted list is empty.\n"); + } else { + + while (Next != &(Table->SortOrderHead)) { + DbgPrint("\n0x%lx: ", Next); + (*DumpElement)( ((PGTB_TWO_THREE_LEAF)((PVOID)Next))->Element ); + Next = Next->Flink; + } //end_while + } //end_if + + DbgPrint("\n\n\nTree Structure..."); + + if (Table->Root == NULL) { + DbgPrint(" Root of table is NULL - no tree present\n"); + } else { + + // + // Walk the tree first-SubTree, second-SubTree, third-SubTree order + // + + GtbpDevDumpNode(NULL, Table->Root, NULL, 0, DumpElement); + } + + DbgPrint("\n\n"); + + + return; +} + + + +BOOLEAN +GtbpDevValidateLeaf( + IN PGTB_TWO_THREE_LEAF Leaf, + IN PLIST_ENTRY ListHead, + IN OUT ULONG *ElementCount, + IN OUT PLIST_ENTRY *ListEntry + ) + +/*++ + +Routine Description: + + Validate the specified leaf matches the next leaf in the + SortOrder list. + + +Arguments: + + Leaf - Points to the leaf to validate. + + ListHead - Points to the head of the SortOrderList. + + ElementCount - Contains a count of elements already validated. + This parameter will be incremented by 1 if the leaf is + found to be valid. + + ListEntry - Points to the next element in the SortOrderList. + This pointer will be updated to point to the next element + in the list if the leaf is found to be valid. + +Return Value: + + TRUE - Leaf is valid. + + FALSE - Leaf is not valid. + +--*/ +{ + + if ((*ListEntry) == ListHead) { + GtbpDiagPrint( VALIDATE, + ("GTB: Exhausted entries in SortOrderList while there are still\n" + " entries in the tree.\n")); + } + + + if ((PVOID)Leaf == (PVOID)(*ListEntry)) { + (*ElementCount)++; + (*ListEntry) = (*ListEntry)->Flink; + return(TRUE); + } else { + GtbpDiagPrint( VALIDATE, + ("GTB: Tree leaf doesn't match sort order leaf.\n" + " Tree Leaf : 0x%lx\n" + " sort order leaf: 0x%lx\n", + Leaf, (*ListEntry))); + return(FALSE); + } +} + + +BOOLEAN +GtbpDevValidateTwoThreeSubTree( + IN PGTB_TWO_THREE_NODE Node, + IN PLIST_ENTRY ListHead, + IN OUT ULONG *ElementCount, + IN OUT PLIST_ENTRY *ListEntry + ) + +/*++ + +Routine Description: + + Validate the specified subtree of a 2-3 tree. + + The ParentNode of the tree is expected to already have been + validated by the caller of this routine. + + +Arguments: + + Node - Pointer to the root node of the subtree to validate. + Validate the specified leaf matches the next leaf in the + SortOrder list. + + +Arguments: + + Leaf - Points to the leaf to validate. + + ListHead - points to the SortOrderList's listhead. + + ElementCount - Contains a count of elements already validated. + This parameter will be incremented by the number of elements + in this subtree. + + ListEntry - Points to the next element in the SortOrderList. + This pointer will be updated as elements are encountered and + compared to the elements in the SortOrderList. + +Return Value: + + TRUE - SubTree structure is valid. + + FALSE - SubTree structure is not valid. + +--*/ +{ + + BOOLEAN + Result; + + + // + // Must have at least two children unless we are the root node. + // + + if (Node->ParentNode != NULL) { + if (Node->SecondChild == NULL) { + GtbpDiagPrint( VALIDATE, + ("GTB: Non-root node has fewer than two children.\n" + " Node : 0x%lx\n" + " FirstChild : 0x%lx\n" + " SecondChild: 0x%lx\n" + " ThirdChild : 0x%lx\n", + Node, Node->FirstChild, Node->SecondChild, + Node->ThirdChild)); + return(FALSE); + } + } + + if (Node->FirstChild == NULL) { + GtbpDiagPrint( VALIDATE, + ("GTB: Non-root node does not have first child.\n" + " Node : 0x%lx\n" + " FirstChild : 0x%lx\n" + " SecondChild: 0x%lx\n" + " ThirdChild : 0x%lx\n", + Node, Node->FirstChild, Node->SecondChild, + Node->ThirdChild)); + return(FALSE); + } + + + + if (!GtbpChildrenAreLeaves(Node)) { + + + Result = GtbpDevValidateTwoThreeSubTree( Node->FirstChild, + ListHead, + ElementCount, + ListEntry); + + if ( Result && (Node->SecondChild != NULL) ) { + Result = GtbpDevValidateTwoThreeSubTree( Node->SecondChild, + ListHead, + ElementCount, + ListEntry); + if ( Result && (Node->ThirdChild != NULL) ) { + Result = GtbpDevValidateTwoThreeSubTree( Node->ThirdChild, + ListHead, + ElementCount, + ListEntry); + } + } + + return(Result); + } + + + // + // We are at a leaf node + // Check that we have a SortOrderList entry matching each + // leaf. + // + + Result = GtbpDevValidateLeaf( (PGTB_TWO_THREE_LEAF)Node->FirstChild, + ListHead, + ElementCount, + ListEntry); + + if (Result && (Node->SecondChild != NULL)) { + Result = GtbpDevValidateLeaf( (PGTB_TWO_THREE_LEAF)Node->SecondChild, + ListHead, + ElementCount, + ListEntry); + if (Result && (Node->ThirdChild != NULL)) { + Result = GtbpDevValidateLeaf( (PGTB_TWO_THREE_LEAF)Node->ThirdChild, + ListHead, + ElementCount, + ListEntry); + } + } + + if (!Result) { + GtbpDiagPrint( VALIDATE, + ("GTB: previous error in child analysis prevents further\n" + " validation of node: 0x%lx\n", Node)); + } + + return(Result); +} + +BOOLEAN +GtbpDevValidateGenericTable2( + PRTL_GENERIC_TABLE2 Table + ) +/*++ + +Routine Description: + + This routine causes the entire 2-3 tree's structure to be + validated. + + !! DOESN'T YET VALIDATE LowOfChild VALUES !! + +Arguments: + + Table - The table containing the 2-3 tree to validate. + + +Return Value: + + TRUE - Table structure is valid. + + FALSE - Table structure is not valid. + +--*/ +{ + ULONG + ElementCount, + ElementsInList; + + PGTB_TWO_THREE_NODE + Node; + + PLIST_ENTRY + ListEntry; + + BOOLEAN + Result; + + // + // Walk the tree and the linked list simultaneously. + // Walk the tree first-child, second-child, third-child + // order to get ascending values that match the linked list. + // + + + if (Table->ElementCount == 0) { + if (Table->Root != NULL) { + GtbpDiagPrint( VALIDATE, + ("GTB: ElementCount is zero, but root node is not null.\n" + " Table: 0x%lx Root: 0x%lx\n", Table, Table->Root)); + Result = FALSE; + } else { + return(TRUE); + } + + + } else { + if (Table->Root == NULL) { + GtbpDiagPrint( VALIDATE, + ("GTB: ElementCount is non-zero, but root node is null.\n" + " Table: 0x%lx ElementCount: %d\n", + Table, Table->ElementCount)); + Result = FALSE; + } + + + if (Table->SortOrderHead.Flink == &Table->SortOrderHead) { + GtbpDiagPrint( VALIDATE, + ("GTB: ElementCount is non-zero, but sort order list is empty.\n" + " Table: 0x%lx ElementCount: %d\n", + Table, Table->ElementCount)); + Result = FALSE; + } + + } + + if (Result) { + + ListEntry = Table->SortOrderHead.Flink; + Node = Table->Root; + + // + // Verify parent pointer (responsibility of caller) + // + + if (Node->ParentNode != NULL) { + GtbpDiagPrint( VALIDATE, + ("GTB: Root parent pointer is non-null.\n" + " Table: 0x%lx Root: 0x%lx ParentNode: 0x%lx\n", + Table, Node, Node->ParentNode)); + Result = FALSE; + } + + if (Result) { + + ElementCount = 0; + Result = GtbpDevValidateTwoThreeSubTree( Node, + &Table->SortOrderHead, + &ElementCount, + &ListEntry); + if (Result) { + + ElementsInList = ElementCount; + while (ListEntry != &Table->SortOrderHead) { + ElementsInList++; + ListEntry = ListEntry->Flink; + } + if ( (ElementCount != Table->ElementCount) || + (ElementsInList != ElementCount) ) { + GtbpDiagPrint( VALIDATE, + ("GTB: Table is valid except either the ElementCount doesn't match\n" + " the number of elements in the table or there were entries on\n" + " the SortOrderList that weren't in the table.\n" + " Table : 0x%lx\n" + " Root : 0x%lx\n" + " ElementCount : %d\n" + " Elements In Tree: %d\n" + " Elements In List: %d\n", + Table, Node, Table->ElementCount, + ElementCount, ElementsInList)); + Result = FALSE; + } + } else { + GtbpDiagPrint( VALIDATE, + ("GTB: previous validation error in table 0x%lx prevents\n" + " further processing.\n", Table)); + } + } + } + + if (!Result) { + DbgBreakPoint(); + } + + + return(Result); +} +#endif //GTBP_DEVELOPER_BUILD diff --git a/private/newsam2/server/global.c b/private/newsam2/server/global.c new file mode 100644 index 000000000..ca02876cb --- /dev/null +++ b/private/newsam2/server/global.c @@ -0,0 +1,305 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + global.c + +Abstract: + + This file contains global variables for the SAM server program. + + Note: There are also some global variables in the files generated + by the RPC midl compiler. These variables start with the + prefix "samr_". + +Author: + + Jim Kelly (JimK) 4-July-1991 + +Environment: + + User Mode - Win32 + +Revision History: + + +--*/ + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include <samsrvp.h> + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Global variables // +// // +/////////////////////////////////////////////////////////////////////////////// + + + +#if SAMP_DIAGNOSTICS +// +// SAM Global Controls - see flags in samsrvp.h +// + +ULONG SampGlobalFlag = 0; +#endif //SAMP_DIAGNOSTICS + + +// +// Internal data structure and Registry database synchronization lock +// +// The SampTransactionWithinDomain field is used to track whether a +// lock held for exclusive WRITE access is for a transaction within +// a single domain. If so, then SampTransactionDomainIndex contains +// the index into SampDefinedDomains of the domain being modified. +// + +RTL_RESOURCE SampLock; +BOOLEAN SampTransactionWithinDomain; +ULONG SampTransactionDomainIndex; + + +// +// The type of product this SAM server is running in +// + +NT_PRODUCT_TYPE SampProductType; + + + +// +// Used to indicate whether the SAM service is currently processing +// normal client calls. If not, then trusted client calls will still +// be processed, but non-trusted client calls will be rejected. +// + +SAMP_SERVICE_STATE SampServiceState; + + +// +// This boolean is set to TRUE if the LSA auditing policy indicates +// account auditing is enabled. Otherwise, this will be FALSE. +// +// This enables SAM to skip all auditing processing unless auditing +// is currently enabled. +// + +BOOLEAN SampAccountAuditingEnabled; + + + +// +// This is a handle to the root of the SAM backstore information in the +// registry. This is the level at which the RXACT information is +// established. This key can not be closed if there are any SERVER object +// context blocks active. +// ("SAM") +// + +HANDLE SampKey; + + +// +// This is the pointer to the RXactContext structure that will be created +// when RXact is initialized. It must be passed into each RXact call. +// + +PRTL_RXACT_CONTEXT SampRXactContext; + + +// +// Keep a list of server and domain contexts +// + +LIST_ENTRY SampContextListHead; + +// +// This array contains information about each domain known to this +// SAM server. Reference and Modification of this array is protected +// by the SampLock. +// + +ULONG SampDefinedDomainsCount; +PSAMP_DEFINED_DOMAINS SampDefinedDomains; + + + + + +// +// Object type-independent information for each of the various +// SAM defined objects. +// This information is READ-ONLY once initialized. + +SAMP_OBJECT_INFORMATION SampObjectInformation[ SampUnknownObjectType ]; + + + + +// +// Count of the number of active opens +// + +ULONG SampActiveContextCount; + + + +// +// Address of DLL routine to do password filtering. +// + +//PSAM_PF_PASSWORD_FILTER SampPasswordFilterDllRoutine; + + + +// +// Unicode strings containing well known registry key names. +// These are read-only values once initialized. +// + +UNICODE_STRING SampNameDomains; +UNICODE_STRING SampNameDomainGroups; +UNICODE_STRING SampNameDomainAliases; +UNICODE_STRING SampNameDomainAliasesMembers; +UNICODE_STRING SampNameDomainUsers; +UNICODE_STRING SampNameDomainAliasesNames; +UNICODE_STRING SampNameDomainGroupsNames; +UNICODE_STRING SampNameDomainUsersNames; +UNICODE_STRING SampCombinedAttributeName; +UNICODE_STRING SampFixedAttributeName; +UNICODE_STRING SampVariableAttributeName; + + + +// +// A plethora of other useful characters or strings +// + +UNICODE_STRING SampBackSlash; // "/" +UNICODE_STRING SampNullString; // Null string +UNICODE_STRING SampSamSubsystem; // "Security Account Manager" +UNICODE_STRING SampServerObjectName; // Name of root SamServer object + + +// +// Useful times +// + +LARGE_INTEGER SampImmediatelyDeltaTime; +LARGE_INTEGER SampNeverDeltaTime; +LARGE_INTEGER SampHasNeverTime; +LARGE_INTEGER SampWillNeverTime; + + +// +// Useful encryption constants +// + +LM_OWF_PASSWORD SampNullLmOwfPassword; +NT_OWF_PASSWORD SampNullNtOwfPassword; + + +// +// Useful Sids +// + +PSID SampWorldSid; +PSID SampAnonymousSid; +PSID SampAdministratorUserSid; +PSID SampAdministratorsAliasSid; + + +// +// Variables for the thread that flushes changes to the registry. +// +// LastUnflushedChange - if there are no changes to be flushed, this +// has a value of "Never". If there are changes to be flushed, +// this is the time of the last change that was made. The flush +// thread will flush if a SampFlushThreadMinWaitSeconds has passed +// since the last change. +// +// FlushThreadCreated - set TRUE as soon as the flush thread is created, +// and FALSE when the thread exits. A new thread will be created +// when this is FALSE, unless FlushImmediately is TRUE. +// +// FlushImmediately - an important event has occurred, so we want to +// flush the changes immediately rather than waiting for the flush +// thread to do it. LastUnflushedChange should be set to "Never" +// so the flush thread knows it doesn't have to flush. +// + +LARGE_INTEGER LastUnflushedChange; +BOOLEAN FlushThreadCreated; +BOOLEAN FlushImmediately; + +// +// These should probably be #defines, but we want to play with them. +// +// SampFlushThreadMinWaitSeconds - The unit of time that the flush thread +// waits. If one of these has passed since the last unflushed change, +// the changes will be flushed. +// +// SampFlushThreadMaxWaitSeconds - If this amount of time has passed since +// the flush thread was created or last flushed, the thread will force +// a flush even if the database is still being changed. +// +// SampFlushThreadExitDelaySeconds - How long the flush thread waits +// around after a flush to see if any more changes occur. If they +// do, it starts waiting again; but if they don't, it will exit +// to keep down thread overhead. +// + +LONG SampFlushThreadMinWaitSeconds; +LONG SampFlushThreadMaxWaitSeconds; +LONG SampFlushThreadExitDelaySeconds; + +// +// Special SIDs +// + +PSID SampBuiltinDomainSid = NULL; +PSID SampAccountDomainSid = NULL; + +// +// Null token handle. This is used when clients connect via unauthenticated +// RPC instead of authenticated RPC or named pipes. Since they can't be +// authenticated, we impersonate this pre-built Null sesssion token. +// + +HANDLE SampNullSessionToken; + +// +// Flag indicating whether Netware server installed. +// + +BOOLEAN SampNetwareServerInstalled = FALSE; + +// +// Flag indicating whether to start listening on TCP/IP +// + +BOOLEAN SampIpServerInstalled = FALSE; + + +// +// Flag indicating whether current global lock is for read or write. +// Used by dslayer routines to optimize DS transaction. +// + +BOOLEAN SampWriteLock = TRUE; + +// +// Buffer to store DSNAME of the Root Object in the DS +// + +UCHAR RootObjectName[256]; + diff --git a/private/newsam2/server/group.c b/private/newsam2/server/group.c new file mode 100644 index 000000000..3b8a0d0d9 --- /dev/null +++ b/private/newsam2/server/group.c @@ -0,0 +1,3548 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + group.c + +Abstract: + + This file contains services related to the SAM "group" object. + + +Author: + + Jim Kelly (JimK) 4-July-1991 + +Environment: + + User Mode - Win32 + +Revision History: + + +--*/ + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include <samsrvp.h> +#include <msaudite.h> +#include <dslayer.h> +#include <dsmember.h> + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// private service prototypes // +// // +/////////////////////////////////////////////////////////////////////////////// + + +NTSTATUS +SampDeleteGroupKeys( + IN PSAMP_OBJECT Context + ); + +NTSTATUS +SampChangeGroupAccountName( + IN PSAMP_OBJECT Context, + IN PUNICODE_STRING NewAccountName, + OUT PUNICODE_STRING OldAccountName + ); + +NTSTATUS +SampReplaceGroupMembers( + IN PSAMP_OBJECT GroupContext, + IN ULONG MemberCount, + IN PULONG Members + ); + +NTSTATUS +SampAddAccountToGroupMembers( + IN PSAMP_OBJECT GroupContext, + IN ULONG UserRid + ); + +NTSTATUS +SampRemoveAccountFromGroupMembers( + IN PSAMP_OBJECT GroupContext, + IN ULONG AccountRid + ); + +NTSTATUS +SampAddGroupToGroupMembership( + IN ULONG GroupRid, + IN ULONG Attributes, + IN ULONG MemberRid, + IN SAMP_MEMBERSHIP_DELTA AdminGroup, + IN SAMP_MEMBERSHIP_DELTA OperatorGroup + ); + +NTSTATUS +SampRemoveMembershipGroup( + IN ULONG GroupRid, + IN ULONG MemberRid, + IN SAMP_MEMBERSHIP_DELTA AdminGroup, + IN SAMP_MEMBERSHIP_DELTA OperatorGroup + ); + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Exposed RPC'able Services // +// // +/////////////////////////////////////////////////////////////////////////////// + + + + + + +NTSTATUS +SamrOpenGroup( + IN SAMPR_HANDLE DomainHandle, + IN ACCESS_MASK DesiredAccess, + IN ULONG GroupId, + OUT SAMPR_HANDLE *GroupHandle + ) + +/*++ + +Routine Description: + + This API opens an existing group in the account database. The group + is specified by a ID value that is relative to the SID of the + domain. The operations that will be performed on the group must be + declared at this time. + + This call returns a handle to the newly opened group that may be + used for successive operations on the group. This handle may be + closed with the SamCloseHandle API. + + + +Parameters: + + DomainHandle - A domain handle returned from a previous call to + SamOpenDomain. + + DesiredAccess - Is an access mask indicating which access types + are desired to the group. These access types are reconciled + with the Discretionary Access Control list of the group to + determine whether the accesses will be granted or denied. + + GroupId - Specifies the relative ID value of the group to be + opened. + + GroupHandle - Receives a handle referencing the newly opened + group. This handle will be required in successive calls to + operate on the group. + +Return Values: + + STATUS_SUCCESS - The group was successfully opened. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_NO_SUCH_GROUP - The specified group does not exist. + + STATUS_INVALID_HANDLE - The domain handle passed is invalid. + +--*/ +{ + NTSTATUS NtStatus; + + SAMTRACE("SamrOpenGroup"); + + NtStatus = SampOpenAccount( + SampGroupObjectType, + DomainHandle, + DesiredAccess, + GroupId, + FALSE, + GroupHandle + ); + + return(NtStatus); +} + + +NTSTATUS +SamrQueryInformationGroup( + IN SAMPR_HANDLE GroupHandle, + IN GROUP_INFORMATION_CLASS GroupInformationClass, + OUT PSAMPR_GROUP_INFO_BUFFER *Buffer + ) + +/*++ + +Routine Description: + + This API retrieves information on the group specified. + + + +Parameters: + + GroupHandle - The handle of an opened group to operate on. + + GroupInformationClass - Class of information to retrieve. The + accesses required for each class is shown below: + + Info Level Required Access Type + ----------------------- ---------------------- + + GroupGeneralInformation GROUP_READ_INFORMATION + GroupNameInformation GROUP_READ_INFORMATION + GroupAttributeInformation GROUP_READ_INFORMATION + GroupAdminInformation GROUP_READ_INFORMATION + + Buffer - Receives a pointer to a buffer containing the requested + information. When this information is no longer needed, this + buffer must be freed using SamFreeMemory(). + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_INVALID_INFO_CLASS - The class provided was invalid. + +--*/ +{ + + NTSTATUS NtStatus; + NTSTATUS IgnoreStatus; + PSAMP_OBJECT AccountContext; + SAMP_OBJECT_TYPE FoundType; + ACCESS_MASK DesiredAccess; + ULONG i; + SAMP_V1_0A_FIXED_LENGTH_GROUP V1Fixed; + + // + // Used for tracking allocated blocks of memory - so we can deallocate + // them in case of error. Don't exceed this number of allocated buffers. + // || + // vv + PVOID AllocatedBuffer[10]; + ULONG AllocatedBufferCount = 0; + + SAMTRACE("SamrQueryInformationGroup"); + + #define RegisterBuffer(Buffer) \ + { \ + if ((Buffer) != NULL) { \ + \ + ASSERT(AllocatedBufferCount < \ + sizeof(AllocatedBuffer) / sizeof(*AllocatedBuffer)); \ + \ + AllocatedBuffer[AllocatedBufferCount++] = (Buffer); \ + } \ + } + + #define AllocateBuffer(NewBuffer, Size) \ + { \ + (NewBuffer) = MIDL_user_allocate(Size); \ + RegisterBuffer(NewBuffer); \ + } \ + + + // + // Make sure we understand what RPC is doing for (to) us. + // + + ASSERT (Buffer != NULL); + ASSERT ((*Buffer) == NULL); + + + + // + // Set the desired access based upon the Info class + // + + switch (GroupInformationClass) { + + case GroupGeneralInformation: + case GroupNameInformation: + case GroupAttributeInformation: + case GroupAdminCommentInformation: + + DesiredAccess = GROUP_READ_INFORMATION; + break; + + default: + (*Buffer) = NULL; + return(STATUS_INVALID_INFO_CLASS); + } // end_switch + + + + // + // Allocate the info structure + // + + (*Buffer) = MIDL_user_allocate( sizeof(SAMPR_GROUP_INFO_BUFFER) ); + if ((*Buffer) == NULL) { + return(STATUS_INSUFFICIENT_RESOURCES); + } + RegisterBuffer(*Buffer); + + + + SampAcquireReadLock(); + + + // + // Validate type of, and access to object. + // + + AccountContext = (PSAMP_OBJECT)GroupHandle; + NtStatus = SampLookupContext( + AccountContext, + DesiredAccess, + SampGroupObjectType, // ExpectedType + &FoundType + ); + + + if (NT_SUCCESS(NtStatus)) { + + + // + // If the information level requires, retrieve the V1_FIXED record + // from the registry. + // + + switch (GroupInformationClass) { + + case GroupGeneralInformation: + case GroupAttributeInformation: + + NtStatus = SampRetrieveGroupV1Fixed( + AccountContext, + &V1Fixed + ); + break; //out of switch + + default: + NtStatus = STATUS_SUCCESS; + + } // end_switch + + if (NT_SUCCESS(NtStatus)) { + + // + // case on the type information requested + // + + switch (GroupInformationClass) { + + case GroupGeneralInformation: + + + (*Buffer)->General.Attributes = V1Fixed.Attributes; + + + // + // Get the member count + // + + NtStatus = SampRetrieveGroupMembers( + AccountContext, + &(*Buffer)->General.MemberCount, + NULL // Only need members + ); + + + if (NT_SUCCESS(NtStatus)) { + + + // + // Get copies of the strings we must retrieve from + // the registry. + // + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_GROUP_NAME, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->General.Name) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->General.Name.Buffer); + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_GROUP_ADMIN_COMMENT, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->General.AdminComment) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->General.AdminComment.Buffer); + } + } + } + + + break; + + + case GroupNameInformation: + + // + // Get copies of the strings we must retrieve from + // the registry. + // + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_GROUP_NAME, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->Name.Name) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->Name.Name.Buffer); + } + + + break; + + + case GroupAdminCommentInformation: + + // + // Get copies of the strings we must retrieve from + // the registry. + // + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_GROUP_ADMIN_COMMENT, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->AdminComment.AdminComment) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->AdminComment.AdminComment.Buffer); + } + + + break; + + + case GroupAttributeInformation: + + + (*Buffer)->Attribute.Attributes = V1Fixed.Attributes; + + break; + + } // end_switch + + + } // end_if + + + + // + // De-reference the object, discarding changes + // + + IgnoreStatus = SampDeReferenceContext( AccountContext, FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + } + + // + // Free the read lock + // + + SampReleaseReadLock(); + + + + // + // If we didn't succeed, free any allocated memory + // + + if (!NT_SUCCESS(NtStatus)) { + for ( i=0; i<AllocatedBufferCount ; i++ ) { + MIDL_user_free( AllocatedBuffer[i] ); + } + + (*Buffer) = NULL; + } + + return(NtStatus); +} + + + +NTSTATUS +SamrSetInformationGroup( + IN SAMPR_HANDLE GroupHandle, + IN GROUP_INFORMATION_CLASS GroupInformationClass, + IN PSAMPR_GROUP_INFO_BUFFER Buffer + ) + +/*++ + +Routine Description: + + This API allows the caller to modify group information. + + +Parameters: + + GroupHandle - The handle of an opened group to operate on. + + GroupInformationClass - Class of information to retrieve. The + accesses required for each class is shown below: + + Info Level Required Access Type + ------------------------ ------------------------- + + GroupGeneralInformation (can't write) + + GroupNameInformation GROUP_WRITE_ACCOUNT + GroupAttributeInformation GROUP_WRITE_ACCOUNT + GroupAdminInformation GROUP_WRITE_ACCOUNT + + Buffer - Buffer where information retrieved is placed. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_INFO_CLASS - The class provided was invalid. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_NO_SUCH_GROUP - The group specified is unknown. + + STATUS_SPECIAL_GROUP - The group specified is a special group and + cannot be operated on in the requested fashion. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled for this + operation + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. + +--*/ +{ + + NTSTATUS NtStatus, + TmpStatus, + IgnoreStatus; + + PSAMP_OBJECT AccountContext; + + SAMP_OBJECT_TYPE FoundType; + + PSAMP_DEFINED_DOMAINS Domain; + + ACCESS_MASK DesiredAccess; + + SAMP_V1_0A_FIXED_LENGTH_GROUP V1Fixed; + + UNICODE_STRING OldAccountName, + NewAdminComment, + NewAccountName, + NewFullName; + + ULONG ObjectRid, + OldGroupAttributes, + DomainIndex; + + BOOLEAN Modified = FALSE, + MustUpdateAccountDisplay = FALSE; + + SAMTRACE("SamrSetInformationGroup"); + + + // + // Make sure we understand what RPC is doing for (to) us. + // + + if (Buffer == NULL) { + return(STATUS_INVALID_PARAMETER); + } + + // + // Reset any strings that we'll be freeing in clean-up code + // + + RtlInitUnicodeString(&OldAccountName, NULL); + RtlInitUnicodeString(&NewAccountName, NULL); + RtlInitUnicodeString(&NewFullName, NULL); + RtlInitUnicodeString(&NewAdminComment, NULL); + + // + // Set the desired access based upon the Info class + // + + switch (GroupInformationClass) { + + case GroupNameInformation: + case GroupAttributeInformation: + case GroupAdminCommentInformation: + + DesiredAccess = GROUP_WRITE_ACCOUNT; + break; + + + case GroupGeneralInformation: + default: + + return(STATUS_INVALID_INFO_CLASS); + + } // end_switch + + + + // + // Grab the lock + // + + NtStatus = SampAcquireWriteLock(); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + + // + // Validate type of, and access to object. + // + + AccountContext = (PSAMP_OBJECT)GroupHandle; + ObjectRid = AccountContext->TypeBody.Group.Rid; + NtStatus = SampLookupContext( + AccountContext, + DesiredAccess, + SampGroupObjectType, // ExpectedType + &FoundType + ); + + + + if (NT_SUCCESS(NtStatus)) { + + + // + // Get a pointer to the domain this object is in. + // This is used for auditing. + // + + DomainIndex = AccountContext->DomainIndex; + Domain = &SampDefinedDomains[ DomainIndex ]; + + + // + // If the information level requires, retrieve the V1_FIXED record + // from the registry. This includes anything that will cause + // us to update the display cache. + // + + switch (GroupInformationClass) { + + case GroupAdminCommentInformation: + case GroupNameInformation: + case GroupAttributeInformation: + + NtStatus = SampRetrieveGroupV1Fixed( + AccountContext, + &V1Fixed + ); + + MustUpdateAccountDisplay = TRUE; + OldGroupAttributes = V1Fixed.Attributes; + break; //out of switch + + + default: + NtStatus = STATUS_SUCCESS; + + } // end_switch + + if (NT_SUCCESS(NtStatus)) { + + // + // case on the type information requested + // + + switch (GroupInformationClass) { + + case GroupNameInformation: + + NtStatus = SampChangeGroupAccountName( + AccountContext, + (PUNICODE_STRING)&(Buffer->Name.Name), + &OldAccountName + ); + if (!NT_SUCCESS(NtStatus)) { + OldAccountName.Buffer = NULL; + } + + // + // Don't free OldAccountName yet; we'll need it at the + // very end. + // + + break; + + + case GroupAdminCommentInformation: + + // + // build the key name + // + + NtStatus = SampSetUnicodeStringAttribute( + AccountContext, + SAMP_GROUP_ADMIN_COMMENT, + (PUNICODE_STRING)&(Buffer->AdminComment.AdminComment) + ); + + break; + + + case GroupAttributeInformation: + + MustUpdateAccountDisplay = TRUE; + + V1Fixed.Attributes = Buffer->Attribute.Attributes; + + NtStatus = SampReplaceGroupV1Fixed( + AccountContext, // ParentKey + &V1Fixed + ); + + break; + + + } // end_switch + + + } // end_if + + + // + // Go fetch any data we'll need to update the display cache + // Do this before we dereference the context + // + + if (NT_SUCCESS(NtStatus)) { + + if ( MustUpdateAccountDisplay ) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_GROUP_NAME, + TRUE, // Make copy + &NewAccountName + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_GROUP_ADMIN_COMMENT, + TRUE, // Make copy + &NewAdminComment + ); + // + // If the account name has changed, then OldAccountName + // is already filled in. If the account name hasn't changed + // then the OldAccountName is the same as the new! + // + + if (NT_SUCCESS(NtStatus) && (OldAccountName.Buffer == NULL)) { + + NtStatus = SampDuplicateUnicodeString( + &OldAccountName, + &NewAccountName); + } + } + } + } + + + // + // Generate an audit if necessary + // + + if (NT_SUCCESS(NtStatus) && + SampDoAccountAuditing(DomainIndex)) { + + UNICODE_STRING + AccountName; + + IgnoreStatus = SampGetUnicodeStringAttribute( + AccountContext, // Context + SAMP_GROUP_NAME, // AttributeIndex + FALSE, // MakeCopy + &AccountName // UnicodeAttribute + ); + if (NT_SUCCESS(IgnoreStatus)) { + LsaIAuditSamEvent( + STATUS_SUCCESS, + SE_AUDITID_GLOBAL_GROUP_CHANGE, // AuditId + Domain->Sid, // Domain SID + NULL, // Member Rid (not used) + NULL, // Member Sid (not used) + &AccountName, // Account Name + &Domain->ExternalName, // Domain + &AccountContext->TypeBody.Group.Rid, // Account Rid + NULL // Privileges used + ); + } + + } + + // + // Dereference the account context + // + + if (NT_SUCCESS(NtStatus)) { + + // + // De-reference the object, write out any change to current xaction. + // + + NtStatus = SampDeReferenceContext( AccountContext, TRUE ); + + } else { + + // + // De-reference the object, ignore changes + // + + TmpStatus = SampDeReferenceContext( AccountContext, FALSE ); + ASSERT(NT_SUCCESS(TmpStatus)); + } + + + } //end_if + + + + // + // Commit the transaction, update the display cache, + // and notify netlogon of the changes + // + + if ( NT_SUCCESS(NtStatus) ) { + + NtStatus = SampCommitAndRetainWriteLock(); + + + if ( NT_SUCCESS(NtStatus) ) { + + + + // + // Update the display information if the cache may be affected + // + + if ( MustUpdateAccountDisplay ) { + + SAMP_ACCOUNT_DISPLAY_INFO OldAccountInfo; + SAMP_ACCOUNT_DISPLAY_INFO NewAccountInfo; + + OldAccountInfo.Name = OldAccountName; + OldAccountInfo.Rid = ObjectRid; + OldAccountInfo.AccountControl = OldGroupAttributes; + RtlInitUnicodeString(&OldAccountInfo.Comment, NULL); + RtlInitUnicodeString(&OldAccountInfo.FullName, NULL); // Not used for groups + + NewAccountInfo.Name = NewAccountName; + NewAccountInfo.Rid = ObjectRid; + NewAccountInfo.AccountControl = V1Fixed.Attributes; + NewAccountInfo.Comment = NewAdminComment; + NewAccountInfo.FullName = NewFullName; + + IgnoreStatus = SampUpdateDisplayInformation(&OldAccountInfo, + &NewAccountInfo, + SampGroupObjectType); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + + if ( GroupInformationClass == GroupNameInformation ) { + + SampNotifyNetlogonOfDelta( + SecurityDbRename, + SecurityDbObjectSamGroup, + ObjectRid, + &OldAccountName, + (DWORD) FALSE, // Replicate immediately + NULL // Delta data + ); + + } else { + + SampNotifyNetlogonOfDelta( + SecurityDbChange, + SecurityDbObjectSamGroup, + ObjectRid, + (PUNICODE_STRING) NULL, + (DWORD) FALSE, // Replicate immediately + NULL // Delta data + ); + } + + + } + } + + + // + // Release the write lock + // + + TmpStatus = SampReleaseWriteLock( FALSE ); + + if (NT_SUCCESS(NtStatus)) { + NtStatus = TmpStatus; + } + + + // + // Clean up strings + // + + SampFreeUnicodeString( &OldAccountName ); + SampFreeUnicodeString( &NewAccountName ); + SampFreeUnicodeString( &NewFullName ); + SampFreeUnicodeString( &NewAdminComment ); + + return(NtStatus); + +} + + +NTSTATUS +SamrAddMemberToGroup( + IN SAMPR_HANDLE GroupHandle, + IN ULONG MemberId, + IN ULONG Attributes + ) + +/*++ + +Routine Description: + + This API adds a member to a group. Note that this API requires the + GROUP_ADD_MEMBER access type for the group. + + +Parameters: + + GroupHandle - The handle of an opened group to operate on. + + MemberId - Relative ID of the member to add. + + Attributes - The attributes of the group assigned to the user. + The attributes assigned here may have any value. However, + at logon time these attributes are minimized by the + attributes of the group as a whole. + + Mandatory - If the Mandatory attribute is assigned to + the group as a whole, then it will be assigned to + the group for each member of the group. + + EnabledByDefault - This attribute may be set to any value + for each member of the group. It does not matter + what the attribute value for the group as a whole + is. + + Enabled - This attribute may be set to any value for each + member of the group. It does not matter what the + attribute value for the group as a whole is. + + Owner - If the Owner attribute of the group as a + whole is not set, then the value assigned to + members is ignored. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_NO_SUCH_MEMBER - The member specified is unknown. + + STATUS_MEMBER_IN_GROUP - The member already belongs to the group. + + STATUS_INVALID_GROUP_ATTRIBUTES - Indicates the group attribute + values being assigned to the member are not compatible with + the attribute values of the group as a whole. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled for this + operation + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. + + + + +--*/ +{ + + SAMP_V1_0A_FIXED_LENGTH_GROUP GroupV1Fixed; + NTSTATUS NtStatus, TmpStatus; + PSAMP_OBJECT AccountContext; + SAMP_OBJECT_TYPE FoundType; + ULONG ObjectRid; + BOOLEAN UserAccountActive; + UNICODE_STRING GroupName; + + + SAMTRACE("SamrAddMemberToGroup"); + + + // + // Initialize buffers we will cleanup at the end + // + + RtlInitUnicodeString(&GroupName, NULL); + + + + // + // Grab the lock + // + + NtStatus = SampAcquireWriteLock(); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + + + // + // Validate type of, and access to object. + // + + AccountContext = (PSAMP_OBJECT)(GroupHandle); + ObjectRid = AccountContext->TypeBody.Group.Rid; + NtStatus = SampLookupContext( + AccountContext, + GROUP_ADD_MEMBER, + SampGroupObjectType, // ExpectedType + &FoundType + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampRetrieveGroupV1Fixed( + AccountContext, + &GroupV1Fixed + ); + + + if (NT_SUCCESS(NtStatus)) { + + // + // Perform the user object side of things + // + + + NtStatus = SampAddGroupToUserMembership( + ObjectRid, + Attributes, + MemberId, + (GroupV1Fixed.AdminCount == 0) ? NoChange : AddToAdmin, + (GroupV1Fixed.OperatorCount == 0) ? NoChange : AddToAdmin, + &UserAccountActive + ); + + if (NtStatus == STATUS_NO_SUCH_USER) + { + // + // It is not an User Object. It can be a group Object + // as from NT5 Onwards we support adding groups to group + // memberships + // + + NtStatus = SampAddGroupToGroupMembership( + ObjectRid, + Attributes, + MemberId, + (GroupV1Fixed.AdminCount == 0) ? NoChange : AddToAdmin, + (GroupV1Fixed.OperatorCount == 0) ? NoChange : AddToAdmin + ); + } + + // + // Now perform the group side of things + // + + if (NT_SUCCESS(NtStatus)) { + + // + // Add the user to the group (should not fail) + // + + NtStatus = SampAddAccountToGroupMembers( + AccountContext, + MemberId + ); + } + } + + + + if (NT_SUCCESS(NtStatus)) { + + // + // Get and save the account name for + // I_NetNotifyLogonOfDelta. + // + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_GROUP_NAME, + TRUE, // Make copy + &GroupName + ); + + if (!NT_SUCCESS(NtStatus)) { + RtlInitUnicodeString(&GroupName, NULL); + } + } + + + // + // Dereference the account context + // + + if (NT_SUCCESS(NtStatus)) { + + // + // De-reference the object, write out any change to current xaction. + // + + NtStatus = SampDeReferenceContext( AccountContext, TRUE ); + + } else { + + // + // De-reference the object, ignore changes + // + + TmpStatus = SampDeReferenceContext( AccountContext, FALSE ); + ASSERT(NT_SUCCESS(TmpStatus)); + } + + } + + + // + // Commit the transaction and notify net logon of the changes + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampCommitAndRetainWriteLock(); + + if ( NT_SUCCESS( NtStatus ) ) { + + SAM_DELTA_DATA DeltaData; + + // + // Fill in id of member being added + // + + DeltaData.GroupMemberId.MemberRid = MemberId; + + SampNotifyNetlogonOfDelta( + SecurityDbChangeMemberAdd, + SecurityDbObjectSamGroup, + ObjectRid, + &GroupName, + (DWORD) FALSE, // Replicate immediately + &DeltaData + ); + } + } + + + // + // Free up the group name + // + + SampFreeUnicodeString(&GroupName); + + + + TmpStatus = SampReleaseWriteLock( FALSE ); + ASSERT(NT_SUCCESS(TmpStatus)); + + return(NtStatus); +} + + + +NTSTATUS +SamrDeleteGroup( + IN SAMPR_HANDLE *GroupHandle + ) + +/*++ + +Routine Description: + + This API removes a group from the account database. There may be no + members in the group or the deletion request will be rejected. Note + that this API requires DELETE access to the specific group being + deleted. + + +Parameters: + + GroupHandle - The handle of an opened group to operate on. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. This may be + because someone has deleted the group while it was open. + + STATUS_SPECIAL_ACCOUNT - The group specified is a special group and + cannot be operated on in the requested fashion. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled for this + operation + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. + + +--*/ +{ + + UNICODE_STRING GroupName; + NTSTATUS NtStatus, TmpStatus; + PSAMP_OBJECT AccountContext; + PSAMP_DEFINED_DOMAINS Domain; + PSID AccountSid; + SAMP_OBJECT_TYPE FoundType; + ULONG MemberCount, + ObjectRid, + DomainIndex; + + + SAMTRACE("SamrDeleteGroup"); + + + + // + // Grab the lock + // + + NtStatus = SampAcquireWriteLock(); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + + // + // Validate type of, and access to object. + // + + AccountContext = (PSAMP_OBJECT)(*GroupHandle); + NtStatus = SampLookupContext( + AccountContext, + DELETE, + SampGroupObjectType, // ExpectedType + &FoundType + ); + + if (NT_SUCCESS(NtStatus)) { + + ObjectRid = AccountContext->TypeBody.Group.Rid; + + // + // Get a pointer to the domain this object is in. + // This is used for auditing. + // + + DomainIndex = AccountContext->DomainIndex; + Domain = &SampDefinedDomains[ DomainIndex ]; + + // + // Make sure the account is one that can be deleted. + // Can't be a built-in account, unless the caller is trusted. + // + + if ( !AccountContext->TrustedClient ) { + + NtStatus = SampIsAccountBuiltIn( ObjectRid ); + } + + + if (NT_SUCCESS( NtStatus) ) { + + // + // and it can't have any members + // + + NtStatus = SampRetrieveGroupMembers( + AccountContext, + &MemberCount, + NULL // Only need member count (not list) + ); + + if (MemberCount != 0) { + NtStatus = STATUS_MEMBER_IN_GROUP; + } + + } + + + + if (NT_SUCCESS(NtStatus)) { + + // + // Remove this account from all aliases + // + + + NtStatus = SampCreateAccountSid(AccountContext, &AccountSid); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampRemoveAccountFromAllAliases( + AccountSid, + FALSE, + NULL, + NULL, + NULL ); + } + } + + + // + // Looks promising. + + if (NT_SUCCESS(NtStatus)) { + + // + // First get and save the account name for + // I_NetNotifyLogonOfDelta. + // + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_GROUP_NAME, + TRUE, // Make copy + &GroupName + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // This must be done before we invalidate contexts, because our + // own handle to the group gets closed as well. + // + + NtStatus = SampDeleteGroupKeys( AccountContext ); + + if (NT_SUCCESS(NtStatus)) { + + // + // We must invalidate any open contexts to this group. + // This will close all handles to the group's keys. + // THIS IS AN IRREVERSIBLE PROCESS. + // + + SampInvalidateGroupContexts( ObjectRid ); + + + // + // Commit the whole mess + // + + NtStatus = SampCommitAndRetainWriteLock(); + + if ( NT_SUCCESS( NtStatus ) ) { + + SAMP_ACCOUNT_DISPLAY_INFO AccountInfo; + + // + // Update the Cached Alias Information + // + + NtStatus = SampAlRemoveAccountFromAllAliases( + AccountSid, + FALSE, + NULL, + NULL, + NULL + ); + + MIDL_user_free(AccountSid); + + + // + // Update the display information + // + + AccountInfo.Name = GroupName; + AccountInfo.Rid = ObjectRid; + AccountInfo.AccountControl = 0; // Don't care about this value for delete + RtlInitUnicodeString(&AccountInfo.Comment, NULL); + RtlInitUnicodeString(&AccountInfo.FullName, NULL); + + TmpStatus = SampUpdateDisplayInformation(&AccountInfo, + NULL, + SampGroupObjectType); + ASSERT(NT_SUCCESS(TmpStatus)); + + // + // Audit the deletion before we free the write lock + // so that we have access to the context block. + // + + if (SampDoAccountAuditing(DomainIndex) && + NT_SUCCESS(NtStatus) ) { + + LsaIAuditSamEvent( + STATUS_SUCCESS, + SE_AUDITID_GLOBAL_GROUP_DELETED, // AuditId + Domain->Sid, // Domain SID + NULL, // Member Rid (not used) + NULL, // Member Sid (not used) + &GroupName, // Account Name + &Domain->ExternalName, // Domain + &ObjectRid, // Account Rid + NULL // Privileges used + ); + + } + + // + // Do delete auditing + // + + if (NT_SUCCESS(NtStatus)) { + (VOID) NtDeleteObjectAuditAlarm( + &SampSamSubsystem, + *GroupHandle, + AccountContext->AuditOnClose + ); + } + + // + // Notify netlogon of the change + // + + SampNotifyNetlogonOfDelta( + SecurityDbDelete, + SecurityDbObjectSamGroup, + ObjectRid, + &GroupName, + (DWORD) FALSE, // Replicate immediately + NULL // Delta data + ); + } + } + + SampFreeUnicodeString( &GroupName ); + } + } + + + + // + // De-reference the object, discarding changes + // + + TmpStatus = SampDeReferenceContext( AccountContext, FALSE ); + ASSERT(NT_SUCCESS(TmpStatus)); + + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // If we actually deleted the group, delete the context and + // let RPC know that the handle is invalid. + // + + SampDeleteContext( AccountContext ); + + (*GroupHandle) = NULL; + } + + } //end_if + + // + // Free the lock - + // + // Everything has already been committed above, so we must indicate + // no additional changes have taken place. + // + // + // + + + TmpStatus = SampReleaseWriteLock( FALSE ); + + if (NtStatus == STATUS_SUCCESS) { + NtStatus = TmpStatus; + } + + return(NtStatus); + +} + + +NTSTATUS +SamrRemoveMemberFromGroup( + IN SAMPR_HANDLE GroupHandle, + IN ULONG MemberId + ) + +/*++ + +Routine Description: + + This service + +Arguments: + + ???? + +Return Value: + + + ???? + + +--*/ +{ + SAMP_V1_0A_FIXED_LENGTH_GROUP GroupV1Fixed; + NTSTATUS NtStatus, TmpStatus; + PSAMP_OBJECT AccountContext; + SAMP_OBJECT_TYPE FoundType; + ULONG ObjectRid; + BOOLEAN UserAccountActive; + UNICODE_STRING GroupName; + + + SAMTRACE("SamrRemoveMemberFromGroup"); + + + // + // Initialize buffers to be cleaned up at the end + // + + RtlInitUnicodeString(&GroupName, NULL); + + + + // + // Grab the lock + // + + NtStatus = SampAcquireWriteLock(); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + + + // + // Validate type of, and access to object. + // + + AccountContext = (PSAMP_OBJECT)(GroupHandle); + ObjectRid = AccountContext->TypeBody.Group.Rid; + NtStatus = SampLookupContext( + AccountContext, + GROUP_REMOVE_MEMBER, + SampGroupObjectType, // ExpectedType + &FoundType + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampRetrieveGroupV1Fixed( + AccountContext, + &GroupV1Fixed + ); + + + if (NT_SUCCESS(NtStatus)) { + + // + // Perform the user object side of things + // + + NtStatus = SampRemoveMembershipUser( + ObjectRid, + MemberId, + (GroupV1Fixed.AdminCount == 0) ? NoChange : RemoveFromAdmin, + (GroupV1Fixed.OperatorCount == 0) ? NoChange : RemoveFromAdmin, + &UserAccountActive + ); + if (NtStatus == STATUS_NO_SUCH_USER) + { + // + // It is not an User Object. It can be a group Object + // as from NT5 Onwards we support adding groups to group + // memberships + // + + NtStatus = SampRemoveMembershipGroup( + ObjectRid, + MemberId, + (GroupV1Fixed.AdminCount == 0) ? NoChange : AddToAdmin, + (GroupV1Fixed.OperatorCount == 0) ? NoChange : AddToAdmin + ); + } + + + + + // + // Now perform the group side of things + // + + if (NT_SUCCESS(NtStatus)) { + + // + // Remove the user from the group (should not fail) + // + + NtStatus = SampRemoveAccountFromGroupMembers( + AccountContext, + MemberId + ); + } + } + + + if (NT_SUCCESS(NtStatus)) { + + // + // Get and save the account name for + // I_NetNotifyLogonOfDelta. + // + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_GROUP_NAME, + TRUE, // Make copy + &GroupName + ); + + if (!NT_SUCCESS(NtStatus)) { + RtlInitUnicodeString(&GroupName, NULL); + } + } + + + // + // Dereference the account context + // + + if (NT_SUCCESS(NtStatus)) { + + // + // De-reference the object, write out any change to current xaction. + // + + NtStatus = SampDeReferenceContext( AccountContext, TRUE ); + + } else { + + // + // De-reference the object, ignore changes + // + + TmpStatus = SampDeReferenceContext( AccountContext, FALSE ); + ASSERT(NT_SUCCESS(TmpStatus)); + } + + } + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampCommitAndRetainWriteLock(); + + if ( NT_SUCCESS( NtStatus ) ) { + + SAM_DELTA_DATA DeltaData; + + // + // Fill in id of member being deleted + // + + DeltaData.GroupMemberId.MemberRid = MemberId; + + SampNotifyNetlogonOfDelta( + SecurityDbChangeMemberDel, + SecurityDbObjectSamGroup, + ObjectRid, + &GroupName, + (DWORD) FALSE, // Replicate immediately + &DeltaData + ); + } + } + + + // + // Free up the group name + // + + SampFreeUnicodeString(&GroupName); + + + TmpStatus = SampReleaseWriteLock( FALSE ); + ASSERT(NT_SUCCESS(TmpStatus)); + + return(NtStatus); +} + + +NTSTATUS +SamrGetMembersInGroup( + IN SAMPR_HANDLE GroupHandle, + OUT PSAMPR_GET_MEMBERS_BUFFER *GetMembersBuffer + ) + +/*++ + +Routine Description: + + This API lists all the members in a group. This API may be called + repeatedly, passing a returned context handle, to retrieve large + amounts of data. This API requires GROUP_LIST_MEMBERS access to the + group. + + + + +Parameters: + + GroupHandle - The handle of an opened group to operate on. + GROUP_LIST_MEMBERS access is needed to the group. + + GetMembersBuffer - Receives a pointer to a set of returned structures + with the following format: + + +-------------+ + --------->| MemberCount | + |-------------+ +-------+ + | Members --|------------------->| Rid-0 | + |-------------| +------------+ | ... | + | Attributes-|-->| Attribute0 | | | + +-------------+ | ... | | Rid-N | + | AttributeN | +-------+ + +------------+ + + Each block individually allocated with MIDL_user_allocate. + + + +Return Values: + + STATUS_SUCCESS - The Service completed successfully, and there + are no addition entries. + + STATUS_ACCESS_DENIED - Caller does not have privilege required to + request that data. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + This service + + + +--*/ +{ + + NTSTATUS NtStatus; + NTSTATUS IgnoreStatus; + ULONG i; + ULONG ObjectRid; + PSAMP_OBJECT AccountContext; + SAMP_OBJECT_TYPE FoundType; + + SAMTRACE("SamrGetMembersInGroup"); + + + // + // Make sure we understand what RPC is doing for (to) us. + // + + ASSERT (GetMembersBuffer != NULL); + + if ((*GetMembersBuffer) != NULL) { + return(STATUS_INVALID_PARAMETER); + } + + + + // + // Allocate the first of the return buffers + // + + (*GetMembersBuffer) = MIDL_user_allocate( sizeof(SAMPR_GET_MEMBERS_BUFFER) ); + + if ( (*GetMembersBuffer) == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Grab the lock + // + + SampAcquireReadLock(); + + + // + // Validate type of, and access to object. + // + + AccountContext = (PSAMP_OBJECT)GroupHandle; + ObjectRid = AccountContext->TypeBody.Group.Rid; + NtStatus = SampLookupContext( + AccountContext, + GROUP_LIST_MEMBERS, + SampGroupObjectType, // ExpectedType + &FoundType + ); + + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampRetrieveGroupMembers( + AccountContext, + &(*GetMembersBuffer)->MemberCount, + &(*GetMembersBuffer)->Members + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Allocate a buffer for the attributes - which we get from + // the individual user records + // + + (*GetMembersBuffer)->Attributes = MIDL_user_allocate((*GetMembersBuffer)->MemberCount * sizeof(ULONG) ); + if ((*GetMembersBuffer)->Attributes == NULL) { + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + } + + for ( i=0; (i<((*GetMembersBuffer)->MemberCount) && NT_SUCCESS(NtStatus)); i++) { + + NtStatus = SampRetrieveUserGroupAttribute( + (*GetMembersBuffer)->Members[i], + ObjectRid, + &(*GetMembersBuffer)->Attributes[i] + ); + } + + if (!NT_SUCCESS(NtStatus)) { + MIDL_user_free( (*GetMembersBuffer)->Members ); + if ((*GetMembersBuffer)->Attributes != NULL) { + MIDL_user_free( (*GetMembersBuffer)->Attributes ); + } + } + } + + + // + // De-reference the object, discarding changes + // + + IgnoreStatus = SampDeReferenceContext( AccountContext, FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + } + + // + // Free the read lock + // + + SampReleaseReadLock(); + + + if (!NT_SUCCESS(NtStatus) || ((*GetMembersBuffer)->MemberCount == 0)){ + + (*GetMembersBuffer)->MemberCount = 0; + (*GetMembersBuffer)->Members = NULL; + (*GetMembersBuffer)->Attributes = NULL; + } + + return( NtStatus ); +} + + +NTSTATUS +SamrSetMemberAttributesOfGroup( + IN SAMPR_HANDLE GroupHandle, + IN ULONG MemberId, + IN ULONG Attributes + ) + +/*++ + +Routine Description: + + + This routine modifies the group attributes of a member of the group. + This routine is a NO - OP for the DS case + + + +Parameters: + + GroupHandle - The handle of an opened group to operate on. + + MemberId - Contains the relative ID of member whose attributes + are to be modified. + + Attributes - The group attributes to set for the member. These + attributes must not conflict with the attributes of the group + as a whole. See SamAddMemberToGroup() for more information + on compatible attribute settings. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully, and there + are no addition entries. + + STATUS_INVALID_INFO_CLASS - The class provided was invalid. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_NO_SUCH_USER - The user specified does not exist. + + STATUS_MEMBER_NOT_IN_GROUP - Indicates the specified relative ID + is not a member of the group. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled for this + operation + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. + + +--*/ + +{ + + NTSTATUS NtStatus, TmpStatus; + PSAMP_OBJECT AccountContext; + SAMP_OBJECT_TYPE FoundType; + ULONG ObjectRid; + + + + SAMTRACE("SamrSetMemberAttributesOfGroup"); + + + + // + // Grab the lock + // + + NtStatus = SampAcquireWriteLock(); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + + + // + // Validate type of, and access to object. + // + + AccountContext = (PSAMP_OBJECT)(GroupHandle); + ObjectRid = AccountContext->TypeBody.Group.Rid; + NtStatus = SampLookupContext( + AccountContext, + GROUP_ADD_MEMBER, + SampGroupObjectType, // ExpectedType + &FoundType + ); + + if ((NT_SUCCESS(NtStatus))&& (!IsDsObject(AccountContext))) { + + // + // Update user object + // + + NtStatus = SampSetGroupAttributesOfUser( + ObjectRid, + Attributes, + MemberId + ); + + // + // Dereference the account context + // + + if (NT_SUCCESS(NtStatus)) { + + // + // De-reference the object, write out any change to current xaction. + // + + NtStatus = SampDeReferenceContext( AccountContext, TRUE ); + + } else { + + // + // De-reference the object, ignore changes + // + + TmpStatus = SampDeReferenceContext( AccountContext, FALSE ); + ASSERT(NT_SUCCESS(TmpStatus)); + } + + } + + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampCommitAndRetainWriteLock(); + + if ( NT_SUCCESS( NtStatus ) ) { + + SampNotifyNetlogonOfDelta( + SecurityDbChange, + SecurityDbObjectSamGroup, + ObjectRid, + (PUNICODE_STRING) NULL, + (DWORD) FALSE, // Replicate immediately + NULL // Delta data + ); + } + } + + TmpStatus = SampReleaseWriteLock( FALSE ); + + + return(NtStatus); +} + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Internal Services Available For Use in Other SAM Modules // +// // +/////////////////////////////////////////////////////////////////////////////// + +NTSTATUS +SampAddUserToGroup( + IN ULONG GroupRid, + IN ULONG UserRid + ) + +/*++ + +Routine Description: + + This service is expected to be used when a user is being created. + It is used to add that user as a member to a specified group. + This is done by simply adding the user's ID to the list of IDs + in the MEMBERS sub-key of the the specified group. + + + The caller of this service is expected to be in the middle of a + RXACT transaction. This service simply adds some actions to that + RXACT transaction. + + + If the group is the DOMAIN_ADMIN group, the caller is responsible + for updating the ActiveAdminCount (if appropriate). + + + +Arguments: + + GroupRid - The RID of the group the user is to be made a member of. + + UserRid - The RID of the user being added as a new member. + +Return Value: + + + STATUS_SUCCESS - The user has been added. + + + +--*/ +{ + NTSTATUS NtStatus; + PSAMP_OBJECT GroupContext; + + SAMTRACE("SampAddUserToGroup"); + + + NtStatus = SampCreateAccountContext( + SampGroupObjectType, + GroupRid, + TRUE, // Trusted client + TRUE, // Account exists + &GroupContext + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Add the user to the group member list. + // + + NtStatus = SampAddAccountToGroupMembers( + GroupContext, + UserRid + ); + + // + // Write out any changes to the group account + // Don't use the open key handle since we'll be deleting the context. + // + + if (NT_SUCCESS(NtStatus)) { + NtStatus = SampStoreObjectAttributes(GroupContext, FALSE); + } + + // + // Clean up the group context + // + + SampDeleteContext(GroupContext); + + } + + return(NtStatus); +} + + + +NTSTATUS +SampRemoveUserFromGroup( + IN ULONG GroupRid, + IN ULONG UserRid + ) + +/*++ + +Routine Description: + + This routine is used to Remove a user from a specified group. + This is done by simply Removing the user's ID From the list of IDs + in the MEMBERS sub-key of the the specified group. + + It is the caller's responsibility to know that the user is, in fact, + currently a member of the group. + + + The caller of this service is expected to be in the middle of a + RXACT transaction. This service simply adds some actions to that + RXACT transaction. + + + If the group is the DOMAIN_ADMIN group, the caller is responsible + for updating the ActiveAdminCount (if appropriate). + + + +Arguments: + + GroupRid - The RID of the group the user is to be removed from. + + UserRid - The RID of the user being Removed. + +Return Value: + + + STATUS_SUCCESS - The user has been Removed. + + + +--*/ +{ + NTSTATUS NtStatus; + PSAMP_OBJECT GroupContext; + + SAMTRACE("SampRemoveUserFromGroup"); + + + NtStatus = SampCreateAccountContext( + SampGroupObjectType, + GroupRid, + TRUE, // Trusted client + TRUE, // Account exists + &GroupContext + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Remove the user from the group member list. + // + + NtStatus = SampRemoveAccountFromGroupMembers( + GroupContext, + UserRid + ); + + // + // Write out any changes to the group account + // Don't use the open key handle since we'll be deleting the context. + // + + if (NT_SUCCESS(NtStatus)) { + NtStatus = SampStoreObjectAttributes(GroupContext, FALSE); + } + + // + // Clean up the group context + // + + SampDeleteContext(GroupContext); + + } + + return(NtStatus); +} + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Services Private to this file // +// // +/////////////////////////////////////////////////////////////////////////////// + + +NTSTATUS +SampRetrieveGroupV1Fixed( + IN PSAMP_OBJECT GroupContext, + IN PSAMP_V1_0A_FIXED_LENGTH_GROUP V1Fixed + ) + +/*++ + +Routine Description: + + This service retrieves the V1 fixed length information related to + a specified group. + + +Arguments: + + GroupRootKey - Root key for the group whose V1_FIXED information is + to be retrieved. + + V1Fixed - Is a buffer into which the information is to be returned. + + + +Return Value: + + + STATUS_SUCCESS - The information has been retrieved. + + Other status values that may be returned are those returned + by: + + SampGetFixedAttributes() + + + +--*/ +{ + NTSTATUS NtStatus; + PVOID FixedData; + + SAMTRACE("SampRetrieveGroupV1Fixed"); + + + NtStatus = SampGetFixedAttributes( + GroupContext, + FALSE, // Don't make copy + &FixedData + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Copy data into return buffer + // *V1Fixed = *((PSAMP_V1_0A_FIXED_LENGTH_GROUP)FixedData); + // + + RtlMoveMemory( + V1Fixed, + FixedData, + sizeof(SAMP_V1_0A_FIXED_LENGTH_GROUP) + ); + } + + + return( NtStatus ); + +} + + + + +NTSTATUS +SampReplaceGroupV1Fixed( + IN PSAMP_OBJECT Context, + IN PSAMP_V1_0A_FIXED_LENGTH_GROUP V1Fixed + ) + +/*++ + +Routine Description: + + This service replaces the current V1 fixed length information related to + a specified group. + + The change is made to the in-memory object data only. + + +Arguments: + + Context - Points to the account context whose V1_FIXED information is + to be replaced. + + V1Fixed - Is a buffer containing the new V1_FIXED information. + + + +Return Value: + + + STATUS_SUCCESS - The information has been replaced. + + Other status values that may be returned are those returned + by: + + SampSetFixedAttributes() + + + +--*/ +{ + NTSTATUS NtStatus; + + SAMTRACE("SampReplaceGroupV1Fixed"); + + NtStatus = SampSetFixedAttributes( + Context, + (PVOID)V1Fixed + ); + + return( NtStatus ); +} + + + +NTSTATUS +SampRetrieveGroupMembers( + IN PSAMP_OBJECT GroupContext, + IN PULONG MemberCount, + IN PULONG *Members OPTIONAL + ) + +/*++ +Routine Description: + + This service retrieves the number of members in a group. If desired, + it will also retrieve an array of RIDs of the members of the group. + + +Arguments: + + GroupContext - Group context block + + MemberCount - Receives the number of members currently in the group. + + Members - (Optional) Receives a pointer to a buffer containing an array + of member Relative IDs. If this value is NULL, then this information + is not returned. The returned buffer is allocated using + MIDL_user_allocate() and must be freed using MIDL_user_free() when + no longer needed. + + The Members array returned always includes space for one new entry. + + +Return Value: + + + STATUS_SUCCESS - The information has been retrieved. + + STATUS_INSUFFICIENT_RESOURCES - Memory could not be allocated for the + string to be returned in. + + Other status values that may be returned are those returned + by: + + SampGetUlongArrayAttribute() + + + +--*/ +{ + NTSTATUS NtStatus; + PULONG Array; + ULONG LengthCount; + + SAMTRACE("SampRetrieveGroupMembers"); + + // + // Do Different things for DS and Registry Cases + // + + if (IsDsObject(GroupContext)) + { + // + // DS case, this routine in DS layer does all the + // work + // + NtStatus = SampDsGetGroupMembershipList( + GroupContext->ObjectNameInDs, + Members, + MemberCount + ); + } + else + { + + // + // Registry Case + // + + + NtStatus = SampGetUlongArrayAttribute( + GroupContext, + SAMP_GROUP_MEMBERS, + FALSE, // Reference data directly + &Array, + MemberCount, + &LengthCount + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Fill in return info + // + + if (Members != NULL) { + + // + // Allocate a buffer large enough to hold the existing membership + // data plus one. + // + + ULONG BytesNow = (*MemberCount) * sizeof(ULONG); + ULONG BytesRequired = BytesNow + sizeof(ULONG); + + *Members = MIDL_user_allocate(BytesRequired); + + if (*Members == NULL) { + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + } else { + RtlCopyMemory(*Members, Array, BytesNow); + } + } + } + } + + return( NtStatus ); +} + + + +NTSTATUS +SampReplaceGroupMembers( + IN PSAMP_OBJECT GroupContext, + IN ULONG MemberCount, + IN PULONG Members + ) + +/*++ +Routine Description: + + This service sets the members of a group. + + The information is updated in the in-memory copy of the group's data only. + The data is not written out by this routine. + + +Arguments: + + GroupContext - The group whose member list is to be replaced + + MemberCount - The number of new members + + Membership - A pointer to a buffer containing an array of account rids. + + +Return Value: + + + STATUS_SUCCESS - The information has been set. + + Other status values that may be returned are those returned + by: + + SampSetUlongArrayAttribute() + + + +--*/ +{ + NTSTATUS NtStatus = STATUS_SUCCESS; + PULONG LocalMembers; + ULONG LengthCount; + ULONG SmallListGrowIncrement = 25; + ULONG BigListGrowIncrement = 250; + ULONG BigListSize = 800; + + SAMTRACE("SampReplaceGroupMembers"); + + // + // ASSERT that this is never called on a DS case + // + + ASSERT(!(IsDsObject(GroupContext))); + + + // + // These group user lists can get pretty big, and grow many + // times by a very small amount as each user is added. The + // registry doesn't like that kind of behaviour (it tends to + // eat up free space something fierce) so we'll try to pad + // out the list size. + // + + if ( MemberCount < BigListSize ) { + + // + // If less than 800 users, make the list size the smallest + // possible multiple of 25 users. + // + + LengthCount = ( ( MemberCount + SmallListGrowIncrement - 1 ) / + SmallListGrowIncrement ) * + SmallListGrowIncrement; + + } else { + + // + // If 800 users or more, make the list size the smallest + // possible multiple of 250 users. + // + + LengthCount = ( ( MemberCount + BigListGrowIncrement - 1 ) / + BigListGrowIncrement ) * + BigListGrowIncrement; + } + + ASSERT( LengthCount >= MemberCount ); + + if ( LengthCount == MemberCount ) { + + // + // Just the right size. Use the buffer that was passed in. + // + + LocalMembers = Members; + + } else { + + // + // We need to allocate a larger buffer before we set the attribute. + // + + LocalMembers = MIDL_user_allocate( LengthCount * sizeof(ULONG)); + + if ( LocalMembers == NULL ) { + + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + + } else { + + // + // Copy the old buffer to the larger buffer, and zero out the + // empty stuff at the end. + // + + RtlCopyMemory( LocalMembers, Members, MemberCount * sizeof(ULONG)); + + RtlZeroMemory( + (LocalMembers + MemberCount), + (LengthCount - MemberCount) * sizeof(ULONG) + ); + } + } + + if ( NT_SUCCESS( NtStatus ) ) { + + NtStatus = SampSetUlongArrayAttribute( + GroupContext, + SAMP_GROUP_MEMBERS, + LocalMembers, + MemberCount, + LengthCount + ); + } + + if ( LocalMembers != Members ) { + + // + // We must have allocated a larger local buffer, so free it. + // + + MIDL_user_free( LocalMembers ); + } + + return( NtStatus ); +} + + + +NTSTATUS +SampDeleteGroupKeys( + IN PSAMP_OBJECT Context + ) + +/*++ +Routine Description: + + This service deletes all registry keys related to a group object. + + +Arguments: + + Context - Points to the group context whose registry keys are + being deleted. + + +Return Value: + + + STATUS_SUCCESS - The information has been retrieved. + + + Other status values that may be returned by: + + RtlAddActionToRXact() + + + +--*/ +{ + + NTSTATUS NtStatus; + ULONG Rid; + UNICODE_STRING AccountName, KeyName; + + SAMTRACE("SampDeleteGroupKeys"); + + + Rid = Context->TypeBody.Group.Rid; + + + // + // Groups are arranged as follows: + // + // +-- Groups [Count] + // ---+-- + // +-- Names + // | --+-- + // | +-- (GroupName) [GroupRid,] + // | + // +-- (GroupRid) [Revision,SecurityDescriptor] + // ---+----- + // +-- V1_Fixed [,SAM_V1_0A_FIXED_LENGTH_GROUP] + // +-- Name [,Name] + // +-- AdminComment [,unicode string] + // +-- Members [Count,(Member0Rid, (...), MemberX-1Rid)] + // + // This all needs to be deleted from the bottom up. + // + + + // + // Decrement the group count + // + + NtStatus = SampAdjustAccountCount(SampGroupObjectType, FALSE ); + + + + + // + // Delete the registry key that has the group's name to RID mapping. + // + + if (NT_SUCCESS(NtStatus)) { + + // + // Get the name + // + + NtStatus = SampGetUnicodeStringAttribute( + Context, + SAMP_GROUP_NAME, + TRUE, // Make copy + &AccountName + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampBuildAccountKeyName( + SampGroupObjectType, + &KeyName, + &AccountName + ); + + SampFreeUnicodeString( &AccountName ); + + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = RtlAddActionToRXact( + SampRXactContext, + RtlRXactOperationDelete, + &KeyName, + 0, + NULL, + 0 + ); + + SampFreeUnicodeString( &KeyName ); + } + } + } + + + + + // + // Delete the attribute keys + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampDeleteAttributeKeys( + Context + ); + } + + + + + // + // Delete the RID key + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampBuildAccountSubKeyName( + SampGroupObjectType, + &KeyName, + Rid, + NULL + ); + + if (NT_SUCCESS(NtStatus)) { + + + NtStatus = RtlAddActionToRXact( + SampRXactContext, + RtlRXactOperationDelete, + &KeyName, + 0, + NULL, + 0 + ); + SampFreeUnicodeString( &KeyName ); + } + + + } + + + + return( NtStatus ); + +} + + + + +NTSTATUS +SampChangeGroupAccountName( + IN PSAMP_OBJECT Context, + IN PUNICODE_STRING NewAccountName, + OUT PUNICODE_STRING OldAccountName + ) + +/*++ +Routine Description: + + This routine changes the account name of a group account. + + THIS SERVICE MUST BE CALLED WITH THE TRANSACTION DOMAIN SET. + +Arguments: + + Context - Points to the group context whose name is to be changed. + + NewAccountName - New name to give this account + + OldAccountName - old name is returned here. The buffer should be freed + by calling MIDL_user_free. + +Return Value: + + + STATUS_SUCCESS - The information has been retrieved. + + + Other status values that may be returned by: + + SampGetUnicodeStringAttribute() + SampSetUnicodeStringAttribute() + SampValidateAccountNameChange() + RtlAddActionToRXact() + + + +--*/ +{ + + NTSTATUS NtStatus; + UNICODE_STRING KeyName; + + SAMTRACE("SampChangeGroupAccountName"); + + ///////////////////////////////////////////////////////////// + // There are two copies of the name of each account. // + // one is under the DOMAIN\(domainName)\GROUP\NAMES key, // + // one is the value of the // + // DOMAIN\(DomainName)\GROUP\(rid)\NAME key // + ///////////////////////////////////////////////////////////// + + // + // Get the current name so we can delete the old Name->Rid + // mapping key. + // + + NtStatus = SampGetUnicodeStringAttribute( + Context, + SAMP_GROUP_NAME, + TRUE, // Make copy + OldAccountName + ); + + // + // Make sure the name is valid and not already in use + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampValidateAccountNameChange( + NewAccountName, + OldAccountName + ); + + if (!IsDsObject(Context)) + { + // + // For Registry Case Re-Create Keys + // + + // + // Delete the old name key + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampBuildAccountKeyName( + SampGroupObjectType, + &KeyName, + OldAccountName + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = RtlAddActionToRXact( + SampRXactContext, + RtlRXactOperationDelete, + &KeyName, + 0, + NULL, + 0 + ); + SampFreeUnicodeString( &KeyName ); + } + + } + + // + // + // Create the new Name->Rid mapping key + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampBuildAccountKeyName( + SampGroupObjectType, + &KeyName, + NewAccountName + ); + + if (NT_SUCCESS(NtStatus)) { + + ULONG GroupRid = Context->TypeBody.Group.Rid; + + NtStatus = RtlAddActionToRXact( + SampRXactContext, + RtlRXactOperationSetValue, + &KeyName, + GroupRid, + (PVOID)NULL, + 0 + ); + SampFreeUnicodeString( &KeyName ); + } + } + + } + + + // + // replace the account's name + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampSetUnicodeStringAttribute( + Context, + SAMP_GROUP_NAME, + NewAccountName + ); + } + + // + // Free up the old account name if we failed + // + + if (!NT_SUCCESS(NtStatus)) { + SampFreeUnicodeString(OldAccountName); + } + + } + + + return(NtStatus); +} + + +NTSTATUS +SampAddAccountToGroupMembers( + IN PSAMP_OBJECT GroupContext, + IN ULONG AccountRid + ) + +/*++ + +Routine Description: + + This service adds the specified account rid to the member list + for the specified group. This is a low-level function that + simply edits the member attribute of the group context passed. + +Arguments: + + GroupContext - The group whose member list will be modified + + AccountRid - The RID of the account being added as a new member. + +Return Value: + + + STATUS_SUCCESS - The account has been added. + + STATUS_MEMBER_IN_GROUP - The account is already a member + +--*/ +{ + NTSTATUS NtStatus; + ULONG MemberCount, i; + PULONG MemberArray; + + SAMTRACE("SampAddAccountToGroupMembers"); + + // + // Do different things for DS and Registry + // + // + + if (IsDsObject(GroupContext)) + { + DSNAME * DsNameOfAccount = NULL; + // + // DS based Domain + // + + // + // Get the DSNAME corresponding to the given SID. + // This may result in a call to the GC server. + // + + NtStatus = SampDsLookupObjectByRid( + DomainObjectFromAccountContext(GroupContext), + AccountRid, + &DsNameOfAccount + ); + if NT_SUCCESS(NtStatus) + { + // + // Add this entry to the Ds + // + + NtStatus = SampDsAddMembershipAttribute( + GroupContext->ObjectNameInDs, + SampGroupObjectType, + DsNameOfAccount + ); + + MIDL_user_free(DsNameOfAccount); + } + + } + else + { + // + // Registry Case + // + + // + // Get the existing member list + // Note that the member array always includes space + // for one new member + // + + NtStatus = SampRetrieveGroupMembers( + GroupContext, + &MemberCount, + &MemberArray + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Fail if the account is already a member + // + + for (i = 0; i<MemberCount ; i++ ) { + + if ( MemberArray[i] == AccountRid ) { + + ASSERT(FALSE); + NtStatus = STATUS_MEMBER_IN_GROUP; + } + } + + + if (NT_SUCCESS(NtStatus)) { + + // + // Add the user's RID to the end of the list + // + + MemberArray[MemberCount] = AccountRid; + MemberCount += 1; + + // + // Set the new group member list + // + + NtStatus = SampReplaceGroupMembers( + GroupContext, + MemberCount, + MemberArray + ); + + + } + + // + // Free up the member list + // + + MIDL_user_free( MemberArray ); + + } + } + + // + // audit this, if necessary. + // + + if (NT_SUCCESS(NtStatus) && + SampDoAccountAuditing(GroupContext->DomainIndex)) { + + PSAMP_DEFINED_DOMAINS Domain; + UNICODE_STRING NameString; + SAMP_OBJECT_TYPE ObjectType; + NTSTATUS Status; + + Domain = &SampDefinedDomains[ GroupContext->DomainIndex ]; + + Status = SampLookupAccountName( + GroupContext->TypeBody.Alias.Rid, + &NameString, + &ObjectType + ); + + if ( !NT_SUCCESS( Status )) { + RtlInitUnicodeString( &NameString, L"-" ); + } + + LsaIAuditSamEvent( + STATUS_SUCCESS, + SE_AUDITID_GLOBAL_GROUP_ADD, // AuditId + Domain->Sid, // Domain SID + &AccountRid, // Member Rid + NULL, // Member Sid (not used) + &NameString, // Account Name + &Domain->ExternalName, // Domain + &GroupContext->TypeBody.Group.Rid, // Account Rid + NULL // Privileges used + ); + + if ( NT_SUCCESS( Status )) { + MIDL_user_free( NameString.Buffer ); + } + } + + + return(NtStatus); +} + + +NTSTATUS +SampRemoveAccountFromGroupMembers( + IN PSAMP_OBJECT GroupContext, + IN ULONG AccountRid + ) + +/*++ + +Routine Description: + + This service removes the specified account rid from the member list + for the specified group. This is a low-level function that + simply edits the member attribute of the group context passed. + +Arguments: + + GroupContext - The group whose member list will be modified + + AccountRid - The RID of the account being added as a new member. + +Return Value: + + + STATUS_SUCCESS - The account has been added. + + STATUS_MEMBER_NOT_IN_GROUP - The account is not a member of the group. + +--*/ +{ + NTSTATUS NtStatus; + ULONG MemberCount, i; + PULONG MemberArray; + + SAMTRACE("SampRemoveAccountFromGroupMembers"); + + // + // Do different things for registry and DS cases + // + + if (IsDsObject(GroupContext)) + { + DSNAME * DsNameOfAccount = NULL; + + // + // DS based Domain + // + + // + // Get the DSNAME corresponding to the given SID. + // This may result in a call to the GC server. + // + + NtStatus = SampDsLookupObjectByRid( + DomainObjectFromAccountContext(GroupContext), + AccountRid, + &DsNameOfAccount + ); + if NT_SUCCESS(NtStatus) + { + // + // Add this entry to the Ds + // + + NtStatus = SampDsRemoveMembershipAttribute( + GroupContext->ObjectNameInDs, + SampGroupObjectType, + DsNameOfAccount + ); + + MIDL_user_free(DsNameOfAccount); + } + + } + + else + { + + // + // Registry based Domain + // + + // + // Get the existing member list + // + + + NtStatus = SampRetrieveGroupMembers( + GroupContext, + &MemberCount, + &MemberArray + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Remove the account + // + + NtStatus = STATUS_MEMBER_NOT_IN_GROUP; + + for (i = 0; i<MemberCount ; i++ ) { + + if (MemberArray[i] == AccountRid) { + + MemberArray[i] = MemberArray[MemberCount-1]; + MemberCount -=1; + + NtStatus = STATUS_SUCCESS; + break; + } + } + + if (NT_SUCCESS(NtStatus)) { + + // + // Set the new group member list + // + + NtStatus = SampReplaceGroupMembers( + GroupContext, + MemberCount, + MemberArray + ); + + } + + // + // Free up the member list + // + + MIDL_user_free( MemberArray ); + } + + } + + // + // audit this, if necessary. + // + + if (NT_SUCCESS(NtStatus) && + SampDoAccountAuditing(GroupContext->DomainIndex)) { + + PSAMP_DEFINED_DOMAINS Domain; + UNICODE_STRING NameString; + SAMP_OBJECT_TYPE ObjectType; + NTSTATUS Status; + + Status = SampLookupAccountName( + GroupContext->TypeBody.Alias.Rid, + &NameString, + &ObjectType + ); + + if ( !NT_SUCCESS( Status )) { + RtlInitUnicodeString( &NameString, L"-" ); + } + Domain = &SampDefinedDomains[ GroupContext->DomainIndex ]; + + LsaIAuditSamEvent( + STATUS_SUCCESS, + SE_AUDITID_GLOBAL_GROUP_REM, // AuditId + Domain->Sid, // Domain SID + &AccountRid, // Member Rid + NULL, // Member Sid (not used) + &NameString, // Account Name + &Domain->ExternalName, // Domain + &GroupContext->TypeBody.Group.Rid, // Account Rid + NULL // Privileges used + ); + + + if ( NT_SUCCESS( Status )) { + MIDL_user_free( NameString.Buffer ); + } + } + + + return(NtStatus); +} + + +NTSTATUS +SampAddGroupToGroupMembership( + IN ULONG GroupRid, + IN ULONG Attributes, + IN ULONG MemberRid, + IN SAMP_MEMBERSHIP_DELTA AdminGroup, + IN SAMP_MEMBERSHIP_DELTA OperatorGroup + ) +/*++ + + Routine Description: + + This Routine is a place holder for a reverse membership list in a group object. + Currently it does the following + + 1. Does a Create Account Context to check the validity of the Group + Account Rid. + + BUG: + + In Future this should also look at the Membership Delta and transitively + modify the ACL's of all the members of the group described by + ToBeMemberGroupRid. + + Arguments: + + GroupRid - The relative ID of the group / Alias + + Attributes - The group attributes as the group is assigned to the + user. + + MemberRid - The relative ID of the user. + + AdminGroup - Indicates whether the group the user is being + added to is an administrator group (that is, directly + or indirectly a member of the Administrators alias). + + OperatorGroup - Indicates whether the group the user is being + added to is an operator group (that is, directly + or indirectly a member of the Account Operators, Print + Operators, Backup Operators, or Server Operators aliases) + + --*/ +{ + + NTSTATUS NtStatus; + PSAMP_OBJECT GroupContext; + + // + // For Now just Check to see if a Group with the Specified Rid Exists + + + NtStatus = SampCreateAccountContext( + SampGroupObjectType, + MemberRid, + TRUE, // We're trusted + TRUE, // Account exists + &GroupContext + ); + + if (NtStatus == STATUS_NO_SUCH_GROUP) + { + // + // Group Object Could not be Found, Try Alias Object + // + + NtStatus = SampCreateAccountContext( + SampAliasObjectType, + MemberRid, + TRUE, // We're trusted + TRUE, // Account exists + &GroupContext + ); + } + + + if (NT_SUCCESS(NtStatus)) + { + + // BUG: ADD Transitive ACL modification here. + SampDeReferenceContext(GroupContext, FALSE); + } + + return NtStatus; +} + +NTSTATUS +SampRemoveMembershipGroup( + IN ULONG GroupRid, + IN ULONG MemberRid, + IN SAMP_MEMBERSHIP_DELTA AdminGroup, + IN SAMP_MEMBERSHIP_DELTA OperatorGroup + ) + /*++ + + Routine Description: + + This Routine is a place holder for a reverse membership list in a group object. + Currently it does the following + + 1. Does a Create Account Context to check the validity of the Group + Account Rid. + + BUG: + + In Future this should also look at the Membership Delta and transitively + modify the ACL's of all the members of the group described by + ToBeMemberGroupRid. + + Arguments: + + GroupRid - The relative ID of the group. + + Attributes - The group attributes as the group is assigned to the + user. + + MemberRid - The relative ID of the user. + + AdminGroup - Indicates whether the group the user is being + added to is an administrator group (that is, directly + or indirectly a member of the Administrators alias). + + OperatorGroup - Indicates whether the group the user is being + added to is an operator group (that is, directly + or indirectly a member of the Account Operators, Print + Operators, Backup Operators, or Server Operators aliases) + + --*/ +{ + + NTSTATUS NtStatus; + PSAMP_OBJECT GroupContext; + + // + // For Now just Check to see if a Group with the Specified Rid Exists + // + + + NtStatus = SampCreateAccountContext( + SampGroupObjectType, + MemberRid, + TRUE, // We're trusted + TRUE, // Account exists + &GroupContext + ); + + if (NtStatus == STATUS_NO_SUCH_GROUP) + { + // + // Group Object Could not be Found, Try Alias Object + // + + NtStatus = SampCreateAccountContext( + SampAliasObjectType, + MemberRid, + TRUE, // We're trusted + TRUE, // Account exists + &GroupContext + ); + } + + if (NT_SUCCESS(NtStatus)) + { + + // BUG: ADD Transitive ACL modification here. + SampDeReferenceContext(GroupContext,FALSE); + } + + return NtStatus; +} + + diff --git a/private/newsam2/server/lsathunk.h b/private/newsam2/server/lsathunk.h new file mode 100644 index 000000000..283d296f9 --- /dev/null +++ b/private/newsam2/server/lsathunk.h @@ -0,0 +1,150 @@ +/*++ + +lsathunk.h + +Header file for the thunk layer for accessing the LSA through the +published NTLSAPI when SAM runs in user mode. User mode SAM is acomplished +by building with USER_MODE_SAM enabled. This causes all the SAM calls to +the LSA be remoted through the published NTLSAPI. + +Author: Murlis 4/30/96 + +Revision History + Murlis 4/30/96 + Created + +--*/ + +#ifndef _LSATHUNK_ +#define _LSATHUNK_ + +#ifdef USER_MODE_SAM + + +#define LSAPR_HANDLE LSA_HANDLE +#define PLSAPR_HANDLE PLSA_HANDLE +#define PLSAPR_POLICY_INFORMATION PVOID + +//++ Function prototypes for the Thunk Layer. + +NTSTATUS LsaThunkIAuditSamEvent( + IN NTSTATUS PassedStatus, + IN ULONG AuditId, + IN PSID DomainSid, + IN PULONG MemberRid OPTIONAL, + IN PSID MemberSid OPTIONAL, + IN PUNICODE_STRING AccountName OPTIONAL, + IN PUNICODE_STRING DomainName, + IN PULONG AccountRid OPTIONAL, + IN PPRIVILEGE_SET Privileges OPTIONAL + ); + +NTSTATUS LsaThunkIOpenPolicyTrusted( + OUT PLSAPR_HANDLE PolicyHandle + ); + + +NTSTATUS LsaThunkIFree_LSAPR_POLICY_INFORMATION( + POLICY_INFORMATION_CLASS InformationClass, + PLSAPR_POLICY_INFORMATION PolicyInformation + ); + + +NTSTATUS LsaThunkIAuditNotifyPackageLoad( + PUNICODE_STRING PackageFileName + ); + + +NTSTATUS LsaThunkrQueryInformationPolicy( + IN LSAPR_HANDLE PolicyHandle, + IN POLICY_INFORMATION_CLASS InformationClass, + OUT PLSAPR_POLICY_INFORMATION *Buffer + ); + +NTSTATUS LsaThunkrClose( + IN OUT LSAPR_HANDLE *ObjectHandle + ); + +NTSTATUS LsaThunkIQueryInformationPolicyTrusted( + IN POLICY_INFORMATION_CLASS InformationClass, + OUT PLSAPR_POLICY_INFORMATION *Buffer + ); + +NTSTATUS LsaThunkIHealthCheck( + IN ULONG CallerId + ); + +// Redifine the SAM functions that call LSA to go through +// the thunk layer. + + +#define LsaIAuditSamEvent(\ + PassedStatus,\ + AuditId,\ + DomainSid,\ + MemberRid,\ + MemberSid,\ + AccountName,\ + Domain,\ + AccountRid,\ + Privileges)\ + LsaThunkIAuditSamEvent(\ + PassedStatus,\ + AuditId,\ + DomainSid,\ + MemberRid,\ + MemberSid,\ + AccountName,\ + Domain,\ + AccountRid,\ + Privileges) + +#define LsaIOpenPolicyTrusted(\ + PolicyHandle)\ + LsaThunkIOpenPolicyTrusted(\ + PolicyHandle) + + +#define LsaIFree_LSAPR_POLICY_INFORMATION(\ + InformationClass,\ + PolicyInformation)\ + LsaThunkIFree_LSAPR_POLICY_INFORMATION(\ + InformationClass,\ + PolicyInformation) + + +#define LsaIAuditNotifyPackageLoad(\ + PackageFileName)\ + LsaThunkIAuditNotifyPackageLoad(\ + PackageFileName) + + +#define LsarQueryInformationPolicy(\ + PolicyHandle,\ + InformationClass,\ + Buffer)\ + LsaThunkrQueryInformationPolicy(\ + PolicyHandle,\ + InformationClass,\ + Buffer) + + + +#define LsarClose(\ + ObjectHandle)\ + LsaThunkrClose(\ + ObjectHandle) + +#define LsaIQueryInformationPolicyTrusted(\ + InformationClass,\ + Buffer)\ + LsaThunkIQueryInformationPolicyTrusted(\ + InformationClass,\ + Buffer) +#define LsaIHealthCheck(\ + CallerId)\ + LsaThunkIHealthCheck(\ + CallerId) + +#endif +#endif diff --git a/private/newsam2/server/notify.c b/private/newsam2/server/notify.c new file mode 100644 index 000000000..6506e4215 --- /dev/null +++ b/private/newsam2/server/notify.c @@ -0,0 +1,478 @@ + +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + notify.c + +Abstract: + + This file contains services which load notification packages and call + them when passwords are changed using the SamChangePasswordUser2 API. + + +Author: + + Mike Swift (MikeSw) 30-December-1994 + +Environment: + + User Mode - Win32 + +Revision History: + + +--*/ + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include <samsrvp.h> + +/////////////////////////////////////////////////////////////////////////////// +// // +// Private prototypes // +// // +/////////////////////////////////////////////////////////////////////////////// + +NTSTATUS +SampConfigurePackage( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// private service data and types // +// // +/////////////////////////////////////////////////////////////////////////////// + + + +typedef struct _SAMP_NOTIFICATION_PACKAGE { + struct _SAMP_NOTIFICATION_PACKAGE * Next; + UNICODE_STRING PackageName; + PSAM_PASSWORD_NOTIFICATION_ROUTINE PasswordNotificationRoutine; + PSAM_DELTA_NOTIFICATION_ROUTINE DeltaNotificationRoutine; +} SAMP_NOTIFICATION_PACKAGE, *PSAMP_NOTIFICATION_PACKAGE; + +PSAMP_NOTIFICATION_PACKAGE SampNotificationPackages = NULL; + +RTL_QUERY_REGISTRY_TABLE SampRegistryConfigTable [] = { + {SampConfigurePackage, 0, L"Notification Packages", + NULL, REG_NONE, NULL, 0}, + {NULL, 0, NULL, + NULL, REG_NONE, NULL, 0} + }; + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Routines // +// // +/////////////////////////////////////////////////////////////////////////////// + + + +NTSTATUS +SampConfigurePackage( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) +/*++ + +Routine Description: + + This routine loads a notification package by loading its DLL and getting + the address of the notification routine. + +Arguments: + ValueName - Contains the name of the registry value, ignored. + ValueType - Contains type of Value, must be REG_SZ. + ValueData - Contains the package name null-terminated string. + ValueLength - Length of package name and null terminator, in bytes. + Context - Passed from caller of RtlQueryRegistryValues, ignored + EntryContext - Ignored + + + +Return Value: + +--*/ +{ + UNICODE_STRING PackageName; + STRING NotificationRoutineName; + PSAMP_NOTIFICATION_PACKAGE NewPackage = NULL; + PVOID ModuleHandle = NULL; + NTSTATUS NtStatus = STATUS_SUCCESS; + ULONG PackageSize; + PSAM_INIT_NOTIFICATION_ROUTINE InitNotificationRoutine = NULL; + + SAMTRACE("SampConfigurePackage"); + + // + // Make sure we got a string. + // + + if (ValueType != REG_SZ) { + return(STATUS_SUCCESS); + } + + // + // Build the package name from the value data. + // + + PackageName.Buffer = (LPWSTR) ValueData; + PackageName.Length = (USHORT) (ValueLength - sizeof( UNICODE_NULL )); + PackageName.MaximumLength = (USHORT) ValueLength; + + // + // Build the package structure. + // + + PackageSize = sizeof(SAMP_NOTIFICATION_PACKAGE) + ValueLength; + NewPackage = (PSAMP_NOTIFICATION_PACKAGE) RtlAllocateHeap( + RtlProcessHeap(), + 0, + PackageSize + ); + if (NewPackage == NULL) { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + RtlZeroMemory( + NewPackage, + PackageSize + ); + + // + // Copy in the package name. + // + + NewPackage->PackageName = PackageName; + + NewPackage->PackageName.Buffer = (LPWSTR) (NewPackage + 1); + + + RtlCopyUnicodeString( + &NewPackage->PackageName, + &PackageName + ); + + // + // Load the notification library. + // + + NtStatus = LdrLoadDll( + NULL, + NULL, + &PackageName, + &ModuleHandle + ); + + + if (NT_SUCCESS(NtStatus)) { + + RtlInitString( + &NotificationRoutineName, + SAM_INIT_NOTIFICATION_ROUTINE + ); + + NtStatus = LdrGetProcedureAddress( + ModuleHandle, + &NotificationRoutineName, + 0, + (PVOID *) &InitNotificationRoutine + ); + if (NT_SUCCESS(NtStatus)) { + ASSERT(InitNotificationRoutine != NULL); + + // + // Call the init routine. If it returns false, unload this + // DLL and continue on. + // + + if (!InitNotificationRoutine()) { + NtStatus = STATUS_INTERNAL_ERROR; + } + + } else { + // + // This call isn't required, so reset the status to + // STATUS_SUCCESS. + // + + NtStatus = STATUS_SUCCESS; + } + + } + + if (NT_SUCCESS(NtStatus)) { + + RtlInitString( + &NotificationRoutineName, + SAM_PASSWORD_CHANGE_NOTIFY_ROUTINE + ); + + (void) LdrGetProcedureAddress( + ModuleHandle, + &NotificationRoutineName, + 0, + (PVOID *) &NewPackage->PasswordNotificationRoutine + ); + + } + + if (NT_SUCCESS(NtStatus)) { + RtlInitString( + &NotificationRoutineName, + SAM_DELTA_NOTIFY_ROUTINE + ); + + (void) LdrGetProcedureAddress( + ModuleHandle, + &NotificationRoutineName, + 0, + (PVOID *) &NewPackage->DeltaNotificationRoutine + ); + } + + // + // At least one of the two functions must be present + // + + if ((NewPackage->PasswordNotificationRoutine == NULL) && + (NewPackage->DeltaNotificationRoutine == NULL)) { + + NtStatus = STATUS_INTERNAL_ERROR; + } + + // + // If all this succeeded, add the routine to the global list. + // + + + if (NT_SUCCESS(NtStatus)) { + + + NewPackage->Next = SampNotificationPackages; + SampNotificationPackages = NewPackage; + + // + // Notify the auditing code to record this event. + // + + LsaIAuditNotifyPackageLoad( + &PackageName + ); + + + } else { + + // + // Otherwise delete the entry. + // + + RtlFreeHeap( + RtlProcessHeap(), + 0, + NewPackage + ); + + if (ModuleHandle != NULL) { + (VOID) LdrUnloadDll( ModuleHandle ); + } + } + + return(STATUS_SUCCESS); +} + + + +NTSTATUS +SampLoadNotificationPackages( + ) +/*++ + +Routine Description: + + This routine retrieves the list of packages to be notified during + password change. + +Arguments: + + none + + +Return Value: + +--*/ +{ + NTSTATUS NtStatus; + + SAMTRACE("SampLoadNotificationPackages"); + + NtStatus = RtlQueryRegistryValues( + RTL_REGISTRY_CONTROL, + L"Lsa", + SampRegistryConfigTable, + NULL, // no context + NULL // no enviroment + ); + // + // Always return STATUS_SUCCESS so we don't block the system from + // booting. + // + + + return(STATUS_SUCCESS); +} + + +NTSTATUS +SampPasswordChangeNotify( + PUNICODE_STRING UserName, + ULONG RelativeId, + PUNICODE_STRING NewPassword + ) +/*++ + +Routine Description: + + This routine notifies packages of a password change. It requires that + the user no longer be locked so that other packages can write to the + user parameters field. + + +Arguments: + + UserName - Name of user whose password changed + + RelativeId - RID of the user whose password changed + + NewPassword - Cleartext new password for the user + +Return Value: + + STATUS_SUCCESS only - errors from packages are ignored. + +--*/ +{ + PSAMP_NOTIFICATION_PACKAGE Package; + NTSTATUS NtStatus; + + SAMTRACE("SampPasswordChangeNotify"); + + Package = SampNotificationPackages; + + while (Package != NULL) { + if ( Package->PasswordNotificationRoutine != NULL ) { + NtStatus = Package->PasswordNotificationRoutine( + UserName, + RelativeId, + NewPassword + ); + if (!NT_SUCCESS(NtStatus)) { + KdPrint(("Package %wZ failed to accept password change for user %wZ\n", + &Package->PackageName, UserName )); + } + } + + Package = Package->Next; + + + } + return(STATUS_SUCCESS); +} + + +NTSTATUS +SampDeltaChangeNotify( + IN PSID DomainSid, + IN SECURITY_DB_DELTA_TYPE DeltaType, + IN SECURITY_DB_OBJECT_TYPE ObjectType, + IN ULONG ObjectRid, + IN PUNICODE_STRING ObjectName, + IN PLARGE_INTEGER ModifiedCount, + IN PSAM_DELTA_DATA DeltaData OPTIONAL + ) +/*++ + +Routine Description: + + This routine notifies packages of a change to the SAM database. The + database is still locked for write access so it requires that nothing + it calls try to lock the database. + +Arguments: + + DomainSid - SID of domain for delta + + DeltaType - Type of delta (change, add ,delete) + + ObjectType - Type of object changed (user, alias, group ...) + + ObjectRid - ID of object changed + + ObjectName - Name of object changed + + ModifiedCount - Serial number of database after this last change + + DeltaData - Data describing the exact modification. + +Return Value: + + STATUS_SUCCESS only - errors from packages are ignored. + +--*/ +{ + PSAMP_NOTIFICATION_PACKAGE Package; + NTSTATUS NtStatus; + + SAMTRACE("SampDeltaChangeNotify"); + + Package = SampNotificationPackages; + + while (Package != NULL) { + + if (Package->DeltaNotificationRoutine != NULL) { + NtStatus = Package->DeltaNotificationRoutine( + DomainSid, + DeltaType, + ObjectType, + ObjectRid, + ObjectName, + ModifiedCount, + DeltaData + ); + + if (!NT_SUCCESS(NtStatus)) { + KdPrint(("Package %wZ failed to accept deltachange for object %wZ\n", + &Package->PackageName, ObjectName )); + } + } + + Package = Package->Next; + + + } + return(STATUS_SUCCESS); +} + diff --git a/private/newsam2/server/oldstub.c b/private/newsam2/server/oldstub.c new file mode 100644 index 000000000..4011d5b87 --- /dev/null +++ b/private/newsam2/server/oldstub.c @@ -0,0 +1,659 @@ +/*++ + +Copyright (c) 1994 Microsoft Corporation + +Module Name: + + oldstub.c + +Abstract: + + This file contains functions generated by midl v1.0. These + function were designed to only be called by the stubs, but + these paticular functions are called by user code. This + file is needed in order to compile sam with midl v2.0 which + doesn't generated these paticular functions anymore. + +Author: + + Mario Goertzel (MarioGo) Jan 10, 1994 + +Environment: + + User Mode - Win32 + +Revision History: + + +--*/ + +#include <samrpc.h> + +/* routine that frees graph for struct _RPC_UNICODE_STRING */ +void _fgs__RPC_UNICODE_STRING (RPC_UNICODE_STRING * _source) + { + if (_source->Buffer !=0) + { + MIDL_user_free((void *)(_source->Buffer)); + } + } + +/* routine that frees graph for struct _SAMPR_RID_ENUMERATION */ +void _fgs__SAMPR_RID_ENUMERATION (SAMPR_RID_ENUMERATION * _source) + { + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->Name); + } + +/* routine that frees graph for struct _SAMPR_ENUMERATION_BUFFER */ +void _fgs__SAMPR_ENUMERATION_BUFFER (SAMPR_ENUMERATION_BUFFER * _source) + { + if (_source->Buffer !=0) + { + { + unsigned long _sym9; + for (_sym9 = 0; _sym9 < (unsigned long )(0 + _source->EntriesRead); _sym9++) + { + _fgs__SAMPR_RID_ENUMERATION ((SAMPR_RID_ENUMERATION *)&_source->Buffer[_sym9]); + } + } + MIDL_user_free((void *)(_source->Buffer)); + } + } + +/* routine that frees graph for struct _SAMPR_SR_SECURITY_DESCRIPTOR */ +void _fgs__SAMPR_SR_SECURITY_DESCRIPTOR (SAMPR_SR_SECURITY_DESCRIPTOR * _source) + { + if (_source->SecurityDescriptor !=0) + { + MIDL_user_free((void *)(_source->SecurityDescriptor)); + } + } + +/* routine that frees graph for struct _SAMPR_GET_GROUPS_BUFFER */ +void _fgs__SAMPR_GET_GROUPS_BUFFER (SAMPR_GET_GROUPS_BUFFER * _source) + { + if (_source->Groups !=0) + { + MIDL_user_free((void *)(_source->Groups)); + } + } + +/* routine that frees graph for struct _SAMPR_GET_MEMBERS_BUFFER */ +void _fgs__SAMPR_GET_MEMBERS_BUFFER (SAMPR_GET_MEMBERS_BUFFER * _source) + { + if (_source->Members !=0) + { + MIDL_user_free((void *)(_source->Members)); + } + if (_source->Attributes !=0) + { + MIDL_user_free((void *)(_source->Attributes)); + } + } + +/* routine that frees graph for struct _SAMPR_LOGON_HOURS */ +void _fgs__SAMPR_LOGON_HOURS (SAMPR_LOGON_HOURS * _source) + { + if (_source->LogonHours !=0) + { + MIDL_user_free((void *)(_source->LogonHours)); + } + } + +/* routine that frees graph for struct _SAMPR_ULONG_ARRAY */ +void _fgs__SAMPR_ULONG_ARRAY (SAMPR_ULONG_ARRAY * _source) + { + if (_source->Element !=0) + { + MIDL_user_free((void *)(_source->Element)); + } + } + +/* routine that frees graph for struct _SAMPR_SID_INFORMATION */ +void _fgs__SAMPR_SID_INFORMATION (SAMPR_SID_INFORMATION * _source) + { + if (_source->SidPointer !=0) + { + MIDL_user_free((void *)(_source->SidPointer)); + } + } + +/* routine that frees graph for struct _SAMPR_PSID_ARRAY */ +void _fgs__SAMPR_PSID_ARRAY (SAMPR_PSID_ARRAY * _source) + { + if (_source->Sids !=0) + { + MIDL_user_free((void *)(_source->Sids)); + } + } + + +/* routine that frees graph for struct _SAMPR_RETURNED_USTRING_ARRAY */ +void _fgs__SAMPR_RETURNED_USTRING_ARRAY (SAMPR_RETURNED_USTRING_ARRAY * _source) + { + if (_source->Element !=0) + { + { + unsigned long _sym26; + for (_sym26 = 0; _sym26 < (unsigned long )(0 + _source->Count); _sym26++) + { + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->Element[_sym26]); + } + } + MIDL_user_free((void *)(_source->Element)); + } + } + +/* routine that frees graph for struct _SAMPR_DOMAIN_GENERAL_INFORMATION */ +void _fgs__SAMPR_DOMAIN_GENERAL_INFORMATION (SAMPR_DOMAIN_GENERAL_INFORMATION * _source) + { + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->OemInformation); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->DomainName); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->ReplicaSourceNodeName); + } + +/* routine that frees graph for struct _SAMPR_DOMAIN_GENERAL_INFORMATION2 */ +void _fgs__SAMPR_DOMAIN_GENERAL_INFORMATION2 (SAMPR_DOMAIN_GENERAL_INFORMATION2 * _source) + { + _fgs__SAMPR_DOMAIN_GENERAL_INFORMATION ((SAMPR_DOMAIN_GENERAL_INFORMATION *)&_source->I1); + } + +/* routine that frees graph for struct _SAMPR_DOMAIN_OEM_INFORMATION */ +void _fgs__SAMPR_DOMAIN_OEM_INFORMATION (SAMPR_DOMAIN_OEM_INFORMATION * _source) + { + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->OemInformation); + } + +/* routine that frees graph for struct _SAMPR_DOMAIN_NAME_INFORMATION */ +void _fgs__SAMPR_DOMAIN_NAME_INFORMATION (SAMPR_DOMAIN_NAME_INFORMATION * _source) + { + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->DomainName); + } + +/* routine that frees graph for struct SAMPR_DOMAIN_REPLICATION_INFORMATION */ +void _fgs_SAMPR_DOMAIN_REPLICATION_INFORMATION (SAMPR_DOMAIN_REPLICATION_INFORMATION * _source) + { + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->ReplicaSourceNodeName); + } + +/* routine that frees graph for union _SAMPR_DOMAIN_INFO_BUFFER */ +void _fgu__SAMPR_DOMAIN_INFO_BUFFER (SAMPR_DOMAIN_INFO_BUFFER * _source, DOMAIN_INFORMATION_CLASS _branch) + { + switch (_branch) + { + case DomainPasswordInformation : + { + break; + } + case DomainGeneralInformation : + { + _fgs__SAMPR_DOMAIN_GENERAL_INFORMATION ((SAMPR_DOMAIN_GENERAL_INFORMATION *)&_source->General); + break; + } + case DomainLogoffInformation : + { + break; + } + case DomainOemInformation : + { + _fgs__SAMPR_DOMAIN_OEM_INFORMATION ((SAMPR_DOMAIN_OEM_INFORMATION *)&_source->Oem); + break; + } + case DomainNameInformation : + { + _fgs__SAMPR_DOMAIN_NAME_INFORMATION ((SAMPR_DOMAIN_NAME_INFORMATION *)&_source->Name); + break; + } + case DomainServerRoleInformation : + { + break; + } + case DomainReplicationInformation : + { + _fgs_SAMPR_DOMAIN_REPLICATION_INFORMATION ((SAMPR_DOMAIN_REPLICATION_INFORMATION *)&_source->Replication); + break; + } + case DomainModifiedInformation : + { + break; + } + case DomainStateInformation : + { + break; + } + case DomainGeneralInformation2 : + { + _fgs__SAMPR_DOMAIN_GENERAL_INFORMATION2 ((SAMPR_DOMAIN_GENERAL_INFORMATION2 *)&_source->General2); + break; + } + case DomainLockoutInformation : + { + break; + } + case DomainModifiedInformation2 : + { + break; + } + } + } + +/* routine that frees graph for struct _SAMPR_GROUP_GENERAL_INFORMATION */ +void _fgs__SAMPR_GROUP_GENERAL_INFORMATION (SAMPR_GROUP_GENERAL_INFORMATION * _source) + { + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->Name); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->AdminComment); + } + +/* routine that frees graph for struct _SAMPR_GROUP_NAME_INFORMATION */ +void _fgs__SAMPR_GROUP_NAME_INFORMATION (SAMPR_GROUP_NAME_INFORMATION * _source) + { + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->Name); + } + +/* routine that frees graph for struct _SAMPR_GROUP_ADM_COMMENT_INFORMATION */ +void _fgs__SAMPR_GROUP_ADM_COMMENT_INFORMATION (SAMPR_GROUP_ADM_COMMENT_INFORMATION * _source) + { + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->AdminComment); + } + +/* routine that frees graph for union _SAMPR_GROUP_INFO_BUFFER */ +void _fgu__SAMPR_GROUP_INFO_BUFFER (SAMPR_GROUP_INFO_BUFFER * _source, GROUP_INFORMATION_CLASS _branch) + { + switch (_branch) + { + case GroupGeneralInformation : + { + _fgs__SAMPR_GROUP_GENERAL_INFORMATION ((SAMPR_GROUP_GENERAL_INFORMATION *)&_source->General); + break; + } + case GroupNameInformation : + { + _fgs__SAMPR_GROUP_NAME_INFORMATION ((SAMPR_GROUP_NAME_INFORMATION *)&_source->Name); + break; + } + case GroupAttributeInformation : + { + break; + } + case GroupAdminCommentInformation : + { + _fgs__SAMPR_GROUP_ADM_COMMENT_INFORMATION ((SAMPR_GROUP_ADM_COMMENT_INFORMATION *)&_source->AdminComment); + break; + } + } + } + +/* routine that frees graph for struct _SAMPR_ALIAS_GENERAL_INFORMATION */ +void _fgs__SAMPR_ALIAS_GENERAL_INFORMATION (SAMPR_ALIAS_GENERAL_INFORMATION * _source) + { + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->Name); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->AdminComment); + } + +/* routine that frees graph for struct _SAMPR_ALIAS_NAME_INFORMATION */ +void _fgs__SAMPR_ALIAS_NAME_INFORMATION (SAMPR_ALIAS_NAME_INFORMATION * _source) + { + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->Name); + } + +/* routine that frees graph for struct _SAMPR_ALIAS_ADM_COMMENT_INFORMATION */ +void _fgs__SAMPR_ALIAS_ADM_COMMENT_INFORMATION (SAMPR_ALIAS_ADM_COMMENT_INFORMATION * _source) + { + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->AdminComment); + } + +/* routine that frees graph for union _SAMPR_ALIAS_INFO_BUFFER */ +void _fgu__SAMPR_ALIAS_INFO_BUFFER (SAMPR_ALIAS_INFO_BUFFER * _source, ALIAS_INFORMATION_CLASS _branch) + { + switch (_branch) + { + case AliasGeneralInformation : + { + _fgs__SAMPR_ALIAS_GENERAL_INFORMATION ((SAMPR_ALIAS_GENERAL_INFORMATION *)&_source->General); + break; + } + case AliasNameInformation : + { + _fgs__SAMPR_ALIAS_NAME_INFORMATION ((SAMPR_ALIAS_NAME_INFORMATION *)&_source->Name); + break; + } + case AliasAdminCommentInformation : + { + _fgs__SAMPR_ALIAS_ADM_COMMENT_INFORMATION ((SAMPR_ALIAS_ADM_COMMENT_INFORMATION *)&_source->AdminComment); + break; + } + } + } + +/* routine that frees graph for struct _SAMPR_USER_ALL_INFORMATION */ +void _fgs__SAMPR_USER_ALL_INFORMATION (SAMPR_USER_ALL_INFORMATION * _source) + { + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->UserName); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->FullName); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->HomeDirectory); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->HomeDirectoryDrive); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->ScriptPath); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->ProfilePath); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->AdminComment); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->WorkStations); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->UserComment); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->Parameters); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->LmOwfPassword); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->NtOwfPassword); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->PrivateData); + _fgs__SAMPR_SR_SECURITY_DESCRIPTOR ((SAMPR_SR_SECURITY_DESCRIPTOR *)&_source->SecurityDescriptor); + _fgs__SAMPR_LOGON_HOURS ((SAMPR_LOGON_HOURS *)&_source->LogonHours); + } + +/* routine that frees graph for struct _SAMPR_USER_INTERNAL3_INFORMATION */ +void _fgs__SAMPR_USER_INTERNAL3_INFORMATION (SAMPR_USER_INTERNAL3_INFORMATION * _source) + { + _fgs__SAMPR_USER_ALL_INFORMATION ((SAMPR_USER_ALL_INFORMATION *)&_source->I1); + } + +/* routine that frees graph for struct _SAMPR_USER_GENERAL_INFORMATION */ +void _fgs__SAMPR_USER_GENERAL_INFORMATION (SAMPR_USER_GENERAL_INFORMATION * _source) + { + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->UserName); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->FullName); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->AdminComment); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->UserComment); + } + +/* routine that frees graph for struct _SAMPR_USER_PREFERENCES_INFORMATION */ +void _fgs__SAMPR_USER_PREFERENCES_INFORMATION (SAMPR_USER_PREFERENCES_INFORMATION * _source) + { + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->UserComment); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->Reserved1); + } + +/* routine that frees graph for struct _SAMPR_USER_PARAMETERS_INFORMATION */ +void _fgs__SAMPR_USER_PARAMETERS_INFORMATION (SAMPR_USER_PARAMETERS_INFORMATION * _source) + { + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->Parameters); + } + +/* routine that frees graph for struct _SAMPR_USER_LOGON_INFORMATION */ +void _fgs__SAMPR_USER_LOGON_INFORMATION (SAMPR_USER_LOGON_INFORMATION * _source) + { + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->UserName); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->FullName); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->HomeDirectory); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->HomeDirectoryDrive); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->ScriptPath); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->ProfilePath); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->WorkStations); + _fgs__SAMPR_LOGON_HOURS ((SAMPR_LOGON_HOURS *)&_source->LogonHours); + } + +/* routine that frees graph for struct _SAMPR_USER_ACCOUNT_INFORMATION */ +void _fgs__SAMPR_USER_ACCOUNT_INFORMATION (SAMPR_USER_ACCOUNT_INFORMATION * _source) + { + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->UserName); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->FullName); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->HomeDirectory); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->HomeDirectoryDrive); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->ScriptPath); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->ProfilePath); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->AdminComment); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->WorkStations); + _fgs__SAMPR_LOGON_HOURS ((SAMPR_LOGON_HOURS *)&_source->LogonHours); + } + +/* routine that frees graph for struct _SAMPR_USER_A_NAME_INFORMATION */ +void _fgs__SAMPR_USER_A_NAME_INFORMATION (SAMPR_USER_A_NAME_INFORMATION * _source) + { + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->UserName); + } + +/* routine that frees graph for struct _SAMPR_USER_F_NAME_INFORMATION */ +void _fgs__SAMPR_USER_F_NAME_INFORMATION (SAMPR_USER_F_NAME_INFORMATION * _source) + { + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->FullName); + } + +/* routine that frees graph for struct _SAMPR_USER_NAME_INFORMATION */ +void _fgs__SAMPR_USER_NAME_INFORMATION (SAMPR_USER_NAME_INFORMATION * _source) + { + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->UserName); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->FullName); + } + +/* routine that frees graph for struct _SAMPR_USER_HOME_INFORMATION */ +void _fgs__SAMPR_USER_HOME_INFORMATION (SAMPR_USER_HOME_INFORMATION * _source) + { + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->HomeDirectory); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->HomeDirectoryDrive); + } + +/* routine that frees graph for struct _SAMPR_USER_SCRIPT_INFORMATION */ +void _fgs__SAMPR_USER_SCRIPT_INFORMATION (SAMPR_USER_SCRIPT_INFORMATION * _source) + { + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->ScriptPath); + } + +/* routine that frees graph for struct _SAMPR_USER_PROFILE_INFORMATION */ +void _fgs__SAMPR_USER_PROFILE_INFORMATION (SAMPR_USER_PROFILE_INFORMATION * _source) + { + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->ProfilePath); + } + +/* routine that frees graph for struct _SAMPR_USER_ADMIN_COMMENT_INFORMATION */ +void _fgs__SAMPR_USER_ADMIN_COMMENT_INFORMATION (SAMPR_USER_ADMIN_COMMENT_INFORMATION * _source) + { + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->AdminComment); + } + +/* routine that frees graph for struct _SAMPR_USER_WORKSTATIONS_INFORMATION */ +void _fgs__SAMPR_USER_WORKSTATIONS_INFORMATION (SAMPR_USER_WORKSTATIONS_INFORMATION * _source) + { + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->WorkStations); + } + +/* routine that frees graph for struct _SAMPR_USER_LOGON_HOURS_INFORMATION */ +void _fgs__SAMPR_USER_LOGON_HOURS_INFORMATION (SAMPR_USER_LOGON_HOURS_INFORMATION * _source) + { + _fgs__SAMPR_LOGON_HOURS ((SAMPR_LOGON_HOURS *)&_source->LogonHours); + } + +/* routine that frees graph for union _SAMPR_USER_INFO_BUFFER */ +void _fgu__SAMPR_USER_INFO_BUFFER (SAMPR_USER_INFO_BUFFER * _source, USER_INFORMATION_CLASS _branch) + { + switch (_branch) + { + case UserGeneralInformation : + { + _fgs__SAMPR_USER_GENERAL_INFORMATION ((SAMPR_USER_GENERAL_INFORMATION *)&_source->General); + break; + } + case UserPreferencesInformation : + { + _fgs__SAMPR_USER_PREFERENCES_INFORMATION ((SAMPR_USER_PREFERENCES_INFORMATION *)&_source->Preferences); + break; + } + case UserLogonInformation : + { + _fgs__SAMPR_USER_LOGON_INFORMATION ((SAMPR_USER_LOGON_INFORMATION *)&_source->Logon); + break; + } + case UserLogonHoursInformation : + { + _fgs__SAMPR_USER_LOGON_HOURS_INFORMATION ((SAMPR_USER_LOGON_HOURS_INFORMATION *)&_source->LogonHours); + break; + } + case UserAccountInformation : + { + _fgs__SAMPR_USER_ACCOUNT_INFORMATION ((SAMPR_USER_ACCOUNT_INFORMATION *)&_source->Account); + break; + } + case UserNameInformation : + { + _fgs__SAMPR_USER_NAME_INFORMATION ((SAMPR_USER_NAME_INFORMATION *)&_source->Name); + break; + } + case UserAccountNameInformation : + { + _fgs__SAMPR_USER_A_NAME_INFORMATION ((SAMPR_USER_A_NAME_INFORMATION *)&_source->AccountName); + break; + } + case UserFullNameInformation : + { + _fgs__SAMPR_USER_F_NAME_INFORMATION ((SAMPR_USER_F_NAME_INFORMATION *)&_source->FullName); + break; + } + case UserPrimaryGroupInformation : + { + break; + } + case UserHomeInformation : + { + _fgs__SAMPR_USER_HOME_INFORMATION ((SAMPR_USER_HOME_INFORMATION *)&_source->Home); + break; + } + case UserScriptInformation : + { + _fgs__SAMPR_USER_SCRIPT_INFORMATION ((SAMPR_USER_SCRIPT_INFORMATION *)&_source->Script); + break; + } + case UserProfileInformation : + { + _fgs__SAMPR_USER_PROFILE_INFORMATION ((SAMPR_USER_PROFILE_INFORMATION *)&_source->Profile); + break; + } + case UserAdminCommentInformation : + { + _fgs__SAMPR_USER_ADMIN_COMMENT_INFORMATION ((SAMPR_USER_ADMIN_COMMENT_INFORMATION *)&_source->AdminComment); + break; + } + case UserWorkStationsInformation : + { + _fgs__SAMPR_USER_WORKSTATIONS_INFORMATION ((SAMPR_USER_WORKSTATIONS_INFORMATION *)&_source->WorkStations); + break; + } + case UserControlInformation : + { + break; + } + case UserExpiresInformation : + { + break; + } + case UserInternal1Information : + { + break; + } + case UserInternal2Information : + { + break; + } + case UserParametersInformation : + { + _fgs__SAMPR_USER_PARAMETERS_INFORMATION ((SAMPR_USER_PARAMETERS_INFORMATION *)&_source->Parameters); + break; + } + case UserAllInformation : + { + _fgs__SAMPR_USER_ALL_INFORMATION ((SAMPR_USER_ALL_INFORMATION *)&_source->All); + break; + } + case UserInternal3Information : + { + _fgs__SAMPR_USER_INTERNAL3_INFORMATION ((SAMPR_USER_INTERNAL3_INFORMATION *)&_source->Internal3); + break; + } + } + } + +/* routine that frees graph for struct _SAMPR_DOMAIN_DISPLAY_USER */ +void _fgs__SAMPR_DOMAIN_DISPLAY_USER (SAMPR_DOMAIN_DISPLAY_USER * _source) + { + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->LogonName); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->AdminComment); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->FullName); + } + +/* routine that frees graph for struct _SAMPR_DOMAIN_DISPLAY_MACHINE */ +void _fgs__SAMPR_DOMAIN_DISPLAY_MACHINE (SAMPR_DOMAIN_DISPLAY_MACHINE * _source) + { + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->Machine); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->Comment); + } + +/* routine that frees graph for struct _SAMPR_DOMAIN_DISPLAY_GROUP */ +void _fgs__SAMPR_DOMAIN_DISPLAY_GROUP (SAMPR_DOMAIN_DISPLAY_GROUP * _source) + { + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->Group); + _fgs__RPC_UNICODE_STRING ((RPC_UNICODE_STRING *)&_source->Comment); + } + +/* routine that frees graph for struct _SAMPR_DOMAIN_DISPLAY_USER_BUFFER */ +void _fgs__SAMPR_DOMAIN_DISPLAY_USER_BUFFER (SAMPR_DOMAIN_DISPLAY_USER_BUFFER * _source) + { + if (_source->Buffer !=0) + { + { + unsigned long _sym32; + for (_sym32 = 0; _sym32 < (unsigned long )(0 + _source->EntriesRead); _sym32++) + { + _fgs__SAMPR_DOMAIN_DISPLAY_USER ((SAMPR_DOMAIN_DISPLAY_USER *)&_source->Buffer[_sym32]); + } + } + MIDL_user_free((void *)(_source->Buffer)); + } + } + +/* routine that frees graph for struct _SAMPR_DOMAIN_DISPLAY_MACHINE_BUFFER */ +void _fgs__SAMPR_DOMAIN_DISPLAY_MACHINE_BUFFER (SAMPR_DOMAIN_DISPLAY_MACHINE_BUFFER * _source) + { + if (_source->Buffer !=0) + { + { + unsigned long _sym38; + for (_sym38 = 0; _sym38 < (unsigned long )(0 + _source->EntriesRead); _sym38++) + { + _fgs__SAMPR_DOMAIN_DISPLAY_MACHINE ((SAMPR_DOMAIN_DISPLAY_MACHINE *)&_source->Buffer[_sym38]); + } + } + MIDL_user_free((void *)(_source->Buffer)); + } + } + +/* routine that frees graph for struct _SAMPR_DOMAIN_DISPLAY_GROUP_BUFFER */ +void _fgs__SAMPR_DOMAIN_DISPLAY_GROUP_BUFFER (SAMPR_DOMAIN_DISPLAY_GROUP_BUFFER * _source) + { + if (_source->Buffer !=0) + { + { + unsigned long _sym44; + for (_sym44 = 0; _sym44 < (unsigned long )(0 + _source->EntriesRead); _sym44++) + { + _fgs__SAMPR_DOMAIN_DISPLAY_GROUP ((SAMPR_DOMAIN_DISPLAY_GROUP *)&_source->Buffer[_sym44]); + } + } + MIDL_user_free((void *)(_source->Buffer)); + } + } + +/* routine that frees graph for union _SAMPR_DISPLAY_INFO_BUFFER */ +void _fgu__SAMPR_DISPLAY_INFO_BUFFER (SAMPR_DISPLAY_INFO_BUFFER * _source, DOMAIN_DISPLAY_INFORMATION _branch) + { + switch (_branch) + { + case DomainDisplayUser : + { + _fgs__SAMPR_DOMAIN_DISPLAY_USER_BUFFER ((SAMPR_DOMAIN_DISPLAY_USER_BUFFER *)&_source->UserInformation); + break; + } + case DomainDisplayMachine : + { + _fgs__SAMPR_DOMAIN_DISPLAY_MACHINE_BUFFER ((SAMPR_DOMAIN_DISPLAY_MACHINE_BUFFER *)&_source->MachineInformation); + break; + } + case DomainDisplayGroup : + { + _fgs__SAMPR_DOMAIN_DISPLAY_GROUP_BUFFER ((SAMPR_DOMAIN_DISPLAY_GROUP_BUFFER *)&_source->GroupInformation); + break; + } + } + } + diff --git a/private/newsam2/server/rundown.c b/private/newsam2/server/rundown.c new file mode 100644 index 000000000..810742252 --- /dev/null +++ b/private/newsam2/server/rundown.c @@ -0,0 +1,150 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + rundown.c + +Abstract: + + This file contains context handle rundown services + related to the SAM server RPC interface package.. + + +Author: + + Jim Kelly (JimK) 4-July-1991 + +Environment: + + User Mode - Win32 + +Revision History: + + +--*/ + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include <samsrvp.h> + + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// private service prototypes // +// // +/////////////////////////////////////////////////////////////////////////////// + + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Routines // +// // +/////////////////////////////////////////////////////////////////////////////// + + + + + + +void +SAMPR_HANDLE_rundown( + IN SAMPR_HANDLE SamHandle + ) + +/*++ + +Routine Description: + + This service is called if a binding breaks when a context_handle is + still active. + + Note that the RPC runtime will eliminate any race condition caused + by an outstanding call to the server with this context handle that + might cause the handle to become invalid before the rundown routine + is called. + +Arguments: + + SamHandle - The context handle value whose context must be rundown. + Note that as far as RPC is concerned, this handle is no longer + valid at the time the rundown call is made. + +Return Value: + + + None. + + +--*/ +{ + + NTSTATUS NtStatus; + PSAMP_OBJECT Context; + SAMP_OBJECT_TYPE FoundType; + + Context = (PSAMP_OBJECT)(SamHandle); + + + + SampAcquireReadLock(); + + // + // Lookup the context block + // + + NtStatus = SampLookupContext( + Context, // Context + 0L, // DesiredAccess + SampUnknownObjectType, // ExpectedType + &FoundType // FoundType + ); + + if (NT_SUCCESS(NtStatus)) { + + // TEMPORARY + //DbgPrint("Rundown of "); + //if (FoundType == SampServerObjectType) DbgPrint("Server "); + //if (FoundType == SampDomainObjectType) DbgPrint("Domain "); + //if (FoundType == SampGroupObjectType) DbgPrint("Group "); + //if (FoundType == SampUserObjectType) DbgPrint("User "); + //DbgPrint("context.\n"); + //DbgPrint(" Handle Value is: 0x%lx\n", Context ); + //if (Context->ReferenceCount != 2) { + //DbgPrint(" REFERENCE COUNT is: 0x%lx\n", Context->ReferenceCount); + //} + // TEMPORARY + + // + // Delete this context... + // + + SampDeleteContext( Context ); + + + // + // And drop our reference from the lookup operation + // + + SampDeReferenceContext( Context, FALSE); + + + } + + + SampReleaseReadLock(); + + + return; +} diff --git a/private/newsam2/server/sam_rev.rc b/private/newsam2/server/sam_rev.rc new file mode 100644 index 000000000..a0d85b4e0 --- /dev/null +++ b/private/newsam2/server/sam_rev.rc @@ -0,0 +1,10 @@ +#include <windows.h> +#include <ntverp.h> + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "SAM Server DLL" +#define VER_INTERNALNAME_STR "samsrv.dll" + +#include "common.ver" +RCINCLUDE sampmsgs.rc diff --git a/private/newsam2/server/samifree.c b/private/newsam2/server/samifree.c new file mode 100644 index 000000000..e8a3b9c9d --- /dev/null +++ b/private/newsam2/server/samifree.c @@ -0,0 +1,405 @@ +/*++ + +Copyright (c) 1992 Microsoft Corporation + +Module Name: + + samifree.c + +Abstract: + + This file contains routines to free structure allocated by the Samr + routines. These routines are used by SAM clients which live in the + same process as the SAM server and call the Samr routines directly. + + +Author: + + Cliff Van Dyke (CliffV) 26-Feb-1992 + +Environment: + + User Mode - Win32 + +Revision History: + + +--*/ + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include <samsrvp.h> + + + + + +VOID +SamIFree_SAMPR_SR_SECURITY_DESCRIPTOR ( + PSAMPR_SR_SECURITY_DESCRIPTOR Source + ) + +/*++ + +Routine Description: + + This routine free a SAMPR_SR_SECURITY_DESCRIPTOR and the graph of + allocated nodes it points to. + +Parameters: + + Source - A pointer to the node to free. + +Return Values: + + None. + +--*/ +{ + if ( Source != NULL ) { + _fgs__SAMPR_SR_SECURITY_DESCRIPTOR ( Source ); + MIDL_user_free (Source); + } +} + + + +VOID +SamIFree_SAMPR_DOMAIN_INFO_BUFFER ( + PSAMPR_DOMAIN_INFO_BUFFER Source, + DOMAIN_INFORMATION_CLASS Branch + ) + +/*++ + +Routine Description: + + This routine free a SAMPR_DOMAIN_INFO_BUFFER and the graph of + allocated nodes it points to. + +Parameters: + + Source - A pointer to the node to free. + + Branch - Specifies which branch of the union to free. + +Return Values: + + None. + +--*/ +{ + if ( Source != NULL ) { + _fgu__SAMPR_DOMAIN_INFO_BUFFER ( Source, Branch ); + MIDL_user_free (Source); + } +} + + +VOID +SamIFree_SAMPR_ENUMERATION_BUFFER ( + PSAMPR_ENUMERATION_BUFFER Source + ) + +/*++ + +Routine Description: + + This routine free a SAMPR_ENUMERATION_BUFFER and the graph of + allocated nodes it points to. + +Parameters: + + Source - A pointer to the node to free. + +Return Values: + + None. + +--*/ +{ + if ( Source != NULL ) { + _fgs__SAMPR_ENUMERATION_BUFFER ( Source ); + MIDL_user_free (Source); + } +} + + +VOID +SamIFree_SAMPR_PSID_ARRAY ( + PSAMPR_PSID_ARRAY Source + ) + +/*++ + +Routine Description: + + This routine free a the graph of allocated nodes pointed to + by a PSAMPR_PSID_ARRAY + +Parameters: + + Source - A pointer to the node to free. + +Return Values: + + None. + +--*/ +{ + if ( Source != NULL ) { + _fgs__SAMPR_PSID_ARRAY ( Source ); + } +} + + +VOID +SamIFree_SAMPR_ULONG_ARRAY ( + PSAMPR_ULONG_ARRAY Source + ) + +/*++ + +Routine Description: + + This routine free a SAMPR_ULONG_ARRAY and the graph of + allocated nodes it points to. + +Parameters: + + Source - A pointer to the node to free. + +Return Values: + + None. + +--*/ +{ + if ( Source != NULL ) { + _fgs__SAMPR_ULONG_ARRAY ( Source ); + // SAM never allocates this. + // MIDL_user_free (Source); + } +} + + +VOID +SamIFree_SAMPR_RETURNED_USTRING_ARRAY ( + PSAMPR_RETURNED_USTRING_ARRAY Source + ) + +/*++ + +Routine Description: + + This routine free a SAMPR_RETURNED_USTRING_ARRAY and the graph of + allocated nodes it points to. + +Parameters: + + Source - A pointer to the node to free. + +Return Values: + + None. + +--*/ +{ + if ( Source != NULL ) { + _fgs__SAMPR_RETURNED_USTRING_ARRAY ( Source ); + // SAM never allocates this. + // MIDL_user_free (Source); + } +} + + +VOID +SamIFree_SAMPR_GROUP_INFO_BUFFER ( + PSAMPR_GROUP_INFO_BUFFER Source, + GROUP_INFORMATION_CLASS Branch + ) + +/*++ + +Routine Description: + + This routine free a SAMPR_GROUP_INFO_BUFFER and the graph of + allocated nodes it points to. + +Parameters: + + Source - A pointer to the node to free. + + Branch - Specifies which branch of the union to free. + +Return Values: + + None. + +--*/ +{ + if ( Source != NULL ) { + _fgu__SAMPR_GROUP_INFO_BUFFER ( Source, Branch ); + MIDL_user_free (Source); + } +} + + +VOID +SamIFree_SAMPR_ALIAS_INFO_BUFFER ( + PSAMPR_ALIAS_INFO_BUFFER Source, + ALIAS_INFORMATION_CLASS Branch + ) + +/*++ + +Routine Description: + + This routine free a SAMPR_ALIAS_INFO_BUFFER and the graph of + allocated nodes it points to. + +Parameters: + + Source - A pointer to the node to free. + + Branch - Specifies which branch of the union to free. + +Return Values: + + None. + +--*/ +{ + if ( Source != NULL ) { + _fgu__SAMPR_ALIAS_INFO_BUFFER ( Source, Branch ); + MIDL_user_free (Source); + } +} + + +VOID +SamIFree_SAMPR_GET_MEMBERS_BUFFER ( + PSAMPR_GET_MEMBERS_BUFFER Source + ) + +/*++ + +Routine Description: + + This routine free a SAMPR_GET_MEMBERS_BUFFER and the graph of + allocated nodes it points to. + +Parameters: + + Source - A pointer to the node to free. + +Return Values: + + None. + +--*/ +{ + if ( Source != NULL ) { + _fgs__SAMPR_GET_MEMBERS_BUFFER ( Source ); + MIDL_user_free (Source); + } +} + + +VOID +SamIFree_SAMPR_USER_INFO_BUFFER ( + PSAMPR_USER_INFO_BUFFER Source, + USER_INFORMATION_CLASS Branch + ) + +/*++ + +Routine Description: + + This routine free a SAMPR_USER_INFO_BUFFER and the graph of + allocated nodes it points to. + +Parameters: + + Source - A pointer to the node to free. + + Branch - Specifies which branch of the union to free. + +Return Values: + + None. + +--*/ +{ + if ( Source != NULL ) { + _fgu__SAMPR_USER_INFO_BUFFER ( Source, Branch ); + MIDL_user_free (Source); + } +} + + +VOID +SamIFree_SAMPR_GET_GROUPS_BUFFER ( + PSAMPR_GET_GROUPS_BUFFER Source + ) + +/*++ + +Routine Description: + + This routine free a SAMPR_GET_GROUPS_BUFFER and the graph of + allocated nodes it points to. + +Parameters: + + Source - A pointer to the node to free. + +Return Values: + + None. + +--*/ +{ + if ( Source != NULL ) { + _fgs__SAMPR_GET_GROUPS_BUFFER ( Source ); + MIDL_user_free (Source); + } +} + + + +VOID +SamIFree_SAMPR_DISPLAY_INFO_BUFFER ( + PSAMPR_DISPLAY_INFO_BUFFER Source, + DOMAIN_DISPLAY_INFORMATION Branch + ) + +/*++ + +Routine Description: + + This routine free a SAMPR_DISPLAY_INFO_BUFFER and the graph of + allocated nodes it points to. + +Parameters: + + Source - A pointer to the node to free. + + Branch - Specifies which branch of the union to free. + +Return Values: + + None. + +--*/ +{ + if ( Source != NULL ) { + _fgu__SAMPR_DISPLAY_INFO_BUFFER ( Source, Branch ); + // SAM never allocates this. + // MIDL_user_free (Source); + } +} diff --git a/private/newsam2/server/sampmsgs.mc b/private/newsam2/server/sampmsgs.mc new file mode 100644 index 000000000..c74e1401b --- /dev/null +++ b/private/newsam2/server/sampmsgs.mc @@ -0,0 +1,258 @@ +;/*++ BUILD Version: 0001 // Increment this if a change has global effects +; +;Copyright (c) 1991-1993 Microsoft Corporation +; +;Module Name: +; +; sampmsgs.mc +; +;Abstract: +; +; SAM localizable text +; +;Author: +; +; Jim Kelly 1-Apr-1993 +; +;Revision History: +; +;Notes: +; +; +;--*/ +; +;#ifndef _SAMPMSGS_ +;#define _SAMPMSGS_ +; +;/*lint -save -e767 */ // Don't complain about different definitions // winnt + +MessageIdTypedef=DWORD + + +;// +;// Force facility code message to be placed in .h file +;// +MessageId=0x1FFF SymbolicName=SAMP_UNUSED_MESSAGE +Language=English +. + + +;//////////////////////////////////////////////////////////////////////////// +;// // +;// // +;// SAM Account Names // +;// // +;// // +;//////////////////////////////////////////////////////////////////////////// + + +MessageId=0x2000 SymbolicName=SAMP_USER_NAME_ADMIN +Language=English +Administrator +. + +MessageId=0x2001 SymbolicName=SAMP_USER_NAME_GUEST +Language=English +Guest +. + +MessageId=0x2002 SymbolicName=SAMP_GROUP_NAME_ADMINS +Language=English +Domain Admins +. + +MessageId=0x2003 SymbolicName=SAMP_GROUP_NAME_USERS +Language=English +Domain Users +. + +MessageId=0x2004 SymbolicName=SAMP_GROUP_NAME_NONE +Language=English +None +. + +MessageId=0x2005 SymbolicName=SAMP_ALIAS_NAME_ADMINS +Language=English +Administrators +. + +MessageId=0x2006 SymbolicName=SAMP_ALIAS_NAME_SERVER_OPS +Language=English +Server Operators +. + +MessageId=0x2007 SymbolicName=SAMP_ALIAS_NAME_POWER_USERS +Language=English +Power Users +. + +MessageId=0x2008 SymbolicName=SAMP_ALIAS_NAME_USERS +Language=English +Users +. + +MessageId=0x2009 SymbolicName=SAMP_ALIAS_NAME_GUESTS +Language=English +Guests +. + +MessageId=0x200A SymbolicName=SAMP_ALIAS_NAME_ACCOUNT_OPS +Language=English +Account Operators +. + +MessageId=0x200B SymbolicName=SAMP_ALIAS_NAME_PRINT_OPS +Language=English +Print Operators +. + +MessageId=0x200C SymbolicName=SAMP_ALIAS_NAME_BACKUP_OPS +Language=English +Backup Operators +. + +MessageId=0x200D SymbolicName=SAMP_ALIAS_NAME_REPLICATOR +Language=English +Replicator +. + + +;// Added for NT 1.0A +MessageId=0x200E SymbolicName=SAMP_GROUP_NAME_GUESTS +Language=English +Domain Guests +. + + + + +;//////////////////////////////////////////////////////////////////////////// +;// // +;// // +;// SAM Account Comments // +;// // +;// // +;//////////////////////////////////////////////////////////////////////////// + + +MessageId=0x2100 SymbolicName=SAMP_USER_COMMENT_ADMIN +Language=English +Built-in account for administering the computer/domain +. + +MessageId=0x2101 SymbolicName=SAMP_USER_COMMENT_GUEST +Language=English +Built-in account for guest access to the computer/domain +. + +MessageId=0x2102 SymbolicName=SAMP_GROUP_COMMENT_ADMINS +Language=English +Designated administrators of the domain +. + +MessageId=0x2103 SymbolicName=SAMP_GROUP_COMMENT_USERS +Language=English +All domain users +. + +MessageId=0x2104 SymbolicName=SAMP_GROUP_COMMENT_NONE +Language=English +Ordinary users +. + +MessageId=0x2105 SymbolicName=SAMP_ALIAS_COMMENT_ADMINS +Language=English +Members can fully administer the computer/domain +. + +MessageId=0x2106 SymbolicName=SAMP_ALIAS_COMMENT_SERVER_OPS +Language=English +Members can administer domain servers +. + +MessageId=0x2107 SymbolicName=SAMP_ALIAS_COMMENT_POWER_USERS +Language=English +Members can share directories and printers +. + +MessageId=0x2108 SymbolicName=SAMP_ALIAS_COMMENT_USERS +Language=English +Ordinary users +. + +MessageId=0x2109 SymbolicName=SAMP_ALIAS_COMMENT_GUESTS +Language=English +Users granted guest access to the computer/domain +. + +MessageId=0x210A SymbolicName=SAMP_ALIAS_COMMENT_ACCOUNT_OPS +Language=English +Members can administer domain user and group accounts +. + +MessageId=0x210B SymbolicName=SAMP_ALIAS_COMMENT_PRINT_OPS +Language=English +Members can administer domain printers +. + +MessageId=0x210C SymbolicName=SAMP_ALIAS_COMMENT_BACKUP_OPS +Language=English +Members can bypass file security to back up files +. + +MessageId=0x210D SymbolicName=SAMP_ALIAS_COMMENT_REPLICATOR +Language=English +Supports file replication in a domain +. + + +;// Added for NT1.0A +MessageId=0x210E SymbolicName=SAMP_GROUP_COMMENT_GUESTS +Language=English +All domain guests +. + + +;////////////////////////////////////////////////////////////////////// +;// +;// SAM Database Commit/Refresh Events +;// +;////////////////////////////////////////////////////////////////////// + +MessageId=0x3000 + SymbolicName=SAMMSG_COMMIT_FAILED + Language=English +SAM failed to write changes to the database. This is most likely due to +a memory or disk-space shortage. The SAM database will be restored to +an earlier state. Recent changes will be lost. Check the disk-space +available and maximum pagefile size setting. +. + + +MessageId=0x3001 + SymbolicName=SAMMSG_REFRESH_FAILED + Language=English +SAM failed to restore the database to an earlier state. SAM has shutdown. +You must reboot the machine to re-enable SAM. +. + + +MessageId=0x3002 + SymbolicName=SAMMSG_UPDATE_FAILED + Language=English +SAM failed to update the SAM database. It will try again next time you +reboot the machine. +. + +MessageId=0x3003 + SymbolicName=SAMMSG_RPC_INIT_FAILED + Language=English +SAM failed to start the TCP/IP or SPX/IPX listening thread +. + + + +;/*lint -restore */ // Resume checking for different macro definitions // winnt +; +; +;#endif // _SAMPMSGS_ diff --git a/private/newsam2/server/samrpc_s.c b/private/newsam2/server/samrpc_s.c new file mode 100644 index 000000000..e954d517a --- /dev/null +++ b/private/newsam2/server/samrpc_s.c @@ -0,0 +1,8293 @@ +/* this ALWAYS GENERATED file contains the RPC server stubs */ + + +/* File created by MIDL compiler version 3.00.15 */ +/* at Thu May 09 10:01:40 1996 + */ +/* Compiler settings for samrpc.idl, samsrv.acf: + Os, W1, Zp8, env=Win32, ms_ext, c_ext, oldnames + error checks: allocation ref +*/ +//@@MIDL_FILE_HEADING( ) + +#include <string.h> +#include "samrpc.h" + +#define TYPE_FORMAT_STRING_SIZE 3209 +#define PROC_FORMAT_STRING_SIZE 651 + +typedef struct _MIDL_TYPE_FORMAT_STRING + { + short Pad; + unsigned char Format[ TYPE_FORMAT_STRING_SIZE ]; + } MIDL_TYPE_FORMAT_STRING; + +typedef struct _MIDL_PROC_FORMAT_STRING + { + short Pad; + unsigned char Format[ PROC_FORMAT_STRING_SIZE ]; + } MIDL_PROC_FORMAT_STRING; + +extern const MIDL_TYPE_FORMAT_STRING __MIDLTypeFormatString; +extern const MIDL_PROC_FORMAT_STRING __MIDLProcFormatString; + +/* Standard interface: samr, ver. 1.0, + GUID={0x12345778,0x1234,0xABCD,{0xEF,0x00,0x01,0x23,0x45,0x67,0x89,0xAC}} */ + + +extern RPC_DISPATCH_TABLE samr_DispatchTable; + +static const RPC_SERVER_INTERFACE samr___RpcServerInterface = + { + sizeof(RPC_SERVER_INTERFACE), + {{0x12345778,0x1234,0xABCD,{0xEF,0x00,0x01,0x23,0x45,0x67,0x89,0xAC}},{1,0}}, + {{0x8A885D04,0x1CEB,0x11C9,{0x9F,0xE8,0x08,0x00,0x2B,0x10,0x48,0x60}},{2,0}}, + &samr_DispatchTable, + 0, + 0, + 0, + 0, + 0 + }; +RPC_IF_HANDLE samr_ServerIfHandle = (RPC_IF_HANDLE)& samr___RpcServerInterface; + +extern const MIDL_STUB_DESC samr_StubDesc; + +void __RPC_STUB +samr_SamrConnect( + PRPC_MESSAGE _pRpcMessage ) +{ + ACCESS_MASK DesiredAccess; + NDR_SCONTEXT ServerHandle; + PSAMPR_SERVER_NAME ServerName; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + ServerName = 0; + ServerHandle = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[0] ); + + NdrPointerUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&ServerName, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[0], + (unsigned char)0 ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *)(((long)_StubMsg.Buffer + 3) & ~ 0x3); + DesiredAccess = *(( ACCESS_MASK __RPC_FAR * )_StubMsg.Buffer)++; + + ServerHandle = NDRSContextUnmarshall( (char *)0, _pRpcMessage->DataRepresentation ); + + + _RetVal = SamrConnect( + ServerName, + ( SAMPR_HANDLE __RPC_FAR * )NDRSContextValue(ServerHandle), + DesiredAccess); + + _StubMsg.BufferLength = 20U + 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + NdrServerContextMarshall( + ( PMIDL_STUB_MESSAGE )&_StubMsg, + ( NDR_SCONTEXT )ServerHandle, + ( NDR_RUNDOWN )SAMPR_HANDLE_rundown); + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrCloseHandle( + PRPC_MESSAGE _pRpcMessage ) +{ + NDR_SCONTEXT SamHandle; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + SamHandle = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[12] ); + + SamHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + + _RetVal = SamrCloseHandle(( SAMPR_HANDLE __RPC_FAR * )NDRSContextValue(SamHandle)); + + _StubMsg.BufferLength = 20U + 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + NdrServerContextMarshall( + ( PMIDL_STUB_MESSAGE )&_StubMsg, + ( NDR_SCONTEXT )SamHandle, + ( NDR_RUNDOWN )SAMPR_HANDLE_rundown); + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrSetSecurityObject( + PRPC_MESSAGE _pRpcMessage ) +{ + NDR_SCONTEXT ObjectHandle; + PSAMPR_SR_SECURITY_DESCRIPTOR SecurityDescriptor; + SECURITY_INFORMATION SecurityInformation; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + SecurityDescriptor = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[18] ); + + ObjectHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + SecurityInformation = *(( SECURITY_INFORMATION __RPC_FAR * )_StubMsg.Buffer)++; + + NdrSimpleStructUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&SecurityDescriptor, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[38], + (unsigned char)0 ); + + + _RetVal = SamrSetSecurityObject( + ( SAMPR_HANDLE )*NDRSContextValue(ObjectHandle), + SecurityInformation, + SecurityDescriptor); + + _StubMsg.BufferLength = 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)SecurityDescriptor, + &__MIDLTypeFormatString.Format[24] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrQuerySecurityObject( + PRPC_MESSAGE _pRpcMessage ) +{ + NDR_SCONTEXT ObjectHandle; + PSAMPR_SR_SECURITY_DESCRIPTOR __RPC_FAR *SecurityDescriptor; + SECURITY_INFORMATION SecurityInformation; + PSAMPR_SR_SECURITY_DESCRIPTOR _M8; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + SecurityDescriptor = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[30] ); + + ObjectHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + SecurityInformation = *(( SECURITY_INFORMATION __RPC_FAR * )_StubMsg.Buffer)++; + + SecurityDescriptor = &_M8; + _M8 = 0; + + _RetVal = SamrQuerySecurityObject( + ( SAMPR_HANDLE )*NDRSContextValue(ObjectHandle), + SecurityInformation, + SecurityDescriptor); + + _StubMsg.BufferLength = 4U + 11U; + NdrPointerBufferSize( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR *)SecurityDescriptor, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[58] ); + + _StubMsg.BufferLength += 16; + + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + NdrPointerMarshall( (PMIDL_STUB_MESSAGE)& _StubMsg, + (unsigned char __RPC_FAR *)SecurityDescriptor, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[58] ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *)(((long)_StubMsg.Buffer + 3) & ~ 0x3); + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)SecurityDescriptor, + &__MIDLTypeFormatString.Format[58] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrShutdownSamServer( + PRPC_MESSAGE _pRpcMessage ) +{ + NDR_SCONTEXT ServerHandle; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[42] ); + + ServerHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + + _RetVal = SamrShutdownSamServer(( SAMPR_HANDLE )*NDRSContextValue(ServerHandle)); + + _StubMsg.BufferLength = 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrLookupDomainInSamServer( + PRPC_MESSAGE _pRpcMessage ) +{ + PRPC_SID __RPC_FAR *DomainId; + PRPC_UNICODE_STRING Name; + NDR_SCONTEXT ServerHandle; + PRPC_SID _M9; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + Name = 0; + DomainId = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[48] ); + + ServerHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + NdrSimpleStructUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&Name, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[84], + (unsigned char)0 ); + + DomainId = &_M9; + _M9 = 0; + + _RetVal = SamrLookupDomainInSamServer( + ( SAMPR_HANDLE )*NDRSContextValue(ServerHandle), + Name, + DomainId); + + _StubMsg.BufferLength = 4U + 11U; + NdrPointerBufferSize( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR *)DomainId, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[106] ); + + _StubMsg.BufferLength += 16; + + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + NdrPointerMarshall( (PMIDL_STUB_MESSAGE)& _StubMsg, + (unsigned char __RPC_FAR *)DomainId, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[106] ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *)(((long)_StubMsg.Buffer + 3) & ~ 0x3); + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)Name, + &__MIDLTypeFormatString.Format[66] ); + + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)DomainId, + &__MIDLTypeFormatString.Format[106] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrEnumerateDomainsInSamServer( + PRPC_MESSAGE _pRpcMessage ) +{ + PSAMPR_ENUMERATION_BUFFER __RPC_FAR *Buffer; + PULONG CountReturned; + PSAM_ENUMERATE_HANDLE EnumerationContext; + ULONG PreferedMaximumLength; + NDR_SCONTEXT ServerHandle; + PSAMPR_ENUMERATION_BUFFER _M10; + ULONG _M11; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + EnumerationContext = 0; + Buffer = 0; + CountReturned = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[62] ); + + ServerHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + EnumerationContext = ( ULONG __RPC_FAR * )_StubMsg.Buffer; + _StubMsg.Buffer += sizeof( ULONG ); + + PreferedMaximumLength = *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++; + + Buffer = &_M10; + _M10 = 0; + CountReturned = &_M11; + + _RetVal = SamrEnumerateDomainsInSamServer( + ( SAMPR_HANDLE )*NDRSContextValue(ServerHandle), + EnumerationContext, + Buffer, + PreferedMaximumLength, + CountReturned); + + _StubMsg.BufferLength = 4U + 4U + 11U + 7U; + NdrPointerBufferSize( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR *)Buffer, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[158] ); + + _StubMsg.BufferLength += 16; + + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++ = *EnumerationContext; + + NdrPointerMarshall( (PMIDL_STUB_MESSAGE)& _StubMsg, + (unsigned char __RPC_FAR *)Buffer, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[158] ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *)(((long)_StubMsg.Buffer + 3) & ~ 0x3); + *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++ = *CountReturned; + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)Buffer, + &__MIDLTypeFormatString.Format[158] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrOpenDomain( + PRPC_MESSAGE _pRpcMessage ) +{ + ACCESS_MASK DesiredAccess; + NDR_SCONTEXT DomainHandle; + PRPC_SID DomainId; + NDR_SCONTEXT ServerHandle; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + DomainId = 0; + DomainHandle = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[82] ); + + ServerHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + DesiredAccess = *(( ACCESS_MASK __RPC_FAR * )_StubMsg.Buffer)++; + + NdrConformantStructUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&DomainId, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[140], + (unsigned char)0 ); + + DomainHandle = NDRSContextUnmarshall( (char *)0, _pRpcMessage->DataRepresentation ); + + + _RetVal = SamrOpenDomain( + ( SAMPR_HANDLE )*NDRSContextValue(ServerHandle), + DesiredAccess, + DomainId, + ( SAMPR_HANDLE __RPC_FAR * )NDRSContextValue(DomainHandle)); + + _StubMsg.BufferLength = 20U + 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + NdrServerContextMarshall( + ( PMIDL_STUB_MESSAGE )&_StubMsg, + ( NDR_SCONTEXT )DomainHandle, + ( NDR_RUNDOWN )SAMPR_HANDLE_rundown); + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrQueryInformationDomain( + PRPC_MESSAGE _pRpcMessage ) +{ + PSAMPR_DOMAIN_INFO_BUFFER __RPC_FAR *Buffer; + NDR_SCONTEXT DomainHandle; + DOMAIN_INFORMATION_CLASS DomainInformationClass; + PSAMPR_DOMAIN_INFO_BUFFER _M12; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + Buffer = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[98] ); + + DomainHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + NdrSimpleTypeUnmarshall( + ( PMIDL_STUB_MESSAGE )&_StubMsg, + ( unsigned char __RPC_FAR * )&DomainInformationClass, + 13); + Buffer = &_M12; + _M12 = 0; + + _RetVal = SamrQueryInformationDomain( + ( SAMPR_HANDLE )*NDRSContextValue(DomainHandle), + DomainInformationClass, + Buffer); + + _StubMsg.BufferLength = 4U + 8U; + _StubMsg.MaxCount = DomainInformationClass; + + NdrPointerBufferSize( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR *)Buffer, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[272] ); + + _StubMsg.BufferLength += 16; + + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + _StubMsg.MaxCount = DomainInformationClass; + + NdrPointerMarshall( (PMIDL_STUB_MESSAGE)& _StubMsg, + (unsigned char __RPC_FAR *)Buffer, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[272] ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *)(((long)_StubMsg.Buffer + 3) & ~ 0x3); + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + _StubMsg.MaxCount = DomainInformationClass; + + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)Buffer, + &__MIDLTypeFormatString.Format[272] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrSetInformationDomain( + PRPC_MESSAGE _pRpcMessage ) +{ + NDR_SCONTEXT DomainHandle; + PSAMPR_DOMAIN_INFO_BUFFER DomainInformation; + DOMAIN_INFORMATION_CLASS DomainInformationClass; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + DomainInformation = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[110] ); + + DomainHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + NdrSimpleTypeUnmarshall( + ( PMIDL_STUB_MESSAGE )&_StubMsg, + ( unsigned char __RPC_FAR * )&DomainInformationClass, + 13); + NdrNonEncapsulatedUnionUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&DomainInformation, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[606], + (unsigned char)0 ); + + + _RetVal = SamrSetInformationDomain( + ( SAMPR_HANDLE )*NDRSContextValue(DomainHandle), + DomainInformationClass, + DomainInformation); + + _StubMsg.BufferLength = 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + _StubMsg.MaxCount = DomainInformationClass; + + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)DomainInformation, + &__MIDLTypeFormatString.Format[602] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrCreateGroupInDomain( + PRPC_MESSAGE _pRpcMessage ) +{ + ACCESS_MASK DesiredAccess; + NDR_SCONTEXT DomainHandle; + NDR_SCONTEXT GroupHandle; + PRPC_UNICODE_STRING Name; + PULONG RelativeId; + ULONG _M13; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + Name = 0; + GroupHandle = 0; + RelativeId = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[122] ); + + DomainHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + NdrSimpleStructUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&Name, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[84], + (unsigned char)0 ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *)(((long)_StubMsg.Buffer + 3) & ~ 0x3); + DesiredAccess = *(( ACCESS_MASK __RPC_FAR * )_StubMsg.Buffer)++; + + GroupHandle = NDRSContextUnmarshall( (char *)0, _pRpcMessage->DataRepresentation ); + + RelativeId = &_M13; + + _RetVal = SamrCreateGroupInDomain( + ( SAMPR_HANDLE )*NDRSContextValue(DomainHandle), + Name, + DesiredAccess, + ( SAMPR_HANDLE __RPC_FAR * )NDRSContextValue(GroupHandle), + RelativeId); + + _StubMsg.BufferLength = 20U + 4U + 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + NdrServerContextMarshall( + ( PMIDL_STUB_MESSAGE )&_StubMsg, + ( NDR_SCONTEXT )GroupHandle, + ( NDR_RUNDOWN )SAMPR_HANDLE_rundown); + + *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++ = *RelativeId; + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)Name, + &__MIDLTypeFormatString.Format[66] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrEnumerateGroupsInDomain( + PRPC_MESSAGE _pRpcMessage ) +{ + PSAMPR_ENUMERATION_BUFFER __RPC_FAR *Buffer; + PULONG CountReturned; + NDR_SCONTEXT DomainHandle; + PSAM_ENUMERATE_HANDLE EnumerationContext; + ULONG PreferedMaximumLength; + PSAMPR_ENUMERATION_BUFFER _M14; + ULONG _M15; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + EnumerationContext = 0; + Buffer = 0; + CountReturned = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[62] ); + + DomainHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + EnumerationContext = ( ULONG __RPC_FAR * )_StubMsg.Buffer; + _StubMsg.Buffer += sizeof( ULONG ); + + PreferedMaximumLength = *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++; + + Buffer = &_M14; + _M14 = 0; + CountReturned = &_M15; + + _RetVal = SamrEnumerateGroupsInDomain( + ( SAMPR_HANDLE )*NDRSContextValue(DomainHandle), + EnumerationContext, + Buffer, + PreferedMaximumLength, + CountReturned); + + _StubMsg.BufferLength = 4U + 4U + 11U + 7U; + NdrPointerBufferSize( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR *)Buffer, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[158] ); + + _StubMsg.BufferLength += 16; + + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++ = *EnumerationContext; + + NdrPointerMarshall( (PMIDL_STUB_MESSAGE)& _StubMsg, + (unsigned char __RPC_FAR *)Buffer, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[158] ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *)(((long)_StubMsg.Buffer + 3) & ~ 0x3); + *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++ = *CountReturned; + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)Buffer, + &__MIDLTypeFormatString.Format[158] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrCreateUserInDomain( + PRPC_MESSAGE _pRpcMessage ) +{ + ACCESS_MASK DesiredAccess; + NDR_SCONTEXT DomainHandle; + PRPC_UNICODE_STRING Name; + PULONG RelativeId; + NDR_SCONTEXT UserHandle; + ULONG _M16; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + Name = 0; + UserHandle = 0; + RelativeId = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[122] ); + + DomainHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + NdrSimpleStructUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&Name, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[84], + (unsigned char)0 ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *)(((long)_StubMsg.Buffer + 3) & ~ 0x3); + DesiredAccess = *(( ACCESS_MASK __RPC_FAR * )_StubMsg.Buffer)++; + + UserHandle = NDRSContextUnmarshall( (char *)0, _pRpcMessage->DataRepresentation ); + + RelativeId = &_M16; + + _RetVal = SamrCreateUserInDomain( + ( SAMPR_HANDLE )*NDRSContextValue(DomainHandle), + Name, + DesiredAccess, + ( SAMPR_HANDLE __RPC_FAR * )NDRSContextValue(UserHandle), + RelativeId); + + _StubMsg.BufferLength = 20U + 4U + 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + NdrServerContextMarshall( + ( PMIDL_STUB_MESSAGE )&_StubMsg, + ( NDR_SCONTEXT )UserHandle, + ( NDR_RUNDOWN )SAMPR_HANDLE_rundown); + + *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++ = *RelativeId; + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)Name, + &__MIDLTypeFormatString.Format[66] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrEnumerateUsersInDomain( + PRPC_MESSAGE _pRpcMessage ) +{ + PSAMPR_ENUMERATION_BUFFER __RPC_FAR *Buffer; + PULONG CountReturned; + NDR_SCONTEXT DomainHandle; + PSAM_ENUMERATE_HANDLE EnumerationContext; + ULONG PreferedMaximumLength; + ULONG UserAccountControl; + PSAMPR_ENUMERATION_BUFFER _M17; + ULONG _M18; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + EnumerationContext = 0; + Buffer = 0; + CountReturned = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[142] ); + + DomainHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + EnumerationContext = ( ULONG __RPC_FAR * )_StubMsg.Buffer; + _StubMsg.Buffer += sizeof( ULONG ); + + UserAccountControl = *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++; + + PreferedMaximumLength = *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++; + + Buffer = &_M17; + _M17 = 0; + CountReturned = &_M18; + + _RetVal = SamrEnumerateUsersInDomain( + ( SAMPR_HANDLE )*NDRSContextValue(DomainHandle), + EnumerationContext, + UserAccountControl, + Buffer, + PreferedMaximumLength, + CountReturned); + + _StubMsg.BufferLength = 4U + 4U + 11U + 7U; + NdrPointerBufferSize( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR *)Buffer, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[158] ); + + _StubMsg.BufferLength += 16; + + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++ = *EnumerationContext; + + NdrPointerMarshall( (PMIDL_STUB_MESSAGE)& _StubMsg, + (unsigned char __RPC_FAR *)Buffer, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[158] ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *)(((long)_StubMsg.Buffer + 3) & ~ 0x3); + *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++ = *CountReturned; + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)Buffer, + &__MIDLTypeFormatString.Format[158] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrCreateAliasInDomain( + PRPC_MESSAGE _pRpcMessage ) +{ + PRPC_UNICODE_STRING AccountName; + NDR_SCONTEXT AliasHandle; + ACCESS_MASK DesiredAccess; + NDR_SCONTEXT DomainHandle; + PULONG RelativeId; + ULONG _M19; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + AccountName = 0; + AliasHandle = 0; + RelativeId = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[122] ); + + DomainHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + NdrSimpleStructUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&AccountName, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[84], + (unsigned char)0 ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *)(((long)_StubMsg.Buffer + 3) & ~ 0x3); + DesiredAccess = *(( ACCESS_MASK __RPC_FAR * )_StubMsg.Buffer)++; + + AliasHandle = NDRSContextUnmarshall( (char *)0, _pRpcMessage->DataRepresentation ); + + RelativeId = &_M19; + + _RetVal = SamrCreateAliasInDomain( + ( SAMPR_HANDLE )*NDRSContextValue(DomainHandle), + AccountName, + DesiredAccess, + ( SAMPR_HANDLE __RPC_FAR * )NDRSContextValue(AliasHandle), + RelativeId); + + _StubMsg.BufferLength = 20U + 4U + 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + NdrServerContextMarshall( + ( PMIDL_STUB_MESSAGE )&_StubMsg, + ( NDR_SCONTEXT )AliasHandle, + ( NDR_RUNDOWN )SAMPR_HANDLE_rundown); + + *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++ = *RelativeId; + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)AccountName, + &__MIDLTypeFormatString.Format[66] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrEnumerateAliasesInDomain( + PRPC_MESSAGE _pRpcMessage ) +{ + PSAMPR_ENUMERATION_BUFFER __RPC_FAR *Buffer; + PULONG CountReturned; + NDR_SCONTEXT DomainHandle; + PSAM_ENUMERATE_HANDLE EnumerationContext; + ULONG PreferedMaximumLength; + PSAMPR_ENUMERATION_BUFFER _M20; + ULONG _M21; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + EnumerationContext = 0; + Buffer = 0; + CountReturned = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[62] ); + + DomainHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + EnumerationContext = ( ULONG __RPC_FAR * )_StubMsg.Buffer; + _StubMsg.Buffer += sizeof( ULONG ); + + PreferedMaximumLength = *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++; + + Buffer = &_M20; + _M20 = 0; + CountReturned = &_M21; + + _RetVal = SamrEnumerateAliasesInDomain( + ( SAMPR_HANDLE )*NDRSContextValue(DomainHandle), + EnumerationContext, + Buffer, + PreferedMaximumLength, + CountReturned); + + _StubMsg.BufferLength = 4U + 4U + 11U + 7U; + NdrPointerBufferSize( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR *)Buffer, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[158] ); + + _StubMsg.BufferLength += 16; + + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++ = *EnumerationContext; + + NdrPointerMarshall( (PMIDL_STUB_MESSAGE)& _StubMsg, + (unsigned char __RPC_FAR *)Buffer, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[158] ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *)(((long)_StubMsg.Buffer + 3) & ~ 0x3); + *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++ = *CountReturned; + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)Buffer, + &__MIDLTypeFormatString.Format[158] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrGetAliasMembership( + PRPC_MESSAGE _pRpcMessage ) +{ + NDR_SCONTEXT DomainHandle; + PSAMPR_ULONG_ARRAY Membership; + PSAMPR_PSID_ARRAY SidArray; + struct _SAMPR_ULONG_ARRAY _MembershipM; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + SidArray = 0; + Membership = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[164] ); + + DomainHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + NdrSimpleStructUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&SidArray, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[670], + (unsigned char)0 ); + + Membership = &_MembershipM; + Membership -> Element = 0; + + _RetVal = SamrGetAliasMembership( + ( SAMPR_HANDLE )*NDRSContextValue(DomainHandle), + SidArray, + Membership); + + _StubMsg.BufferLength = 0U + 11U; + NdrSimpleStructBufferSize( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR *)Membership, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[704] ); + + _StubMsg.BufferLength += 16; + + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + NdrSimpleStructMarshall( (PMIDL_STUB_MESSAGE)& _StubMsg, + (unsigned char __RPC_FAR *)Membership, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[704] ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *)(((long)_StubMsg.Buffer + 3) & ~ 0x3); + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)SidArray, + &__MIDLTypeFormatString.Format[614] ); + + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)Membership, + &__MIDLTypeFormatString.Format[690] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrLookupNamesInDomain( + PRPC_MESSAGE _pRpcMessage ) +{ + ULONG Count; + NDR_SCONTEXT DomainHandle; + RPC_UNICODE_STRING ( __RPC_FAR *Names )[ ]; + PSAMPR_ULONG_ARRAY RelativeIds; + PSAMPR_ULONG_ARRAY Use; + struct _SAMPR_ULONG_ARRAY _RelativeIdsM; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + struct _SAMPR_ULONG_ARRAY _UseM; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + Names = 0; + RelativeIds = 0; + Use = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[178] ); + + DomainHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + Count = *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++; + + NdrConformantVaryingArrayUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&Names, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[724], + (unsigned char)0 ); + + RelativeIds = &_RelativeIdsM; + RelativeIds -> Element = 0; + Use = &_UseM; + Use -> Element = 0; + + _RetVal = SamrLookupNamesInDomain( + ( SAMPR_HANDLE )*NDRSContextValue(DomainHandle), + Count, + *Names, + RelativeIds, + Use); + + _StubMsg.BufferLength = 0U + 0U + 11U; + NdrSimpleStructBufferSize( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR *)RelativeIds, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[704] ); + + NdrSimpleStructBufferSize( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR *)Use, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[704] ); + + _StubMsg.BufferLength += 16; + + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + NdrSimpleStructMarshall( (PMIDL_STUB_MESSAGE)& _StubMsg, + (unsigned char __RPC_FAR *)RelativeIds, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[704] ); + + NdrSimpleStructMarshall( (PMIDL_STUB_MESSAGE)& _StubMsg, + (unsigned char __RPC_FAR *)Use, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[704] ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *)(((long)_StubMsg.Buffer + 3) & ~ 0x3); + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + _StubMsg.MaxCount = 1000; + _StubMsg.Offset = 0; + _StubMsg.ActualCount = Count; + + NdrConformantVaryingArrayFree( &_StubMsg, + (unsigned char __RPC_FAR *)Names, + &__MIDLTypeFormatString.Format[724] ); + + if ( Names ) + _StubMsg.pfnFree( Names ); + + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)RelativeIds, + &__MIDLTypeFormatString.Format[690] ); + + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)Use, + &__MIDLTypeFormatString.Format[690] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrLookupIdsInDomain( + PRPC_MESSAGE _pRpcMessage ) +{ + ULONG Count; + NDR_SCONTEXT DomainHandle; + PSAMPR_RETURNED_USTRING_ARRAY Names; + PULONG RelativeIds; + PSAMPR_ULONG_ARRAY Use; + struct _SAMPR_RETURNED_USTRING_ARRAY _NamesM; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + struct _SAMPR_ULONG_ARRAY _UseM; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + RelativeIds = 0; + Names = 0; + Use = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[198] ); + + DomainHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + Count = *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++; + + NdrConformantVaryingArrayUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&RelativeIds, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[764], + (unsigned char)0 ); + + Names = &_NamesM; + Names -> Element = 0; + Use = &_UseM; + Use -> Element = 0; + + _RetVal = SamrLookupIdsInDomain( + ( SAMPR_HANDLE )*NDRSContextValue(DomainHandle), + Count, + RelativeIds, + Names, + Use); + + _StubMsg.BufferLength = 0U + 0U + 11U; + NdrSimpleStructBufferSize( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR *)Names, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[814] ); + + NdrSimpleStructBufferSize( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR *)Use, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[704] ); + + _StubMsg.BufferLength += 16; + + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + NdrSimpleStructMarshall( (PMIDL_STUB_MESSAGE)& _StubMsg, + (unsigned char __RPC_FAR *)Names, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[814] ); + + NdrSimpleStructMarshall( (PMIDL_STUB_MESSAGE)& _StubMsg, + (unsigned char __RPC_FAR *)Use, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[704] ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *)(((long)_StubMsg.Buffer + 3) & ~ 0x3); + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + _StubMsg.MaxCount = 1000; + _StubMsg.Offset = 0; + _StubMsg.ActualCount = Count; + + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)RelativeIds, + &__MIDLTypeFormatString.Format[760] ); + + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)Names, + &__MIDLTypeFormatString.Format[778] ); + + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)Use, + &__MIDLTypeFormatString.Format[690] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrOpenGroup( + PRPC_MESSAGE _pRpcMessage ) +{ + ACCESS_MASK DesiredAccess; + NDR_SCONTEXT DomainHandle; + NDR_SCONTEXT GroupHandle; + ULONG GroupId; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + GroupHandle = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[218] ); + + DomainHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + DesiredAccess = *(( ACCESS_MASK __RPC_FAR * )_StubMsg.Buffer)++; + + GroupId = *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++; + + GroupHandle = NDRSContextUnmarshall( (char *)0, _pRpcMessage->DataRepresentation ); + + + _RetVal = SamrOpenGroup( + ( SAMPR_HANDLE )*NDRSContextValue(DomainHandle), + DesiredAccess, + GroupId, + ( SAMPR_HANDLE __RPC_FAR * )NDRSContextValue(GroupHandle)); + + _StubMsg.BufferLength = 20U + 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + NdrServerContextMarshall( + ( PMIDL_STUB_MESSAGE )&_StubMsg, + ( NDR_SCONTEXT )GroupHandle, + ( NDR_RUNDOWN )SAMPR_HANDLE_rundown); + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrQueryInformationGroup( + PRPC_MESSAGE _pRpcMessage ) +{ + PSAMPR_GROUP_INFO_BUFFER __RPC_FAR *Buffer; + NDR_SCONTEXT GroupHandle; + GROUP_INFORMATION_CLASS GroupInformationClass; + PSAMPR_GROUP_INFO_BUFFER _M24; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + Buffer = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[232] ); + + GroupHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + NdrSimpleTypeUnmarshall( + ( PMIDL_STUB_MESSAGE )&_StubMsg, + ( unsigned char __RPC_FAR * )&GroupInformationClass, + 13); + Buffer = &_M24; + _M24 = 0; + + _RetVal = SamrQueryInformationGroup( + ( SAMPR_HANDLE )*NDRSContextValue(GroupHandle), + GroupInformationClass, + Buffer); + + _StubMsg.BufferLength = 4U + 7U; + _StubMsg.MaxCount = GroupInformationClass; + + NdrPointerBufferSize( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR *)Buffer, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[834] ); + + _StubMsg.BufferLength += 16; + + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + _StubMsg.MaxCount = GroupInformationClass; + + NdrPointerMarshall( (PMIDL_STUB_MESSAGE)& _StubMsg, + (unsigned char __RPC_FAR *)Buffer, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[834] ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *)(((long)_StubMsg.Buffer + 3) & ~ 0x3); + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + _StubMsg.MaxCount = GroupInformationClass; + + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)Buffer, + &__MIDLTypeFormatString.Format[834] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrSetInformationGroup( + PRPC_MESSAGE _pRpcMessage ) +{ + PSAMPR_GROUP_INFO_BUFFER Buffer; + NDR_SCONTEXT GroupHandle; + GROUP_INFORMATION_CLASS GroupInformationClass; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + Buffer = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[244] ); + + GroupHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + NdrSimpleTypeUnmarshall( + ( PMIDL_STUB_MESSAGE )&_StubMsg, + ( unsigned char __RPC_FAR * )&GroupInformationClass, + 13); + NdrNonEncapsulatedUnionUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&Buffer, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[928], + (unsigned char)0 ); + + + _RetVal = SamrSetInformationGroup( + ( SAMPR_HANDLE )*NDRSContextValue(GroupHandle), + GroupInformationClass, + Buffer); + + _StubMsg.BufferLength = 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + _StubMsg.MaxCount = GroupInformationClass; + + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)Buffer, + &__MIDLTypeFormatString.Format[924] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrAddMemberToGroup( + PRPC_MESSAGE _pRpcMessage ) +{ + ULONG Attributes; + NDR_SCONTEXT GroupHandle; + ULONG MemberId; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[256] ); + + GroupHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + MemberId = *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++; + + Attributes = *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++; + + + _RetVal = SamrAddMemberToGroup( + ( SAMPR_HANDLE )*NDRSContextValue(GroupHandle), + MemberId, + Attributes); + + _StubMsg.BufferLength = 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrDeleteGroup( + PRPC_MESSAGE _pRpcMessage ) +{ + NDR_SCONTEXT GroupHandle; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + GroupHandle = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[12] ); + + GroupHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + + _RetVal = SamrDeleteGroup(( SAMPR_HANDLE __RPC_FAR * )NDRSContextValue(GroupHandle)); + + _StubMsg.BufferLength = 20U + 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + NdrServerContextMarshall( + ( PMIDL_STUB_MESSAGE )&_StubMsg, + ( NDR_SCONTEXT )GroupHandle, + ( NDR_RUNDOWN )SAMPR_HANDLE_rundown); + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrRemoveMemberFromGroup( + PRPC_MESSAGE _pRpcMessage ) +{ + NDR_SCONTEXT GroupHandle; + ULONG MemberId; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[266] ); + + GroupHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + MemberId = *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++; + + + _RetVal = SamrRemoveMemberFromGroup(( SAMPR_HANDLE )*NDRSContextValue(GroupHandle),MemberId); + + _StubMsg.BufferLength = 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrGetMembersInGroup( + PRPC_MESSAGE _pRpcMessage ) +{ + NDR_SCONTEXT GroupHandle; + PSAMPR_GET_MEMBERS_BUFFER __RPC_FAR *Members; + PSAMPR_GET_MEMBERS_BUFFER _M25; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + Members = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[274] ); + + GroupHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + Members = &_M25; + _M25 = 0; + + _RetVal = SamrGetMembersInGroup(( SAMPR_HANDLE )*NDRSContextValue(GroupHandle),Members); + + _StubMsg.BufferLength = 4U + 11U; + NdrPointerBufferSize( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR *)Members, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[936] ); + + _StubMsg.BufferLength += 16; + + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + NdrPointerMarshall( (PMIDL_STUB_MESSAGE)& _StubMsg, + (unsigned char __RPC_FAR *)Members, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[936] ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *)(((long)_StubMsg.Buffer + 3) & ~ 0x3); + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)Members, + &__MIDLTypeFormatString.Format[936] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrSetMemberAttributesOfGroup( + PRPC_MESSAGE _pRpcMessage ) +{ + ULONG Attributes; + NDR_SCONTEXT GroupHandle; + ULONG MemberId; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[256] ); + + GroupHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + MemberId = *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++; + + Attributes = *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++; + + + _RetVal = SamrSetMemberAttributesOfGroup( + ( SAMPR_HANDLE )*NDRSContextValue(GroupHandle), + MemberId, + Attributes); + + _StubMsg.BufferLength = 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrOpenAlias( + PRPC_MESSAGE _pRpcMessage ) +{ + NDR_SCONTEXT AliasHandle; + ULONG AliasId; + ACCESS_MASK DesiredAccess; + NDR_SCONTEXT DomainHandle; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + AliasHandle = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[218] ); + + DomainHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + DesiredAccess = *(( ACCESS_MASK __RPC_FAR * )_StubMsg.Buffer)++; + + AliasId = *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++; + + AliasHandle = NDRSContextUnmarshall( (char *)0, _pRpcMessage->DataRepresentation ); + + + _RetVal = SamrOpenAlias( + ( SAMPR_HANDLE )*NDRSContextValue(DomainHandle), + DesiredAccess, + AliasId, + ( SAMPR_HANDLE __RPC_FAR * )NDRSContextValue(AliasHandle)); + + _StubMsg.BufferLength = 20U + 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + NdrServerContextMarshall( + ( PMIDL_STUB_MESSAGE )&_StubMsg, + ( NDR_SCONTEXT )AliasHandle, + ( NDR_RUNDOWN )SAMPR_HANDLE_rundown); + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrQueryInformationAlias( + PRPC_MESSAGE _pRpcMessage ) +{ + NDR_SCONTEXT AliasHandle; + ALIAS_INFORMATION_CLASS AliasInformationClass; + PSAMPR_ALIAS_INFO_BUFFER __RPC_FAR *Buffer; + PSAMPR_ALIAS_INFO_BUFFER _M26; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + Buffer = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[284] ); + + AliasHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + NdrSimpleTypeUnmarshall( + ( PMIDL_STUB_MESSAGE )&_StubMsg, + ( unsigned char __RPC_FAR * )&AliasInformationClass, + 13); + Buffer = &_M26; + _M26 = 0; + + _RetVal = SamrQueryInformationAlias( + ( SAMPR_HANDLE )*NDRSContextValue(AliasHandle), + AliasInformationClass, + Buffer); + + _StubMsg.BufferLength = 4U + 7U; + _StubMsg.MaxCount = AliasInformationClass; + + NdrPointerBufferSize( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR *)Buffer, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[976] ); + + _StubMsg.BufferLength += 16; + + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + _StubMsg.MaxCount = AliasInformationClass; + + NdrPointerMarshall( (PMIDL_STUB_MESSAGE)& _StubMsg, + (unsigned char __RPC_FAR *)Buffer, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[976] ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *)(((long)_StubMsg.Buffer + 3) & ~ 0x3); + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + _StubMsg.MaxCount = AliasInformationClass; + + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)Buffer, + &__MIDLTypeFormatString.Format[976] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrSetInformationAlias( + PRPC_MESSAGE _pRpcMessage ) +{ + NDR_SCONTEXT AliasHandle; + ALIAS_INFORMATION_CLASS AliasInformationClass; + PSAMPR_ALIAS_INFO_BUFFER Buffer; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + Buffer = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[296] ); + + AliasHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + NdrSimpleTypeUnmarshall( + ( PMIDL_STUB_MESSAGE )&_StubMsg, + ( unsigned char __RPC_FAR * )&AliasInformationClass, + 13); + NdrNonEncapsulatedUnionUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&Buffer, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[1072], + (unsigned char)0 ); + + + _RetVal = SamrSetInformationAlias( + ( SAMPR_HANDLE )*NDRSContextValue(AliasHandle), + AliasInformationClass, + Buffer); + + _StubMsg.BufferLength = 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + _StubMsg.MaxCount = AliasInformationClass; + + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)Buffer, + &__MIDLTypeFormatString.Format[1068] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrDeleteAlias( + PRPC_MESSAGE _pRpcMessage ) +{ + NDR_SCONTEXT AliasHandle; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + AliasHandle = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[12] ); + + AliasHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + + _RetVal = SamrDeleteAlias(( SAMPR_HANDLE __RPC_FAR * )NDRSContextValue(AliasHandle)); + + _StubMsg.BufferLength = 20U + 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + NdrServerContextMarshall( + ( PMIDL_STUB_MESSAGE )&_StubMsg, + ( NDR_SCONTEXT )AliasHandle, + ( NDR_RUNDOWN )SAMPR_HANDLE_rundown); + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrAddMemberToAlias( + PRPC_MESSAGE _pRpcMessage ) +{ + NDR_SCONTEXT AliasHandle; + PRPC_SID MemberId; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + MemberId = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[308] ); + + AliasHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + NdrConformantStructUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&MemberId, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[140], + (unsigned char)0 ); + + + _RetVal = SamrAddMemberToAlias(( SAMPR_HANDLE )*NDRSContextValue(AliasHandle),MemberId); + + _StubMsg.BufferLength = 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrRemoveMemberFromAlias( + PRPC_MESSAGE _pRpcMessage ) +{ + NDR_SCONTEXT AliasHandle; + PRPC_SID MemberId; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + MemberId = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[308] ); + + AliasHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + NdrConformantStructUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&MemberId, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[140], + (unsigned char)0 ); + + + _RetVal = SamrRemoveMemberFromAlias(( SAMPR_HANDLE )*NDRSContextValue(AliasHandle),MemberId); + + _StubMsg.BufferLength = 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrGetMembersInAlias( + PRPC_MESSAGE _pRpcMessage ) +{ + NDR_SCONTEXT AliasHandle; + PSAMPR_PSID_ARRAY Members; + struct _SAMPR_PSID_ARRAY _MembersM; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + Members = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[318] ); + + AliasHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + Members = &_MembersM; + Members -> Sids = 0; + + _RetVal = SamrGetMembersInAlias(( SAMPR_HANDLE )*NDRSContextValue(AliasHandle),Members); + + _StubMsg.BufferLength = 0U + 11U; + NdrSimpleStructBufferSize( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR *)Members, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[670] ); + + _StubMsg.BufferLength += 16; + + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + NdrSimpleStructMarshall( (PMIDL_STUB_MESSAGE)& _StubMsg, + (unsigned char __RPC_FAR *)Members, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[670] ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *)(((long)_StubMsg.Buffer + 3) & ~ 0x3); + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)Members, + &__MIDLTypeFormatString.Format[1080] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrOpenUser( + PRPC_MESSAGE _pRpcMessage ) +{ + ACCESS_MASK DesiredAccess; + NDR_SCONTEXT DomainHandle; + NDR_SCONTEXT UserHandle; + ULONG UserId; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + UserHandle = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[218] ); + + DomainHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + DesiredAccess = *(( ACCESS_MASK __RPC_FAR * )_StubMsg.Buffer)++; + + UserId = *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++; + + UserHandle = NDRSContextUnmarshall( (char *)0, _pRpcMessage->DataRepresentation ); + + + _RetVal = SamrOpenUser( + ( SAMPR_HANDLE )*NDRSContextValue(DomainHandle), + DesiredAccess, + UserId, + ( SAMPR_HANDLE __RPC_FAR * )NDRSContextValue(UserHandle)); + + _StubMsg.BufferLength = 20U + 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + NdrServerContextMarshall( + ( PMIDL_STUB_MESSAGE )&_StubMsg, + ( NDR_SCONTEXT )UserHandle, + ( NDR_RUNDOWN )SAMPR_HANDLE_rundown); + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrDeleteUser( + PRPC_MESSAGE _pRpcMessage ) +{ + NDR_SCONTEXT UserHandle; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + UserHandle = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[12] ); + + UserHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + + _RetVal = SamrDeleteUser(( SAMPR_HANDLE __RPC_FAR * )NDRSContextValue(UserHandle)); + + _StubMsg.BufferLength = 20U + 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + NdrServerContextMarshall( + ( PMIDL_STUB_MESSAGE )&_StubMsg, + ( NDR_SCONTEXT )UserHandle, + ( NDR_RUNDOWN )SAMPR_HANDLE_rundown); + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrQueryInformationUser( + PRPC_MESSAGE _pRpcMessage ) +{ + PSAMPR_USER_INFO_BUFFER __RPC_FAR *Buffer; + NDR_SCONTEXT UserHandle; + USER_INFORMATION_CLASS UserInformationClass; + PSAMPR_USER_INFO_BUFFER _M27; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + Buffer = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[328] ); + + UserHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + NdrSimpleTypeUnmarshall( + ( PMIDL_STUB_MESSAGE )&_StubMsg, + ( unsigned char __RPC_FAR * )&UserInformationClass, + 13); + Buffer = &_M27; + _M27 = 0; + + _RetVal = SamrQueryInformationUser( + ( SAMPR_HANDLE )*NDRSContextValue(UserHandle), + UserInformationClass, + Buffer); + + _StubMsg.BufferLength = 4U + 8U; + _StubMsg.MaxCount = UserInformationClass; + + NdrPointerBufferSize( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR *)Buffer, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[1084] ); + + _StubMsg.BufferLength += 16; + + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + _StubMsg.MaxCount = UserInformationClass; + + NdrPointerMarshall( (PMIDL_STUB_MESSAGE)& _StubMsg, + (unsigned char __RPC_FAR *)Buffer, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[1084] ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *)(((long)_StubMsg.Buffer + 3) & ~ 0x3); + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + _StubMsg.MaxCount = UserInformationClass; + + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)Buffer, + &__MIDLTypeFormatString.Format[1084] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrSetInformationUser( + PRPC_MESSAGE _pRpcMessage ) +{ + PSAMPR_USER_INFO_BUFFER Buffer; + NDR_SCONTEXT UserHandle; + USER_INFORMATION_CLASS UserInformationClass; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + Buffer = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[340] ); + + UserHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + NdrSimpleTypeUnmarshall( + ( PMIDL_STUB_MESSAGE )&_StubMsg, + ( unsigned char __RPC_FAR * )&UserInformationClass, + 13); + NdrNonEncapsulatedUnionUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&Buffer, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[2650], + (unsigned char)0 ); + + + _RetVal = SamrSetInformationUser( + ( SAMPR_HANDLE )*NDRSContextValue(UserHandle), + UserInformationClass, + Buffer); + + _StubMsg.BufferLength = 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + _StubMsg.MaxCount = UserInformationClass; + + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)Buffer, + &__MIDLTypeFormatString.Format[2646] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrChangePasswordUser( + PRPC_MESSAGE _pRpcMessage ) +{ + BOOLEAN LmCrossEncryptionPresent; + PENCRYPTED_LM_OWF_PASSWORD LmNewEncryptedWithLmOld; + PENCRYPTED_LM_OWF_PASSWORD LmNtNewEncryptedWithNtNew; + PENCRYPTED_LM_OWF_PASSWORD LmOldEncryptedWithLmNew; + BOOLEAN LmPresent; + BOOLEAN NtCrossEncryptionPresent; + PENCRYPTED_NT_OWF_PASSWORD NtNewEncryptedWithLmNew; + PENCRYPTED_NT_OWF_PASSWORD NtNewEncryptedWithNtOld; + PENCRYPTED_NT_OWF_PASSWORD NtOldEncryptedWithNtNew; + BOOLEAN NtPresent; + NDR_SCONTEXT UserHandle; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + LmOldEncryptedWithLmNew = 0; + LmNewEncryptedWithLmOld = 0; + NtOldEncryptedWithNtNew = 0; + NtNewEncryptedWithNtOld = 0; + NtNewEncryptedWithLmNew = 0; + LmNtNewEncryptedWithNtNew = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[352] ); + + UserHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + LmPresent = *(( BOOLEAN __RPC_FAR * )_StubMsg.Buffer)++; + + NdrPointerUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&LmOldEncryptedWithLmNew, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[2658], + (unsigned char)0 ); + + NdrPointerUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&LmNewEncryptedWithLmOld, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[2658], + (unsigned char)0 ); + + NtPresent = *(( BOOLEAN __RPC_FAR * )_StubMsg.Buffer)++; + + NdrPointerUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&NtOldEncryptedWithNtNew, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[2658], + (unsigned char)0 ); + + NdrPointerUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&NtNewEncryptedWithNtOld, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[2658], + (unsigned char)0 ); + + NtCrossEncryptionPresent = *(( BOOLEAN __RPC_FAR * )_StubMsg.Buffer)++; + + NdrPointerUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&NtNewEncryptedWithLmNew, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[2658], + (unsigned char)0 ); + + LmCrossEncryptionPresent = *(( BOOLEAN __RPC_FAR * )_StubMsg.Buffer)++; + + NdrPointerUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&LmNtNewEncryptedWithNtNew, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[2658], + (unsigned char)0 ); + + + _RetVal = SamrChangePasswordUser( + ( SAMPR_HANDLE )*NDRSContextValue(UserHandle), + LmPresent, + LmOldEncryptedWithLmNew, + LmNewEncryptedWithLmOld, + NtPresent, + NtOldEncryptedWithNtNew, + NtNewEncryptedWithNtOld, + NtCrossEncryptionPresent, + NtNewEncryptedWithLmNew, + LmCrossEncryptionPresent, + LmNtNewEncryptedWithNtNew); + + _StubMsg.BufferLength = 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrGetGroupsForUser( + PRPC_MESSAGE _pRpcMessage ) +{ + PSAMPR_GET_GROUPS_BUFFER __RPC_FAR *Groups; + NDR_SCONTEXT UserHandle; + PSAMPR_GET_GROUPS_BUFFER _M28; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + Groups = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[390] ); + + UserHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + Groups = &_M28; + _M28 = 0; + + _RetVal = SamrGetGroupsForUser(( SAMPR_HANDLE )*NDRSContextValue(UserHandle),Groups); + + _StubMsg.BufferLength = 4U + 11U; + NdrPointerBufferSize( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR *)Groups, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[2662] ); + + _StubMsg.BufferLength += 16; + + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + NdrPointerMarshall( (PMIDL_STUB_MESSAGE)& _StubMsg, + (unsigned char __RPC_FAR *)Groups, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[2662] ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *)(((long)_StubMsg.Buffer + 3) & ~ 0x3); + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)Groups, + &__MIDLTypeFormatString.Format[2662] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrQueryDisplayInformation( + PRPC_MESSAGE _pRpcMessage ) +{ + PSAMPR_DISPLAY_INFO_BUFFER Buffer; + DOMAIN_DISPLAY_INFORMATION DisplayInformationClass; + NDR_SCONTEXT DomainHandle; + ULONG EntryCount; + ULONG Index; + ULONG PreferredMaximumLength; + PULONG TotalAvailable; + PULONG TotalReturned; + union _SAMPR_DISPLAY_INFO_BUFFER _BufferM; + ULONG _M29; + ULONG _M30; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + TotalAvailable = 0; + TotalReturned = 0; + Buffer = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[400] ); + + DomainHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + NdrSimpleTypeUnmarshall( + ( PMIDL_STUB_MESSAGE )&_StubMsg, + ( unsigned char __RPC_FAR * )&DisplayInformationClass, + 13); + _StubMsg.Buffer += 2; + Index = *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++; + + EntryCount = *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++; + + PreferredMaximumLength = *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++; + + TotalAvailable = &_M29; + TotalReturned = &_M30; + Buffer = &_BufferM; + MIDL_memset( + Buffer, + 0, + sizeof( union _SAMPR_DISPLAY_INFO_BUFFER )); + + _RetVal = SamrQueryDisplayInformation( + ( SAMPR_HANDLE )*NDRSContextValue(DomainHandle), + DisplayInformationClass, + Index, + EntryCount, + PreferredMaximumLength, + TotalAvailable, + TotalReturned, + Buffer); + + _StubMsg.BufferLength = 4U + 4U + 0U + 7U; + _StubMsg.MaxCount = DisplayInformationClass; + + NdrNonEncapsulatedUnionBufferSize( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR *)Buffer, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[2708] ); + + _StubMsg.BufferLength += 16; + + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++ = *TotalAvailable; + + *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++ = *TotalReturned; + + _StubMsg.MaxCount = DisplayInformationClass; + + NdrNonEncapsulatedUnionMarshall( (PMIDL_STUB_MESSAGE)& _StubMsg, + (unsigned char __RPC_FAR *)Buffer, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[2708] ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *)(((long)_StubMsg.Buffer + 3) & ~ 0x3); + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + _StubMsg.MaxCount = DisplayInformationClass; + + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)Buffer, + &__MIDLTypeFormatString.Format[2704] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrGetDisplayEnumerationIndex( + PRPC_MESSAGE _pRpcMessage ) +{ + DOMAIN_DISPLAY_INFORMATION DisplayInformationClass; + NDR_SCONTEXT DomainHandle; + PULONG Index; + PRPC_UNICODE_STRING Prefix; + ULONG _M31; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + Prefix = 0; + Index = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[426] ); + + DomainHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + NdrSimpleTypeUnmarshall( + ( PMIDL_STUB_MESSAGE )&_StubMsg, + ( unsigned char __RPC_FAR * )&DisplayInformationClass, + 13); + NdrSimpleStructUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&Prefix, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[84], + (unsigned char)0 ); + + Index = &_M31; + + _RetVal = SamrGetDisplayEnumerationIndex( + ( SAMPR_HANDLE )*NDRSContextValue(DomainHandle), + DisplayInformationClass, + Prefix, + Index); + + _StubMsg.BufferLength = 4U + 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++ = *Index; + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)Prefix, + &__MIDLTypeFormatString.Format[66] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrTestPrivateFunctionsDomain( + PRPC_MESSAGE _pRpcMessage ) +{ + NDR_SCONTEXT DomainHandle; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[42] ); + + DomainHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + + _RetVal = SamrTestPrivateFunctionsDomain(( SAMPR_HANDLE )*NDRSContextValue(DomainHandle)); + + _StubMsg.BufferLength = 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrTestPrivateFunctionsUser( + PRPC_MESSAGE _pRpcMessage ) +{ + NDR_SCONTEXT UserHandle; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[42] ); + + UserHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + + _RetVal = SamrTestPrivateFunctionsUser(( SAMPR_HANDLE )*NDRSContextValue(UserHandle)); + + _StubMsg.BufferLength = 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrGetUserDomainPasswordInformation( + PRPC_MESSAGE _pRpcMessage ) +{ + PUSER_DOMAIN_PASSWORD_INFORMATION PasswordInformation; + NDR_SCONTEXT UserHandle; + struct _USER_DOMAIN_PASSWORD_INFORMATION _PasswordInformationM; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + PasswordInformation = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[442] ); + + UserHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + PasswordInformation = &_PasswordInformationM; + + _RetVal = SamrGetUserDomainPasswordInformation(( SAMPR_HANDLE )*NDRSContextValue(UserHandle),PasswordInformation); + + _StubMsg.BufferLength = 0U + 11U; + NdrSimpleStructBufferSize( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR *)PasswordInformation, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[3068] ); + + _StubMsg.BufferLength += 16; + + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + NdrSimpleStructMarshall( (PMIDL_STUB_MESSAGE)& _StubMsg, + (unsigned char __RPC_FAR *)PasswordInformation, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[3068] ); + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrRemoveMemberFromForeignDomain( + PRPC_MESSAGE _pRpcMessage ) +{ + NDR_SCONTEXT DomainHandle; + PRPC_SID MemberSid; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + MemberSid = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[308] ); + + DomainHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + NdrConformantStructUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&MemberSid, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[140], + (unsigned char)0 ); + + + _RetVal = SamrRemoveMemberFromForeignDomain(( SAMPR_HANDLE )*NDRSContextValue(DomainHandle),MemberSid); + + _StubMsg.BufferLength = 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrQueryInformationDomain2( + PRPC_MESSAGE _pRpcMessage ) +{ + PSAMPR_DOMAIN_INFO_BUFFER __RPC_FAR *Buffer; + NDR_SCONTEXT DomainHandle; + DOMAIN_INFORMATION_CLASS DomainInformationClass; + PSAMPR_DOMAIN_INFO_BUFFER _M32; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + Buffer = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[452] ); + + DomainHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + NdrSimpleTypeUnmarshall( + ( PMIDL_STUB_MESSAGE )&_StubMsg, + ( unsigned char __RPC_FAR * )&DomainInformationClass, + 13); + Buffer = &_M32; + _M32 = 0; + + _RetVal = SamrQueryInformationDomain2( + ( SAMPR_HANDLE )*NDRSContextValue(DomainHandle), + DomainInformationClass, + Buffer); + + _StubMsg.BufferLength = 4U + 8U; + _StubMsg.MaxCount = DomainInformationClass; + + NdrPointerBufferSize( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR *)Buffer, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[3076] ); + + _StubMsg.BufferLength += 16; + + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + _StubMsg.MaxCount = DomainInformationClass; + + NdrPointerMarshall( (PMIDL_STUB_MESSAGE)& _StubMsg, + (unsigned char __RPC_FAR *)Buffer, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[3076] ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *)(((long)_StubMsg.Buffer + 3) & ~ 0x3); + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + _StubMsg.MaxCount = DomainInformationClass; + + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)Buffer, + &__MIDLTypeFormatString.Format[3076] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrQueryInformationUser2( + PRPC_MESSAGE _pRpcMessage ) +{ + PSAMPR_USER_INFO_BUFFER __RPC_FAR *Buffer; + NDR_SCONTEXT UserHandle; + USER_INFORMATION_CLASS UserInformationClass; + PSAMPR_USER_INFO_BUFFER _M33; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + Buffer = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[464] ); + + UserHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + NdrSimpleTypeUnmarshall( + ( PMIDL_STUB_MESSAGE )&_StubMsg, + ( unsigned char __RPC_FAR * )&UserInformationClass, + 13); + Buffer = &_M33; + _M33 = 0; + + _RetVal = SamrQueryInformationUser2( + ( SAMPR_HANDLE )*NDRSContextValue(UserHandle), + UserInformationClass, + Buffer); + + _StubMsg.BufferLength = 4U + 8U; + _StubMsg.MaxCount = UserInformationClass; + + NdrPointerBufferSize( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR *)Buffer, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[3092] ); + + _StubMsg.BufferLength += 16; + + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + _StubMsg.MaxCount = UserInformationClass; + + NdrPointerMarshall( (PMIDL_STUB_MESSAGE)& _StubMsg, + (unsigned char __RPC_FAR *)Buffer, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[3092] ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *)(((long)_StubMsg.Buffer + 3) & ~ 0x3); + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + _StubMsg.MaxCount = UserInformationClass; + + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)Buffer, + &__MIDLTypeFormatString.Format[3092] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrQueryDisplayInformation2( + PRPC_MESSAGE _pRpcMessage ) +{ + PSAMPR_DISPLAY_INFO_BUFFER Buffer; + DOMAIN_DISPLAY_INFORMATION DisplayInformationClass; + NDR_SCONTEXT DomainHandle; + ULONG EntryCount; + ULONG Index; + ULONG PreferredMaximumLength; + PULONG TotalAvailable; + PULONG TotalReturned; + union _SAMPR_DISPLAY_INFO_BUFFER _BufferM; + ULONG _M34; + ULONG _M35; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + TotalAvailable = 0; + TotalReturned = 0; + Buffer = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[476] ); + + DomainHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + NdrSimpleTypeUnmarshall( + ( PMIDL_STUB_MESSAGE )&_StubMsg, + ( unsigned char __RPC_FAR * )&DisplayInformationClass, + 13); + _StubMsg.Buffer += 2; + Index = *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++; + + EntryCount = *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++; + + PreferredMaximumLength = *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++; + + TotalAvailable = &_M34; + TotalReturned = &_M35; + Buffer = &_BufferM; + MIDL_memset( + Buffer, + 0, + sizeof( union _SAMPR_DISPLAY_INFO_BUFFER )); + + _RetVal = SamrQueryDisplayInformation2( + ( SAMPR_HANDLE )*NDRSContextValue(DomainHandle), + DisplayInformationClass, + Index, + EntryCount, + PreferredMaximumLength, + TotalAvailable, + TotalReturned, + Buffer); + + _StubMsg.BufferLength = 4U + 4U + 0U + 7U; + _StubMsg.MaxCount = DisplayInformationClass; + + NdrNonEncapsulatedUnionBufferSize( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR *)Buffer, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[3112] ); + + _StubMsg.BufferLength += 16; + + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++ = *TotalAvailable; + + *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++ = *TotalReturned; + + _StubMsg.MaxCount = DisplayInformationClass; + + NdrNonEncapsulatedUnionMarshall( (PMIDL_STUB_MESSAGE)& _StubMsg, + (unsigned char __RPC_FAR *)Buffer, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[3112] ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *)(((long)_StubMsg.Buffer + 3) & ~ 0x3); + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + _StubMsg.MaxCount = DisplayInformationClass; + + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)Buffer, + &__MIDLTypeFormatString.Format[3108] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrGetDisplayEnumerationIndex2( + PRPC_MESSAGE _pRpcMessage ) +{ + DOMAIN_DISPLAY_INFORMATION DisplayInformationClass; + NDR_SCONTEXT DomainHandle; + PULONG Index; + PRPC_UNICODE_STRING Prefix; + ULONG _M36; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + Prefix = 0; + Index = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[426] ); + + DomainHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + NdrSimpleTypeUnmarshall( + ( PMIDL_STUB_MESSAGE )&_StubMsg, + ( unsigned char __RPC_FAR * )&DisplayInformationClass, + 13); + NdrSimpleStructUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&Prefix, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[84], + (unsigned char)0 ); + + Index = &_M36; + + _RetVal = SamrGetDisplayEnumerationIndex2( + ( SAMPR_HANDLE )*NDRSContextValue(DomainHandle), + DisplayInformationClass, + Prefix, + Index); + + _StubMsg.BufferLength = 4U + 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++ = *Index; + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)Prefix, + &__MIDLTypeFormatString.Format[66] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrCreateUser2InDomain( + PRPC_MESSAGE _pRpcMessage ) +{ + ULONG AccountType; + ACCESS_MASK DesiredAccess; + NDR_SCONTEXT DomainHandle; + PULONG GrantedAccess; + PRPC_UNICODE_STRING Name; + PULONG RelativeId; + NDR_SCONTEXT UserHandle; + ULONG _M37; + ULONG _M38; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + Name = 0; + UserHandle = 0; + GrantedAccess = 0; + RelativeId = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[502] ); + + DomainHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + NdrSimpleStructUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&Name, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[84], + (unsigned char)0 ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *)(((long)_StubMsg.Buffer + 3) & ~ 0x3); + AccountType = *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++; + + DesiredAccess = *(( ACCESS_MASK __RPC_FAR * )_StubMsg.Buffer)++; + + UserHandle = NDRSContextUnmarshall( (char *)0, _pRpcMessage->DataRepresentation ); + + GrantedAccess = &_M37; + RelativeId = &_M38; + + _RetVal = SamrCreateUser2InDomain( + ( SAMPR_HANDLE )*NDRSContextValue(DomainHandle), + Name, + AccountType, + DesiredAccess, + ( SAMPR_HANDLE __RPC_FAR * )NDRSContextValue(UserHandle), + GrantedAccess, + RelativeId); + + _StubMsg.BufferLength = 20U + 4U + 4U + 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + NdrServerContextMarshall( + ( PMIDL_STUB_MESSAGE )&_StubMsg, + ( NDR_SCONTEXT )UserHandle, + ( NDR_RUNDOWN )SAMPR_HANDLE_rundown); + + *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++ = *GrantedAccess; + + *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++ = *RelativeId; + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)Name, + &__MIDLTypeFormatString.Format[66] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrQueryDisplayInformation3( + PRPC_MESSAGE _pRpcMessage ) +{ + PSAMPR_DISPLAY_INFO_BUFFER Buffer; + DOMAIN_DISPLAY_INFORMATION DisplayInformationClass; + NDR_SCONTEXT DomainHandle; + ULONG EntryCount; + ULONG Index; + ULONG PreferredMaximumLength; + PULONG TotalAvailable; + PULONG TotalReturned; + union _SAMPR_DISPLAY_INFO_BUFFER _BufferM; + ULONG _M39; + ULONG _M40; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + TotalAvailable = 0; + TotalReturned = 0; + Buffer = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[528] ); + + DomainHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + NdrSimpleTypeUnmarshall( + ( PMIDL_STUB_MESSAGE )&_StubMsg, + ( unsigned char __RPC_FAR * )&DisplayInformationClass, + 13); + _StubMsg.Buffer += 2; + Index = *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++; + + EntryCount = *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++; + + PreferredMaximumLength = *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++; + + TotalAvailable = &_M39; + TotalReturned = &_M40; + Buffer = &_BufferM; + MIDL_memset( + Buffer, + 0, + sizeof( union _SAMPR_DISPLAY_INFO_BUFFER )); + + _RetVal = SamrQueryDisplayInformation3( + ( SAMPR_HANDLE )*NDRSContextValue(DomainHandle), + DisplayInformationClass, + Index, + EntryCount, + PreferredMaximumLength, + TotalAvailable, + TotalReturned, + Buffer); + + _StubMsg.BufferLength = 4U + 4U + 0U + 7U; + _StubMsg.MaxCount = DisplayInformationClass; + + NdrNonEncapsulatedUnionBufferSize( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR *)Buffer, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[3132] ); + + _StubMsg.BufferLength += 16; + + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++ = *TotalAvailable; + + *(( ULONG __RPC_FAR * )_StubMsg.Buffer)++ = *TotalReturned; + + _StubMsg.MaxCount = DisplayInformationClass; + + NdrNonEncapsulatedUnionMarshall( (PMIDL_STUB_MESSAGE)& _StubMsg, + (unsigned char __RPC_FAR *)Buffer, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[3132] ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *)(((long)_StubMsg.Buffer + 3) & ~ 0x3); + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + _StubMsg.MaxCount = DisplayInformationClass; + + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)Buffer, + &__MIDLTypeFormatString.Format[3128] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrAddMultipleMembersToAlias( + PRPC_MESSAGE _pRpcMessage ) +{ + NDR_SCONTEXT AliasHandle; + PSAMPR_PSID_ARRAY MembersBuffer; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + MembersBuffer = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[554] ); + + AliasHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + NdrSimpleStructUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&MembersBuffer, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[670], + (unsigned char)0 ); + + + _RetVal = SamrAddMultipleMembersToAlias(( SAMPR_HANDLE )*NDRSContextValue(AliasHandle),MembersBuffer); + + _StubMsg.BufferLength = 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)MembersBuffer, + &__MIDLTypeFormatString.Format[614] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrRemoveMultipleMembersFromAlias( + PRPC_MESSAGE _pRpcMessage ) +{ + NDR_SCONTEXT AliasHandle; + PSAMPR_PSID_ARRAY MembersBuffer; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + MembersBuffer = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[554] ); + + AliasHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + NdrSimpleStructUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&MembersBuffer, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[670], + (unsigned char)0 ); + + + _RetVal = SamrRemoveMultipleMembersFromAlias(( SAMPR_HANDLE )*NDRSContextValue(AliasHandle),MembersBuffer); + + _StubMsg.BufferLength = 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)MembersBuffer, + &__MIDLTypeFormatString.Format[614] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrOemChangePasswordUser2( + PRPC_MESSAGE _pRpcMessage ) +{ + handle_t BindingHandle; + PSAMPR_ENCRYPTED_USER_PASSWORD NewPasswordEncryptedWithOldLm; + PENCRYPTED_LM_OWF_PASSWORD OldLmOwfPassswordEncryptedWithNewLm; + PRPC_STRING ServerName; + PRPC_STRING UserName; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + BindingHandle = _pRpcMessage->Handle; + ServerName = 0; + UserName = 0; + NewPasswordEncryptedWithOldLm = 0; + OldLmOwfPassswordEncryptedWithNewLm = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[564] ); + + NdrPointerUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&ServerName, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[3140], + (unsigned char)0 ); + + NdrSimpleStructUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&UserName, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[3158], + (unsigned char)0 ); + + NdrPointerUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&NewPasswordEncryptedWithOldLm, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[3184], + (unsigned char)0 ); + + NdrPointerUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&OldLmOwfPassswordEncryptedWithNewLm, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[2658], + (unsigned char)0 ); + + + _RetVal = SamrOemChangePasswordUser2( + BindingHandle, + ServerName, + UserName, + NewPasswordEncryptedWithOldLm, + OldLmOwfPassswordEncryptedWithNewLm); + + _StubMsg.BufferLength = 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)ServerName, + &__MIDLTypeFormatString.Format[3140] ); + + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)UserName, + &__MIDLTypeFormatString.Format[3180] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrUnicodeChangePasswordUser2( + PRPC_MESSAGE _pRpcMessage ) +{ + handle_t BindingHandle; + BOOLEAN LmPresent; + PSAMPR_ENCRYPTED_USER_PASSWORD NewPasswordEncryptedWithOldLm; + PSAMPR_ENCRYPTED_USER_PASSWORD NewPasswordEncryptedWithOldNt; + PENCRYPTED_LM_OWF_PASSWORD OldLmOwfPassswordEncryptedWithNewLmOrNt; + PENCRYPTED_NT_OWF_PASSWORD OldNtOwfPasswordEncryptedWithNewNt; + PRPC_UNICODE_STRING ServerName; + PRPC_UNICODE_STRING UserName; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + BindingHandle = _pRpcMessage->Handle; + ServerName = 0; + UserName = 0; + NewPasswordEncryptedWithOldNt = 0; + OldNtOwfPasswordEncryptedWithNewNt = 0; + NewPasswordEncryptedWithOldLm = 0; + OldLmOwfPassswordEncryptedWithNewLmOrNt = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[584] ); + + NdrPointerUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&ServerName, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[3188], + (unsigned char)0 ); + + NdrSimpleStructUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&UserName, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[84], + (unsigned char)0 ); + + NdrPointerUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&NewPasswordEncryptedWithOldNt, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[3184], + (unsigned char)0 ); + + NdrPointerUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&OldNtOwfPasswordEncryptedWithNewNt, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[2658], + (unsigned char)0 ); + + LmPresent = *(( BOOLEAN __RPC_FAR * )_StubMsg.Buffer)++; + + NdrPointerUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&NewPasswordEncryptedWithOldLm, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[3184], + (unsigned char)0 ); + + NdrPointerUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&OldLmOwfPassswordEncryptedWithNewLmOrNt, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[2658], + (unsigned char)0 ); + + + _RetVal = SamrUnicodeChangePasswordUser2( + BindingHandle, + ServerName, + UserName, + NewPasswordEncryptedWithOldNt, + OldNtOwfPasswordEncryptedWithNewNt, + LmPresent, + NewPasswordEncryptedWithOldLm, + OldLmOwfPassswordEncryptedWithNewLmOrNt); + + _StubMsg.BufferLength = 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)ServerName, + &__MIDLTypeFormatString.Format[3188] ); + + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)UserName, + &__MIDLTypeFormatString.Format[66] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrGetDomainPasswordInformation( + PRPC_MESSAGE _pRpcMessage ) +{ + handle_t BindingHandle; + PUSER_DOMAIN_PASSWORD_INFORMATION PasswordInformation; + PRPC_UNICODE_STRING ServerName; + struct _USER_DOMAIN_PASSWORD_INFORMATION _PasswordInformationM; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + BindingHandle = _pRpcMessage->Handle; + ServerName = 0; + PasswordInformation = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[614] ); + + NdrPointerUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&ServerName, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[3188], + (unsigned char)0 ); + + PasswordInformation = &_PasswordInformationM; + + _RetVal = SamrGetDomainPasswordInformation( + BindingHandle, + ServerName, + PasswordInformation); + + _StubMsg.BufferLength = 0U + 11U; + NdrSimpleStructBufferSize( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR *)PasswordInformation, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[3068] ); + + _StubMsg.BufferLength += 16; + + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + NdrSimpleStructMarshall( (PMIDL_STUB_MESSAGE)& _StubMsg, + (unsigned char __RPC_FAR *)PasswordInformation, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[3068] ); + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)ServerName, + &__MIDLTypeFormatString.Format[3188] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrConnect2( + PRPC_MESSAGE _pRpcMessage ) +{ + ACCESS_MASK DesiredAccess; + NDR_SCONTEXT ServerHandle; + PSAMPR_SERVER_NAME ServerName; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + ServerName = 0; + ServerHandle = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[626] ); + + NdrPointerUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&ServerName, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[3192], + (unsigned char)0 ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *)(((long)_StubMsg.Buffer + 3) & ~ 0x3); + DesiredAccess = *(( ACCESS_MASK __RPC_FAR * )_StubMsg.Buffer)++; + + ServerHandle = NDRSContextUnmarshall( (char *)0, _pRpcMessage->DataRepresentation ); + + + _RetVal = SamrConnect2( + ServerName, + ( SAMPR_HANDLE __RPC_FAR * )NDRSContextValue(ServerHandle), + DesiredAccess); + + _StubMsg.BufferLength = 20U + 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + NdrServerContextMarshall( + ( PMIDL_STUB_MESSAGE )&_StubMsg, + ( NDR_SCONTEXT )ServerHandle, + ( NDR_RUNDOWN )SAMPR_HANDLE_rundown); + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +void __RPC_STUB +samr_SamrSetInformationUser2( + PRPC_MESSAGE _pRpcMessage ) +{ + PSAMPR_USER_INFO_BUFFER Buffer; + NDR_SCONTEXT UserHandle; + USER_INFORMATION_CLASS UserInformationClass; + NTSTATUS _RetVal; + MIDL_STUB_MESSAGE _StubMsg; + RPC_STATUS _Status; + + ((void)(_Status)); + NdrServerInitializeNew( + _pRpcMessage, + &_StubMsg, + &samr_StubDesc); + + Buffer = 0; + RpcTryFinally + { + if ( (_pRpcMessage->DataRepresentation & 0X0000FFFFUL) != NDR_LOCAL_DATA_REPRESENTATION ) + NdrConvert( (PMIDL_STUB_MESSAGE) &_StubMsg, (PFORMAT_STRING) &__MIDLProcFormatString.Format[638] ); + + UserHandle = NdrServerContextUnmarshall(( PMIDL_STUB_MESSAGE )&_StubMsg); + + NdrSimpleTypeUnmarshall( + ( PMIDL_STUB_MESSAGE )&_StubMsg, + ( unsigned char __RPC_FAR * )&UserInformationClass, + 13); + NdrNonEncapsulatedUnionUnmarshall( (PMIDL_STUB_MESSAGE) &_StubMsg, + (unsigned char __RPC_FAR * __RPC_FAR *)&Buffer, + (PFORMAT_STRING) &__MIDLTypeFormatString.Format[3200], + (unsigned char)0 ); + + + _RetVal = SamrSetInformationUser2( + ( SAMPR_HANDLE )*NDRSContextValue(UserHandle), + UserInformationClass, + Buffer); + + _StubMsg.BufferLength = 4U; + _pRpcMessage->BufferLength = _StubMsg.BufferLength; + + _Status = I_RpcGetBuffer( _pRpcMessage ); + if ( _Status ) + RpcRaiseException( _Status ); + + _StubMsg.Buffer = (unsigned char __RPC_FAR *) _pRpcMessage->Buffer; + + *(( NTSTATUS __RPC_FAR * )_StubMsg.Buffer)++ = _RetVal; + + } + RpcFinally + { + _StubMsg.MaxCount = UserInformationClass; + + NdrPointerFree( &_StubMsg, + (unsigned char __RPC_FAR *)Buffer, + &__MIDLTypeFormatString.Format[3196] ); + + } + RpcEndFinally + _pRpcMessage->BufferLength = + (unsigned int)((long)_StubMsg.Buffer - (long)_pRpcMessage->Buffer); + +} + +extern const EXPR_EVAL ExprEvalRoutines[]; + +static const MIDL_STUB_DESC samr_StubDesc = + { + (void __RPC_FAR *)& samr___RpcServerInterface, + MIDL_user_allocate, + MIDL_user_free, + 0, + 0, + 0, + ExprEvalRoutines, + 0, + __MIDLTypeFormatString.Format, + 0, /* -error bounds_check flag */ + 0x10001, /* Ndr library version */ + 0, + 0x300000f, /* MIDL Version 3.0.15 */ + 0, + 0, + 0, /* Reserved1 */ + 0, /* Reserved2 */ + 0, /* Reserved3 */ + 0, /* Reserved4 */ + 0 /* Reserved5 */ + }; + +static RPC_DISPATCH_FUNCTION samr_table[] = + { + samr_SamrConnect, + samr_SamrCloseHandle, + samr_SamrSetSecurityObject, + samr_SamrQuerySecurityObject, + samr_SamrShutdownSamServer, + samr_SamrLookupDomainInSamServer, + samr_SamrEnumerateDomainsInSamServer, + samr_SamrOpenDomain, + samr_SamrQueryInformationDomain, + samr_SamrSetInformationDomain, + samr_SamrCreateGroupInDomain, + samr_SamrEnumerateGroupsInDomain, + samr_SamrCreateUserInDomain, + samr_SamrEnumerateUsersInDomain, + samr_SamrCreateAliasInDomain, + samr_SamrEnumerateAliasesInDomain, + samr_SamrGetAliasMembership, + samr_SamrLookupNamesInDomain, + samr_SamrLookupIdsInDomain, + samr_SamrOpenGroup, + samr_SamrQueryInformationGroup, + samr_SamrSetInformationGroup, + samr_SamrAddMemberToGroup, + samr_SamrDeleteGroup, + samr_SamrRemoveMemberFromGroup, + samr_SamrGetMembersInGroup, + samr_SamrSetMemberAttributesOfGroup, + samr_SamrOpenAlias, + samr_SamrQueryInformationAlias, + samr_SamrSetInformationAlias, + samr_SamrDeleteAlias, + samr_SamrAddMemberToAlias, + samr_SamrRemoveMemberFromAlias, + samr_SamrGetMembersInAlias, + samr_SamrOpenUser, + samr_SamrDeleteUser, + samr_SamrQueryInformationUser, + samr_SamrSetInformationUser, + samr_SamrChangePasswordUser, + samr_SamrGetGroupsForUser, + samr_SamrQueryDisplayInformation, + samr_SamrGetDisplayEnumerationIndex, + samr_SamrTestPrivateFunctionsDomain, + samr_SamrTestPrivateFunctionsUser, + samr_SamrGetUserDomainPasswordInformation, + samr_SamrRemoveMemberFromForeignDomain, + samr_SamrQueryInformationDomain2, + samr_SamrQueryInformationUser2, + samr_SamrQueryDisplayInformation2, + samr_SamrGetDisplayEnumerationIndex2, + samr_SamrCreateUser2InDomain, + samr_SamrQueryDisplayInformation3, + samr_SamrAddMultipleMembersToAlias, + samr_SamrRemoveMultipleMembersFromAlias, + samr_SamrOemChangePasswordUser2, + samr_SamrUnicodeChangePasswordUser2, + samr_SamrGetDomainPasswordInformation, + samr_SamrConnect2, + samr_SamrSetInformationUser2, + 0 + }; +RPC_DISPATCH_TABLE samr_DispatchTable = + { + 59, + samr_table + }; + +static void __RPC_USER samr_SAMPR_USER_INTERNAL4_INFORMATIONExprEval_0004( PMIDL_STUB_MESSAGE pStubMsg ) +{ + SAMPR_USER_INTERNAL4_INFORMATION __RPC_FAR *pS = ( SAMPR_USER_INTERNAL4_INFORMATION __RPC_FAR * )pStubMsg->StackTop; + + pStubMsg->Offset = 0; + pStubMsg->MaxCount = (pS->I1.LogonHours.UnitsPerWeek + 7) / 8; +} + +static void __RPC_USER samr_SAMPR_USER_LOGON_INFORMATIONExprEval_0000( PMIDL_STUB_MESSAGE pStubMsg ) +{ + SAMPR_USER_LOGON_INFORMATION __RPC_FAR *pS = ( SAMPR_USER_LOGON_INFORMATION __RPC_FAR * )pStubMsg->StackTop; + + pStubMsg->Offset = 0; + pStubMsg->MaxCount = (pS->LogonHours.UnitsPerWeek + 7) / 8; +} + +static void __RPC_USER samr_SAMPR_USER_LOGON_HOURS_INFORMATIONExprEval_0001( PMIDL_STUB_MESSAGE pStubMsg ) +{ + SAMPR_USER_LOGON_HOURS_INFORMATION __RPC_FAR *pS = ( SAMPR_USER_LOGON_HOURS_INFORMATION __RPC_FAR * )pStubMsg->StackTop; + + pStubMsg->Offset = 0; + pStubMsg->MaxCount = (pS->LogonHours.UnitsPerWeek + 7) / 8; +} + +static void __RPC_USER samr_SAMPR_USER_ACCOUNT_INFORMATIONExprEval_0002( PMIDL_STUB_MESSAGE pStubMsg ) +{ + SAMPR_USER_ACCOUNT_INFORMATION __RPC_FAR *pS = ( SAMPR_USER_ACCOUNT_INFORMATION __RPC_FAR * )pStubMsg->StackTop; + + pStubMsg->Offset = 0; + pStubMsg->MaxCount = (pS->LogonHours.UnitsPerWeek + 7) / 8; +} + +static void __RPC_USER samr_SAMPR_USER_ALL_INFORMATIONExprEval_0003( PMIDL_STUB_MESSAGE pStubMsg ) +{ + SAMPR_USER_ALL_INFORMATION __RPC_FAR *pS = ( SAMPR_USER_ALL_INFORMATION __RPC_FAR * )pStubMsg->StackTop; + + pStubMsg->Offset = 0; + pStubMsg->MaxCount = (pS->LogonHours.UnitsPerWeek + 7) / 8; +} + +static const EXPR_EVAL ExprEvalRoutines[] = + { + samr_SAMPR_USER_LOGON_INFORMATIONExprEval_0000 + ,samr_SAMPR_USER_LOGON_HOURS_INFORMATIONExprEval_0001 + ,samr_SAMPR_USER_ACCOUNT_INFORMATIONExprEval_0002 + ,samr_SAMPR_USER_ALL_INFORMATIONExprEval_0003 + ,samr_SAMPR_USER_INTERNAL4_INFORMATIONExprEval_0004 + }; + + +#if !defined(__RPC_WIN32__) +#error Invalid build platform for this stub. +#endif + +static const MIDL_PROC_FORMAT_STRING __MIDLProcFormatString = + { + 0, + { + + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 2 */ NdrFcShort( 0x0 ), /* Type Offset=0 */ +/* 4 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 6 */ NdrFcShort( 0x4 ), /* Type Offset=4 */ +/* 8 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 10 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 12 */ + 0x50, /* FC_IN_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 14 */ NdrFcShort( 0xc ), /* Type Offset=12 */ +/* 16 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 18 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 20 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 22 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 24 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 26 */ NdrFcShort( 0x18 ), /* Type Offset=24 */ +/* 28 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 30 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 32 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 34 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 36 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 38 */ NdrFcShort( 0x3a ), /* Type Offset=58 */ +/* 40 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 42 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 44 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 46 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 48 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 50 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 52 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 54 */ NdrFcShort( 0x42 ), /* Type Offset=66 */ +/* 56 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 58 */ NdrFcShort( 0x6a ), /* Type Offset=106 */ +/* 60 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 62 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 64 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 66 */ + 0x50, /* FC_IN_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 68 */ NdrFcShort( 0x9a ), /* Type Offset=154 */ +/* 70 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 72 */ NdrFcShort( 0x9e ), /* Type Offset=158 */ +/* 74 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 76 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 78 */ NdrFcShort( 0x100 ), /* Type Offset=256 */ +/* 80 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 82 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 84 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 86 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 88 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 90 */ NdrFcShort( 0x104 ), /* Type Offset=260 */ +/* 92 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 94 */ NdrFcShort( 0x108 ), /* Type Offset=264 */ +/* 96 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 98 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 100 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 102 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0xd, /* FC_ENUM16 */ +/* 104 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 106 */ NdrFcShort( 0x110 ), /* Type Offset=272 */ +/* 108 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 110 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 112 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 114 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0xd, /* FC_ENUM16 */ +/* 116 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 118 */ NdrFcShort( 0x25a ), /* Type Offset=602 */ +/* 120 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 122 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 124 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 126 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 128 */ NdrFcShort( 0x42 ), /* Type Offset=66 */ +/* 130 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 132 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 134 */ NdrFcShort( 0x108 ), /* Type Offset=264 */ +/* 136 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 138 */ NdrFcShort( 0x100 ), /* Type Offset=256 */ +/* 140 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 142 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 144 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 146 */ + 0x50, /* FC_IN_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 148 */ NdrFcShort( 0x9a ), /* Type Offset=154 */ +/* 150 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 152 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 154 */ NdrFcShort( 0x9e ), /* Type Offset=158 */ +/* 156 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 158 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 160 */ NdrFcShort( 0x100 ), /* Type Offset=256 */ +/* 162 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 164 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 166 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 168 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 170 */ NdrFcShort( 0x266 ), /* Type Offset=614 */ +/* 172 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 174 */ NdrFcShort( 0x2b2 ), /* Type Offset=690 */ +/* 176 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 178 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 180 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 182 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 184 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 186 */ NdrFcShort( 0x2d4 ), /* Type Offset=724 */ +/* 188 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 190 */ NdrFcShort( 0x2b2 ), /* Type Offset=690 */ +/* 192 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 194 */ NdrFcShort( 0x2b2 ), /* Type Offset=690 */ +/* 196 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 198 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 200 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 202 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 204 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 206 */ NdrFcShort( 0x2f8 ), /* Type Offset=760 */ +/* 208 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 210 */ NdrFcShort( 0x30a ), /* Type Offset=778 */ +/* 212 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 214 */ NdrFcShort( 0x2b2 ), /* Type Offset=690 */ +/* 216 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 218 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 220 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 222 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 224 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 226 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 228 */ NdrFcShort( 0x108 ), /* Type Offset=264 */ +/* 230 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 232 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 234 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 236 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0xd, /* FC_ENUM16 */ +/* 238 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 240 */ NdrFcShort( 0x342 ), /* Type Offset=834 */ +/* 242 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 244 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 246 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 248 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0xd, /* FC_ENUM16 */ +/* 250 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 252 */ NdrFcShort( 0x39c ), /* Type Offset=924 */ +/* 254 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 256 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 258 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 260 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 262 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 264 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 266 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 268 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 270 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 272 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 274 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 276 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 278 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 280 */ NdrFcShort( 0x3a8 ), /* Type Offset=936 */ +/* 282 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 284 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 286 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 288 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0xd, /* FC_ENUM16 */ +/* 290 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 292 */ NdrFcShort( 0x3d0 ), /* Type Offset=976 */ +/* 294 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 296 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 298 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 300 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0xd, /* FC_ENUM16 */ +/* 302 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 304 */ NdrFcShort( 0x42c ), /* Type Offset=1068 */ +/* 306 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 308 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 310 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 312 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 314 */ NdrFcShort( 0x104 ), /* Type Offset=260 */ +/* 316 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 318 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 320 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 322 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 324 */ NdrFcShort( 0x438 ), /* Type Offset=1080 */ +/* 326 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 328 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 330 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 332 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0xd, /* FC_ENUM16 */ +/* 334 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 336 */ NdrFcShort( 0x43c ), /* Type Offset=1084 */ +/* 338 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 340 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 342 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 344 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0xd, /* FC_ENUM16 */ +/* 346 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 348 */ NdrFcShort( 0xa56 ), /* Type Offset=2646 */ +/* 350 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 352 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 354 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 356 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0x2, /* FC_CHAR */ +/* 358 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 360 */ NdrFcShort( 0xa62 ), /* Type Offset=2658 */ +/* 362 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 364 */ NdrFcShort( 0xa62 ), /* Type Offset=2658 */ +/* 366 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0x2, /* FC_CHAR */ +/* 368 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 370 */ NdrFcShort( 0xa62 ), /* Type Offset=2658 */ +/* 372 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 374 */ NdrFcShort( 0xa62 ), /* Type Offset=2658 */ +/* 376 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0x2, /* FC_CHAR */ +/* 378 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 380 */ NdrFcShort( 0xa62 ), /* Type Offset=2658 */ +/* 382 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0x2, /* FC_CHAR */ +/* 384 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 386 */ NdrFcShort( 0xa62 ), /* Type Offset=2658 */ +/* 388 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 390 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 392 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 394 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 396 */ NdrFcShort( 0xa66 ), /* Type Offset=2662 */ +/* 398 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 400 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 402 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 404 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0xd, /* FC_ENUM16 */ +/* 406 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 408 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 410 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 412 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 414 */ NdrFcShort( 0x100 ), /* Type Offset=256 */ +/* 416 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 418 */ NdrFcShort( 0x100 ), /* Type Offset=256 */ +/* 420 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 422 */ NdrFcShort( 0xa90 ), /* Type Offset=2704 */ +/* 424 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 426 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 428 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 430 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0xd, /* FC_ENUM16 */ +/* 432 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 434 */ NdrFcShort( 0x42 ), /* Type Offset=66 */ +/* 436 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 438 */ NdrFcShort( 0x100 ), /* Type Offset=256 */ +/* 440 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 442 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 444 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 446 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 448 */ NdrFcShort( 0xbf8 ), /* Type Offset=3064 */ +/* 450 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 452 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 454 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 456 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0xd, /* FC_ENUM16 */ +/* 458 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 460 */ NdrFcShort( 0xc04 ), /* Type Offset=3076 */ +/* 462 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 464 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 466 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 468 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0xd, /* FC_ENUM16 */ +/* 470 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 472 */ NdrFcShort( 0xc14 ), /* Type Offset=3092 */ +/* 474 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 476 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 478 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 480 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0xd, /* FC_ENUM16 */ +/* 482 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 484 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 486 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 488 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 490 */ NdrFcShort( 0x100 ), /* Type Offset=256 */ +/* 492 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 494 */ NdrFcShort( 0x100 ), /* Type Offset=256 */ +/* 496 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 498 */ NdrFcShort( 0xc24 ), /* Type Offset=3108 */ +/* 500 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 502 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 504 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 506 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 508 */ NdrFcShort( 0x42 ), /* Type Offset=66 */ +/* 510 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 512 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 514 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 516 */ NdrFcShort( 0xc30 ), /* Type Offset=3120 */ +/* 518 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 520 */ NdrFcShort( 0x100 ), /* Type Offset=256 */ +/* 522 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 524 */ NdrFcShort( 0x100 ), /* Type Offset=256 */ +/* 526 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 528 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 530 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 532 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0xd, /* FC_ENUM16 */ +/* 534 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 536 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 538 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 540 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 542 */ NdrFcShort( 0x100 ), /* Type Offset=256 */ +/* 544 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 546 */ NdrFcShort( 0x100 ), /* Type Offset=256 */ +/* 548 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 550 */ NdrFcShort( 0xc38 ), /* Type Offset=3128 */ +/* 552 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 554 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 556 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 558 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 560 */ NdrFcShort( 0x266 ), /* Type Offset=614 */ +/* 562 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 564 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0xf, /* FC_IGNORE */ +/* 566 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 568 */ NdrFcShort( 0xc44 ), /* Type Offset=3140 */ +/* 570 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 572 */ NdrFcShort( 0xc6c ), /* Type Offset=3180 */ +/* 574 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 576 */ NdrFcShort( 0xc70 ), /* Type Offset=3184 */ +/* 578 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 580 */ NdrFcShort( 0xa62 ), /* Type Offset=2658 */ +/* 582 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 584 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0xf, /* FC_IGNORE */ +/* 586 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 588 */ NdrFcShort( 0xc74 ), /* Type Offset=3188 */ +/* 590 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 592 */ NdrFcShort( 0x42 ), /* Type Offset=66 */ +/* 594 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 596 */ NdrFcShort( 0xc70 ), /* Type Offset=3184 */ +/* 598 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 600 */ NdrFcShort( 0xa62 ), /* Type Offset=2658 */ +/* 602 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0x2, /* FC_CHAR */ +/* 604 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 606 */ NdrFcShort( 0xc70 ), /* Type Offset=3184 */ +/* 608 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 610 */ NdrFcShort( 0xa62 ), /* Type Offset=2658 */ +/* 612 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 614 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0xf, /* FC_IGNORE */ +/* 616 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 618 */ NdrFcShort( 0xc74 ), /* Type Offset=3188 */ +/* 620 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 622 */ NdrFcShort( 0xbf8 ), /* Type Offset=3064 */ +/* 624 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 626 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 628 */ NdrFcShort( 0xc78 ), /* Type Offset=3192 */ +/* 630 */ + 0x51, /* FC_OUT_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 632 */ NdrFcShort( 0x4 ), /* Type Offset=4 */ +/* 634 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 636 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ +/* 638 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 640 */ NdrFcShort( 0x14 ), /* Type Offset=20 */ +/* 642 */ 0x4e, /* FC_IN_PARAM_BASETYPE */ + 0xd, /* FC_ENUM16 */ +/* 644 */ + 0x4d, /* FC_IN_PARAM */ +#ifndef _ALPHA_ + 0x1, /* x86, MIPS & PPC Stack size = 1 */ +#else + 0x2, /* Alpha Stack size = 2 */ +#endif +/* 646 */ NdrFcShort( 0xc7c ), /* Type Offset=3196 */ +/* 648 */ 0x53, /* FC_RETURN_PARAM_BASETYPE */ + 0x8, /* FC_LONG */ + + 0x0 + } + }; + +static const MIDL_TYPE_FORMAT_STRING __MIDLTypeFormatString = + { + 0, + { + 0x12, 0x8, /* FC_UP [simple_pointer] */ +/* 2 */ 0x5, /* FC_WCHAR */ + 0x5c, /* FC_PAD */ +/* 4 */ + 0x11, 0x0, /* FC_RP */ +/* 6 */ NdrFcShort( 0x2 ), /* Offset= 2 (8) */ +/* 8 */ 0x30, /* FC_BIND_CONTEXT */ + 0xa0, /* 160 */ +/* 10 */ 0x0, /* 0 */ + 0x1, /* 1 */ +/* 12 */ + 0x11, 0x0, /* FC_RP */ +/* 14 */ NdrFcShort( 0x2 ), /* Offset= 2 (16) */ +/* 16 */ 0x30, /* FC_BIND_CONTEXT */ + 0xe0, /* 224 */ +/* 18 */ 0x0, /* 0 */ + 0x0, /* 0 */ +/* 20 */ 0x30, /* FC_BIND_CONTEXT */ + 0x40, /* 64 */ +/* 22 */ 0x0, /* 0 */ + 0x0, /* 0 */ +/* 24 */ + 0x11, 0x0, /* FC_RP */ +/* 26 */ NdrFcShort( 0xc ), /* Offset= 12 (38) */ +/* 28 */ + 0x1b, /* FC_CARRAY */ + 0x0, /* 0 */ +/* 30 */ NdrFcShort( 0x1 ), /* 1 */ +/* 32 */ 0x18, /* 24 */ + 0x0, /* */ +/* 34 */ NdrFcShort( 0x0 ), /* 0 */ +/* 36 */ 0x2, /* FC_CHAR */ + 0x5b, /* FC_END */ +/* 38 */ + 0x16, /* FC_PSTRUCT */ + 0x3, /* 3 */ +/* 40 */ NdrFcShort( 0x8 ), /* 8 */ +/* 42 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 44 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 46 */ NdrFcShort( 0x4 ), /* 4 */ +/* 48 */ NdrFcShort( 0x4 ), /* 4 */ +/* 50 */ 0x12, 0x0, /* FC_UP */ +/* 52 */ NdrFcShort( 0xffffffe8 ), /* Offset= -24 (28) */ +/* 54 */ + 0x5b, /* FC_END */ + + 0x8, /* FC_LONG */ +/* 56 */ 0x8, /* FC_LONG */ + 0x5b, /* FC_END */ +/* 58 */ + 0x11, 0x14, /* FC_RP [alloced_on_stack] */ +/* 60 */ NdrFcShort( 0x2 ), /* Offset= 2 (62) */ +/* 62 */ + 0x12, 0x0, /* FC_UP */ +/* 64 */ NdrFcShort( 0xffffffe6 ), /* Offset= -26 (38) */ +/* 66 */ + 0x11, 0x0, /* FC_RP */ +/* 68 */ NdrFcShort( 0x10 ), /* Offset= 16 (84) */ +/* 70 */ + 0x1c, /* FC_CVARRAY */ + 0x1, /* 1 */ +/* 72 */ NdrFcShort( 0x2 ), /* 2 */ +/* 74 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 76 */ NdrFcShort( 0x2 ), /* 2 */ +/* 78 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 80 */ NdrFcShort( 0x0 ), /* 0 */ +/* 82 */ 0x5, /* FC_WCHAR */ + 0x5b, /* FC_END */ +/* 84 */ + 0x16, /* FC_PSTRUCT */ + 0x3, /* 3 */ +/* 86 */ NdrFcShort( 0x8 ), /* 8 */ +/* 88 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 90 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 92 */ NdrFcShort( 0x4 ), /* 4 */ +/* 94 */ NdrFcShort( 0x4 ), /* 4 */ +/* 96 */ 0x12, 0x0, /* FC_UP */ +/* 98 */ NdrFcShort( 0xffffffe4 ), /* Offset= -28 (70) */ +/* 100 */ + 0x5b, /* FC_END */ + + 0x6, /* FC_SHORT */ +/* 102 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 104 */ 0x8, /* FC_LONG */ + 0x5b, /* FC_END */ +/* 106 */ + 0x11, 0x14, /* FC_RP [alloced_on_stack] */ +/* 108 */ NdrFcShort( 0x2 ), /* Offset= 2 (110) */ +/* 110 */ + 0x12, 0x0, /* FC_UP */ +/* 112 */ NdrFcShort( 0x1c ), /* Offset= 28 (140) */ +/* 114 */ + 0x1d, /* FC_SMFARRAY */ + 0x0, /* 0 */ +/* 116 */ NdrFcShort( 0x6 ), /* 6 */ +/* 118 */ 0x2, /* FC_CHAR */ + 0x5b, /* FC_END */ +/* 120 */ + 0x15, /* FC_STRUCT */ + 0x0, /* 0 */ +/* 122 */ NdrFcShort( 0x6 ), /* 6 */ +/* 124 */ 0x4c, /* FC_EMBEDDED_COMPLEX */ + 0x0, /* 0 */ +/* 126 */ NdrFcShort( 0xfffffff4 ), /* Offset= -12 (114) */ +/* 128 */ 0x5c, /* FC_PAD */ + 0x5b, /* FC_END */ +/* 130 */ + 0x1b, /* FC_CARRAY */ + 0x3, /* 3 */ +/* 132 */ NdrFcShort( 0x4 ), /* 4 */ +/* 134 */ 0x3, /* 3 */ + 0x0, /* */ +/* 136 */ NdrFcShort( 0xfffffff9 ), /* -7 */ +/* 138 */ 0x8, /* FC_LONG */ + 0x5b, /* FC_END */ +/* 140 */ + 0x17, /* FC_CSTRUCT */ + 0x3, /* 3 */ +/* 142 */ NdrFcShort( 0x8 ), /* 8 */ +/* 144 */ NdrFcShort( 0xfffffff2 ), /* Offset= -14 (130) */ +/* 146 */ 0x2, /* FC_CHAR */ + 0x2, /* FC_CHAR */ +/* 148 */ 0x4c, /* FC_EMBEDDED_COMPLEX */ + 0x0, /* 0 */ +/* 150 */ NdrFcShort( 0xffffffe2 ), /* Offset= -30 (120) */ +/* 152 */ 0x5c, /* FC_PAD */ + 0x5b, /* FC_END */ +/* 154 */ + 0x11, 0x8, /* FC_RP [simple_pointer] */ +/* 156 */ 0x8, /* FC_LONG */ + 0x5c, /* FC_PAD */ +/* 158 */ + 0x11, 0x14, /* FC_RP [alloced_on_stack] */ +/* 160 */ NdrFcShort( 0x2 ), /* Offset= 2 (162) */ +/* 162 */ + 0x12, 0x0, /* FC_UP */ +/* 164 */ NdrFcShort( 0x48 ), /* Offset= 72 (236) */ +/* 166 */ + 0x1c, /* FC_CVARRAY */ + 0x1, /* 1 */ +/* 168 */ NdrFcShort( 0x2 ), /* 2 */ +/* 170 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 172 */ NdrFcShort( 0x6 ), /* 6 */ +/* 174 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 176 */ NdrFcShort( 0x4 ), /* 4 */ +/* 178 */ 0x5, /* FC_WCHAR */ + 0x5b, /* FC_END */ +/* 180 */ + 0x16, /* FC_PSTRUCT */ + 0x3, /* 3 */ +/* 182 */ NdrFcShort( 0xc ), /* 12 */ +/* 184 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 186 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 188 */ NdrFcShort( 0x8 ), /* 8 */ +/* 190 */ NdrFcShort( 0x8 ), /* 8 */ +/* 192 */ 0x12, 0x0, /* FC_UP */ +/* 194 */ NdrFcShort( 0xffffffe4 ), /* Offset= -28 (166) */ +/* 196 */ + 0x5b, /* FC_END */ + + 0x8, /* FC_LONG */ +/* 198 */ 0x6, /* FC_SHORT */ + 0x6, /* FC_SHORT */ +/* 200 */ 0x38, /* FC_ALIGNM4 */ + 0x8, /* FC_LONG */ +/* 202 */ 0x5c, /* FC_PAD */ + 0x5b, /* FC_END */ +/* 204 */ + 0x1b, /* FC_CARRAY */ + 0x3, /* 3 */ +/* 206 */ NdrFcShort( 0xc ), /* 12 */ +/* 208 */ 0x18, /* 24 */ + 0x0, /* */ +/* 210 */ NdrFcShort( 0x0 ), /* 0 */ +/* 212 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 214 */ + 0x48, /* FC_VARIABLE_REPEAT */ + 0x49, /* FC_FIXED_OFFSET */ +/* 216 */ NdrFcShort( 0xc ), /* 12 */ +/* 218 */ NdrFcShort( 0x0 ), /* 0 */ +/* 220 */ NdrFcShort( 0x1 ), /* 1 */ +/* 222 */ NdrFcShort( 0x8 ), /* 8 */ +/* 224 */ NdrFcShort( 0x8 ), /* 8 */ +/* 226 */ 0x12, 0x0, /* FC_UP */ +/* 228 */ NdrFcShort( 0xffffffc2 ), /* Offset= -62 (166) */ +/* 230 */ + 0x5b, /* FC_END */ + + 0x4c, /* FC_EMBEDDED_COMPLEX */ +/* 232 */ 0x0, /* 0 */ + NdrFcShort( 0xffffffcb ), /* Offset= -53 (180) */ + 0x5b, /* FC_END */ +/* 236 */ + 0x16, /* FC_PSTRUCT */ + 0x3, /* 3 */ +/* 238 */ NdrFcShort( 0x8 ), /* 8 */ +/* 240 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 242 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 244 */ NdrFcShort( 0x4 ), /* 4 */ +/* 246 */ NdrFcShort( 0x4 ), /* 4 */ +/* 248 */ 0x12, 0x0, /* FC_UP */ +/* 250 */ NdrFcShort( 0xffffffd2 ), /* Offset= -46 (204) */ +/* 252 */ + 0x5b, /* FC_END */ + + 0x8, /* FC_LONG */ +/* 254 */ 0x8, /* FC_LONG */ + 0x5b, /* FC_END */ +/* 256 */ + 0x11, 0xc, /* FC_RP [alloced_on_stack] [simple_pointer] */ +/* 258 */ 0x8, /* FC_LONG */ + 0x5c, /* FC_PAD */ +/* 260 */ + 0x11, 0x0, /* FC_RP */ +/* 262 */ NdrFcShort( 0xffffff86 ), /* Offset= -122 (140) */ +/* 264 */ + 0x11, 0x0, /* FC_RP */ +/* 266 */ NdrFcShort( 0x2 ), /* Offset= 2 (268) */ +/* 268 */ 0x30, /* FC_BIND_CONTEXT */ + 0xa0, /* 160 */ +/* 270 */ 0x0, /* 0 */ + 0x3, /* 3 */ +/* 272 */ + 0x11, 0x14, /* FC_RP [alloced_on_stack] */ +/* 274 */ NdrFcShort( 0x2 ), /* Offset= 2 (276) */ +/* 276 */ + 0x12, 0x0, /* FC_UP */ +/* 278 */ NdrFcShort( 0x2 ), /* Offset= 2 (280) */ +/* 280 */ + 0x2b, /* FC_NON_ENCAPSULATED_UNION */ + 0xd, /* FC_ENUM16 */ +/* 282 */ 0x26, /* 38 */ + 0x0, /* */ +#ifndef _ALPHA_ +/* 284 */ NdrFcShort( 0x4 ), /* x86, MIPS, PPC Stack size/offset = 4 */ +#else + NdrFcShort( 0x8 ), /* Alpha Stack size/offset = 8 */ +#endif +/* 286 */ NdrFcShort( 0x2 ), /* Offset= 2 (288) */ +/* 288 */ NdrFcShort( 0x58 ), /* 88 */ +/* 290 */ NdrFcShort( 0x700c ), /* 28684 */ +/* 292 */ NdrFcLong( 0x1 ), /* 1 */ +/* 296 */ NdrFcShort( 0x4e ), /* Offset= 78 (374) */ +/* 298 */ NdrFcLong( 0x2 ), /* 2 */ +/* 302 */ NdrFcShort( 0x84 ), /* Offset= 132 (434) */ +/* 304 */ NdrFcLong( 0x3 ), /* 3 */ +/* 308 */ NdrFcShort( 0xc0 ), /* Offset= 192 (500) */ +/* 310 */ NdrFcLong( 0x4 ), /* 4 */ +/* 314 */ NdrFcShort( 0xffffff1a ), /* Offset= -230 (84) */ +/* 316 */ NdrFcLong( 0x5 ), /* 5 */ +/* 320 */ NdrFcShort( 0xffffff14 ), /* Offset= -236 (84) */ +/* 322 */ NdrFcLong( 0x7 ), /* 7 */ +/* 326 */ NdrFcShort( 0xb8 ), /* Offset= 184 (510) */ +/* 328 */ NdrFcLong( 0x6 ), /* 6 */ +/* 332 */ NdrFcShort( 0xffffff08 ), /* Offset= -248 (84) */ +/* 334 */ NdrFcLong( 0x8 ), /* 8 */ +/* 338 */ NdrFcShort( 0xb6 ), /* Offset= 182 (520) */ +/* 340 */ NdrFcLong( 0x9 ), /* 9 */ +/* 344 */ NdrFcShort( 0xa6 ), /* Offset= 166 (510) */ +/* 346 */ NdrFcLong( 0xb ), /* 11 */ +/* 350 */ NdrFcShort( 0xbe ), /* Offset= 190 (540) */ +/* 352 */ NdrFcLong( 0xc ), /* 12 */ +/* 356 */ NdrFcShort( 0xd0 ), /* Offset= 208 (564) */ +/* 358 */ NdrFcLong( 0xd ), /* 13 */ +/* 362 */ NdrFcShort( 0xde ), /* Offset= 222 (584) */ +/* 364 */ NdrFcShort( 0xffffffff ), /* Offset= -1 (363) */ +/* 366 */ + 0x15, /* FC_STRUCT */ + 0x3, /* 3 */ +/* 368 */ NdrFcShort( 0x8 ), /* 8 */ +/* 370 */ 0x8, /* FC_LONG */ + 0x8, /* FC_LONG */ +/* 372 */ 0x5c, /* FC_PAD */ + 0x5b, /* FC_END */ +/* 374 */ + 0x15, /* FC_STRUCT */ + 0x3, /* 3 */ +/* 376 */ NdrFcShort( 0x18 ), /* 24 */ +/* 378 */ 0x6, /* FC_SHORT */ + 0x6, /* FC_SHORT */ +/* 380 */ 0x38, /* FC_ALIGNM4 */ + 0x8, /* FC_LONG */ +/* 382 */ 0x4c, /* FC_EMBEDDED_COMPLEX */ + 0x0, /* 0 */ +/* 384 */ NdrFcShort( 0xffffffee ), /* Offset= -18 (366) */ +/* 386 */ 0x4c, /* FC_EMBEDDED_COMPLEX */ + 0x0, /* 0 */ +/* 388 */ NdrFcShort( 0xffffffea ), /* Offset= -22 (366) */ +/* 390 */ 0x5c, /* FC_PAD */ + 0x5b, /* FC_END */ +/* 392 */ + 0x1c, /* FC_CVARRAY */ + 0x1, /* 1 */ +/* 394 */ NdrFcShort( 0x2 ), /* 2 */ +/* 396 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 398 */ NdrFcShort( 0xa ), /* 10 */ +/* 400 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 402 */ NdrFcShort( 0x8 ), /* 8 */ +/* 404 */ 0x5, /* FC_WCHAR */ + 0x5b, /* FC_END */ +/* 406 */ + 0x1c, /* FC_CVARRAY */ + 0x1, /* 1 */ +/* 408 */ NdrFcShort( 0x2 ), /* 2 */ +/* 410 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 412 */ NdrFcShort( 0x12 ), /* 18 */ +/* 414 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 416 */ NdrFcShort( 0x10 ), /* 16 */ +/* 418 */ 0x5, /* FC_WCHAR */ + 0x5b, /* FC_END */ +/* 420 */ + 0x1c, /* FC_CVARRAY */ + 0x1, /* 1 */ +/* 422 */ NdrFcShort( 0x2 ), /* 2 */ +/* 424 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 426 */ NdrFcShort( 0x1a ), /* 26 */ +/* 428 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 430 */ NdrFcShort( 0x18 ), /* 24 */ +/* 432 */ 0x5, /* FC_WCHAR */ + 0x5b, /* FC_END */ +/* 434 */ + 0x16, /* FC_PSTRUCT */ + 0x3, /* 3 */ +/* 436 */ NdrFcShort( 0x40 ), /* 64 */ +/* 438 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 440 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 442 */ NdrFcShort( 0xc ), /* 12 */ +/* 444 */ NdrFcShort( 0xc ), /* 12 */ +/* 446 */ 0x12, 0x0, /* FC_UP */ +/* 448 */ NdrFcShort( 0xffffffc8 ), /* Offset= -56 (392) */ +/* 450 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 452 */ NdrFcShort( 0x14 ), /* 20 */ +/* 454 */ NdrFcShort( 0x14 ), /* 20 */ +/* 456 */ 0x12, 0x0, /* FC_UP */ +/* 458 */ NdrFcShort( 0xffffffcc ), /* Offset= -52 (406) */ +/* 460 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 462 */ NdrFcShort( 0x1c ), /* 28 */ +/* 464 */ NdrFcShort( 0x1c ), /* 28 */ +/* 466 */ 0x12, 0x0, /* FC_UP */ +/* 468 */ NdrFcShort( 0xffffffd0 ), /* Offset= -48 (420) */ +/* 470 */ + 0x5b, /* FC_END */ + + 0x4c, /* FC_EMBEDDED_COMPLEX */ +/* 472 */ 0x0, /* 0 */ + NdrFcShort( 0xffffff95 ), /* Offset= -107 (366) */ + 0x6, /* FC_SHORT */ +/* 476 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 478 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 480 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 482 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 484 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 486 */ 0x8, /* FC_LONG */ + 0x4c, /* FC_EMBEDDED_COMPLEX */ +/* 488 */ 0x0, /* 0 */ + NdrFcShort( 0xffffff85 ), /* Offset= -123 (366) */ + 0x8, /* FC_LONG */ +/* 492 */ 0x8, /* FC_LONG */ + 0x2, /* FC_CHAR */ +/* 494 */ 0x38, /* FC_ALIGNM4 */ + 0x8, /* FC_LONG */ +/* 496 */ 0x8, /* FC_LONG */ + 0x8, /* FC_LONG */ +/* 498 */ 0x5c, /* FC_PAD */ + 0x5b, /* FC_END */ +/* 500 */ + 0x15, /* FC_STRUCT */ + 0x3, /* 3 */ +/* 502 */ NdrFcShort( 0x8 ), /* 8 */ +/* 504 */ 0x4c, /* FC_EMBEDDED_COMPLEX */ + 0x0, /* 0 */ +/* 506 */ NdrFcShort( 0xffffff74 ), /* Offset= -140 (366) */ +/* 508 */ 0x5c, /* FC_PAD */ + 0x5b, /* FC_END */ +/* 510 */ + 0x1a, /* FC_BOGUS_STRUCT */ + 0x1, /* 1 */ +/* 512 */ NdrFcShort( 0x4 ), /* 4 */ +/* 514 */ NdrFcShort( 0x0 ), /* 0 */ +/* 516 */ NdrFcShort( 0x0 ), /* Offset= 0 (516) */ +/* 518 */ 0xd, /* FC_ENUM16 */ + 0x5b, /* FC_END */ +/* 520 */ + 0x15, /* FC_STRUCT */ + 0x3, /* 3 */ +/* 522 */ NdrFcShort( 0x10 ), /* 16 */ +/* 524 */ 0x4c, /* FC_EMBEDDED_COMPLEX */ + 0x0, /* 0 */ +/* 526 */ NdrFcShort( 0xffffff60 ), /* Offset= -160 (366) */ +/* 528 */ 0x4c, /* FC_EMBEDDED_COMPLEX */ + 0x0, /* 0 */ +/* 530 */ NdrFcShort( 0xffffff5c ), /* Offset= -164 (366) */ +/* 532 */ 0x5c, /* FC_PAD */ + 0x5b, /* FC_END */ +/* 534 */ + 0x15, /* FC_STRUCT */ + 0x7, /* 7 */ +/* 536 */ NdrFcShort( 0x8 ), /* 8 */ +/* 538 */ 0xb, /* FC_HYPER */ + 0x5b, /* FC_END */ +/* 540 */ + 0x1a, /* FC_BOGUS_STRUCT */ + 0x7, /* 7 */ +/* 542 */ NdrFcShort( 0x54 ), /* 84 */ +/* 544 */ NdrFcShort( 0x0 ), /* 0 */ +/* 546 */ NdrFcShort( 0x0 ), /* Offset= 0 (546) */ +/* 548 */ 0x4c, /* FC_EMBEDDED_COMPLEX */ + 0x0, /* 0 */ +/* 550 */ NdrFcShort( 0xffffff8c ), /* Offset= -116 (434) */ +/* 552 */ 0x4c, /* FC_EMBEDDED_COMPLEX */ + 0x0, /* 0 */ +/* 554 */ NdrFcShort( 0xffffffec ), /* Offset= -20 (534) */ +/* 556 */ 0x4c, /* FC_EMBEDDED_COMPLEX */ + 0x0, /* 0 */ +/* 558 */ NdrFcShort( 0xffffffe8 ), /* Offset= -24 (534) */ +/* 560 */ 0x6, /* FC_SHORT */ + 0x3e, /* FC_STRUCTPAD2 */ +/* 562 */ 0x5c, /* FC_PAD */ + 0x5b, /* FC_END */ +/* 564 */ + 0x1a, /* FC_BOGUS_STRUCT */ + 0x7, /* 7 */ +/* 566 */ NdrFcShort( 0x18 ), /* 24 */ +/* 568 */ NdrFcShort( 0x0 ), /* 0 */ +/* 570 */ NdrFcShort( 0x0 ), /* Offset= 0 (570) */ +/* 572 */ 0x4c, /* FC_EMBEDDED_COMPLEX */ + 0x0, /* 0 */ +/* 574 */ NdrFcShort( 0xffffffd8 ), /* Offset= -40 (534) */ +/* 576 */ 0x4c, /* FC_EMBEDDED_COMPLEX */ + 0x0, /* 0 */ +/* 578 */ NdrFcShort( 0xffffffd4 ), /* Offset= -44 (534) */ +/* 580 */ 0x6, /* FC_SHORT */ + 0x42, /* FC_STRUCTPAD6 */ +/* 582 */ 0x5c, /* FC_PAD */ + 0x5b, /* FC_END */ +/* 584 */ + 0x15, /* FC_STRUCT */ + 0x3, /* 3 */ +/* 586 */ NdrFcShort( 0x18 ), /* 24 */ +/* 588 */ 0x4c, /* FC_EMBEDDED_COMPLEX */ + 0x0, /* 0 */ +/* 590 */ NdrFcShort( 0xffffff20 ), /* Offset= -224 (366) */ +/* 592 */ 0x4c, /* FC_EMBEDDED_COMPLEX */ + 0x0, /* 0 */ +/* 594 */ NdrFcShort( 0xffffff1c ), /* Offset= -228 (366) */ +/* 596 */ 0x4c, /* FC_EMBEDDED_COMPLEX */ + 0x0, /* 0 */ +/* 598 */ NdrFcShort( 0xffffff18 ), /* Offset= -232 (366) */ +/* 600 */ 0x5c, /* FC_PAD */ + 0x5b, /* FC_END */ +/* 602 */ + 0x11, 0x0, /* FC_RP */ +/* 604 */ NdrFcShort( 0x2 ), /* Offset= 2 (606) */ +/* 606 */ + 0x2b, /* FC_NON_ENCAPSULATED_UNION */ + 0xd, /* FC_ENUM16 */ +/* 608 */ 0x26, /* 38 */ + 0x0, /* */ +#ifndef _ALPHA_ +/* 610 */ NdrFcShort( 0x4 ), /* x86, MIPS, PPC Stack size/offset = 4 */ +#else + NdrFcShort( 0x8 ), /* Alpha Stack size/offset = 8 */ +#endif +/* 612 */ NdrFcShort( 0xfffffebc ), /* Offset= -324 (288) */ +/* 614 */ + 0x11, 0x0, /* FC_RP */ +/* 616 */ NdrFcShort( 0x36 ), /* Offset= 54 (670) */ +/* 618 */ + 0x16, /* FC_PSTRUCT */ + 0x3, /* 3 */ +/* 620 */ NdrFcShort( 0x4 ), /* 4 */ +/* 622 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 624 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 626 */ NdrFcShort( 0x0 ), /* 0 */ +/* 628 */ NdrFcShort( 0x0 ), /* 0 */ +/* 630 */ 0x12, 0x0, /* FC_UP */ +/* 632 */ NdrFcShort( 0xfffffe14 ), /* Offset= -492 (140) */ +/* 634 */ + 0x5b, /* FC_END */ + + 0x8, /* FC_LONG */ +/* 636 */ 0x5c, /* FC_PAD */ + 0x5b, /* FC_END */ +/* 638 */ + 0x1b, /* FC_CARRAY */ + 0x3, /* 3 */ +/* 640 */ NdrFcShort( 0x4 ), /* 4 */ +/* 642 */ 0x18, /* 24 */ + 0x0, /* */ +/* 644 */ NdrFcShort( 0x0 ), /* 0 */ +/* 646 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 648 */ + 0x48, /* FC_VARIABLE_REPEAT */ + 0x49, /* FC_FIXED_OFFSET */ +/* 650 */ NdrFcShort( 0x4 ), /* 4 */ +/* 652 */ NdrFcShort( 0x0 ), /* 0 */ +/* 654 */ NdrFcShort( 0x1 ), /* 1 */ +/* 656 */ NdrFcShort( 0x0 ), /* 0 */ +/* 658 */ NdrFcShort( 0x0 ), /* 0 */ +/* 660 */ 0x12, 0x0, /* FC_UP */ +/* 662 */ NdrFcShort( 0xfffffdf6 ), /* Offset= -522 (140) */ +/* 664 */ + 0x5b, /* FC_END */ + + 0x4c, /* FC_EMBEDDED_COMPLEX */ +/* 666 */ 0x0, /* 0 */ + NdrFcShort( 0xffffffcf ), /* Offset= -49 (618) */ + 0x5b, /* FC_END */ +/* 670 */ + 0x16, /* FC_PSTRUCT */ + 0x3, /* 3 */ +/* 672 */ NdrFcShort( 0x8 ), /* 8 */ +/* 674 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 676 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 678 */ NdrFcShort( 0x4 ), /* 4 */ +/* 680 */ NdrFcShort( 0x4 ), /* 4 */ +/* 682 */ 0x12, 0x1, /* FC_UP [all_nodes] */ +/* 684 */ NdrFcShort( 0xffffffd2 ), /* Offset= -46 (638) */ +/* 686 */ + 0x5b, /* FC_END */ + + 0x8, /* FC_LONG */ +/* 688 */ 0x8, /* FC_LONG */ + 0x5b, /* FC_END */ +/* 690 */ + 0x11, 0x4, /* FC_RP [alloced_on_stack] */ +/* 692 */ NdrFcShort( 0xc ), /* Offset= 12 (704) */ +/* 694 */ + 0x1b, /* FC_CARRAY */ + 0x3, /* 3 */ +/* 696 */ NdrFcShort( 0x4 ), /* 4 */ +/* 698 */ 0x18, /* 24 */ + 0x0, /* */ +/* 700 */ NdrFcShort( 0x0 ), /* 0 */ +/* 702 */ 0x8, /* FC_LONG */ + 0x5b, /* FC_END */ +/* 704 */ + 0x16, /* FC_PSTRUCT */ + 0x3, /* 3 */ +/* 706 */ NdrFcShort( 0x8 ), /* 8 */ +/* 708 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 710 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 712 */ NdrFcShort( 0x4 ), /* 4 */ +/* 714 */ NdrFcShort( 0x4 ), /* 4 */ +/* 716 */ 0x12, 0x0, /* FC_UP */ +/* 718 */ NdrFcShort( 0xffffffe8 ), /* Offset= -24 (694) */ +/* 720 */ + 0x5b, /* FC_END */ + + 0x8, /* FC_LONG */ +/* 722 */ 0x8, /* FC_LONG */ + 0x5b, /* FC_END */ +/* 724 */ + 0x1c, /* FC_CVARRAY */ + 0x3, /* 3 */ +/* 726 */ NdrFcShort( 0x8 ), /* 8 */ +/* 728 */ 0x40, /* 64 */ + 0x0, /* 0 */ +/* 730 */ NdrFcShort( 0x3e8 ), /* 1000 */ +/* 732 */ 0x28, /* 40 */ + 0x0, /* */ +#ifndef _ALPHA_ +/* 734 */ NdrFcShort( 0x4 ), /* x86, MIPS, PPC Stack size/offset = 4 */ +#else + NdrFcShort( 0x8 ), /* Alpha Stack size/offset = 8 */ +#endif +/* 736 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 738 */ + 0x48, /* FC_VARIABLE_REPEAT */ + 0x4a, /* FC_VARIABLE_OFFSET */ +/* 740 */ NdrFcShort( 0x8 ), /* 8 */ +/* 742 */ NdrFcShort( 0x0 ), /* 0 */ +/* 744 */ NdrFcShort( 0x1 ), /* 1 */ +/* 746 */ NdrFcShort( 0x4 ), /* 4 */ +/* 748 */ NdrFcShort( 0x4 ), /* 4 */ +/* 750 */ 0x12, 0x0, /* FC_UP */ +/* 752 */ NdrFcShort( 0xfffffd56 ), /* Offset= -682 (70) */ +/* 754 */ + 0x5b, /* FC_END */ + + 0x4c, /* FC_EMBEDDED_COMPLEX */ +/* 756 */ 0x0, /* 0 */ + NdrFcShort( 0xfffffd5f ), /* Offset= -673 (84) */ + 0x5b, /* FC_END */ +/* 760 */ + 0x11, 0x0, /* FC_RP */ +/* 762 */ NdrFcShort( 0x2 ), /* Offset= 2 (764) */ +/* 764 */ + 0x1c, /* FC_CVARRAY */ + 0x3, /* 3 */ +/* 766 */ NdrFcShort( 0x4 ), /* 4 */ +/* 768 */ 0x40, /* 64 */ + 0x0, /* 0 */ +/* 770 */ NdrFcShort( 0x3e8 ), /* 1000 */ +/* 772 */ 0x28, /* 40 */ + 0x0, /* */ +#ifndef _ALPHA_ +/* 774 */ NdrFcShort( 0x4 ), /* x86, MIPS, PPC Stack size/offset = 4 */ +#else + NdrFcShort( 0x8 ), /* Alpha Stack size/offset = 8 */ +#endif +/* 776 */ 0x8, /* FC_LONG */ + 0x5b, /* FC_END */ +/* 778 */ + 0x11, 0x4, /* FC_RP [alloced_on_stack] */ +/* 780 */ NdrFcShort( 0x22 ), /* Offset= 34 (814) */ +/* 782 */ + 0x1b, /* FC_CARRAY */ + 0x3, /* 3 */ +/* 784 */ NdrFcShort( 0x8 ), /* 8 */ +/* 786 */ 0x18, /* 24 */ + 0x0, /* */ +/* 788 */ NdrFcShort( 0x0 ), /* 0 */ +/* 790 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 792 */ + 0x48, /* FC_VARIABLE_REPEAT */ + 0x49, /* FC_FIXED_OFFSET */ +/* 794 */ NdrFcShort( 0x8 ), /* 8 */ +/* 796 */ NdrFcShort( 0x0 ), /* 0 */ +/* 798 */ NdrFcShort( 0x1 ), /* 1 */ +/* 800 */ NdrFcShort( 0x4 ), /* 4 */ +/* 802 */ NdrFcShort( 0x4 ), /* 4 */ +/* 804 */ 0x12, 0x0, /* FC_UP */ +/* 806 */ NdrFcShort( 0xfffffd20 ), /* Offset= -736 (70) */ +/* 808 */ + 0x5b, /* FC_END */ + + 0x4c, /* FC_EMBEDDED_COMPLEX */ +/* 810 */ 0x0, /* 0 */ + NdrFcShort( 0xfffffd29 ), /* Offset= -727 (84) */ + 0x5b, /* FC_END */ +/* 814 */ + 0x16, /* FC_PSTRUCT */ + 0x3, /* 3 */ +/* 816 */ NdrFcShort( 0x8 ), /* 8 */ +/* 818 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 820 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 822 */ NdrFcShort( 0x4 ), /* 4 */ +/* 824 */ NdrFcShort( 0x4 ), /* 4 */ +/* 826 */ 0x12, 0x0, /* FC_UP */ +/* 828 */ NdrFcShort( 0xffffffd2 ), /* Offset= -46 (782) */ +/* 830 */ + 0x5b, /* FC_END */ + + 0x8, /* FC_LONG */ +/* 832 */ 0x8, /* FC_LONG */ + 0x5b, /* FC_END */ +/* 834 */ + 0x11, 0x14, /* FC_RP [alloced_on_stack] */ +/* 836 */ NdrFcShort( 0x2 ), /* Offset= 2 (838) */ +/* 838 */ + 0x12, 0x0, /* FC_UP */ +/* 840 */ NdrFcShort( 0x2 ), /* Offset= 2 (842) */ +/* 842 */ + 0x2b, /* FC_NON_ENCAPSULATED_UNION */ + 0xd, /* FC_ENUM16 */ +/* 844 */ 0x26, /* 38 */ + 0x0, /* */ +#ifndef _ALPHA_ +/* 846 */ NdrFcShort( 0x4 ), /* x86, MIPS, PPC Stack size/offset = 4 */ +#else + NdrFcShort( 0x8 ), /* Alpha Stack size/offset = 8 */ +#endif +/* 848 */ NdrFcShort( 0x2 ), /* Offset= 2 (850) */ +/* 850 */ NdrFcShort( 0x18 ), /* 24 */ +/* 852 */ NdrFcShort( 0x3004 ), /* 12292 */ +/* 854 */ NdrFcLong( 0x1 ), /* 1 */ +/* 858 */ NdrFcShort( 0x16 ), /* Offset= 22 (880) */ +/* 860 */ NdrFcLong( 0x2 ), /* 2 */ +/* 864 */ NdrFcShort( 0xfffffcf4 ), /* Offset= -780 (84) */ +/* 866 */ NdrFcLong( 0x3 ), /* 3 */ +/* 870 */ NdrFcShort( 0x30 ), /* Offset= 48 (918) */ +/* 872 */ NdrFcLong( 0x4 ), /* 4 */ +/* 876 */ NdrFcShort( 0xfffffce8 ), /* Offset= -792 (84) */ +/* 878 */ NdrFcShort( 0xffffffff ), /* Offset= -1 (877) */ +/* 880 */ + 0x16, /* FC_PSTRUCT */ + 0x3, /* 3 */ +/* 882 */ NdrFcShort( 0x18 ), /* 24 */ +/* 884 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 886 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 888 */ NdrFcShort( 0x4 ), /* 4 */ +/* 890 */ NdrFcShort( 0x4 ), /* 4 */ +/* 892 */ 0x12, 0x0, /* FC_UP */ +/* 894 */ NdrFcShort( 0xfffffcc8 ), /* Offset= -824 (70) */ +/* 896 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 898 */ NdrFcShort( 0x14 ), /* 20 */ +/* 900 */ NdrFcShort( 0x14 ), /* 20 */ +/* 902 */ 0x12, 0x0, /* FC_UP */ +/* 904 */ NdrFcShort( 0xfffffe0e ), /* Offset= -498 (406) */ +/* 906 */ + 0x5b, /* FC_END */ + + 0x6, /* FC_SHORT */ +/* 908 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 910 */ 0x8, /* FC_LONG */ + 0x8, /* FC_LONG */ +/* 912 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 914 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 916 */ 0x8, /* FC_LONG */ + 0x5b, /* FC_END */ +/* 918 */ + 0x15, /* FC_STRUCT */ + 0x3, /* 3 */ +/* 920 */ NdrFcShort( 0x4 ), /* 4 */ +/* 922 */ 0x8, /* FC_LONG */ + 0x5b, /* FC_END */ +/* 924 */ + 0x11, 0x0, /* FC_RP */ +/* 926 */ NdrFcShort( 0x2 ), /* Offset= 2 (928) */ +/* 928 */ + 0x2b, /* FC_NON_ENCAPSULATED_UNION */ + 0xd, /* FC_ENUM16 */ +/* 930 */ 0x26, /* 38 */ + 0x0, /* */ +#ifndef _ALPHA_ +/* 932 */ NdrFcShort( 0x4 ), /* x86, MIPS, PPC Stack size/offset = 4 */ +#else + NdrFcShort( 0x8 ), /* Alpha Stack size/offset = 8 */ +#endif +/* 934 */ NdrFcShort( 0xffffffac ), /* Offset= -84 (850) */ +/* 936 */ + 0x11, 0x14, /* FC_RP [alloced_on_stack] */ +/* 938 */ NdrFcShort( 0x2 ), /* Offset= 2 (940) */ +/* 940 */ + 0x12, 0x0, /* FC_UP */ +/* 942 */ NdrFcShort( 0x2 ), /* Offset= 2 (944) */ +/* 944 */ + 0x16, /* FC_PSTRUCT */ + 0x3, /* 3 */ +/* 946 */ NdrFcShort( 0xc ), /* 12 */ +/* 948 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 950 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 952 */ NdrFcShort( 0x4 ), /* 4 */ +/* 954 */ NdrFcShort( 0x4 ), /* 4 */ +/* 956 */ 0x12, 0x0, /* FC_UP */ +/* 958 */ NdrFcShort( 0xfffffef8 ), /* Offset= -264 (694) */ +/* 960 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 962 */ NdrFcShort( 0x8 ), /* 8 */ +/* 964 */ NdrFcShort( 0x8 ), /* 8 */ +/* 966 */ 0x12, 0x0, /* FC_UP */ +/* 968 */ NdrFcShort( 0xfffffeee ), /* Offset= -274 (694) */ +/* 970 */ + 0x5b, /* FC_END */ + + 0x8, /* FC_LONG */ +/* 972 */ 0x8, /* FC_LONG */ + 0x8, /* FC_LONG */ +/* 974 */ 0x5c, /* FC_PAD */ + 0x5b, /* FC_END */ +/* 976 */ + 0x11, 0x14, /* FC_RP [alloced_on_stack] */ +/* 978 */ NdrFcShort( 0x2 ), /* Offset= 2 (980) */ +/* 980 */ + 0x12, 0x0, /* FC_UP */ +/* 982 */ NdrFcShort( 0x2 ), /* Offset= 2 (984) */ +/* 984 */ + 0x2b, /* FC_NON_ENCAPSULATED_UNION */ + 0xd, /* FC_ENUM16 */ +/* 986 */ 0x26, /* 38 */ + 0x0, /* */ +#ifndef _ALPHA_ +/* 988 */ NdrFcShort( 0x4 ), /* x86, MIPS, PPC Stack size/offset = 4 */ +#else + NdrFcShort( 0x8 ), /* Alpha Stack size/offset = 8 */ +#endif +/* 990 */ NdrFcShort( 0x2 ), /* Offset= 2 (992) */ +/* 992 */ NdrFcShort( 0x14 ), /* 20 */ +/* 994 */ NdrFcShort( 0x3003 ), /* 12291 */ +/* 996 */ NdrFcLong( 0x1 ), /* 1 */ +/* 1000 */ NdrFcShort( 0x1e ), /* Offset= 30 (1030) */ +/* 1002 */ NdrFcLong( 0x2 ), /* 2 */ +/* 1006 */ NdrFcShort( 0xfffffc66 ), /* Offset= -922 (84) */ +/* 1008 */ NdrFcLong( 0x3 ), /* 3 */ +/* 1012 */ NdrFcShort( 0xfffffc60 ), /* Offset= -928 (84) */ +/* 1014 */ NdrFcShort( 0xffffffff ), /* Offset= -1 (1013) */ +/* 1016 */ + 0x1c, /* FC_CVARRAY */ + 0x1, /* 1 */ +/* 1018 */ NdrFcShort( 0x2 ), /* 2 */ +/* 1020 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 1022 */ NdrFcShort( 0xe ), /* 14 */ +/* 1024 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 1026 */ NdrFcShort( 0xc ), /* 12 */ +/* 1028 */ 0x5, /* FC_WCHAR */ + 0x5b, /* FC_END */ +/* 1030 */ + 0x16, /* FC_PSTRUCT */ + 0x3, /* 3 */ +/* 1032 */ NdrFcShort( 0x14 ), /* 20 */ +/* 1034 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 1036 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 1038 */ NdrFcShort( 0x4 ), /* 4 */ +/* 1040 */ NdrFcShort( 0x4 ), /* 4 */ +/* 1042 */ 0x12, 0x0, /* FC_UP */ +/* 1044 */ NdrFcShort( 0xfffffc32 ), /* Offset= -974 (70) */ +/* 1046 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 1048 */ NdrFcShort( 0x10 ), /* 16 */ +/* 1050 */ NdrFcShort( 0x10 ), /* 16 */ +/* 1052 */ 0x12, 0x0, /* FC_UP */ +/* 1054 */ NdrFcShort( 0xffffffda ), /* Offset= -38 (1016) */ +/* 1056 */ + 0x5b, /* FC_END */ + + 0x6, /* FC_SHORT */ +/* 1058 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 1060 */ 0x8, /* FC_LONG */ + 0x8, /* FC_LONG */ +/* 1062 */ 0x6, /* FC_SHORT */ + 0x6, /* FC_SHORT */ +/* 1064 */ 0x38, /* FC_ALIGNM4 */ + 0x8, /* FC_LONG */ +/* 1066 */ 0x5c, /* FC_PAD */ + 0x5b, /* FC_END */ +/* 1068 */ + 0x11, 0x0, /* FC_RP */ +/* 1070 */ NdrFcShort( 0x2 ), /* Offset= 2 (1072) */ +/* 1072 */ + 0x2b, /* FC_NON_ENCAPSULATED_UNION */ + 0xd, /* FC_ENUM16 */ +/* 1074 */ 0x26, /* 38 */ + 0x0, /* */ +#ifndef _ALPHA_ +/* 1076 */ NdrFcShort( 0x4 ), /* x86, MIPS, PPC Stack size/offset = 4 */ +#else + NdrFcShort( 0x8 ), /* Alpha Stack size/offset = 8 */ +#endif +/* 1078 */ NdrFcShort( 0xffffffaa ), /* Offset= -86 (992) */ +/* 1080 */ + 0x11, 0x4, /* FC_RP [alloced_on_stack] */ +/* 1082 */ NdrFcShort( 0xfffffe64 ), /* Offset= -412 (670) */ +/* 1084 */ + 0x11, 0x14, /* FC_RP [alloced_on_stack] */ +/* 1086 */ NdrFcShort( 0x2 ), /* Offset= 2 (1088) */ +/* 1088 */ + 0x12, 0x0, /* FC_UP */ +/* 1090 */ NdrFcShort( 0x2 ), /* Offset= 2 (1092) */ +/* 1092 */ + 0x2b, /* FC_NON_ENCAPSULATED_UNION */ + 0xd, /* FC_ENUM16 */ +/* 1094 */ 0x26, /* 38 */ + 0x0, /* */ +#ifndef _ALPHA_ +/* 1096 */ NdrFcShort( 0x4 ), /* x86, MIPS, PPC Stack size/offset = 4 */ +#else + NdrFcShort( 0x8 ), /* Alpha Stack size/offset = 8 */ +#endif +/* 1098 */ NdrFcShort( 0x2 ), /* Offset= 2 (1100) */ +/* 1100 */ NdrFcShort( 0x2c8 ), /* 712 */ +/* 1102 */ NdrFcShort( 0x7017 ), /* 28695 */ +/* 1104 */ NdrFcLong( 0x1 ), /* 1 */ +/* 1108 */ NdrFcShort( 0xa4 ), /* Offset= 164 (1272) */ +/* 1110 */ NdrFcLong( 0x2 ), /* 2 */ +/* 1114 */ NdrFcShort( 0xe0 ), /* Offset= 224 (1338) */ +/* 1116 */ NdrFcLong( 0x3 ), /* 3 */ +/* 1120 */ NdrFcShort( 0x146 ), /* Offset= 326 (1446) */ +/* 1122 */ NdrFcLong( 0x4 ), /* 4 */ +/* 1126 */ NdrFcShort( 0x1e0 ), /* Offset= 480 (1606) */ +/* 1128 */ NdrFcLong( 0x5 ), /* 5 */ +/* 1132 */ NdrFcShort( 0x20c ), /* Offset= 524 (1656) */ +/* 1134 */ NdrFcLong( 0x6 ), /* 6 */ +/* 1138 */ NdrFcShort( 0x2a2 ), /* Offset= 674 (1812) */ +/* 1140 */ NdrFcLong( 0x7 ), /* 7 */ +/* 1144 */ NdrFcShort( 0xfffffbdc ), /* Offset= -1060 (84) */ +/* 1146 */ NdrFcLong( 0x8 ), /* 8 */ +/* 1150 */ NdrFcShort( 0xfffffbd6 ), /* Offset= -1066 (84) */ +/* 1152 */ NdrFcLong( 0x9 ), /* 9 */ +/* 1156 */ NdrFcShort( 0xffffff12 ), /* Offset= -238 (918) */ +/* 1158 */ NdrFcLong( 0xa ), /* 10 */ +/* 1162 */ NdrFcShort( 0x28a ), /* Offset= 650 (1812) */ +/* 1164 */ NdrFcLong( 0xb ), /* 11 */ +/* 1168 */ NdrFcShort( 0xfffffbc4 ), /* Offset= -1084 (84) */ +/* 1170 */ NdrFcLong( 0xc ), /* 12 */ +/* 1174 */ NdrFcShort( 0xfffffbbe ), /* Offset= -1090 (84) */ +/* 1176 */ NdrFcLong( 0xd ), /* 13 */ +/* 1180 */ NdrFcShort( 0xfffffbb8 ), /* Offset= -1096 (84) */ +/* 1182 */ NdrFcLong( 0xe ), /* 14 */ +/* 1186 */ NdrFcShort( 0xfffffbb2 ), /* Offset= -1102 (84) */ +/* 1188 */ NdrFcLong( 0x10 ), /* 16 */ +/* 1192 */ NdrFcShort( 0xfffffeee ), /* Offset= -274 (918) */ +/* 1194 */ NdrFcLong( 0x11 ), /* 17 */ +/* 1198 */ NdrFcShort( 0xfffffd46 ), /* Offset= -698 (500) */ +/* 1200 */ NdrFcLong( 0x12 ), /* 18 */ +/* 1204 */ NdrFcShort( 0x2a8 ), /* Offset= 680 (1884) */ +/* 1206 */ NdrFcLong( 0x13 ), /* 19 */ +/* 1210 */ NdrFcShort( 0x2b2 ), /* Offset= 690 (1900) */ +/* 1212 */ NdrFcLong( 0x14 ), /* 20 */ +/* 1216 */ NdrFcShort( 0xfffffb94 ), /* Offset= -1132 (84) */ +/* 1218 */ NdrFcLong( 0x15 ), /* 21 */ +/* 1222 */ NdrFcShort( 0x35a ), /* Offset= 858 (2080) */ +/* 1224 */ NdrFcLong( 0x16 ), /* 22 */ +/* 1228 */ NdrFcShort( 0x450 ), /* Offset= 1104 (2332) */ +/* 1230 */ NdrFcLong( 0x17 ), /* 23 */ +/* 1234 */ NdrFcShort( 0x47a ), /* Offset= 1146 (2380) */ +/* 1236 */ NdrFcLong( 0x18 ), /* 24 */ +/* 1240 */ NdrFcShort( 0x574 ), /* Offset= 1396 (2636) */ +/* 1242 */ NdrFcShort( 0xffffffff ), /* Offset= -1 (1241) */ +/* 1244 */ + 0x1c, /* FC_CVARRAY */ + 0x1, /* 1 */ +/* 1246 */ NdrFcShort( 0x2 ), /* 2 */ +/* 1248 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 1250 */ NdrFcShort( 0x16 ), /* 22 */ +/* 1252 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 1254 */ NdrFcShort( 0x14 ), /* 20 */ +/* 1256 */ 0x5, /* FC_WCHAR */ + 0x5b, /* FC_END */ +/* 1258 */ + 0x1c, /* FC_CVARRAY */ + 0x1, /* 1 */ +/* 1260 */ NdrFcShort( 0x2 ), /* 2 */ +/* 1262 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 1264 */ NdrFcShort( 0x1e ), /* 30 */ +/* 1266 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 1268 */ NdrFcShort( 0x1c ), /* 28 */ +/* 1270 */ 0x5, /* FC_WCHAR */ + 0x5b, /* FC_END */ +/* 1272 */ + 0x16, /* FC_PSTRUCT */ + 0x3, /* 3 */ +/* 1274 */ NdrFcShort( 0x24 ), /* 36 */ +/* 1276 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 1278 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 1280 */ NdrFcShort( 0x4 ), /* 4 */ +/* 1282 */ NdrFcShort( 0x4 ), /* 4 */ +/* 1284 */ 0x12, 0x0, /* FC_UP */ +/* 1286 */ NdrFcShort( 0xfffffb40 ), /* Offset= -1216 (70) */ +/* 1288 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 1290 */ NdrFcShort( 0xc ), /* 12 */ +/* 1292 */ NdrFcShort( 0xc ), /* 12 */ +/* 1294 */ 0x12, 0x0, /* FC_UP */ +/* 1296 */ NdrFcShort( 0xfffffc78 ), /* Offset= -904 (392) */ +/* 1298 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 1300 */ NdrFcShort( 0x18 ), /* 24 */ +/* 1302 */ NdrFcShort( 0x18 ), /* 24 */ +/* 1304 */ 0x12, 0x0, /* FC_UP */ +/* 1306 */ NdrFcShort( 0xffffffc2 ), /* Offset= -62 (1244) */ +/* 1308 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 1310 */ NdrFcShort( 0x20 ), /* 32 */ +/* 1312 */ NdrFcShort( 0x20 ), /* 32 */ +/* 1314 */ 0x12, 0x0, /* FC_UP */ +/* 1316 */ NdrFcShort( 0xffffffc6 ), /* Offset= -58 (1258) */ +/* 1318 */ + 0x5b, /* FC_END */ + + 0x6, /* FC_SHORT */ +/* 1320 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 1322 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 1324 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 1326 */ 0x8, /* FC_LONG */ + 0x8, /* FC_LONG */ +/* 1328 */ 0x6, /* FC_SHORT */ + 0x6, /* FC_SHORT */ +/* 1330 */ 0x38, /* FC_ALIGNM4 */ + 0x8, /* FC_LONG */ +/* 1332 */ 0x6, /* FC_SHORT */ + 0x6, /* FC_SHORT */ +/* 1334 */ 0x38, /* FC_ALIGNM4 */ + 0x8, /* FC_LONG */ +/* 1336 */ 0x5c, /* FC_PAD */ + 0x5b, /* FC_END */ +/* 1338 */ + 0x16, /* FC_PSTRUCT */ + 0x3, /* 3 */ +/* 1340 */ NdrFcShort( 0x14 ), /* 20 */ +/* 1342 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 1344 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 1346 */ NdrFcShort( 0x4 ), /* 4 */ +/* 1348 */ NdrFcShort( 0x4 ), /* 4 */ +/* 1350 */ 0x12, 0x0, /* FC_UP */ +/* 1352 */ NdrFcShort( 0xfffffafe ), /* Offset= -1282 (70) */ +/* 1354 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 1356 */ NdrFcShort( 0xc ), /* 12 */ +/* 1358 */ NdrFcShort( 0xc ), /* 12 */ +/* 1360 */ 0x12, 0x0, /* FC_UP */ +/* 1362 */ NdrFcShort( 0xfffffc36 ), /* Offset= -970 (392) */ +/* 1364 */ + 0x5b, /* FC_END */ + + 0x6, /* FC_SHORT */ +/* 1366 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 1368 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 1370 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 1372 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 1374 */ 0x6, /* FC_SHORT */ + 0x5b, /* FC_END */ +/* 1376 */ + 0x1c, /* FC_CVARRAY */ + 0x1, /* 1 */ +/* 1378 */ NdrFcShort( 0x2 ), /* 2 */ +/* 1380 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 1382 */ NdrFcShort( 0x22 ), /* 34 */ +/* 1384 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 1386 */ NdrFcShort( 0x20 ), /* 32 */ +/* 1388 */ 0x5, /* FC_WCHAR */ + 0x5b, /* FC_END */ +/* 1390 */ + 0x1c, /* FC_CVARRAY */ + 0x1, /* 1 */ +/* 1392 */ NdrFcShort( 0x2 ), /* 2 */ +/* 1394 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 1396 */ NdrFcShort( 0x2a ), /* 42 */ +/* 1398 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 1400 */ NdrFcShort( 0x28 ), /* 40 */ +/* 1402 */ 0x5, /* FC_WCHAR */ + 0x5b, /* FC_END */ +/* 1404 */ + 0x1c, /* FC_CVARRAY */ + 0x1, /* 1 */ +/* 1406 */ NdrFcShort( 0x2 ), /* 2 */ +/* 1408 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 1410 */ NdrFcShort( 0x32 ), /* 50 */ +/* 1412 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 1414 */ NdrFcShort( 0x30 ), /* 48 */ +/* 1416 */ 0x5, /* FC_WCHAR */ + 0x5b, /* FC_END */ +/* 1418 */ + 0x1c, /* FC_CVARRAY */ + 0x1, /* 1 */ +/* 1420 */ NdrFcShort( 0x2 ), /* 2 */ +/* 1422 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 1424 */ NdrFcShort( 0x3a ), /* 58 */ +/* 1426 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 1428 */ NdrFcShort( 0x38 ), /* 56 */ +/* 1430 */ 0x5, /* FC_WCHAR */ + 0x5b, /* FC_END */ +/* 1432 */ + 0x1c, /* FC_CVARRAY */ + 0x0, /* 0 */ +/* 1434 */ NdrFcShort( 0x1 ), /* 1 */ +/* 1436 */ 0x40, /* 64 */ + 0x0, /* 0 */ +/* 1438 */ NdrFcShort( 0x4ec ), /* 1260 */ +/* 1440 */ 0x10, /* 16 */ + 0x59, /* FC_CALLBACK */ +/* 1442 */ NdrFcShort( 0x0 ), /* 0 */ +/* 1444 */ 0x2, /* FC_CHAR */ + 0x5b, /* FC_END */ +/* 1446 */ + 0x16, /* FC_PSTRUCT */ + 0x3, /* 3 */ +/* 1448 */ NdrFcShort( 0x78 ), /* 120 */ +/* 1450 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 1452 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 1454 */ NdrFcShort( 0x4 ), /* 4 */ +/* 1456 */ NdrFcShort( 0x4 ), /* 4 */ +/* 1458 */ 0x12, 0x0, /* FC_UP */ +/* 1460 */ NdrFcShort( 0xfffffa92 ), /* Offset= -1390 (70) */ +/* 1462 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 1464 */ NdrFcShort( 0xc ), /* 12 */ +/* 1466 */ NdrFcShort( 0xc ), /* 12 */ +/* 1468 */ 0x12, 0x0, /* FC_UP */ +/* 1470 */ NdrFcShort( 0xfffffbca ), /* Offset= -1078 (392) */ +/* 1472 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 1474 */ NdrFcShort( 0x1c ), /* 28 */ +/* 1476 */ NdrFcShort( 0x1c ), /* 28 */ +/* 1478 */ 0x12, 0x0, /* FC_UP */ +/* 1480 */ NdrFcShort( 0xfffffbdc ), /* Offset= -1060 (420) */ +/* 1482 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 1484 */ NdrFcShort( 0x24 ), /* 36 */ +/* 1486 */ NdrFcShort( 0x24 ), /* 36 */ +/* 1488 */ 0x12, 0x0, /* FC_UP */ +/* 1490 */ NdrFcShort( 0xffffff8e ), /* Offset= -114 (1376) */ +/* 1492 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 1494 */ NdrFcShort( 0x2c ), /* 44 */ +/* 1496 */ NdrFcShort( 0x2c ), /* 44 */ +/* 1498 */ 0x12, 0x0, /* FC_UP */ +/* 1500 */ NdrFcShort( 0xffffff92 ), /* Offset= -110 (1390) */ +/* 1502 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 1504 */ NdrFcShort( 0x34 ), /* 52 */ +/* 1506 */ NdrFcShort( 0x34 ), /* 52 */ +/* 1508 */ 0x12, 0x0, /* FC_UP */ +/* 1510 */ NdrFcShort( 0xffffff96 ), /* Offset= -106 (1404) */ +/* 1512 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 1514 */ NdrFcShort( 0x3c ), /* 60 */ +/* 1516 */ NdrFcShort( 0x3c ), /* 60 */ +/* 1518 */ 0x12, 0x0, /* FC_UP */ +/* 1520 */ NdrFcShort( 0xffffff9a ), /* Offset= -102 (1418) */ +/* 1522 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 1524 */ NdrFcShort( 0x6c ), /* 108 */ +/* 1526 */ NdrFcShort( 0x6c ), /* 108 */ +/* 1528 */ 0x12, 0x0, /* FC_UP */ +/* 1530 */ NdrFcShort( 0xffffff9e ), /* Offset= -98 (1432) */ +/* 1532 */ + 0x5b, /* FC_END */ + + 0x6, /* FC_SHORT */ +/* 1534 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 1536 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 1538 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 1540 */ 0x8, /* FC_LONG */ + 0x8, /* FC_LONG */ +/* 1542 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 1544 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 1546 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 1548 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 1550 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 1552 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 1554 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 1556 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 1558 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 1560 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 1562 */ 0x8, /* FC_LONG */ + 0x4c, /* FC_EMBEDDED_COMPLEX */ +/* 1564 */ 0x0, /* 0 */ + NdrFcShort( 0xfffffb51 ), /* Offset= -1199 (366) */ + 0x4c, /* FC_EMBEDDED_COMPLEX */ +/* 1568 */ 0x0, /* 0 */ + NdrFcShort( 0xfffffb4d ), /* Offset= -1203 (366) */ + 0x4c, /* FC_EMBEDDED_COMPLEX */ +/* 1572 */ 0x0, /* 0 */ + NdrFcShort( 0xfffffb49 ), /* Offset= -1207 (366) */ + 0x4c, /* FC_EMBEDDED_COMPLEX */ +/* 1576 */ 0x0, /* 0 */ + NdrFcShort( 0xfffffb45 ), /* Offset= -1211 (366) */ + 0x4c, /* FC_EMBEDDED_COMPLEX */ +/* 1580 */ 0x0, /* 0 */ + NdrFcShort( 0xfffffb41 ), /* Offset= -1215 (366) */ + 0x6, /* FC_SHORT */ +/* 1584 */ 0x38, /* FC_ALIGNM4 */ + 0x8, /* FC_LONG */ +/* 1586 */ 0x6, /* FC_SHORT */ + 0x6, /* FC_SHORT */ +/* 1588 */ 0x38, /* FC_ALIGNM4 */ + 0x8, /* FC_LONG */ +/* 1590 */ 0x5c, /* FC_PAD */ + 0x5b, /* FC_END */ +/* 1592 */ + 0x1c, /* FC_CVARRAY */ + 0x0, /* 0 */ +/* 1594 */ NdrFcShort( 0x1 ), /* 1 */ +/* 1596 */ 0x40, /* 64 */ + 0x0, /* 0 */ +/* 1598 */ NdrFcShort( 0x4ec ), /* 1260 */ +/* 1600 */ 0x10, /* 16 */ + 0x59, /* FC_CALLBACK */ +/* 1602 */ NdrFcShort( 0x1 ), /* 1 */ +/* 1604 */ 0x2, /* FC_CHAR */ + 0x5b, /* FC_END */ +/* 1606 */ + 0x16, /* FC_PSTRUCT */ + 0x3, /* 3 */ +/* 1608 */ NdrFcShort( 0x8 ), /* 8 */ +/* 1610 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 1612 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 1614 */ NdrFcShort( 0x4 ), /* 4 */ +/* 1616 */ NdrFcShort( 0x4 ), /* 4 */ +/* 1618 */ 0x12, 0x0, /* FC_UP */ +/* 1620 */ NdrFcShort( 0xffffffe4 ), /* Offset= -28 (1592) */ +/* 1622 */ + 0x5b, /* FC_END */ + + 0x6, /* FC_SHORT */ +/* 1624 */ 0x38, /* FC_ALIGNM4 */ + 0x8, /* FC_LONG */ +/* 1626 */ 0x5c, /* FC_PAD */ + 0x5b, /* FC_END */ +/* 1628 */ + 0x1c, /* FC_CVARRAY */ + 0x1, /* 1 */ +/* 1630 */ NdrFcShort( 0x2 ), /* 2 */ +/* 1632 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 1634 */ NdrFcShort( 0x42 ), /* 66 */ +/* 1636 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 1638 */ NdrFcShort( 0x40 ), /* 64 */ +/* 1640 */ 0x5, /* FC_WCHAR */ + 0x5b, /* FC_END */ +/* 1642 */ + 0x1c, /* FC_CVARRAY */ + 0x0, /* 0 */ +/* 1644 */ NdrFcShort( 0x1 ), /* 1 */ +/* 1646 */ 0x40, /* 64 */ + 0x0, /* 0 */ +/* 1648 */ NdrFcShort( 0x4ec ), /* 1260 */ +/* 1650 */ 0x10, /* 16 */ + 0x59, /* FC_CALLBACK */ +/* 1652 */ NdrFcShort( 0x2 ), /* 2 */ +/* 1654 */ 0x2, /* FC_CHAR */ + 0x5b, /* FC_END */ +/* 1656 */ + 0x16, /* FC_PSTRUCT */ + 0x3, /* 3 */ +/* 1658 */ NdrFcShort( 0x78 ), /* 120 */ +/* 1660 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 1662 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 1664 */ NdrFcShort( 0x4 ), /* 4 */ +/* 1666 */ NdrFcShort( 0x4 ), /* 4 */ +/* 1668 */ 0x12, 0x0, /* FC_UP */ +/* 1670 */ NdrFcShort( 0xfffff9c0 ), /* Offset= -1600 (70) */ +/* 1672 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 1674 */ NdrFcShort( 0xc ), /* 12 */ +/* 1676 */ NdrFcShort( 0xc ), /* 12 */ +/* 1678 */ 0x12, 0x0, /* FC_UP */ +/* 1680 */ NdrFcShort( 0xfffffaf8 ), /* Offset= -1288 (392) */ +/* 1682 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 1684 */ NdrFcShort( 0x1c ), /* 28 */ +/* 1686 */ NdrFcShort( 0x1c ), /* 28 */ +/* 1688 */ 0x12, 0x0, /* FC_UP */ +/* 1690 */ NdrFcShort( 0xfffffb0a ), /* Offset= -1270 (420) */ +/* 1692 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 1694 */ NdrFcShort( 0x24 ), /* 36 */ +/* 1696 */ NdrFcShort( 0x24 ), /* 36 */ +/* 1698 */ 0x12, 0x0, /* FC_UP */ +/* 1700 */ NdrFcShort( 0xfffffebc ), /* Offset= -324 (1376) */ +/* 1702 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 1704 */ NdrFcShort( 0x2c ), /* 44 */ +/* 1706 */ NdrFcShort( 0x2c ), /* 44 */ +/* 1708 */ 0x12, 0x0, /* FC_UP */ +/* 1710 */ NdrFcShort( 0xfffffec0 ), /* Offset= -320 (1390) */ +/* 1712 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 1714 */ NdrFcShort( 0x34 ), /* 52 */ +/* 1716 */ NdrFcShort( 0x34 ), /* 52 */ +/* 1718 */ 0x12, 0x0, /* FC_UP */ +/* 1720 */ NdrFcShort( 0xfffffec4 ), /* Offset= -316 (1404) */ +/* 1722 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 1724 */ NdrFcShort( 0x3c ), /* 60 */ +/* 1726 */ NdrFcShort( 0x3c ), /* 60 */ +/* 1728 */ 0x12, 0x0, /* FC_UP */ +/* 1730 */ NdrFcShort( 0xfffffec8 ), /* Offset= -312 (1418) */ +/* 1732 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 1734 */ NdrFcShort( 0x44 ), /* 68 */ +/* 1736 */ NdrFcShort( 0x44 ), /* 68 */ +/* 1738 */ 0x12, 0x0, /* FC_UP */ +/* 1740 */ NdrFcShort( 0xffffff90 ), /* Offset= -112 (1628) */ +/* 1742 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 1744 */ NdrFcShort( 0x5c ), /* 92 */ +/* 1746 */ NdrFcShort( 0x5c ), /* 92 */ +/* 1748 */ 0x12, 0x0, /* FC_UP */ +/* 1750 */ NdrFcShort( 0xffffff94 ), /* Offset= -108 (1642) */ +/* 1752 */ + 0x5b, /* FC_END */ + + 0x6, /* FC_SHORT */ +/* 1754 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 1756 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 1758 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 1760 */ 0x8, /* FC_LONG */ + 0x8, /* FC_LONG */ +/* 1762 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 1764 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 1766 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 1768 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 1770 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 1772 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 1774 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 1776 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 1778 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 1780 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 1782 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 1784 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 1786 */ 0x8, /* FC_LONG */ + 0x4c, /* FC_EMBEDDED_COMPLEX */ +/* 1788 */ 0x0, /* 0 */ + NdrFcShort( 0xfffffa71 ), /* Offset= -1423 (366) */ + 0x4c, /* FC_EMBEDDED_COMPLEX */ +/* 1792 */ 0x0, /* 0 */ + NdrFcShort( 0xfffffa6d ), /* Offset= -1427 (366) */ + 0x6, /* FC_SHORT */ +/* 1796 */ 0x38, /* FC_ALIGNM4 */ + 0x8, /* FC_LONG */ +/* 1798 */ 0x6, /* FC_SHORT */ + 0x6, /* FC_SHORT */ +/* 1800 */ 0x4c, /* FC_EMBEDDED_COMPLEX */ + 0x0, /* 0 */ +/* 1802 */ NdrFcShort( 0xfffffa64 ), /* Offset= -1436 (366) */ +/* 1804 */ 0x4c, /* FC_EMBEDDED_COMPLEX */ + 0x0, /* 0 */ +/* 1806 */ NdrFcShort( 0xfffffa60 ), /* Offset= -1440 (366) */ +/* 1808 */ 0x38, /* FC_ALIGNM4 */ + 0x8, /* FC_LONG */ +/* 1810 */ 0x5c, /* FC_PAD */ + 0x5b, /* FC_END */ +/* 1812 */ + 0x16, /* FC_PSTRUCT */ + 0x3, /* 3 */ +/* 1814 */ NdrFcShort( 0x10 ), /* 16 */ +/* 1816 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 1818 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 1820 */ NdrFcShort( 0x4 ), /* 4 */ +/* 1822 */ NdrFcShort( 0x4 ), /* 4 */ +/* 1824 */ 0x12, 0x0, /* FC_UP */ +/* 1826 */ NdrFcShort( 0xfffff924 ), /* Offset= -1756 (70) */ +/* 1828 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 1830 */ NdrFcShort( 0xc ), /* 12 */ +/* 1832 */ NdrFcShort( 0xc ), /* 12 */ +/* 1834 */ 0x12, 0x0, /* FC_UP */ +/* 1836 */ NdrFcShort( 0xfffffa5c ), /* Offset= -1444 (392) */ +/* 1838 */ + 0x5b, /* FC_END */ + + 0x6, /* FC_SHORT */ +/* 1840 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 1842 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 1844 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 1846 */ 0x8, /* FC_LONG */ + 0x5b, /* FC_END */ +/* 1848 */ + 0x1d, /* FC_SMFARRAY */ + 0x0, /* 0 */ +/* 1850 */ NdrFcShort( 0x8 ), /* 8 */ +/* 1852 */ 0x2, /* FC_CHAR */ + 0x5b, /* FC_END */ +/* 1854 */ + 0x15, /* FC_STRUCT */ + 0x0, /* 0 */ +/* 1856 */ NdrFcShort( 0x8 ), /* 8 */ +/* 1858 */ 0x4c, /* FC_EMBEDDED_COMPLEX */ + 0x0, /* 0 */ +/* 1860 */ NdrFcShort( 0xfffffff4 ), /* Offset= -12 (1848) */ +/* 1862 */ 0x5c, /* FC_PAD */ + 0x5b, /* FC_END */ +/* 1864 */ + 0x1d, /* FC_SMFARRAY */ + 0x0, /* 0 */ +/* 1866 */ NdrFcShort( 0x10 ), /* 16 */ +/* 1868 */ 0x4c, /* FC_EMBEDDED_COMPLEX */ + 0x0, /* 0 */ +/* 1870 */ NdrFcShort( 0xfffffff0 ), /* Offset= -16 (1854) */ +/* 1872 */ 0x5c, /* FC_PAD */ + 0x5b, /* FC_END */ +/* 1874 */ + 0x15, /* FC_STRUCT */ + 0x0, /* 0 */ +/* 1876 */ NdrFcShort( 0x10 ), /* 16 */ +/* 1878 */ 0x4c, /* FC_EMBEDDED_COMPLEX */ + 0x0, /* 0 */ +/* 1880 */ NdrFcShort( 0xfffffff0 ), /* Offset= -16 (1864) */ +/* 1882 */ 0x5c, /* FC_PAD */ + 0x5b, /* FC_END */ +/* 1884 */ + 0x15, /* FC_STRUCT */ + 0x0, /* 0 */ +/* 1886 */ NdrFcShort( 0x23 ), /* 35 */ +/* 1888 */ 0x4c, /* FC_EMBEDDED_COMPLEX */ + 0x0, /* 0 */ +/* 1890 */ NdrFcShort( 0xfffffff0 ), /* Offset= -16 (1874) */ +/* 1892 */ 0x4c, /* FC_EMBEDDED_COMPLEX */ + 0x0, /* 0 */ +/* 1894 */ NdrFcShort( 0xffffffec ), /* Offset= -20 (1874) */ +/* 1896 */ 0x2, /* FC_CHAR */ + 0x2, /* FC_CHAR */ +/* 1898 */ 0x2, /* FC_CHAR */ + 0x5b, /* FC_END */ +/* 1900 */ + 0x15, /* FC_STRUCT */ + 0x3, /* 3 */ +/* 1902 */ NdrFcShort( 0x18 ), /* 24 */ +/* 1904 */ 0x8, /* FC_LONG */ + 0x4c, /* FC_EMBEDDED_COMPLEX */ +/* 1906 */ 0x0, /* 0 */ + NdrFcShort( 0xfffff9fb ), /* Offset= -1541 (366) */ + 0x4c, /* FC_EMBEDDED_COMPLEX */ +/* 1910 */ 0x0, /* 0 */ + NdrFcShort( 0xfffff9f7 ), /* Offset= -1545 (366) */ + 0x6, /* FC_SHORT */ +/* 1914 */ 0x6, /* FC_SHORT */ + 0x5b, /* FC_END */ +/* 1916 */ + 0x1c, /* FC_CVARRAY */ + 0x1, /* 1 */ +/* 1918 */ NdrFcShort( 0x2 ), /* 2 */ +/* 1920 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 1922 */ NdrFcShort( 0x4a ), /* 74 */ +/* 1924 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 1926 */ NdrFcShort( 0x48 ), /* 72 */ +/* 1928 */ 0x5, /* FC_WCHAR */ + 0x5b, /* FC_END */ +/* 1930 */ + 0x1c, /* FC_CVARRAY */ + 0x1, /* 1 */ +/* 1932 */ NdrFcShort( 0x2 ), /* 2 */ +/* 1934 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 1936 */ NdrFcShort( 0x52 ), /* 82 */ +/* 1938 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 1940 */ NdrFcShort( 0x50 ), /* 80 */ +/* 1942 */ 0x5, /* FC_WCHAR */ + 0x5b, /* FC_END */ +/* 1944 */ + 0x1c, /* FC_CVARRAY */ + 0x1, /* 1 */ +/* 1946 */ NdrFcShort( 0x2 ), /* 2 */ +/* 1948 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 1950 */ NdrFcShort( 0x5a ), /* 90 */ +/* 1952 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 1954 */ NdrFcShort( 0x58 ), /* 88 */ +/* 1956 */ 0x5, /* FC_WCHAR */ + 0x5b, /* FC_END */ +/* 1958 */ + 0x1c, /* FC_CVARRAY */ + 0x1, /* 1 */ +/* 1960 */ NdrFcShort( 0x2 ), /* 2 */ +/* 1962 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 1964 */ NdrFcShort( 0x62 ), /* 98 */ +/* 1966 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 1968 */ NdrFcShort( 0x60 ), /* 96 */ +/* 1970 */ 0x5, /* FC_WCHAR */ + 0x5b, /* FC_END */ +/* 1972 */ + 0x1c, /* FC_CVARRAY */ + 0x1, /* 1 */ +/* 1974 */ NdrFcShort( 0x2 ), /* 2 */ +/* 1976 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 1978 */ NdrFcShort( 0x6a ), /* 106 */ +/* 1980 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 1982 */ NdrFcShort( 0x68 ), /* 104 */ +/* 1984 */ 0x5, /* FC_WCHAR */ + 0x5b, /* FC_END */ +/* 1986 */ + 0x1c, /* FC_CVARRAY */ + 0x1, /* 1 */ +/* 1988 */ NdrFcShort( 0x2 ), /* 2 */ +/* 1990 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 1992 */ NdrFcShort( 0x72 ), /* 114 */ +/* 1994 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 1996 */ NdrFcShort( 0x70 ), /* 112 */ +/* 1998 */ 0x5, /* FC_WCHAR */ + 0x5b, /* FC_END */ +/* 2000 */ + 0x1c, /* FC_CVARRAY */ + 0x1, /* 1 */ +/* 2002 */ NdrFcShort( 0x2 ), /* 2 */ +/* 2004 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 2006 */ NdrFcShort( 0x7a ), /* 122 */ +/* 2008 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 2010 */ NdrFcShort( 0x78 ), /* 120 */ +/* 2012 */ 0x5, /* FC_WCHAR */ + 0x5b, /* FC_END */ +/* 2014 */ + 0x1c, /* FC_CVARRAY */ + 0x1, /* 1 */ +/* 2016 */ NdrFcShort( 0x2 ), /* 2 */ +/* 2018 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 2020 */ NdrFcShort( 0x82 ), /* 130 */ +/* 2022 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 2024 */ NdrFcShort( 0x80 ), /* 128 */ +/* 2026 */ 0x5, /* FC_WCHAR */ + 0x5b, /* FC_END */ +/* 2028 */ + 0x1c, /* FC_CVARRAY */ + 0x1, /* 1 */ +/* 2030 */ NdrFcShort( 0x2 ), /* 2 */ +/* 2032 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 2034 */ NdrFcShort( 0x8a ), /* 138 */ +/* 2036 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 2038 */ NdrFcShort( 0x88 ), /* 136 */ +/* 2040 */ 0x5, /* FC_WCHAR */ + 0x5b, /* FC_END */ +/* 2042 */ + 0x1c, /* FC_CVARRAY */ + 0x1, /* 1 */ +/* 2044 */ NdrFcShort( 0x2 ), /* 2 */ +/* 2046 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 2048 */ NdrFcShort( 0x92 ), /* 146 */ +/* 2050 */ 0x16, /* 22 */ + 0x55, /* FC_DIV_2 */ +/* 2052 */ NdrFcShort( 0x90 ), /* 144 */ +/* 2054 */ 0x5, /* FC_WCHAR */ + 0x5b, /* FC_END */ +/* 2056 */ + 0x1b, /* FC_CARRAY */ + 0x0, /* 0 */ +/* 2058 */ NdrFcShort( 0x1 ), /* 1 */ +/* 2060 */ 0x18, /* 24 */ + 0x0, /* */ +/* 2062 */ NdrFcShort( 0x98 ), /* 152 */ +/* 2064 */ 0x2, /* FC_CHAR */ + 0x5b, /* FC_END */ +/* 2066 */ + 0x1c, /* FC_CVARRAY */ + 0x0, /* 0 */ +/* 2068 */ NdrFcShort( 0x1 ), /* 1 */ +/* 2070 */ 0x40, /* 64 */ + 0x0, /* 0 */ +/* 2072 */ NdrFcShort( 0x4ec ), /* 1260 */ +/* 2074 */ 0x10, /* 16 */ + 0x59, /* FC_CALLBACK */ +/* 2076 */ NdrFcShort( 0x3 ), /* 3 */ +/* 2078 */ 0x2, /* FC_CHAR */ + 0x5b, /* FC_END */ +/* 2080 */ + 0x16, /* FC_PSTRUCT */ + 0x3, /* 3 */ +/* 2082 */ NdrFcShort( 0xc4 ), /* 196 */ +/* 2084 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 2086 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2088 */ NdrFcShort( 0x34 ), /* 52 */ +/* 2090 */ NdrFcShort( 0x34 ), /* 52 */ +/* 2092 */ 0x12, 0x0, /* FC_UP */ +/* 2094 */ NdrFcShort( 0xfffffd4e ), /* Offset= -690 (1404) */ +/* 2096 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2098 */ NdrFcShort( 0x3c ), /* 60 */ +/* 2100 */ NdrFcShort( 0x3c ), /* 60 */ +/* 2102 */ 0x12, 0x0, /* FC_UP */ +/* 2104 */ NdrFcShort( 0xfffffd52 ), /* Offset= -686 (1418) */ +/* 2106 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2108 */ NdrFcShort( 0x44 ), /* 68 */ +/* 2110 */ NdrFcShort( 0x44 ), /* 68 */ +/* 2112 */ 0x12, 0x0, /* FC_UP */ +/* 2114 */ NdrFcShort( 0xfffffe1a ), /* Offset= -486 (1628) */ +/* 2116 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2118 */ NdrFcShort( 0x4c ), /* 76 */ +/* 2120 */ NdrFcShort( 0x4c ), /* 76 */ +/* 2122 */ 0x12, 0x0, /* FC_UP */ +/* 2124 */ NdrFcShort( 0xffffff30 ), /* Offset= -208 (1916) */ +/* 2126 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2128 */ NdrFcShort( 0x54 ), /* 84 */ +/* 2130 */ NdrFcShort( 0x54 ), /* 84 */ +/* 2132 */ 0x12, 0x0, /* FC_UP */ +/* 2134 */ NdrFcShort( 0xffffff34 ), /* Offset= -204 (1930) */ +/* 2136 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2138 */ NdrFcShort( 0x5c ), /* 92 */ +/* 2140 */ NdrFcShort( 0x5c ), /* 92 */ +/* 2142 */ 0x12, 0x0, /* FC_UP */ +/* 2144 */ NdrFcShort( 0xffffff38 ), /* Offset= -200 (1944) */ +/* 2146 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2148 */ NdrFcShort( 0x64 ), /* 100 */ +/* 2150 */ NdrFcShort( 0x64 ), /* 100 */ +/* 2152 */ 0x12, 0x0, /* FC_UP */ +/* 2154 */ NdrFcShort( 0xffffff3c ), /* Offset= -196 (1958) */ +/* 2156 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2158 */ NdrFcShort( 0x6c ), /* 108 */ +/* 2160 */ NdrFcShort( 0x6c ), /* 108 */ +/* 2162 */ 0x12, 0x0, /* FC_UP */ +/* 2164 */ NdrFcShort( 0xffffff40 ), /* Offset= -192 (1972) */ +/* 2166 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2168 */ NdrFcShort( 0x74 ), /* 116 */ +/* 2170 */ NdrFcShort( 0x74 ), /* 116 */ +/* 2172 */ 0x12, 0x0, /* FC_UP */ +/* 2174 */ NdrFcShort( 0xffffff44 ), /* Offset= -188 (1986) */ +/* 2176 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2178 */ NdrFcShort( 0x7c ), /* 124 */ +/* 2180 */ NdrFcShort( 0x7c ), /* 124 */ +/* 2182 */ 0x12, 0x0, /* FC_UP */ +/* 2184 */ NdrFcShort( 0xffffff48 ), /* Offset= -184 (2000) */ +/* 2186 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2188 */ NdrFcShort( 0x84 ), /* 132 */ +/* 2190 */ NdrFcShort( 0x84 ), /* 132 */ +/* 2192 */ 0x12, 0x0, /* FC_UP */ +/* 2194 */ NdrFcShort( 0xffffff4c ), /* Offset= -180 (2014) */ +/* 2196 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2198 */ NdrFcShort( 0x8c ), /* 140 */ +/* 2200 */ NdrFcShort( 0x8c ), /* 140 */ +/* 2202 */ 0x12, 0x0, /* FC_UP */ +/* 2204 */ NdrFcShort( 0xffffff50 ), /* Offset= -176 (2028) */ +/* 2206 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2208 */ NdrFcShort( 0x94 ), /* 148 */ +/* 2210 */ NdrFcShort( 0x94 ), /* 148 */ +/* 2212 */ 0x12, 0x0, /* FC_UP */ +/* 2214 */ NdrFcShort( 0xffffff54 ), /* Offset= -172 (2042) */ +/* 2216 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2218 */ NdrFcShort( 0x9c ), /* 156 */ +/* 2220 */ NdrFcShort( 0x9c ), /* 156 */ +/* 2222 */ 0x12, 0x0, /* FC_UP */ +/* 2224 */ NdrFcShort( 0xffffff58 ), /* Offset= -168 (2056) */ +/* 2226 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2228 */ NdrFcShort( 0xb4 ), /* 180 */ +/* 2230 */ NdrFcShort( 0xb4 ), /* 180 */ +/* 2232 */ 0x12, 0x0, /* FC_UP */ +/* 2234 */ NdrFcShort( 0xffffff58 ), /* Offset= -168 (2066) */ +/* 2236 */ + 0x5b, /* FC_END */ + + 0x4c, /* FC_EMBEDDED_COMPLEX */ +/* 2238 */ 0x0, /* 0 */ + NdrFcShort( 0xfffff8af ), /* Offset= -1873 (366) */ + 0x4c, /* FC_EMBEDDED_COMPLEX */ +/* 2242 */ 0x0, /* 0 */ + NdrFcShort( 0xfffff8ab ), /* Offset= -1877 (366) */ + 0x4c, /* FC_EMBEDDED_COMPLEX */ +/* 2246 */ 0x0, /* 0 */ + NdrFcShort( 0xfffff8a7 ), /* Offset= -1881 (366) */ + 0x4c, /* FC_EMBEDDED_COMPLEX */ +/* 2250 */ 0x0, /* 0 */ + NdrFcShort( 0xfffff8a3 ), /* Offset= -1885 (366) */ + 0x4c, /* FC_EMBEDDED_COMPLEX */ +/* 2254 */ 0x0, /* 0 */ + NdrFcShort( 0xfffff89f ), /* Offset= -1889 (366) */ + 0x4c, /* FC_EMBEDDED_COMPLEX */ +/* 2258 */ 0x0, /* 0 */ + NdrFcShort( 0xfffff89b ), /* Offset= -1893 (366) */ + 0x6, /* FC_SHORT */ +/* 2262 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 2264 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 2266 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 2268 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 2270 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 2272 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 2274 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 2276 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 2278 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 2280 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 2282 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 2284 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 2286 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 2288 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 2290 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 2292 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 2294 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 2296 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 2298 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 2300 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 2302 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 2304 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 2306 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 2308 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 2310 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 2312 */ 0x8, /* FC_LONG */ + 0x8, /* FC_LONG */ +/* 2314 */ 0x8, /* FC_LONG */ + 0x8, /* FC_LONG */ +/* 2316 */ 0x8, /* FC_LONG */ + 0x8, /* FC_LONG */ +/* 2318 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 2320 */ 0x38, /* FC_ALIGNM4 */ + 0x8, /* FC_LONG */ +/* 2322 */ 0x6, /* FC_SHORT */ + 0x6, /* FC_SHORT */ +/* 2324 */ 0x6, /* FC_SHORT */ + 0x6, /* FC_SHORT */ +/* 2326 */ 0x2, /* FC_CHAR */ + 0x2, /* FC_CHAR */ +/* 2328 */ 0x2, /* FC_CHAR */ + 0x2, /* FC_CHAR */ +/* 2330 */ 0x5c, /* FC_PAD */ + 0x5b, /* FC_END */ +/* 2332 */ + 0x1a, /* FC_BOGUS_STRUCT */ + 0x7, /* 7 */ +/* 2334 */ NdrFcShort( 0xcc ), /* 204 */ +/* 2336 */ NdrFcShort( 0x0 ), /* 0 */ +/* 2338 */ NdrFcShort( 0x0 ), /* Offset= 0 (2338) */ +/* 2340 */ 0x4c, /* FC_EMBEDDED_COMPLEX */ + 0x0, /* 0 */ +/* 2342 */ NdrFcShort( 0xfffffefa ), /* Offset= -262 (2080) */ +/* 2344 */ 0x4c, /* FC_EMBEDDED_COMPLEX */ + 0x0, /* 0 */ +/* 2346 */ NdrFcShort( 0xfffff8ec ), /* Offset= -1812 (534) */ +/* 2348 */ 0x5c, /* FC_PAD */ + 0x5b, /* FC_END */ +/* 2350 */ + 0x1d, /* FC_SMFARRAY */ + 0x0, /* 0 */ +/* 2352 */ NdrFcShort( 0x204 ), /* 516 */ +/* 2354 */ 0x2, /* FC_CHAR */ + 0x5b, /* FC_END */ +/* 2356 */ + 0x15, /* FC_STRUCT */ + 0x0, /* 0 */ +/* 2358 */ NdrFcShort( 0x204 ), /* 516 */ +/* 2360 */ 0x4c, /* FC_EMBEDDED_COMPLEX */ + 0x0, /* 0 */ +/* 2362 */ NdrFcShort( 0xfffffff4 ), /* Offset= -12 (2350) */ +/* 2364 */ 0x5c, /* FC_PAD */ + 0x5b, /* FC_END */ +/* 2366 */ + 0x1c, /* FC_CVARRAY */ + 0x0, /* 0 */ +/* 2368 */ NdrFcShort( 0x1 ), /* 1 */ +/* 2370 */ 0x40, /* 64 */ + 0x0, /* 0 */ +/* 2372 */ NdrFcShort( 0x4ec ), /* 1260 */ +/* 2374 */ 0x10, /* 16 */ + 0x59, /* FC_CALLBACK */ +/* 2376 */ NdrFcShort( 0x4 ), /* 4 */ +/* 2378 */ 0x2, /* FC_CHAR */ + 0x5b, /* FC_END */ +/* 2380 */ + 0x16, /* FC_PSTRUCT */ + 0x3, /* 3 */ +/* 2382 */ NdrFcShort( 0x2c8 ), /* 712 */ +/* 2384 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 2386 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2388 */ NdrFcShort( 0x34 ), /* 52 */ +/* 2390 */ NdrFcShort( 0x34 ), /* 52 */ +/* 2392 */ 0x12, 0x0, /* FC_UP */ +/* 2394 */ NdrFcShort( 0xfffffc22 ), /* Offset= -990 (1404) */ +/* 2396 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2398 */ NdrFcShort( 0x3c ), /* 60 */ +/* 2400 */ NdrFcShort( 0x3c ), /* 60 */ +/* 2402 */ 0x12, 0x0, /* FC_UP */ +/* 2404 */ NdrFcShort( 0xfffffc26 ), /* Offset= -986 (1418) */ +/* 2406 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2408 */ NdrFcShort( 0x44 ), /* 68 */ +/* 2410 */ NdrFcShort( 0x44 ), /* 68 */ +/* 2412 */ 0x12, 0x0, /* FC_UP */ +/* 2414 */ NdrFcShort( 0xfffffcee ), /* Offset= -786 (1628) */ +/* 2416 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2418 */ NdrFcShort( 0x4c ), /* 76 */ +/* 2420 */ NdrFcShort( 0x4c ), /* 76 */ +/* 2422 */ 0x12, 0x0, /* FC_UP */ +/* 2424 */ NdrFcShort( 0xfffffe04 ), /* Offset= -508 (1916) */ +/* 2426 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2428 */ NdrFcShort( 0x54 ), /* 84 */ +/* 2430 */ NdrFcShort( 0x54 ), /* 84 */ +/* 2432 */ 0x12, 0x0, /* FC_UP */ +/* 2434 */ NdrFcShort( 0xfffffe08 ), /* Offset= -504 (1930) */ +/* 2436 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2438 */ NdrFcShort( 0x5c ), /* 92 */ +/* 2440 */ NdrFcShort( 0x5c ), /* 92 */ +/* 2442 */ 0x12, 0x0, /* FC_UP */ +/* 2444 */ NdrFcShort( 0xfffffe0c ), /* Offset= -500 (1944) */ +/* 2446 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2448 */ NdrFcShort( 0x64 ), /* 100 */ +/* 2450 */ NdrFcShort( 0x64 ), /* 100 */ +/* 2452 */ 0x12, 0x0, /* FC_UP */ +/* 2454 */ NdrFcShort( 0xfffffe10 ), /* Offset= -496 (1958) */ +/* 2456 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2458 */ NdrFcShort( 0x6c ), /* 108 */ +/* 2460 */ NdrFcShort( 0x6c ), /* 108 */ +/* 2462 */ 0x12, 0x0, /* FC_UP */ +/* 2464 */ NdrFcShort( 0xfffffe14 ), /* Offset= -492 (1972) */ +/* 2466 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2468 */ NdrFcShort( 0x74 ), /* 116 */ +/* 2470 */ NdrFcShort( 0x74 ), /* 116 */ +/* 2472 */ 0x12, 0x0, /* FC_UP */ +/* 2474 */ NdrFcShort( 0xfffffe18 ), /* Offset= -488 (1986) */ +/* 2476 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2478 */ NdrFcShort( 0x7c ), /* 124 */ +/* 2480 */ NdrFcShort( 0x7c ), /* 124 */ +/* 2482 */ 0x12, 0x0, /* FC_UP */ +/* 2484 */ NdrFcShort( 0xfffffe1c ), /* Offset= -484 (2000) */ +/* 2486 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2488 */ NdrFcShort( 0x84 ), /* 132 */ +/* 2490 */ NdrFcShort( 0x84 ), /* 132 */ +/* 2492 */ 0x12, 0x0, /* FC_UP */ +/* 2494 */ NdrFcShort( 0xfffffe20 ), /* Offset= -480 (2014) */ +/* 2496 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2498 */ NdrFcShort( 0x8c ), /* 140 */ +/* 2500 */ NdrFcShort( 0x8c ), /* 140 */ +/* 2502 */ 0x12, 0x0, /* FC_UP */ +/* 2504 */ NdrFcShort( 0xfffffe24 ), /* Offset= -476 (2028) */ +/* 2506 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2508 */ NdrFcShort( 0x94 ), /* 148 */ +/* 2510 */ NdrFcShort( 0x94 ), /* 148 */ +/* 2512 */ 0x12, 0x0, /* FC_UP */ +/* 2514 */ NdrFcShort( 0xfffffe28 ), /* Offset= -472 (2042) */ +/* 2516 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2518 */ NdrFcShort( 0x9c ), /* 156 */ +/* 2520 */ NdrFcShort( 0x9c ), /* 156 */ +/* 2522 */ 0x12, 0x0, /* FC_UP */ +/* 2524 */ NdrFcShort( 0xfffffe2c ), /* Offset= -468 (2056) */ +/* 2526 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2528 */ NdrFcShort( 0xb4 ), /* 180 */ +/* 2530 */ NdrFcShort( 0xb4 ), /* 180 */ +/* 2532 */ 0x12, 0x0, /* FC_UP */ +/* 2534 */ NdrFcShort( 0xffffff58 ), /* Offset= -168 (2366) */ +/* 2536 */ + 0x5b, /* FC_END */ + + 0x4c, /* FC_EMBEDDED_COMPLEX */ +/* 2538 */ 0x0, /* 0 */ + NdrFcShort( 0xfffff783 ), /* Offset= -2173 (366) */ + 0x4c, /* FC_EMBEDDED_COMPLEX */ +/* 2542 */ 0x0, /* 0 */ + NdrFcShort( 0xfffff77f ), /* Offset= -2177 (366) */ + 0x4c, /* FC_EMBEDDED_COMPLEX */ +/* 2546 */ 0x0, /* 0 */ + NdrFcShort( 0xfffff77b ), /* Offset= -2181 (366) */ + 0x4c, /* FC_EMBEDDED_COMPLEX */ +/* 2550 */ 0x0, /* 0 */ + NdrFcShort( 0xfffff777 ), /* Offset= -2185 (366) */ + 0x4c, /* FC_EMBEDDED_COMPLEX */ +/* 2554 */ 0x0, /* 0 */ + NdrFcShort( 0xfffff773 ), /* Offset= -2189 (366) */ + 0x4c, /* FC_EMBEDDED_COMPLEX */ +/* 2558 */ 0x0, /* 0 */ + NdrFcShort( 0xfffff76f ), /* Offset= -2193 (366) */ + 0x6, /* FC_SHORT */ +/* 2562 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 2564 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 2566 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 2568 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 2570 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 2572 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 2574 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 2576 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 2578 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 2580 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 2582 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 2584 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 2586 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 2588 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 2590 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 2592 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 2594 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 2596 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 2598 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 2600 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 2602 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 2604 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 2606 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 2608 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 2610 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 2612 */ 0x8, /* FC_LONG */ + 0x8, /* FC_LONG */ +/* 2614 */ 0x8, /* FC_LONG */ + 0x8, /* FC_LONG */ +/* 2616 */ 0x8, /* FC_LONG */ + 0x8, /* FC_LONG */ +/* 2618 */ 0x8, /* FC_LONG */ + 0x6, /* FC_SHORT */ +/* 2620 */ 0x38, /* FC_ALIGNM4 */ + 0x8, /* FC_LONG */ +/* 2622 */ 0x6, /* FC_SHORT */ + 0x6, /* FC_SHORT */ +/* 2624 */ 0x6, /* FC_SHORT */ + 0x6, /* FC_SHORT */ +/* 2626 */ 0x2, /* FC_CHAR */ + 0x2, /* FC_CHAR */ +/* 2628 */ 0x2, /* FC_CHAR */ + 0x2, /* FC_CHAR */ +/* 2630 */ 0x4c, /* FC_EMBEDDED_COMPLEX */ + 0x0, /* 0 */ +/* 2632 */ NdrFcShort( 0xfffffeec ), /* Offset= -276 (2356) */ +/* 2634 */ 0x5c, /* FC_PAD */ + 0x5b, /* FC_END */ +/* 2636 */ + 0x15, /* FC_STRUCT */ + 0x0, /* 0 */ +/* 2638 */ NdrFcShort( 0x205 ), /* 517 */ +/* 2640 */ 0x4c, /* FC_EMBEDDED_COMPLEX */ + 0x0, /* 0 */ +/* 2642 */ NdrFcShort( 0xfffffee2 ), /* Offset= -286 (2356) */ +/* 2644 */ 0x2, /* FC_CHAR */ + 0x5b, /* FC_END */ +/* 2646 */ + 0x11, 0x0, /* FC_RP */ +/* 2648 */ NdrFcShort( 0x2 ), /* Offset= 2 (2650) */ +/* 2650 */ + 0x2b, /* FC_NON_ENCAPSULATED_UNION */ + 0xd, /* FC_ENUM16 */ +/* 2652 */ 0x26, /* 38 */ + 0x0, /* */ +#ifndef _ALPHA_ +/* 2654 */ NdrFcShort( 0x4 ), /* x86, MIPS, PPC Stack size/offset = 4 */ +#else + NdrFcShort( 0x8 ), /* Alpha Stack size/offset = 8 */ +#endif +/* 2656 */ NdrFcShort( 0xfffff9ec ), /* Offset= -1556 (1100) */ +/* 2658 */ + 0x12, 0x0, /* FC_UP */ +/* 2660 */ NdrFcShort( 0xfffffcee ), /* Offset= -786 (1874) */ +/* 2662 */ + 0x11, 0x14, /* FC_RP [alloced_on_stack] */ +/* 2664 */ NdrFcShort( 0x2 ), /* Offset= 2 (2666) */ +/* 2666 */ + 0x12, 0x0, /* FC_UP */ +/* 2668 */ NdrFcShort( 0x10 ), /* Offset= 16 (2684) */ +/* 2670 */ + 0x1b, /* FC_CARRAY */ + 0x3, /* 3 */ +/* 2672 */ NdrFcShort( 0x8 ), /* 8 */ +/* 2674 */ 0x18, /* 24 */ + 0x0, /* */ +/* 2676 */ NdrFcShort( 0x0 ), /* 0 */ +/* 2678 */ 0x4c, /* FC_EMBEDDED_COMPLEX */ + 0x0, /* 0 */ +/* 2680 */ NdrFcShort( 0xfffff6f6 ), /* Offset= -2314 (366) */ +/* 2682 */ 0x5c, /* FC_PAD */ + 0x5b, /* FC_END */ +/* 2684 */ + 0x16, /* FC_PSTRUCT */ + 0x3, /* 3 */ +/* 2686 */ NdrFcShort( 0x8 ), /* 8 */ +/* 2688 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 2690 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2692 */ NdrFcShort( 0x4 ), /* 4 */ +/* 2694 */ NdrFcShort( 0x4 ), /* 4 */ +/* 2696 */ 0x12, 0x0, /* FC_UP */ +/* 2698 */ NdrFcShort( 0xffffffe4 ), /* Offset= -28 (2670) */ +/* 2700 */ + 0x5b, /* FC_END */ + + 0x8, /* FC_LONG */ +/* 2702 */ 0x8, /* FC_LONG */ + 0x5b, /* FC_END */ +/* 2704 */ + 0x11, 0x4, /* FC_RP [alloced_on_stack] */ +/* 2706 */ NdrFcShort( 0x2 ), /* Offset= 2 (2708) */ +/* 2708 */ + 0x2b, /* FC_NON_ENCAPSULATED_UNION */ + 0xd, /* FC_ENUM16 */ +/* 2710 */ 0x26, /* 38 */ + 0x0, /* */ +#ifndef _ALPHA_ +/* 2712 */ NdrFcShort( 0x4 ), /* x86, MIPS, PPC Stack size/offset = 4 */ +#else + NdrFcShort( 0x8 ), /* Alpha Stack size/offset = 8 */ +#endif +/* 2714 */ NdrFcShort( 0x2 ), /* Offset= 2 (2716) */ +/* 2716 */ NdrFcShort( 0x8 ), /* 8 */ +/* 2718 */ NdrFcShort( 0x3005 ), /* 12293 */ +/* 2720 */ NdrFcLong( 0x1 ), /* 1 */ +/* 2724 */ NdrFcShort( 0x82 ), /* Offset= 130 (2854) */ +/* 2726 */ NdrFcLong( 0x2 ), /* 2 */ +/* 2730 */ NdrFcShort( 0xe0 ), /* Offset= 224 (2954) */ +/* 2732 */ NdrFcLong( 0x3 ), /* 3 */ +/* 2736 */ NdrFcShort( 0xda ), /* Offset= 218 (2954) */ +/* 2738 */ NdrFcLong( 0x4 ), /* 4 */ +/* 2742 */ NdrFcShort( 0x12e ), /* Offset= 302 (3044) */ +/* 2744 */ NdrFcLong( 0x5 ), /* 5 */ +/* 2748 */ NdrFcShort( 0x128 ), /* Offset= 296 (3044) */ +/* 2750 */ NdrFcShort( 0xffffffff ), /* Offset= -1 (2749) */ +/* 2752 */ + 0x16, /* FC_PSTRUCT */ + 0x3, /* 3 */ +/* 2754 */ NdrFcShort( 0x24 ), /* 36 */ +/* 2756 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 2758 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2760 */ NdrFcShort( 0x10 ), /* 16 */ +/* 2762 */ NdrFcShort( 0x10 ), /* 16 */ +/* 2764 */ 0x12, 0x0, /* FC_UP */ +/* 2766 */ NdrFcShort( 0xfffff92a ), /* Offset= -1750 (1016) */ +/* 2768 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2770 */ NdrFcShort( 0x18 ), /* 24 */ +/* 2772 */ NdrFcShort( 0x18 ), /* 24 */ +/* 2774 */ 0x12, 0x0, /* FC_UP */ +/* 2776 */ NdrFcShort( 0xfffffa04 ), /* Offset= -1532 (1244) */ +/* 2778 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2780 */ NdrFcShort( 0x20 ), /* 32 */ +/* 2782 */ NdrFcShort( 0x20 ), /* 32 */ +/* 2784 */ 0x12, 0x0, /* FC_UP */ +/* 2786 */ NdrFcShort( 0xfffffa08 ), /* Offset= -1528 (1258) */ +/* 2788 */ + 0x5b, /* FC_END */ + + 0x8, /* FC_LONG */ +/* 2790 */ 0x8, /* FC_LONG */ + 0x8, /* FC_LONG */ +/* 2792 */ 0x6, /* FC_SHORT */ + 0x6, /* FC_SHORT */ +/* 2794 */ 0x38, /* FC_ALIGNM4 */ + 0x8, /* FC_LONG */ +/* 2796 */ 0x6, /* FC_SHORT */ + 0x6, /* FC_SHORT */ +/* 2798 */ 0x38, /* FC_ALIGNM4 */ + 0x8, /* FC_LONG */ +/* 2800 */ 0x6, /* FC_SHORT */ + 0x6, /* FC_SHORT */ +/* 2802 */ 0x38, /* FC_ALIGNM4 */ + 0x8, /* FC_LONG */ +/* 2804 */ 0x5c, /* FC_PAD */ + 0x5b, /* FC_END */ +/* 2806 */ + 0x1b, /* FC_CARRAY */ + 0x3, /* 3 */ +/* 2808 */ NdrFcShort( 0x24 ), /* 36 */ +/* 2810 */ 0x18, /* 24 */ + 0x0, /* */ +/* 2812 */ NdrFcShort( 0x0 ), /* 0 */ +/* 2814 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 2816 */ + 0x48, /* FC_VARIABLE_REPEAT */ + 0x49, /* FC_FIXED_OFFSET */ +/* 2818 */ NdrFcShort( 0x24 ), /* 36 */ +/* 2820 */ NdrFcShort( 0x0 ), /* 0 */ +/* 2822 */ NdrFcShort( 0x3 ), /* 3 */ +/* 2824 */ NdrFcShort( 0x10 ), /* 16 */ +/* 2826 */ NdrFcShort( 0x10 ), /* 16 */ +/* 2828 */ 0x12, 0x0, /* FC_UP */ +/* 2830 */ NdrFcShort( 0xfffff8ea ), /* Offset= -1814 (1016) */ +/* 2832 */ NdrFcShort( 0x18 ), /* 24 */ +/* 2834 */ NdrFcShort( 0x18 ), /* 24 */ +/* 2836 */ 0x12, 0x0, /* FC_UP */ +/* 2838 */ NdrFcShort( 0xfffff9c6 ), /* Offset= -1594 (1244) */ +/* 2840 */ NdrFcShort( 0x20 ), /* 32 */ +/* 2842 */ NdrFcShort( 0x20 ), /* 32 */ +/* 2844 */ 0x12, 0x0, /* FC_UP */ +/* 2846 */ NdrFcShort( 0xfffff9cc ), /* Offset= -1588 (1258) */ +/* 2848 */ + 0x5b, /* FC_END */ + + 0x4c, /* FC_EMBEDDED_COMPLEX */ +/* 2850 */ 0x0, /* 0 */ + NdrFcShort( 0xffffff9d ), /* Offset= -99 (2752) */ + 0x5b, /* FC_END */ +/* 2854 */ + 0x16, /* FC_PSTRUCT */ + 0x3, /* 3 */ +/* 2856 */ NdrFcShort( 0x8 ), /* 8 */ +/* 2858 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 2860 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2862 */ NdrFcShort( 0x4 ), /* 4 */ +/* 2864 */ NdrFcShort( 0x4 ), /* 4 */ +/* 2866 */ 0x12, 0x0, /* FC_UP */ +/* 2868 */ NdrFcShort( 0xffffffc2 ), /* Offset= -62 (2806) */ +/* 2870 */ + 0x5b, /* FC_END */ + + 0x8, /* FC_LONG */ +/* 2872 */ 0x8, /* FC_LONG */ + 0x5b, /* FC_END */ +/* 2874 */ + 0x16, /* FC_PSTRUCT */ + 0x3, /* 3 */ +/* 2876 */ NdrFcShort( 0x1c ), /* 28 */ +/* 2878 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 2880 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2882 */ NdrFcShort( 0x10 ), /* 16 */ +/* 2884 */ NdrFcShort( 0x10 ), /* 16 */ +/* 2886 */ 0x12, 0x0, /* FC_UP */ +/* 2888 */ NdrFcShort( 0xfffff8b0 ), /* Offset= -1872 (1016) */ +/* 2890 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2892 */ NdrFcShort( 0x18 ), /* 24 */ +/* 2894 */ NdrFcShort( 0x18 ), /* 24 */ +/* 2896 */ 0x12, 0x0, /* FC_UP */ +/* 2898 */ NdrFcShort( 0xfffff98a ), /* Offset= -1654 (1244) */ +/* 2900 */ + 0x5b, /* FC_END */ + + 0x8, /* FC_LONG */ +/* 2902 */ 0x8, /* FC_LONG */ + 0x8, /* FC_LONG */ +/* 2904 */ 0x6, /* FC_SHORT */ + 0x6, /* FC_SHORT */ +/* 2906 */ 0x38, /* FC_ALIGNM4 */ + 0x8, /* FC_LONG */ +/* 2908 */ 0x6, /* FC_SHORT */ + 0x6, /* FC_SHORT */ +/* 2910 */ 0x38, /* FC_ALIGNM4 */ + 0x8, /* FC_LONG */ +/* 2912 */ 0x5c, /* FC_PAD */ + 0x5b, /* FC_END */ +/* 2914 */ + 0x1b, /* FC_CARRAY */ + 0x3, /* 3 */ +/* 2916 */ NdrFcShort( 0x1c ), /* 28 */ +/* 2918 */ 0x18, /* 24 */ + 0x0, /* */ +/* 2920 */ NdrFcShort( 0x0 ), /* 0 */ +/* 2922 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 2924 */ + 0x48, /* FC_VARIABLE_REPEAT */ + 0x49, /* FC_FIXED_OFFSET */ +/* 2926 */ NdrFcShort( 0x1c ), /* 28 */ +/* 2928 */ NdrFcShort( 0x0 ), /* 0 */ +/* 2930 */ NdrFcShort( 0x2 ), /* 2 */ +/* 2932 */ NdrFcShort( 0x10 ), /* 16 */ +/* 2934 */ NdrFcShort( 0x10 ), /* 16 */ +/* 2936 */ 0x12, 0x0, /* FC_UP */ +/* 2938 */ NdrFcShort( 0xfffff87e ), /* Offset= -1922 (1016) */ +/* 2940 */ NdrFcShort( 0x18 ), /* 24 */ +/* 2942 */ NdrFcShort( 0x18 ), /* 24 */ +/* 2944 */ 0x12, 0x0, /* FC_UP */ +/* 2946 */ NdrFcShort( 0xfffff95a ), /* Offset= -1702 (1244) */ +/* 2948 */ + 0x5b, /* FC_END */ + + 0x4c, /* FC_EMBEDDED_COMPLEX */ +/* 2950 */ 0x0, /* 0 */ + NdrFcShort( 0xffffffb3 ), /* Offset= -77 (2874) */ + 0x5b, /* FC_END */ +/* 2954 */ + 0x16, /* FC_PSTRUCT */ + 0x3, /* 3 */ +/* 2956 */ NdrFcShort( 0x8 ), /* 8 */ +/* 2958 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 2960 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2962 */ NdrFcShort( 0x4 ), /* 4 */ +/* 2964 */ NdrFcShort( 0x4 ), /* 4 */ +/* 2966 */ 0x12, 0x0, /* FC_UP */ +/* 2968 */ NdrFcShort( 0xffffffca ), /* Offset= -54 (2914) */ +/* 2970 */ + 0x5b, /* FC_END */ + + 0x8, /* FC_LONG */ +/* 2972 */ 0x8, /* FC_LONG */ + 0x5b, /* FC_END */ +/* 2974 */ + 0x1c, /* FC_CVARRAY */ + 0x0, /* 0 */ +/* 2976 */ NdrFcShort( 0x1 ), /* 1 */ +/* 2978 */ 0x16, /* 22 */ + 0x0, /* */ +/* 2980 */ NdrFcShort( 0x6 ), /* 6 */ +/* 2982 */ 0x16, /* 22 */ + 0x0, /* */ +/* 2984 */ NdrFcShort( 0x4 ), /* 4 */ +/* 2986 */ 0x2, /* FC_CHAR */ + 0x5b, /* FC_END */ +/* 2988 */ + 0x16, /* FC_PSTRUCT */ + 0x3, /* 3 */ +/* 2990 */ NdrFcShort( 0xc ), /* 12 */ +/* 2992 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 2994 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 2996 */ NdrFcShort( 0x8 ), /* 8 */ +/* 2998 */ NdrFcShort( 0x8 ), /* 8 */ +/* 3000 */ 0x12, 0x0, /* FC_UP */ +/* 3002 */ NdrFcShort( 0xffffffe4 ), /* Offset= -28 (2974) */ +/* 3004 */ + 0x5b, /* FC_END */ + + 0x8, /* FC_LONG */ +/* 3006 */ 0x6, /* FC_SHORT */ + 0x6, /* FC_SHORT */ +/* 3008 */ 0x38, /* FC_ALIGNM4 */ + 0x8, /* FC_LONG */ +/* 3010 */ 0x5c, /* FC_PAD */ + 0x5b, /* FC_END */ +/* 3012 */ + 0x1b, /* FC_CARRAY */ + 0x3, /* 3 */ +/* 3014 */ NdrFcShort( 0xc ), /* 12 */ +/* 3016 */ 0x18, /* 24 */ + 0x0, /* */ +/* 3018 */ NdrFcShort( 0x0 ), /* 0 */ +/* 3020 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 3022 */ + 0x48, /* FC_VARIABLE_REPEAT */ + 0x49, /* FC_FIXED_OFFSET */ +/* 3024 */ NdrFcShort( 0xc ), /* 12 */ +/* 3026 */ NdrFcShort( 0x0 ), /* 0 */ +/* 3028 */ NdrFcShort( 0x1 ), /* 1 */ +/* 3030 */ NdrFcShort( 0x8 ), /* 8 */ +/* 3032 */ NdrFcShort( 0x8 ), /* 8 */ +/* 3034 */ 0x12, 0x0, /* FC_UP */ +/* 3036 */ NdrFcShort( 0xffffffc2 ), /* Offset= -62 (2974) */ +/* 3038 */ + 0x5b, /* FC_END */ + + 0x4c, /* FC_EMBEDDED_COMPLEX */ +/* 3040 */ 0x0, /* 0 */ + NdrFcShort( 0xffffffcb ), /* Offset= -53 (2988) */ + 0x5b, /* FC_END */ +/* 3044 */ + 0x16, /* FC_PSTRUCT */ + 0x3, /* 3 */ +/* 3046 */ NdrFcShort( 0x8 ), /* 8 */ +/* 3048 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 3050 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 3052 */ NdrFcShort( 0x4 ), /* 4 */ +/* 3054 */ NdrFcShort( 0x4 ), /* 4 */ +/* 3056 */ 0x12, 0x0, /* FC_UP */ +/* 3058 */ NdrFcShort( 0xffffffd2 ), /* Offset= -46 (3012) */ +/* 3060 */ + 0x5b, /* FC_END */ + + 0x8, /* FC_LONG */ +/* 3062 */ 0x8, /* FC_LONG */ + 0x5b, /* FC_END */ +/* 3064 */ + 0x11, 0x4, /* FC_RP [alloced_on_stack] */ +/* 3066 */ NdrFcShort( 0x2 ), /* Offset= 2 (3068) */ +/* 3068 */ + 0x15, /* FC_STRUCT */ + 0x3, /* 3 */ +/* 3070 */ NdrFcShort( 0x8 ), /* 8 */ +/* 3072 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 3074 */ 0x8, /* FC_LONG */ + 0x5b, /* FC_END */ +/* 3076 */ + 0x11, 0x14, /* FC_RP [alloced_on_stack] */ +/* 3078 */ NdrFcShort( 0x2 ), /* Offset= 2 (3080) */ +/* 3080 */ + 0x12, 0x0, /* FC_UP */ +/* 3082 */ NdrFcShort( 0x2 ), /* Offset= 2 (3084) */ +/* 3084 */ + 0x2b, /* FC_NON_ENCAPSULATED_UNION */ + 0xd, /* FC_ENUM16 */ +/* 3086 */ 0x26, /* 38 */ + 0x0, /* */ +#ifndef _ALPHA_ +/* 3088 */ NdrFcShort( 0x4 ), /* x86, MIPS, PPC Stack size/offset = 4 */ +#else + NdrFcShort( 0x8 ), /* Alpha Stack size/offset = 8 */ +#endif +/* 3090 */ NdrFcShort( 0xfffff50e ), /* Offset= -2802 (288) */ +/* 3092 */ + 0x11, 0x14, /* FC_RP [alloced_on_stack] */ +/* 3094 */ NdrFcShort( 0x2 ), /* Offset= 2 (3096) */ +/* 3096 */ + 0x12, 0x0, /* FC_UP */ +/* 3098 */ NdrFcShort( 0x2 ), /* Offset= 2 (3100) */ +/* 3100 */ + 0x2b, /* FC_NON_ENCAPSULATED_UNION */ + 0xd, /* FC_ENUM16 */ +/* 3102 */ 0x26, /* 38 */ + 0x0, /* */ +#ifndef _ALPHA_ +/* 3104 */ NdrFcShort( 0x4 ), /* x86, MIPS, PPC Stack size/offset = 4 */ +#else + NdrFcShort( 0x8 ), /* Alpha Stack size/offset = 8 */ +#endif +/* 3106 */ NdrFcShort( 0xfffff82a ), /* Offset= -2006 (1100) */ +/* 3108 */ + 0x11, 0x4, /* FC_RP [alloced_on_stack] */ +/* 3110 */ NdrFcShort( 0x2 ), /* Offset= 2 (3112) */ +/* 3112 */ + 0x2b, /* FC_NON_ENCAPSULATED_UNION */ + 0xd, /* FC_ENUM16 */ +/* 3114 */ 0x26, /* 38 */ + 0x0, /* */ +#ifndef _ALPHA_ +/* 3116 */ NdrFcShort( 0x4 ), /* x86, MIPS, PPC Stack size/offset = 4 */ +#else + NdrFcShort( 0x8 ), /* Alpha Stack size/offset = 8 */ +#endif +/* 3118 */ NdrFcShort( 0xfffffe6e ), /* Offset= -402 (2716) */ +/* 3120 */ + 0x11, 0x0, /* FC_RP */ +/* 3122 */ NdrFcShort( 0x2 ), /* Offset= 2 (3124) */ +/* 3124 */ 0x30, /* FC_BIND_CONTEXT */ + 0xa0, /* 160 */ +/* 3126 */ 0x0, /* 0 */ + 0x4, /* 4 */ +/* 3128 */ + 0x11, 0x4, /* FC_RP [alloced_on_stack] */ +/* 3130 */ NdrFcShort( 0x2 ), /* Offset= 2 (3132) */ +/* 3132 */ + 0x2b, /* FC_NON_ENCAPSULATED_UNION */ + 0xd, /* FC_ENUM16 */ +/* 3134 */ 0x26, /* 38 */ + 0x0, /* */ +#ifndef _ALPHA_ +/* 3136 */ NdrFcShort( 0x4 ), /* x86, MIPS, PPC Stack size/offset = 4 */ +#else + NdrFcShort( 0x8 ), /* Alpha Stack size/offset = 8 */ +#endif +/* 3138 */ NdrFcShort( 0xfffffe5a ), /* Offset= -422 (2716) */ +/* 3140 */ + 0x12, 0x0, /* FC_UP */ +/* 3142 */ NdrFcShort( 0x10 ), /* Offset= 16 (3158) */ +/* 3144 */ + 0x1c, /* FC_CVARRAY */ + 0x0, /* 0 */ +/* 3146 */ NdrFcShort( 0x1 ), /* 1 */ +/* 3148 */ 0x16, /* 22 */ + 0x0, /* */ +/* 3150 */ NdrFcShort( 0x2 ), /* 2 */ +/* 3152 */ 0x16, /* 22 */ + 0x0, /* */ +/* 3154 */ NdrFcShort( 0x0 ), /* 0 */ +/* 3156 */ 0x2, /* FC_CHAR */ + 0x5b, /* FC_END */ +/* 3158 */ + 0x16, /* FC_PSTRUCT */ + 0x3, /* 3 */ +/* 3160 */ NdrFcShort( 0x8 ), /* 8 */ +/* 3162 */ + 0x4b, /* FC_PP */ + 0x5c, /* FC_PAD */ +/* 3164 */ + 0x46, /* FC_NO_REPEAT */ + 0x5c, /* FC_PAD */ +/* 3166 */ NdrFcShort( 0x4 ), /* 4 */ +/* 3168 */ NdrFcShort( 0x4 ), /* 4 */ +/* 3170 */ 0x12, 0x0, /* FC_UP */ +/* 3172 */ NdrFcShort( 0xffffffe4 ), /* Offset= -28 (3144) */ +/* 3174 */ + 0x5b, /* FC_END */ + + 0x6, /* FC_SHORT */ +/* 3176 */ 0x6, /* FC_SHORT */ + 0x38, /* FC_ALIGNM4 */ +/* 3178 */ 0x8, /* FC_LONG */ + 0x5b, /* FC_END */ +/* 3180 */ + 0x11, 0x0, /* FC_RP */ +/* 3182 */ NdrFcShort( 0xffffffe8 ), /* Offset= -24 (3158) */ +/* 3184 */ + 0x12, 0x0, /* FC_UP */ +/* 3186 */ NdrFcShort( 0xfffffcc2 ), /* Offset= -830 (2356) */ +/* 3188 */ + 0x12, 0x0, /* FC_UP */ +/* 3190 */ NdrFcShort( 0xfffff3de ), /* Offset= -3106 (84) */ +/* 3192 */ + 0x12, 0x8, /* FC_UP [simple_pointer] */ +/* 3194 */ + 0x25, /* FC_C_WSTRING */ + 0x5c, /* FC_PAD */ +/* 3196 */ + 0x11, 0x0, /* FC_RP */ +/* 3198 */ NdrFcShort( 0x2 ), /* Offset= 2 (3200) */ +/* 3200 */ + 0x2b, /* FC_NON_ENCAPSULATED_UNION */ + 0xd, /* FC_ENUM16 */ +/* 3202 */ 0x26, /* 38 */ + 0x0, /* */ +#ifndef _ALPHA_ +/* 3204 */ NdrFcShort( 0x4 ), /* x86, MIPS, PPC Stack size/offset = 4 */ +#else + NdrFcShort( 0x8 ), /* Alpha Stack size/offset = 8 */ +#endif +/* 3206 */ NdrFcShort( 0xfffff7c6 ), /* Offset= -2106 (1100) */ + + 0x0 + } + }; diff --git a/private/newsam2/server/samsrvp.h b/private/newsam2/server/samsrvp.h new file mode 100644 index 000000000..84f8aacfd --- /dev/null +++ b/private/newsam2/server/samsrvp.h @@ -0,0 +1,3337 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + samsrvp.h + +Abstract: + + This file contains definitions private to the SAM server program. + +Author: + + Jim Kelly (JimK) 4-July-1991 + +Environment: + + User Mode - Win32 + +Revision History: + + JimK 04-Jul-1991 + Created initial file. + ChrisMay 10-Jun-1996 + Added macros and flags/defines for IsDsObject tests. + Murlis 27-Jun-1996 + Moved SAMP_OBJECT_TYPE and mapping table structure defines + to mappings.h in dsamain\src\include + +--*/ + +#ifndef _NTSAMP_ +#define _NTSAMP_ + + +#ifndef UNICODE +#define UNICODE +#endif // UNICODE + +//////////////////////////////////////////////////////////////////////// +// // +// // +// Diagnostics // +// // +//////////////////////////////////////////////////////////////////////// + +// +// The following define controls the diagnostic capabilities that +// are built into SAM. +// + +#if DBG +#define SAMP_DIAGNOSTICS 1 +#endif // DBG + + +// +// These definitions are useful diagnostics aids +// + +#if SAMP_DIAGNOSTICS + +// +// Diagnostics included in build +// + +// +// Test for diagnostics enabled +// + +#define IF_SAMP_GLOBAL( FlagName ) \ + if (SampGlobalFlag & (SAMP_DIAG_##FlagName)) + +#define IF_NOT_SAMP_GLOBAL( FlagName ) \ + if ( !(SampGlobalFlag & (SAMP_DIAG_##FlagName)) ) + +// +// Diagnostics print statement +// + +#define SampDiagPrint( FlagName, _Text_ ) \ + IF_SAMP_GLOBAL( FlagName ) \ + DbgPrint _Text_ + + +#else + +// +// No diagnostics included in build +// + +// +// Test for diagnostics enabled +// + +#define IF_SAMP_GLOBAL( FlagName ) if (FALSE) +#define IF_NOT_SAMP_GLOBAL ( FlagName ) if (TRUE) + + +// +// Diagnostics print statement (nothing) +// + +#define SampDiagPrint( FlagName, Text ) ; + + +#endif // SAMP_DIAGNOSTICS + + + + + +// +// The following flags enable or disable various diagnostic +// capabilities within SAM. These flags are set in +// SampGlobalFlag. +// +// DISPLAY_CACHE - print diagnostic messages related +// to the display cache (additions, deletions, +// modifications, etc). +// +// DISPLAY_LOCKOUT - print diagnostic messages related +// to account lockout. +// +// DISPLAY_ROLE_CHANGES - print diagnostic messages related +// to primary/backup role changes. +// +// DISPLAY_CACHE_ERRORS - print diagnostic messages related to +// errors when manipulating the display cache. +// +// DISPLAY_STORAGE_FAIL - print diagnostic messages related to +// backing store failures. +// +// BREAK_ON_STORAGE_FAIL - breakpoint if an attempt to write +// to backing store fails. +// +// CONTEXT_TRACKING - print diagnostic messages related to +// object context usage (creation / deletion, etc.). +// +// ACTIVATE_DEBUG_PROC - activate a process for use as a diagnostic +// aid. This is expected to be used only during SETUP testing. +// +// DISPLAY_ADMIN_CHANGES - print diagnostic messages related to +// changing user account protection to allow or dissallow +// Account Operator access to admin or normal user accounts. +// + +#define SAMP_DIAG_DISPLAY_CACHE ((ULONG) 0x00000001L) +#define SAMP_DIAG_DISPLAY_LOCKOUT ((ULONG) 0x00000002L) +#define SAMP_DIAG_DISPLAY_ROLE_CHANGES ((ULONG) 0x00000004L) +#define SAMP_DIAG_DISPLAY_CACHE_ERRORS ((ULONG) 0x00000008L) +#define SAMP_DIAG_DISPLAY_STORAGE_FAIL ((ULONG) 0x00000010L) +#define SAMP_DIAG_BREAK_ON_STORAGE_FAIL ((ULONG) 0x00000020L) +#define SAMP_DIAG_CONTEXT_TRACKING ((ULONG) 0x00000040L) +#define SAMP_DIAG_ACTIVATE_DEBUG_PROC ((ULONG) 0x00000080L) +#define SAMP_DIAG_DISPLAY_ADMIN_CHANGES ((ULONG) 0x00000100L) + + + + +// +// Choose a print type appropriate to how we are building. +// + +#ifdef SAMP_BUILD_CONSOLE_PROGRAM + +#define BldPrint printf + +#else + +#define BldPrint DbgPrint + +#endif + +#if DBG + +#define SUCCESS_ASSERT(Status, Msg) \ +{ \ + if ( !NT_SUCCESS(Status) ) { \ + UnexpectedProblem(); \ + BldPrint(Msg); \ + BldPrint("Status is: 0x%lx \n", Status); \ + return(Status); \ + \ + } \ +} + +#else + +#define SUCCESS_ASSERT(Status, Msg) \ +{ \ + if ( !NT_SUCCESS(Status) ) { \ + return(Status); \ + } \ +} + +#endif // DBG + + +// +// Define this symbol to get context tracking messages printed +// (otherwise, comment it out) +// + +//#define SAMP_DBG_CONTEXT_TRACKING + +// +// Maximum number of digits that may be specified to +// SampRtlConvertRidToUnicodeString +// + +#define SAMP_MAXIMUM_ACCOUNT_RID_DIGITS ((ULONG) 8) + +// +// Account never expires timestamp (in ULONG form ) +// + +#define SAMP_ACCOUNT_NEVER_EXPIRES ((ULONG) 0) + + + +// +// SAM's shutdown order level (index). +// Shutdown notifications are made in the order of highest level +// to lowest level value. +// + +#define SAMP_SHUTDOWN_LEVEL ((DWORD) 481) + + + +// Define a Macro to set and unset the state of the DS object in the Context +// blob. + +#define SAMP_REG_OBJECT ((ULONG) 0x00000001) +#define SAMP_DS_OBJECT ((ULONG) 0x00000002) + + +// Define a Macro to set and Unset the state of the DS object +// in the Context blob + +#define SetDsObject(c) ((c->ObjectFlags) |= SAMP_DS_OBJECT);\ + ((c->ObjectFlags) &= ~(SAMP_REG_OBJECT)); + + +#define SetRegistryObject(c) ((c->ObjectFlags) |= SAMP_REG_OBJECT);\ + ((c->ObjectFlags) &= ~(SAMP_DS_OBJECT)); + + +//Define a Macro to check if the object is in the DS +#define IsDsObject(c) (((c->ObjectFlags)& SAMP_DS_OBJECT)==SAMP_DS_OBJECT) + + +// Define a Macro to obtain the domain object given an account Object +#define DomainObjectFromAccountContext(C)\ + SampDefinedDomains[C->DomainIndex].Context->ObjectNameInDs + +// Define a Macro to access the Root Object + +#define ROOT_OBJECT ((DSNAME *) RootObjectName) + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include <nt.h> +#include <ntrtl.h> // DbgPrint prototype +#include <nturtl.h> // needed for winbase.h +#include <rpc.h> // DataTypes and runtime APIs + +#include <string.h> // strlen +#include <stdio.h> // sprintf +#define UnicodeTerminate(p) ((PUNICODE_STRING)(p))->Buffer[(((PUNICODE_STRING)(p))->Length + 1)/sizeof(WCHAR)] = UNICODE_NULL + +#include <ntrpcp.h> // prototypes for MIDL user functions +#include <samrpc.h> // midl generated SAM RPC definitions +#include <ntlsa.h> +#include <samisrv.h> // SamIConnect() +#include <lsarpc.h> +#include <lsaisrv.h> +#include <ntsam.h> +#include <ntsamp.h> +#include <samsrv.h> // prototypes available to security process +#include "sampmsgs.h" +#include "lsathunk.h" +#include "dbgutilp.h" // supplimental debugging routines +#include <mappings.h> + +VOID +UnexpectedProblem( VOID ); + + +/////////////////////////////////////////////////////////////////////////////// +// // +// TEMPORARY GenTab2 definitions // +// These structures should be considered opaque. // +// // +/////////////////////////////////////////////////////////////////////////////// + + + +// +// Each element in the tree is pointed to from a leaf structure. +// The leafs are linked together to arrange the elements in +// ascending sorted order. +// + +typedef struct _GTB_TWO_THREE_LEAF { + + // + // Sort order list links + // + + LIST_ENTRY SortOrderEntry; + + // + // Pointer to element + // + + PVOID Element; + +} GTB_TWO_THREE_LEAF, *PGTB_TWO_THREE_LEAF; + + + +typedef struct _GTB_TWO_THREE_NODE { + + // + // Pointer to parent node. If this is the root node, + // then this pointer is null. + // + + struct _GTB_TWO_THREE_NODE *ParentNode; + + + // + // Pointers to child nodes. + // + // 1) If a pointer is null, then this node does not have + // that child. In this case, the control value MUST + // indicate that the children are leaves. + // + // 2) If the children are leaves, then each child pointer + // is either NULL (indicating this node doesn't have + // that child) or points to a GTB_TWO_THREE_LEAF. + // If ThirdChild is Non-Null, then so is SecondChild. + // If SecondChild is Non-Null, then so is FirstChild. + // (that is, you can't have a third child without a + // second child, or a second child without a first + // child). + // + + struct _GTB_TWO_THREE_NODE *FirstChild; + struct _GTB_TWO_THREE_NODE *SecondChild; + struct _GTB_TWO_THREE_NODE *ThirdChild; + + // + // Flags provding control information about this node + // + + ULONG Control; + + + // + // These fields point to the element that has the lowest + // value of all elements in the second and third subtrees + // (respectively). These fields are only valid if the + // corresponding child subtree pointer is non-null. + // + + PGTB_TWO_THREE_LEAF LowOfSecond; + PGTB_TWO_THREE_LEAF LowOfThird; + +} GTB_TWO_THREE_NODE, *PGTB_TWO_THREE_NODE; + + +// +// The comparison function takes as input pointers to elements containing +// user defined structures and returns the results of comparing the two +// elements. The result must indicate whether the FirstElement +// is GreaterThan, LessThan, or EqualTo the SecondElement. +// + +typedef +RTL_GENERIC_COMPARE_RESULTS +(NTAPI *PRTL_GENERIC_2_COMPARE_ROUTINE) ( + PVOID FirstElement, + PVOID SecondElement + ); + +// +// The allocation function is called by the generic table package whenever +// it needs to allocate memory for the table. +// + +typedef +PVOID +(NTAPI *PRTL_GENERIC_2_ALLOCATE_ROUTINE) ( + CLONG ByteSize + ); + +// +// The deallocation function is called by the generic table package whenever +// it needs to deallocate memory from the table that was allocated by calling +// the user supplied allocation function. +// + +typedef +VOID +(NTAPI *PRTL_GENERIC_2_FREE_ROUTINE) ( + PVOID Buffer + ); + + +typedef struct _RTL_GENERIC_TABLE2 { + + // + // Pointer to root node. + // + + PGTB_TWO_THREE_NODE Root; + + // + // Number of elements in table + // + + ULONG ElementCount; + + // + // Link list of leafs (and thus elements) in sort order + // + + LIST_ENTRY SortOrderHead; + + + // + // Caller supplied routines + // + + PRTL_GENERIC_2_COMPARE_ROUTINE Compare; + PRTL_GENERIC_2_ALLOCATE_ROUTINE Allocate; + PRTL_GENERIC_2_FREE_ROUTINE Free; + + +} RTL_GENERIC_TABLE2, *PRTL_GENERIC_TABLE2; + + + +////////////////////////////////////////////////////////////////////////// +// // +// Generic Table2 Routine Definitions... // +// // +////////////////////////////////////////////////////////////////////////// + + + +//NTSYSAPI +VOID +//NTAPI +RtlInitializeGenericTable2 ( + PRTL_GENERIC_TABLE2 Table, + PRTL_GENERIC_2_COMPARE_ROUTINE CompareRoutine, + PRTL_GENERIC_2_ALLOCATE_ROUTINE AllocateRoutine, + PRTL_GENERIC_2_FREE_ROUTINE FreeRoutine + ); + + +//NTSYSAPI +PVOID +//NTAPI +RtlInsertElementGenericTable2 ( + PRTL_GENERIC_TABLE2 Table, + PVOID Element, + PBOOLEAN NewElement + ); + + +//NTSYSAPI +BOOLEAN +//NTAPI +RtlDeleteElementGenericTable2 ( + PRTL_GENERIC_TABLE2 Table, + PVOID Element + ); + + +//NTSYSAPI +PVOID +//NTAPI +RtlLookupElementGenericTable2 ( + PRTL_GENERIC_TABLE2 Table, + PVOID Element + ); + + +//NTSYSAPI +PVOID +//NTAPI +RtlEnumerateGenericTable2 ( + PRTL_GENERIC_TABLE2 Table, + PVOID *RestartKey + ); + + +//NTSYSAPI +PVOID +//NTAPI +RtlRestartKeyByIndexGenericTable2( + PRTL_GENERIC_TABLE2 Table, + ULONG I, + PVOID *RestartKey + ); + +//NTSYSAPI +PVOID +//NTAPI +RtlRestartKeyByValueGenericTable2( + PRTL_GENERIC_TABLE2 Table, + PVOID Element, + PVOID *RestartKey + ); + +//NTSYSAPI +ULONG +//NTAPI +RtlNumberElementsGenericTable2( + PRTL_GENERIC_TABLE2 Table + ); + +// +// The function IsGenericTableEmpty will return to the caller TRUE if +// the generic table is empty (i.e., does not contain any elements) +// and FALSE otherwise. +// + +//NTSYSAPI +BOOLEAN +//NTAPI +RtlIsGenericTable2Empty ( + PRTL_GENERIC_TABLE2 Table + ); + + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Macros // +// // +/////////////////////////////////////////////////////////////////////////////// + + +// +// This macro generates TRUE if account auditing is enabled and this +// server is a PDC. Otherwise, this macro generates FALSE. +// +// SampDoAccountAuditing( +// IN ULONG i +// ) +// +// Where: +// +// i - is the index of the domain whose state is to be checked. +// + +#define SampDoAccountAuditing( i ) \ + ((SampAccountAuditingEnabled == TRUE) && \ + (SampDefinedDomains[i].CurrentFixed.ServerRole == DomainServerRolePrimary)) + +// +// VOID +// SampSetAuditingInformation( +// IN PPOLICY_AUDIT_EVENTS_INFO PolicyAuditEventsInfo +// ) +// +// Routine Description: +// +// This macro function sets the Audit Event Information relevant to SAM +// given LSA Audit Events Information. +// +// Arguments: +// +// PolicyAuditEventsInfo - Pointer to Audit Events Information +// structure. +// +// Return Values: +// +// None. +// + +#define SampSetAuditingInformation( PolicyAuditEventsInfo ) { \ + \ + if (PolicyAuditEventsInfo->AuditingMode && \ + (PolicyAuditEventsInfo->EventAuditingOptions[ AuditCategoryAccountManagement ] & \ + POLICY_AUDIT_EVENT_SUCCESS) \ + ) { \ + \ + SampAccountAuditingEnabled = TRUE; \ + \ + } else { \ + \ + SampAccountAuditingEnabled = FALSE; \ + } \ +} + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Defines // +// // +/////////////////////////////////////////////////////////////////////////////// + + +// +// Major and minor revision are stored as a single 32-bit +// value with the major revision in the upper 16-bits and +// the minor revision in the lower 16-bits. +// +// Major Revision: 1 - NT Version 1.0 +// Minor Revisions: 1 - NT Revision 1.0 +// 2 - NT Revision 1.0A +// + +#define SAMP_MAJOR_REVISION (0x00010000) +#define SAMP_MINOR_REVISION_V1_0 (0x00000001) +#define SAMP_MINOR_REVISION_V1_0A (0x00000002) +#define SAMP_MINOR_REVISION (0x00000002) +#define SAMP_REVISION (SAMP_MAJOR_REVISION + SAMP_MINOR_REVISION) +#define SAMP_SERVER_REVISION (SAMP_REVISION + 1) +#define SAMP_DS_REVISION 1 + + +// +// Maximum supported name length (in bytes) for this revision... +// + +#define SAMP_MAXIMUM_NAME_LENGTH (1024) + + +// +// Maximum amount of memory anyone can ask us to spend on a single +// request +// + +#define SAMP_MAXIMUM_MEMORY_TO_USE (4096*4096) + + +// +// Maximum allowable number of object opens. +// After this, opens will be rejected with INSUFFICIENT_RESOURCES +// + +#define SAMP_MAXIMUM_ACTIVE_CONTEXTS (2048) + + +// +// The number of SAM Local Domains +// + +#define SAMP_DEFINED_DOMAINS_COUNT ((ULONG) 2) + +// +// Defines the maximum number of well-known (restricted) accounts +// in the SAM database. Restricted accounts have rids less than this +// value. User-defined accounts have rids >= this value. +// + +#define SAMP_RESTRICTED_ACCOUNT_COUNT 1000 + + +// +// Maximum password history length. We store OWFs (16 bytes) in +// a string (up to 64k), so we could have up to 4k. However, that's +// much larger than necessary, and we'd like to leave room in case +// OWFs grow or somesuch. So we'll limit it to 1k. +// + +#define SAMP_MAXIMUM_PASSWORD_HISTORY_LENGTH 1024 + + +///////////////////////////////////////////////////////////////////////// +// // +// Each object has an associated set of attributes on disk. // +// These attributes are divided into fixed-length and variable-length. // +// Each object type defines whether its fixed and variable length // +// attributes are stored together or separately. // +// // +///////////////////////////////////////////////////////////////////////// + + +#define SAMP_SERVER_STORED_SEPARATELY (FALSE) + +#define SAMP_DOMAIN_STORED_SEPARATELY (TRUE) + +#define SAMP_USER_STORED_SEPARATELY (TRUE) + +#define SAMP_GROUP_STORED_SEPARATELY (FALSE) + +#define SAMP_ALIAS_STORED_SEPARATELY (FALSE) + + + + +/////////////////////////////////////////////////////////////////////////////// +// +// Data structures used for tracking allocated memory +// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct _SAMP_MEMORY { + struct _SAMP_MEMORY *Next; + PVOID Memory; +} SAMP_MEMORY, *PSAMP_MEMORY; + + + +/////////////////////////////////////////////////////////////////////////////// +// +// Data structures used for enumeration +// +/////////////////////////////////////////////////////////////////////////////// + + +typedef struct _SAMP_ENUMERATION_ELEMENT { + struct _SAMP_ENUMERATION_ELEMENT *Next; + SAMPR_RID_ENUMERATION Entry; +} SAMP_ENUMERATION_ELEMENT, *PSAMP_ENUMERATION_ELEMENT; + + +/////////////////////////////////////////////////////////////////////////////// +// +// Data structures related to service administration +// +/////////////////////////////////////////////////////////////////////////////// + +// +// SAM Service operation states. +// Valid state transition diagram is: +// +// Initializing ----> Enabled <====> Disabled ---> Shutdown -->Terminating +// + +typedef enum _SAMP_SERVICE_STATE { + SampServiceInitializing = 1, + SampServiceEnabled, + SampServiceDisabled, + SampServiceShutdown, + SampServiceTerminating +} SAMP_SERVICE_STATE, *PSAMP_SERVICE_STATE; + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Enumeration context associated with Enumerating Accounts in the // +// DS. This maintains the State Information regarding Paged Results // +// type of search in the DS, on a per domain context basis. // +// // +/////////////////////////////////////////////////////////////////////////////// + + +typedef struct _SAMP_DS_ENUMERATION_CONTEXT { + + // Used to Link to other Objects of this Type + LIST_ENTRY ContextListEntry; + // Pointer to a DS Restart Structure + PRESTART Restart; + // The Enumeration Handle associated with this structure. + SAM_ENUMERATE_HANDLE EnumerateHandle; +} SAMP_DS_ENUMERATION_CONTEXT, *PSAMP_DS_ENUMERATION_CONTEXT; + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Data structures associated with object types // +// // +/////////////////////////////////////////////////////////////////////////////// + + + +// +// Object type-dependent information +// + +typedef struct _SAMP_OBJECT_INFORMATION { + + // + // Generic mapping for this object type + // + + GENERIC_MAPPING GenericMapping; + + + // + // Mask of access types that are not valid for + // this object type when the access mask has been + // mapped from generic to specific access types. + // + + ACCESS_MASK InvalidMappedAccess; + + + // + // Mask of accesses representing write operations. These are + // used on a BDC to determine if an operation should be allowed + // or not. + // + + ACCESS_MASK WriteOperations; + + // + // Name of the object type - used for auditing. + // + + UNICODE_STRING ObjectTypeName; + + + // + // The following fields provide information about the attributes + // of this object and how they are stored on disk. These values + // are set at SAM initialization time and are not changed + // thereafter. NOTE: changing these values in the build will + // result in an on-disk format change - so don't change them just + // for the hell-of-it. + // + // FixedStoredSeparately - When TRUE indicates the fixed and + // variable-length attributes of the object are stored + // separately (in two registry-key-attributes). When FALSE, + // indicates they are stored together (in a single + // registry-key-attribute). + // + // + // FixedAttributesOffset - Offset from the beginning of the + // on-disk buffer to the beginning of the fixed-length + // attributes structure. + // + // VariableBufferOffset - Offset from the beginning of the + // on-disk buffer to the beginning of the Variable-length + // data buffer. If fixed and variable-length data are + // stored together, this will be zero. + // + // VariableArrayOffset - Offset from the beginning of the + // on-disk buffer to the beginning of the array of + // variable-length attributes descriptors. + // + // VariableDataOffset - Offset from the beginning of the + // on-disk buffer to the beginning of the variable-length + // attribute data. + // + + BOOLEAN FixedStoredSeparately; + ULONG FixedAttributesOffset, + VariableBufferOffset, + VariableArrayOffset, + VariableDataOffset; + + // + // Indicates the length of the fixed length information + // for this object type. + // + + ULONG FixedLengthSize; + + // + // The following fields provide information about the attributes of this + // object. Modifying SAM to utilize the DS as the backing store for domain + // account information, while still using the registry backing store for + // workstation account information, means that there are two similar, but + // slightly different data representations for SAM account information. + // + // All account information is represented in memory in terms of the fixed + // and variable-length data buffers (as defined in earlier versions of the + // SAM library). The source of the information, however, has changed in + // that domain-account information (i.e. Domain Controller accounts) comes + // from the DS backing store. + // + // Consequently, there is no need to store KEY_VALUE_PARTIAL_INFORMATION + // within the SAM buffer (because that is registry specific). + // + // Additionally, because some of the DS data types are different from the + // types used in previous SAM implementations, buffer offsets and lengths + // have changed from those stored in the registry, and mapped into memory + // by SAM code. + // + // The upshot of this is that whenever SAM buffers, constructed from the + // registry information are referenced, the above offsets (e.g. Fixed- + // AttributesOffset) are used. Alternatively, whenever SAM buffers, con- + // structed from DS information are referenced, the below offsets (e.g + // FixedDsAttributesOffset) are used. + // + + ULONG FixedDsAttributesOffset, + FixedDsLengthSize, + VariableDsBufferOffset, + VariableDsArrayOffset, + VariableDsDataOffset; + + // + // Indicates the number of variable length attributes + // for this object type. + // + + ULONG VariableAttributeCount; + + +} SAMP_OBJECT_INFORMATION, *PSAMP_OBJECT_INFORMATION; + + + +///////////////////////////////////////////////////////////////////////// +// // +// The following structures represent the in-memory body of each // +// object type. This is typically used to link instances of object // +// types together, and track dynamic state information related to // +// the object type. // +// // +// This information does not include the on-disk representation of // +// the object data. That information is kept in a separate structure // +// both on-disk and when in-memory. // +// // +///////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////// +// // +// SERVER object in-memory body // +// // +///////////////////////////////////////////////////////////////////////// + +typedef struct _SAMP_SERVER_OBJECT { + ULONG Reserved1; +} SAMP_SERVER_OBJECT, *PSAMP_SERVER_OBJECT; + + +///////////////////////////////////////////////////////////////////////// +// // +// DOMAIN object in-memory body // +// // +///////////////////////////////////////////////////////////////////////// + +typedef struct _SAMP_DOMAIN_OBJECT { + ULONG Reserved1; + // This is for maintaining state information regarding enumerations + // in the DS. Refer to the dicussion in utility.c on Enumeration + // routines. + LIST_ENTRY DsEnumerationContext; +} SAMP_DOMAIN_OBJECT, *PSAMP_DOMAIN_OBJECT; + + +///////////////////////////////////////////////////////////////////////// +// // +// USER object in-memory body // +// // +///////////////////////////////////////////////////////////////////////// + +typedef struct _SAMP_USER_OBJECT { + ULONG Rid; + BOOLEAN DomainPasswordInformationAccessible; +} SAMP_USER_OBJECT, *PSAMP_USER_OBJECT; + + +///////////////////////////////////////////////////////////////////////// +// // +// GROUP object in-memory body // +// // +///////////////////////////////////////////////////////////////////////// + +typedef struct _SAMP_GROUP_OBJECT { + ULONG Rid; +} SAMP_GROUP_OBJECT, *PSAMP_GROUP_OBJECT; + + +///////////////////////////////////////////////////////////////////////// +// // +// ALIAS object in-memory body // +// // +///////////////////////////////////////////////////////////////////////// + +typedef struct _SAMP_ALIAS_OBJECT { + ULONG Rid; +} SAMP_ALIAS_OBJECT, *PSAMP_ALIAS_OBJECT; + + +///////////////////////////////////////////////////////////////////////// +// // +// // +// The following data structure is the in-memory context associated // +// with an open object. // +// // +///////////////////////////////////////////////////////////////////////// + +typedef struct _SAMP_OBJECT { + + // + // Structure used to link this structure into lists + // + + LIST_ENTRY ContextListEntry; + + // + // Indicates the type of object stored. + // This is used to access an array of object type descriptors. + // + + SAMP_OBJECT_TYPE ObjectType; + + + // + // The FixedValid and VariableValid indicate whether the data in + // the fixed and variable-length on-disk image buffers are valid + // (i.e., were read from disk) or invalid (uninitialized). + // TRUE indicates the attribute is valid, FALSE indicates it is not. + // + + BOOLEAN FixedValid; + BOOLEAN VariableValid; + + + // + // The following flags indicate whether the fixed and/or variable + // length attributes portion of this object are dirty (i.e., have + // been changed since read from disk). If TRUE, then the data is + // dirty and will have to be flushed upon commit. These flags are + // only meaningful if the corresponding FixedValid or VariableValid + // flag is TRUE. + // + // When attributes are read from disk, the data is said to be + // "clean". If any changes are made to that data, then it is + // said to be "dirty". Dirty object attributes will be flushed + // to disk when the object is de-referenced at the end of a + // client call. + // + + BOOLEAN FixedDirty; + BOOLEAN VariableDirty; + + + + // + // This field points to the on-disk attributes of the object. This + // is one of: + // SAMP_ON_DISK_SERVER_OBJECT + // SAMP_ON_DISK_DOMAIN_OBJECT + // SAMP_ON_DISK_USER_OBJECT + // SAMP_ON_DISK_GROUP_OBJECT + // SAMP_ON_DISK_ALIAS_OBJECT + // + // The memory pointed to by this field is one allocation unit, even + // if fixed and variable length attributes are stored as seperate + // registry key attributes. This means that any time additions to + // the variable length attributes causes a new buffer to be allocated, + // both the fixed and variable length portions of the structure must + // be copied to the newly allocated memory. + // + + PVOID OnDisk; + + + // + // The OnDiskAllocated, OnDiskUsed, and OnDiskFree fields describe the + // memory pointed to by the OnDisk field. The OnDiskAllocated field + // indicates how many bytes long the block of memory is. The OnDiskUsed + // field indicates how much of the allocated memory is already in use. + // The variable length attributes are all packed upon any modification + // so that all free space is at the end of the block. The OnDiskFree + // field indicates how many bytes of the allocated block are available + // for use (note that this should be Allocated minus Used ). + // + // NOTE: The allocated and used values will ALWAYS be rounded up to ensure + // they are integral multiples of 4 bytes in length. This ensures + // any use of these fields directly will be dword aligned. + // + // Also note that when the VariableValid flag is FALSE, + // then the then OnDiskUsed OnDiskFree do NOT contain valid + // values. + // + + ULONG OnDiskAllocated; + ULONG OnDiskUsed; + ULONG OnDiskFree; + + + + // + // Before a context handle may be used, it must be referenced. + // This prevents the data from being deallocated from underneath it. + // + // Note, this count reflects one reference for the open itself, and + // then another reference for each time the object is looked up or + // subsequently referenced. Therefore, a handle close should + // dereference the object twice - once to counter the Lookup operation + // and once to represent elimination of the handle itself. + // + + ULONG ReferenceCount; + + + + // + // This field indicates the accesses that the client has been granted + // via this context. + // + + ACCESS_MASK GrantedAccess; + + + + // + // This handle is to the root registry key of the corresponding + // object. If this value is NULL, then the corresponding + // object was created in a previous call, but has not yet been + // opened. This will only occur for USERS, GROUPS, and ALIASES + // (and DOMAINS when we support DOMAIN creation). + // + + HANDLE RootKey; + + // + // This is the registry name of the corresponding object. It is + // set when the object is created, when RootKey is null. It is + // used to add the attributes changes out to the RXACT in the absence + // of the RootKey. After being used once, it is deleted - because + // the next time the object is used, the LookupContext() will fill + // in the RootKey. + // + + UNICODE_STRING RootName; + + + + // The Following field indicates the name of the Object in the DS, + // if the Object resides in the DS. + + DSNAME *ObjectNameInDs; + + // Defined Flags area as follows + // SAMP_OBJ_FLAG_DS -- Determines whether the object is present in the DS + // or in the Registry. If present in the Registry then the RootKey and Root + // Name fields indicate the registry fields the object is associated + // with. Else the ObjectNameInDs field indicates the object in the DS + + ULONG ObjectFlags; + + + // + // If the object is other than a Server object, then this field + // contains the index of the domain the object is in. This provides + // access to things like the domain's name. + // + + ULONG DomainIndex; + + // + // This field indicates a context block is to be deleted. + // Actual deallocation of the memory for the context block + // will not occur until the reference count drops to zero. + // + + BOOLEAN MarkedForDelete; + + // + // This field is used to indicate that the client associated with + // this context block is to be fully trusted. When TRUE, no access + // checks are performed against the client. This allows a single + // interface to be used by both RPC clients and internal procedures. + // + + BOOLEAN TrustedClient; + + + // + // This field indicates whether an audit generation routine must be + // called when this context block is deleted (which represents a + // handle being deleted). + // + + BOOLEAN AuditOnClose; + + + // + // This flag is TRUE when this context is valid. It may be necessary + // to invalidate before we can eliminate all references to it due to + // the way RPC works. RPC will only allow you to invalidate a context + // handle when called by the client using an API that has the context + // handle as an OUT parameter. + // + // Since someone may delete a user or group object (which invalidates + // all handles to that object), we must have a way of tracking handle + // validity independent of RPC's method. + // + + BOOLEAN Valid; + + // + // The body of each object. + // + + union { + + SAMP_SERVER_OBJECT Server; // local-account object types + SAMP_DOMAIN_OBJECT Domain; + SAMP_GROUP_OBJECT Group; + SAMP_ALIAS_OBJECT Alias; + SAMP_USER_OBJECT User; + + } TypeBody; + +} SAMP_OBJECT, *PSAMP_OBJECT; + + + + +/////////////////////////////////////////////////////////////////////////////// +// +// Data structures used to store information in the registry +// +/////////////////////////////////////////////////////////////////////////////// + +// +// Fixed length portion of a revision 1 Server object +// + +typedef struct _SAMP_V1_FIXED_LENGTH_SERVER { + + ULONG RevisionLevel; + +} SAMP_V1_FIXED_LENGTH_SERVER, *PSAMP_V1_FIXED_LENGTH_SERVER; + + +// +// Fixed length portion of a Domain +// (previous release formats of this structure follow) +// +// Note: in version 1.0 of NT, the fixed length portion of +// a domain was stored separate from the variable length +// portion. This allows us to compare the size of the +// data read from disk against the size of a V1_0A form +// of the fixed length data to determine whether it is +// a Version 1 format or later format. +// +// + +typedef struct _SAMP_V1_0A_FIXED_LENGTH_DOMAIN { + + ULONG Revision; + ULONG Unused1; + + LARGE_INTEGER CreationTime; + LARGE_INTEGER ModifiedCount; + LARGE_INTEGER MaxPasswordAge; + LARGE_INTEGER MinPasswordAge; + LARGE_INTEGER ForceLogoff; + LARGE_INTEGER LockoutDuration; + LARGE_INTEGER LockoutObservationWindow; + LARGE_INTEGER ModifiedCountAtLastPromotion; + + + ULONG NextRid; + ULONG PasswordProperties; + + USHORT MinPasswordLength; + USHORT PasswordHistoryLength; + + USHORT LockoutThreshold; + + DOMAIN_SERVER_ENABLE_STATE ServerState; + DOMAIN_SERVER_ROLE ServerRole; + + BOOLEAN UasCompatibilityRequired; + +} SAMP_V1_0A_FIXED_LENGTH_DOMAIN, *PSAMP_V1_0A_FIXED_LENGTH_DOMAIN; + +typedef struct _SAMP_V1_0_FIXED_LENGTH_DOMAIN { + + LARGE_INTEGER CreationTime; + LARGE_INTEGER ModifiedCount; + LARGE_INTEGER MaxPasswordAge; + LARGE_INTEGER MinPasswordAge; + LARGE_INTEGER ForceLogoff; + + ULONG NextRid; + + DOMAIN_SERVER_ENABLE_STATE ServerState; + DOMAIN_SERVER_ROLE ServerRole; + + USHORT MinPasswordLength; + USHORT PasswordHistoryLength; + ULONG PasswordProperties; + + BOOLEAN UasCompatibilityRequired; + +} SAMP_V1_0_FIXED_LENGTH_DOMAIN, *PSAMP_V1_0_FIXED_LENGTH_DOMAIN; + + + + + + +// +// Fixed length portion of a revision 1 group account +// +// Note: MemberCount could be treated as part of the fixed length +// data, but it is more convenient to keep it with the Member RID +// list in the MEMBERS key. +// + +typedef struct _SAMP_V1_FIXED_LENGTH_GROUP { + + ULONG RelativeId; + ULONG Attributes; + UCHAR AdminGroup; + +} SAMP_V1_FIXED_LENGTH_GROUP, *PSAMP_V1_FIXED_LENGTH_GROUP; + +typedef struct _SAMP_V1_0A_FIXED_LENGTH_GROUP { + + ULONG Revision; + ULONG RelativeId; + ULONG Attributes; + ULONG Unused1; + UCHAR AdminCount; + UCHAR OperatorCount; + +} SAMP_V1_0A_FIXED_LENGTH_GROUP, *PSAMP_V1_0A_FIXED_LENGTH_GROUP; + + +// +// Fixed length portion of a revision 1 alias account +// +// Note: MemberCount could be treated as part of the fixed length +// data, but it is more convenient to keep it with the Member RID +// list in the MEMBERS key. +// + +typedef struct _SAMP_V1_FIXED_LENGTH_ALIAS { + + ULONG RelativeId; + +} SAMP_V1_FIXED_LENGTH_ALIAS, *PSAMP_V1_FIXED_LENGTH_ALIAS; + + + +// +// Fixed length portion of a user account +// (previous release formats of this structure follow) +// +// Note: GroupCount could be treated as part of the fixed length +// data, but it is more convenient to keep it with the Group RID +// list in the GROUPS key. +// +// Note: in version 1.0 of NT, the fixed length portion of +// a user was stored separate from the variable length +// portion. This allows us to compare the size of the +// data read from disk against the size of a V1_0A form +// of the fixed length data to determine whether it is +// a Version 1 format or later format. + + +// +// This is the fixed length user from NT3.51 QFE and SUR +// + + +typedef struct _SAMP_V1_0A_FIXED_LENGTH_USER { + + ULONG Revision; + ULONG Unused1; + + LARGE_INTEGER LastLogon; + LARGE_INTEGER LastLogoff; + LARGE_INTEGER PasswordLastSet; + LARGE_INTEGER AccountExpires; + LARGE_INTEGER LastBadPasswordTime; + + ULONG UserId; + ULONG PrimaryGroupId; + ULONG UserAccountControl; + + USHORT CountryCode; + USHORT CodePage; + USHORT BadPasswordCount; + USHORT LogonCount; + USHORT AdminCount; + USHORT Unused2; + USHORT OperatorCount; + +} SAMP_V1_0A_FIXED_LENGTH_USER, *PSAMP_V1_0A_FIXED_LENGTH_USER; + +// +// This is the fixed length user from NT3.5 and NT3.51 +// + + +typedef struct _SAMP_V1_0_FIXED_LENGTH_USER { + + ULONG Revision; + ULONG Unused1; + + LARGE_INTEGER LastLogon; + LARGE_INTEGER LastLogoff; + LARGE_INTEGER PasswordLastSet; + LARGE_INTEGER AccountExpires; + LARGE_INTEGER LastBadPasswordTime; + + ULONG UserId; + ULONG PrimaryGroupId; + ULONG UserAccountControl; + + USHORT CountryCode; + USHORT CodePage; + USHORT BadPasswordCount; + USHORT LogonCount; + USHORT AdminCount; + +} SAMP_V1_0_FIXED_LENGTH_USER, *PSAMP_V1_0_FIXED_LENGTH_USER; + + +// +// This is the fixed length user from NT3.1 +// + +typedef struct _SAMP_V1_FIXED_LENGTH_USER { + + LARGE_INTEGER LastLogon; + LARGE_INTEGER LastLogoff; + LARGE_INTEGER PasswordLastSet; + LARGE_INTEGER AccountExpires; + + ULONG UserId; + ULONG PrimaryGroupId; + ULONG UserAccountControl; + + USHORT CountryCode; + USHORT CodePage; + USHORT BadPasswordCount; + USHORT LogonCount; + USHORT AdminCount; + + +} SAMP_V1_FIXED_LENGTH_USER, *PSAMP_V1_FIXED_LENGTH_USER; + + +// +// Domain account information is cached in memory in a sorted list. +// This allows fast return of information to user-interactive clients. +// One of these structures is part of the in-memory information for each domain. +// + +typedef struct _PSAMP_DOMAIN_DISPLAY_INFORMATION { + + RTL_GENERIC_TABLE2 RidTable; + ULONG TotalBytesInRidTable; + + RTL_GENERIC_TABLE2 UserTable; + ULONG TotalBytesInUserTable; + + RTL_GENERIC_TABLE2 MachineTable; + ULONG TotalBytesInMachineTable; + + RTL_GENERIC_TABLE2 InterdomainTable; + ULONG TotalBytesInInterdomainTable; + + RTL_GENERIC_TABLE2 GroupTable; + ULONG TotalBytesInGroupTable; + + + // + // These fields specify whether the cached information is valid. + // If TRUE, the cache contains valid information + // If FALSE, trees are empty. + // + + BOOLEAN UserAndMachineTablesValid; + BOOLEAN GroupTableValid; + +} SAMP_DOMAIN_DISPLAY_INFORMATION, *PSAMP_DOMAIN_DISPLAY_INFORMATION; + + +// +// Domain account information data structure used to pass data to the +// cache manipulation routines. This structure is the union of the cached +// data for all the account types that we keep in the cache. Other SAM routines +// can call fill this structure in without knowing which type of account +// requires which elements. +// + +typedef struct _SAMP_ACCOUNT_DISPLAY_INFO { + ULONG Rid; + ULONG AccountControl; // Also used as Attributes for groups + UNICODE_STRING Name; + UNICODE_STRING Comment; + UNICODE_STRING FullName; + +} SAMP_ACCOUNT_DISPLAY_INFO, *PSAMP_ACCOUNT_DISPLAY_INFO; + +///////////////////////////////////////////////////////////////////////////// +// // +// Alias Membership Lists. // +// // +///////////////////////////////////////////////////////////////////////////// + +typedef struct _SAMP_AL_REFERENCED_DOMAIN { + + ULONG DomainReference; + PSID DomainSid; + +} SAMP_AL_REFERENCED_DOMAIN, *PSAMP_AL_REFERENCED_DOMAIN; + +typedef struct _SAMP_AL_REFERENCED_DOMAIN_LIST { + + ULONG SRMaximumLength; + ULONG SRUsedLength; + ULONG MaximumCount; + ULONG UsedCount; + PSAMP_AL_REFERENCED_DOMAIN Domains; + +} SAMP_AL_REFERENCED_DOMAIN_LIST, *PSAMP_AL_REFERENCED_DOMAIN_LIST; + +typedef struct _SAMP_AL_SR_REFERENCED_DOMAIN { + + ULONG Length; + ULONG DomainReference; + SID DomainSid; + +} SAMP_AL_SR_REFERENCED_DOMAIN, *PSAMP_AL_SR_REFERENCED_DOMAIN; + +typedef struct _SAMP_AL_SR_REFERENCED_DOMAIN_LIST { + + ULONG SRMaximumLength; + ULONG SRUsedLength; + ULONG MaximumCount; + ULONG UsedCount; + SAMP_AL_SR_REFERENCED_DOMAIN Domains[ANYSIZE_ARRAY]; + +} SAMP_AL_SR_REFERENCED_DOMAIN_LIST, *PSAMP_AL_SR_REFERENCED_DOMAIN_LIST; + + +// +// The Alias Membership Lists are global data structures maintained by SAM +// to provide rapid retrieval of Alias membership information. There are +// two types of Lists, the Alias Member List which is used to retrieve members +// of Aliases and the Member Alias List which is used to retrieve the aliases +// that members belong to. A pair of these lists exists for each local +// SAM Domain (currently, the BUILTIN and Accounts domain are the only two) +// +// Currently, these lists are used as memory caches. They are generated at +// system boot from the information stored in the SAM Database in the Registry +// and SAM keeps them up to date when Alias memberships change. Thus SAM +// API which perform lookup/read operations can use these lists instead of +// accessing the Registry keys directly. At a future date, it may be possible +// to back up the lists directly to the Registry and make obsolete the current +// information for Alias membership stored there. Because these lists are +// used as caches, they can be invalidated when the going gets tough, in which +// case, API will read their information directly from the Registry. +// +// Alias Member List +// +// This is the 'Alias-to-Member' List. Given one or more Aliases, it is used to +// find their members. One of these lists exists for each local SAM Domain. +// The Alias Member List specifies all/ of the information describing aliases +// in the local SAM Domain. It is designed for fast retrieval of alias +// membership information for an account given the account's Sid. +// +// An Alias Member List is structured. For each Alias in the list, the accounts that +// are mebers of the Alias are classified by their Referenced Domain. If an +// account is a member of n aliases in the SAM Local Domain to which an Alias +// List relates, there will be n entries for the account in the Alias Member List - +// +// are classified by domain. If an AccountSid is a member of n aliases in a given SAM +// Local Domain, there are n entries for it in the Alias Member List. +// +// The structure of an Alias Member List consists of three levels. These are, from +// the top down: +// +// * The Alias Member List structure (SAMP_AL_ALIAS_LIST) +// +// The Alias Member List structure specifies all aliases in the local SAM Domain. +// One of these exists per local SAM domain. It contains a list of Alias +// structures. +// +// * The Alias structure +// +// One Alias structure exists for each alias in the local SAM Domain. An +// Alias structure contains an array of Domain structures. +// +// * The Domain structure +// +// The Domain structure describes a Domain which has one or more accounts +// belonging to one or more aliases in the local SAM domain. The structure +// contains a list of these member accounts. +// +// The entire Alias Member List is self relative, facilitating easy storage and +// retrieval from backing storage. +// + +typedef struct _SAMP_AL_DOMAIN { + + ULONG MaximumLength; + ULONG UsedLength; + ULONG DomainReference; + ULONG RidCount; + ULONG Rids[ANYSIZE_ARRAY]; + +} SAMP_AL_DOMAIN, *PSAMP_AL_DOMAIN; + +typedef struct _SAMP_AL_ALIAS { + + ULONG MaximumLength; + ULONG UsedLength; + ULONG AliasRid; + ULONG DomainCount; + SAMP_AL_DOMAIN Domains[ANYSIZE_ARRAY]; + +} SAMP_AL_ALIAS, *PSAMP_AL_ALIAS; + +typedef struct _SAMP_AL_ALIAS_MEMBER_LIST { + + ULONG MaximumLength; + ULONG UsedLength; + ULONG AliasCount; + ULONG DomainIndex; + ULONG Enabled; + SAMP_AL_ALIAS Aliases[ANYSIZE_ARRAY]; + +} SAMP_AL_ALIAS_MEMBER_LIST, *PSAMP_AL_ALIAS_MEMBER_LIST; + +// +// Member Alias List. +// +// This is the 'Member to Alias' List. Given one or more member account Sids, +// this list is used to find all the Aliases to which one or more of the +// members belongs. One Member Alias List exists for each local SAM Domain. +// The list contains all of the membership relationships for aliases in the +// Domain. The member accounts are grouped by sorted Rid within Domain +// Sid, and for each Rid the list contains an array of the Rids of the Aliases +// to which it belongs. +// +// This list is implemented in a Self-Relative format for easy backup and +// restore. For now, the list is being used simply as a cache, which is +// constructed at system load, and updated whenever membership relationships +// change. When the going gets tough, we just ditch the cache. Later, it +// may be desirable to save this list to a backing store (e.g. to a Registry +// Key) +// +// The list is implemented as a 3-tier hierarchy. These are described +// from the top down. +// +// Member Alias List (SAMP_AL_MEMBER_ALIAS_LIST) +// +// This top-level structure contains the list header. The list header +// contains a count of the Member Domains and also the DomainIndex of the +// SAM Local Domain to which the list relates. +// +// Member Domain +// +// One of these exists for each Domain that contains one or more accounts +// that are members of one or more Aliases in the SAM local Domain. +// +// Member Account +// +// One of these exists for each account that is a member of one or more +// Aliases in the SAM Local Domain. A Member Account structure specifies +// the Rid of the member and the Rid of the Aliases to which it belongs +// (only Aliases in the associated local SAM Domain are listed). +// + +typedef struct _SAMP_AL_MEMBER_ACCOUNT { + + ULONG Signature; + ULONG MaximumLength; + ULONG UsedLength; + ULONG Rid; + ULONG AliasCount; + ULONG AliasRids[ ANYSIZE_ARRAY]; + +} SAMP_AL_MEMBER_ACCOUNT, *PSAMP_AL_MEMBER_ACCOUNT; + +typedef struct _SAMP_AL_MEMBER_DOMAIN { + + ULONG Signature; + ULONG MaximumLength; + ULONG UsedLength; + ULONG RidCount; + SID DomainSid; + +} SAMP_AL_MEMBER_DOMAIN, *PSAMP_AL_MEMBER_DOMAIN; + +typedef struct _SAMP_AL_MEMBER_ALIAS_LIST { + + ULONG Signature; + ULONG MaximumLength; + ULONG UsedLength; + ULONG DomainIndex; + ULONG DomainCount; + SAMP_AL_MEMBER_DOMAIN MemberDomains[ANYSIZE_ARRAY]; + +} SAMP_AL_MEMBER_ALIAS_LIST, *PSAMP_AL_MEMBER_ALIAS_LIST; + +// +// Alias Information +// +// This is the top level structure which connects the Lists. One of these +// appears in the SAMP_DEFINED_DOMAINS structure. +// +// The connection between the lists is as follows +// +// SAMP_DEFINED_DOMAINS Contains SAMP_AL_ALIAS_INFORMATION +// +// SAMP_AL_ALIAS_INFORMATION contains pointers to +// SAMP_AL_ALIAS_MEMBER_LIST and SAMP_AL_MEMBER_ALIAS_LIST +// +// SAMP_AL_ALIAS_MEMBER_LIST and SAMP_AL_MEMBER_ALIAS_LIST contain +// the DomainIndex of the SAMP_DEFINED_DOMAINS structure. +// +// Thus it is possible to navigate from any list to any other. +// + +typedef struct _SAMP_AL_ALIAS_INFORMATION { + + BOOLEAN Valid; + UNICODE_STRING AliasMemberListKeyName; + UNICODE_STRING MemberAliasListKeyName; + + HANDLE AliasMemberListKeyHandle; + HANDLE MemberAliasListKeyHandle; + + PSAMP_AL_ALIAS_MEMBER_LIST AliasMemberList; + PSAMP_AL_MEMBER_ALIAS_LIST MemberAliasList; + + SAMP_AL_REFERENCED_DOMAIN_LIST ReferencedDomainList; + +} SAMP_AL_ALIAS_INFORMATION, *PSAMP_AL_ALIAS_INFORMATION; + +typedef struct _SAMP_AL_SPLIT_MEMBER_SID { + + ULONG Rid; + PSID DomainSid; + PSAMP_AL_MEMBER_DOMAIN MemberDomain; + +} SAMP_AL_SPLIT_MEMBER_SID, *PSAMP_AL_SPLIT_MEMBER_SID; + +typedef struct _SAMP_AL_SPLIT_MEMBER_SID_LIST { + + ULONG Count; + PSAMP_AL_SPLIT_MEMBER_SID Sids; + +} SAMP_AL_SPLIT_MEMBER_SID_LIST, *PSAMP_AL_SPLIT_MEMBER_SID_LIST; + + +///////////////////////////////////////////////////////////////////////////// +// // +// Information about each domain that is kept readily available in memory // +// // +///////////////////////////////////////////////////////////////////////////// + +typedef struct _PSAMP_DEFINED_DOMAINS { + + // + // This field contains a handle to a context open to the domain object. + // This handle can be used to reference in-memory copies of all + // attributes and is used when writing out changes to the object. + // + + PSAMP_OBJECT Context; + + // + // (Should keep the domain's security descriptor here) + // + + + + + // + // This field contains the SID of the domain. + // + + PSID Sid; + + // + // This field contains the external name of this domain. This is the + // name by which the domain is known outside SAM and is the name + // recorded by the LSA in the PolicyAccountDomainInformation + // information class for the Policy Object. + // + + UNICODE_STRING ExternalName; + + // + // This field contains the internal name of this domain. This is the + // name by which the domain is known inside SAM. It is set at + // installation and never changes. + // + + UNICODE_STRING InternalName; + + // + // These fields contain standard security descriptors for new user, + // group, and alias accounts within the corresponding domain. + // + // The following security descriptors are prepared: + // + // AdminUserSD - Contains a SD appropriate for applying to + // a user object that is a member of the ADMINISTRATORS + // alias. + // + // AdminGroupSD - Contains a SD appropriate for applying to + // a group object that is a member of the ADMINISTRATORS + // alias. + // + // NormalUserSD - Contains a SD appropriate for applying to + // a user object that is NOT a member of the ADMINISTRATORS + // alias. + // + // NormalGroupSD - Contains a SD appropriate for applying to + // a Group object that is NOT a member of the ADMINISTRATORS + // alias. + // + // NormalAliasSD - Contains a SD appropriate for applying to + // newly created alias objects. + // + // + // + // Additionally, the following related information is provided: + // + // AdminUserRidPointer + // NormalUserRidPointer + // + // Points to the last RID of the ACE in the corresponding + // SD's DACL which grants access to the user. This rid + // must be replaced with the user's rid being the SD is + // applied to the user object. + // + // + // + // AdminUserSDLength + // AdminGroupSDLength + // NormalUserSDLength + // NormalGroupSDLength + // NormalAliasSDLength + // + // The length, in bytes, of the corresponding security + // descriptor. + // + + PSECURITY_DESCRIPTOR + AdminUserSD, + AdminGroupSD, + NormalUserSD, + NormalGroupSD, + NormalAliasSD; + + PULONG AdminUserRidPointer, + NormalUserRidPointer; + + ULONG AdminUserSDLength, + AdminGroupSDLength, + NormalUserSDLength, + NormalGroupSDLength, + NormalAliasSDLength; + + + // + // Context blocks for open objects in this domain are kept in the + // following lists. Both valid and invalid objects are kept on the + // list. They are removed upon deletion. + // + + LIST_ENTRY GroupContextHead, + AliasContextHead, + UserContextHead; + + + // + // There are two copies of the fixed length domain information. + // When a transaction is started, the "UnmodifiedFixed" field is copied + // to the "CurrentFixed" field. The CurrentFixed field is the field + // all operations should be performed on (like allocating new RIDs). + // When a write-lock is released, the CurrentFixed information will + // either be automatically written out (if the transaction is to be + // committed) or discarded (if the transaction is to be rolled back). + // If the transaction is committed, then the CurrentField will also be + // copied to the UnmodifiedFixed field, making it available for the next + // transaction. + // + // This allows an operation to proceed, operating on fields + // (specifically, the NextRid and ModifiedCount fields) without + // regard to whether the operation will ultimately be committed or + // rolled back. + // + + SAMP_V1_0A_FIXED_LENGTH_DOMAIN + CurrentFixed, + UnmodifiedFixed; + + + // + // Cached display information + // + + SAMP_DOMAIN_DISPLAY_INFORMATION DisplayInformation; + + // + // Cached Alias Information + // + + SAMP_AL_ALIAS_INFORMATION AliasInformation; + +} SAMP_DEFINED_DOMAINS, *PSAMP_DEFINED_DOMAINS; + + + +///////////////////////////////////////////////////////////////////////// +// // +// This structure is used to describe where the data for // +// an object's variable length attribute is. This is a // +// self-relative structure, allowing it to be stored on disk // +// and later retrieved and used without fixing pointers. // +// // +///////////////////////////////////////////////////////////////////////// + + +typedef struct _SAMP_VARIABLE_LENGTH_ATTRIBUTE { + // + // Indicates the offset of data from the address of this data + // structure. + // + + LONG Offset; + + + // + // Indicates the length of the data. + // + + ULONG Length; + + + // + // A 32-bit value that may be associated with each variable + // length attribute. This may be used, for example, to indicate + // how many elements are in the variable-length attribute. + // + + ULONG Qualifier; + +} SAMP_VARIABLE_LENGTH_ATTRIBUTE, *PSAMP_VARIABLE_LENGTH_ATTRIBUTE; + + + + +///////////////////////////////////////////////////////////////////////// +// // +// The following structures represent the On-Disk Structure of each // +// object type. Each object has a fixed length data portion and a // +// variable length data portion. Information in the object type // +// descriptor indicates how many variable length attributes the object // +// has and whether the fixed and variable length data are stored // +// together in one registry key attribute, or, alternatively, each is // +// stored in its own registry key attribute. // +// // +// // +// // +// // +///////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////// +// // +// SERVER object on-disk structure // +// // +///////////////////////////////////////////////////////////////////////// + +typedef struct _SAMP_ON_DISK_SERVER_OBJECT { + + + // + // This field is needed for registry i/o operations. + // This marks the beginning of the i/o buffer address. + // + + KEY_VALUE_PARTIAL_INFORMATION Header1; + + + // + // This field contains the fixed length attributes of the object + // + + SAMP_V1_FIXED_LENGTH_SERVER V1Fixed; + + +#if SAMP_SERVER_STORED_SEPARATELY + + // + // This header is needed for registry operations if fixed and + // variable length attributes are stored separately. This + // field marks the beginning of the i/o buffer address for + // variable-length attribute i/o. + // + + KEY_VALUE_PARTIAL_INFORMATION Header2; +#endif //SAMP_SERVER_STORED_SEPARATELY + + // + // Elements of this array point to variable-length attribute + // values. + // + + SAMP_VARIABLE_LENGTH_ATTRIBUTE Attribute[SAMP_SERVER_VARIABLE_ATTRIBUTES]; + + +} SAMP_ON_DISK_SERVER_OBJECT, *PSAMP_ON_DISK_SERVER_OBJECT; + + + + +///////////////////////////////////////////////////////////////////////// +// // +// DOMAIN object on-disk structure // +// // +///////////////////////////////////////////////////////////////////////// + +typedef struct _SAMP_ON_DISK_DOMAIN_OBJECT { + + + // + // This field is needed for registry i/o operations. + // This marks the beginning of the i/o buffer address. + // + + KEY_VALUE_PARTIAL_INFORMATION Header1; + + + // + // This field contains the fixed length attributes of the object + // + + SAMP_V1_0A_FIXED_LENGTH_DOMAIN V1Fixed; + + +#if SAMP_DOMAIN_STORED_SEPARATELY + + // + // This header is needed for registry operations if fixed and + // variable length attributes are stored separately. This + // field marks the beginning of the i/o buffer address for + // variable-length attribute i/o. + // + + KEY_VALUE_PARTIAL_INFORMATION Header2; +#endif //SAMP_DOMAIN_STORED_SEPARATELY + + // + // Elements of this array point to variable-length attribute + // values. + // + + SAMP_VARIABLE_LENGTH_ATTRIBUTE Attribute[SAMP_DOMAIN_VARIABLE_ATTRIBUTES]; + + +} SAMP_ON_DISK_DOMAIN_OBJECT, *PSAMP_ON_DISK_DOMAIN_OBJECT; + + + +///////////////////////////////////////////////////////////////////////// +// // +// USER object on-disk structure // +// // +///////////////////////////////////////////////////////////////////////// + +typedef struct _SAMP_ON_DISK_USER_OBJECT { + + + // + // This field is needed for registry i/o operations. + // This marks the beginning of the i/o buffer address. + // + + KEY_VALUE_PARTIAL_INFORMATION Header1; + + + // + // This field contains the fixed length attributes of the object + // + + SAMP_V1_0A_FIXED_LENGTH_USER V1Fixed; + + +#if SAMP_USER_STORED_SEPARATELY + + // + // This header is needed for registry operations if fixed and + // variable length attributes are stored separately. This + // field marks the beginning of the i/o buffer address for + // variable-length attribute i/o. + // + + KEY_VALUE_PARTIAL_INFORMATION Header2; +#endif //SAMP_USER_STORED_SEPARATELY + + // + // Elements of this array point to variable-length attribute + // values. + // + + SAMP_VARIABLE_LENGTH_ATTRIBUTE Attribute[SAMP_USER_VARIABLE_ATTRIBUTES]; + + +} SAMP_ON_DISK_USER_OBJECT, *PSAMP_ON_DISK_USER_OBJECT; + + +///////////////////////////////////////////////////////////////////////// +// // +// GROUP object on-disk structure // +// // +///////////////////////////////////////////////////////////////////////// + +typedef struct _SAMP_ON_DISK_GROUP_OBJECT { + + + // + // This field is needed for registry i/o operations. + // This marks the beginning of the i/o buffer address. + // + + KEY_VALUE_PARTIAL_INFORMATION Header1; + + + // + // This field contains the fixed length attributes of the object + // + + SAMP_V1_0A_FIXED_LENGTH_GROUP V1Fixed; + + +#if SAMP_GROUP_STORED_SEPARATELY + + // + // This header is needed for registry operations if fixed and + // variable length attributes are stored separately. This + // field marks the beginning of the i/o buffer address for + // variable-length attribute i/o. + // + + KEY_VALUE_PARTIAL_INFORMATION Header2; +#endif //SAMP_GROUP_STORED_SEPARATELY + + // + // Elements of this array point to variable-length attribute + // values. + // + + SAMP_VARIABLE_LENGTH_ATTRIBUTE Attribute[SAMP_GROUP_VARIABLE_ATTRIBUTES]; + + +} SAMP_ON_DISK_GROUP_OBJECT, *PSAMP_ON_DISK_GROUP_OBJECT; + + +///////////////////////////////////////////////////////////////////////// +// // +// ALIAS object on-disk structure // +// // +///////////////////////////////////////////////////////////////////////// + +typedef struct _SAMP_ON_DISK_ALIAS_OBJECT { + + + // + // This field is needed for registry i/o operations. + // This marks the beginning of the i/o buffer address. + // + + KEY_VALUE_PARTIAL_INFORMATION Header1; + + + // + // This field contains the fixed length attributes of the object + // + + SAMP_V1_FIXED_LENGTH_ALIAS V1Fixed; + + +#if SAMP_ALIAS_STORED_SEPARATELY + + // + // This header is needed for registry operations if fixed and + // variable length attributes are stored separately. This + // field marks the beginning of the i/o buffer address for + // variable-length attribute i/o. + // + + KEY_VALUE_PARTIAL_INFORMATION Header2; +#endif //SAMP_ALIAS_STORED_SEPARATELY + + // + // Elements of this array point to variable-length attribute + // values. + // + + SAMP_VARIABLE_LENGTH_ATTRIBUTE Attribute[SAMP_ALIAS_VARIABLE_ATTRIBUTES]; + + +} SAMP_ON_DISK_ALIAS_OBJECT, *PSAMP_ON_DISK_ALIAS_OBJECT; + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Enumerated types for manipulating group memberships // +// // +/////////////////////////////////////////////////////////////////////////////// + +typedef enum _SAMP_MEMBERSHIP_DELTA { + AddToAdmin, + NoChange, + RemoveFromAdmin +} SAMP_MEMBERSHIP_DELTA, *PSAMP_MEMBERSHIP_DELTA; + + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// The following prototypes are usable throughout the process that SAM // +// resides in. THESE ROUTINES MUST NOT BE CALLED BY NON-SAM CODE ! // +// // +// // +/////////////////////////////////////////////////////////////////////////////// + +// +// SAM's shutdown notification routine +// + + +BOOL SampShutdownNotification( DWORD dwCtrlType ); + + +// +// Sub-Component initialization routines +// + +BOOLEAN SampInitializeDomainObject(VOID); + +NTSTATUS SampInitializeRegistry ( VOID ); + +NTSTATUS +SampReInitializeSingleDomain( + ULONG Index + ); + +// +// database lock related services +// + +VOID +SampAcquireReadLock(VOID); + +VOID +SampReleaseReadLock(VOID); + + +NTSTATUS +SampAcquireWriteLock( VOID ); + +VOID +SampSetTransactionDomain( + IN ULONG DomainIndex + ); + +NTSTATUS +SampCommitAndRetainWriteLock( + VOID + ); + +NTSTATUS +SampReleaseWriteLock( + IN BOOLEAN Commit + ); + + +// +// Context block manipulation services +// + +PSAMP_OBJECT +SampCreateContext( + IN SAMP_OBJECT_TYPE Type, + IN BOOLEAN TrustedClient + ); + +VOID +SampDeleteContext( + IN PSAMP_OBJECT Context + ); + +NTSTATUS +SampLookupContext( + IN PSAMP_OBJECT Context, + IN ACCESS_MASK DesiredAccess, + IN SAMP_OBJECT_TYPE ExpectedType, + OUT PSAMP_OBJECT_TYPE FoundType + ); + +VOID +SampReferenceContext( + IN PSAMP_OBJECT Context + ); + +NTSTATUS +SampDeReferenceContext( + IN PSAMP_OBJECT Context, + IN BOOLEAN Commit + ); + + +VOID +SampInvalidateContextAddress( + IN PSAMP_OBJECT Context + ); + +VOID +SampInvalidateGroupContexts( + IN ULONG Rid + ); + +VOID +SampInvalidateAliasContexts( + IN ULONG Rid + ); + + +VOID +SampInvalidateUserContexts( + IN ULONG Rid + ); + +VOID +SampInvalidateContextListKeys( + IN PLIST_ENTRY Head, + IN BOOLEAN Close + ); + +#ifdef SAMP_DBG_CONTEXT_TRACKING +VOID +SampDumpContexts( + VOID + ); +#endif + +// +// Unicode String related services - These use MIDL_user_allocate and +// MIDL_user_free so that the resultant strings can be given to the +// RPC runtime. +// + +NTSTATUS +SampInitUnicodeString( + OUT PUNICODE_STRING String, + IN USHORT MaximumLength + ); + +NTSTATUS +SampAppendUnicodeString( + IN OUT PUNICODE_STRING Target, + IN PUNICODE_STRING StringToAdd + ); + +VOID +SampFreeUnicodeString( + IN PUNICODE_STRING String + ); + +VOID +SampFreeOemString( + IN POEM_STRING String + ); + +NTSTATUS +SampDuplicateUnicodeString( + IN PUNICODE_STRING OutString, + IN PUNICODE_STRING InString + ); + +NTSTATUS +SampUnicodeToOemString( + IN POEM_STRING OutString, + IN PUNICODE_STRING InString + ); + +NTSTATUS +SampBuildDomainSubKeyName( + OUT PUNICODE_STRING KeyName, + IN PUNICODE_STRING SubKeyName OPTIONAL + ); + + +NTSTATUS +SampRetrieveStringFromRegistry( + IN HANDLE ParentKey, + IN PUNICODE_STRING SubKeyName, + OUT PUNICODE_STRING Body + ); + + +NTSTATUS +SampPutStringToRegistry( + IN BOOLEAN RelativeToDomain, + IN PUNICODE_STRING SubKeyName, + IN PUNICODE_STRING Body + ); + + +// +// user, group and alias Account services +// + + +NTSTATUS +SampBuildAccountKeyName( + IN SAMP_OBJECT_TYPE ObjectType, + OUT PUNICODE_STRING AccountKeyName, + IN PUNICODE_STRING AccountName + ); + +NTSTATUS +SampBuildAccountSubKeyName( + IN SAMP_OBJECT_TYPE ObjectType, + OUT PUNICODE_STRING AccountKeyName, + IN ULONG AccountRid, + IN PUNICODE_STRING SubKeyName OPTIONAL + ); + +NTSTATUS +SampBuildAliasMembersKeyName( + IN PSID AccountSid, + OUT PUNICODE_STRING DomainKeyName, + OUT PUNICODE_STRING AccountKeyName + ); + +NTSTATUS +SampValidateNewAccountName( + PUNICODE_STRING NewAccountName + ); + +NTSTATUS +SampValidateAccountNameChange( + IN PUNICODE_STRING NewAccountName, + IN PUNICODE_STRING OldAccountName + ); + +NTSTATUS +SampIsAccountBuiltIn( + ULONG Rid + ); + + + +NTSTATUS +SampAdjustAccountCount( + IN SAMP_OBJECT_TYPE ObjectType, + IN BOOLEAN Increment + ); + +NTSTATUS +SampRetrieveAccountCounts( + OUT PULONG UserCount, + OUT PULONG GroupCount, + OUT PULONG AliasCount + ); + + +NTSTATUS +SampEnumerateAccountNamesCommon( + IN SAMPR_HANDLE DomainHandle, + IN SAMP_OBJECT_TYPE ObjectType, + IN OUT PSAM_ENUMERATE_HANDLE EnumerationHandle, + OUT PSAMPR_ENUMERATION_BUFFER *Buffer, + IN ULONG PreferedMaximumLength, + IN ULONG Filter, + OUT PULONG CountReturned + ); + + +NTSTATUS +SampEnumerateAccountNames( + IN SAMP_OBJECT_TYPE ObjectType, + IN OUT PSAM_ENUMERATE_HANDLE EnumerationContext, + OUT PSAMPR_ENUMERATION_BUFFER *Buffer, + IN ULONG PreferedMaximumLength, + IN ULONG Filter, + OUT PULONG CountReturned, + IN BOOLEAN TrustedClient + ); + +VOID +SampFreeRestart( + IN PRESTART Restart + ); + +NTSTATUS +SampLookupAccountRid( + IN PSAMP_OBJECT DomainContext, + IN SAMP_OBJECT_TYPE ObjectType, + IN PUNICODE_STRING Name, + IN NTSTATUS NotFoundStatus, + OUT PULONG Rid, + OUT PSID_NAME_USE Use + ); + +NTSTATUS +SampLookupAccountRidRegistry( + IN SAMP_OBJECT_TYPE ObjectType, + IN PUNICODE_STRING Name, + IN NTSTATUS NotFoundStatus, + OUT PULONG Rid, + OUT PSID_NAME_USE Use + ); + +NTSTATUS +SampLookupAccountName( + IN ULONG Rid, + OUT PUNICODE_STRING Name OPTIONAL, + OUT PSAMP_OBJECT_TYPE ObjectType + ); + +NTSTATUS +SampOpenAccount( + IN SAMP_OBJECT_TYPE ObjectType, + IN SAMPR_HANDLE DomainHandle, + IN ACCESS_MASK DesiredAccess, + IN ULONG AccountId, + IN BOOLEAN WriteLockHeld, + OUT SAMPR_HANDLE *AccountHandle + ); + +NTSTATUS +SampCreateAccountContext( + IN SAMP_OBJECT_TYPE ObjectType, + IN ULONG AccountId, + IN BOOLEAN TrustedClient, + IN BOOLEAN AccountExists, + OUT PSAMP_OBJECT *AccountContext + ); + + +NTSTATUS +SampCreateAccountSid( + PSAMP_OBJECT AccountContext, + PSID *AccountSid + ); + +NTSTATUS +SampRetrieveGroupV1Fixed( + IN PSAMP_OBJECT GroupContext, + IN PSAMP_V1_0A_FIXED_LENGTH_GROUP V1Fixed + ); + +NTSTATUS +SampReplaceGroupV1Fixed( + IN PSAMP_OBJECT Context, + IN PSAMP_V1_0A_FIXED_LENGTH_GROUP V1Fixed + ); + +NTSTATUS +SampRetrieveUserV1aFixed( + IN PSAMP_OBJECT UserContext, + OUT PSAMP_V1_0A_FIXED_LENGTH_USER V1aFixed + ); + +NTSTATUS +SampReplaceUserV1aFixed( + IN PSAMP_OBJECT Context, + IN PSAMP_V1_0A_FIXED_LENGTH_USER V1aFixed + ); + +NTSTATUS +SampRetrieveGroupMembers( + IN PSAMP_OBJECT GroupContext, + IN PULONG MemberCount, + IN PULONG *Members OPTIONAL + ); + +NTSTATUS +SampChangeAccountOperatorAccessToMember( + IN PRPC_SID MemberSid, + IN SAMP_MEMBERSHIP_DELTA ChangingToAdmin, + IN SAMP_MEMBERSHIP_DELTA ChangingToOperator + ); + +NTSTATUS +SampChangeOperatorAccessToUser( + IN ULONG UserRid, + IN SAMP_MEMBERSHIP_DELTA ChangingToAdmin, + IN SAMP_MEMBERSHIP_DELTA ChangingToOperator + ); + +NTSTATUS +SampChangeOperatorAccessToUser2( + IN PSAMP_OBJECT UserContext, + IN PSAMP_V1_0A_FIXED_LENGTH_USER V1aFixed, + IN SAMP_MEMBERSHIP_DELTA AddingToAdmin, + IN SAMP_MEMBERSHIP_DELTA AddingToOperator + ); + +// +// Access validation and auditing related services +// + +NTSTATUS +SampValidateObjectAccess( + IN PSAMP_OBJECT Context, + IN ACCESS_MASK DesiredAccess, + IN BOOLEAN ObjectCreation + ); + +VOID +SampAuditOnClose( + IN PSAMP_OBJECT Context + ); + + +NTSTATUS +SampCreateNullToken( + ); + +// +// Authenticated RPC and SPX support services +// + + +ULONG +SampSecureRpcInit( + PVOID Ignored + ); + +BOOLEAN +SampStartNonNamedPipeTransports( + ); + + +// +// Notification package routines. +// + +NTSTATUS +SampPasswordChangeNotify( + PUNICODE_STRING UserName, + ULONG RelativeId, + PUNICODE_STRING NewPassword + ); + +NTSTATUS +SampLoadNotificationPackages( + ); + +NTSTATUS +SampDeltaChangeNotify( + IN PSID DomainSid, + IN SECURITY_DB_DELTA_TYPE DeltaType, + IN SECURITY_DB_OBJECT_TYPE ObjectType, + IN ULONG ObjectRid, + IN PUNICODE_STRING ObjectName, + IN PLARGE_INTEGER ModifiedCount, + IN PSAM_DELTA_DATA DeltaData OPTIONAL + ); + + + +// +// Security Descriptor production services +// + + +NTSTATUS +SampInitializeDomainDescriptors( + ULONG Index + ); + +NTSTATUS +SampGetNewAccountSecurity( + IN SAMP_OBJECT_TYPE ObjectType, + IN BOOLEAN Admin, + IN BOOLEAN TrustedClient, + IN BOOLEAN RestrictCreatorAccess, + IN ULONG NewAccountRid, + OUT PSECURITY_DESCRIPTOR *NewDescriptor, + OUT PULONG DescriptorLength + ); + +NTSTATUS +SampGetObjectSD( + IN PSAMP_OBJECT Context, + OUT PULONG SecurityDescriptorLength, + OUT PSECURITY_DESCRIPTOR *SecurityDescriptor + ); + + +NTSTATUS +SampModifyAccountSecurity( + IN SAMP_OBJECT_TYPE ObjectType, + IN BOOLEAN Admin, + IN PSECURITY_DESCRIPTOR OldDescriptor, + OUT PSECURITY_DESCRIPTOR *NewDescriptor, + OUT PULONG DescriptorLength + ); + + +// +// Group related services +// + +NTSTATUS +SampAddUserToGroup( + IN ULONG GroupRid, + IN ULONG UserRid + ); + +NTSTATUS +SampRemoveUserFromGroup( + IN ULONG GroupRid, + IN ULONG UserRid + ); + + +// +// Alias related services +// + +NTSTATUS +SampAlBuildAliasInformation( + ); + +NTSTATUS +SampAlQueryAliasMembership( + IN SAMPR_HANDLE DomainHandle, + IN PSAMPR_PSID_ARRAY SidArray, + OUT PSAMPR_ULONG_ARRAY Membership + ); + +NTSTATUS +SampAlQueryMembersOfAlias( + IN SAMPR_HANDLE AliasHandle, + OUT PSAMPR_PSID_ARRAY MemberSids + ); + +NTSTATUS +SampAlAddMembersToAlias( + IN SAMPR_HANDLE AliasHandle, + IN ULONG Options, + IN PSAMPR_PSID_ARRAY MemberSids + ); + +NTSTATUS +SampAlRemoveMembersFromAlias( + IN SAMPR_HANDLE AliasHandle, + IN ULONG Options, + IN PSAMPR_PSID_ARRAY MemberSids + ); + +NTSTATUS +SampAlLookupMembersInAlias( + IN SAMPR_HANDLE AliasHandle, + IN ULONG AliasRid, + IN PSAMPR_PSID_ARRAY MemberSids, + OUT PULONG MembershipCount + ); + +NTSTATUS +SampAlDeleteAlias( + IN SAMPR_HANDLE *AliasHandle + ); + +NTSTATUS +SampAlRemoveAccountFromAllAliases( + IN PSID AccountSid, + IN BOOLEAN CheckAccess, + IN SAMPR_HANDLE DomainHandle OPTIONAL, + IN PULONG MembershipCount OPTIONAL, + IN PULONG *Membership OPTIONAL + ); + +NTSTATUS +SampRetrieveAliasMembers( + IN PSAMP_OBJECT AliasContext, + IN PULONG MemberCount, + IN PSID **Members OPTIONAL + ); + + +NTSTATUS +SampRemoveAccountFromAllAliases( + IN PSID AccountSid, + IN BOOLEAN CheckAccess, + IN SAMPR_HANDLE DomainHandle OPTIONAL, + IN PULONG MembershipCount OPTIONAL, + IN PULONG *Membership OPTIONAL + ); + +NTSTATUS +SampAlSlowQueryAliasMembership( + IN SAMPR_HANDLE DomainHandle, + IN PSAMPR_PSID_ARRAY SidArray, + OUT PSAMPR_ULONG_ARRAY Membership + ); + +NTSTATUS +SampRetrieveAliasMembership( + IN PSID Account, + OUT PULONG MemberCount OPTIONAL, + IN OUT PULONG BufferSize OPTIONAL, + OUT PULONG Buffer OPTIONAL + ); + +// +// User related services +// + + +NTSTATUS +SampGetPrivateUserData( + PSAMP_OBJECT UserContext, + OUT PULONG DataLength, + OUT PVOID *Data + ); + +NTSTATUS +SampSetPrivateUserData( + PSAMP_OBJECT UserContext, + IN ULONG DataLength, + IN PVOID Data + ); +NTSTATUS +SampRetrieveUserGroupAttribute( + IN ULONG UserRid, + IN ULONG GroupRid, + OUT PULONG Attribute + ); + +NTSTATUS +SampAddGroupToUserMembership( + IN ULONG GroupRid, + IN ULONG Attributes, + IN ULONG UserRid, + IN SAMP_MEMBERSHIP_DELTA AdminGroup, + IN SAMP_MEMBERSHIP_DELTA OperatorGroup, + OUT PBOOLEAN UserActive + ); + +NTSTATUS +SampSetGroupAttributesOfUser( + IN ULONG GroupRid, + IN ULONG Attributes, + IN ULONG UserRid + ); + +NTSTATUS +SampRemoveMembershipUser( + IN ULONG GroupRid, + IN ULONG UserRid, + IN SAMP_MEMBERSHIP_DELTA AdminGroup, + IN SAMP_MEMBERSHIP_DELTA OperatorGroup, + OUT PBOOLEAN UserActive + ); + +BOOLEAN +SampStillInLockoutObservationWindow( + PSAMP_OBJECT UserContext, + PSAMP_V1_0A_FIXED_LENGTH_USER V1aFixed + ); + + +// +// Cached display information services +// + +NTSTATUS +SampInitializeDisplayInformation ( + ULONG DomainIndex + ); + +NTSTATUS +SampMarkDisplayInformationInvalid ( + SAMP_OBJECT_TYPE ObjectType + ); + +NTSTATUS +SampUpdateDisplayInformation ( + PSAMP_ACCOUNT_DISPLAY_INFO OldAccountInfo OPTIONAL, + PSAMP_ACCOUNT_DISPLAY_INFO NewAccountInfo OPTIONAL, + SAMP_OBJECT_TYPE ObjectType + ); + + +// +// Miscellaneous services +// + +LARGE_INTEGER +SampAddDeltaTime( + IN LARGE_INTEGER Time, + IN LARGE_INTEGER DeltaTime + ); + +NTSTATUS +SampCreateFullSid( + PSID DomainSid, + ULONG Rid, + PSID *AccountSid + ); + +NTSTATUS +SampSplitSid( + IN PSID AccountSid, + OUT PSID *DomainSid, + OUT ULONG *Rid + ); + +VOID +SampNotifyNetlogonOfDelta( + IN SECURITY_DB_DELTA_TYPE DeltaType, + IN SECURITY_DB_OBJECT_TYPE ObjectType, + IN ULONG ObjectRid, + IN PUNICODE_STRING ObjectName, + IN DWORD ReplicateImmediately, + IN PSAM_DELTA_DATA DeltaData OPTIONAL + ); + +VOID +SampWriteEventLog ( + IN USHORT EventType, + IN USHORT EventCategory OPTIONAL, + IN ULONG EventID, + IN PSID UserSid OPTIONAL, + IN USHORT NumStrings, + IN ULONG DataSize, + IN PUNICODE_STRING *Strings OPTIONAL, + IN PVOID Data OPTIONAL + ); + +NTSTATUS +SampGetAccountDomainInfo( + PPOLICY_ACCOUNT_DOMAIN_INFO *PolicyAccountDomainInfo + ); + +NTSTATUS +SampUpgradeSamDatabase( + ULONG Revision + ); + + +// +// Old RPC stub routine definitions used in SamIFree() +// + +void _fgs__RPC_UNICODE_STRING (RPC_UNICODE_STRING * _source); +void _fgs__SAMPR_RID_ENUMERATION (SAMPR_RID_ENUMERATION * _source); +void _fgs__SAMPR_ENUMERATION_BUFFER (SAMPR_ENUMERATION_BUFFER * _source); +void _fgs__SAMPR_SR_SECURITY_DESCRIPTOR (SAMPR_SR_SECURITY_DESCRIPTOR * _source); +void _fgs__SAMPR_GET_GROUPS_BUFFER (SAMPR_GET_GROUPS_BUFFER * _source); +void _fgs__SAMPR_GET_MEMBERS_BUFFER (SAMPR_GET_MEMBERS_BUFFER * _source); +void _fgs__SAMPR_LOGON_HOURS (SAMPR_LOGON_HOURS * _source); +void _fgs__SAMPR_ULONG_ARRAY (SAMPR_ULONG_ARRAY * _source); +void _fgs__SAMPR_SID_INFORMATION (SAMPR_SID_INFORMATION * _source); +void _fgs__SAMPR_PSID_ARRAY (SAMPR_PSID_ARRAY * _source); +void _fgs__SAMPR_RETURNED_USTRING_ARRAY (SAMPR_RETURNED_USTRING_ARRAY * _source); +void _fgs__SAMPR_DOMAIN_GENERAL_INFORMATION (SAMPR_DOMAIN_GENERAL_INFORMATION * _source); +void _fgs__SAMPR_DOMAIN_GENERAL_INFORMATION2 (SAMPR_DOMAIN_GENERAL_INFORMATION2 * _source); +void _fgs__SAMPR_DOMAIN_OEM_INFORMATION (SAMPR_DOMAIN_OEM_INFORMATION * _source); +void _fgs__SAMPR_DOMAIN_NAME_INFORMATION (SAMPR_DOMAIN_NAME_INFORMATION * _source); +void _fgs_SAMPR_DOMAIN_REPLICATION_INFORMATION (SAMPR_DOMAIN_REPLICATION_INFORMATION * _source); +void _fgu__SAMPR_DOMAIN_INFO_BUFFER (SAMPR_DOMAIN_INFO_BUFFER * _source, DOMAIN_INFORMATION_CLASS _branch); +void _fgu__SAMPR_GROUP_INFO_BUFFER (SAMPR_GROUP_INFO_BUFFER * _source, GROUP_INFORMATION_CLASS _branch); +void _fgu__SAMPR_ALIAS_INFO_BUFFER (SAMPR_ALIAS_INFO_BUFFER * _source, ALIAS_INFORMATION_CLASS _branch); +void _fgu__SAMPR_USER_INFO_BUFFER (SAMPR_USER_INFO_BUFFER * _source, USER_INFORMATION_CLASS _branch); +void _fgu__SAMPR_DISPLAY_INFO_BUFFER (SAMPR_DISPLAY_INFO_BUFFER * _source, DOMAIN_DISPLAY_INFORMATION _branch); + + + +// +// SAM object attribute manipulation services +// + + + +VOID +SampInitObjectInfoAttributes(); + +NTSTATUS +SampStoreObjectAttributes( + IN PSAMP_OBJECT Context, + IN BOOLEAN UseKeyHandle + ); + +NTSTATUS +SampDeleteAttributeKeys( + IN PSAMP_OBJECT Context + ); + +NTSTATUS +SampGetFixedAttributes( + IN PSAMP_OBJECT Context, + IN BOOLEAN MakeCopy, + OUT PVOID *FixedData + ); + +NTSTATUS +SampSetFixedAttributes( + IN PSAMP_OBJECT Context, + IN PVOID FixedData + ); + +NTSTATUS +SampGetUnicodeStringAttribute( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex, + IN BOOLEAN MakeCopy, + OUT PUNICODE_STRING UnicodeAttribute + ); + +NTSTATUS +SampSetUnicodeStringAttribute( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex, + IN PUNICODE_STRING Attribute + ); + +NTSTATUS +SampGetSidAttribute( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex, + IN BOOLEAN MakeCopy, + OUT PSID *Sid + ); + +NTSTATUS +SampSetSidAttribute( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex, + IN PSID Attribute + ); + +NTSTATUS +SampGetAccessAttribute( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex, + IN BOOLEAN MakeCopy, + OUT PULONG Revision, + OUT PSECURITY_DESCRIPTOR *SecurityDescriptor + ); + +NTSTATUS +SampSetAccessAttribute( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex, + IN PSECURITY_DESCRIPTOR Attribute, + IN ULONG Length + ); + +NTSTATUS +SampGetUlongArrayAttribute( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex, + IN BOOLEAN MakeCopy, + OUT PULONG *UlongArray, + OUT PULONG UsedCount, + OUT PULONG LengthCount + ); + +NTSTATUS +SampSetUlongArrayAttribute( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex, + IN PULONG Attribute, + IN ULONG UsedCount, + IN ULONG LengthCount + ); + +NTSTATUS +SampGetLargeIntArrayAttribute( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex, + IN BOOLEAN MakeCopy, + OUT PLARGE_INTEGER *LargeIntArray, + OUT PULONG Count + ); + +NTSTATUS +SampSetLargeIntArrayAttribute( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex, + IN PLARGE_INTEGER Attribute, + IN ULONG Count + ); + +NTSTATUS +SampGetSidArrayAttribute( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex, + IN BOOLEAN MakeCopy, + OUT PSID *SidArray, + OUT PULONG Length, + OUT PULONG Count + ); + +NTSTATUS +SampSetSidArrayAttribute( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex, + IN PSID Attribute, + IN ULONG Length, + IN ULONG Count + ); + +NTSTATUS +SampGetLogonHoursAttribute( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex, + IN BOOLEAN MakeCopy, + OUT PLOGON_HOURS LogonHours + ); + +NTSTATUS +SampSetLogonHoursAttribute( + IN PSAMP_OBJECT Context, + IN ULONG AttributeIndex, + IN PLOGON_HOURS Attribute + ); + +VOID +SampFreeAttributeBuffer( + IN PSAMP_OBJECT Context + ); + +NTSTATUS +SampRtlConvertUlongToUnicodeString( + IN ULONG Value, + IN ULONG Base OPTIONAL, + IN ULONG DigitCount, + IN BOOLEAN AllocateDestinationString, + OUT PUNICODE_STRING UnicodeString + ); + +NTSTATUS +SampRtlWellKnownPrivilegeCheck( + BOOLEAN ImpersonateClient, + IN ULONG PrivilegeId, + IN OPTIONAL PCLIENT_ID ClientId + ); + +// +// Functions to upgrade the SAM database and fix SAM bugs +// + +NTSTATUS +SampUpgradeSamDatabase( + ULONG Revision + ); + +///////////////////////////////////////////////////////////////////////// +// // +// 2-3 tree generic table routines // +// These should be moved to RTL directory if a general need arises. // +// // +///////////////////////////////////////////////////////////////////////// + + +VOID +RtlInitializeGenericTable2 ( + PRTL_GENERIC_TABLE2 Table, + PRTL_GENERIC_2_COMPARE_ROUTINE CompareRoutine, + PRTL_GENERIC_2_ALLOCATE_ROUTINE AllocateRoutine, + PRTL_GENERIC_2_FREE_ROUTINE FreeRoutine + ); + +PVOID +RtlInsertElementGenericTable2 ( + PRTL_GENERIC_TABLE2 Table, + PVOID Element, + PBOOLEAN NewElement + ); + +BOOLEAN +RtlDeleteElementGenericTable2 ( + PRTL_GENERIC_TABLE2 Table, + PVOID Element + ); + +PVOID +RtlLookupElementGenericTable2 ( + PRTL_GENERIC_TABLE2 Table, + PVOID Element + ); + +PVOID +RtlEnumerateGenericTable2 ( + PRTL_GENERIC_TABLE2 Table, + PVOID *RestartKey + ); + +PVOID +RtlRestartKeyByIndexGenericTable2( + PRTL_GENERIC_TABLE2 Table, + ULONG I, + PVOID *RestartKey + ); + +PVOID +RtlRestartKeyByValueGenericTable2( + PRTL_GENERIC_TABLE2 Table, + PVOID Element, + PVOID *RestartKey + ); + +ULONG +RtlNumberElementsGenericTable2( + PRTL_GENERIC_TABLE2 Table + ); + +BOOLEAN +RtlIsGenericTable2Empty ( + PRTL_GENERIC_TABLE2 Table + ); + +///////////////////////////////////////////////////////////////////////////// +// // +// DS Seeding routine implemented in seed.c // +// // +///////////////////////////////////////////////////////////////////////////// + +NTSTATUS +SampSeedDS( + WCHAR * NamePrefix, + ULONG NamePrefixLen + ); + +VOID +SampUnseedDs( + WCHAR * NamePrefix, + ULONG NamePrefixLen + ); + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Shared global variables // +// // +/////////////////////////////////////////////////////////////////////////////// + + +extern NT_PRODUCT_TYPE SampProductType; + +extern RTL_RESOURCE SampLock; +extern BOOLEAN SampTransactionWithinDomain; +extern ULONG SampTransactionDomainIndex; + +extern SAMP_SERVICE_STATE SampServiceState; + +extern BOOLEAN SampAccountAuditingEnabled; + +extern HANDLE SampKey; +extern PRTL_RXACT_CONTEXT SampRXactContext; + +extern SAMP_OBJECT_INFORMATION SampObjectInformation[ SampUnknownObjectType ]; + +extern ULONG SampActiveContextCount; +extern LIST_ENTRY SampContextListHead; + +extern ULONG SampDefinedDomainsCount; +extern PSAMP_DEFINED_DOMAINS SampDefinedDomains; +extern UNICODE_STRING SampFixedAttributeName; +extern UNICODE_STRING SampVariableAttributeName; +extern UNICODE_STRING SampCombinedAttributeName; + +extern UNICODE_STRING SampNameDomains; +extern UNICODE_STRING SampNameDomainGroups; +extern UNICODE_STRING SampNameDomainAliases; +extern UNICODE_STRING SampNameDomainAliasesMembers; +extern UNICODE_STRING SampNameDomainUsers; +extern UNICODE_STRING SampNameDomainAliasesNames; +extern UNICODE_STRING SampNameDomainGroupsNames; +extern UNICODE_STRING SampNameDomainUsersNames; + +extern UNICODE_STRING SampBackSlash; +extern UNICODE_STRING SampNullString; +extern UNICODE_STRING SampSamSubsystem; +extern UNICODE_STRING SampServerObjectName; + + +extern LARGE_INTEGER SampImmediatelyDeltaTime; +extern LARGE_INTEGER SampNeverDeltaTime; +extern LARGE_INTEGER SampHasNeverTime; +extern LARGE_INTEGER SampWillNeverTime; + +extern LM_OWF_PASSWORD SampNullLmOwfPassword; +extern NT_OWF_PASSWORD SampNullNtOwfPassword; + +extern TIME LastUnflushedChange; +extern BOOLEAN FlushThreadCreated; +extern BOOLEAN FlushImmediately; + +extern LONG SampFlushThreadMinWaitSeconds; +extern LONG SampFlushThreadMaxWaitSeconds; +extern LONG SampFlushThreadExitDelaySeconds; + +// +// Warning: these SIDs are only defined during the first boot of setup, +// when the code in bldsam3.c for building the SAM database, has been +// run. On a normal build they are both NULL. +// + +extern PSID SampBuiltinDomainSid; +extern PSID SampAccountDomainSid; + +extern PSID SampWorldSid; +extern PSID SampAnonymousSid; +extern PSID SampAdministratorUserSid; +extern PSID SampAdministratorsAliasSid; +extern HANDLE SampNullSessionToken; +extern BOOLEAN SampNetwareServerInstalled; +extern BOOLEAN SampIpServerInstalled; +extern BOOLEAN SampWriteLock; +extern UCHAR RootObjectName[]; + +#if SAMP_DIAGNOSTICS + +extern ULONG SampGlobalFlag; + +#endif //SAMP_DIAGNOSTICS + +#endif // _NTSAMP_ diff --git a/private/newsam2/server/samss.c b/private/newsam2/server/samss.c new file mode 100644 index 000000000..be311a2b4 --- /dev/null +++ b/private/newsam2/server/samss.c @@ -0,0 +1,1861 @@ +/*++ + +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); +} diff --git a/private/newsam2/server/secdescr.c b/private/newsam2/server/secdescr.c new file mode 100644 index 000000000..12fe24ab2 --- /dev/null +++ b/private/newsam2/server/secdescr.c @@ -0,0 +1,3087 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + SecDescr.c + +Abstract: + + This file contains services related to the establishment of and modification + of security descriptors for SAM objects. + + Note that there are a couple of special security descriptors that this routine + does not build. These are the security descriptors for the DOMAIN_ADMIN group, + the ADMIN user account, and the SAM object. For the first release, in which + creation of domains is not supported, the DOMAIN object's security descriptor + is also not created here. + + These security descriptors are built by the program that initializes a SAM + database. + + +Author: + + Jim Kelly (JimK) 14-Oct-1991 + +Environment: + + User Mode - Win32 + +Revision History: + + +--*/ + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include <samsrvp.h> + + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// private service prototypes // +// // +/////////////////////////////////////////////////////////////////////////////// + + + +NTSTATUS +SampValidatePassedSD( + IN ULONG Length, + IN PISECURITY_DESCRIPTOR PassedSD + ); + +NTSTATUS +SampCheckForDescriptorRestrictions( + IN PSAMP_OBJECT Context, + IN SAMP_OBJECT_TYPE ObjectType, + IN ULONG ObjectRid, + IN PISECURITY_DESCRIPTOR PassedSD + ); + +NTSTATUS +SampBuildSamProtection( + IN PSID WorldSid, + IN PSID AdminsAliasSid, + IN ULONG AceCount, + IN PSID AceSid[], + IN ACCESS_MASK AceMask[], + IN PGENERIC_MAPPING GenericMap, + IN BOOLEAN UserObject, + OUT PULONG DescriptorLength, + OUT PSECURITY_DESCRIPTOR *Descriptor, + OUT PULONG *RidToReplace OPTIONAL + ); + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Services available for use throughout SAM // +// // +/////////////////////////////////////////////////////////////////////////////// + + + +NTSTATUS +SampInitializeDomainDescriptors( + ULONG Index + ) +/*++ + + +Routine Description: + + This routine initializes security descriptors needed to protect + user, group, and alias objects. + + These security descriptors are placed in the SampDefinedDomains[] array. + + This routine expects all SIDs to be previously initialized. + + The following security descriptors are prepared: + + AdminUserSD - Contains a SD appropriate for applying to + a user object that is a member of the ADMINISTRATORS + alias. + + AdminGroupSD - Contains a SD appropriate for applying to + a group object that is a member of the ADMINISTRATORS + alias. + + NormalUserSD - Contains a SD appropriate for applying to + a user object that is NOT a member of the ADMINISTRATORS + alias. + + NormalGroupSD - Contains a SD appropriate for applying to + a Group object that is NOT a member of the ADMINISTRATORS + alias. + + NormalAliasSD - Contains a SD appropriate for applying to + newly created alias objects. + + + + Additionally, the following related information is provided: + + AdminUserRidPointer + NormalUserRidPointer + + Points to the last RID of the ACE in the corresponding + SD's DACL which grants access to the user. This rid + must be replaced with the user's rid being the SD is + applied to the user object. + + + + AdminUserSDLength + AdminGroupSDLength + NormalUserSDLength + NormalGroupSDLength + NormalAliasSDLength + + The length, in bytes, of the corresponding security + descriptor. + + + + +Arguments: + + Index - The index of the domain whose security descriptors are being + created. The Sid field of this domain's data structure is already + expected to be set. + +Return Value: + + STATUS_SUCCESS - The security descriptors have been successfully initialized. + + STATUS_INSUFFICIENT_RESOURCES - Heap could not be allocated to produce the needed + security descriptors. + +--*/ +{ + + NTSTATUS Status; + ULONG Size; + + PSID AceSid[10]; // Don't expect more than 10 ACEs in any of these. + ACCESS_MASK AceMask[10]; // Access masks corresponding to Sids + + GENERIC_MAPPING AliasMap = {ALIAS_READ, + ALIAS_WRITE, + ALIAS_EXECUTE, + ALIAS_ALL_ACCESS + }; + + GENERIC_MAPPING GroupMap = {GROUP_READ, + GROUP_WRITE, + GROUP_EXECUTE, + GROUP_ALL_ACCESS + }; + + GENERIC_MAPPING UserMap = {USER_READ, + USER_WRITE, + USER_EXECUTE, + USER_ALL_ACCESS + }; + + + SID_IDENTIFIER_AUTHORITY + BuiltinAuthority = SECURITY_NT_AUTHORITY; + + + ULONG AdminsSidBuffer[8], + AccountSidBuffer[8]; + + PSID AdminsAliasSid = &AdminsSidBuffer[0], + AccountAliasSid = &AccountSidBuffer[0], + AnySidInAccountDomain = NULL; + + SAMTRACE("SampInitializeDomainDescriptors"); + + + // + // Make sure the buffer we've alloted for the simple sids above + // are large enough. + // + // + // ADMINISTRATORS and ACCOUNT_OPERATORS aliases + // are is S-1-5-20-x (2 sub-authorities) + // + ASSERT( RtlLengthRequiredSid(2) <= ( 8 * sizeof(ULONG) ) ); + + + //////////////////////////////////////////////////////////////////////////////////// + // // + // Initialize the SIDs we'll need. + // // + //////////////////////////////////////////////////////////////////////////////////// + + + RtlInitializeSid( AdminsAliasSid, &BuiltinAuthority, 2 ); + *(RtlSubAuthoritySid( AdminsAliasSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID; + *(RtlSubAuthoritySid( AdminsAliasSid, 1 )) = DOMAIN_ALIAS_RID_ADMINS; + + RtlInitializeSid( AccountAliasSid, &BuiltinAuthority, 2 ); + *(RtlSubAuthoritySid( AccountAliasSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID; + *(RtlSubAuthoritySid( AccountAliasSid, 1 )) = DOMAIN_ALIAS_RID_ACCOUNT_OPS; + + // + // Initialize a SID that can be used to represent accounts + // in this domain. + // + // This is the same as the domain sid found in the DefinedDomains[] + // array except it has one more sub-authority. + // It doesn't matter what the value of the last RID is because it + // is always replaced before use. + // + + Size = RtlLengthSid( SampDefinedDomains[Index].Sid ) + sizeof(ULONG); + AnySidInAccountDomain = RtlAllocateHeap( RtlProcessHeap(), 0, Size); + ASSERT( AnySidInAccountDomain != NULL ); + Status = RtlCopySid( + Size, + AnySidInAccountDomain, + SampDefinedDomains[Index].Sid ); + ASSERT(NT_SUCCESS(Status)); + (*RtlSubAuthorityCountSid( AnySidInAccountDomain )) += 1; + + + + + + + + + /////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + // + // + // + // + // The following security is assigned to groups that are made + // members of the ADMINISTRATORS alias. + // + // + // Owner: Administrators Alias + // Group: Administrators Alias + // + // Dacl: Grant Grant + // WORLD Administrators + // (Execute | Read) GenericAll + // + // Sacl: Audit + // Success | Fail + // WORLD + // (Write | Delete | WriteDacl | AccessSystemSecurity) + // + // + // + // All other aliases and groups must be assigned the following + // security: + // + // Owner: Administrators Alias + // Group: Administrators Alias + // + // Dacl: Grant Grant Grant + // WORLD Administrators AccountOperators Alias + // (Execute | Read) GenericAll GenericAll + // + // Sacl: Audit + // Success | Fail + // WORLD + // (Write | Delete | WriteDacl | AccessSystemSecurity) + // + // + // + // + // + // The following security is assigned to users that are made a + // member of the ADMINISTRATORS alias. This includes direct + // inclusion or indirect inclusion through group membership. + // + // + // Owner: Administrators Alias + // Group: Administrators Alias + // + // Dacl: Grant Grant Grant + // WORLD Administrators User's SID + // (Execute | Read) GenericAll GenericWrite + // + // Sacl: Audit + // Success | Fail + // WORLD + // (Write | Delete | WriteDacl | AccessSystemSecurity) + // + // + // + // + // All other users must be assigned the following + // security: + // + // Owner: AccountOperators Alias + // Group: AccountOperators Alias + // + // Dacl: Grant Grant Grant Grant + // WORLD Administrators Account Operators Alias User's SID + // (Execute | Read) GenericAll GenericAll GenericWrite + // + // Sacl: Audit + // Success | Fail + // WORLD + // (Write | Delete | WriteDacl | AccessSystemSecurity) + // + // + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // + // Note that because we are going to cram these ACLs directly + // into the backing store, we must map the generic accesses + // beforehand. + // + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // + /////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + + + + + + // + // We're not particularly good about freeing memory on error + // conditions below. Generally speaking, if this doens't + // initialize correctly, the system is hosed. + // + + + // + // Normal Alias SD + // + + AceSid[0] = SampWorldSid; + AceMask[0] = (ALIAS_EXECUTE | ALIAS_READ); + + AceSid[1] = AdminsAliasSid; + AceMask[1] = (ALIAS_ALL_ACCESS); + + AceSid[2] = AccountAliasSid; + AceMask[2] = (ALIAS_ALL_ACCESS); + + + Status = SampBuildSamProtection( + SampWorldSid, // WorldSid + AdminsAliasSid, // AdminsAliasSid + 3, // AceCount + &AceSid[0], // AceSid array + &AceMask[0], // Ace Mask array + &AliasMap, // GenericMap + FALSE, // Not user object + &SampDefinedDomains[Index].NormalAliasSDLength, // Descriptor + &SampDefinedDomains[Index].NormalAliasSD, // Descriptor + NULL // RidToReplace + ); + if (!NT_SUCCESS(Status)) { + goto done; + } + + + + + + // + // Admin Group SD + // + + AceSid[0] = SampWorldSid; + AceMask[0] = (GROUP_EXECUTE | GROUP_READ); + + AceSid[1] = AdminsAliasSid; + AceMask[1] = (GROUP_ALL_ACCESS); + + + Status = SampBuildSamProtection( + SampWorldSid, // WorldSid + AdminsAliasSid, // AdminsAliasSid + 2, // AceCount + &AceSid[0], // AceSid array + &AceMask[0], // Ace Mask array + &GroupMap, // GenericMap + FALSE, // Not user object + &SampDefinedDomains[Index].AdminGroupSDLength, // Descriptor + &SampDefinedDomains[Index].AdminGroupSD, // Descriptor + NULL // RidToReplace + ); + if (!NT_SUCCESS(Status)) { + goto done; + } + + + + // + // Normal GROUP SD + // + + AceSid[0] = SampWorldSid; + AceMask[0] = (GROUP_EXECUTE | GROUP_READ); + + AceSid[1] = AdminsAliasSid; + AceMask[1] = (GROUP_ALL_ACCESS); + + AceSid[2] = AccountAliasSid; + AceMask[2] = (GROUP_ALL_ACCESS); + + + Status = SampBuildSamProtection( + SampWorldSid, // WorldSid + AdminsAliasSid, // AdminsAliasSid + 3, // AceCount + &AceSid[0], // AceSid array + &AceMask[0], // Ace Mask array + &GroupMap, // GenericMap + FALSE, // Not user object + &SampDefinedDomains[Index].NormalGroupSDLength, // Descriptor + &SampDefinedDomains[Index].NormalGroupSD, // Descriptor + NULL // RidToReplace + ); + if (!NT_SUCCESS(Status)) { + goto done; + } + + + + + // + // Admin User SD + // + + AceSid[0] = SampWorldSid; + AceMask[0] = (USER_EXECUTE | USER_READ); + + AceSid[1] = AdminsAliasSid; + AceMask[1] = (USER_ALL_ACCESS); + + AceSid[2] = AnySidInAccountDomain; + AceMask[2] = (USER_WRITE); + + + Status = SampBuildSamProtection( + SampWorldSid, // WorldSid + AdminsAliasSid, // AdminsAliasSid + 3, // AceCount + &AceSid[0], // AceSid array + &AceMask[0], // Ace Mask array + &UserMap, // GenericMap + TRUE, // Not user object + &SampDefinedDomains[Index].AdminUserSDLength, // Descriptor + &SampDefinedDomains[Index].AdminUserSD, // Descriptor + &SampDefinedDomains[Index].AdminUserRidPointer // RidToReplace + ); + if (!NT_SUCCESS(Status)) { + goto done; + } + + + + // + // Normal User SD + // + + AceSid[0] = SampWorldSid; + AceMask[0] = (USER_EXECUTE | USER_READ); + + AceSid[1] = AdminsAliasSid; + AceMask[1] = (USER_ALL_ACCESS); + + AceSid[2] = AccountAliasSid; + AceMask[2] = (USER_ALL_ACCESS); + + AceSid[3] = AnySidInAccountDomain; + AceMask[3] = (USER_WRITE); + + + Status = SampBuildSamProtection( + SampWorldSid, // WorldSid + AdminsAliasSid, // AdminsAliasSid + 4, // AceCount + &AceSid[0], // AceSid array + &AceMask[0], // Ace Mask array + &UserMap, // GenericMap + TRUE, // Not user object + &SampDefinedDomains[Index].NormalUserSDLength, // Descriptor + &SampDefinedDomains[Index].NormalUserSD, // Descriptor + &SampDefinedDomains[Index].NormalUserRidPointer // RidToReplace + ); + if (!NT_SUCCESS(Status)) { + goto done; + } + +done: + + + RtlFreeHeap( RtlProcessHeap(), 0, AnySidInAccountDomain ); + + + return(Status); + +} + + +NTSTATUS +SampBuildSamProtection( + IN PSID WorldSid, + IN PSID AdminsAliasSid, + IN ULONG AceCount, + IN PSID AceSid[], + IN ACCESS_MASK AceMask[], + IN PGENERIC_MAPPING GenericMap, + IN BOOLEAN UserObject, + OUT PULONG DescriptorLength, + OUT PSECURITY_DESCRIPTOR *Descriptor, + OUT PULONG *RidToReplace OPTIONAL + ) + +/*++ + + +Routine Description: + + This routine builds a self-relative security descriptor ready + to be applied to one of the SAM objects. + + If so indicated, a pointer to the last RID of the SID in the last + ACE of the DACL is returned and a flag set indicating that the RID + must be replaced before the security descriptor is applied to an object. + This is to support USER object protection, which must grant some + access to the user represented by the object. + + The owner and group of each security descriptor will be set + to: + + Owner: Administrators Alias + Group: Administrators Alias + + + The SACL of each of these objects will be set to: + + + Audit + Success | Fail + WORLD + (Write | Delete | WriteDacl | AccessSystemSecurity) + + + +Arguments: + + AceCount - The number of ACEs to be included in the DACL. + + AceSid - Points to an array of SIDs to be granted access by the DACL. + If the target SAM object is a User object, then the last entry + in this array is expected to be the SID of an account within the + domain with the last RID not yet set. The RID will be set during + actual account creation. + + AceMask - Points to an array of accesses to be granted by the DACL. + The n'th entry of this array corresponds to the n'th entry of + the AceSid array. These masks should not include any generic + access types. + + GenericMap - Points to a generic mapping for the target object type. + + + UserObject - Indicates whether the target SAM object is a User object + or not. If TRUE (it is a User object), then the resultant + protection will be set up indicating Rid replacement is necessary. + + + DescriptorLength - Receives the length of the resultant SD. + + Descriptor - Receives a pointer to the resultant SD. + + RidToReplace - Is required aif userObject is TRUE and will be set + to point to the user's RID. + + +Return Value: + + TBS. + +--*/ +{ + + NTSTATUS Status; + + SECURITY_DESCRIPTOR Absolute; + PSECURITY_DESCRIPTOR Relative; + PACL TmpAcl; + PACCESS_ALLOWED_ACE TmpAce; + PSID TmpSid; + ULONG Length, i; + PULONG RidLocation; + BOOLEAN IgnoreBoolean; + ACCESS_MASK MappedMask; + + SAMTRACE("SampBuildSamProtection"); + + // + // The approach is to set up an absolute security descriptor that + // looks like what we want and then copy it to make a self-relative + // security descriptor. + // + + + Status = RtlCreateSecurityDescriptor( + &Absolute, + SECURITY_DESCRIPTOR_REVISION1 + ); + ASSERT( NT_SUCCESS(Status) ); + + + + // + // Owner + // + + Status = RtlSetOwnerSecurityDescriptor (&Absolute, AdminsAliasSid, FALSE ); + ASSERT(NT_SUCCESS(Status)); + + + + // + // Group + // + + Status = RtlSetGroupSecurityDescriptor (&Absolute, AdminsAliasSid, FALSE ); + ASSERT(NT_SUCCESS(Status)); + + + + + // + // Discretionary ACL + // + // Calculate its length, + // Allocate it, + // Initialize it, + // Add each ACE + // Add it to the security descriptor + // + + Length = (ULONG)sizeof(ACL); + for (i=0; i<AceCount; i++) { + + Length += RtlLengthSid( AceSid[i] ) + + (ULONG)sizeof(ACCESS_ALLOWED_ACE) - + (ULONG)sizeof(ULONG); //Subtract out SidStart field length + } + + TmpAcl = RtlAllocateHeap( RtlProcessHeap(), 0, Length ); + ASSERT(TmpAcl != NULL); + + + Status = RtlCreateAcl( TmpAcl, Length, ACL_REVISION2); + ASSERT( NT_SUCCESS(Status) ); + + for (i=0; i<AceCount; i++) { + MappedMask = AceMask[i]; + RtlMapGenericMask( &MappedMask, GenericMap ); + Status = RtlAddAccessAllowedAce ( + TmpAcl, + ACL_REVISION2, + MappedMask, + AceSid[i] + ); + ASSERT( NT_SUCCESS(Status) ); + } + + Status = RtlSetDaclSecurityDescriptor (&Absolute, TRUE, TmpAcl, FALSE ); + ASSERT(NT_SUCCESS(Status)); + + + + + // + // Sacl + // + + + Length = (ULONG)sizeof(ACL) + + RtlLengthSid( WorldSid ) + + (ULONG)sizeof(SYSTEM_AUDIT_ACE) - + (ULONG)sizeof(ULONG); //Subtract out SidStart field length + TmpAcl = RtlAllocateHeap( RtlProcessHeap(), 0, Length ); + ASSERT(TmpAcl != NULL); + + Status = RtlCreateAcl( TmpAcl, Length, ACL_REVISION2); + ASSERT( NT_SUCCESS(Status) ); + + Status = RtlAddAuditAccessAce ( + TmpAcl, + ACL_REVISION2, + GenericMap->GenericWrite | DELETE | WRITE_DAC | ACCESS_SYSTEM_SECURITY, + WorldSid, + TRUE, //AuditSuccess, + TRUE //AuditFailure + ); + ASSERT( NT_SUCCESS(Status) ); + + Status = RtlSetSaclSecurityDescriptor (&Absolute, TRUE, TmpAcl, FALSE ); + ASSERT(NT_SUCCESS(Status)); + + + + + + + // + // Convert the Security Descriptor to Self-Relative + // + // Get the length needed + // Allocate that much memory + // Copy it + // Free the generated absolute ACLs + // + + Length = 0; + Status = RtlAbsoluteToSelfRelativeSD( &Absolute, NULL, &Length ); + ASSERT(Status == STATUS_BUFFER_TOO_SMALL); + + Relative = RtlAllocateHeap( RtlProcessHeap(), 0, Length ); + ASSERT(Relative != NULL); + Status = RtlAbsoluteToSelfRelativeSD(&Absolute, Relative, &Length ); + ASSERT(NT_SUCCESS(Status)); + + + RtlFreeHeap( RtlProcessHeap(), 0, Absolute.Dacl ); + RtlFreeHeap( RtlProcessHeap(), 0, Absolute.Sacl ); + + + + + // + // If the object is a user object, then get the address of the + // last RID of the SID in the last ACE in the DACL. + // + + if (UserObject == TRUE) { + + Status = RtlGetDaclSecurityDescriptor( + Relative, + &IgnoreBoolean, + &TmpAcl, + &IgnoreBoolean + ); + ASSERT(NT_SUCCESS(Status)); + Status = RtlGetAce ( TmpAcl, AceCount-1, (PVOID *)&TmpAce ); + ASSERT(NT_SUCCESS(Status)); + TmpSid = (PSID)(&TmpAce->SidStart), + + RidLocation = RtlSubAuthoritySid( + TmpSid, + (ULONG)(*RtlSubAuthorityCountSid( TmpSid ) - 1) + ); + } + + + + + + + + // + // Set the result information + // + + (*DescriptorLength) = Length; + (*Descriptor) = Relative; + if (ARGUMENT_PRESENT(RidToReplace)) { + (*RidToReplace) = RidLocation; + } + + + + return(Status); + +} + + + +NTSTATUS +SampGetNewAccountSecurity( + IN SAMP_OBJECT_TYPE ObjectType, + IN BOOLEAN Admin, + IN BOOLEAN TrustedClient, + IN BOOLEAN RestrictCreatorAccess, + IN ULONG NewAccountRid, + OUT PSECURITY_DESCRIPTOR *NewDescriptor, + OUT PULONG DescriptorLength + ) + + +/*++ + +Routine Description: + + This service creates a standard self-relative security descriptor + for a new USER, GROUP or ALIAS account. + + + Note: THIS ROUTINE REFERENCES THE CURRENT TRANSACTION DOMAIN + (ESTABLISHED USING SampSetTransactioDomain()). THIS + SERVICE MAY ONLY BE CALLED AFTER SampSetTransactionDomain() + AND BEFORE SampReleaseWriteLock(). + + +Arguments: + + ObjectType - Indicates the type of account for which a new security + descriptor is required. This must be either SampGroupObjectType + or SampUserObjectType. + + Admin - if TRUE, indicates the security descriptor will be protecting + an object that is an admin object (e.g., is a member, directly + or indirectly, of the ADMINISTRATORS alias). + + TrustedClient - Indicates whether the client is a trusted client + or not. TRUE indicates the client is trusted, FALSE indicates + the client is not trusted. + + RestrictCreatorAccess - Indicates whether or not the creator's + access to the object is to be restricted according to + specific rules. Also indicates whether or not the account + is to be given any access to itself. An account will only + be given access to itself if there are no creator access + restrictions. + + The following ObjectTypes have restriction rules that may + be requested: + + User: + - Admin is assigned as owner of the object. + - Creator is given (DELETE | USER_WRITE) access. + + + NewAccountRid - The relative ID of the new account. + + NewDescriptor - Receives a pointer to the new account's self-relative + security descriptor. Be sure to free this descriptor with + MIDL_user_free() when done. + + DescriptorLength - Receives the length (in bytes) of the returned + security descriptor + + +Return Value: + + STATUS_SUCCESS - A new security descriptor has been produced. + + STATUS_INSUFFICIENT_RESOURCES - Memory could not be allocated to + produce the security descriptor. + + + +--*/ + +{ + SID_IDENTIFIER_AUTHORITY BuiltinAuthority = SECURITY_NT_AUTHORITY; + ULONG AccountSidBuffer[8]; + PSID AccountAliasSid = &AccountSidBuffer[0]; + + SECURITY_DESCRIPTOR DaclDescriptor; + NTSTATUS NtStatus = STATUS_SUCCESS; + NTSTATUS IgnoreStatus; + HANDLE ClientToken = INVALID_HANDLE_VALUE; + ULONG DataLength = 0; + ACCESS_ALLOWED_ACE *NewAce = NULL; + PACL NewDacl = NULL; + PACL OldDacl = NULL; + PSECURITY_DESCRIPTOR StaticDescriptor = NULL; + PSECURITY_DESCRIPTOR LocalDescriptor = NULL; + PTOKEN_GROUPS ClientGroups = NULL; + PTOKEN_OWNER SubjectOwner = NULL; + PSID SubjectSid = NULL; + ULONG AceLength = 0; + ULONG i; + BOOLEAN AdminAliasFound = FALSE; + BOOLEAN AccountAliasFound = FALSE; + BOOLEAN DaclPresent, DaclDefaulted; + + GENERIC_MAPPING GenericMapping; + GENERIC_MAPPING AliasMap = {ALIAS_READ, + ALIAS_WRITE, + ALIAS_EXECUTE, + ALIAS_ALL_ACCESS + }; + + GENERIC_MAPPING GroupMap = {GROUP_READ, + GROUP_WRITE, + GROUP_EXECUTE, + GROUP_ALL_ACCESS + }; + + GENERIC_MAPPING UserMap = {USER_READ, + USER_WRITE, + USER_EXECUTE, + USER_ALL_ACCESS + }; + + SAMTRACE("SampGetNewAccountSecurity"); + + // + // Security account objects don't pick up security in the normal + // fashion in the release 1 timeframe. They are assigned a well-known + // security descriptor based upon their object type. + // + // Notice that all the accounts with tricky security are created when + // the domain is created (e.g., admin groups and admin user account). + // + + switch (ObjectType) { + + case SampGroupObjectType: + + ASSERT(RestrictCreatorAccess == FALSE); + + // + // NewAccountRid parameter is ignored for groups. + // + + if (Admin == TRUE) { + + StaticDescriptor = + SampDefinedDomains[SampTransactionDomainIndex].AdminGroupSD; + (*DescriptorLength) = + SampDefinedDomains[SampTransactionDomainIndex].AdminGroupSDLength; + } else { + + StaticDescriptor = + SampDefinedDomains[SampTransactionDomainIndex].NormalGroupSD; + (*DescriptorLength) = + SampDefinedDomains[SampTransactionDomainIndex].NormalGroupSDLength; + } + + GenericMapping = GroupMap; + + break; + + + case SampAliasObjectType: + + ASSERT(RestrictCreatorAccess == FALSE); + + // + // Admin and NewAccountRid parameters are ignored for aliases. + // + + StaticDescriptor = + SampDefinedDomains[SampTransactionDomainIndex].NormalAliasSD; + (*DescriptorLength) = + SampDefinedDomains[SampTransactionDomainIndex].NormalAliasSDLength; + + GenericMapping = AliasMap; + + break; + + + case SampUserObjectType: + + if (Admin == TRUE) { + + StaticDescriptor = + SampDefinedDomains[SampTransactionDomainIndex].AdminUserSD; + (*DescriptorLength) = + SampDefinedDomains[SampTransactionDomainIndex].AdminUserSDLength; + (*SampDefinedDomains[SampTransactionDomainIndex].AdminUserRidPointer) + = NewAccountRid; + + } else { + + StaticDescriptor = + SampDefinedDomains[SampTransactionDomainIndex].NormalUserSD; + (*DescriptorLength) = + SampDefinedDomains[SampTransactionDomainIndex].NormalUserSDLength; + (*SampDefinedDomains[SampTransactionDomainIndex].NormalUserRidPointer) + = NewAccountRid; + } + + GenericMapping = UserMap; + + break; + + } + + // + // We have a pointer to SAM's static security descriptor. Copy it + // into a heap buffer that RtlSetSecurityObject() will like. + // + + LocalDescriptor = RtlAllocateHeap( RtlProcessHeap(), 0, (*DescriptorLength) ); + + if ( LocalDescriptor == NULL ) { + + (*NewDescriptor) = NULL; + (*DescriptorLength) = 0; + + return(STATUS_INSUFFICIENT_RESOURCES); + } + + RtlCopyMemory( + LocalDescriptor, + StaticDescriptor, + (*DescriptorLength) + ); + + // + // If the caller is to have restricted access to this account, + // then remove the last ACE from the ACL (the one intended for + // the account itself). + // + + if (RestrictCreatorAccess) { + NtStatus = RtlGetDaclSecurityDescriptor( + LocalDescriptor, + &DaclPresent, + &OldDacl, + &DaclDefaulted + ); + ASSERT(NT_SUCCESS(NtStatus)); + ASSERT(DaclPresent); + ASSERT(OldDacl->AceCount >= 1); + + OldDacl->AceCount -= 1; // Remove the last ACE from the ACL. + } + + + // + // If the caller is not a trusted client, see if the caller is an + // administrator or an account operator. If not, add an ACCESS_ALLOWED + // ACE to the DACL that gives full access to the creator (or restricted + // access, if so specified). + // + + if ( !TrustedClient ) { + + NtStatus = I_RpcMapWin32Status(RpcImpersonateClient( NULL )); + + if (NT_SUCCESS(NtStatus)) { // if (ImpersonatingClient) + + NtStatus = NtOpenThreadToken( + NtCurrentThread(), + TOKEN_QUERY, + TRUE, //OpenAsSelf + &ClientToken + ); + + // + // Stop impersonating the client + // + + IgnoreStatus = I_RpcMapWin32Status(RpcRevertToSelf()); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + if (NT_SUCCESS(NtStatus)) { // if (TokenOpened) + + + + + // + // See if the caller is an administrator or an account + // operator. First, see how big + // a buffer we need to hold the caller's groups. + // + + NtStatus = NtQueryInformationToken( + ClientToken, + TokenGroups, + NULL, + 0, + &DataLength + ); + + if ( ( NtStatus == STATUS_BUFFER_TOO_SMALL ) && + ( DataLength > 0 ) ) { + + ClientGroups = MIDL_user_allocate( DataLength ); + + if ( ClientGroups == NULL ) { + + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + + } else { + + // + // Now get a list of the caller's groups. + // + + NtStatus = NtQueryInformationToken( + ClientToken, + TokenGroups, + ClientGroups, + DataLength, + &DataLength + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + + // + // Build the SID of the ACCOUNT_OPS alias, so we + // can see if the user is included in it. + // + + RtlInitializeSid( + AccountAliasSid, + &BuiltinAuthority, + 2 ); + + *(RtlSubAuthoritySid( AccountAliasSid, 0 )) = + SECURITY_BUILTIN_DOMAIN_RID; + + *(RtlSubAuthoritySid( AccountAliasSid, 1 )) = + DOMAIN_ALIAS_RID_ACCOUNT_OPS; + + // + // See if the ADMIN or ACCOUNT_OPS alias is in + // the caller's groups. + // + + for ( i = 0; i < ClientGroups->GroupCount; i++ ) { + + SubjectSid = ClientGroups->Groups[i].Sid; + ASSERT( SubjectSid != NULL ); + + if ( RtlEqualSid( SubjectSid, SampAdministratorsAliasSid ) ) { + + AdminAliasFound = TRUE; + break; + } + if ( RtlEqualSid( SubjectSid, AccountAliasSid ) ) { + + AccountAliasFound = TRUE; + break; + } + } + + // + // If the callers groups did not include the admins + // alias, add an ACCESS_ALLOWED ACE for the owner. + // + + if ( !AdminAliasFound && !AccountAliasFound ) { + + // + // First, find out what size buffer we need + // to get the owner. + // + + NtStatus = NtQueryInformationToken( + ClientToken, + TokenOwner, + NULL, + 0, + &DataLength + ); + + if ( ( NtStatus == STATUS_BUFFER_TOO_SMALL ) && + ( DataLength > 0 ) ) { + + SubjectOwner = MIDL_user_allocate( DataLength ); + + if ( SubjectOwner == NULL ) { + + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + + } else { + + // + // Now, query the owner that will be + // given access to the object + // created. + // + + NtStatus = NtQueryInformationToken( + ClientToken, + TokenOwner, + SubjectOwner, + DataLength, + &DataLength + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // Create an ACE that gives the + // owner full access. + // + + AceLength = sizeof( ACE_HEADER ) + + sizeof( ACCESS_MASK ) + + RtlLengthSid( + SubjectOwner->Owner ); + + NewAce = (ACCESS_ALLOWED_ACE *) + MIDL_user_allocate( AceLength ); + + if ( NewAce == NULL ) { + + NtStatus = + STATUS_INSUFFICIENT_RESOURCES; + + } else { + + NewAce->Header.AceType = + ACCESS_ALLOWED_ACE_TYPE; + + NewAce->Header.AceSize = (USHORT) AceLength; + NewAce->Header.AceFlags = 0; + NewAce->Mask = USER_ALL_ACCESS; + + // + // If the creator's access is + // to be restricted, change the + // AccessMask. + // + + if (RestrictCreatorAccess) { + NewAce->Mask = DELETE | + USER_WRITE | + USER_FORCE_PASSWORD_CHANGE; + } + + RtlCopySid( + RtlLengthSid( + SubjectOwner->Owner ), + (PSID)( &NewAce->SidStart ), + SubjectOwner->Owner ); + + // + // Allocate a new, larger ACL and + // copy the old one into it. + // + + NtStatus = + RtlGetDaclSecurityDescriptor( + LocalDescriptor, + &DaclPresent, + &OldDacl, + &DaclDefaulted + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + NewDacl = MIDL_user_allocate( + OldDacl->AclSize + + AceLength ); + + if ( NewDacl == NULL ) { + + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + + } else { + + RtlCopyMemory( + NewDacl, + OldDacl, + OldDacl->AclSize + ); + + NewDacl->AclSize = + OldDacl->AclSize + + (USHORT) AceLength; + + // + // Add the new ACE + // to the new ACL. + // + + NtStatus = RtlAddAce( + NewDacl, + ACL_REVISION2, + 1, // add after first ACE (world) + (PVOID)NewAce, + AceLength + ); + } // end_if (allocated NewDacl) + } // end_if (get DACL from SD) + } // end_if (allocated NewAce) + } // end_if (Query TokenOwner Succeeded) + } // end_if (Allocated TokenOwner buffer) + } // end_if (Query TokenOwner size Succeeded) + } // end_if (not admin) + } // end_if (Query TokenGroups Succeeded) + } // end_if (Allocated TokenGroups buffer) + } // end_if (Query TokenGroups size Succeeded) + + IgnoreStatus = NtClose( ClientToken ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + } // end_if (TokenOpened) + } // end_if (ImpersonatingClient) + } // end_if (TrustedClient) + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // If we created a new DACL above, stick it on the security + // descriptor. + // + + if ( NewDacl != NULL ) { + + NtStatus = RtlCreateSecurityDescriptor( + &DaclDescriptor, + SECURITY_DESCRIPTOR_REVISION1 + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // Set the DACL on the LocalDescriptor. Note that this + // call will RtlFreeHeap() the old descriptor, and allocate + // a new one. + // + + DaclDescriptor.Control = SE_DACL_PRESENT; + DaclDescriptor.Dacl = NewDacl; + + NtStatus = RtlSetSecurityObject( + DACL_SECURITY_INFORMATION, + &DaclDescriptor, + &LocalDescriptor, + &GenericMapping, + NULL + ); + } + } + } + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // Copy the security descriptor and length into buffers for the + // caller. AceLength is 0 if we didn't add an ACE to the DACL + // above. + // + + (*DescriptorLength) = (*DescriptorLength) + AceLength; + + (*NewDescriptor) = MIDL_user_allocate( (*DescriptorLength) ); + + if ( (*NewDescriptor) == NULL ) { + + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + + } else { + + RtlCopyMemory( + (*NewDescriptor), + LocalDescriptor, + (*DescriptorLength) + ); + } + } + + // + // Free up local items that may have been allocated. + // + + if ( LocalDescriptor != NULL ) { + RtlFreeHeap( RtlProcessHeap(), 0, LocalDescriptor ); + } + + if ( ClientGroups != NULL ) { + MIDL_user_free( ClientGroups ); + } + + if ( SubjectOwner != NULL ) { + MIDL_user_free( SubjectOwner ); + } + + if ( NewAce != NULL ) { + MIDL_user_free( NewAce ); + } + + if ( NewDacl != NULL ) { + MIDL_user_free( NewDacl ); + } + + + if ( !NT_SUCCESS( NtStatus ) ) { + + (*NewDescriptor) = NULL; + (*DescriptorLength) = 0; + } + + return( NtStatus ); +} + + +NTSTATUS +SampModifyAccountSecurity( + IN SAMP_OBJECT_TYPE ObjectType, + IN BOOLEAN Admin, + IN PSECURITY_DESCRIPTOR OldDescriptor, + OUT PSECURITY_DESCRIPTOR *NewDescriptor, + OUT PULONG DescriptorLength + ) +/*++ + +Routine Description: + + This service modifies a self-relative security descriptor + for a USER or GROUP to add or remove account operator access. + + +Arguments: + + ObjectType - Indicates the type of account for which a new security + descriptor is required. This must be either SampGroupObjectType + or SampUserObjectType. + + Admin - if TRUE, indicates the security descriptor will be protecting + an object that is an admin object (e.g., is a member, directly + or indirectly, of the ADMINISTRATORS or an operator alias). + + NewDescriptor - Receives a pointer to the new account's self-relative + security descriptor. Be sure to free this descriptor with + MIDL_user_free() when done. + + DescriptorLength - Receives the length (in bytes) of the returned + security descriptor + + +Return Value: + + STATUS_SUCCESS - A new security descriptor has been produced. + + STATUS_INSUFFICIENT_RESOURCES - Memory could not be allocated to + produce the security descriptor. + + + +--*/ + +{ + SID_IDENTIFIER_AUTHORITY BuiltinAuthority = SECURITY_NT_AUTHORITY; + ULONG AccountSidBuffer[8]; + PSID AccountAliasSid = &AccountSidBuffer[0]; + NTSTATUS NtStatus = STATUS_SUCCESS; + NTSTATUS IgnoreStatus; + ULONG Length; + ULONG i,j; + ULONG AccountOpAceIndex; + ULONG AceCount; + PACL OldDacl; + PACL NewDacl = NULL; + BOOLEAN DaclDefaulted; + BOOLEAN DaclPresent; + ACL_SIZE_INFORMATION AclSizeInfo; + PACCESS_ALLOWED_ACE Ace; + PGENERIC_MAPPING GenericMapping; + ACCESS_MASK AccountOpAccess; + SECURITY_DESCRIPTOR AbsoluteDescriptor; + PSECURITY_DESCRIPTOR LocalDescriptor = NULL; + + GENERIC_MAPPING GroupMap = {GROUP_READ, + GROUP_WRITE, + GROUP_EXECUTE, + GROUP_ALL_ACCESS + }; + + GENERIC_MAPPING UserMap = {USER_READ, + USER_WRITE, + USER_EXECUTE, + USER_ALL_ACCESS + }; + + SAMTRACE("SampModifyAccountSecurity"); + + + NtStatus = RtlCopySecurityDescriptor( + OldDescriptor, + &LocalDescriptor + ); + + if (!NT_SUCCESS(NtStatus)) { + goto Cleanup; + } + + // + // Build the SID of the ACCOUNT_OPS alias, so we + // can see if is in the DACL or we can add it to the DACL. + // + + RtlInitializeSid( + AccountAliasSid, + &BuiltinAuthority, + 2 + ); + + *(RtlSubAuthoritySid( AccountAliasSid, 0 )) = + SECURITY_BUILTIN_DOMAIN_RID; + + *(RtlSubAuthoritySid( AccountAliasSid, 1 )) = + DOMAIN_ALIAS_RID_ACCOUNT_OPS; + + // + // The approach is to set up an absolute security descriptor that + // contains the new DACL, and then merge that into the existing + // security descriptor. + // + + + IgnoreStatus = RtlCreateSecurityDescriptor( + &AbsoluteDescriptor, + SECURITY_DESCRIPTOR_REVISION1 + ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + // + // Figure out the access granted to account operators and the + // generic mask to use. + // + + if (ObjectType == SampUserObjectType) { + AccountOpAccess = USER_ALL_ACCESS; + GenericMapping = &UserMap; + } else if (ObjectType == SampGroupObjectType) { + AccountOpAccess = GROUP_ALL_ACCESS; + GenericMapping = &GroupMap; + } else { + // + // This doesn't apply to aliases, domains, or servers. + // + return(STATUS_INVALID_PARAMETER); + } + + // + // Get the old DACL off the passed in security descriptor. + // + + IgnoreStatus = RtlGetDaclSecurityDescriptor( + OldDescriptor, + &DaclPresent, + &OldDacl, + &DaclDefaulted + ); + + ASSERT(NT_SUCCESS(IgnoreStatus)); + + // + // We will only modify the DACL if it is present + // + + if (!DaclPresent) { + *NewDescriptor = LocalDescriptor; + *DescriptorLength = RtlLengthSecurityDescriptor(LocalDescriptor); + return(STATUS_SUCCESS); + } + + // + // Get the count of ACEs + // + + IgnoreStatus = RtlQueryInformationAcl( + OldDacl, + &AclSizeInfo, + sizeof(AclSizeInfo), + AclSizeInformation + ); + + + ASSERT(NT_SUCCESS(IgnoreStatus)); + + // + // Calculate the lenght of the new ACL. + // + + Length = (ULONG)sizeof(ACL); + AccountOpAceIndex = 0xffffffff; + + + for (i = 0; i < AclSizeInfo.AceCount; i++) { + IgnoreStatus = RtlGetAce( + OldDacl, + i, + (PVOID *) &Ace + ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + // + // Check if this is an access allowed ACE, and the ACE is for + // the Account Operators alias. + // + + if ( (Ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE) && + RtlEqualSid( AccountAliasSid, + &Ace->SidStart ) ) { + + AccountOpAceIndex = i; + continue; + } + Length += Ace->Header.AceSize; + } + + + if (!Admin) { + + // + // If we are making this account not be an admin account and it already + // has an account operator ace, we are done. + // + + if ( AccountOpAceIndex != 0xffffffff ) { + + *NewDescriptor = LocalDescriptor; + *DescriptorLength = RtlLengthSecurityDescriptor(LocalDescriptor); + return(STATUS_SUCCESS); + } else { + + // + // Add the size of an account operator ace to the required length + // + + Length += sizeof(ACCESS_ALLOWED_ACE) + + RtlLengthSid(AccountAliasSid) - + sizeof(ULONG); + } + + } + + NewDacl = RtlAllocateHeap( RtlProcessHeap(), 0, Length ); + + if (NewDacl == NULL) { + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + goto Cleanup; + } + + IgnoreStatus = RtlCreateAcl( NewDacl, Length, ACL_REVISION2); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + + // + // Add the old ACEs back into this ACL. + // + + for (i = 0, j = 0; i < AclSizeInfo.AceCount; i++) { + if (i == AccountOpAceIndex) { + ASSERT(Admin); + continue; + } + // + // Add back in the old ACEs + // + + IgnoreStatus = RtlGetAce( + OldDacl, + i, + (PVOID *) &Ace + ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + IgnoreStatus = RtlAddAce ( + NewDacl, + ACL_REVISION2, + j, + Ace, + Ace->Header.AceSize + ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + } + + // + // If we are making this account not be an administrator, add the + // access allowed ACE for the account operator. This ACE is always + // the second to last one. + // + + if (!Admin) { + IgnoreStatus = RtlAddAccessAllowedAce( + NewDacl, + ACL_REVISION2, + AccountOpAccess, + AccountAliasSid + ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + // + // Insert this DACL into the security descriptor. + // + + IgnoreStatus = RtlSetDaclSecurityDescriptor ( + &AbsoluteDescriptor, + TRUE, // DACL present + NewDacl, + FALSE // DACL not defaulted + ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + // + // Now call RtlSetSecurityObject to merge the existing security descriptor + // with the new DACL we just created. + // + + + NtStatus = RtlSetSecurityObject( + DACL_SECURITY_INFORMATION, + &AbsoluteDescriptor, + &LocalDescriptor, + GenericMapping, + NULL + ); + if (!NT_SUCCESS(NtStatus)) { + goto Cleanup; + } + *NewDescriptor = LocalDescriptor; + *DescriptorLength = RtlLengthSecurityDescriptor(LocalDescriptor); + LocalDescriptor = NULL; +Cleanup: + + if ( NewDacl != NULL ) { + RtlFreeHeap(RtlProcessHeap(),0, NewDacl ); + } + if (LocalDescriptor != NULL) { + RtlDeleteSecurityObject(&LocalDescriptor); + } + + return( NtStatus ); +} + + + +NTSTATUS +SampGetObjectSD( + IN PSAMP_OBJECT Context, + OUT PULONG SecurityDescriptorLength, + OUT PSECURITY_DESCRIPTOR *SecurityDescriptor + ) + +/*++ + +Routine Description: + + This retrieves a security descriptor from a SAM object's backing store. + + + + +Arguments: + + Context - The object to which access is being requested. + + SecurityDescriptorLength - Receives the length of the security descriptor. + + SecurityDescriptor - Receives a pointer to the security descriptor. + + + +Return Value: + + STATUS_SUCCESS - The security descriptor has been retrieved. + + STATUS_INTERNAL_DB_CORRUPTION - The object does not have a security descriptor. + This is bad. + + + STATUS_INSUFFICIENT_RESOURCES - Memory could not be allocated to retrieve the + security descriptor. + + STATUS_UNKNOWN_REVISION - The security descriptor retrieved is no one known by + this revision of SAM. + + + +--*/ +{ + + NTSTATUS NtStatus; + ULONG Revision; + + SAMTRACE("SampGetObjectSD"); + + + (*SecurityDescriptorLength) = 0; + + NtStatus = SampGetAccessAttribute( + Context, + SAMP_OBJECT_SECURITY_DESCRIPTOR, + TRUE, // Make copy + &Revision, + SecurityDescriptor + ); + + if (NT_SUCCESS(NtStatus)) { + + if ( ((Revision && 0xFFFF0000) > SAMP_MAJOR_REVISION) || + (Revision > SAMP_REVISION) ) { + + NtStatus = STATUS_UNKNOWN_REVISION; + } + + + if (!NT_SUCCESS(NtStatus)) { + MIDL_user_free( (*SecurityDescriptor) ); + } + } + + + if (NT_SUCCESS(NtStatus)) { + *SecurityDescriptorLength = RtlLengthSecurityDescriptor( + (*SecurityDescriptor) ); + } + + return(NtStatus); +} + + + +NTSTATUS +SamrSetSecurityObject( + IN SAMPR_HANDLE ObjectHandle, + IN SECURITY_INFORMATION SecurityInformation, + IN PSAMPR_SR_SECURITY_DESCRIPTOR SecurityDescriptor + ) + +/*++ + +Routine Description: + + This function (SamrSetSecurityObject) takes a well formed Security + Descriptor provided by the caller and assigns specified portions of + it to an object. Based on the flags set in the SecurityInformation + parameter and the caller's access rights, this procedure will + replace any or all of the security information associated with an + object. + + This is the only function available to users and applications for + changing security information, including the owner ID, group ID, and + the discretionary and system ACLs of an object. The caller must + have WRITE_OWNER access to the object to change the owner or primary + group of the object. The caller must have WRITE_DAC access to the + object to change the discretionary ACL. The caller must have + ACCESS_SYSTEM_SECURITY access to an object to assign a system ACL + to the object. + + This API is modelled after the NtSetSecurityObject() system service. + + +Parameters: + + ObjectHandle - A handle to an existing object. + + SecurityInformation - Indicates which security information is to + be applied to the object. The value(s) to be assigned are + passed in the SecurityDescriptor parameter. + + + SecurityDescriptor - A pointer to a well formed self-relative Security + Descriptor and corresponding length. + + +Return Values: + + STATUS_SUCCESS - normal, successful completion. + + STATUS_ACCESS_DENIED - The specified handle was not opened for + either WRITE_OWNER, WRITE_DAC, or ACCESS_SYSTEM_SECURITY + access. + + STATUS_INVALID_HANDLE - The specified handle is not that of an + opened SAM object. + + STATUS_BAD_DESCRIPTOR_FORMAT - Indicates something about security descriptor + is not valid. This may indicate that the structure of the descriptor is + not valid or that a component of the descriptor specified via the + SecurityInformation parameter is not present in the security descriptor. + + STATUS_INVALID_PARAMETER - Indicates no security information was specified. + + STATUS_LAST_ADMIN - Indicates the new SD could potentially lead + to the administrator account being unusable and therefore + the new protection is being rejected. + +--*/ +{ + + NTSTATUS NtStatus, IgnoreStatus, TmpStatus; + PSAMP_OBJECT Context; + SAMP_OBJECT_TYPE FoundType; + SECURITY_DB_OBJECT_TYPE SecurityDbObjectType; + ACCESS_MASK DesiredAccess; + PSECURITY_DESCRIPTOR RetrieveSD, SetSD; + PISECURITY_DESCRIPTOR PassedSD; + ULONG RetrieveSDLength; + ULONG ObjectRid; + ULONG SecurityDescriptorIndex; + HANDLE ClientToken; + BOOLEAN NotificationType = TRUE; + + + SAMTRACE("SamrSetSecurityObject"); + + + + + // + // Make sure we understand what RPC is doing for (to) us. + // + + if (SecurityDescriptor == NULL) { + return(STATUS_BAD_DESCRIPTOR_FORMAT); + } + if (SecurityDescriptor->SecurityDescriptor == NULL) { + return(STATUS_BAD_DESCRIPTOR_FORMAT); + } + + PassedSD = (PISECURITY_DESCRIPTOR)(SecurityDescriptor->SecurityDescriptor); + + + // + // Validate the passed security descriptor + // + + NtStatus = SampValidatePassedSD( SecurityDescriptor->Length, PassedSD ); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + + + // + // Set the desired access based upon the specified SecurityInformation + // + + DesiredAccess = 0; + if ( SecurityInformation & SACL_SECURITY_INFORMATION) { + DesiredAccess |= ACCESS_SYSTEM_SECURITY; + } + if ( SecurityInformation & (OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION)) { + DesiredAccess |= WRITE_OWNER; + } + if ( SecurityInformation & DACL_SECURITY_INFORMATION ) { + DesiredAccess |= WRITE_DAC; + } + + + // + // If no information was specified, then return invalid parameter. + // + if (DesiredAccess == 0) { + + return(STATUS_INVALID_PARAMETER); + } + + + // + // Make sure the specified fields are present in the provided security descriptor. + // You can't screw up an SACL or DACL, but you can screw up an owner or group. + // Security descriptors must have owner and group fields. + // + + + if ( (SecurityInformation & OWNER_SECURITY_INFORMATION) ) { + if (PassedSD->Owner == NULL) { + return(STATUS_BAD_DESCRIPTOR_FORMAT); + } + } + + + if ( (SecurityInformation & GROUP_SECURITY_INFORMATION) ) { + if (PassedSD->Group == NULL) { + return(STATUS_BAD_DESCRIPTOR_FORMAT); + } + } + + + + + // + // See if the handle is valid and opened for the requested access + // + + + NtStatus = SampAcquireWriteLock(); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + Context = (PSAMP_OBJECT)ObjectHandle; + NtStatus = SampLookupContext( + Context, + DesiredAccess, + SampUnknownObjectType, // ExpectedType + &FoundType + ); + + switch ( FoundType ) { + + case SampServerObjectType: { + + SecurityDescriptorIndex = SAMP_SERVER_SECURITY_DESCRIPTOR; + ObjectRid = 0L; + NotificationType = FALSE; + break; + } + + case SampDomainObjectType: { + + // + // BUGBUG Bug #4388 - allow account operators to access pre-340 + // databases + // + // This fix should NEVER be checked in. It is for one-time + // internal use only. + // + // Pre-340 databases had security descriptors on the domain + // objects that didn't allow account operators to operate on + // accounts. Replicating one of these old databases to a + // newer release copies the bad security descriptors. To avoid + // this, do the following ONE TIME only: + // + // Uncomment the code immediately below. Build a new + // SAMSRV.DLL. Put it on a BDC running the target version + // of the system. Replicate the pre-340 database from the + // PDC (which may be running a more recent version of the + // system, but still has the bad pre-340 descriptors from + // earlier replications). + // + // That's it; you've got a fixed database that can be safely + // replicated in the future. Get rid of this temporary code. + // (And put the real SAMSRV.DLL back on the BDC). + // + // IgnoreStatus = SampDeReferenceContext( Context, FALSE ); + // IgnoreStatus = SampReleaseWriteLock( FALSE ); + // return( STATUS_SUCCESS ); + // + + SecurityDbObjectType = SecurityDbObjectSamDomain; + SecurityDescriptorIndex = SAMP_DOMAIN_SECURITY_DESCRIPTOR; + ObjectRid = 0L; + break; + } + + case SampUserObjectType: { + + SecurityDbObjectType = SecurityDbObjectSamUser; + SecurityDescriptorIndex = SAMP_USER_SECURITY_DESCRIPTOR; + ObjectRid = Context->TypeBody.User.Rid; + break; + } + + case SampGroupObjectType: { + + SecurityDbObjectType = SecurityDbObjectSamGroup; + SecurityDescriptorIndex = SAMP_GROUP_SECURITY_DESCRIPTOR; + ObjectRid = Context->TypeBody.Group.Rid; + break; + } + + case SampAliasObjectType: { + + SecurityDbObjectType = SecurityDbObjectSamAlias; + SecurityDescriptorIndex = SAMP_ALIAS_SECURITY_DESCRIPTOR; + ObjectRid = Context->TypeBody.Alias.Rid; + break; + } + + default: { + + NotificationType = FALSE; + } + } + + if (NT_SUCCESS(NtStatus)) { + + + // + // Get the security descriptor + // + + + RetrieveSD = NULL; + RetrieveSDLength = 0; + NtStatus = SampGetObjectSD( Context, &RetrieveSDLength, &RetrieveSD); + + // + // Make sure the descriptor does not break any Administrator + // restrictions. + // + + NtStatus = SampCheckForDescriptorRestrictions( Context, + FoundType, + ObjectRid, + PassedSD ); + + if (NT_SUCCESS(NtStatus)) { + + // + // copy the retrieved descriptor into process heap so we can use RTL routines. + // + + SetSD = NULL; + if (NT_SUCCESS(NtStatus)) { + + SetSD = RtlAllocateHeap( RtlProcessHeap(), 0, RetrieveSDLength ); + if ( SetSD == NULL) { + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + } else { + RtlCopyMemory( SetSD, RetrieveSD, RetrieveSDLength ); + } + } + + if (NT_SUCCESS(NtStatus)) { + + // + // if the caller is replacing the owner and he is not + // trusted, then a handle to the impersonation token is + // necessary. If the caller is trusted then take process + // token. + // + + ClientToken = 0; + if ( (SecurityInformation & OWNER_SECURITY_INFORMATION) ) { + + if(!Context->TrustedClient) { + + NtStatus = I_RpcMapWin32Status(RpcImpersonateClient( NULL )); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = NtOpenThreadToken( + NtCurrentThread(), + TOKEN_QUERY, + TRUE, //OpenAsSelf + &ClientToken + ); + ASSERT( (ClientToken == 0) || NT_SUCCESS(NtStatus) ); + + + + // + // Stop impersonating the client + // + + IgnoreStatus = I_RpcMapWin32Status(RpcRevertToSelf()); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + } + } + else { + + // + // trusted client + // + + NtStatus = NtOpenProcessToken( + NtCurrentProcess(), + TOKEN_QUERY, + &ClientToken ); + + ASSERT( (ClientToken == 0) || NT_SUCCESS(NtStatus) ); + + } + + } + + if (NT_SUCCESS(NtStatus)) { + + + // + // Build the replacement security descriptor. + // This must be done in process heap to satisfy the needs of the RTL + // routine. + // + + + NtStatus = RtlSetSecurityObject( + SecurityInformation, + PassedSD, + &SetSD, + &SampObjectInformation[FoundType].GenericMapping, + ClientToken + ); + if (ClientToken != 0) { + IgnoreStatus = NtClose( ClientToken ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + + + if (NT_SUCCESS(NtStatus)) { + + // + // Apply the security descriptor back onto the object. + // + + NtStatus = SampSetAccessAttribute( + Context, + SecurityDescriptorIndex, + SetSD, + RtlLengthSecurityDescriptor(SetSD) + ); + } + + } + + + + } + + + + + // + // Free up allocated memory + // + + if (RetrieveSD != NULL) { + MIDL_user_free( RetrieveSD ); + } + if (SetSD != NULL) { + RtlFreeHeap( RtlProcessHeap(), 0, SetSD ); + } + + + + + // + // De-reference the object + // + + if ( NT_SUCCESS( NtStatus ) ) { + + NtStatus = SampDeReferenceContext( Context, TRUE ); + + } else { + + IgnoreStatus = SampDeReferenceContext( Context, FALSE ); + } + } + + } //end_if + + + + // + // Commit the changes to disk. + // + + if ( NT_SUCCESS( NtStatus ) ) { + + NtStatus = SampCommitAndRetainWriteLock(); + + if ( NotificationType && NT_SUCCESS( NtStatus ) ) { + + SampNotifyNetlogonOfDelta( + SecurityDbChange, + SecurityDbObjectType, + ObjectRid, + (PUNICODE_STRING) NULL, + (DWORD) FALSE, // Replicate immediately + NULL // Delta data + ); + } + } + + + + // + // Release lock and propagate errors + // + + TmpStatus = SampReleaseWriteLock( FALSE ); + + if (NT_SUCCESS(NtStatus)) { + NtStatus = TmpStatus; + } + + + return(NtStatus); + + +} + +NTSTATUS +SampValidatePassedSD( + IN ULONG Length, + IN PISECURITY_DESCRIPTOR PassedSD + ) + +/*++ + +Routine Description: + + This routine validates that a passed security descriptor is valid and does + not extend beyond its expressed length. + + +Parameters: + + Length - The length of the security descriptor. This should be what RPC + used to allocate memory to receive the security descriptor. + + PassedSD - Points to the security descriptor to inspect. + + +Return Values: + + STATUS_SUCCESS - The security descriptor is valid. + + STATUS_BAD_DESCRIPTOR_FORMAT - Something was wrong with the security + descriptor. It might have extended beyond its limits or had an + invalid component. + + + +--*/ +{ + NTSTATUS NtStatus; + + PACL Acl; + PSID Sid; + PUCHAR SDEnd; + BOOLEAN Present, IgnoreBoolean; + + SAMTRACE("SampValidatePassedSD"); + + + if (Length < SECURITY_DESCRIPTOR_MIN_LENGTH) { + return(STATUS_BAD_DESCRIPTOR_FORMAT); + } + + SDEnd = (PUCHAR)PassedSD + Length; + + + try { + + + // + // Make sure the DACL is within the SD + // + + NtStatus = RtlGetDaclSecurityDescriptor( + (PSECURITY_DESCRIPTOR)PassedSD, + &Present, + &Acl, + &IgnoreBoolean + ); + if (!NT_SUCCESS(NtStatus)) { + return( STATUS_BAD_DESCRIPTOR_FORMAT ); + } + + if (Present) { + if (Acl != NULL) { + + // + // Make sure the ACl header is in the buffer. + // + + if ( (((PUCHAR)Acl)+sizeof(ACL) > SDEnd) || + (((PUCHAR)Acl) < (PUCHAR)PassedSD) ) { + return( STATUS_BAD_DESCRIPTOR_FORMAT ); + } + + // + // Make sure the rest of the ACL is within the buffer + // + + if ( ((PUCHAR)Acl)+Acl->AclSize > SDEnd) { + return( STATUS_BAD_DESCRIPTOR_FORMAT ); + } + + // + // Make sure the rest of the ACL is valid + // + + if (!RtlValidAcl( Acl )) { + return( STATUS_BAD_DESCRIPTOR_FORMAT ); + } + } + } + + + + // + // Make sure the SACL is within the SD + // + + NtStatus = RtlGetSaclSecurityDescriptor( + (PSECURITY_DESCRIPTOR)PassedSD, + &Present, + &Acl, + &IgnoreBoolean + ); + if (!NT_SUCCESS(NtStatus)) { + return( STATUS_BAD_DESCRIPTOR_FORMAT ); + } + + if (Present) { + if (Acl != NULL) { + + // + // Make sure the ACl header is in the buffer. + // + + if ( (((PUCHAR)Acl)+sizeof(ACL) > SDEnd) || + (((PUCHAR)Acl) < (PUCHAR)PassedSD) ) { + return( STATUS_BAD_DESCRIPTOR_FORMAT ); + } + + // + // Make sure the rest of the ACL is within the buffer + // + + if ( ((PUCHAR)Acl)+Acl->AclSize > SDEnd) { + return( STATUS_BAD_DESCRIPTOR_FORMAT ); + } + + // + // Make sure the rest of the ACL is valid + // + + if (!RtlValidAcl( Acl )) { + return( STATUS_BAD_DESCRIPTOR_FORMAT ); + } + } + } + + + // + // Make sure the Owner SID is within the SD + // + + NtStatus = RtlGetOwnerSecurityDescriptor( + (PSECURITY_DESCRIPTOR)PassedSD, + &Sid, + &IgnoreBoolean + ); + if (!NT_SUCCESS(NtStatus)) { + return( STATUS_BAD_DESCRIPTOR_FORMAT ); + } + + if (Sid != NULL) { + + // + // Make sure the SID header is in the SD + // + + if ( (((PUCHAR)Sid)+sizeof(SID)-(ANYSIZE_ARRAY*sizeof(ULONG)) > SDEnd) || + (((PUCHAR)Sid) < (PUCHAR)PassedSD) ) { + return( STATUS_BAD_DESCRIPTOR_FORMAT ); + } + + + // + // Make sure there aren't too many sub-authorities + // + + if (((PISID)Sid)->SubAuthorityCount > SID_MAX_SUB_AUTHORITIES) { + return( STATUS_BAD_DESCRIPTOR_FORMAT ); + } + + + // + // Make sure the rest of the SID is within the SD + // + + if ( ((PUCHAR)Sid)+RtlLengthSid(Sid) > SDEnd) { + return( STATUS_BAD_DESCRIPTOR_FORMAT ); + } + + } + + + + // + // Make sure the Group SID is within the SD + // + + NtStatus = RtlGetGroupSecurityDescriptor( + (PSECURITY_DESCRIPTOR)PassedSD, + &Sid, + &IgnoreBoolean + ); + if (!NT_SUCCESS(NtStatus)) { + return( STATUS_BAD_DESCRIPTOR_FORMAT ); + } + + if (Sid != NULL) { + + // + // Make sure the SID header is in the SD + // + + if ( (((PUCHAR)Sid)+sizeof(SID)-(ANYSIZE_ARRAY*sizeof(ULONG)) > SDEnd) || + (((PUCHAR)Sid) < (PUCHAR)PassedSD) ) { + return( STATUS_BAD_DESCRIPTOR_FORMAT ); + } + + + // + // Make sure there aren't too many sub-authorities + // + + if (((PISID)Sid)->SubAuthorityCount > SID_MAX_SUB_AUTHORITIES) { + return( STATUS_BAD_DESCRIPTOR_FORMAT ); + } + + + // + // Make sure the rest of the SID is within the SD + // + + if ( ((PUCHAR)Sid)+RtlLengthSid(Sid) > SDEnd) { + return( STATUS_BAD_DESCRIPTOR_FORMAT ); + } + + } + + + + + } except(EXCEPTION_EXECUTE_HANDLER) { + return( STATUS_BAD_DESCRIPTOR_FORMAT ); + } // end_try + + + + + return(STATUS_SUCCESS); +} + +NTSTATUS +SampCheckForDescriptorRestrictions( + IN PSAMP_OBJECT Context, + IN SAMP_OBJECT_TYPE ObjectType, + IN ULONG ObjectRid, + IN PISECURITY_DESCRIPTOR PassedSD + ) + +/*++ + +Routine Description: + + This function ensures that the passed security descriptor, + which is being applied to an object of type 'FoundType' with + a Rid of value 'ObjectRid', does not violate any policies. + For example, you can not set protection on the Administrator + user account such that the administrator is unable to change + her password. + + + +Parameters: + + Context - The caller's context. This is used to determine + whether the caller is trusted or not. If the caller is + trusted, then there are no restrictions. + + ObjectType - The type of object the new security descriptor + is being applied to. + + ObjectRid - The RID of the object the new security descriptor + is being applied to. + + PassedSD - The security descriptor passed by the client. + + +Return Values: + + STATUS_SUCCESS - normal, successful completion. + + STATUS_LAST_ADMIN - Indicates the new SD could potentially lead + to the administrator account being unusable and therefore + the new protection is being rejected. + + + +--*/ +{ + + NTSTATUS + NtStatus; + + BOOLEAN + DaclPresent, + AdminSid, + Done, + IgnoreBoolean; + + PACL + Dacl; + + ACL_SIZE_INFORMATION + DaclInfo; + + PACCESS_ALLOWED_ACE + Ace; + + ACCESS_MASK + Accesses, + Remaining; + + ULONG + AceIndex; + + GENERIC_MAPPING + UserMap = {USER_READ, + USER_WRITE, + USER_EXECUTE, + USER_ALL_ACCESS}; + + SAMTRACE("SampCheckForDescriptorRestrictions"); + + // + // No checking for trusted client operations + // + + if (Context->TrustedClient) { + return(STATUS_SUCCESS); + } + + + + NtStatus = RtlGetDaclSecurityDescriptor ( (PSECURITY_DESCRIPTOR)PassedSD, + &DaclPresent, + &Dacl, + &IgnoreBoolean //DaclDefaulted + ); + ASSERT(NT_SUCCESS(NtStatus)); + + + if (!DaclPresent) { + + // + // Not replacing the DACL + // + + return(STATUS_SUCCESS); + } + + if (Dacl == NULL) { + + // + // Assigning "World all access" + // + + return(STATUS_SUCCESS); + } + + if (!RtlValidAcl(Dacl)) { + return(STATUS_INVALID_ACL); + } + + NtStatus = RtlQueryInformationAcl ( Dacl, + &DaclInfo, + sizeof(ACL_SIZE_INFORMATION), + AclSizeInformation + ); + ASSERT(NT_SUCCESS(NtStatus)); + + + + + // + // Enforce Administrator user policies + // + + NtStatus = STATUS_SUCCESS; + if (ObjectRid == DOMAIN_USER_RID_ADMIN) { + + ASSERT(ObjectType == SampUserObjectType); + + // + // For the administrator account, the ACL must grant + // these accesses: + // + + Remaining = USER_READ_GENERAL | + USER_READ_PREFERENCES | + USER_WRITE_PREFERENCES | + USER_READ_LOGON | + USER_READ_ACCOUNT | + USER_WRITE_ACCOUNT | + USER_CHANGE_PASSWORD | + USER_FORCE_PASSWORD_CHANGE | + USER_LIST_GROUPS | + USER_READ_GROUP_INFORMATION | + USER_WRITE_GROUP_INFORMATION; + + // + // to these SIDs: + // + // <domain>\Administrator + // <builtin>\Administrators + // + // It doesn't matter which accesses are granted to which SIDs, + // as long as collectively all the accesses are granted. + // + + // + // Walk the ACEs collecting accesses that are granted to these + // SIDs. Make sure there are no DENYs that prevent them from + // being granted. + // + + Done = FALSE; + for ( AceIndex=0; + (AceIndex < DaclInfo.AceCount) && !Done; + AceIndex++) { + + NtStatus = RtlGetAce ( Dacl, AceIndex, &((PVOID)Ace) ); + + // + // Don't do anything with inherit-only ACEs + // + + if ((Ace->Header.AceFlags & INHERIT_ONLY_ACE) == 0) { + + // + // Note that we expect ACCESS_ALLOWED_ACE and ACCESS_DENIED_ACE + // to be identical structures in the following switch statement. + // + + switch (Ace->Header.AceType) { + case ACCESS_ALLOWED_ACE_TYPE: + case ACCESS_DENIED_ACE_TYPE: + { + // + // Is this an interesting SID + // + + AdminSid = + RtlEqualSid( ((PSID)(&Ace->SidStart)), + SampAdministratorUserSid) + || + RtlEqualSid( ((PSID)(&Ace->SidStart)), + SampAdministratorsAliasSid); + if (AdminSid) { + + // + // Map the accesses granted or denied + // + + Accesses = Ace->Mask; + RtlMapGenericMask( &Accesses, &UserMap ); + + if (Ace->Header.AceType == ACCESS_ALLOWED_ACE_TYPE) { + + Remaining &= ~Accesses; + if (Remaining == 0) { + + // + // All necessary accesses granted + // + + Done = TRUE; + } + + } else { + ASSERT(Ace->Header.AceType == ACCESS_DENIED_ACE_TYPE); + + if (Remaining & Accesses) { + + // + // We've just been denied some necessary + // accesses that haven't yet been granted. + // + + Done = TRUE; + } + } + + } + + break; + } + + default: + break; + } // end_switch + + if (Done) { + break; + } + } + + } // end_for + + if (Remaining != 0) { + NtStatus = STATUS_LAST_ADMIN; + } + + + } // end_if (Administrator Account) + + + + return(NtStatus); +} + + + +NTSTATUS +SamrQuerySecurityObject( + IN SAMPR_HANDLE ObjectHandle, + IN SECURITY_INFORMATION SecurityInformation, + OUT PSAMPR_SR_SECURITY_DESCRIPTOR *SecurityDescriptor + ) + +/*++ + +Routine Description: + + This function (SamrQuerySecurityObject) returns to the caller requested + security information currently assigned to an object. + + Based on the caller's access rights this procedure + will return a security descriptor containing any or all of the + object's owner ID, group ID, discretionary ACL or system ACL. To + read the owner ID, group ID, or the discretionary ACL the caller + must be granted READ_CONTROL access to the object. To read the + system ACL the caller must be granted ACCESS_SYSTEM_SECURITY + access. + + This API is modelled after the NtQuerySecurityObject() system + service. + + +Parameters: + + ObjectHandle - A handle to an existing object. + + SecurityInformation - Supplies a value describing which pieces of + security information are being queried. + + SecurityDescriptor - Provides a pointer to a structure to be filled + in with a security descriptor containing the requested security + information. This information is returned in the form of a + self-relative security descriptor. + +Return Values: + + STATUS_SUCCESS - normal, successful completion. + + STATUS_ACCESS_DENIED - The specified handle was not opened for + either READ_CONTROL or ACCESS_SYSTEM_SECURITY + access. + + STATUS_INVALID_HANDLE - The specified handle is not that of an + opened SAM object. + + +--*/ +{ + NTSTATUS NtStatus, IgnoreStatus; + PSAMP_OBJECT Context; + SAMP_OBJECT_TYPE FoundType; + ACCESS_MASK DesiredAccess; + PSAMPR_SR_SECURITY_DESCRIPTOR RpcSD; + PSECURITY_DESCRIPTOR RetrieveSD, ReturnSD; + ULONG RetrieveSDLength, ReturnSDLength; + + + SAMTRACE("SamrQuerySecurityObject"); + + + ReturnSD = NULL; + + + + // + // Make sure we understand what RPC is doing for (to) us. + // + + ASSERT (*SecurityDescriptor == NULL); + + + + + + + + // + // Set the desired access based upon the requested SecurityInformation + // + + DesiredAccess = 0; + if ( SecurityInformation & SACL_SECURITY_INFORMATION) { + DesiredAccess |= ACCESS_SYSTEM_SECURITY; + } + if ( SecurityInformation & (DACL_SECURITY_INFORMATION | + OWNER_SECURITY_INFORMATION | + GROUP_SECURITY_INFORMATION) + ) { + DesiredAccess |= READ_CONTROL; + } + + + + + + // + // Allocate the first block of returned memory + // + + RpcSD = MIDL_user_allocate( sizeof(SAMPR_SR_SECURITY_DESCRIPTOR) ); + if (RpcSD == NULL) { + return(STATUS_INSUFFICIENT_RESOURCES); + } + RpcSD->Length = 0; + RpcSD->SecurityDescriptor = NULL; + + + + // + // See if the handle is valid and opened for the requested access + // + + + SampAcquireReadLock(); + Context = (PSAMP_OBJECT)ObjectHandle; + NtStatus = SampLookupContext( + Context, + DesiredAccess, + SampUnknownObjectType, // ExpectedType + &FoundType + ); + + + if (NT_SUCCESS(NtStatus)) { + + + // + // Get the security descriptor + // + + + RetrieveSDLength = 0; + NtStatus = SampGetObjectSD( Context, &RetrieveSDLength, &RetrieveSD); + + if (NT_SUCCESS(NtStatus)) { + + // + // blank out the parts that aren't to be returned + // + + if ( !(SecurityInformation & SACL_SECURITY_INFORMATION) ) { + ((PISECURITY_DESCRIPTOR)RetrieveSD)->Control &= ~SE_SACL_PRESENT; + } + + + if ( !(SecurityInformation & DACL_SECURITY_INFORMATION) ) { + ((PISECURITY_DESCRIPTOR)RetrieveSD)->Control &= ~SE_DACL_PRESENT; + } + + + if ( !(SecurityInformation & OWNER_SECURITY_INFORMATION) ) { + ((PISECURITY_DESCRIPTOR)RetrieveSD)->Owner = NULL; + } + + + if ( !(SecurityInformation & GROUP_SECURITY_INFORMATION) ) { + ((PISECURITY_DESCRIPTOR)RetrieveSD)->Group = NULL; + } + + + // + // Determine how much memory is needed for a self-relative + // security descriptor containing just this information. + // + + + ReturnSDLength = 0; + NtStatus = RtlMakeSelfRelativeSD( + RetrieveSD, + NULL, + &ReturnSDLength + ); + ASSERT(!NT_SUCCESS(NtStatus)); + + if (NtStatus == STATUS_BUFFER_TOO_SMALL) { + + + ReturnSD = MIDL_user_allocate( ReturnSDLength ); + if (ReturnSD == NULL) { + + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + + } else { + + + // + // make an appropriate self-relative security descriptor + // + + NtStatus = RtlMakeSelfRelativeSD( + RetrieveSD, + ReturnSD, + &ReturnSDLength + ); + } + + } + + + // + // Free up the retrieved SD + // + + MIDL_user_free( RetrieveSD ); + } + + + + // + // De-reference the object + // + + IgnoreStatus = SampDeReferenceContext( Context, FALSE ); + } + + // + // Free the read lock + // + + SampReleaseReadLock(); + + + + // + // If we succeeded, set up the return buffer. + // Otherwise, free any allocated memory. + // + + if (NT_SUCCESS(NtStatus)) { + + RpcSD->Length = ReturnSDLength; + RpcSD->SecurityDescriptor = (PUCHAR)ReturnSD; + (*SecurityDescriptor) = RpcSD; + + } else { + + MIDL_user_free( RpcSD ); + if (ReturnSD != NULL) { + MIDL_user_free(ReturnSD); + } + (*SecurityDescriptor) = NULL; + } + + + + return(NtStatus); + + +} diff --git a/private/newsam2/server/security.c b/private/newsam2/server/security.c new file mode 100644 index 000000000..1cb96df35 --- /dev/null +++ b/private/newsam2/server/security.c @@ -0,0 +1,932 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + security.c + +Abstract: + + This file contains services which perform access validation on + attempts to access SAM objects. It also performs auditing on + both open and close operations. + + +Author: + + Jim Kelly (JimK) 6-July-1991 + +Environment: + + User Mode - Win32 + +Revision History: + + +--*/ + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include <samsrvp.h> +#include <ntseapi.h> +#include <seopaque.h> + + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// private service prototypes // +// // +/////////////////////////////////////////////////////////////////////////////// + + +VOID +SampRemoveAnonymousChangePasswordAccess( + IN OUT PSECURITY_DESCRIPTOR Sd + ); + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Routines // +// // +/////////////////////////////////////////////////////////////////////////////// + + +NTSTATUS +SampImpersonateNullSession( + ) +/*++ + +Routine Description: + + Impersonates the null session token + +Arguments: + + None + +Return Value: + + STATUS_CANNOT_IMPERSONATE - there is no null session token to imperonate + +--*/ +{ + SAMTRACE("SampImpersonateNullSession"); + + if (SampNullSessionToken == NULL) { + return(STATUS_CANNOT_IMPERSONATE); + } + return( NtSetInformationThread( + NtCurrentThread(), + ThreadImpersonationToken, + (PVOID) &SampNullSessionToken, + sizeof(HANDLE) + ) ); + +} + +NTSTATUS +SampRevertNullSession( + ) +/*++ + +Routine Description: + + Reverts a thread from impersonating the null session token. + +Arguments: + + None + +Return Value: + + STATUS_CANNOT_IMPERSONATE - there was no null session token to be + imperonating. + +--*/ +{ + + HANDLE NullHandle = NULL; + + SAMTRACE("SampRevertNullSession"); + + if (SampNullSessionToken == NULL) { + return(STATUS_CANNOT_IMPERSONATE); + } + + return( NtSetInformationThread( + NtCurrentThread(), + ThreadImpersonationToken, + (PVOID) &NullHandle, + sizeof(HANDLE) + ) ); + +} + + + + +NTSTATUS +SampValidateObjectAccess( + IN PSAMP_OBJECT Context, + IN ACCESS_MASK DesiredAccess, + IN BOOLEAN ObjectCreation + ) + +/*++ + +Routine Description: + + This service performs access validation on the specified object. + The security descriptor of the object is expected to be in a sub-key + of the ObjectRootKey named "SecurityDescriptor". + + + This service: + + 1) Retrieves the target object's SecurityDescriptor from the + the ObjectRootKey, + + 2) Impersonates the client. If this fails, and we have a + null session token to use, imperonate that. + + 3) Uses NtAccessCheckAndAuditAlarm() to validate access to the + object, + + 4) Stops impersonating the client. + + Upon successful completion, the passed context's GrantedAccess mask + and AuditOnClose fields will be properly set to represent the results + of the access validation. If the AuditOnClose field is set to TRUE, + then the caller is responsible for calling SampAuditOnClose() when + the object is closed. + + +Arguments: + + Context - The handle value that will be assigned if the access validation + is successful. + + DesiredAccess - Specifies the accesses being requested to the target + object. + + ObjectCreation - A boolean flag indicated whether the access will + result in a new object being created if granted. A value of TRUE + indicates an object will be created, FALSE indicates an existing + object will be opened. + + + + + + +Return Value: + + STATUS_SUCCESS - Indicates access has been granted. + + Other values that may be returned are those returned by: + + NtAccessCheckAndAuditAlarm() + + + + +--*/ +{ + + NTSTATUS NtStatus, IgnoreStatus, AccessStatus; + ULONG SecurityDescriptorLength; + PSECURITY_DESCRIPTOR SecurityDescriptor; + ACCESS_MASK MappedDesiredAccess; + BOOLEAN TrustedClient; + SAMP_OBJECT_TYPE ObjectType; + PUNICODE_STRING ObjectName; + ULONG DomainIndex; + BOOLEAN ImpersonatingNullSession = FALSE; + + SAMTRACE("SampValidateObjectAccess"); + + + // + // Extract various fields from the account context + // + + TrustedClient = Context->TrustedClient; + ObjectType = Context->ObjectType; + DomainIndex = Context->DomainIndex; + + + + + // + // Map the desired access + // + + + MappedDesiredAccess = DesiredAccess; + RtlMapGenericMask( + &MappedDesiredAccess, + &SampObjectInformation[ ObjectType ].GenericMapping + ); + + // This doesn't take ACCESS_SYSTEM_SECURITY into account. + // + //if ((SampObjectInformation[ObjectType].InvalidMappedAccess & + // MappedDesiredAccess) != 0) { + // return(STATUS_ACCESS_DENIED); + //} + + if (TrustedClient) { + Context->GrantedAccess = MappedDesiredAccess; + Context->AuditOnClose = FALSE; + return(STATUS_SUCCESS); + } + + + + // + // Calculate the string to use as an object name for auditing + // + + NtStatus = STATUS_SUCCESS; + + switch (ObjectType) { + + case SampServerObjectType: + ObjectName = &SampServerObjectName; + break; + + case SampDomainObjectType: + ObjectName = &SampDefinedDomains[DomainIndex].ExternalName; + break; + + case SampUserObjectType: + case SampGroupObjectType: + case SampAliasObjectType: + ObjectName = &Context->RootName; + break; + + default: + ASSERT(FALSE); + break; + } + + + + + if ( NT_SUCCESS(NtStatus)) { + + // + // Fetch the object security descriptor so we can validate + // the access against it + // + + NtStatus = SampGetObjectSD( Context, &SecurityDescriptorLength, &SecurityDescriptor); + if ( NT_SUCCESS(NtStatus)) { + + // + // If this is a USER object, then we may have to mask the + // ability for Anonymous logons to change passwords. + // + + if ( (ObjectType == SampUserObjectType) && + (SampDefinedDomains[DomainIndex].UnmodifiedFixed.PasswordProperties + & DOMAIN_PASSWORD_NO_ANON_CHANGE) ) { + + // + // Change our (local) copy of the object's DACL + // so that it doesn't grant CHANGE_PASSWORD to + // either WORLD or ANONYMOUS + // + + SampRemoveAnonymousChangePasswordAccess(SecurityDescriptor); + } + + + + // + // Impersonate the client. If RPC impersonation fails because + // it is not supported (came in unauthenticated), then impersonate + // the null session. + // + + NtStatus = I_RpcMapWin32Status(RpcImpersonateClient( NULL )); + + if (NtStatus == RPC_NT_CANNOT_SUPPORT) { + NtStatus = SampImpersonateNullSession(); + ImpersonatingNullSession = TRUE; + } + + if (NT_SUCCESS(NtStatus)) { + + + // + // Access validate the client + // + + NtStatus = NtAccessCheckAndAuditAlarm( + &SampSamSubsystem, + (PVOID)Context, + &SampObjectInformation[ ObjectType ].ObjectTypeName, + ObjectName, + SecurityDescriptor, + MappedDesiredAccess, + &SampObjectInformation[ ObjectType ].GenericMapping, + ObjectCreation, + &Context->GrantedAccess, + &AccessStatus, + &Context->AuditOnClose + ); + + + // + // Stop impersonating the client + // + + if (ImpersonatingNullSession) { + IgnoreStatus = SampRevertNullSession(); + } + IgnoreStatus = I_RpcMapWin32Status(RpcRevertToSelf()); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + } + + // + // Free up the security descriptor + // + + MIDL_user_free( SecurityDescriptor ); + + } + } + + + + // + // If we got an error back from the access check, return that as + // status. Otherwise, return the access check status. + // + + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + return(AccessStatus); +} + + +VOID +SampAuditOnClose( + IN PSAMP_OBJECT Context + ) + +/*++ + +Routine Description: + + This service performs auditing necessary during a handle close operation. + + This service may ONLY be called if the corresponding call to + SampValidateObjectAccess() during openned returned TRUE. + + + +Arguments: + + Context - This must be the same value that was passed to the corresponding + SampValidateObjectAccess() call. This value is used for auditing + purposes only. + +Return Value: + + None. + + +--*/ +{ + + SAMTRACE("SampAuditOnClose"); + + //FIX, FIX - Call NtAuditClose() (or whatever it is). + + return; + + DBG_UNREFERENCED_PARAMETER( Context ); + +} + + +VOID +SampRemoveAnonymousChangePasswordAccess( + IN OUT PSECURITY_DESCRIPTOR Sd + ) + +/*++ + +Routine Description: + + This routine removes USER_CHANGE_PASSWORD access from + any GRANT aces in the discretionary acl that have either + the WORLD or ANONYMOUS SIDs in the ACE. + +Parameters: + + Sd - Is a pointer to a security descriptor of a SAM USER + object. + +Returns: + + None. + +--*/ +{ + PACL + Dacl; + + ULONG + i, + AceCount; + + PACE_HEADER + Ace; + + BOOLEAN + DaclPresent, + DaclDefaulted; + + SAMTRACE("SampRemoveAnonymousChangePasswordAccess"); + + + RtlGetDaclSecurityDescriptor( Sd, + &DaclPresent, + &Dacl, + &DaclDefaulted + ); + + if ( !DaclPresent || (Dacl == NULL)) { + return; + } + + if ((AceCount = Dacl->AceCount) == 0) { + return; + } + + for ( i = 0, Ace = FirstAce( Dacl ) ; + i < AceCount ; + i++, Ace = NextAce( Ace ) + ) { + + if ( !(((PACE_HEADER)Ace)->AceFlags & INHERIT_ONLY_ACE)) { + + if ( (((PACE_HEADER)Ace)->AceType == ACCESS_ALLOWED_ACE_TYPE) ) { + + if ( (RtlEqualSid( SampWorldSid, &((PACCESS_ALLOWED_ACE)Ace)->SidStart )) || + (RtlEqualSid( SampAnonymousSid, &((PACCESS_ALLOWED_ACE)Ace)->SidStart ))) { + + // + // Turn off CHANGE_PASSWORD access + // + + ((PACCESS_ALLOWED_ACE)Ace)->Mask &= ~USER_CHANGE_PASSWORD; + } + } + } + } + + return; +} + + +NTSTATUS +SampCreateNullToken( + ) + +/*++ + +Routine Description: + + This function creates a token representing a null logon. + +Arguments: + + +Return Value: + + The status value of the NtCreateToken() call. + + + +--*/ + +{ + NTSTATUS Status; + + TOKEN_USER UserId; + TOKEN_PRIMARY_GROUP PrimaryGroup; + TOKEN_GROUPS GroupIds; + TOKEN_PRIVILEGES Privileges; + TOKEN_SOURCE SourceContext; + OBJECT_ATTRIBUTES ObjectAttributes; + SECURITY_QUALITY_OF_SERVICE ImpersonationQos; + LARGE_INTEGER ExpirationTime; + LUID LogonId = SYSTEM_LUID; + + SAMTRACE("SampCreateNullToken"); + + + + UserId.User.Sid = SampWorldSid; + UserId.User.Attributes = 0; + GroupIds.GroupCount = 0; + Privileges.PrivilegeCount = 0; + PrimaryGroup.PrimaryGroup = SampWorldSid; + ExpirationTime.LowPart = 0xfffffff; + ExpirationTime.LowPart = 0x7ffffff; + + + // + // Build a token source for SAM. + // + + Status = NtAllocateLocallyUniqueId( &SourceContext.SourceIdentifier ); + if (!NT_SUCCESS(Status)) { + return(Status); + } + + strncpy(SourceContext.SourceName,"SamSS ",sizeof(SourceContext.SourceName)); + + + // + // Set the object attributes to specify an Impersonation impersonation + // level. + // + + InitializeObjectAttributes( &ObjectAttributes, NULL, 0, NULL, NULL ); + ImpersonationQos.ImpersonationLevel = SecurityImpersonation; + ImpersonationQos.ContextTrackingMode = SECURITY_STATIC_TRACKING; + ImpersonationQos.EffectiveOnly = TRUE; + ImpersonationQos.Length = (ULONG)sizeof(SECURITY_QUALITY_OF_SERVICE); + ObjectAttributes.SecurityQualityOfService = &ImpersonationQos; + + Status = NtCreateToken( + &SampNullSessionToken, // Handle + (TOKEN_ALL_ACCESS), // DesiredAccess + &ObjectAttributes, // ObjectAttributes + TokenImpersonation, // TokenType + &LogonId, // Authentication LUID + &ExpirationTime, // Expiration Time + &UserId, // User ID + &GroupIds, // Group IDs + &Privileges, // Privileges + NULL, // Owner + &PrimaryGroup, // Primary Group + NULL, // Default Dacl + &SourceContext // TokenSource + ); + + return Status; + +} + +ULONG +SampSecureRpcInit( + PVOID Ignored + ) +/*++ + +Routine Description: + + This routine waits for the NTLMSSP service to start and then registers + security information with RPC to allow authenticated RPC to be used to + SAM. It also registers an SPX endpoint if FPNW is installed. + +Arguments: + + Ignored - required parameter for starting a thread. + +Return Value: + + None. + +--*/ +{ + +#define MAX_RPC_RETRIES 30 + + ULONG RpcStatus; + ULONG RpcRetry; + ULONG RpcSleepTime = 10 * 1000; // retry every ten seconds + RPC_BINDING_VECTOR * BindingVector = NULL; + + + SAMTRACE("SampSecureRpcInit"); + + + RpcStatus = RpcServerRegisterAuthInfoW( + NULL, // server principal name + RPC_C_AUTHN_WINNT, + NULL, // no get key function + NULL // no get key argument + ); + + if (RpcStatus != 0) { + KdPrint(("SAMSS: Could not register auth. info: %d\n", + RpcStatus )); + goto ErrorReturn; + } + + // + // If the Netware server is installed, register the SPX protocol. + // Since the transport may not be loaded yet, retry a couple of times + // if we get a CANT_CREATE_ENDPOINT error (meaning the transport isn't + // there). + // + + if (SampNetwareServerInstalled) { + + RpcRetry = MAX_RPC_RETRIES; + while (RpcRetry != 0) { + + RpcStatus = RpcServerUseProtseqW( + L"ncacn_spx", + 10, + NULL // no security descriptor + ); + + // + // If it succeded break out of the loop. + // + if (RpcStatus == ERROR_SUCCESS) { + break; + } + Sleep(RpcSleepTime); + RpcRetry--; + continue; + + } + + if (RpcStatus != 0) { + KdPrint(("SAMSS: Could not register SPX endpoint: %d\n", RpcStatus )); + goto ErrorReturn; + } + + } + + // + // do the same thing all over again with TcpIp + // + + if (SampIpServerInstalled) { + + RpcRetry = MAX_RPC_RETRIES; + while (RpcRetry != 0) { + + RpcStatus = RpcServerUseProtseqW( + L"ncacn_ip_tcp", + 10, + NULL // no security descriptor + ); + + // + // If it succeeded, break out of the loop. + // + + if (RpcStatus == ERROR_SUCCESS) { + break; + } + Sleep(RpcSleepTime); + RpcRetry--; + continue; + + } + + if (RpcStatus != 0) { + KdPrint(("SAMSS: Could not register TCP endpoint: %d\n", RpcStatus )); + goto ErrorReturn; + return(RpcStatus); + } + + } + + // + // If we started Tcp/Ip or Spx, go on to register the endpoints + // + + if (SampNetwareServerInstalled || SampIpServerInstalled) { + RpcStatus = RpcServerInqBindings(&BindingVector); + if (RpcStatus != 0) { + KdPrint(("SAMSS: Could not inq bindings: %d\n",RpcStatus)); + goto ErrorReturn; + } + RpcStatus = RpcEpRegister( + samr_ServerIfHandle, + BindingVector, + NULL, // no uuid vector + L"" // no annotation + ); + + RpcBindingVectorFree(&BindingVector); + if (RpcStatus != 0) { + KdPrint(("SAMSS: Could not register endpoints: %d\n",RpcStatus)); + goto ErrorReturn; + } + } + return(ERROR_SUCCESS); + +ErrorReturn: + + SampWriteEventLog( + EVENTLOG_ERROR_TYPE, + 0, // Category + SAMMSG_RPC_INIT_FAILED, + NULL, // User Sid + 0, // Num strings + sizeof(NTSTATUS), // Data size + NULL, // String array + (PVOID)&RpcStatus // Data + ); + + return(RpcStatus); +} + + +BOOLEAN +SampStartNonNamedPipeTransports( + ) +/*++ + +Routine Description: + + This routine checks to see if we should listen on a non-named pipe + transport. We check the registry for flags indicating that we should + listen on Tcp/Ip and SPX. There is a flag + in the registry under system\currentcontrolset\Control\Lsa\ + NetwareClientSupport and TcpipClientSupport indicating whether or not + to setup the endpoint. + + +Arguments: + + +Return Value: + + TRUE - Netware (FPNW or SmallWorld) is installed and the SPX endpoint + should be started. + + FALSE - Either Netware is not installed, or an error occurred while + checking for it. +--*/ +{ + NTSTATUS NtStatus; + UNICODE_STRING KeyName; + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE KeyHandle; + UCHAR Buffer[100]; + PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation = (PKEY_VALUE_PARTIAL_INFORMATION) Buffer; + ULONG KeyValueLength = 100; + ULONG ResultLength; + PULONG SpxFlag; + + SAMTRACE("SampStartNonNamedPipeTransport"); + + SampNetwareServerInstalled = FALSE; + SampIpServerInstalled = FALSE; + + // + // Open the Lsa key in the registry + // + + RtlInitUnicodeString( + &KeyName, + L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Lsa" + ); + + InitializeObjectAttributes( + &ObjectAttributes, + &KeyName, + OBJ_CASE_INSENSITIVE, + 0, + NULL + ); + + SampDumpNtOpenKey((KEY_READ), &ObjectAttributes, 0); + + NtStatus = NtOpenKey( + &KeyHandle, + KEY_READ, + &ObjectAttributes + ); + + if (!NT_SUCCESS(NtStatus)) { + return(FALSE); + } + + // + // Query the NetwareClientSupport value + // + + RtlInitUnicodeString( + &KeyName, + L"NetWareClientSupport" + ); + + NtStatus = NtQueryValueKey( + KeyHandle, + &KeyName, + KeyValuePartialInformation, + KeyValueInformation, + KeyValueLength, + &ResultLength + ); + + SampDumpNtQueryValueKey(&KeyName, + KeyValuePartialInformation, + KeyValueInformation, + KeyValueLength, + &ResultLength); + + + if (NT_SUCCESS(NtStatus)) { + + // + // Check that the data is the correct size and type - a ULONG. + // + + if ((KeyValueInformation->DataLength >= sizeof(ULONG)) && + (KeyValueInformation->Type == REG_DWORD)) { + + + SpxFlag = (PULONG) KeyValueInformation->Data; + + if (*SpxFlag == 1) { + SampNetwareServerInstalled = TRUE; + } + } + + } + // + // Query the Tcp/IpClientSupport value + // + + RtlInitUnicodeString( + &KeyName, + L"TcpipClientSupport" + ); + + NtStatus = NtQueryValueKey( + KeyHandle, + &KeyName, + KeyValuePartialInformation, + KeyValueInformation, + KeyValueLength, + &ResultLength + ); + + SampDumpNtQueryValueKey(&KeyName, + KeyValuePartialInformation, + KeyValueInformation, + KeyValueLength, + &ResultLength); + + + if (NT_SUCCESS(NtStatus)) { + + // + // Check that the data is the correct size and type - a ULONG. + // + + if ((KeyValueInformation->DataLength >= sizeof(ULONG)) && + (KeyValueInformation->Type == REG_DWORD)) { + + + SpxFlag = (PULONG) KeyValueInformation->Data; + + if (*SpxFlag == 1) { + SampIpServerInstalled = TRUE; + } + } + + } + + NtClose(KeyHandle); + + if (SampNetwareServerInstalled || SampIpServerInstalled) + { + return(TRUE); + } + else + { + return(FALSE); + }; +} + + diff --git a/private/newsam2/server/server.c b/private/newsam2/server/server.c new file mode 100644 index 000000000..50a0c2a4a --- /dev/null +++ b/private/newsam2/server/server.c @@ -0,0 +1,945 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + server.c + +Abstract: + + This file contains services related to the SAM "server" object. + + +Author: + + Jim Kelly (JimK) 4-July-1991 + +Environment: + + User Mode - Win32 + +Revision History: + + +--*/ + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include <samsrvp.h> + + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// private service prototypes // +// // +/////////////////////////////////////////////////////////////////////////////// + + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Routines // +// // +/////////////////////////////////////////////////////////////////////////////// + + + + + + +NTSTATUS +SamrConnect2( + IN PSAMPR_SERVER_NAME ServerName, + OUT SAMPR_HANDLE * ServerHandle, + IN ACCESS_MASK DesiredAccess + ) + +/*++ + +Routine Description: + + This service is the dispatch routine for SamConnect. It performs + an access validation to determine whether the caller may connect + to SAM for the access specified. If so, a context block is established. + This is different from the SamConnect call in that the entire server + name is passed instead of just the first character. + + +Arguments: + + ServerName - Name of the node this SAM reside on. Ignored by this + routine. + + ServerHandle - If the connection is successful, the value returned + via this parameter serves as a context handle to the openned + SERVER object. + + DesiredAccess - Specifies the accesses desired to the SERVER object. + + +Return Value: + + Status values returned by SamIConnect(). + + +--*/ +{ + BOOLEAN TrustedClient; + + SAMTRACE("SamrConnect2"); + + // + // If we ever want to support trusted remote clients, then the test + // for whether or not the client is trusted can be made here and + // TrustedClient set appropriately. For now, all remote clients are + // considered untrusted. + + // All clients are considered trusted clients for user mode SAM. This + // is because the NtAccessCheckAndAuditAlarm returns + // STATUS_PRIVILEGE_NOT_HELD while running from a user account as opposed + // to being part of the system + +#ifdef USER_MODE_SAM + TrustedClient = TRUE; +#else + TrustedClient = FALSE; +#endif + + return SamIConnect(ServerName, ServerHandle, DesiredAccess, TrustedClient ); + +} + + +NTSTATUS +SamrConnect( + IN PSAMPR_SERVER_NAME ServerName, + OUT SAMPR_HANDLE * ServerHandle, + IN ACCESS_MASK DesiredAccess + ) + +/*++ + +Routine Description: + + This service is the dispatch routine for SamConnect. It performs + an access validation to determine whether the caller may connect + to SAM for the access specified. If so, a context block is established + + +Arguments: + + ServerName - Name of the node this SAM reside on. Ignored by this + routine. The name contains only a single character. + + ServerHandle - If the connection is successful, the value returned + via this parameter serves as a context handle to the openned + SERVER object. + + DesiredAccess - Specifies the accesses desired to the SERVER object. + + +Return Value: + + Status values returned by SamIConnect(). + + +--*/ +{ + BOOLEAN TrustedClient; + + SAMTRACE("SamrConnect"); + + + // + // If we ever want to support trusted remote clients, then the test + // for whether or not the client is trusted can be made here and + // TrustedClient set appropriately. For now, all remote clients are + // considered untrusted. + + // All clients are considered trusted clients for user mode SAM. This + // is because the NT call for checking access will return + // STATUS_PRIVILEGE_NOT_HELD while running from a user account as opposed + // to being part of the system + +#ifdef USER_MODE_SAM + TrustedClient = TRUE; +#else + TrustedClient = FALSE; +#endif + + return SamIConnect(NULL, ServerHandle, DesiredAccess, TrustedClient ); + +} + + +NTSTATUS +SamIConnect( + IN PSAMPR_SERVER_NAME ServerName, + OUT SAMPR_HANDLE * ServerHandle, + IN ACCESS_MASK DesiredAccess, + IN BOOLEAN TrustedClient + ) + +/*++ + +Routine Description: + + This service is the dispatch routine for SamConnect. It performs + an access validation to determine whether the caller may connect + to SAM for the access specified. If so, a context block is established + + + NOTE: If the caller is trusted, then the DesiredAccess parameter may + NOT contain any Generic access types or MaximumAllowed. All + mapping must be done by the caller. + +Arguments: + + ServerName - Name of the node this SAM reside on. Ignored by this + routine. + + ServerHandle - If the connection is successful, the value returned + via this parameter serves as a context handle to the openned + SERVER object. + + DesiredAccess - Specifies the accesses desired to the SERVER object. + + TrustedClient - Indicates whether the client is known to be part of + the trusted computer base (TCB). If so (TRUE), no access validation + is performed and all requested accesses are granted. If not + (FALSE), then the client is impersonated and access validation + performed against the SecurityDescriptor on the SERVER object. + +Return Value: + + + STATUS_SUCCESS - The SERVER object has been successfully openned. + + STATUS_INSUFFICIENT_RESOURCES - The SAM server processes doesn't + have sufficient resources to process or accept another connection + at this time. + + Other values as may be returned from: + + NtAccessCheckAndAuditAlarm() + + +--*/ +{ + NTSTATUS NtStatus; + PSAMP_OBJECT Context; + + UNREFERENCED_PARAMETER( ServerName ); //Ignored by this routine + + SAMTRACE("SamIConnect"); + + // + // If the SAM server is not initialized, reject the connection. + // + + if (SampServiceState != SampServiceEnabled) { + + return(STATUS_INVALID_SERVER_STATE); + } + + SampAcquireReadLock(); + + + Context = SampCreateContext( SampServerObjectType, TrustedClient ); + + if (Context != NULL) { + + // + // The RootKey for a SERVER object is the root of the SAM database. + // This key should not be closed when the context is deleted. + // + + Context->RootKey = SampKey; + + // + // The rootkeyname has been initialized to NULL inside CreateContext. + // + + // + // Perform access validation ... + // + + NtStatus = SampValidateObjectAccess( + Context, //Context + DesiredAccess, //DesiredAccess + FALSE //ObjectCreation + ); + + + + // + // if we didn't pass the access test, then free up the context block + // and return the error status returned from the access validation + // routine. Otherwise, return the context handle value. + // + + if (!NT_SUCCESS(NtStatus)) { + SampDeleteContext( Context ); + } else { + (*ServerHandle) = Context; + } + + } else { + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Free the read lock + // + + SampReleaseReadLock(); + + return(NtStatus); + +} + + +NTSTATUS +SamrShutdownSamServer( + IN SAMPR_HANDLE ServerHandle + ) + +/*++ + +Routine Description: + + This service shuts down the SAM server. + + In the long run, this routine will perform an orderly shutdown. + In the short term, it is useful for debug purposes to shutdown + in a brute force un-orderly fashion. + +Arguments: + + ServerHandle - Received from a previous call to SamIConnect(). + +Return Value: + + STATUS_SUCCESS - The services completed successfully. + + + STATUS_ACCESS_DENIED - The caller doesn't have the appropriate access + to perform the requested operation. + + +--*/ +{ + + NTSTATUS NtStatus, IgnoreStatus; + PSAMP_OBJECT ServerContext; + SAMP_OBJECT_TYPE FoundType; + + SAMTRACE("SamrShutdownSamServer"); + + + + NtStatus = SampAcquireWriteLock(); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + // + // Validate type of, and access to server object. + // + + ServerContext = (PSAMP_OBJECT)ServerHandle; + NtStatus = SampLookupContext( + ServerContext, + SAM_SERVER_SHUTDOWN, // DesiredAccess + SampServerObjectType, // ExpectedType + &FoundType + ); + + if (NT_SUCCESS(NtStatus)) { + + + // + // Signal the event that will cut loose the main thread. + // The main thread will then exit - causing the walls to + // come tumbling down. + // + + IgnoreStatus = RpcMgmtStopServerListening(0); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + + + // + // De-reference the server object + // + + IgnoreStatus = SampDeReferenceContext( ServerContext, FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + // + // Free the write lock and roll-back the transaction + // + + IgnoreStatus = SampReleaseWriteLock( FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + return(NtStatus); + +} + + +NTSTATUS +SamrLookupDomainInSamServer( + IN SAMPR_HANDLE ServerHandle, + IN PRPC_UNICODE_STRING Name, + OUT PRPC_SID *DomainId + ) + +/*++ + +Routine Description: + + This service + +Arguments: + + ServerHandle - A context handle returned by a previous call + to SamConnect(). + + Name - contains the name of the domain to look up. + + DomainSid - Receives a pointer to a buffer containing the SID of + the domain. The buffer pointed to must be deallocated by the + caller using MIDL_user_free() when no longer needed. + + +Return Value: + + + STATUS_SUCCESS - The services completed successfully. + + STATUS_ACCESS_DENIED - The caller doesn't have the appropriate access + to perform the requested operation. + + STATUS_NO_SUCH_DOMAIN - The specified domain does not exist at this + server. + + + STATUS_INVALID_SERVER_STATE - Indicates the SAM server is currently + disabled. + + + + +--*/ +{ + + NTSTATUS NtStatus, IgnoreStatus; + PSAMP_OBJECT ServerContext; + SAMP_OBJECT_TYPE FoundType; + ULONG i, SidLength; + BOOLEAN DomainFound; + PSID FoundSid; + + SAMTRACE("SamrLookupDomainInSamServer"); + + + // + // Make sure we understand what RPC is doing for (to) us. + // + + ASSERT (DomainId != NULL); + ASSERT ((*DomainId) == NULL); + + + + + + + SampAcquireReadLock(); + + + // + // Validate type of, and access to object. + // + + ServerContext = (PSAMP_OBJECT)ServerHandle; + NtStatus = SampLookupContext( + ServerContext, + SAM_SERVER_LOOKUP_DOMAIN, + SampServerObjectType, // ExpectedType + &FoundType + ); + + + if (NT_SUCCESS(NtStatus)) { + + + + // + // Set our default completion status + // + + NtStatus = STATUS_NO_SUCH_DOMAIN; + + + // + // Search the list of defined domains for a match. + // + + DomainFound = FALSE; + for (i = 0; + (i<SampDefinedDomainsCount && (!DomainFound)); + i++ ) { + + if (RtlEqualDomainName(&SampDefinedDomains[i].ExternalName, (PUNICODE_STRING)Name) ) { + + + DomainFound = TRUE; + + + // + // Allocate and fill in the return buffer + // + + SidLength = RtlLengthSid( SampDefinedDomains[i].Sid ); + FoundSid = MIDL_user_allocate( SidLength ); + if (FoundSid != NULL) { + NtStatus = + RtlCopySid( SidLength, FoundSid, SampDefinedDomains[i].Sid ); + + if (!NT_SUCCESS(NtStatus) ) { + MIDL_user_free( FoundSid ); + NtStatus = STATUS_INTERNAL_ERROR; + } + + (*DomainId) = FoundSid; + } + + + NtStatus = STATUS_SUCCESS; + } + + } + + + + // + // De-reference the object + // + + if ( NT_SUCCESS( NtStatus ) ) { + + NtStatus = SampDeReferenceContext( ServerContext, FALSE ); + + } else { + + IgnoreStatus = SampDeReferenceContext( ServerContext, FALSE ); + } + } + + // + // Free the read lock + // + + SampReleaseReadLock(); + + + + + return(NtStatus); +} + + +NTSTATUS +SamrEnumerateDomainsInSamServer( + IN SAMPR_HANDLE ServerHandle, + IN OUT PSAM_ENUMERATE_HANDLE EnumerationContext, + OUT PSAMPR_ENUMERATION_BUFFER *Buffer, + IN ULONG PreferedMaximumLength, + OUT PULONG CountReturned + ) + +/*++ + +Routine Description: + + This API lists all the domains defined in the account database. + Since there may be more domains than can fit into a buffer, the + caller is provided with a handle that can be used across calls to + the API. On the initial call, EnumerationContext should point to a + SAM_ENUMERATE_HANDLE variable that is set to 0. + + If the API returns STATUS_MORE_ENTRIES, then the API should be + called again with EnumerationContext. When the API returns + STATUS_SUCCESS or any error return, the handle becomes invalid for + future use. + + This API requires SAM_SERVER_ENUMERATE_DOMAINS access to the + SamServer object. + +Arguments: + + ConnectHandle - Handle obtained from a previous SamConnect call. + + EnumerationContext - API specific handle to allow multiple calls + (see below). This is a zero based index. + + Buffer - Receives a pointer to the buffer where the information + is placed. The information returned is contiguous + SAM_RID_ENUMERATION data structures. However, the + RelativeId field of each of these structures is not valid. + This buffer must be freed when no longer needed using + SamFreeMemory(). + + PreferedMaximumLength - Prefered maximum length of returned data + (in 8-bit bytes). This is not a hard upper limit, but serves + as a guide to the server. Due to data conversion between + systems with different natural data sizes, the actual amount + of data returned may be greater than this value. + + CountReturned - Number of entries returned. + +Return Value: + + STATUS_SUCCESS - The Service completed successfully, and there + are no addition entries. + + STATUS_MORE_ENTRIES - There are more entries, so call again. + This is a successful return. + + STATUS_ACCESS_DENIED - Caller does not have the access required + to enumerate the domains. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_INVALID_SERVER_STATE - Indicates the SAM server is + currently disabled. + + + +--*/ +{ + NTSTATUS NtStatus, IgnoreStatus; + ULONG i; + PSAMP_OBJECT Context; + SAMP_OBJECT_TYPE FoundType; + ULONG TotalLength = 0; + ULONG NewTotalLength; + PSAMP_ENUMERATION_ELEMENT SampHead, NextEntry, NewEntry; + BOOLEAN LengthLimitReached = FALSE; + PSAMPR_RID_ENUMERATION ArrayBuffer; + ULONG ArrayBufferLength; + + SAMTRACE("SamrEnumerateDomainsInSamServer"); + + + // + // Make sure we understand what RPC is doing for (to) us. + // + + ASSERT (ServerHandle != NULL); + ASSERT (EnumerationContext != NULL); + ASSERT ( Buffer != NULL); + ASSERT ((*Buffer) == NULL); + ASSERT (CountReturned != NULL); + + + // + // Initialize the list of names being returned. + // This is a singly linked list. + // + + SampHead = NULL; + + + // + // Initialize the count returned + // + + (*CountReturned) = 0; + + + + + + + SampAcquireReadLock(); + + + // + // Validate type of, and access to object. + // + + Context = (PSAMP_OBJECT)ServerHandle; + NtStatus = SampLookupContext( + Context, + SAM_SERVER_ENUMERATE_DOMAINS, + SampServerObjectType, // ExpectedType + &FoundType + ); + + + if (NT_SUCCESS(NtStatus)) { + + + // + // Enumerating domains is easy. We keep a list in memory. + // All we have to do is use the enumeration context as an + // index into the defined domains array. + // + + + + // + // Set our default completion status + // Note that this is a SUCCESS status code. + // That is NT_SUCCESS(STATUS_MORE_ENTRIES) will return TRUE. + + // + + NtStatus = STATUS_MORE_ENTRIES; + + + + // + // Search the list of defined domains for a match. + // + + for ( i = (ULONG)(*EnumerationContext); + ( (i < SampDefinedDomainsCount) && + (NT_SUCCESS(NtStatus)) && + (!LengthLimitReached) ); + i++ ) { + + + // + // See if there is room for the next name. If TotalLength + // is still zero then we haven't yet even gotten one name. + // We have to return at least one name even if it exceeds + // the length request. + // + + + NewTotalLength = TotalLength + + sizeof(UNICODE_STRING) + + (ULONG)SampDefinedDomains[i].ExternalName.Length + + sizeof(UNICODE_NULL); + + if ( (NewTotalLength < PreferedMaximumLength) || + (TotalLength == 0) ) { + + if (NewTotalLength > SAMP_MAXIMUM_MEMORY_TO_USE) { + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + } else { + + + TotalLength = NewTotalLength; + (*CountReturned) += 1; + + // + // Room for this name as well. + // Allocate a new return list entry, and a buffer for the + // name. + // + + NewEntry = MIDL_user_allocate(sizeof(SAMP_ENUMERATION_ELEMENT)); + if (NewEntry == NULL) { + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + } else { + + NewEntry->Entry.Name.Buffer = + MIDL_user_allocate( + (ULONG)SampDefinedDomains[i].ExternalName.Length + + sizeof(UNICODE_NULL) + ); + + if (NewEntry->Entry.Name.Buffer == NULL) { + MIDL_user_free(NewEntry); + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + } else { + + // + // Copy the name into the return buffer + // + + RtlCopyMemory( NewEntry->Entry.Name.Buffer, + SampDefinedDomains[i].ExternalName.Buffer, + SampDefinedDomains[i].ExternalName.Length + ); + NewEntry->Entry.Name.Length = SampDefinedDomains[i].ExternalName.Length; + NewEntry->Entry.Name.MaximumLength = NewEntry->Entry.Name.Length + (USHORT)sizeof(UNICODE_NULL); + UnicodeTerminate((PUNICODE_STRING)(&NewEntry->Entry.Name)); + + + // + // The Rid field of the ENUMERATION_INFORMATION is not + // filled in for domains. + // Just for good measure, set it to zero. + // + + NewEntry->Entry.RelativeId = 0; + + + + // + // Now add this to the list of names to be returned. + // + + NewEntry->Next = (PSAMP_ENUMERATION_ELEMENT)SampHead; + SampHead = NewEntry; + } + + } + } + + } else { + + LengthLimitReached = TRUE; + + } + + } + + + + + if ( NT_SUCCESS(NtStatus) ) { + + // + // Set the enumeration context + // + + (*EnumerationContext) = (*EnumerationContext) + (*CountReturned); + + + + // + // If we are returning the last of the names, then change our + // status code to indicate this condition. + // + + if ( ((*EnumerationContext) >= SampDefinedDomainsCount) ) { + + NtStatus = STATUS_SUCCESS; + } + + + + + // + // Build a return buffer containing an array of the + // SAM_RID_ENUMERATIONs pointed to by another + // buffer containing the number of elements in that + // array. + // + + (*Buffer) = MIDL_user_allocate( sizeof(SAMPR_ENUMERATION_BUFFER) ); + + if ( (*Buffer) == NULL) { + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + } else { + + (*Buffer)->EntriesRead = (*CountReturned); + + ArrayBufferLength = sizeof( SAM_RID_ENUMERATION ) * + (*CountReturned); + ArrayBuffer = MIDL_user_allocate( ArrayBufferLength ); + (*Buffer)->Buffer = ArrayBuffer; + + if ( ArrayBuffer == NULL) { + + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + MIDL_user_free( (*Buffer) ); + + } else { + + // + // Walk the list of return entries, copying + // them into the return buffer + // + + NextEntry = SampHead; + i = 0; + while (NextEntry != NULL) { + + NewEntry = NextEntry; + NextEntry = NewEntry->Next; + + ArrayBuffer[i] = NewEntry->Entry; + i += 1; + + MIDL_user_free( NewEntry ); + } + + } + + } + } + + + + + if ( !NT_SUCCESS(NtStatus) ) { + + // + // Free the memory we've allocated + // + + NextEntry = SampHead; + while (NextEntry != NULL) { + + NewEntry = NextEntry; + NextEntry = NewEntry->Next; + + MIDL_user_free( NewEntry->Entry.Name.Buffer ); + MIDL_user_free( NewEntry ); + } + + (*EnumerationContext) = 0; + (*CountReturned) = 0; + (*Buffer) = NULL; + + } + + // + // De-reference the object + // Note that NtStatus could be STATUS_MORE_ENTRIES, which is a + // successful return code - we want to make sure we return that, + // without wiping it out here. + // + + if ( NtStatus == STATUS_SUCCESS ) { + + NtStatus = SampDeReferenceContext( Context, FALSE ); + + } else { + + IgnoreStatus = SampDeReferenceContext( Context, FALSE ); + } + } + + + + // + // Free the read lock + // + + SampReleaseReadLock(); + + + + return(NtStatus); + +} diff --git a/private/newsam2/server/string.c b/private/newsam2/server/string.c new file mode 100644 index 000000000..26cdf6ca0 --- /dev/null +++ b/private/newsam2/server/string.c @@ -0,0 +1,266 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + string.c + +Abstract: + + This file contains services for retrieving and replacing string field + values. + + +Author: + + Jim Kelly (JimK) 10-July-1991 + +Environment: + + User Mode - Win32 + +Revision History: + + +--*/ + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include <samsrvp.h> + + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// private service prototypes // +// // +/////////////////////////////////////////////////////////////////////////////// + + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Routines // +// // +/////////////////////////////////////////////////////////////////////////////// + + +NTSTATUS +SampGetUnicodeStringField( + IN PSAMP_OBJECT Context, + IN PUNICODE_STRING SubKeyName, + OUT PUNICODE_STRING *String + ) + +/*++ + +Routine Description: + + This service retrieves a unicode string from a named sub-key of + the root key provided in the Context argument. + + The returned unicode string is returned in two buffers allocated + using MIDL_user_allocate() and are therefore suitable for returning as + [out] parameters of an RPC call. The first buffer will be the unicode + string body. The second buffer will contain the unicode string + characters and will include 2 bytes of zeros. + + THIS SERVICE MUST BE CALLED WITH THE SampLock HELD FOR WRITE ACCESS. + + +Arguments: + + Context - Pointer to an active context block whose RootKey is valid. + + SubKeyName - The name of the sub-key containing the unicode string + to retrieve. + + String - Receives a pointer to a set of allocated buffers containing + the unicode string. The buffers are allocated using + MIDL_userAllocate(). If any errors are returned, these buffers + will not be allocated. + +Return Value: + + + STATUS_SUCCESS - The string value has been successfully retrieved. + + STATUS_NO_MEMORY - There was insufficient memory to allocate a + buffer to read the unicode string into. + + STATUS_INTERNAL_ERROR - The value of the sub-key seems to have changed + during the execution of this service. This should not happen since + the service must be called with the WRITE LOCK held. + + Other error values are those returned by: + + NtQueryValueKey() + + +--*/ +{ + + NTSTATUS NtStatus, IgnoreStatus; + HANDLE SubKeyHandle; + OBJECT_ATTRIBUTES ObjectAttributes; + ULONG StringLength, ActualStringLength, IgnoreKeyValueType; + PUNICODE_STRING StringBody; + PCHAR CharacterBuffer; + LARGE_INTEGER LastWriteTime; + + SAMTRACE("SampGetUnicodeStringField"); + + // + // Prepare for failure + // + + *String = NULL; + + + // + // Open the named sub-key ... + // + + InitializeObjectAttributes( + &ObjectAttributes, // Resultant object attributes + SubKeyName, // Relative Name + OBJ_CASE_INSENSITIVE, // Attributes + Context->RootKey, // Parent key handle + NULL // SecurityDescriptor + ); + + SampDumpNtOpenKey((KEY_READ), &ObjectAttributes, 0); + + NtStatus = RtlpNtOpenKey( // Don't use NtCreateKey() - it must already exist + &SubKeyHandle, + KEY_READ, + &ObjectAttributes, + 0 + ); + + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + + // + // Query the length of the unicode string in the sub-key + // + + NtStatus = RtlpNtQueryValueKey( + SubKeyHandle, + &IgnoreKeyValueType, + NULL, // No buffer yet + &StringLength, + &LastWriteTime + ); + + SampDumpRtlpNtQueryValueKey(&IgnoreKeyValueType, + NULL, + &StringLength, + &LastWriteTime); + + if (!NT_SUCCESS(NtStatus)) { + IgnoreStatus = NtClose( SubKeyHandle ); + return(NtStatus); + } + + + + // + // Allocate buffers for both the string body and the + // character buffer. + // + + CharacterBuffer = MIDL_user_allocate( StringLength + sizeof(UNICODE_NULL) ); + StringBody = MIDL_user_allocate( sizeof(UNICODE_STRING) ); + + if ((CharacterBuffer == NULL) || (StringBody == NULL)) { + + // + // We couldn't allocate pool ... + // + + IgnoreStatus = NtClose( SubKeyHandle ); + + if (CharacterBuffer != NULL) { + MIDL_user_free( CharacterBuffer ); + } + if (StringBody != NULL) { + MIDL_user_free( StringBody ); + } + + return(STATUS_NO_MEMORY); + } + + + + // + // Initialize the string body + // + + StringBody->Length = (USHORT)StringLength; + StringBody->MaximumLength = (USHORT)StringLength + (USHORT)sizeof(UNICODE_NULL); + StringBody->Buffer = (PWSTR)CharacterBuffer; + + // + // Read the string value into the character buffer. + // + + NtStatus = RtlpNtQueryValueKey( + SubKeyHandle, + &IgnoreKeyValueType, + CharacterBuffer, + &ActualStringLength, + &LastWriteTime + ); + + SampDumpRtlpNtQueryValueKey(&IgnoreKeyValueType, + CharacterBuffer, + &ActualStringLength, + &LastWriteTime); + + if (NT_SUCCESS(NtStatus)) { + if (ActualStringLength != StringLength) { + + // + // Hmmm - we just queuried the length and got StringLength. + // Then we read the buffer and its different, yet the + // whole time we've held the write lock. Something + // has screwed up our database. + // + + NtStatus = STATUS_INTERNAL_ERROR; + } + } + + if (!NT_SUCCESS(NtStatus)) { + + IgnoreStatus = NtClose( SubKeyHandle ); + + MIDL_user_free( CharacterBuffer ); + MIDL_user_free( StringBody ); + + return(NtStatus); + } + + + // + // Null terminate the string + // + + UnicodeTerminate(StringBody); + *String = StringBody; + + return(STATUS_SUCCESS); +} diff --git a/private/newsam2/server/upgrade.c b/private/newsam2/server/upgrade.c new file mode 100644 index 000000000..78cbe9fb1 --- /dev/null +++ b/private/newsam2/server/upgrade.c @@ -0,0 +1,1610 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + tconnect.c + +Abstract: + + This is the file for a simple connection test to SAM. + +Author: + + Jim Kelly (JimK) 4-July-1991 + +Environment: + + User Mode - Win32 + +Revision History: + + +--*/ + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include <samsrvp.h> +#include <msaudite.h> + + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Global data structures // +// // +/////////////////////////////////////////////////////////////////////////////// +ULONG AdministrativeRids[] = { + DOMAIN_ALIAS_RID_ADMINS, + DOMAIN_ALIAS_RID_SYSTEM_OPS, + DOMAIN_ALIAS_RID_PRINT_OPS, + DOMAIN_ALIAS_RID_BACKUP_OPS, + DOMAIN_ALIAS_RID_ACCOUNT_OPS + }; + +#define ADMINISTRATIVE_ALIAS_COUNT (sizeof(AdministrativeRids)/sizeof(ULONG)) + +#define RTLP_RXACT_KEY_NAME L"RXACT" +#define RTLP_RXACT_KEY_NAME_SIZE (sizeof(RTLP_RXACT_KEY_NAME) - sizeof(WCHAR)) + +#define SAMP_FIX_18471_KEY_NAME L"\\Registry\\Machine\\Security\\SAM\\Fix18471" +#define SAMP_FIX_18471_SHORT_KEY_NAME L"Fix18471" +#define SAMP_LSA_KEY_NAME L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Lsa" + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Routines // +// // +/////////////////////////////////////////////////////////////////////////////// + + + + +BOOLEAN +SampMatchDomainPrefix( + IN PSID AccountSid, + IN PSID DomainSid + ) + +/*++ + +Routine Description: + + This function compares the domain sid to the domain prefix of an + account sid. + +Arguments: + + AccountSid - Specifies the account Sid to be compared. The Sid is assumed to be + syntactically valid. + + DomainSid - Specifies the domain Sid to compare against. + + +Return Value: + + TRUE - The account Sid is from the Domain specified by the domain Sid + + FALSE - The domain prefix of the account Sid did not match the domain. + +--*/ + +{ + // + // Check if the account Sid has one more subauthority than the + // domain Sid. + // + + if (*RtlSubAuthorityCountSid(DomainSid) + 1 != + *RtlSubAuthorityCountSid(AccountSid)) { + return(FALSE); + } + + if (memcmp( + RtlIdentifierAuthoritySid(DomainSid), + RtlIdentifierAuthoritySid(AccountSid), + sizeof(SID_IDENTIFIER_AUTHORITY) ) ) { + + return(FALSE); + } + + // + // Compare the sub authorities + // + + if (memcmp( + RtlSubAuthoritySid(DomainSid, 0) , + RtlSubAuthoritySid(AccountSid, 0) , + *RtlSubAuthorityCountSid(DomainSid) + )) + { + return(FALSE); + } + + return(TRUE); + +} + + + +NTSTATUS +SampCreate18471Key( + ) +/*++ + +Routine Description: + + This routine creates the 18471 key used to transaction this fix. + +Arguments: + + +Return Value: + + Codes from the NT registry APIs + +--*/ +{ + NTSTATUS Status; + UNICODE_STRING KeyName; + + + // + // Open the 18471 key in the registry to see if an upgrade is in + // progress + // + + + // + // Start a transaction with to create this key. + // + + Status = SampAcquireWriteLock(); + + if (!NT_SUCCESS(Status)) { + return(Status); + } + + SampSetTransactionDomain(0); + SampTransactionWithinDomain = FALSE; + + // + // Create the fix18471 key in the registry + // + + RtlInitUnicodeString( + &KeyName, + SAMP_FIX_18471_SHORT_KEY_NAME + ); + + Status = RtlAddActionToRXact( + SampRXactContext, + RtlRXactOperationSetValue, + &KeyName, + 0, // no value type + NULL, // no value + 0 // no value length + ); + + // + // Commit this change + // + + if (NT_SUCCESS(Status)) { + Status = SampReleaseWriteLock( TRUE ); + } else { + (void) SampReleaseWriteLock( FALSE ); + } + + return(Status); +} + +NTSTATUS +SampAddAliasTo18471Key( + IN ULONG AliasRid + ) +/*++ + +Routine Description: + + This routine creates the 18471 key used to transaction this fix. + +Arguments: + + +Return Value: + + Codes from the NT registry APIs + +--*/ +{ + NTSTATUS Status; + WCHAR KeyName[100]; + WCHAR AliasName[15]; // big enough for 4 billion + UNICODE_STRING KeyNameString; + UNICODE_STRING AliasString; + + // + // Build the key name. It will be "fix18471\rid_in_hex" + // + + wcscpy( + KeyName, + SAMP_FIX_18471_SHORT_KEY_NAME L"\\" + ); + + AliasString.Buffer = AliasName; + AliasString.MaximumLength = sizeof(AliasName); + Status = RtlIntegerToUnicodeString( + AliasRid, + 16, + &AliasString + ); + ASSERT(NT_SUCCESS(Status)); + + wcscat( + KeyName, + AliasString.Buffer + ); + + RtlInitUnicodeString( + &KeyNameString, + KeyName + ); + + + Status = SampAcquireWriteLock(); + + if (!NT_SUCCESS(Status)) { + return(Status); + } + + SampSetTransactionDomain(0); + SampTransactionWithinDomain = FALSE; + + // + // Open the Lsa key in the registry + // + + Status = RtlAddActionToRXact( + SampRXactContext, + RtlRXactOperationSetValue, + &KeyNameString, + 0, // no value type + NULL, // no value + 0 // no value length + ); + + // + // Commit this change + // + + if (NT_SUCCESS(Status)) { + Status = SampReleaseWriteLock( TRUE ); + + } else { + (void) SampReleaseWriteLock( FALSE ); + } + + return(Status); +} + + + +NTSTATUS +SampAddMemberRidTo18471Key( + IN ULONG AliasRid, + IN ULONG MemberRid + ) +/*++ + +Routine Description: + + This routine adds a key for this member under the key for this alias + to the current registry transaction. + +Arguments: + + AliasRid - the rid of the alias + + MemberRid - The rid of the member of the alias + +Returns: + + Errors from the RtlRXact APIs + +--*/ +{ + NTSTATUS Status; + WCHAR KeyName[100]; + WCHAR AliasName[15]; // big enough for 4 billion + UNICODE_STRING KeyNameString; + UNICODE_STRING AliasString; + + + // + // Build the full key name. It is of the form: + // "fix18471\alias_rid\member_rid" + // + + wcscpy( + KeyName, + SAMP_FIX_18471_SHORT_KEY_NAME L"\\" + ); + + AliasString.Buffer = AliasName; + AliasString.MaximumLength = sizeof(AliasName); + Status = RtlIntegerToUnicodeString( + AliasRid, + 16, + &AliasString + ); + ASSERT(NT_SUCCESS(Status)); + + wcscat( + KeyName, + AliasString.Buffer + ); + + wcscat( + KeyName, + L"\\" + ); + + AliasString.MaximumLength = sizeof(AliasName); + Status = RtlIntegerToUnicodeString( + MemberRid, + 16, + &AliasString + ); + ASSERT(NT_SUCCESS(Status)); + + wcscat( + KeyName, + AliasString.Buffer + ); + + RtlInitUnicodeString( + &KeyNameString, + KeyName + ); + + // + // Add this action to the RXact + // + + Status = RtlAddActionToRXact( + SampRXactContext, + RtlRXactOperationSetValue, + &KeyNameString, + 0, // no value type + NULL, // no value + 0 // no value length + ); + + return(Status); + +} + +NTSTATUS +SampCheckMemberUpgradedFor18471( + IN ULONG AliasRid, + IN ULONG MemberRid + ) +/*++ + +Routine Description: + + This routine checks if the SAM upgrade flag is set. The upgrade + flag is: + + HKEY_LOCAL_MACHINE\System\CurrentControlSet\control\lsa + UpgradeSam = REG_DWORD 1 + + +Arguments: + + +Return Value: + + TRUE - The flag was set + + FALSE - The flag was not set or the value was not present + +--*/ +{ + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE KeyHandle; + NTSTATUS Status; + WCHAR KeyName[100]; + WCHAR AliasName[15]; // big enough for 4 billion + UNICODE_STRING KeyNameString; + UNICODE_STRING AliasString; + + + // + // Build the full key name. It is of the form: + // "fix18471\alias_rid\member_rid" + // + + wcscpy( + KeyName, + SAMP_FIX_18471_KEY_NAME L"\\" + ); + + AliasString.Buffer = AliasName; + AliasString.MaximumLength = sizeof(AliasName); + Status = RtlIntegerToUnicodeString( + AliasRid, + 16, + &AliasString + ); + ASSERT(NT_SUCCESS(Status)); + + wcscat( + KeyName, + AliasString.Buffer + ); + + wcscat( + KeyName, + L"\\" + ); + + AliasString.MaximumLength = sizeof(AliasName); + Status = RtlIntegerToUnicodeString( + MemberRid, + 16, + &AliasString + ); + ASSERT(NT_SUCCESS(Status)); + + wcscat( + KeyName, + AliasString.Buffer + ); + + RtlInitUnicodeString( + &KeyNameString, + KeyName + ); + + + // + // Open the member key in the registry + // + + + InitializeObjectAttributes( + &ObjectAttributes, + &KeyNameString, + OBJ_CASE_INSENSITIVE, + 0, + NULL + ); + + SampDumpNtOpenKey((KEY_READ), &ObjectAttributes, 0); + + Status = NtOpenKey( + &KeyHandle, + KEY_READ, + &ObjectAttributes + ); + + NtClose(KeyHandle); + return(Status); + +} + +VOID +SampBuild18471CleanupKey( + OUT PUNICODE_STRING KeyName, + IN PWCHAR AliasName, + IN ULONG AliasNameLength, + IN PWCHAR MemberName, + IN ULONG MemberNameLength + ) +/*++ + +Routine Description: + + Builds the key "Fix18471\alias_rid\member_rid" + +Arguments: + + +Return Value: + + None + +--*/ +{ + PUCHAR Where = (PUCHAR) KeyName->Buffer; + + RtlCopyMemory( + Where, + SAMP_FIX_18471_SHORT_KEY_NAME L"\\", + sizeof(SAMP_FIX_18471_SHORT_KEY_NAME) // terminating NULL used for '\' + ); + + Where += sizeof(SAMP_FIX_18471_SHORT_KEY_NAME); + + RtlCopyMemory( + Where, + AliasName, + AliasNameLength + ); + Where += AliasNameLength; + + // + // If there is a member name to this alias, add it now. + // + + if (MemberName != NULL) { + RtlCopyMemory( + Where, + L"\\", + sizeof(WCHAR) + ); + Where += sizeof(WCHAR); + + RtlCopyMemory( + Where, + MemberName, + MemberNameLength + ); + Where += MemberNameLength; + + } + + KeyName->Length = (USHORT) (Where - (PUCHAR) KeyName->Buffer); + ASSERT(KeyName->Length <= KeyName->MaximumLength); +} + + +NTSTATUS +SampCleanup18471( + ) +/*++ + +Routine Description: + + Cleans up the transaction log left by fixing bug 18471. This routine + builds a transaction with all the keys in the log and then commits + the transaction + +Arguments: + + None. + +Return Value: + + Status codes from the NT registry APIs and NT RXact APIs + +--*/ +{ + OBJECT_ATTRIBUTES ObjectAttributes; + NTSTATUS Status; + HANDLE RootKey = NULL; + HANDLE AliasKey = NULL; + UCHAR Buffer[sizeof(KEY_BASIC_INFORMATION) + 15 * sizeof(WCHAR)]; + UCHAR Buffer2[sizeof(KEY_BASIC_INFORMATION) + 15 * sizeof(WCHAR)]; + UNICODE_STRING KeyName; + WCHAR KeyBuffer[100]; + PKEY_BASIC_INFORMATION BasicInfo = (PKEY_BASIC_INFORMATION) Buffer; + PKEY_BASIC_INFORMATION BasicInfo2 = (PKEY_BASIC_INFORMATION) Buffer2; + ULONG BasicInfoLength; + ULONG Index, Index2; + + // + // Open the 18471 key in the registry + // + + RtlInitUnicodeString( + &KeyName, + SAMP_FIX_18471_KEY_NAME + ); + + InitializeObjectAttributes( + &ObjectAttributes, + &KeyName, + OBJ_CASE_INSENSITIVE, + 0, + NULL + ); + + SampDumpNtOpenKey((KEY_READ | DELETE), &ObjectAttributes, 0); + + Status = NtOpenKey( + &RootKey, + KEY_READ | DELETE, + &ObjectAttributes + ); + + if (!NT_SUCCESS(Status)) { + + // + // If the error was that the key did not exist, then there + // is nothing to cleanup, so return success. + // + + if (Status == STATUS_OBJECT_NAME_NOT_FOUND) { + return(STATUS_SUCCESS); + } + return(Status); + } + + // + // Create a transaction to add all the keys to delete to + // + + Status = SampAcquireWriteLock(); + if (!NT_SUCCESS(Status)) { + goto Cleanup; + } + + SampSetTransactionDomain(0); + SampTransactionWithinDomain = FALSE; + + // + // Now enumerate all the subkeys of the root 18471 key + // + + Index = 0; + do + { + + Status = NtEnumerateKey( + RootKey, + Index, + KeyBasicInformation, + BasicInfo, + sizeof(Buffer), + &BasicInfoLength + ); + + SampDumpNtEnumerateKey(Index, + KeyBasicInformation, + BasicInfo, + sizeof(Buffer), + &BasicInfoLength); + + // + // + // Check if this is the RXACT key. If it is, we don't want + // to add it to the delete log. + // + // Otherwise open this key and enumerate all the subkeys of it. + // + + if (NT_SUCCESS(Status) && + ((BasicInfo->NameLength != RTLP_RXACT_KEY_NAME_SIZE) || + memcmp( + BasicInfo->Name, + RTLP_RXACT_KEY_NAME, + RTLP_RXACT_KEY_NAME_SIZE + ) ) ) { + + KeyName.Buffer = BasicInfo->Name; + KeyName.Length = (USHORT) BasicInfo->NameLength; + KeyName.MaximumLength = KeyName.Length; + + InitializeObjectAttributes( + &ObjectAttributes, + &KeyName, + OBJ_CASE_INSENSITIVE, + RootKey, + NULL + ); + + // + // Open the key for the alias rid. This really should + // succeed + // + + SampDumpNtOpenKey((KEY_READ), &ObjectAttributes, 0); + + Status = NtOpenKey( + &AliasKey, + KEY_READ, + &ObjectAttributes + ); + if (!NT_SUCCESS(Status)) { + break; + } + + // + // Enumerate all the subkeys (the alias members) and add them + // to the transaction + // + + Index2 = 0; + do + { + Status = NtEnumerateKey( + AliasKey, + Index2, + KeyBasicInformation, + BasicInfo2, + sizeof(Buffer2), + &BasicInfoLength + ); + + SampDumpNtEnumerateKey(Index2, + KeyBasicInformation, + BasicInfo2, + sizeof(Buffer2), + &BasicInfoLength); + + if (NT_SUCCESS(Status)) { + + // + // Build the name of this key from the alias rid and the + // member rid + // + + KeyName.Buffer = KeyBuffer; + KeyName.MaximumLength = sizeof(KeyBuffer); + + SampBuild18471CleanupKey( + &KeyName, + BasicInfo->Name, + BasicInfo->NameLength, + BasicInfo2->Name, + BasicInfo2->NameLength + ); + + Status = RtlAddActionToRXact( + SampRXactContext, + RtlRXactOperationDelete, + &KeyName, + 0, + NULL, + 0 + ); + + + } + Index2++; + + } while (NT_SUCCESS(Status)); + + NtClose(AliasKey); + AliasKey = NULL; + + // + // If we suffered a serious error, get out of here now + // + + if (!NT_SUCCESS(Status)) { + if (Status != STATUS_NO_MORE_ENTRIES) { + break; + } else { + Status = STATUS_SUCCESS; + } + } + + // + // Add the alias RID key to the RXact now - we need to add it + // after deleting all the children + // + + KeyName.Buffer = KeyBuffer; + KeyName.MaximumLength = sizeof(KeyBuffer); + SampBuild18471CleanupKey( + &KeyName, + BasicInfo->Name, + BasicInfo->NameLength, + NULL, + 0 + ); + + + Status = RtlAddActionToRXact( + SampRXactContext, + RtlRXactOperationDelete, + &KeyName, + 0, + NULL, + 0 + ); + + } + + Index++; + } while (NT_SUCCESS(Status)); + + if (Status == STATUS_NO_MORE_ENTRIES) { + Status = STATUS_SUCCESS; + } + + if (!NT_SUCCESS(Status)) { + goto Cleanup; + } + + + RtlInitUnicodeString( + &KeyName, + SAMP_FIX_18471_SHORT_KEY_NAME + ); + + Status = RtlAddActionToRXact( + SampRXactContext, + RtlRXactOperationDelete, + &KeyName, + 0, + NULL, + 0 + ); + + if (NT_SUCCESS(Status)) { + + // + // Write the new server revision to indicate that this + // upgrade has been performed + // + + ULONG Revision = SAMP_SERVER_REVISION; + PSAMP_OBJECT ServerContext; + + // + // We need to read the fixed attributes of the server objects. + // Create a context to do that. + // + + ServerContext = SampCreateContext( SampServerObjectType, TRUE ); + + if ( ServerContext != NULL ) { + + ServerContext->RootKey = SampKey; + + Status = SampSetFixedAttributes( + ServerContext, + &Revision + ); + if (NT_SUCCESS(Status)) { + Status = SampStoreObjectAttributes( + ServerContext, + TRUE + ); + } + + SampDeleteContext( ServerContext ); + } else { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + + // + // Apply the RXACT and delete the remaining keys. + // + +Cleanup: + + // + // Cleanup any floating bits from above. + // + + if (NT_SUCCESS(Status)) { + Status = SampReleaseWriteLock( TRUE ); + } else { + (VOID) SampReleaseWriteLock( FALSE ); + } + + if (RootKey != NULL) { + NtClose(RootKey); + } + + ASSERT(AliasKey == NULL); + + + return(Status); + +} + +NTSTATUS +SampFixBug18471 ( + IN ULONG Revision + ) +/*++ + +Routine Description: + + This routine fixes bug 18471, that SAM does not adjust the protection + on groups that are members of administrative aliases in the builtin + domain. It fixes this by opening a fixed set of known aliases + (Administrators, Account Operators, Backup Operators, Print Operators, + and Server Operators), and enumerating their members. To fix this, + we will remove all the members of these aliases (except the + Administrator user account) and re-add them. + +Arguments: + + Revision - Revision of the Sam server. + +Return Value: + + + Note: + + +--*/ +{ + NTSTATUS Status; + ULONG Index, Index2; + PSID BuiltinDomainSid = NULL; + SID_IDENTIFIER_AUTHORITY BuiltinAuthority = SECURITY_NT_AUTHORITY; + PSID AccountDomainSid; + ULONG AccountDomainIndex = 0xffffffff; + ULONG BuiltinDomainIndex = 0xffffffff; + SAMPR_PSID_ARRAY AliasMembership; + ULONG MemberRid; + ULONG SdRevision; + PSECURITY_DESCRIPTOR OldDescriptor; + PSECURITY_DESCRIPTOR SecurityDescriptor; + ULONG SecurityDescriptorLength; + SAMP_OBJECT_TYPE MemberType; + PSAMP_OBJECT MemberContext; + PSAMP_OBJECT AliasContext; + SAMP_V1_0A_FIXED_LENGTH_GROUP GroupV1Fixed; + SAMP_V1_0A_FIXED_LENGTH_USER UserV1Fixed; + + // + // Check the revision on the server to see if this upgrade has + // already been performed. + // + + + if (Revision >= SAMP_SERVER_REVISION) { + + // + // This upgrade has already been performed. + // + + goto Cleanup; + } + + + // + // Build a the BuiltIn domain SID. + // + + BuiltinDomainSid = RtlAllocateHeap(RtlProcessHeap(), 0,RtlLengthRequiredSid( 1 )); + + if ( BuiltinDomainSid == NULL ) { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto Cleanup; + } + + RtlInitializeSid( BuiltinDomainSid, &BuiltinAuthority, 1 ); + *(RtlSubAuthoritySid( BuiltinDomainSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID; + + + // + // Lookup the index of the account domain + // + + for (Index = 0; + Index < SampDefinedDomainsCount ; + Index++ ) { + + if (RtlEqualSid( BuiltinDomainSid, SampDefinedDomains[Index].Sid)) { + BuiltinDomainIndex = Index; + } else { + AccountDomainIndex = Index; + } + } + + ASSERT(AccountDomainIndex < SampDefinedDomainsCount); + ASSERT(BuiltinDomainIndex < SampDefinedDomainsCount); + + AccountDomainSid = SampDefinedDomains[AccountDomainIndex].Sid; + + // + // Create out transaction log + // + + Status = SampCreate18471Key(); + if (!NT_SUCCESS(Status)) { + goto Cleanup; + } + + + + + // + // Now loop through and open the aliases we are intersted in + // + + for (Index = 0; + Index < ADMINISTRATIVE_ALIAS_COUNT ; + Index++ ) + { + + SampSetTransactionDomain( BuiltinDomainIndex ); + + SampAcquireReadLock(); + + Status = SampCreateAccountContext( + SampAliasObjectType, + AdministrativeRids[Index], + TRUE, // Trusted client + TRUE, // Account exists + &AliasContext + ); + + if ( !NT_SUCCESS(Status) ) { + + SampReleaseReadLock(); + if (Status == STATUS_NO_SUCH_ALIAS) { + Status = STATUS_SUCCESS; + continue; + } else { + + goto Cleanup; + } + } + + + // + // Get the members in the alias so we can remove and re-add them + // + + Status = SampRetrieveAliasMembers( + AliasContext, + &(AliasMembership.Count), + (PSID **)&(AliasMembership.Sids) + ); + + SampDeleteContext(AliasContext); + SampReleaseReadLock(); + if (!NT_SUCCESS(Status)) { + break; + } + + // + // Write that we are opening this alias to the log. We don't need + // to do this for administrators, since for them we the update is + // idempotent. + // + + if (AdministrativeRids[Index] != DOMAIN_ALIAS_RID_ADMINS) { + Status = SampAddAliasTo18471Key( + AdministrativeRids[Index] + ); + if (!NT_SUCCESS(Status)) { + break; + } + } + + + // + // Loop through the members and split each sid. For every + // member in the account domain , remove it and re-add it from + // this alias. + // + + + + + for (Index2 = 0; Index2 < AliasMembership.Count ; Index2++ ) + { + // + // Check to see if this account is in the account domain + // + + if ( SampMatchDomainPrefix( + (PSID) AliasMembership.Sids[Index2].SidPointer, + AccountDomainSid + ) ) + { + + // + // Get the RID for this member + // + + MemberRid = *RtlSubAuthoritySid( + AliasMembership.Sids[Index2].SidPointer, + *RtlSubAuthorityCountSid( + AliasMembership.Sids[Index2].SidPointer + ) - 1 + ); + + // + // Now remove and re-add the administratie nature of this + // membership + // + + if (AdministrativeRids[Index] == DOMAIN_ALIAS_RID_ADMINS) { + + Status = SampAcquireWriteLock(); + if (!NT_SUCCESS(Status)) { + break; + } + + SampSetTransactionDomain( AccountDomainIndex ); + + // + // Try to create a context for the account as a group. + // + + Status = SampCreateAccountContext( + SampGroupObjectType, + MemberRid, + TRUE, // Trusted client + TRUE, // Account exists + &MemberContext + ); + + if (!NT_SUCCESS( Status ) ) { + + // + // If this ID does not exist as a group, that's fine - + // it might be a user or might have been deleted. + // + + SampReleaseWriteLock( FALSE ); + if (Status == STATUS_NO_SUCH_GROUP) { + Status = STATUS_SUCCESS; + continue; + } + break; + } + + // + // Now set a flag in the group itself, + // so that when users are added and removed + // in the future it is known whether this + // group is in an ADMIN alias or not. + // + + Status = SampRetrieveGroupV1Fixed( + MemberContext, + &GroupV1Fixed + ); + + if ( NT_SUCCESS(Status)) { + + GroupV1Fixed.AdminCount = 1; + + Status = SampReplaceGroupV1Fixed( + MemberContext, + &GroupV1Fixed + ); + // + // Modify the security descriptor to + // prevent account operators from adding + // anybody to this group + // + + if ( NT_SUCCESS( Status ) ) { + + Status = SampGetAccessAttribute( + MemberContext, + SAMP_GROUP_SECURITY_DESCRIPTOR, + FALSE, // don't make copy + &SdRevision, + &OldDescriptor + ); + + if (NT_SUCCESS(Status)) { + + Status = SampModifyAccountSecurity( + SampGroupObjectType, + TRUE, // this is an admin + OldDescriptor, + &SecurityDescriptor, + &SecurityDescriptorLength + ); + } + + if ( NT_SUCCESS( Status ) ) { + + // + // Write the new security descriptor into the object + // + + Status = SampSetAccessAttribute( + MemberContext, + SAMP_USER_SECURITY_DESCRIPTOR, + SecurityDescriptor, + SecurityDescriptorLength + ); + + MIDL_user_free( SecurityDescriptor ); + } + + + + } + if (NT_SUCCESS(Status)) { + + // + // Add the modified group to the current transaction + // Don't use the open key handle since we'll be deleting the context. + // + + Status = SampStoreObjectAttributes(MemberContext, FALSE); + + } + + } + + // + // Clean up the group context + // + + SampDeleteContext(MemberContext); + + // + // we don't want the modified count to change + // + + SampTransactionWithinDomain = FALSE; + + if (NT_SUCCESS(Status)) { + Status = SampReleaseWriteLock( TRUE ); + } else { + (VOID) SampReleaseWriteLock( FALSE ); + } + + } + else + { + + + // + // Check to see if we've already upgraded this member + // + + Status = SampCheckMemberUpgradedFor18471( + AdministrativeRids[Index], + MemberRid); + + if (NT_SUCCESS(Status)) { + + // + // This member already was upgraded. + // + + continue; + } else { + + // + // We continue on with the upgrade + // + + Status = STATUS_SUCCESS; + } + + // + // Change the operator account for the other + // aliases. + // + + if (NT_SUCCESS(Status)) { + + Status = SampAcquireWriteLock(); + if (!NT_SUCCESS(Status)) { + break; + } + + SampSetTransactionDomain( AccountDomainIndex ); + + Status = SampChangeAccountOperatorAccessToMember( + AliasMembership.Sids[Index2].SidPointer, + NoChange, + AddToAdmin + ); + + // + // If that succeeded, add this member to the log + // as one that was upgraded. + // + + if (NT_SUCCESS(Status)) { + Status = SampAddMemberRidTo18471Key( + AdministrativeRids[Index], + MemberRid + ); + + } + + // + // We don't want the modified count to be updated so + // make this not a domain transaction + // + + SampTransactionWithinDomain = FALSE; + if (NT_SUCCESS(Status)) { + Status = SampReleaseWriteLock( TRUE ); + } else { + (VOID) SampReleaseWriteLock( FALSE ); + } + + } + + if (!NT_SUCCESS(Status)) { + break; + } + + } + } + } + + SamIFree_SAMPR_PSID_ARRAY( + &AliasMembership + ); + AliasMembership.Sids = NULL; + + + // + // If something up above failed or the upgrade was already done, + // exit now. + // + + if (!NT_SUCCESS(Status)) { + break; + } + } + +Cleanup: + + if (BuiltinDomainSid != NULL) { + RtlFreeHeap( + RtlProcessHeap(), + 0, + BuiltinDomainSid + ); + } + + if (NT_SUCCESS(Status)) { + Status = SampCleanup18471(); + } + return(Status); +} + +#if 0 + +BOOLEAN +SampUpgradeFlagSet( + ) +/*++ + +Routine Description: + + This routine checks if the SAM upgrade flag is set. The upgrade + flag is: + + HKEY_LOCAL_MACHINE\System\CurrentControlSet\control\lsa + UpgradeSam = REG_DWORD 1 + + +Arguments: + + +Return Value: + + TRUE - The flag was set + + FALSE - The flag was not set or the value was not present + +--*/ +{ + NTSTATUS NtStatus; + UNICODE_STRING KeyName; + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE KeyHandle; + UCHAR Buffer[100]; + PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation = (PKEY_VALUE_PARTIAL_INFORMATION) Buffer; + ULONG KeyValueLength = 100; + ULONG ResultLength; + PULONG UpgradeFlag; + + // + // Open the Lsa key in the registry + // + + RtlInitUnicodeString( + &KeyName, + SAMP_LSA_KEY_NAME + ); + + InitializeObjectAttributes( + &ObjectAttributes, + &KeyName, + OBJ_CASE_INSENSITIVE, + 0, + NULL + ); + + SampDumpNtOpenKey((KEY_READ), &ObjectAttributes, 0); + + NtStatus = NtOpenKey( + &KeyHandle, + KEY_READ, + &ObjectAttributes + ); + + if (!NT_SUCCESS(NtStatus)) { + return(FALSE); + } + + // + // Query the Notification Packages value + // + + RtlInitUnicodeString( + &KeyName, + L"UpgradeSam" + ); + + NtStatus = NtQueryValueKey( + KeyHandle, + &KeyName, + KeyValuePartialInformation, + KeyValueInformation, + KeyValueLength, + &ResultLength + ); + + SampDumpNtQueryValueKey(&KeyName, + KeyValuePartialInformation, + KeyValueInformation, + KeyValueLength, + &ResultLength); + + NtClose(KeyHandle); + + if (!NT_SUCCESS(NtStatus)) { + + return(FALSE); + } + + // + // Check that the data is the correct size and type - a ULONG. + // + + if ((KeyValueInformation->DataLength < sizeof(ULONG)) || + (KeyValueInformation->Type != REG_DWORD)) { + + return(FALSE); + } + + // + // Check the flag. + // + + UpgradeFlag = (PULONG) KeyValueInformation->Data; + + if (*UpgradeFlag == 1) { + return(TRUE); + } + + return(FALSE); + + +} + +BOOLEAN +SampSetUpgradeFlag( + ) +/*++ + +Routine Description: + + This routine sets SAM upgrade flag is set. The upgrade + flag is: + + HKEY_LOCAL_MACHINE\System\CurrentControlSet\control\lsa + UpgradeSam = REG_DWORD 1 + + and the value will be deleted. + + +Arguments: + + +Return Value: + + TRUE - The flag was set + + FALSE - The flag was not set or the value was not present + +--*/ +{ + NTSTATUS NtStatus; + UNICODE_STRING KeyName; + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE KeyHandle; + UCHAR Buffer[100]; + PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation = (PKEY_VALUE_PARTIAL_INFORMATION) Buffer; + ULONG KeyValueLength = 100; + ULONG ResultLength; + PULONG UpgradeFlag; + + // + // Open the Lsa key in the registry + // + + RtlInitUnicodeString( + &KeyName, + SAMP_LSA_KEY_NAME + ); + + InitializeObjectAttributes( + &ObjectAttributes, + &KeyName, + OBJ_CASE_INSENSITIVE, + 0, + NULL + ); + + SampDumpNtOpenKey((KEY_SET_VALUE), &ObjectAttributes, 0); + + NtStatus = NtOpenKey( + &KeyHandle, + KEY_SET_VALUE, + &ObjectAttributes + ); + + if (!NT_SUCCESS(NtStatus)) { + return(FALSE); + } + + // + // Query the Notification Packages value + // + + RtlInitUnicodeString( + &KeyName, + L"UpgradeSam" + ); + + NtStatus = NtDeleteValueKey( + KeyHandle, + &KeyName + ); + + NtClose(KeyHandle); + + +} +#endif + +NTSTATUS +SampUpgradeSamDatabase( + IN ULONG Revision + ) +/*++ + +Routine Description: + + Upgrades the SAM database. + +Arguments: + + Revision - The revision stored in the Server fixed length attributes + +Return Value: + + + Note: + + +--*/ +{ + NTSTATUS Status; + Status = SampFixBug18471(Revision); + + return(Status); +} diff --git a/private/newsam2/server/user.c b/private/newsam2/server/user.c new file mode 100644 index 000000000..7351cf402 --- /dev/null +++ b/private/newsam2/server/user.c @@ -0,0 +1,11284 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + user.c + +Abstract: + + This file contains services related to the SAM "user" object. + + +Author: + + Jim Kelly (JimK) 4-July-1991 + +Environment: + + User Mode - Win32 + +Revision History: + + +--*/ + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include <samsrvp.h> +#include <lmcons.h> +#include <nturtl.h> +#include <ntlsa.h> // need for nlrepl.h +#include <nlrepl.h> // I_NetNotifyMachineAccount prototype +#include <msaudite.h> +#include <rc4.h> // rc4_key(), rc4() + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// private service prototypes // +// // +/////////////////////////////////////////////////////////////////////////////// + +LARGE_INTEGER +SampGetPasswordMustChange( + IN ULONG UserAccountControl, + IN LARGE_INTEGER PasswordLastSet, + IN LARGE_INTEGER MaxPasswordAge + ); + +NTSTATUS +SampComputePasswordExpired( + IN BOOLEAN PasswordExpired, + OUT PLARGE_INTEGER PasswordLastSet + ); + +NTSTATUS +SampStorePasswordExpired( + IN PSAMP_OBJECT Context, + IN BOOLEAN PasswordExpired + ); + +NTSTATUS +SampStoreUserPasswords( + IN PSAMP_OBJECT Context, + IN PLM_OWF_PASSWORD LmOwfPassword, + IN BOOLEAN LmPasswordPresent, + IN PNT_OWF_PASSWORD NtOwfPassword, + IN BOOLEAN NtPasswordPresent, + IN BOOLEAN CheckHistory + ); + +NTSTATUS +SampRetrieveUserPasswords( + IN PSAMP_OBJECT Context, + OUT PLM_OWF_PASSWORD LmOwfPassword, + OUT PBOOLEAN LmPasswordNonNull, + OUT PNT_OWF_PASSWORD NtOwfPassword, + OUT PBOOLEAN NtPasswordPresent, + OUT PBOOLEAN NtPasswordNonNull + ); + +NTSTATUS +SampRetrieveUserMembership( + IN PSAMP_OBJECT UserContext, + IN BOOLEAN MakeCopy, + OUT PULONG MembershipCount, + OUT PGROUP_MEMBERSHIP *Membership OPTIONAL + ); + +NTSTATUS +SampReplaceUserMembership( + IN PSAMP_OBJECT UserContext, + IN ULONG MembershipCount, + IN PGROUP_MEMBERSHIP Membership + ); + +NTSTATUS +SampRetrieveUserLogonHours( + IN PSAMP_OBJECT Context, + OUT PLOGON_HOURS LogonHours + ); + +NTSTATUS +SampReplaceUserLogonHours( + IN PSAMP_OBJECT Context, + IN PLOGON_HOURS LogonHours + ); + + +NTSTATUS +SampAssignPrimaryGroup( + IN PSAMP_OBJECT Context, + IN ULONG GroupRid + ); + +NTSTATUS +SampDeleteUserKeys( + IN PSAMP_OBJECT Context + ); + +NTSTATUS +SampCheckPasswordHistory( + IN PVOID EncryptedPassword, + IN ULONG EncryptedPasswordLength, + IN USHORT PasswordHistoryLength, + IN ULONG HistoryAttributeIndex, + IN PSAMP_OBJECT Context, + IN BOOLEAN CheckHistory, + OUT PUNICODE_STRING OwfHistoryBuffer + ); + +NTSTATUS +SampAddPasswordHistory( + IN PSAMP_OBJECT Context, + IN ULONG HistoryAttributeIndex, + IN PUNICODE_STRING NtOwfHistoryBuffer, + IN PVOID EncryptedPassword, + IN ULONG EncryptedPasswordLength, + IN USHORT PasswordHistoryLength + ); + +NTSTATUS +SampMatchworkstation( + IN PUNICODE_STRING LogonWorkStation, + IN PUNICODE_STRING WorkStations + ); + +NTSTATUS +SampChangeUserAccountName( + IN PSAMP_OBJECT Context, + IN PUNICODE_STRING NewAccountName, + OUT PUNICODE_STRING OldAccountName + ); + +USHORT +SampQueryBadPasswordCount( + PSAMP_OBJECT UserContext, + PSAMP_V1_0A_FIXED_LENGTH_USER V1aFixed + ); +BOOLEAN +SampIncrementBadPasswordCount( + PSAMP_OBJECT UserContext, + PSAMP_V1_0A_FIXED_LENGTH_USER V1aFixed + ); +VOID +SampUpdateAccountLockedOutFlag( + PSAMP_OBJECT Context, + PSAMP_V1_0A_FIXED_LENGTH_USER V1aFixed, + PBOOLEAN IsLocked + ); + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Routines // +// // +/////////////////////////////////////////////////////////////////////////////// + + + + +NTSTATUS +SamrOpenUser( + IN SAMPR_HANDLE DomainHandle, + IN ACCESS_MASK DesiredAccess, + IN ULONG UserId, + OUT SAMPR_HANDLE *UserHandle + ) + + +/*++ + + This API opens an existing user in the account database. The user + is specified by a ID value that is relative to the SID of the + domain. The operations that will be performed on the user must be + declared at this time. + + This call returns a handle to the newly opened user that may be + used for successive operations on the user. This handle may be + closed with the SamCloseHandle API. + + + +Parameters: + + DomainHandle - A domain handle returned from a previous call to + SamOpenDomain. + + DesiredAccess - Is an access mask indicating which access types + are desired to the user. These access types are reconciled + with the Discretionary Access Control list of the user to + determine whether the accesses will be granted or denied. + + UserId - Specifies the relative ID value of the user to be + opened. + + UserHandle - Receives a handle referencing the newly opened + user. This handle will be required in successive calls to + operate on the user. + +Return Values: + + STATUS_SUCCESS - The user was successfully opened. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_NO_SUCH_USER - The specified user does not exist. + + STATUS_INVALID_HANDLE - The domain handle passed is invalid. + +--*/ +{ + NTSTATUS NtStatus, IgnoreStatus; + SAMP_OBJECT_TYPE FoundType; + + SAMTRACE("SamrOpenUser"); + + NtStatus = SampOpenAccount( + SampUserObjectType, + DomainHandle, + DesiredAccess, + UserId, + FALSE, + UserHandle + ); + + if ( NT_SUCCESS( NtStatus ) ) { + NTSTATUS NtStatus1; + + // + // If the domain handle allows reading the password + // parameters, note that in the context to make life + // easy for SampGetUserDomainPasswordInformation(). + // + + SampAcquireReadLock(); + + NtStatus1 = SampLookupContext( + DomainHandle, + DOMAIN_READ_PASSWORD_PARAMETERS, // DesiredAccess + SampDomainObjectType, // ExpectedType + &FoundType + ); + + if ( NT_SUCCESS( NtStatus1 ) ) { + + ((PSAMP_OBJECT)(*UserHandle))->TypeBody.User.DomainPasswordInformationAccessible = TRUE; + + // + // De-reference the object, discarding changes + // + + IgnoreStatus = SampDeReferenceContext( DomainHandle, FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + } else { + + ((PSAMP_OBJECT)(*UserHandle))->TypeBody.User.DomainPasswordInformationAccessible = FALSE; + } + + // + // Release the lock + // + + SampReleaseReadLock(); + } + + return(NtStatus); +} + + +NTSTATUS +SamrDeleteUser( + IN OUT SAMPR_HANDLE *UserHandle + ) + + +/*++ + +Routine Description: + + This API deletes a user from the account database. If the account + being deleted is the last account in the database in the ADMIN + group, then STATUS_LAST_ADMIN is returned, and the Delete fails. + + Note that following this call, the UserHandle is no longer valid. + +Parameters: + + UserHandle - The handle of an opened user to operate on. The handle must be + openned for DELETE access. + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_LAST_ADMIN - Cannot delete the last enabled administrator account + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled for this + operation + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. + +--*/ + +{ + SAMP_V1_0A_FIXED_LENGTH_USER V1aFixed; + UNICODE_STRING UserName; + NTSTATUS NtStatus, IgnoreStatus, TmpStatus; + PSAMP_OBJECT AccountContext = NULL; + PSAMP_DEFINED_DOMAINS Domain = NULL; + SAMP_OBJECT_TYPE FoundType; + PSID AccountSid = NULL; + PGROUP_MEMBERSHIP Groups = NULL; + ULONG ObjectRid, + GroupCount, + DomainIndex, + i; + + + SAMTRACE("SamrDeleteUser"); + + + + // + // Grab the lock + // + + NtStatus = SampAcquireWriteLock(); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + + // + // Validate type of, and access to object. + // + + AccountContext = (PSAMP_OBJECT)(*UserHandle); + NtStatus = SampLookupContext( + AccountContext, + DELETE, + SampUserObjectType, // ExpectedType + &FoundType + ); + + if (NT_SUCCESS(NtStatus)) { + + + ObjectRid = AccountContext->TypeBody.User.Rid; + + // + // Get a pointer to the domain this object is in. + // This is used for auditing. + // + + DomainIndex = AccountContext->DomainIndex; + Domain = &SampDefinedDomains[ DomainIndex ]; + + // + // built-in accounts can't be deleted, unless the caller is trusted + // + + if ( !AccountContext->TrustedClient ) { + + NtStatus = SampIsAccountBuiltIn( ObjectRid ); + } + + // + // Get the list of groups this user is a member of. + // Remove the user from each group. + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampRetrieveUserMembership( + AccountContext, + FALSE, // Make copy + &GroupCount, + &Groups + ); + + + if (NT_SUCCESS(NtStatus)) { + + ASSERT( GroupCount > 0); + ASSERT( Groups != NULL ); + + + // + // Remove the user from each group. + // + + for ( i=0; i<GroupCount && NT_SUCCESS(NtStatus); i++) { + + NtStatus = SampRemoveUserFromGroup( + Groups[i].RelativeId, + ObjectRid + ); + } + } + } + + // + // So far, so good. The user has been removed from all groups. + // Now remove the user from all aliases + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampCreateAccountSid(AccountContext, &AccountSid); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampRemoveAccountFromAllAliases( + AccountSid, + FALSE, + NULL, + NULL, + NULL + ); + } + } + + // + // Get the AccountControl flags for when we update + // the display cache, and to let Netlogon know if this + // is a machine account that is going away. + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampRetrieveUserV1aFixed( + AccountContext, + &V1aFixed + ); + } + + // + // Now we just need to clean up the user keys themselves. + // + + if (NT_SUCCESS(NtStatus)) { + + // + // First get and save the account name for + // I_NetNotifyLogonOfDelta. + // + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_ACCOUNT_NAME, + TRUE, // Make copy + &UserName + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // This must be done before we invalidate contexts, because our + // own handle to the group gets closed as well. + // + + NtStatus = SampDeleteUserKeys( AccountContext ); + + if (NT_SUCCESS(NtStatus)) { + + // + // We must invalidate any open contexts to this user. + // This will close all handles to the user's keys. + // THIS IS AN IRREVERSIBLE PROCESS. + // + + SampInvalidateUserContexts( ObjectRid ); + + // + // Commit the whole mess + // + + NtStatus = SampCommitAndRetainWriteLock(); + + if ( NT_SUCCESS( NtStatus ) ) { + + SAMP_ACCOUNT_DISPLAY_INFO AccountInfo; + + // + // Update the cached Alias Information + // + + IgnoreStatus = SampAlRemoveAccountFromAllAliases( + AccountSid, + FALSE, + NULL, + NULL, + NULL + ); + + // + // Update the display information + // + + AccountInfo.Name = UserName; + AccountInfo.Rid = ObjectRid; + AccountInfo.AccountControl = V1aFixed.UserAccountControl; + RtlInitUnicodeString(&AccountInfo.Comment, NULL); + RtlInitUnicodeString(&AccountInfo.FullName, NULL); + + IgnoreStatus = SampUpdateDisplayInformation(&AccountInfo, + NULL, + SampUserObjectType); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + + + // + // Audit the deletion before we free the write lock + // so that we have access to the context block. + // + + if (SampDoAccountAuditing(DomainIndex) && + NT_SUCCESS(NtStatus) ) { + + LsaIAuditSamEvent( + STATUS_SUCCESS, + SE_AUDITID_USER_DELETED, // AuditId + Domain->Sid, // Domain SID + NULL, // Member Rid (not used) + NULL, // Member Sid (not used) + &UserName, // Account Name + &Domain->ExternalName, // Domain + &ObjectRid, // Account Rid + NULL // Privileges used + ); + + } + + // + // Notify netlogon of the change + // + + SampNotifyNetlogonOfDelta( + SecurityDbDelete, + SecurityDbObjectSamUser, + ObjectRid, + &UserName, + (DWORD) FALSE, // Replicate immediately + NULL // Delta data + ); + + // + // Do delete auditing + // + + if (NT_SUCCESS(NtStatus)) { + (VOID) NtDeleteObjectAuditAlarm( + &SampSamSubsystem, + *UserHandle, + AccountContext->AuditOnClose + ); + } + + + if ( ( V1aFixed.UserAccountControl & + USER_MACHINE_ACCOUNT_MASK ) != 0 ) { + + // + // This was a machine account. Let + // NetLogon know of the change. + // + + IgnoreStatus = I_NetNotifyMachineAccount( + ObjectRid, + SampDefinedDomains[SampTransactionDomainIndex].Sid, + V1aFixed.UserAccountControl, + 0, + &UserName + ); + } + } + } + + SampFreeUnicodeString( &UserName ); + } + } + + // + // De-reference the object, discarding changes, and delete the context + // + + IgnoreStatus = SampDeReferenceContext( AccountContext, FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // If we actually deleted the user, delete the context and + // let RPC know that the handle is invalid. + // + + SampDeleteContext( AccountContext ); + + (*UserHandle) = NULL; + } + + } //end_if + + // + // Free the lock - + // + // Everything has already been committed above, so we must indicate + // no additional changes have taken place. + // + // + // + + TmpStatus = SampReleaseWriteLock( FALSE ); + + if (NtStatus == STATUS_SUCCESS) { + NtStatus = TmpStatus; + } + + // + // If necessary, free the AccountSid. + // + + if (AccountSid != NULL) { + + MIDL_user_free(AccountSid); + AccountSid = NULL; + } + + return(NtStatus); +} + + +NTSTATUS +SamrQueryInformationUser( + IN SAMPR_HANDLE UserHandle, + IN USER_INFORMATION_CLASS UserInformationClass, + OUT PSAMPR_USER_INFO_BUFFER *Buffer + ) +{ + // + // This is a thin veil to SamrQueryInformationUser2(). + // This is needed so that new-release systems can call + // this routine without the danger of passing an info + // level that release 1.0 systems didn't understand. + // + + return( SamrQueryInformationUser2(UserHandle, UserInformationClass, Buffer ) ); +} + + +NTSTATUS +SamrQueryInformationUser2( + IN SAMPR_HANDLE UserHandle, + IN USER_INFORMATION_CLASS UserInformationClass, + OUT PSAMPR_USER_INFO_BUFFER *Buffer + ) + +/*++ + +Routine Description: + + User object QUERY information routine. + +Arguments: + + UserHandle - RPC context handle for an open user object. + + UserInformationClass - Type of information being queried. + + Buffer - To receive the output (queried) information. + + +Return Value: + + + STATUS_INVALID_INFO_CLASS - An unknown information class was requested. + No information has been returned. + + STATUS_INSUFFICIENT_RESOURCES - Memory could not be allocated to + return(the requested information in. + + +--*/ +{ + + NTSTATUS NtStatus; + NTSTATUS IgnoreStatus; + PSAMP_OBJECT AccountContext; + PSAMP_DEFINED_DOMAINS Domain; + PUSER_ALL_INFORMATION All; + SAMP_OBJECT_TYPE FoundType; + ACCESS_MASK DesiredAccess; + ULONG i, WhichFields; + SAMP_V1_0A_FIXED_LENGTH_USER V1aFixed; + BOOLEAN NoErrorsYet; + LM_OWF_PASSWORD LmOwfPassword; + NT_OWF_PASSWORD NtOwfPassword; + BOOLEAN NtPasswordNonNull, LmPasswordNonNull; + BOOLEAN NtPasswordPresent; + + // + // Used for tracking allocated blocks of memory - so we can deallocate + // them in case of error. Don't exceed this number of allocated buffers. + // || + // vv + PVOID AllocatedBuffer[40]; + ULONG AllocatedBufferCount = 0; + LARGE_INTEGER TempTime; + + SAMTRACE("SamrQueryInformationUser2"); + + #define RegisterBuffer(Buffer) \ + { \ + if ((Buffer) != NULL) { \ + \ + ASSERT(AllocatedBufferCount < \ + sizeof(AllocatedBuffer) / sizeof(*AllocatedBuffer)); \ + \ + AllocatedBuffer[AllocatedBufferCount++] = (Buffer); \ + } \ + } + + #define AllocateBuffer(NewBuffer, Size) \ + { \ + (NewBuffer) = MIDL_user_allocate(Size); \ + RegisterBuffer(NewBuffer); \ + } \ + + + // + // Make sure we understand what RPC is doing for (to) us. + // + + ASSERT (Buffer != NULL); + ASSERT ((*Buffer) == NULL); + + + + // + // Set the desired access based upon information class. + // + + switch (UserInformationClass) { + + case UserInternal3Information: + case UserAllInformation: + + // + // For trusted clients, we will return everything. For + // others, we will return everything that they have access to. + // In either case, we'll have to look at some variables in the + // context so we'll do the work after the SampLookupContext() + // below. + // + + DesiredAccess = 0; + break; + + case UserAccountInformation: + + DesiredAccess = (USER_READ_GENERAL | + USER_READ_PREFERENCES | + USER_READ_LOGON | + USER_READ_ACCOUNT); + break; + + case UserGeneralInformation: + case UserPrimaryGroupInformation: + case UserNameInformation: + case UserAccountNameInformation: + case UserFullNameInformation: + case UserAdminCommentInformation: + + DesiredAccess = USER_READ_GENERAL; + break; + + + case UserPreferencesInformation: + + DesiredAccess = (USER_READ_PREFERENCES | + USER_READ_GENERAL); + break; + + + case UserLogonInformation: + + DesiredAccess = (USER_READ_GENERAL | + USER_READ_PREFERENCES | + USER_READ_LOGON | + USER_READ_ACCOUNT); + break; + + case UserLogonHoursInformation: + case UserHomeInformation: + case UserScriptInformation: + case UserProfileInformation: + case UserWorkStationsInformation: + + DesiredAccess = USER_READ_LOGON; + break; + + + case UserControlInformation: + case UserExpiresInformation: + case UserParametersInformation: + + DesiredAccess = USER_READ_ACCOUNT; + break; + + + case UserInternal1Information: + case UserInternal2Information: + + // + // These levels are only queryable by trusted clients. The code + // below will check AccountContext->TrustedClient after calling + // SampLookupContext, and only return the data if it is TRUE. + // + + DesiredAccess = (ACCESS_MASK)0; // Trusted client; no need to verify + break; + + + case UserSetPasswordInformation: // Can't query password + default: + + return(STATUS_INVALID_INFO_CLASS); + + } // end_switch + + + + + + // + // Allocate the info structure + // + + AllocateBuffer(*Buffer, sizeof(SAMPR_USER_INFO_BUFFER) ); + if ((*Buffer) == NULL) { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + + + SampAcquireReadLock(); + + + // + // Validate type of, and access to object. + // + + AccountContext = (PSAMP_OBJECT)UserHandle; + NtStatus = SampLookupContext( + AccountContext, + DesiredAccess, + SampUserObjectType, // ExpectedType + &FoundType + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // If the information level requires, retrieve the V1_FIXED record + // from the registry. + // + + switch (UserInformationClass) { + + case UserInternal3Information: + // + // Only trusted clients may query for this class. + // + + if ( !AccountContext->TrustedClient ) { + NtStatus = STATUS_INVALID_INFO_CLASS; + break; + } + + // + // Drop through to the UserAll case + // + + case UserAllInformation: { + + // + // We weren't able to check the security stuff above, so do + // it now. + // + + if ( AccountContext->TrustedClient ) { + + // + // Give everything to trusted clients, except fields that + // can't be queried at all. + // + + WhichFields = USER_ALL_READ_GENERAL_MASK | + USER_ALL_READ_LOGON_MASK | + USER_ALL_READ_ACCOUNT_MASK | + USER_ALL_READ_PREFERENCES_MASK | + USER_ALL_READ_TRUSTED_MASK; + + } else { + + + // + // Only return fields that the caller has access to. + // + + WhichFields = 0; + + if ( RtlAreAllAccessesGranted( + AccountContext->GrantedAccess, + USER_READ_GENERAL ) ) { + + WhichFields |= USER_ALL_READ_GENERAL_MASK; + } + + if ( RtlAreAllAccessesGranted( + AccountContext->GrantedAccess, + USER_READ_LOGON ) ) { + + WhichFields |= USER_ALL_READ_LOGON_MASK; + } + + if ( RtlAreAllAccessesGranted( + AccountContext->GrantedAccess, + USER_READ_ACCOUNT ) ) { + + WhichFields |= USER_ALL_READ_ACCOUNT_MASK; + } + + if ( RtlAreAllAccessesGranted( + AccountContext->GrantedAccess, + USER_READ_PREFERENCES ) ) { + + WhichFields |= USER_ALL_READ_PREFERENCES_MASK; + } + + if ( WhichFields == 0 ) { + + // + // Caller doesn't have access to ANY fields. + // + + NtStatus = STATUS_ACCESS_DENIED; + break; + } + } + } + + // + // fall through to pick up the V1aFixed information + // + + case UserGeneralInformation: + case UserPrimaryGroupInformation: + case UserPreferencesInformation: + case UserLogonInformation: + case UserAccountInformation: + case UserControlInformation: + case UserExpiresInformation: + case UserInternal2Information: + + NtStatus = SampRetrieveUserV1aFixed( + AccountContext, + &V1aFixed + ); + break; + + default: + + NtStatus = STATUS_SUCCESS; + + } // end_switch + + if (NT_SUCCESS(NtStatus)) { + + // + // case on the type information requested + // + + switch (UserInformationClass) { + + case UserInternal3Information: + case UserAllInformation: + + // + // All and Internal3 are the same except Internal3 has + // an extra field. + + All = (PUSER_ALL_INFORMATION)(*Buffer); + + RtlZeroMemory( (PVOID)All, sizeof(SAMPR_USER_INFO_BUFFER) ); + + Domain = &SampDefinedDomains[ AccountContext->DomainIndex ]; + + if ( WhichFields & ( USER_ALL_PASSWORDMUSTCHANGE | + USER_ALL_NTPASSWORDPRESENT ) ) { + + // + // These fields will need some info from + // SampRetrieveUserPasswords(). + // + + NtStatus = SampRetrieveUserPasswords( + AccountContext, + &LmOwfPassword, + &LmPasswordNonNull, + &NtOwfPassword, + &NtPasswordPresent, + &NtPasswordNonNull + ); + } + + if ( (NT_SUCCESS( NtStatus )) && + ( WhichFields & USER_ALL_USERNAME ) ) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_ACCOUNT_NAME, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->All.UserName) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer(All->UserName.Buffer); + } + } + + if ( (NT_SUCCESS( NtStatus )) && + ( WhichFields & USER_ALL_FULLNAME ) ) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_FULL_NAME, + TRUE, // Make copy + (PUNICODE_STRING)&(All->FullName) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer(All->FullName.Buffer); + } + } + + if ( (NT_SUCCESS( NtStatus )) && + ( WhichFields & USER_ALL_USERID ) ) { + + All->UserId = V1aFixed.UserId; + } + + if ( (NT_SUCCESS( NtStatus )) && + ( WhichFields & USER_ALL_PRIMARYGROUPID ) ) { + + All->PrimaryGroupId = V1aFixed.PrimaryGroupId; + } + + if ( (NT_SUCCESS( NtStatus )) && + ( WhichFields & USER_ALL_ADMINCOMMENT ) ) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_ADMIN_COMMENT, + TRUE, // Make copy + (PUNICODE_STRING)&(All->AdminComment) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer(All->AdminComment.Buffer); + } + } + + if ( (NT_SUCCESS( NtStatus )) && + ( WhichFields & USER_ALL_USERCOMMENT ) ) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_USER_COMMENT, + TRUE, // Make copy + (PUNICODE_STRING)&(All->UserComment) // Body + ); + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer(All->UserComment.Buffer); + } + } + + if ( (NT_SUCCESS( NtStatus )) && + ( WhichFields & USER_ALL_HOMEDIRECTORY ) ) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_HOME_DIRECTORY, + TRUE, // Make copy + (PUNICODE_STRING)&(All->HomeDirectory) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer(All->HomeDirectory.Buffer); + } + } + + if ( (NT_SUCCESS( NtStatus )) && + ( WhichFields & USER_ALL_HOMEDIRECTORYDRIVE ) ) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_HOME_DIRECTORY_DRIVE, + TRUE, // Make copy + (PUNICODE_STRING)&(All->HomeDirectoryDrive) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer(All->HomeDirectoryDrive.Buffer); + } + } + + if ( (NT_SUCCESS( NtStatus )) && + ( WhichFields & USER_ALL_SCRIPTPATH ) ) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_SCRIPT_PATH, + TRUE, // Make copy + (PUNICODE_STRING)&(All->ScriptPath) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer(All->ScriptPath.Buffer); + } + } + + if ( (NT_SUCCESS( NtStatus )) && + ( WhichFields & USER_ALL_PROFILEPATH ) ) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_PROFILE_PATH, + TRUE, // Make copy + (PUNICODE_STRING)&(All->ProfilePath) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer(All->ProfilePath.Buffer); + } + } + + if ( (NT_SUCCESS( NtStatus )) && + ( WhichFields & USER_ALL_WORKSTATIONS ) ) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_WORKSTATIONS, + TRUE, // Make copy + (PUNICODE_STRING)&(All->WorkStations) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer(All->WorkStations.Buffer); + } + } + + if ( (NT_SUCCESS( NtStatus )) && + ( WhichFields & USER_ALL_LASTLOGON ) ) { + + All->LastLogon = V1aFixed.LastLogon; + } + + if ( (NT_SUCCESS( NtStatus )) && + ( WhichFields & USER_ALL_LASTLOGOFF ) ) { + + All->LastLogoff = V1aFixed.LastLogoff; + } + + if ( (NT_SUCCESS( NtStatus )) && + ( WhichFields & USER_ALL_LOGONHOURS ) ) { + + NtStatus = SampRetrieveUserLogonHours( + AccountContext, + (PLOGON_HOURS)&(All->LogonHours) + ); + + if (NT_SUCCESS(NtStatus)) { + + if (All->LogonHours.LogonHours != NULL) { + + RegisterBuffer(All->LogonHours.LogonHours); + } + } + } + + if ( (NT_SUCCESS( NtStatus )) && + ( WhichFields & USER_ALL_BADPASSWORDCOUNT ) ) { + + All->BadPasswordCount = SampQueryBadPasswordCount( AccountContext, &V1aFixed ); + + if (UserInformationClass == UserInternal3Information) { + (*Buffer)->Internal3.LastBadPasswordTime = V1aFixed.LastBadPasswordTime; + } + + } + + if ( (NT_SUCCESS( NtStatus )) && + ( WhichFields & USER_ALL_LOGONCOUNT ) ) { + + All->LogonCount = V1aFixed.LogonCount; + } + + if ( (NT_SUCCESS( NtStatus )) && + ( WhichFields & USER_ALL_PASSWORDCANCHANGE ) ) { + + if ( !NtPasswordNonNull && !LmPasswordNonNull ) { + + // + // Null passwords can be changed immediately. + // + + All->PasswordCanChange = SampHasNeverTime; + + } else { + + All->PasswordCanChange = SampAddDeltaTime( + V1aFixed.PasswordLastSet, + Domain->UnmodifiedFixed.MinPasswordAge); + } + } + + if ( (NT_SUCCESS( NtStatus )) && + ( WhichFields & + (USER_ALL_PASSWORDMUSTCHANGE|USER_ALL_PASSWORDEXPIRED) ) ) { + + All->PasswordMustChange = SampGetPasswordMustChange( + V1aFixed.UserAccountControl, + V1aFixed.PasswordLastSet, + Domain->UnmodifiedFixed.MaxPasswordAge); + } + + if ( (NT_SUCCESS( NtStatus )) && + ( WhichFields & USER_ALL_PASSWORDEXPIRED ) ) { + + LARGE_INTEGER TimeNow; + + NtStatus = NtQuerySystemTime( &TimeNow ); + if (NT_SUCCESS(NtStatus)) { + if ( TimeNow.QuadPart >= All->PasswordMustChange.QuadPart) { + + All->PasswordExpired = TRUE; + + } else { + + All->PasswordExpired = FALSE; + } + } + } + + if ( (NT_SUCCESS( NtStatus )) && + ( WhichFields & USER_ALL_PASSWORDLASTSET ) ) { + + All->PasswordLastSet = V1aFixed.PasswordLastSet; + } + + if ( (NT_SUCCESS( NtStatus )) && + ( WhichFields & USER_ALL_ACCOUNTEXPIRES ) ) { + + All->AccountExpires = V1aFixed.AccountExpires; + } + + if ( (NT_SUCCESS( NtStatus )) && + ( WhichFields & USER_ALL_USERACCOUNTCONTROL ) ) { + + All->UserAccountControl = V1aFixed.UserAccountControl; + } + + if ( (NT_SUCCESS( NtStatus )) && + ( WhichFields & USER_ALL_PARAMETERS ) ) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_PARAMETERS, + TRUE, // Make copy + (PUNICODE_STRING)&(All->Parameters) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer(All->Parameters.Buffer); + } + } + + if ( (NT_SUCCESS( NtStatus )) && + ( WhichFields & USER_ALL_COUNTRYCODE ) ) { + + All->CountryCode = V1aFixed.CountryCode; + } + + if ( (NT_SUCCESS( NtStatus )) && + ( WhichFields & USER_ALL_CODEPAGE ) ) { + + All->CodePage = V1aFixed.CodePage; + } + + if ( (NT_SUCCESS( NtStatus )) && + ( WhichFields & USER_ALL_NTPASSWORDPRESENT ) ) { + + ASSERT( WhichFields & USER_ALL_LMPASSWORDPRESENT); + + All->LmPasswordPresent = LmPasswordNonNull; + All->NtPasswordPresent = NtPasswordNonNull; + + RtlInitUnicodeString(&All->LmPassword, NULL); + RtlInitUnicodeString(&All->NtPassword, NULL); + + if ( LmPasswordNonNull ) { + + All->LmPassword.Buffer = + MIDL_user_allocate( LM_OWF_PASSWORD_LENGTH ); + + if ( All->LmPassword.Buffer == NULL ) { + + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + + } else { + + RegisterBuffer(All->LmPassword.Buffer); + + All->LmPassword.Length = LM_OWF_PASSWORD_LENGTH; + All->LmPassword.MaximumLength = + LM_OWF_PASSWORD_LENGTH; + RtlCopyMemory( + All->LmPassword.Buffer, + &LmOwfPassword, + LM_OWF_PASSWORD_LENGTH + ); + } + } + + if ( NT_SUCCESS( NtStatus ) ) { + + if ( NtPasswordPresent ) { + + All->NtPassword.Buffer = + MIDL_user_allocate( NT_OWF_PASSWORD_LENGTH ); + + if ( All->NtPassword.Buffer == NULL ) { + + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + + } else { + + RegisterBuffer(All->NtPassword.Buffer); + + All->NtPassword.Length = NT_OWF_PASSWORD_LENGTH; + All->NtPassword.MaximumLength = + NT_OWF_PASSWORD_LENGTH; + RtlCopyMemory( + All->NtPassword.Buffer, + &NtOwfPassword, + NT_OWF_PASSWORD_LENGTH + ); + } + } + } + } + + if ( (NT_SUCCESS( NtStatus )) && + ( WhichFields & USER_ALL_PRIVATEDATA ) ) { + + All->PrivateDataSensitive = TRUE; + + NtStatus = SampGetPrivateUserData( + AccountContext, + (PULONG) + (&(All->PrivateData.Length)), + (PVOID *) + (&(All->PrivateData.Buffer)) + ); + if (NT_SUCCESS(NtStatus)) { + + All->PrivateData.MaximumLength = + All->PrivateData.Length; + + RegisterBuffer(All->PrivateData.Buffer); + } + } + + if ( (NT_SUCCESS( NtStatus )) && + ( WhichFields & USER_ALL_SECURITYDESCRIPTOR ) ) { + + NtStatus = SampGetObjectSD( + AccountContext, + &(All->SecurityDescriptor.Length), + (PSECURITY_DESCRIPTOR *) + &(All->SecurityDescriptor.SecurityDescriptor) + ); + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer(All->SecurityDescriptor.SecurityDescriptor); + } + } + + if ( NT_SUCCESS( NtStatus ) ) { + + All->WhichFields = WhichFields; + } + + break; + + case UserAccountInformation: + + NoErrorsYet = TRUE; + + + (*Buffer)->Account.UserId = V1aFixed.UserId; + (*Buffer)->Account.PrimaryGroupId = V1aFixed.PrimaryGroupId; + + (*Buffer)->Account.LastLogon = + *((POLD_LARGE_INTEGER)&V1aFixed.LastLogon); + + (*Buffer)->Account.LastLogoff = + *((POLD_LARGE_INTEGER)&V1aFixed.LastLogoff); + + + (*Buffer)->Account.BadPasswordCount = SampQueryBadPasswordCount( AccountContext, &V1aFixed ); + (*Buffer)->Account.LogonCount = V1aFixed.LogonCount; + + (*Buffer)->Account.PasswordLastSet = + *((POLD_LARGE_INTEGER)&V1aFixed.PasswordLastSet); + + (*Buffer)->Account.AccountExpires = + *((POLD_LARGE_INTEGER)&V1aFixed.AccountExpires); + + (*Buffer)->Account.UserAccountControl = V1aFixed.UserAccountControl; + + + // + // Get copies of the strings we must retrieve from + // the registry. + // + + if (NoErrorsYet == TRUE) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_ACCOUNT_NAME, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->Account.UserName) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->Account.UserName.Buffer); + + } else { + NoErrorsYet = FALSE; + } + } + + + if (NoErrorsYet == TRUE) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_FULL_NAME, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->Account.FullName) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->Account.FullName.Buffer); + + } else { + NoErrorsYet = FALSE; + } + } + + + if (NoErrorsYet == TRUE) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_HOME_DIRECTORY, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->Account.HomeDirectory) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->Account.HomeDirectory.Buffer); + + } else { + NoErrorsYet = FALSE; + } + } + + + if (NoErrorsYet == TRUE) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_HOME_DIRECTORY_DRIVE, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->Account.HomeDirectoryDrive) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->Account.HomeDirectoryDrive.Buffer); + + } else { + NoErrorsYet = FALSE; + } + } + + + if (NoErrorsYet == TRUE) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_SCRIPT_PATH, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->Account.ScriptPath) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->Account.ScriptPath.Buffer); + + } else { + NoErrorsYet = FALSE; + } + } + + + + if (NoErrorsYet == TRUE) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_PROFILE_PATH, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->Account.ProfilePath) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->Account.ProfilePath.Buffer); + + } else { + NoErrorsYet = FALSE; + } + } + + + + if (NoErrorsYet == TRUE) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_ADMIN_COMMENT, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->Account.AdminComment) // Body + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->Account.AdminComment.Buffer); + + } else { + NoErrorsYet = FALSE; + } + } + + + + if (NoErrorsYet == TRUE) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_WORKSTATIONS, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->Account.WorkStations) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->Account.WorkStations.Buffer); + + } else { + NoErrorsYet = FALSE; + } + } + + + + + // + // Now get the logon hours + // + + + if (NoErrorsYet == TRUE) { + + NtStatus = SampRetrieveUserLogonHours( + AccountContext, + (PLOGON_HOURS)&((*Buffer)->Account.LogonHours) + ); + + if (NT_SUCCESS(NtStatus)) { + + if ((*Buffer)->Account.LogonHours.LogonHours != NULL) { + + RegisterBuffer((*Buffer)->Account.LogonHours.LogonHours); + } + + } else { + NoErrorsYet = FALSE; + } + } + + break; + + + case UserGeneralInformation: + + + (*Buffer)->General.PrimaryGroupId = V1aFixed.PrimaryGroupId; + + + + // + // Get copies of the strings we must retrieve from + // the registry. + // + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_ACCOUNT_NAME, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->General.UserName) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->General.UserName.Buffer); + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_FULL_NAME, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->General.FullName) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->General.FullName.Buffer); + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_ADMIN_COMMENT, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->General.AdminComment) // Body + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->General.AdminComment.Buffer); + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_USER_COMMENT, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->General.UserComment) // Body + ); + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->General.UserComment.Buffer); + } + } + } + } + + + break; + + + case UserNameInformation: + + // + // Get copies of the strings we must retrieve from + // the registry. + // + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_ACCOUNT_NAME, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->Name.UserName) // Body + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->Name.UserName.Buffer); + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_FULL_NAME, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->Name.FullName) // Body + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->Name.FullName.Buffer); + } + } + + + break; + + + case UserAccountNameInformation: + + // + // Get copy of the string we must retrieve from + // the registry. + // + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_ACCOUNT_NAME, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->AccountName.UserName) // Body + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->AccountName.UserName.Buffer); + } + + + break; + + + case UserFullNameInformation: + + // + // Get copy of the string we must retrieve from + // the registry. + // + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_FULL_NAME, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->FullName.FullName) // Body + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->FullName.FullName.Buffer); + } + + + break; + + + case UserAdminCommentInformation: + + // + // Get copies of the strings we must retrieve from + // the registry. + // + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_ADMIN_COMMENT, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->AdminComment.AdminComment) // Body + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->AdminComment.AdminComment.Buffer); + } + + + break; + + + case UserPrimaryGroupInformation: + + + (*Buffer)->PrimaryGroup.PrimaryGroupId = V1aFixed.PrimaryGroupId; + + break; + + + case UserPreferencesInformation: + + + (*Buffer)->Preferences.CountryCode = V1aFixed.CountryCode; + (*Buffer)->Preferences.CodePage = V1aFixed.CodePage; + + + + // + // Read the UserComment field from the registry. + // + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_USER_COMMENT, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->Preferences.UserComment) // Body + ); + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->Preferences.UserComment.Buffer); + + // + // This field isn't used, but make sure RPC doesn't + // choke on it. + // + + (*Buffer)->Preferences.Reserved1.Length = 0; + (*Buffer)->Preferences.Reserved1.MaximumLength = 0; + (*Buffer)->Preferences.Reserved1.Buffer = NULL; + } + + + break; + + + case UserParametersInformation: + + + // + // Read the Parameters field from the registry. + // + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_PARAMETERS, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->Parameters.Parameters) + ); + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->Parameters.Parameters.Buffer); + } + + + break; + + + case UserLogonInformation: + + NoErrorsYet = TRUE; + + Domain = &SampDefinedDomains[ AccountContext->DomainIndex ]; + + (*Buffer)->Logon.UserId = V1aFixed.UserId; + (*Buffer)->Logon.PrimaryGroupId = V1aFixed.PrimaryGroupId; + + (*Buffer)->Logon.LastLogon = + *((POLD_LARGE_INTEGER)&V1aFixed.LastLogon); + + (*Buffer)->Logon.LastLogoff = + *((POLD_LARGE_INTEGER)&V1aFixed.LastLogoff); + + (*Buffer)->Logon.BadPasswordCount = V1aFixed.BadPasswordCount; + + (*Buffer)->Logon.PasswordLastSet = + *((POLD_LARGE_INTEGER)&V1aFixed.PasswordLastSet); + + TempTime = SampAddDeltaTime( + V1aFixed.PasswordLastSet, + Domain->UnmodifiedFixed.MinPasswordAge ); + + (*Buffer)->Logon.PasswordCanChange = + *((POLD_LARGE_INTEGER)&TempTime); + + + TempTime = SampGetPasswordMustChange( + V1aFixed.UserAccountControl, + V1aFixed.PasswordLastSet, + Domain->UnmodifiedFixed.MaxPasswordAge); + + (*Buffer)->Logon.PasswordMustChange = + *((POLD_LARGE_INTEGER)&TempTime); + + + (*Buffer)->Logon.LogonCount = V1aFixed.LogonCount; + (*Buffer)->Logon.UserAccountControl = V1aFixed.UserAccountControl; + + + // + // If there is no password on the account then + // modify the password can/must change times + // so that the password never expires and can + // be changed immediately. + // + + NtStatus = SampRetrieveUserPasswords( + AccountContext, + &LmOwfPassword, + &LmPasswordNonNull, + &NtOwfPassword, + &NtPasswordPresent, + &NtPasswordNonNull + ); + + if (NT_SUCCESS(NtStatus)) { + + if ( !NtPasswordNonNull && !LmPasswordNonNull ) { + + // + // The password is NULL. + // It can be changed immediately. + // + + (*Buffer)->Logon.PasswordCanChange = + *((POLD_LARGE_INTEGER)&SampHasNeverTime); + + } + } else { + NoErrorsYet = FALSE; + } + + + // + // Get copies of the strings we must retrieve from + // the registry. + // + + if (NoErrorsYet == TRUE) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_ACCOUNT_NAME, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->Logon.UserName) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->Logon.UserName.Buffer); + + } else { + NoErrorsYet = FALSE; + } + } + + + if (NoErrorsYet == TRUE) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_FULL_NAME, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->Logon.FullName) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->Logon.FullName.Buffer); + + } else { + NoErrorsYet = FALSE; + } + } + + + if (NoErrorsYet == TRUE) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_HOME_DIRECTORY, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->Logon.HomeDirectory) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->Logon.HomeDirectory.Buffer); + + } else { + NoErrorsYet = FALSE; + } + } + + + if (NoErrorsYet == TRUE) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_HOME_DIRECTORY_DRIVE, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->Logon.HomeDirectoryDrive) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->Logon.HomeDirectoryDrive.Buffer); + + } else { + NoErrorsYet = FALSE; + } + } + + + if (NoErrorsYet == TRUE) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_SCRIPT_PATH, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->Logon.ScriptPath) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->Logon.ScriptPath.Buffer); + + } else { + NoErrorsYet = FALSE; + } + } + + + + if (NoErrorsYet == TRUE) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_PROFILE_PATH, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->Logon.ProfilePath) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->Logon.ProfilePath.Buffer); + + } else { + NoErrorsYet = FALSE; + } + } + + + + if (NoErrorsYet == TRUE) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_WORKSTATIONS, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->Logon.WorkStations) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->Logon.WorkStations.Buffer); + + } else { + NoErrorsYet = FALSE; + } + } + + + + + // + // Now get the logon hours + // + + + if (NoErrorsYet == TRUE) { + + NtStatus = SampRetrieveUserLogonHours( + AccountContext, + (PLOGON_HOURS)&((*Buffer)->Logon.LogonHours) + ); + + if (NT_SUCCESS(NtStatus)) { + + if ((*Buffer)->Logon.LogonHours.LogonHours != NULL) { + + RegisterBuffer((*Buffer)->Logon.LogonHours.LogonHours); + } + + } else { + NoErrorsYet = FALSE; + } + } + + break; + + + case UserLogonHoursInformation: + + NtStatus = SampRetrieveUserLogonHours( + AccountContext, + (PLOGON_HOURS)&((*Buffer)->LogonHours.LogonHours) + ); + + if (NT_SUCCESS(NtStatus)) { + + if ((*Buffer)->LogonHours.LogonHours.LogonHours != NULL) { + + RegisterBuffer((*Buffer)->LogonHours.LogonHours.LogonHours); + } + } + + break; + + + case UserHomeInformation: + + NoErrorsYet = TRUE; + + // + // Get copies of the strings we must retrieve from + // the registry. + // + + if (NoErrorsYet == TRUE) { + + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_HOME_DIRECTORY, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->Home.HomeDirectory) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->Home.HomeDirectory.Buffer); + + } else { + NoErrorsYet = FALSE; + } + } + + + if (NoErrorsYet == TRUE) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_HOME_DIRECTORY_DRIVE, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->Home.HomeDirectoryDrive) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->Home.HomeDirectoryDrive.Buffer); + + } else { + NoErrorsYet = FALSE; + } + } + + break; + + + case UserScriptInformation: + + // + // Get copies of the strings we must retrieve from + // the registry. + // + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_SCRIPT_PATH, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->Script.ScriptPath) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->Script.ScriptPath.Buffer); + } + + break; + + + case UserProfileInformation: + + // + // Get copies of the strings we must retrieve from + // the registry. + // + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_PROFILE_PATH, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->Profile.ProfilePath) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->Profile.ProfilePath.Buffer); + } + + break; + + + case UserWorkStationsInformation: + + // + // Get copies of the strings we must retrieve from + // the registry. + // + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_WORKSTATIONS, + TRUE, // Make copy + (PUNICODE_STRING)&((*Buffer)->WorkStations.WorkStations) + ); + + if (NT_SUCCESS(NtStatus)) { + + RegisterBuffer((*Buffer)->WorkStations.WorkStations.Buffer); + } + + break; + + + case UserControlInformation: + + (*Buffer)->Control.UserAccountControl = V1aFixed.UserAccountControl; + break; + + + case UserExpiresInformation: + + (*Buffer)->Expires.AccountExpires = V1aFixed.AccountExpires; + + break; + + + case UserInternal1Information: + + if ( AccountContext->TrustedClient ) { + + // + // PasswordExpired is a 'write only' flag. + // We always return FALSE on read. + // + + (*Buffer)->Internal1.PasswordExpired = FALSE; + + // + // Retrieve the OWF passwords. + // Since this is a trusted client, we don't need to + // reencrypt the OWFpasswords we return - so we stuff + // the OWFs into the structure that holds encryptedOWFs. + // + + ASSERT( ENCRYPTED_LM_OWF_PASSWORD_LENGTH == LM_OWF_PASSWORD_LENGTH ); + ASSERT( ENCRYPTED_NT_OWF_PASSWORD_LENGTH == NT_OWF_PASSWORD_LENGTH ); + + NtStatus = SampRetrieveUserPasswords( + AccountContext, + (PLM_OWF_PASSWORD)&(*Buffer)->Internal1. + EncryptedLmOwfPassword, + &(*Buffer)->Internal1. + LmPasswordPresent, + (PNT_OWF_PASSWORD)&(*Buffer)->Internal1. + EncryptedNtOwfPassword, + &NtPasswordPresent, + &(*Buffer)->Internal1.NtPasswordPresent // Return the Non-NULL flag here + ); + + } else { + + // + // This information is only queryable by trusted + // clients. + // + + NtStatus = STATUS_INVALID_INFO_CLASS; + } + + break; + + + case UserInternal2Information: + + if ( AccountContext->TrustedClient ) { + + (*Buffer)->Internal2.LastLogon = + *((POLD_LARGE_INTEGER)&V1aFixed.LastLogon); + + (*Buffer)->Internal2.LastLogoff = + *((POLD_LARGE_INTEGER)&V1aFixed.LastLogoff); + + (*Buffer)->Internal2.BadPasswordCount = V1aFixed.BadPasswordCount; + (*Buffer)->Internal2.LogonCount = V1aFixed.LogonCount; + + } else { + + // + // This information is only queryable by trusted + // clients. + // + + NtStatus = STATUS_INVALID_INFO_CLASS; + } + + break; + + } + + } + + // + // De-reference the object, discarding changes + // + + IgnoreStatus = SampDeReferenceContext( AccountContext, FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + } + + // + // Free the read lock + // + + SampReleaseReadLock(); + + + + // + // If we didn't succeed, free any allocated memory + // + + if (!NT_SUCCESS(NtStatus)) { + for ( i=0; i<AllocatedBufferCount ; i++ ) { + MIDL_user_free( AllocatedBuffer[i] ); + } + + (*Buffer) = NULL; + } + + return(NtStatus); + +} + + + +NTSTATUS +SampIsUserAccountControlValid( + IN PSAMP_OBJECT Context, + IN ULONG UserAccountControl + ) + +/*++ + +Routine Description: + + This routine checks a UserAccountControl field to make sure that + the bits set make sense. + + NOTE: if the set operation is also setting passwords, it must set the + passwords BEFORE calling this routine! + + +Parameters: + + Context - the context of the account being changed. + + UserAccountControl - the field that is about to be set. + + +Return Values: + + STATUS_SUCCESS - The UserAccountControl field is valid. + + STATUS_SPECIAL_ACCOUNT - The administrator account can't be disabled. + + STATUS_INVALID_PARAMETER - an undefined bit is set, or more than one + account type bit is set. + + STATUS_INVALID_PARAMETER_MIX - USER_PASSWORD_NOT_REQUIRED has been + turned off, but there isn't a bonafide password on the account. + +--*/ + +{ + NTSTATUS NtStatus = STATUS_SUCCESS; + + SAMTRACE("SampIsUserAccountControlValid"); + +#if DBG + // + // Make sure that undefined bits aren't set. + // + + if ( ( UserAccountControl & 0xfffff800 ) != 0 ) { + + DbgPrint("SAM: Setting undefined AccountControl flag(s): 0x%lx for user %d\n", + UserAccountControl, Context->TypeBody.User.Rid); + } +#endif //DBG + + // + // Make sure that the administrator isn't being disabled. + // + + if ( UserAccountControl & USER_ACCOUNT_DISABLED ) { + + if ( Context->TypeBody.User.Rid == DOMAIN_USER_RID_ADMIN ) { + + return( STATUS_SPECIAL_ACCOUNT ); + } + } + + // + // Make sure that exactly one of the account type bits is set. + // + + switch ( UserAccountControl & USER_ACCOUNT_TYPE_MASK ) { + + case USER_TEMP_DUPLICATE_ACCOUNT: + case USER_NORMAL_ACCOUNT: + case USER_SERVER_TRUST_ACCOUNT: + case USER_WORKSTATION_TRUST_ACCOUNT: + case USER_INTERDOMAIN_TRUST_ACCOUNT: + + break; + + default: + + return( STATUS_INVALID_PARAMETER ); + } + + // + // If USER_PASSWORD_NOT_REQUIRED is turned off, make sure that there + // already is a password. Note that this requires that the password + // be set before calling this routine, if both are being done at once. + // + + if ( ( UserAccountControl & USER_PASSWORD_NOT_REQUIRED ) == 0 ) { + + NT_OWF_PASSWORD NtOwfPassword; + LM_OWF_PASSWORD LmOwfPassword; + BOOLEAN LmPasswordNonNull, NtPasswordPresent, NtPasswordNonNull; + + NtStatus = SampRetrieveUserPasswords( + Context, + &LmOwfPassword, + &LmPasswordNonNull, + &NtOwfPassword, + &NtPasswordPresent, + &NtPasswordNonNull + ); + + if ( NT_SUCCESS( NtStatus ) && + ( (!LmPasswordNonNull) && (!NtPasswordNonNull) ) ) { + + } + } + + return( NtStatus ); +} + + + + +NTSTATUS +SampCalculateLmPassword( + IN PUNICODE_STRING NtPassword, + OUT PCHAR *LmPasswordBuffer + ) + +/*++ + +Routine Description: + + This service converts an NT password into a LM password. + +Parameters: + + NtPassword - The Nt password to be converted. + + LmPasswordBuffer - On successful return, points at the LM password + The buffer should be freed using MIDL_user_free + +Return Values: + + STATUS_SUCCESS - LMPassword contains the LM version of the password. + + STATUS_NULL_LM_PASSWORD - The password is too complex to be represented + by a LM password. The LM password returned is a NULL string. + + +--*/ +{ + +#define LM_BUFFER_LENGTH (LM20_PWLEN + 1) + + NTSTATUS NtStatus; + ANSI_STRING LmPassword; + + SAMTRACE("SampCalculateLMPassword"); + + // + // Prepare for failure + // + + *LmPasswordBuffer = NULL; + + + // + // Compute the Ansi version to the Unicode password. + // + // The Ansi version of the Cleartext password is at most 14 bytes long, + // exists in a trailing zero filled 15 byte buffer, + // is uppercased. + // + + LmPassword.Buffer = MIDL_user_allocate(LM_BUFFER_LENGTH); + if (LmPassword.Buffer == NULL) { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + LmPassword.MaximumLength = LmPassword.Length = LM_BUFFER_LENGTH; + RtlZeroMemory( LmPassword.Buffer, LM_BUFFER_LENGTH ); + + NtStatus = RtlUpcaseUnicodeStringToOemString( &LmPassword, NtPassword, FALSE ); + + + if ( !NT_SUCCESS(NtStatus) ) { + + // + // The password is longer than the max LM password length + // + + NtStatus = STATUS_NULL_LM_PASSWORD; // Informational return code + RtlZeroMemory( LmPassword.Buffer, LM_BUFFER_LENGTH ); + + } + + + + + // + // Return a pointer to the allocated LM password + // + + if (NT_SUCCESS(NtStatus)) { + + *LmPasswordBuffer = LmPassword.Buffer; + + } else { + + MIDL_user_free(LmPassword.Buffer); + } + + return(NtStatus); +} + + + +NTSTATUS +SampCalculateLmAndNtOwfPasswords( + IN PUNICODE_STRING ClearNtPassword, + OUT PBOOLEAN LmPasswordPresent, + OUT PLM_OWF_PASSWORD LmOwfPassword, + OUT PNT_OWF_PASSWORD NtOwfPassword + ) +/*++ + +Routine Description: + + This routine calculates the LM and NT OWF passwordw from the cleartext + password. + +Arguments: + + ClearNtPassword - A Cleartext unicode password + + LmPasswordPresent - indicates whether an LM OWF password could be + calculated + + LmOwfPassword - Gets the LM OWF hash of the cleartext password. + + NtOwfPassword - Gets the NT OWF hash of the cleartext password. + + +Return Value: + +--*/ +{ + PCHAR LmPassword = NULL; + NTSTATUS NtStatus; + + SAMTRACE("SampCalculateLmAndNtOwfPassword"); + + // + // First compute the LM password. If the password is too complex + // this may not be possible. + // + + + NtStatus = SampCalculateLmPassword( + ClearNtPassword, + &LmPassword + ); + + // + // If it faield because the LM password could not be calculated, that + // is o.k. + // + + if (NtStatus != STATUS_SUCCESS) { + + if (NtStatus == STATUS_NULL_LM_PASSWORD) { + *LmPasswordPresent = FALSE; + NtStatus = STATUS_SUCCESS; + + } + + } else { + + // + // Now compute the OWF passwords + // + + *LmPasswordPresent = TRUE; + + NtStatus = RtlCalculateLmOwfPassword( + LmPassword, + LmOwfPassword + ); + + } + + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = RtlCalculateNtOwfPassword( + ClearNtPassword, + NtOwfPassword + ); + } + + if (LmPassword != NULL) { + MIDL_user_free(LmPassword); + } + + return(NtStatus); + +} + + + +NTSTATUS +SampDecryptPasswordWithKey( + IN PSAMPR_ENCRYPTED_USER_PASSWORD EncryptedPassword, + IN PBYTE Key, + IN ULONG KeySize, + IN BOOLEAN UnicodePasswords, + OUT PUNICODE_STRING ClearNtPassword + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + +--*/ +{ + struct RC4_KEYSTRUCT Rc4Key; + NTSTATUS NtStatus; + OEM_STRING OemPassword; + PSAMPR_USER_PASSWORD Password = (PSAMPR_USER_PASSWORD) EncryptedPassword; + + SAMTRACE("SampDecryptPasswordWithKey"); + + // + // Decrypt the key. + // + + rc4_key( + &Rc4Key, + KeySize, + Key + ); + + rc4(&Rc4Key, + sizeof(SAMPR_ENCRYPTED_USER_PASSWORD), + (PUCHAR) Password + ); + + // + // Check that the length is valid. If it isn't bail here. + // + + if (Password->Length > SAM_MAX_PASSWORD_LENGTH * sizeof(WCHAR)) { + return(STATUS_WRONG_PASSWORD); + } + + + // + // Convert the password into a unicode string. + // + + if (UnicodePasswords) { + NtStatus = SampInitUnicodeString( + ClearNtPassword, + (USHORT) (Password->Length + sizeof(WCHAR)) + ); + if (NT_SUCCESS(NtStatus)) { + + ClearNtPassword->Length = (USHORT) Password->Length; + + RtlCopyMemory( + ClearNtPassword->Buffer, + ((PCHAR) Password->Buffer) + + (SAM_MAX_PASSWORD_LENGTH * sizeof(WCHAR)) - + Password->Length, + Password->Length + ); + NtStatus = STATUS_SUCCESS; + } + } else { + + // + // The password is in the OEM character set. Convert it to Unicode + // and then copy it into the ClearNtPassword structure. + // + + OemPassword.Buffer = ((PCHAR)Password->Buffer) + + (SAM_MAX_PASSWORD_LENGTH * sizeof(WCHAR)) - + Password->Length; + + OemPassword.Length = (USHORT) Password->Length; + + + NtStatus = RtlOemStringToUnicodeString( + ClearNtPassword, + &OemPassword, + TRUE // allocate destination + ); + } + + return(NtStatus); +} + + +NTSTATUS +SampDecryptPasswordWithSessionKey( + IN SAMPR_HANDLE UserHandle, + IN PSAMPR_ENCRYPTED_USER_PASSWORD EncryptedPassword, + OUT PUNICODE_STRING ClearNtPassword + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + +--*/ +{ + NTSTATUS NtStatus; + USER_SESSION_KEY UserSessionKey; + + SAMTRACE("SampDecryptPasswordWithSessionKey"); + + NtStatus = RtlGetUserSessionKeyServer( + (RPC_BINDING_HANDLE)UserHandle, + &UserSessionKey + ); + + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + + return(SampDecryptPasswordWithKey( + EncryptedPassword, + (PUCHAR) &UserSessionKey, + sizeof(USER_SESSION_KEY), + TRUE, + ClearNtPassword + ) ); +} + + + +NTSTATUS +SampCheckPasswordRestrictions( + IN SAMPR_HANDLE UserHandle, + PUNICODE_STRING NewNtPassword + ) + +/*++ + +Routine Description: + + This service is called to make sure that the password presented meets + our quality requirements. + + +Arguments: + + UserHandle - Handle to a user. + + NewNtPassword - Pointer to the UNICODE_STRING containing the new + password. + + +Return Value: + + STATUS_SUCCESS - The password is acceptable. + + STATUS_PASSWORD_RESTRICTION - The password is too short, or is not + complex enough, etc. + + STATUS_INVALID_RESOURCES - There was not enough memory to do the + password checking. + + +--*/ +{ + USER_DOMAIN_PASSWORD_INFORMATION PasswordInformation; + NTSTATUS NtStatus; + PWORD CharInfoBuffer = NULL; + ULONG i; + PSAMP_DEFINED_DOMAINS Domain; + SAMP_V1_0A_FIXED_LENGTH_USER V1aFixed; + PSAMP_OBJECT AccountContext = (PSAMP_OBJECT) UserHandle; + + + SAMTRACE("SampCheckPasswordRestrictions"); + + + // + // Query information domain to get password length and + // complexity requirements. + // + + // + // BUGBUG: this code was copied from SamrGetUserDomainPasswordInformation + // + + // + // When the user was opened, we checked to see if the domain handle + // allowed access to the domain password information. Check that here. + // + + if ( !( AccountContext->TypeBody.User.DomainPasswordInformationAccessible ) ) { + + NtStatus = STATUS_ACCESS_DENIED; + + } else { + + Domain = &SampDefinedDomains[ AccountContext->DomainIndex ]; + + // + // If the user account is a machine account, + // then restrictions are generally not enforced. + // This is so that simple initial passwords can be + // established. IT IS EXPECTED THAT COMPLEX PASSWORDS, + // WHICH MEET THE MOST STRINGENT RESTRICTIONS, WILL BE + // AUTOMATICALLY ESTABLISHED AND MAINTAINED ONCE THE MACHINE + // JOINS THE DOMAIN. It is the UI's responsibility to + // maintain this level of complexity. + // + + + NtStatus = SampRetrieveUserV1aFixed( + AccountContext, + &V1aFixed + ); + + if (NT_SUCCESS(NtStatus)) { + if ( (V1aFixed.UserAccountControl & + (USER_WORKSTATION_TRUST_ACCOUNT | USER_SERVER_TRUST_ACCOUNT)) + != 0 ) { + + PasswordInformation.MinPasswordLength = 0; + PasswordInformation.PasswordProperties = 0; + } else { + + PasswordInformation.MinPasswordLength = Domain->UnmodifiedFixed.MinPasswordLength; + PasswordInformation.PasswordProperties = Domain->UnmodifiedFixed.PasswordProperties; + } + } + } + + + if ( NT_SUCCESS( NtStatus ) ) { + + if ( (USHORT)( NewNtPassword->Length / sizeof(WCHAR) ) < PasswordInformation.MinPasswordLength ) { + + NtStatus = STATUS_PASSWORD_RESTRICTION; + + } else { + + // + // Check password complexity. + // + + if ( PasswordInformation.PasswordProperties & DOMAIN_PASSWORD_COMPLEX ) { + + // + // Make sure that the password meets our requirements for + // complexity. If it's got an odd byte count, it's + // obviously not a hand-entered UNICODE string so we'll + // consider it complex by default. + // + + if ( !( NewNtPassword->Length & 1 ) ) { + + USHORT NumsInPassword = 0; + USHORT UppersInPassword = 0; + USHORT LowersInPassword = 0; + USHORT OthersInPassword = 0; + + CharInfoBuffer = MIDL_user_allocate( NewNtPassword->Length ); + + if ( CharInfoBuffer == NULL ) { + + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + + } else { + + if ( GetStringTypeW( + CT_CTYPE1, + NewNtPassword->Buffer, + NewNtPassword->Length / 2, + CharInfoBuffer ) ) { + + for ( i = 0; i < (ULONG)( NewNtPassword->Length / sizeof(WCHAR) ); i++ ) { + + if ( CharInfoBuffer[i] & C1_DIGIT ) { + + NumsInPassword = 1; + } + + if ( CharInfoBuffer[i] & C1_UPPER ) { + + UppersInPassword = 1; + } + + if ( CharInfoBuffer[i] & C1_LOWER ) { + + LowersInPassword = 1; + } + + if ( !( CharInfoBuffer[i] & ( C1_ALPHA | C1_DIGIT ) ) ) { + + // + // Having any "other" characters is + // sufficient to make the password + // complex. + // + + OthersInPassword = 2; + } + } + + if ( ( NumsInPassword + UppersInPassword + + LowersInPassword + OthersInPassword ) < 2 ) { + + // + // It didn't have at least two of the four + // types of characters, so it's not complex + // enough. + // + + NtStatus = STATUS_PASSWORD_RESTRICTION; + } + + } else { + + // + // GetStringTypeW failed; dunno why. Perhaps the + // password is binary. Consider it complex by + // default. + // + + NtStatus = STATUS_SUCCESS; + } + + MIDL_user_free( CharInfoBuffer ); + } + } + } + } + } + + return( NtStatus ); +} + + + +NTSTATUS +SamrSetInformationUser2( + IN SAMPR_HANDLE UserHandle, + IN USER_INFORMATION_CLASS UserInformationClass, + IN PSAMPR_USER_INFO_BUFFER Buffer + ) +{ + // + // This is a thin veil to SamrSetInformationUser(). + // This is needed so that new-release systems can call + // this routine without the danger of passing an info + // level that release 1.0 systems didn't understand. + // + + + return( SamrSetInformationUser( + UserHandle, + UserInformationClass, + Buffer + ) ); +} + +NTSTATUS +SamrSetInformationUser( + IN SAMPR_HANDLE UserHandle, + IN USER_INFORMATION_CLASS UserInformationClass, + IN PSAMPR_USER_INFO_BUFFER Buffer + ) + + +/*++ + +Routine Description: + + + This API modifies information in a user record. The data modified + is determined by the UserInformationClass parameter. + In general, a user may call GetInformation with class + UserLogonInformation, but may only call SetInformation with class + UserPreferencesInformation. Access type USER_WRITE_ACCOUNT allows + changes to be made to all fields. + + NOTE: If the password is set to a new password then the password- + set timestamp is reset as well. + + + +Parameters: + + UserHandle - The handle of an opened user to operate on. + + UserInformationClass - Class of information provided. The + accesses required for each class is shown below: + + Info Level Required Access Type + ----------------------- ------------------------ + UserGeneralInformation USER_WRITE_ACCOUNT and + USER_WRITE_PREFERENCES + + UserPreferencesInformation USER_WRITE_PREFERENCES + + UserParametersInformation USER_WRITE_ACCOUNT + + UserLogonInformation (Can't set) + + UserLogonHoursInformation USER_WRITE_ACCOUNT + + UserAccountInformation (Can't set) + + UserNameInformation USER_WRITE_ACCOUNT + UserAccountNameInformation USER_WRITE_ACCOUNT + UserFullNameInformation USER_WRITE_ACCOUNT + UserPrimaryGroupInformation USER_WRITE_ACCOUNT + UserHomeInformation USER_WRITE_ACCOUNT + UserScriptInformation USER_WRITE_ACCOUNT + UserProfileInformation USER_WRITE_ACCOUNT + UserAdminCommentInformation USER_WRITE_ACCOUNT + UserWorkStationsInformation USER_WRITE_ACCOUNT + UserSetPasswordInformation USER_FORCE_PASSWORD_CHANGE + UserControlInformation USER_WRITE_ACCOUNT + UserExpiresInformation USER_WRITE_ACCOUNT + + UserInternal1Information USER_FORCE_PASSWORD_CHANGE + UserInternal2Information (Trusted client only) + UserInternal3Information (Trusted client only) - + UserInternal4Information Similar to All Information + UserInternal5Information Similar to SetPassword + UserAllInformation Will set fields that are + requested by caller. Access + to fields to be set must be + held as defined above. + + + Buffer - Buffer containing a user info struct. + + + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_INVALID_INFO_CLASS - The class provided was invalid. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled for this + operation + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. + +--*/ + +{ + NTSTATUS NtStatus, + IgnoreStatus; + + PSAMP_OBJECT AccountContext = (PSAMP_OBJECT) UserHandle; + + PUSER_ALL_INFORMATION All; + + SAMP_OBJECT_TYPE FoundType; + + PSAMP_DEFINED_DOMAINS Domain; + + ACCESS_MASK DesiredAccess; + + SAMP_V1_0A_FIXED_LENGTH_USER V1aFixed; + + UNICODE_STRING OldAccountName, + ApiList, + NewAdminComment, + NewAccountName, + NewFullName; + + NT_OWF_PASSWORD NtOwfPassword; + + LM_OWF_PASSWORD LmOwfPassword; + + USER_SESSION_KEY UserSessionKey; + + BOOLEAN LmPresent; + BOOLEAN NtPresent; + BOOLEAN PasswordExpired; + + ULONG ObjectRid, + OldUserAccountControl, + DomainIndex; + + BOOLEAN UserAccountControlChanged = FALSE, + MustUpdateAccountDisplay = FALSE, + MustQueryV1aFixed = FALSE, + ReplicateImmediately = FALSE, + TellNetlogon = TRUE, + AccountLockedOut, + CurrentlyLocked, + Unlocking; + + SECURITY_DB_DELTA_TYPE DeltaType = SecurityDbChange; + UNICODE_STRING ClearTextPassword; + UNICODE_STRING AccountName; + ULONG UserRid = 0; + +#if DBG + + TIME_FIELDS + T1; + +#endif //DBG + + SAMTRACE("SamrSetInformationUser"); + + // + // Initialization. + // + + ClearTextPassword.Buffer = NULL; + ClearTextPassword.Length = 0; + AccountName.Buffer = NULL; + + // + // Make sure we understand what RPC is doing for (to) us. + // + + if (Buffer == NULL) { + return(STATUS_INVALID_PARAMETER); + } + + // + // Reset any strings that we'll be freeing in clean-up code + // + + RtlInitUnicodeString(&OldAccountName, NULL); + RtlInitUnicodeString(&NewAccountName, NULL); + RtlInitUnicodeString(&NewFullName, NULL); + RtlInitUnicodeString(&NewAdminComment, NULL); + + + // + // Set the desired access based upon the Info class + // + + switch (UserInformationClass) { + + case UserPreferencesInformation: + + DesiredAccess = USER_WRITE_PREFERENCES; + break; + + case UserParametersInformation: + case UserLogonHoursInformation: + case UserNameInformation: + case UserAccountNameInformation: + case UserFullNameInformation: + case UserPrimaryGroupInformation: + case UserHomeInformation: + case UserScriptInformation: + case UserProfileInformation: + case UserAdminCommentInformation: + case UserWorkStationsInformation: + case UserControlInformation: + case UserExpiresInformation: + + DesiredAccess = USER_WRITE_ACCOUNT; + break; + + case UserSetPasswordInformation: + case UserInternal1Information: + case UserInternal5Information: + + DeltaType = SecurityDbChangePassword; + DesiredAccess = USER_FORCE_PASSWORD_CHANGE; + break; + + + + case UserAllInformation: + case UserInternal3Information: + case UserInternal4Information: + + ////////////////////////////////////////////////////////////// + // // + // !!!! WARNING !!!! // + // // + // Be warned that the buffer structure for // + // UserInternal3/4Information MUST begin with the same // + // structure as UserAllInformation. // + // // + ////////////////////////////////////////////////////////////// + + DesiredAccess = 0; + + All = (PUSER_ALL_INFORMATION)Buffer; + + if ( ( All->WhichFields == 0 ) || + ( All->WhichFields & USER_ALL_WRITE_CANT_MASK ) ) { + + // + // Passed in something silly (no fields to set), or is + // trying to set fields that can't be set. + // + + return STATUS_INVALID_PARAMETER; + } + + // + // If the user is the special account Administrator, return an + // error if trying to set the expiry information, except to the value + // that means that the account never expires. + // + + if ( (All->WhichFields & USER_ALL_ACCOUNTEXPIRES) && + (!(AccountContext->TrustedClient)) && + ( AccountContext->TypeBody.User.Rid == DOMAIN_USER_RID_ADMIN )) { + + LARGE_INTEGER AccountNeverExpires, Temp; + + AccountNeverExpires = RtlConvertUlongToLargeInteger( + SAMP_ACCOUNT_NEVER_EXPIRES + ); + + OLD_TO_NEW_LARGE_INTEGER(All->AccountExpires, Temp); + + if (!( Temp.QuadPart == AccountNeverExpires.QuadPart)) { + + return( STATUS_SPECIAL_ACCOUNT ); + } + } + + // + // If the caller is trying to set trusted values, assume the + // caller is trusted, leave DesiredAccess = 0, and proceed. + // We'll check to make sure caller is trusted later. + // + + if ( !(All->WhichFields & USER_ALL_WRITE_TRUSTED_MASK) ) { + + // + // Set desired access based on which fields the caller is + // trying to change. + // + + if ( All->WhichFields & USER_ALL_WRITE_ACCOUNT_MASK ) { + + DesiredAccess |= USER_WRITE_ACCOUNT; + } + + if ( All->WhichFields & USER_ALL_WRITE_PREFERENCES_MASK ) { + + DesiredAccess |= USER_WRITE_PREFERENCES; + } + + if ( All->WhichFields & USER_ALL_WRITE_FORCE_PASSWORD_CHANGE_MASK ) { + + DesiredAccess |= USER_FORCE_PASSWORD_CHANGE; + } + + ASSERT( DesiredAccess != 0 ); + } + + break; + + case UserInternal2Information: + + // + // These levels are only setable by trusted clients. The code + // below will check AccountContext->TrustedClient after calling + // SampLookupContext, and only set the data if it is TRUE. + // + + DesiredAccess = (ACCESS_MASK)0; // trusted client; no need to verify + break; + + case UserGeneralInformation: + case UserAccountInformation: + case UserLogonInformation: + default: + + return(STATUS_INVALID_INFO_CLASS); + + } // end_switch + + + + // + // Grab the lock + // + + NtStatus = SampAcquireWriteLock(); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + + // + // Validate type of, and access to object. + // + + AccountContext = (PSAMP_OBJECT)UserHandle; + ObjectRid = AccountContext->TypeBody.User.Rid; + NtStatus = SampLookupContext( + AccountContext, + DesiredAccess, + SampUserObjectType, // ExpectedType + &FoundType + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Get a pointer to the domain this object is in. + // This is used for auditing. + // + + DomainIndex = AccountContext->DomainIndex; + Domain = &SampDefinedDomains[ DomainIndex ]; + + // + // Get the user's rid. This is used for notifying other + // packages of a password change. + // + + UserRid = AccountContext->TypeBody.User.Rid; + + + // + // If this information level contains reversibly encrypted passwords + // it is not allowed if the DOMAIN_PASSWORD_NO_CLEAR_CHANGE bit is + // set. If that happens, return an error indicating that + // the older information level should be used. + // + + if ((UserInformationClass == UserInternal4Information) || + (UserInformationClass == UserInternal5Information)) { + + if (Domain->UnmodifiedFixed.PasswordProperties & + DOMAIN_PASSWORD_NO_CLEAR_CHANGE) { + + NtStatus = RPC_NT_INVALID_TAG; + } + + } + + if (NT_SUCCESS(NtStatus)) { + + // + // If the information level requires, retrieve the V1_FIXED + // record from the registry. We need to fetch V1_FIXED if we + // are going to change it or if we need the AccountControl + // flags for display cache updating. + // + // The following information levels change data that is in the cached + // display list. + // + + switch (UserInformationClass) { + + case UserAllInformation: + case UserInternal3Information: + case UserInternal4Information: + + if ( ( All->WhichFields & + ( USER_ALL_USERNAME | USER_ALL_FULLNAME | + USER_ALL_ADMINCOMMENT | USER_ALL_USERACCOUNTCONTROL ) ) + == 0 ) { + + // + // We're not changing any of the fields in the display + // info, we don't update the account display. + // + + break; + } + + case UserControlInformation: + case UserNameInformation: + case UserAccountNameInformation: + case UserFullNameInformation: + case UserAdminCommentInformation: + + MustUpdateAccountDisplay = TRUE; + } + + // + // These levels involve updating the V1aFixed structure + // + + switch (UserInformationClass) { + + case UserAllInformation: + case UserInternal3Information: + case UserInternal4Information: + + // + // Earlier, we might have just trusted that the caller + // was a trusted client. Check it out here. + // + + if ( ( DesiredAccess == 0 ) && + ( !AccountContext->TrustedClient ) ) { + + NtStatus = STATUS_ACCESS_DENIED; + break; + } + + // + // Otherwise fall through + // + + case UserPreferencesInformation: + case UserPrimaryGroupInformation: + case UserControlInformation: + case UserExpiresInformation: + case UserSetPasswordInformation: + case UserInternal1Information: + case UserInternal2Information: + case UserInternal5Information: + + MustQueryV1aFixed = TRUE; + + break; + + default: + + NtStatus = STATUS_SUCCESS; + + } // end_switch + + + } + + if ( NT_SUCCESS( NtStatus ) && + ( MustQueryV1aFixed || MustUpdateAccountDisplay ) ) { + + NtStatus = SampRetrieveUserV1aFixed( + AccountContext, + &V1aFixed + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Store away the old account control flags for cache update + // + + OldUserAccountControl = V1aFixed.UserAccountControl; + } + } + + if (NT_SUCCESS(NtStatus)) { + + // + // case on the type information requested + // + + switch (UserInformationClass) { + + case UserAllInformation: + case UserInternal3Information: + case UserInternal4Information: + + // + // Set the string data + // + + if ( All->WhichFields & USER_ALL_WORKSTATIONS ) { + + if ( !AccountContext->TrustedClient ) { + + // + // Convert the workstation list, which is given + // to us in UI/Service format, to API list format + // before storing it. Note that we don't do this + // for trusted clients, since they're just + // propogating data that has already been + // converted. + // + + NtStatus = RtlConvertUiListToApiList( + &(All->WorkStations), + &ApiList, + FALSE ); + } else { + ApiList = All->WorkStations; + } + + if ( NT_SUCCESS( NtStatus ) ) { + + NtStatus = SampSetUnicodeStringAttribute( + AccountContext, + SAMP_USER_WORKSTATIONS, + &ApiList + ); + } + } + + if ( ( NT_SUCCESS( NtStatus ) ) && + ( All->WhichFields & USER_ALL_USERNAME ) ) { + + NtStatus = SampChangeUserAccountName( + AccountContext, + &(All->UserName), + &OldAccountName + ); + + if (!NT_SUCCESS(NtStatus)) { + + OldAccountName.Buffer = NULL; + } + } + + if ( ( NT_SUCCESS( NtStatus ) ) && + ( All->WhichFields & USER_ALL_FULLNAME ) ) { + + NtStatus = SampSetUnicodeStringAttribute( + AccountContext, + SAMP_USER_FULL_NAME, + &(All->FullName) + ); + } + + if ( ( NT_SUCCESS( NtStatus ) ) && + ( All->WhichFields & USER_ALL_HOMEDIRECTORY ) ) { + + NtStatus = SampSetUnicodeStringAttribute( + AccountContext, + SAMP_USER_HOME_DIRECTORY, + &(All->HomeDirectory) + ); + } + + if ( ( NT_SUCCESS( NtStatus ) ) && + ( All->WhichFields & USER_ALL_HOMEDIRECTORYDRIVE ) ) { + + NtStatus = SampSetUnicodeStringAttribute( + AccountContext, + SAMP_USER_HOME_DIRECTORY_DRIVE, + &(All->HomeDirectoryDrive) + ); + } + + if ( ( NT_SUCCESS( NtStatus ) ) && + ( All->WhichFields & USER_ALL_SCRIPTPATH ) ) { + + NtStatus = SampSetUnicodeStringAttribute( + AccountContext, + SAMP_USER_SCRIPT_PATH, + &(All->ScriptPath) + ); + } + + if ( ( NT_SUCCESS( NtStatus ) ) && + ( All->WhichFields & USER_ALL_PROFILEPATH ) ) { + + NtStatus = SampSetUnicodeStringAttribute( + AccountContext, + SAMP_USER_PROFILE_PATH, + &(All->ProfilePath) + ); + } + + if ( ( NT_SUCCESS( NtStatus ) ) && + ( All->WhichFields & USER_ALL_ADMINCOMMENT ) ) { + + NtStatus = SampSetUnicodeStringAttribute( + AccountContext, + SAMP_USER_ADMIN_COMMENT, + &(All->AdminComment) + ); + } + + if ( ( NT_SUCCESS( NtStatus ) ) && + ( All->WhichFields & USER_ALL_USERCOMMENT ) ) { + + NtStatus = SampSetUnicodeStringAttribute( + AccountContext, + SAMP_USER_USER_COMMENT, + &(All->UserComment) + ); + } + + if ( ( NT_SUCCESS( NtStatus ) ) && + ( All->WhichFields & USER_ALL_PARAMETERS ) ) { + + NtStatus = SampSetUnicodeStringAttribute( + AccountContext, + SAMP_USER_PARAMETERS, + &(All->Parameters) + ); + } + + if ( ( NT_SUCCESS( NtStatus ) ) && + ( All->WhichFields & USER_ALL_LOGONHOURS ) ) { + + // + // Set the LogonHours + // + + NtStatus = SampReplaceUserLogonHours( + AccountContext, + &(All->LogonHours) + ); + } + + if ( ( NT_SUCCESS( NtStatus ) ) && ( + ( All->WhichFields & USER_ALL_NTPASSWORDPRESENT ) || + ( All->WhichFields & USER_ALL_LMPASSWORDPRESENT ) ) ) { + + NT_OWF_PASSWORD NtOwfBuffer; + LM_OWF_PASSWORD LmOwfBuffer; + PLM_OWF_PASSWORD TmpLmBuffer; + PNT_OWF_PASSWORD TmpNtBuffer; + BOOLEAN TmpLmPresent; + BOOLEAN TmpNtPresent; + + + // + // Get copy of the account name to pass to + // notification packages. + // + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_ACCOUNT_NAME, + TRUE, // Make copy + &AccountName + ); + + if (!NT_SUCCESS(NtStatus)) { + break; + } + + + if (UserInformationClass != UserInternal4Information) { + + // + // Hashed passwords were sent. + // + + if ( AccountContext->TrustedClient ) { + + // + // Set password buffers as trusted client has + // indicated. + // + + if ( All->WhichFields & USER_ALL_LMPASSWORDPRESENT ) { + + TmpLmBuffer = (PLM_OWF_PASSWORD)All->LmPassword.Buffer; + TmpLmPresent = All->LmPasswordPresent; + + } else { + + TmpLmBuffer = (PLM_OWF_PASSWORD)NULL; + TmpLmPresent = FALSE; + } + + if ( All->WhichFields & USER_ALL_NTPASSWORDPRESENT ) { + + TmpNtBuffer = (PNT_OWF_PASSWORD)All->NtPassword.Buffer; + TmpNtPresent = All->NtPasswordPresent; + + } else { + + TmpNtBuffer = (PNT_OWF_PASSWORD)NULL; + TmpNtPresent = FALSE; + } + + } else { + + // + // This call came from the client-side. + // The OWFs will have been encrypted with the session + // key across the RPC link. + // + // Get the session key and decrypt both OWFs + // + + NtStatus = RtlGetUserSessionKeyServer( + (RPC_BINDING_HANDLE)UserHandle, + &UserSessionKey + ); + + if ( !NT_SUCCESS( NtStatus ) ) { + break; // out of switch + } + + // + // Decrypt the LM OWF Password with the session key + // + + if ( All->WhichFields & USER_ALL_LMPASSWORDPRESENT ) { + + NtStatus = RtlDecryptLmOwfPwdWithUserKey( + (PENCRYPTED_LM_OWF_PASSWORD) + All->LmPassword.Buffer, + &UserSessionKey, + &LmOwfBuffer + ); + if ( !NT_SUCCESS( NtStatus ) ) { + break; // out of switch + } + + TmpLmBuffer = &LmOwfBuffer; + TmpLmPresent = All->LmPasswordPresent; + + } else { + + TmpLmBuffer = (PLM_OWF_PASSWORD)NULL; + TmpLmPresent = FALSE; + } + + // + // Decrypt the NT OWF Password with the session key + // + + if ( All->WhichFields & USER_ALL_NTPASSWORDPRESENT ) { + + NtStatus = RtlDecryptNtOwfPwdWithUserKey( + (PENCRYPTED_NT_OWF_PASSWORD) + All->NtPassword.Buffer, + &UserSessionKey, + &NtOwfBuffer + ); + + if ( !NT_SUCCESS( NtStatus ) ) { + break; // out of switch + } + + TmpNtBuffer = &NtOwfBuffer; + TmpNtPresent = All->NtPasswordPresent; + + } else { + + TmpNtBuffer = (PNT_OWF_PASSWORD)NULL; + TmpNtPresent = FALSE; + } + + } + + } else { + + // + // The clear text password was sent, so use that. + // + + NtStatus = SampDecryptPasswordWithSessionKey( + UserHandle, + &Buffer->Internal4.UserPassword, + &ClearTextPassword + ); + if (!NT_SUCCESS(NtStatus)) { + break; + } + + // + // The caller might be simultaneously setting + // the password and changing the account to be + // a machine or trust account. In this case, + // we don't validate the password (e.g., length). + // + + if (!((All->WhichFields & USER_ALL_USERACCOUNTCONTROL) && + (All->UserAccountControl & + (USER_WORKSTATION_TRUST_ACCOUNT | USER_SERVER_TRUST_ACCOUNT)) + )) { + + NtStatus = SampCheckPasswordRestrictions( + UserHandle, + &ClearTextPassword + ); + + if (!NT_SUCCESS(NtStatus)) { + break; + } + } + + + // + // Compute the hashed passwords. + // + + NtStatus = SampCalculateLmAndNtOwfPasswords( + &ClearTextPassword, + &TmpLmPresent, + &LmOwfBuffer, + &NtOwfBuffer + ); + if (!NT_SUCCESS(NtStatus)) { + break; + } + + + TmpNtPresent = TRUE; + TmpLmBuffer = &LmOwfBuffer; + TmpNtBuffer = &NtOwfBuffer; + } + + + // + // Set the password data + // + + NtStatus = SampStoreUserPasswords( + AccountContext, + TmpLmBuffer, + TmpLmPresent, + TmpNtBuffer, + TmpNtPresent, + FALSE + ); + + // + // If we set the password, + // set the PasswordLastSet time to now. + // + + if ( NT_SUCCESS( NtStatus ) ) { + NtStatus = SampComputePasswordExpired( + FALSE, // Password doesn't expire now + &V1aFixed.PasswordLastSet + ); + } + + + // + // Replicate immediately if this is a machine account + // + + if ( (V1aFixed.UserAccountControl & USER_MACHINE_ACCOUNT_MASK) || + ((All->WhichFields & USER_ALL_USERACCOUNTCONTROL ) && + (All->UserAccountControl & USER_MACHINE_ACCOUNT_MASK) )) { + ReplicateImmediately = TRUE; + } + DeltaType = SecurityDbChangePassword; + + } + + if ( ( NT_SUCCESS( NtStatus ) ) && + ( All->WhichFields & USER_ALL_PASSWORDEXPIRED ) ) { + + // + // If the PasswordExpired field is passed in, + // Only update PasswordLastSet if the password is being + // forced to expire or if the password is currently forced + // to expire. + // + // Avoid setting the PasswordLastSet field to the current + // time if it is already non-zero. Otherwise, the field + // will slowly creep forward each time this function is + // called and the password will never expire. + // + if ( All->PasswordExpired || + (SampHasNeverTime.QuadPart == V1aFixed.PasswordLastSet.QuadPart) ) { + + NtStatus = SampComputePasswordExpired( + All->PasswordExpired, + &V1aFixed.PasswordLastSet + ); + } + } + + if ( ( NT_SUCCESS( NtStatus ) ) && + ( All->WhichFields & USER_ALL_PRIVATEDATA ) ) { + + // + // Set the private data + // + + NtStatus = SampSetPrivateUserData( + AccountContext, + All->PrivateData.Length, + All->PrivateData.Buffer + ); + } + + if ( ( NT_SUCCESS( NtStatus ) ) && + ( All->WhichFields & USER_ALL_SECURITYDESCRIPTOR ) ) { + + // + // Set the security descriptor + // + + NtStatus = SampSetAccessAttribute( + AccountContext, + SAMP_USER_SECURITY_DESCRIPTOR, + All->SecurityDescriptor.SecurityDescriptor, + All->SecurityDescriptor.Length + ); + } + + // + // Set the fixed data + // + // Note that PasswordCanChange and PasswordMustChange + // aren't stored; they're calculated when needed. + // + + if ( ( NT_SUCCESS( NtStatus ) ) && + ( All->WhichFields & USER_ALL_USERACCOUNTCONTROL ) ) { + + // + // If passwords were passed in, we've already set them, + // so it's OK to call this now. + // + + NtStatus = SampIsUserAccountControlValid( + AccountContext, + All->UserAccountControl + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + if ( ( V1aFixed.UserAccountControl & + USER_MACHINE_ACCOUNT_MASK ) != + ( All->UserAccountControl & + USER_MACHINE_ACCOUNT_MASK ) ) { + + // + // One or more of the machine account bits has + // changed; we'll notify netlogon below. + // + + UserAccountControlChanged = TRUE; + + IgnoreStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_ACCOUNT_NAME, + TRUE, // Make copy + &OldAccountName + ); + } + + // + // Untrusted clients can: + // + // 1) leave the the ACCOUNT_AUTO_LOCK flag set. + // 2) Clear the ACCOUNT_AUTO_LOCK flag. + // + // They can't set it. So, we must AND the user's + // flag value with the current value and set that + // in the UserAccountControl field. + // + + if (!(AccountContext->TrustedClient)) { + + // + // Minimize the passed in AccountControl + // with the currently set value. + // + + All->UserAccountControl |= + (USER_ACCOUNT_AUTO_LOCKED & + All->UserAccountControl & + V1aFixed.UserAccountControl); + + // + // If an untrusted client is unlocking the account, + // then we also need to re-set the BadPasswordCount. + // Trusted clients are expected to explicitly set + // the BadPasswordCount. + // + + CurrentlyLocked = (V1aFixed.UserAccountControl & + USER_ACCOUNT_AUTO_LOCKED) != 0; + Unlocking = (All->UserAccountControl & + USER_ACCOUNT_AUTO_LOCKED) == 0; + + if (CurrentlyLocked && Unlocking) { + + SampDiagPrint( DISPLAY_LOCKOUT, + ("SAM: SetInformationUser: Administrator unlocking account.\n" + " User Account : 0x%lx\n" + " Clearing lockout flag.\n" + " Resetting BadPassowrdCount.\n", + V1aFixed.UserId) ); + V1aFixed.BadPasswordCount = 0; + } + + } + + // + // Now set the account control flags + // + + V1aFixed.UserAccountControl = All->UserAccountControl; + + } + } + + if ( NT_SUCCESS( NtStatus ) ) { + + if ( All->WhichFields & USER_ALL_LASTLOGON ) { + + V1aFixed.LastLogon = All->LastLogon; + } + + if ( All->WhichFields & USER_ALL_LASTLOGOFF ) { + + V1aFixed.LastLogoff = All->LastLogoff; + } + + if ( All->WhichFields & USER_ALL_PASSWORDLASTSET ) { + + V1aFixed.PasswordLastSet = All->PasswordLastSet; + } + + if ( All->WhichFields & USER_ALL_ACCOUNTEXPIRES ) { + + V1aFixed.AccountExpires = All->AccountExpires; + } + + if ( All->WhichFields & USER_ALL_PRIMARYGROUPID ) { + + // + // Make sure the primary group is legitimate + // (it must be one the user is a member of) + // + + NtStatus = SampAssignPrimaryGroup( + AccountContext, + All->PrimaryGroupId + ); + if (NT_SUCCESS(NtStatus)) { + V1aFixed.PrimaryGroupId = All->PrimaryGroupId; + } else { + break; + } + } + + if ( All->WhichFields & USER_ALL_COUNTRYCODE ) { + + V1aFixed.CountryCode = All->CountryCode; + } + + if ( All->WhichFields & USER_ALL_CODEPAGE ) { + + V1aFixed.CodePage = All->CodePage; + } + + if ( All->WhichFields & USER_ALL_BADPASSWORDCOUNT ) { + + SampDiagPrint( DISPLAY_LOCKOUT, + ("SAM: SetInformationUser: \n" + " User Account : 0x%lx\n" + " Setting BadPasswordCount: %ld\n", + V1aFixed.UserId, + All->BadPasswordCount) + ); + + + + V1aFixed.BadPasswordCount = All->BadPasswordCount; + + if (UserInformationClass == UserInternal3Information) { + // + // Also set LastBadPasswordTime; + // + V1aFixed.LastBadPasswordTime = + Buffer->Internal3.LastBadPasswordTime; + +#if DBG + RtlTimeToTimeFields( + &Buffer->Internal3.LastBadPasswordTime, + &T1); + + SampDiagPrint( DISPLAY_LOCKOUT, + (" LastBadPasswordTime : [0x%lx, 0x%lx] %d:%d:%d\n", + Buffer->Internal3.LastBadPasswordTime.HighPart, + Buffer->Internal3.LastBadPasswordTime.LowPart, + T1.Hour, T1.Minute, T1.Second ) + ); +#endif //DBG + } + } + + if ( All->WhichFields & USER_ALL_LOGONCOUNT ) { + + V1aFixed.LogonCount = All->LogonCount; + } + + NtStatus = SampReplaceUserV1aFixed( + AccountContext, + &V1aFixed + ); + } + + break; + + case UserPreferencesInformation: + + V1aFixed.CountryCode = Buffer->Preferences.CountryCode; + V1aFixed.CodePage = Buffer->Preferences.CodePage; + + NtStatus = SampReplaceUserV1aFixed( + AccountContext, + &V1aFixed + ); + + + // + // replace the user comment + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampSetUnicodeStringAttribute( + AccountContext, + SAMP_USER_USER_COMMENT, + (PUNICODE_STRING)&(Buffer->Preferences.UserComment) + ); + } + + + break; + + + case UserParametersInformation: + + + // + // replace the parameters + // + + NtStatus = SampSetUnicodeStringAttribute( + AccountContext, + SAMP_USER_PARAMETERS, + (PUNICODE_STRING)&(Buffer->Parameters.Parameters) + ); + + break; + + + case UserLogonHoursInformation: + + NtStatus = SampReplaceUserLogonHours( + AccountContext, + (PLOGON_HOURS)&(Buffer->LogonHours.LogonHours) + ); + break; + + + case UserNameInformation: + + // + // first change the Full Name, then change the account name... + // + + // + // replace the full name - no value restrictions + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampSetUnicodeStringAttribute( + AccountContext, + SAMP_USER_FULL_NAME, + (PUNICODE_STRING)&(Buffer->Name.FullName) + ); + + // + // Change the account name + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampChangeUserAccountName( + AccountContext, + (PUNICODE_STRING)&(Buffer->Name.UserName), + &OldAccountName + ); + } + } + + + // + // Don't free the OldAccountName yet; we'll need it at the + // very end. + // + + break; + + + case UserAccountNameInformation: + + NtStatus = SampChangeUserAccountName( + AccountContext, + (PUNICODE_STRING)&(Buffer->AccountName.UserName), + &OldAccountName + ); + + // + // Don't free the OldAccountName; we'll need it at the + // very end. + // + + break; + + + case UserFullNameInformation: + + // + // replace the full name - no value restrictions + // + + NtStatus = SampSetUnicodeStringAttribute( + AccountContext, + SAMP_USER_FULL_NAME, + (PUNICODE_STRING)&(Buffer->FullName.FullName) + ); + break; + + + + + case UserPrimaryGroupInformation: + + // + // Make sure the primary group is legitimate + // (it must be one the user is a member of) + // + + NtStatus = SampAssignPrimaryGroup( + AccountContext, + Buffer->PrimaryGroup.PrimaryGroupId + ); + + // + // Update the V1_FIXED info. + // + + if (NT_SUCCESS(NtStatus)) { + + V1aFixed.PrimaryGroupId = Buffer->PrimaryGroup.PrimaryGroupId; + + NtStatus = SampReplaceUserV1aFixed( + AccountContext, + &V1aFixed + ); + } + + break; + + + case UserHomeInformation: + + // + // replace the home directory + // + + NtStatus = SampSetUnicodeStringAttribute( + AccountContext, + SAMP_USER_HOME_DIRECTORY, + (PUNICODE_STRING)&(Buffer->Home.HomeDirectory) + ); + + // + // replace the home directory drive + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampSetUnicodeStringAttribute( + AccountContext, + SAMP_USER_HOME_DIRECTORY_DRIVE, + (PUNICODE_STRING)&(Buffer->Home.HomeDirectoryDrive) + ); + } + + break; + + case UserScriptInformation: + + // + // replace the script + // + + NtStatus = SampSetUnicodeStringAttribute( + AccountContext, + SAMP_USER_SCRIPT_PATH, + (PUNICODE_STRING)&(Buffer->Script.ScriptPath) + ); + + break; + + + case UserProfileInformation: + + // + // replace the Profile + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampSetUnicodeStringAttribute( + AccountContext, + SAMP_USER_PROFILE_PATH, + (PUNICODE_STRING)&(Buffer->Profile.ProfilePath) + ); + } + + break; + + + case UserAdminCommentInformation: + + // + // replace the admin comment + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampSetUnicodeStringAttribute( + AccountContext, + SAMP_USER_ADMIN_COMMENT, + (PUNICODE_STRING)&(Buffer->AdminComment.AdminComment) + ); + } + + break; + + + case UserWorkStationsInformation: + + // + // Convert the workstation list, which is given to us in + // UI/Service format, to API list format before storing + // it. + // + + NtStatus = RtlConvertUiListToApiList( + (PUNICODE_STRING)&(Buffer->WorkStations.WorkStations), + &ApiList, + FALSE ); + + // + // replace the admin workstations + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampSetUnicodeStringAttribute( + AccountContext, + SAMP_USER_WORKSTATIONS, + &ApiList + ); + + RtlFreeHeap( RtlProcessHeap(), 0, ApiList.Buffer ); + } + + break; + + + case UserControlInformation: + + if ( NT_SUCCESS( NtStatus ) ) { + + NtStatus = SampIsUserAccountControlValid( + AccountContext, + Buffer->Control.UserAccountControl + ); + } + + if (NT_SUCCESS(NtStatus)) { + + if ( ( V1aFixed.UserAccountControl & + USER_MACHINE_ACCOUNT_MASK ) != + ( Buffer->Control.UserAccountControl & + USER_MACHINE_ACCOUNT_MASK ) ) { + + // + // One of the machine account bits has changed; + // we'll notify netlogon below. + // + + UserAccountControlChanged = TRUE; + + IgnoreStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_ACCOUNT_NAME, + TRUE, // Make copy + &OldAccountName + ); + } + + // + // Untrusted clients can: + // + // 1) leave the the ACCOUNT_AUTO_LOCK flag set. + // 2) Clear the ACCOUNT_AUTO_LOCK flag. + // + // They can't set it. So, we must AND the user's + // flag value with the current value and set that + // in the UserAccountControl field. + // + // One more caveat, when an untrusted client clears the + // ACCOUNT_AUTO_LOCK flag, then we need to reset + // the BadPasswordCount flag to zero. We don't + // do this for trusted clients, because we figure + // they know what they are doing and will set + // everthing appropriately. + // + + if (!(AccountContext->TrustedClient)) { + + Buffer->Control.UserAccountControl |= + (USER_ACCOUNT_AUTO_LOCKED & + Buffer->Control.UserAccountControl & + V1aFixed.UserAccountControl); + + // + // If an untrusted client is unlocking the account, + // then we also need to re-set the BadPasswordCount. + // Trusted clients are expected to explicitly set + // the BadPasswordCount. + // + + CurrentlyLocked = (V1aFixed.UserAccountControl & + USER_ACCOUNT_AUTO_LOCKED) != 0; + Unlocking = (Buffer->Control.UserAccountControl & + USER_ACCOUNT_AUTO_LOCKED) == 0; + + if (CurrentlyLocked && Unlocking) { + SampDiagPrint( DISPLAY_LOCKOUT, + ("SAM: SetInformationUser: Administrator unlocking account.\n" + " User Account : 0x%lx\n" + " Clearing lockout flag.\n" + " Resetting BadPassowrdCount.\n", + V1aFixed.UserId) ); + V1aFixed.BadPasswordCount = 0; + } + } + + V1aFixed.UserAccountControl = Buffer->Control.UserAccountControl; + + NtStatus = SampReplaceUserV1aFixed( + AccountContext, + &V1aFixed + ); + } + + break; + + + case UserExpiresInformation: + + // + // If the user is the special account Administrator, return an + // error if trying to set the expiry information, except to the + // value that means that the account never expires. + // + + if ((!AccountContext->TrustedClient) && + ( AccountContext->TypeBody.User.Rid == DOMAIN_USER_RID_ADMIN )) { + + LARGE_INTEGER AccountNeverExpires, Temp; + + AccountNeverExpires = RtlConvertUlongToLargeInteger( + SAMP_ACCOUNT_NEVER_EXPIRES + ); + + OLD_TO_NEW_LARGE_INTEGER(All->AccountExpires, Temp); + + if (!( Temp.QuadPart == AccountNeverExpires.QuadPart)) { + + NtStatus = STATUS_SPECIAL_ACCOUNT; + break; + } + } + + V1aFixed.AccountExpires = Buffer->Expires.AccountExpires; + + NtStatus = SampReplaceUserV1aFixed( + AccountContext, + &V1aFixed + ); + + break; + + + case UserSetPasswordInformation: + + ASSERT(FALSE); // Should have been mapped to INTERNAL1 on client side + NtStatus = STATUS_INVALID_INFO_CLASS; + break; + + + case UserInternal1Information: + case UserInternal5Information: + + // + // Get copy of the account name to pass to + // notification packages. + // + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_ACCOUNT_NAME, + TRUE, // Make copy + &AccountName + ); + + if (!NT_SUCCESS(NtStatus)) { + break; + } + + + + if (UserInformationClass == UserInternal1Information) { + + LmPresent = Buffer->Internal1.LmPasswordPresent; + NtPresent = Buffer->Internal1.NtPasswordPresent; + PasswordExpired = Buffer->Internal1.PasswordExpired; + + // + // If our client is trusted, they are on the server side + // and data from them will not have been encrypted with the + // user session key - so don't decrypt them + // + + if ( AccountContext->TrustedClient ) { + + // + // Copy the (not) encrypted owfs into the owf buffers + // + + ASSERT(ENCRYPTED_LM_OWF_PASSWORD_LENGTH == LM_OWF_PASSWORD_LENGTH); + ASSERT(ENCRYPTED_NT_OWF_PASSWORD_LENGTH == NT_OWF_PASSWORD_LENGTH); + + RtlCopyMemory(&LmOwfPassword, + &Buffer->Internal1.EncryptedLmOwfPassword, + LM_OWF_PASSWORD_LENGTH + ); + + RtlCopyMemory(&NtOwfPassword, + &Buffer->Internal1.EncryptedNtOwfPassword, + NT_OWF_PASSWORD_LENGTH + ); + + } else { + + + // + // This call came from the client-side. The + // The OWFs will have been encrypted with the session + // key across the RPC link. + // + // Get the session key and decrypt both OWFs + // + + NtStatus = RtlGetUserSessionKeyServer( + (RPC_BINDING_HANDLE)UserHandle, + &UserSessionKey + ); + + if ( !NT_SUCCESS( NtStatus ) ) { + break; // out of switch + } + + + // + // Decrypt the LM OWF Password with the session key + // + + if ( Buffer->Internal1.LmPasswordPresent) { + + NtStatus = RtlDecryptLmOwfPwdWithUserKey( + &Buffer->Internal1.EncryptedLmOwfPassword, + &UserSessionKey, + &LmOwfPassword + ); + if ( !NT_SUCCESS( NtStatus ) ) { + break; // out of switch + } + } + + + // + // Decrypt the NT OWF Password with the session key + // + + if ( Buffer->Internal1.NtPasswordPresent) { + + NtStatus = RtlDecryptNtOwfPwdWithUserKey( + &Buffer->Internal1.EncryptedNtOwfPassword, + &UserSessionKey, + &NtOwfPassword + ); + + if ( !NT_SUCCESS( NtStatus ) ) { + break; // out of switch + } + } + } + } else { + + // + // Password was sent cleartext. + // + + NtStatus = SampDecryptPasswordWithSessionKey( + UserHandle, + &Buffer->Internal5.UserPassword, + &ClearTextPassword + ); + if (!NT_SUCCESS(NtStatus)) { + break; + } + + + // + // Compute the hashed passwords. + // + + NtStatus = SampCalculateLmAndNtOwfPasswords( + &ClearTextPassword, + &LmPresent, + &LmOwfPassword, + &NtOwfPassword + ); + if (!NT_SUCCESS(NtStatus)) { + break; + } + + + + NtPresent = TRUE; + PasswordExpired = Buffer->Internal5.PasswordExpired; + + } + // + // Store away the new OWF passwords + // + + NtStatus = SampStoreUserPasswords( + AccountContext, + &LmOwfPassword, + LmPresent, + &NtOwfPassword, + NtPresent, + FALSE + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + NtStatus = SampStorePasswordExpired( + AccountContext, + PasswordExpired + ); + } + + // + // Replicate immediately if this is a machine account + // + + if ( V1aFixed.UserAccountControl & USER_MACHINE_ACCOUNT_MASK ) { + ReplicateImmediately = TRUE; + } + + break; + + + + + case UserInternal2Information: + + if ( AccountContext->TrustedClient ) { + + TellNetlogon = FALSE; + + // + // There are two ways to set logon/logoff statistics: + // + // 1) Directly, specifying each one being set, + // 2) Implicitly, specifying the action to + // represent + // + // These two forms are mutually exclusive. That is, + // you can't specify both a direct action and an + // implicit action. In fact, you can't specify two + // implicit actions either. + // + + if (Buffer->Internal2.StatisticsToApply + & USER_LOGON_INTER_SUCCESS_LOGON) { + + if ( (Buffer->Internal2.StatisticsToApply + & ~USER_LOGON_INTER_SUCCESS_LOGON) != 0 ) { + + NtStatus = STATUS_INVALID_PARAMETER; + break; + } else { + + // + // Set BadPasswordCount = 0 + // Increment LogonCount + // Set LastLogon = NOW + // + // + // + + + V1aFixed.BadPasswordCount = 0; + if (V1aFixed.LogonCount != 0xFFFF) { + V1aFixed.LogonCount += 1; + } + NtQuerySystemTime( &V1aFixed.LastLogon ); + + +#if DBG + RtlTimeToTimeFields( + &V1aFixed.LastLogon, + &T1); + + SampDiagPrint( DISPLAY_LOCKOUT, + ("SAM: SetInformationUser: Successful interactive logon\n" + " User Account : 0x%lx\n" + " ReSetting BadPasswordCount: %ld\n" + " LastLogon Time : [0x%lx, 0x%lx] %d:%d:%d\n", + V1aFixed.UserId, + V1aFixed.BadPasswordCount, + V1aFixed.LastLogon.HighPart, + V1aFixed.LastLogon.LowPart, + T1.Hour, T1.Minute, T1.Second) + ); +#endif //DGB + } + } + + if (Buffer->Internal2.StatisticsToApply + & USER_LOGON_INTER_SUCCESS_LOGOFF) { + if ( (Buffer->Internal2.StatisticsToApply + & ~USER_LOGON_INTER_SUCCESS_LOGOFF) != 0 ) { + + NtStatus = STATUS_INVALID_PARAMETER; + break; + } else { + + // + // Set LastLogoff time + // Decrement LogonCount (don't let it become negative) + // + + if (V1aFixed.LogonCount != 0) { + V1aFixed.LogonCount -= 1; + } + NtQuerySystemTime( &V1aFixed.LastLogoff ); + } + } + + if (Buffer->Internal2.StatisticsToApply + & USER_LOGON_NET_SUCCESS_LOGON) { + + if ( (Buffer->Internal2.StatisticsToApply + & ~USER_LOGON_NET_SUCCESS_LOGON) != 0 ) { + + NtStatus = STATUS_INVALID_PARAMETER; + break; + } else { + + // + // Set BadPasswordCount = 0 + // Set LastLogon = NOW + // + // + // + + + V1aFixed.BadPasswordCount = 0; + NtQuerySystemTime( &V1aFixed.LastLogon ); + + +#if DBG + RtlTimeToTimeFields( + &V1aFixed.LastLogon, + &T1); + + SampDiagPrint( DISPLAY_LOCKOUT, + ("SAM: SetInformationUser: Successful network logon\n" + " User Account : 0x%lx\n" + " ReSetting BadPasswordCount: %ld\n" + " LastLogon Time : [0x%lx, 0x%lx] %d:%d:%d\n", + V1aFixed.UserId, + V1aFixed.BadPasswordCount, + V1aFixed.LastLogon.HighPart, + V1aFixed.LastLogon.LowPart, + T1.Hour, T1.Minute, T1.Second ) + ); +#endif //DBG + } + } + + if (Buffer->Internal2.StatisticsToApply + & USER_LOGON_NET_SUCCESS_LOGOFF) { + if ( (Buffer->Internal2.StatisticsToApply + & ~USER_LOGON_NET_SUCCESS_LOGOFF) != 0 ) { + + NtStatus = STATUS_INVALID_PARAMETER; + break; + } else { + + // + // Set LastLogoff time + // + + NtQuerySystemTime( &V1aFixed.LastLogoff ); + } + } + + if (Buffer->Internal2.StatisticsToApply + & USER_LOGON_BAD_PASSWORD) { + if ( (Buffer->Internal2.StatisticsToApply + & ~USER_LOGON_BAD_PASSWORD) != 0 ) { + + NtStatus = STATUS_INVALID_PARAMETER; + break; + } else { + + // + // Increment BadPasswordCount + // (might lockout account) + // + + + AccountLockedOut = + SampIncrementBadPasswordCount( + AccountContext, + &V1aFixed + ); + + // + // If the account has been locked out, + // ensure the BDCs in the domain are told. + // + + if ( AccountLockedOut ) { + TellNetlogon = TRUE; + ReplicateImmediately = TRUE; + } + } + } + + + + + + + if ( Buffer->Internal2.StatisticsToApply + & USER_LOGON_STAT_LAST_LOGON ) { + + OLD_TO_NEW_LARGE_INTEGER( + Buffer->Internal2.LastLogon, + V1aFixed.LastLogon ); + } + + if ( Buffer->Internal2.StatisticsToApply + & USER_LOGON_STAT_LAST_LOGOFF ) { + + OLD_TO_NEW_LARGE_INTEGER( + Buffer->Internal2.LastLogoff, + V1aFixed.LastLogoff ); + } + + if ( Buffer->Internal2.StatisticsToApply + & USER_LOGON_STAT_BAD_PWD_COUNT ) { + + SampDiagPrint( DISPLAY_LOCKOUT, + ("SAM: SetInformationUser: Bad Password Count\n" + " User Account : 0x%lx\n" + " Old BadPasswordCount: %ld\n" + " New BadPasswordCount: %ld\n", + V1aFixed.UserId, + V1aFixed.BadPasswordCount, + Buffer->Internal2.BadPasswordCount + ) + ); + V1aFixed.BadPasswordCount = + Buffer->Internal2.BadPasswordCount; + } + + if ( Buffer->Internal2.StatisticsToApply + & USER_LOGON_STAT_LOGON_COUNT ) { + + V1aFixed.LogonCount = Buffer->Internal2.LogonCount; + } + + NtStatus = SampReplaceUserV1aFixed( + AccountContext, + &V1aFixed + ); + + } else { + + // + // This information is only settable by trusted + // clients. + // + + NtStatus = STATUS_INVALID_INFO_CLASS; + } + + break; + + + } // end_switch + + + + } // end_if + + + + + // + // Go fetch any data we'll need to update the display cache + // Do this before we dereference the context + // + + if (NT_SUCCESS(NtStatus)) { + + if ( MustUpdateAccountDisplay ) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_ACCOUNT_NAME, + TRUE, // Make copy + &NewAccountName + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_FULL_NAME, + TRUE, // Make copy + &NewFullName + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_ADMIN_COMMENT, + TRUE, // Make copy + &NewAdminComment + ); + // + // If the account name has changed, then OldAccountName + // is already filled in. If the account name hasn't changed + // then the OldAccountName is the same as the new! + // + + if (NT_SUCCESS(NtStatus) && (OldAccountName.Buffer == NULL)) { + + NtStatus = SampDuplicateUnicodeString( + &OldAccountName, + &NewAccountName); + } + } + } + } + } + + + // + // Generate an audit if necessary. We don't account statistic + // updates, which we also don't notify Netlogon of. + // + + if (NT_SUCCESS(NtStatus) && + SampDoAccountAuditing(DomainIndex) && + TellNetlogon) { + + UNICODE_STRING + AccountName; + + IgnoreStatus = SampGetUnicodeStringAttribute( + AccountContext, // Context + SAMP_USER_ACCOUNT_NAME, // AttributeIndex + FALSE, // MakeCopy + &AccountName // UnicodeAttribute + ); + if (NT_SUCCESS(IgnoreStatus)) { + LsaIAuditSamEvent( + STATUS_SUCCESS, + SE_AUDITID_USER_CHANGE, // AuditId + Domain->Sid, // Domain SID + NULL, // Member Rid (not used) + NULL, // Member Sid (not used) + &AccountName, // Account Name + &Domain->ExternalName, // Domain + &AccountContext->TypeBody.User.Rid, // Account Rid + NULL // Privileges used + ); + } + + } + + // + // Dereference the account context + // + + if (NT_SUCCESS(NtStatus)) { + + // + // De-reference the object, write out any change to current xaction. + // + + NtStatus = SampDeReferenceContext( AccountContext, TRUE ); + + } else { + + // + // De-reference the object, ignore changes + // + + IgnoreStatus = SampDeReferenceContext( AccountContext, FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + } // end_if + + + + + // + // Commit the transaction and, if successful, + // notify netlogon of the changes. Also generate any necessary audits. + // + + if (NT_SUCCESS(NtStatus)) { + + if ( !TellNetlogon ) { + + // + // For logon statistics, we don't notify netlogon about changes + // to the database. Which means that we don't want the + // domain's modified count to increase. The commit routine + // will increase it automatically if this isn't a BDC, so we'll + // decrement it here. + // + + if (SampDefinedDomains[SampTransactionDomainIndex].CurrentFixed.ServerRole + != DomainServerRoleBackup) { + + SampDefinedDomains[SampTransactionDomainIndex].CurrentFixed.ModifiedCount.QuadPart = + SampDefinedDomains[SampTransactionDomainIndex].CurrentFixed.ModifiedCount.QuadPart - + 1; + } + } + + + + NtStatus = SampCommitAndRetainWriteLock(); + + + if ( NT_SUCCESS(NtStatus) ) { + + + + // + // Update the display information if the cache may be affected + // + + if ( MustUpdateAccountDisplay ) { + + SAMP_ACCOUNT_DISPLAY_INFO OldAccountInfo; + SAMP_ACCOUNT_DISPLAY_INFO NewAccountInfo; + + OldAccountInfo.Name = OldAccountName; + OldAccountInfo.Rid = ObjectRid; + OldAccountInfo.AccountControl = OldUserAccountControl; + RtlInitUnicodeString(&OldAccountInfo.Comment, NULL); + RtlInitUnicodeString(&OldAccountInfo.FullName, NULL); + + NewAccountInfo.Name = NewAccountName; + NewAccountInfo.Rid = ObjectRid; + NewAccountInfo.AccountControl = V1aFixed.UserAccountControl; + NewAccountInfo.Comment = NewAdminComment; + NewAccountInfo.FullName = NewFullName; + + IgnoreStatus = SampUpdateDisplayInformation(&OldAccountInfo, + &NewAccountInfo, + SampUserObjectType); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + // + // Notify netlogon if machine account changes. + // + + if ( ( UserAccountControlChanged ) && + ( OldAccountName.Buffer != NULL ) ) { + + // + // The UserAccountControl field changed (more specifically, + // the bits that netlogon is interested in changed) and + // we were able to get the name of the account. So notify + // netlogon of the change. + // + + IgnoreStatus = I_NetNotifyMachineAccount( + ObjectRid, + SampDefinedDomains[ + SampTransactionDomainIndex].Sid, + OldUserAccountControl, + V1aFixed.UserAccountControl, + &OldAccountName + ); + } + + // + // Notify netlogon of any user account changes + // + + if ( ( UserInformationClass == UserNameInformation ) || + ( UserInformationClass == UserAccountNameInformation ) || + ( ( UserInformationClass == UserAllInformation ) && + ( All->WhichFields & USER_ALL_USERNAME ) ) ) { + + // + // The account was renamed; let Netlogon know. + // + + SampNotifyNetlogonOfDelta( + SecurityDbRename, + SecurityDbObjectSamUser, + ObjectRid, + &OldAccountName, + (DWORD) ReplicateImmediately, + NULL // Delta data + ); + + } else { + + // + // Something in the account was changed. Notify netlogon about + // everything except logon statistics changes. + // + + if ( TellNetlogon ) { + SampNotifyNetlogonOfDelta( + DeltaType, + SecurityDbObjectSamUser, + ObjectRid, + (PUNICODE_STRING) NULL, + (DWORD) ReplicateImmediately, + NULL // Delta data + ); + } + } + } + } + + // + // Release the lock + // + + IgnoreStatus = SampReleaseWriteLock( FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + + // + // Notify any packages that a password was changed. + // + + if (NT_SUCCESS(NtStatus) && (DeltaType == SecurityDbChangePassword)) { + + // + // If the account name was changed, use the new account name. + // + + if (NewAccountName.Buffer != NULL) { + (void) SampPasswordChangeNotify( + &NewAccountName, + UserRid, + &ClearTextPassword + ); + } else { + (void) SampPasswordChangeNotify( + &AccountName, + UserRid, + &ClearTextPassword + ); + + } + + } + + + // + // Clean up strings + // + + SampFreeUnicodeString( &OldAccountName ); + SampFreeUnicodeString( &NewAccountName ); + SampFreeUnicodeString( &NewFullName ); + SampFreeUnicodeString( &NewAdminComment ); + SampFreeUnicodeString( &AccountName ); + + if (ClearTextPassword.Buffer != NULL) { + + RtlZeroMemory( + ClearTextPassword.Buffer, + ClearTextPassword.Length + ); + + RtlFreeUnicodeString( &ClearTextPassword ); + + } + + + + return(NtStatus); +} + + + +NTSTATUS +SamrChangePasswordUser( + IN SAMPR_HANDLE UserHandle, + IN BOOLEAN LmPresent, + IN PENCRYPTED_LM_OWF_PASSWORD OldLmEncryptedWithNewLm, + IN PENCRYPTED_LM_OWF_PASSWORD NewLmEncryptedWithOldLm, + IN BOOLEAN NtPresent, + IN PENCRYPTED_NT_OWF_PASSWORD OldNtEncryptedWithNewNt, + IN PENCRYPTED_NT_OWF_PASSWORD NewNtEncryptedWithOldNt, + IN BOOLEAN NtCrossEncryptionPresent, + IN PENCRYPTED_NT_OWF_PASSWORD NewNtEncryptedWithNewLm, + IN BOOLEAN LmCrossEncryptionPresent, + IN PENCRYPTED_LM_OWF_PASSWORD NewLmEncryptedWithNewNt + ) + + +/*++ + +Routine Description: + + This service sets the password to NewPassword only if OldPassword + matches the current user password for this user and the NewPassword + is not the same as the domain password parameter PasswordHistoryLength + passwords. This call allows users to change their own password if + they have access USER_CHANGE_PASSWORD. Password update restrictions + apply. + + +Parameters: + + UserHandle - The handle of an opened user to operate on. + + LMPresent - TRUE if the LM parameters (below) are valid. + + LmOldEncryptedWithLmNew - the old LM OWF encrypted with the new LM OWF + + LmNewEncryptedWithLmOld - the new LM OWF encrypted with the old LM OWF + + + NtPresent - TRUE if the NT parameters (below) are valid + + NtOldEncryptedWithNtNew - the old NT OWF encrypted with the new NT OWF + + NtNewEncryptedWithNtOld - the new NT OWF encrypted with the old NT OWF + + + NtCrossEncryptionPresent - TRUE if NtNewEncryptedWithLmNew is valid. + + NtNewEncryptedWithLmNew - the new NT OWF encrypted with the new LM OWF + + + LmCrossEncryptionPresent - TRUE if LmNewEncryptedWithNtNew is valid. + + LmNewEncryptedWithNtNew - the new LM OWF encrypted with the new NT OWF + + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_ILL_FORMED_PASSWORD - The new password is poorly formed, + e.g. contains characters that can't be entered from the + keyboard, etc. + + STATUS_PASSWORD_RESTRICTION - A restriction prevents the password + from being changed. This may be for a number of reasons, + including time restrictions on how often a password may be + changed or length restrictions on the provided password. + + This error might also be returned if the new password matched + a password in the recent history log for the account. + Security administrators indicate how many of the most + recently used passwords may not be re-used. These are kept + in the password recent history log. + + STATUS_WRONG_PASSWORD - OldPassword does not contain the user's + current password. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled for this + operation + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. + + STATUS_CROSS_ENCRYPTION_REQUIRED - No NT password is stored, so the caller + must provide the OldNtEncryptedWithOldLm parameter. + +--*/ +{ + NTSTATUS NtStatus, TmpStatus, IgnoreStatus; + PSAMP_OBJECT AccountContext; + PSAMP_DEFINED_DOMAINS Domain; + SAMP_OBJECT_TYPE FoundType; + LARGE_INTEGER TimeNow; + LM_OWF_PASSWORD StoredLmOwfPassword; + NT_OWF_PASSWORD StoredNtOwfPassword; + NT_OWF_PASSWORD NewNtOwfPassword, OldNtOwfPassword; + LM_OWF_PASSWORD NewLmOwfPassword, OldLmOwfPassword; + BOOLEAN StoredLmPasswordNonNull; + BOOLEAN StoredNtPasswordPresent; + BOOLEAN StoredNtPasswordNonNull; + BOOLEAN AccountLockedOut; + BOOLEAN V1aFixedRetrieved = FALSE; + BOOLEAN V1aFixedModified = FALSE; + ULONG ObjectRid; + UNICODE_STRING AccountName; + ULONG UserRid; + SAMP_V1_0A_FIXED_LENGTH_USER V1aFixed; + + SAMTRACE("SamrChangePasswordUser"); + + + RtlInitUnicodeString( + &AccountName, + NULL + ); + + // + // Grab the lock + // + + NtStatus = SampAcquireWriteLock(); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + // + // Get the current time + // + + NtStatus = NtQuerySystemTime( &TimeNow ); + if (!NT_SUCCESS(NtStatus)) { + IgnoreStatus = SampReleaseWriteLock( FALSE ); + return(NtStatus); + } + + + // + // Validate type of, and access to object. + // + + AccountContext = (PSAMP_OBJECT)UserHandle; + ObjectRid = AccountContext->TypeBody.User.Rid; + NtStatus = SampLookupContext( + AccountContext, + USER_CHANGE_PASSWORD, + SampUserObjectType, // ExpectedType + &FoundType + ); + if (!NT_SUCCESS(NtStatus)) { + IgnoreStatus = SampReleaseWriteLock( FALSE ); + return(NtStatus); + } + + // + // Auditing information + // + + + NtStatus = SampGetUnicodeStringAttribute( AccountContext, + SAMP_USER_ACCOUNT_NAME, + TRUE, // make a copy + &AccountName + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Auditing information + // + + UserRid = AccountContext->TypeBody.User.Rid; + + // + // Get a pointer to the domain object + // + + Domain = &SampDefinedDomains[ AccountContext->DomainIndex ]; + + + // + // Read the old OWF passwords from disk + // + + NtStatus = SampRetrieveUserPasswords( + AccountContext, + &StoredLmOwfPassword, + &StoredLmPasswordNonNull, + &StoredNtOwfPassword, + &StoredNtPasswordPresent, + &StoredNtPasswordNonNull + ); + + // + // Check the password can be changed at this time + // + + if (NT_SUCCESS(NtStatus)) { + + // + // Only do the check if one of the passwords is non-null. + // A Null password can always be changed. + // + + if (StoredNtPasswordNonNull || StoredLmPasswordNonNull) { + + + NtStatus = SampRetrieveUserV1aFixed( + AccountContext, + &V1aFixed + ); + + if (NT_SUCCESS(NtStatus)) { + // + // If the min password age is non zero, check it here + // + if (Domain->UnmodifiedFixed.MinPasswordAge.QuadPart != SampHasNeverTime.QuadPart) { + + LARGE_INTEGER PasswordCanChange = SampAddDeltaTime( + V1aFixed.PasswordLastSet, + Domain->UnmodifiedFixed.MinPasswordAge); + + V1aFixedRetrieved = TRUE; + + if (TimeNow.QuadPart < PasswordCanChange.QuadPart) { + NtStatus = STATUS_ACCOUNT_RESTRICTION; + } + } + + } + } + } + + + + + + // + // Macro that defines whether the old password passed in is complex + // + +#define PassedComplex() (NtPresent && !LmPresent) + + // + // Macro that defines whether the stored passsword is complex + // + +#define StoredComplex() (StoredNtPasswordPresent && \ + StoredNtPasswordNonNull && \ + !StoredLmPasswordNonNull) + + // + // Check that the complexity of the old password passed in matches + // the complexity of the one stored. If it doesn't then the old + // passwords don't match and we might as well give up now. + // + + if (NT_SUCCESS(NtStatus)) { + + if ( (StoredComplex() != 0) != (PassedComplex() != 0) ) { + + NtStatus = STATUS_WRONG_PASSWORD; + } + } + + + if (NT_SUCCESS(NtStatus)) { + + if (LmPresent) { + + // + // Decrypt the doubly-encrypted LM passwords sent to us + // + + NtStatus = RtlDecryptLmOwfPwdWithLmOwfPwd( + NewLmEncryptedWithOldLm, + &StoredLmOwfPassword, + &NewLmOwfPassword + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = RtlDecryptLmOwfPwdWithLmOwfPwd( + OldLmEncryptedWithNewLm, + &NewLmOwfPassword, + &OldLmOwfPassword + ); + } + } + } + + // + // Decrypt the doubly-encrypted NT passwords sent to us + // + + if (NT_SUCCESS(NtStatus)) { + + if (NtPresent) { + + NtStatus = RtlDecryptNtOwfPwdWithNtOwfPwd( + NewNtEncryptedWithOldNt, + &StoredNtOwfPassword, + &NewNtOwfPassword + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = RtlDecryptNtOwfPwdWithNtOwfPwd( + OldNtEncryptedWithNewNt, + &NewNtOwfPassword, + &OldNtOwfPassword + ); + } + } + } + + + + + // + // Authenticate the password change operation based on what + // we have stored and what was passed. + // + + if (NT_SUCCESS(NtStatus)) { + + if (!NtPresent) { + + // + // Called from a down-level machine (no NT password passed) + // + + if (!LmPresent) { + + // + // No NT password passed, no LM password either. + // They're out of luck + // + + NtStatus = STATUS_INVALID_PARAMETER_MIX; + + } else { + + // + // LM data only passed. Use LM data for authentication + // + + if (!RtlEqualLmOwfPassword(&OldLmOwfPassword, &StoredLmOwfPassword)) { + + // + // Old LM passwords didn't match + // + + NtStatus = STATUS_WRONG_PASSWORD; + + } else { + + // + // The operation was authenticated based on the LM data + // + // We have NtPresent = FALSE, LM Present = TRUE + // + // NewLmOwfPassword will be stored. + // No NT password will be stored. + // + } + } + + + } else { + + // + // NtPresent = TRUE, we were passed an NT password + // The client is an NT-level machine (or higher !) + // + + if (!LmPresent) { + + // + // No LM version of old password - the old password is complex + // + // Use NT data for authentication + // + + if (!RtlEqualNtOwfPassword(&OldNtOwfPassword, &StoredNtOwfPassword)) { + + // + // Old NT passwords didn't match + // + + NtStatus = STATUS_WRONG_PASSWORD; + + } else { + + // + // Authentication was successful. + // We need cross encrypted version of the new LM password + // + + if (!LmCrossEncryptionPresent) { + + NtStatus = STATUS_LM_CROSS_ENCRYPTION_REQUIRED; + + } else { + + // + // Calculate the new LM Owf Password + // + + ASSERT(NT_OWF_PASSWORD_LENGTH == LM_OWF_PASSWORD_LENGTH); + + NtStatus = RtlDecryptLmOwfPwdWithLmOwfPwd( + NewLmEncryptedWithNewNt, + (PLM_OWF_PASSWORD)&NewNtOwfPassword, + &NewLmOwfPassword + ); + } + + if (NT_SUCCESS(NtStatus)) { + + LmPresent = TRUE; + + // + // The operation was authenticated based on NT data + // The new LM Password was requested and + // successfully obtained using cross-encryption. + // + // We have NtPresent = TRUE, LM Present = TRUE + // + // NewLmOwfPassword will be stored. + // NewNtOwfPassword will be stored. + // + } + + } + + } else { + + // + // NtPresent == TRUE, LmPresent == TRUE + // + // The old password passed is simple (both LM and NT versions) + // + // Authenticate using both LM and NT data + // + + if (!RtlEqualLmOwfPassword(&OldLmOwfPassword, &StoredLmOwfPassword)) { + + // + // Old LM passwords didn't match + // + + NtStatus = STATUS_WRONG_PASSWORD; + + } else { + + // + // Old LM passwords matched + // + // Do NT authentication if we have a stored NT password + // or the stored LM password is NULL. + // + // (NO stored NT and Stored LM = NULL -> stored pwd=NULL + // We must compare passed old NT Owf against + // NULL NT Owf to ensure user didn't specify complex + // old NT password instead of NULL password) + // + // (StoredNtOwfPassword is already initialized to + // the NullNtOwf if no NT password stored) + // + + if (StoredNtPasswordPresent || !StoredLmPasswordNonNull) { + + if (!RtlEqualNtOwfPassword(&OldNtOwfPassword, + &StoredNtOwfPassword)) { + // + // Old NT passwords didn't match + // + + NtStatus = STATUS_WRONG_PASSWORD; + + } else { + + // + // The operation was authenticated based on + // both LM and NT data. + // + // We have NtPresent = TRUE, LM Present = TRUE + // + // NewLmOwfPassword will be stored. + // NewNtOwfPassword will be stored. + // + + } + + } else { + + // + // The LM authentication was sufficient since + // we have no stored NT password + // + // Go get the new NT password using cross encryption + // + + if (!NtCrossEncryptionPresent) { + + NtStatus = STATUS_NT_CROSS_ENCRYPTION_REQUIRED; + + } else { + + // + // Calculate the new NT Owf Password + // + + ASSERT(NT_OWF_PASSWORD_LENGTH == LM_OWF_PASSWORD_LENGTH); + + NtStatus = RtlDecryptNtOwfPwdWithNtOwfPwd( + NewNtEncryptedWithNewLm, + (PNT_OWF_PASSWORD)&NewLmOwfPassword, + &NewNtOwfPassword + ); + } + + if (NT_SUCCESS(NtStatus)) { + + // + // The operation was authenticated based on LM data + // The new NT Password was requested and + // successfully obtained using cross-encryption. + // + // We have NtPresent = TRUE, LM Present = TRUE + // + // NewLmOwfPassword will be stored. + // NewNtOwfPassword will be stored. + // + } + } + } + } + } + } + + + + // + // We now have a NewLmOwfPassword. + // If NtPresent = TRUE, we also have a NewNtOwfPassword + // + + // + // Write the new passwords to disk + // + + if (NT_SUCCESS(NtStatus)) { + + // + // We should always have a LM password to store. + // + + ASSERT(LmPresent); + + NtStatus = SampStoreUserPasswords( + AccountContext, + &NewLmOwfPassword, + TRUE, + &NewNtOwfPassword, + NtPresent, + TRUE + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // We know the password is not expired. + // + + NtStatus = SampStorePasswordExpired( + AccountContext, + FALSE + ); + } + } + + + + // + // if we have a bad password, then increment the bad password + // count and check to see if the account should be locked. + // + + if (NtStatus == STATUS_WRONG_PASSWORD) { + + // + // Get the V1aFixed so we can update the bad password count + // + + + TmpStatus = STATUS_SUCCESS; + if (!V1aFixedRetrieved) { + TmpStatus = SampRetrieveUserV1aFixed( + AccountContext, + &V1aFixed + ); + } + + if (!NT_SUCCESS(TmpStatus)) { + + // + // If we can't update the V1aFixed, then return this + // error so that the user doesn't find out the password + // was not correct. + // + + NtStatus = TmpStatus; + + } else { + + + // + // Increment BadPasswordCount (might lockout account) + // + + + AccountLockedOut = SampIncrementBadPasswordCount( + AccountContext, + &V1aFixed + ); + + V1aFixedModified = TRUE; + + + } + } + + if (V1aFixedModified) { + TmpStatus = SampReplaceUserV1aFixed( + AccountContext, + &V1aFixed + ); + if (!NT_SUCCESS(TmpStatus)) { + NtStatus = TmpStatus; + } + } + + // + // Dereference the account context + // + + if (NT_SUCCESS(NtStatus) || (NtStatus == STATUS_WRONG_PASSWORD)) { + + + + // + // De-reference the object, write out any change to current xaction. + // + + TmpStatus = SampDeReferenceContext( AccountContext, TRUE ); + + // + // retain previous error/success value unless we have + // an over-riding error from our dereference. + // + + if (!NT_SUCCESS(TmpStatus)) { + NtStatus = TmpStatus; + } + + } else { + + // + // De-reference the object, ignore changes + // + + IgnoreStatus = SampDeReferenceContext( AccountContext, FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + } + + // + // Commit changes to disk. + // + + if ( NT_SUCCESS(NtStatus) || NtStatus == STATUS_WRONG_PASSWORD) { + + TmpStatus = SampCommitAndRetainWriteLock(); + + // + // retain previous error/success value unless we have + // an over-riding error from our dereference. + // + + if (!NT_SUCCESS(TmpStatus)) { + NtStatus = TmpStatus; + } + + if ( NT_SUCCESS(TmpStatus) ) { + + SampNotifyNetlogonOfDelta( + SecurityDbChangePassword, + SecurityDbObjectSamUser, + ObjectRid, + (PUNICODE_STRING) NULL, + (DWORD) FALSE, // Don't Replicate immediately + NULL // Delta data + ); + } + } + + if (SampDoAccountAuditing(AccountContext->DomainIndex)) { + + LsaIAuditSamEvent( NtStatus, + SE_AUDITID_USER_PWD_CHANGED, // AuditId + Domain->Sid, // Domain SID + NULL, // Member Rid (not used) + NULL, // Member Sid (not used) + &AccountName, // Account Name + &Domain->ExternalName, // Domain + &UserRid, // Account Rid + NULL // Privileges used + ); + + } + + + // + // Release the write lock + // + + TmpStatus = SampReleaseWriteLock( FALSE ); + ASSERT(NT_SUCCESS(TmpStatus)); + + if (NT_SUCCESS(NtStatus)) { + + (void) SampPasswordChangeNotify( + &AccountName, + UserRid, + NULL + ); + + } else { + + // + // Sleep for three seconds to prevent dictionary attacks. + // + + Sleep( 3000 ); + + } + + SampFreeUnicodeString( &AccountName ); + + return(NtStatus); +} + + + + + +NTSTATUS +SampDecryptPasswordWithLmOwfPassword( + IN PSAMPR_ENCRYPTED_USER_PASSWORD EncryptedPassword, + IN PLM_OWF_PASSWORD StoredPassword, + IN BOOLEAN UnicodePasswords, + OUT PUNICODE_STRING ClearNtPassword + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + +--*/ +{ + return( SampDecryptPasswordWithKey( + EncryptedPassword, + (PUCHAR) StoredPassword, + LM_OWF_PASSWORD_LENGTH, + UnicodePasswords, + ClearNtPassword + ) ); +} + + +NTSTATUS +SampDecryptPasswordWithNtOwfPassword( + IN PSAMPR_ENCRYPTED_USER_PASSWORD EncryptedPassword, + IN PNT_OWF_PASSWORD StoredPassword, + IN BOOLEAN UnicodePasswords, + OUT PUNICODE_STRING ClearNtPassword + ) +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + +--*/ +{ + // + // The code is the same as for LM owf password. + // + + return(SampDecryptPasswordWithKey( + EncryptedPassword, + (PUCHAR) StoredPassword, + NT_OWF_PASSWORD_LENGTH, + UnicodePasswords, + ClearNtPassword + ) ); +} + +NTSTATUS +SampOpenUserInServer( + PUNICODE_STRING UserName, + BOOLEAN Unicode, + SAMPR_HANDLE * UserHandle + ) +/*++ + +Routine Description: + + Opens a user in the account domain. + +Arguments: + + UserName - an OEM or Unicode string of the user's name + + Unicode - Indicates whether UserName is OEM or Unicode + + UserHandle - Receives handle to the user, opened with SamOpenUser for + USER_CHANGE_PASSWORD access + + +Return Value: + +--*/ + +{ + NTSTATUS NtStatus; + SAM_HANDLE ServerHandle = NULL; + SAM_HANDLE DomainHandle = NULL; + SAMPR_ULONG_ARRAY UserId; + SAMPR_ULONG_ARRAY SidUse; + UNICODE_STRING UnicodeUserName; + ULONG DomainIndex; + + SAMTRACE("SampOpenUserInServer"); + + + UserId.Element = NULL; + SidUse.Element = NULL; + + // + // Get the unicode user name. + // + + if (Unicode) { + UnicodeUserName = *UserName; + } else { + NtStatus = RtlOemStringToUnicodeString( + &UnicodeUserName, + (POEM_STRING) UserName, + TRUE // allocate destination. + ); + + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + } + + + + NtStatus = SamrConnect( + NULL, + &ServerHandle, + SAM_SERVER_LOOKUP_DOMAIN + ); + if (!NT_SUCCESS(NtStatus)) { + goto Cleanup; + } + + NtStatus = SamrOpenDomain( + ServerHandle, + DOMAIN_LOOKUP | + DOMAIN_LIST_ACCOUNTS | + DOMAIN_READ_PASSWORD_PARAMETERS, + SampDefinedDomains[1].Sid, + &DomainHandle + ); + + if (!NT_SUCCESS(NtStatus)) { + goto Cleanup; + } + + // + // If cleartext password change is not allowed, we return the error code + // indicating that the rpc client should try using the old interfaces. + // + + DomainIndex = ((PSAMP_OBJECT) DomainHandle)->DomainIndex; + if (SampDefinedDomains[DomainIndex].UnmodifiedFixed.PasswordProperties & + DOMAIN_PASSWORD_NO_CLEAR_CHANGE) { + + NtStatus = RPC_NT_UNKNOWN_IF; + goto Cleanup; + } + + NtStatus = SamrLookupNamesInDomain( + DomainHandle, + 1, + (PRPC_UNICODE_STRING) &UnicodeUserName, + &UserId, + &SidUse + ); + + if (!NT_SUCCESS(NtStatus)) { + if (NtStatus == STATUS_NONE_MAPPED) { + NtStatus = STATUS_NO_SUCH_USER; + } + goto Cleanup; + } + + NtStatus = SamrOpenUser( + DomainHandle, + USER_CHANGE_PASSWORD, + UserId.Element[0], + UserHandle + ); + + if (!NT_SUCCESS(NtStatus)) { + goto Cleanup; + } + +Cleanup: + if (DomainHandle != NULL) { + SamrCloseHandle(&DomainHandle); + } + if (ServerHandle != NULL) { + SamrCloseHandle(&ServerHandle); + } + if (UserId.Element != NULL) { + MIDL_user_free(UserId.Element); + } + if (SidUse.Element != NULL) { + MIDL_user_free(SidUse.Element); + } + if (!Unicode && UnicodeUserName.Buffer != NULL) { + RtlFreeUnicodeString( &UnicodeUserName ); + } + + return(NtStatus); +} + + +NTSTATUS +SampChangePasswordUser2( + IN PUNICODE_STRING ServerName, + IN PUNICODE_STRING UserName, + IN BOOLEAN Unicode, + IN BOOLEAN NtPresent, + IN PSAMPR_ENCRYPTED_USER_PASSWORD NewEncryptedWithOldNt, + IN PENCRYPTED_NT_OWF_PASSWORD OldNtOwfEncryptedWithNewNt, + IN BOOLEAN LmPresent, + IN PSAMPR_ENCRYPTED_USER_PASSWORD NewEncryptedWithOldLm, + IN BOOLEAN NtKeyUsed, + IN PENCRYPTED_LM_OWF_PASSWORD OldLmOwfEncryptedWithNewLmOrNt + ) + + +/*++ + +Routine Description: + + This service sets the password to NewPassword only if OldPassword + matches the current user password for this user and the NewPassword + is not the same as the domain password parameter PasswordHistoryLength + passwords. This call allows users to change their own password if + they have access USER_CHANGE_PASSWORD. Password update restrictions + apply. + + +Parameters: + + ServerName - Name of the machine this SAM resides on. Ignored by this + routine, may be UNICODE or OEM string depending on Unicode parameter. + + UserName - User Name of account to change password on, may be UNICODE or + OEM depending on Unicode parameter. + + Unicode - Indicated whether the strings passed in are Unicode or OEM + strings. + + NtPresent - Are the Nt encrypted passwords present. + + NewEncryptedWithOldNt - The new cleartext password encrypted with the old + NT OWF password. Dependinf on the Unicode parameter, the clear text + password may be Unicode or OEM. + + OldNtOwfEncryptedWithNewNt - Old NT OWF password encrypted with the new + NT OWF password. + + LmPresent - are the Lm encrypted passwords present. + + NewEncryptedWithOldLm - Contains new cleartext password (OEM or Unicode) + encrypted with the old LM OWF password + + NtKeyUsed - Indicates whether the LM or NT OWF key was used to encrypt + the OldLmOwfEncryptedWithNewlmOrNt parameter. + + OldLmOwfEncryptedWithNewlmOrNt - The old LM OWF password encrypted + with either the new LM OWF password or NT OWF password, depending + on the NtKeyUsed parameter. + + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + STATUS_ILL_FORMED_PASSWORD - The new password is poorly formed, + e.g. contains characters that can't be entered from the + keyboard, etc. + + STATUS_PASSWORD_RESTRICTION - A restriction prevents the password + from being changed. This may be for a number of reasons, + including time restrictions on how often a password may be + changed or length restrictions on the provided password. + + This error might also be returned if the new password matched + a password in the recent history log for the account. + Security administrators indicate how many of the most + recently used passwords may not be re-used. These are kept + in the password recent history log. + + STATUS_WRONG_PASSWORD - OldPassword does not contain the user's + current password. + + STATUS_INVALID_DOMAIN_STATE - The domain server is not in the + correct state (disabled or enabled) to perform the requested + operation. The domain server must be enabled for this + operation + + STATUS_INVALID_DOMAIN_ROLE - The domain server is serving the + incorrect role (primary or backup) to perform the requested + operation. + + STATUS_CROSS_ENCRYPTION_REQUIRED - No NT password is stored, so the caller + must provide the OldNtEncryptedWithOldLm parameter. + +--*/ +{ + NTSTATUS NtStatus, TmpStatus, IgnoreStatus; + PSAMP_OBJECT AccountContext; + PSAMP_DEFINED_DOMAINS Domain; + SAMP_OBJECT_TYPE FoundType; + LARGE_INTEGER TimeNow; + LM_OWF_PASSWORD StoredLmOwfPassword; + NT_OWF_PASSWORD StoredNtOwfPassword; + NT_OWF_PASSWORD NewNtOwfPassword, OldNtOwfPassword; + LM_OWF_PASSWORD NewLmOwfPassword, OldLmOwfPassword; + UNICODE_STRING NewClearPassword; + BOOLEAN LmPasswordPresent; + BOOLEAN StoredLmPasswordNonNull; + BOOLEAN StoredNtPasswordPresent; + BOOLEAN StoredNtPasswordNonNull; + BOOLEAN AccountLockedOut; + BOOLEAN V1aFixedRetrieved = FALSE; + BOOLEAN V1aFixedModified = FALSE; + ULONG ObjectRid; + UNICODE_STRING AccountName; + ULONG UserRid; + SAMP_V1_0A_FIXED_LENGTH_USER V1aFixed; + SAMPR_HANDLE UserHandle = NULL; + + SAMTRACE("SampChangePasswordUser2"); + + // + // Initialize variables + // + + NtStatus = STATUS_SUCCESS; + NewClearPassword.Buffer = NULL; + AccountName.Buffer = NULL; + + // + // Validate some parameters. We require that one of the two passwords + // be present. + // + + if (!NtPresent && !LmPresent) { + + return(STATUS_INVALID_PARAMETER_MIX); + } + + // + // Open the user + // + + NtStatus = SampOpenUserInServer( + (PUNICODE_STRING) UserName, + Unicode, + &UserHandle + ); + + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + // + // Grab the lock + // + + NtStatus = SampAcquireWriteLock(); + if (!NT_SUCCESS(NtStatus)) { + SamrCloseHandle(&UserHandle); + return(NtStatus); + } + + + // + // Get the current time + // + + NtStatus = NtQuerySystemTime( &TimeNow ); + if (!NT_SUCCESS(NtStatus)) { + IgnoreStatus = SampReleaseWriteLock( FALSE ); + SamrCloseHandle(&UserHandle); + return(NtStatus); + } + + + // + // Validate type of, and access to object. + // + + AccountContext = (PSAMP_OBJECT)UserHandle; + ObjectRid = AccountContext->TypeBody.User.Rid; + NtStatus = SampLookupContext( + AccountContext, + USER_CHANGE_PASSWORD, + SampUserObjectType, // ExpectedType + &FoundType + ); + if (!NT_SUCCESS(NtStatus)) { + IgnoreStatus = SampReleaseWriteLock( FALSE ); + SamrCloseHandle(&UserHandle); + return(NtStatus); + } + + // + // Auditing information + // + + NtStatus = SampGetUnicodeStringAttribute( AccountContext, + SAMP_USER_ACCOUNT_NAME, + TRUE, // make a copy + &AccountName + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Auditing information + // + + UserRid = AccountContext->TypeBody.User.Rid; + + // + // Get a pointer to the domain object + // + + Domain = &SampDefinedDomains[ AccountContext->DomainIndex ]; + + + // + // Read the old OWF passwords from disk + // + + NtStatus = SampRetrieveUserPasswords( + AccountContext, + &StoredLmOwfPassword, + &StoredLmPasswordNonNull, + &StoredNtOwfPassword, + &StoredNtPasswordPresent, + &StoredNtPasswordNonNull + ); + + // + // Check the password can be changed at this time + // + + if (NT_SUCCESS(NtStatus)) { + + // + // Only do the check if one of the passwords is non-null. + // A Null password can always be changed. + // + + if (StoredNtPasswordNonNull || StoredLmPasswordNonNull) { + + + NtStatus = SampRetrieveUserV1aFixed( + AccountContext, + &V1aFixed + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // If the min password age is non zero, check it here + // + + if (Domain->UnmodifiedFixed.MinPasswordAge.QuadPart != SampHasNeverTime.QuadPart) { + + LARGE_INTEGER PasswordCanChange = SampAddDeltaTime( + V1aFixed.PasswordLastSet, + Domain->UnmodifiedFixed.MinPasswordAge); + + V1aFixedRetrieved = TRUE; + + if (TimeNow.QuadPart < PasswordCanChange.QuadPart) { + NtStatus = STATUS_ACCOUNT_RESTRICTION; + } + } + } + } + } + + + + // + // If we have old NtOwf passwords, use them + // Decrypt the doubly-encrypted NT passwords sent to us + // + + if (NT_SUCCESS(NtStatus)) { + + if (StoredNtPasswordPresent && NtPresent) { + + NtStatus = SampDecryptPasswordWithNtOwfPassword( + NewEncryptedWithOldNt, + &StoredNtOwfPassword, + Unicode, + &NewClearPassword + ); + + } else if (LmPresent) { + + // + // There was no stored NT password and NT passed, so our only + // hope now is that the stored LM password works. + // + + // + // Decrypt the new password encrypted with the old LM password + // + + NtStatus = SampDecryptPasswordWithLmOwfPassword( + NewEncryptedWithOldLm, + &StoredLmOwfPassword, + Unicode, + &NewClearPassword + ); + + + } else { + + NtStatus = STATUS_NT_CROSS_ENCRYPTION_REQUIRED; + + } + } + + + // + // We now have the cleartext new password. + // Compute the new LmOwf and NtOwf password + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampCalculateLmAndNtOwfPasswords( + &NewClearPassword, + &LmPasswordPresent, + &NewLmOwfPassword, + &NewNtOwfPassword + ); + + } + + // + // If we have both NT passwords, compute the old NT password, + // otherwise compute the old LM password + // + + if (NT_SUCCESS(NtStatus)) { + + if (StoredNtPasswordPresent && NtPresent) { + NtStatus = RtlDecryptNtOwfPwdWithNtOwfPwd( + OldNtOwfEncryptedWithNewNt, + &NewNtOwfPassword, + &OldNtOwfPassword + ); + + } + + if (LmPresent) { + + + // + // If the NT key was used to encrypt this, use the NT key + // to decrypt it. + // + + + if (NtKeyUsed) { + + ASSERT(LM_OWF_PASSWORD_LENGTH == NT_OWF_PASSWORD_LENGTH); + + NtStatus = RtlDecryptLmOwfPwdWithLmOwfPwd( + OldLmOwfEncryptedWithNewLmOrNt, + (PLM_OWF_PASSWORD) &NewNtOwfPassword, + &OldLmOwfPassword + ); + + + } else if (LmPasswordPresent) { + + NtStatus = RtlDecryptLmOwfPwdWithLmOwfPwd( + OldLmOwfEncryptedWithNewLmOrNt, + &NewLmOwfPassword, + &OldLmOwfPassword + ); + + + } else { + NtStatus = STATUS_NT_CROSS_ENCRYPTION_REQUIRED; + } + + } + + } + + + // + // Authenticate the password change operation based on what + // we have stored and what was passed. We authenticate whatever + // passwords were sent . + // + + if (NT_SUCCESS(NtStatus)) { + + if (NtPresent && StoredNtPasswordPresent) { + + // + // NtPresent = TRUE, we were passed an NT password + // + + if (!RtlEqualNtOwfPassword(&OldNtOwfPassword, &StoredNtOwfPassword)) { + + // + // Old NT passwords didn't match + // + + NtStatus = STATUS_WRONG_PASSWORD; + + } + } else if (LmPresent) { + + // + // LM data passed. Use LM data for authentication + // + + if (!RtlEqualLmOwfPassword(&OldLmOwfPassword, &StoredLmOwfPassword)) { + + // + // Old LM passwords didn't match + // + + NtStatus = STATUS_WRONG_PASSWORD; + + } + + } else { + NtStatus = STATUS_NT_CROSS_ENCRYPTION_REQUIRED; + } + + } + + // + // Now we should check password restrictions. + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampCheckPasswordRestrictions( + UserHandle, + &NewClearPassword + ); + + } + + + // + // We now have a NewLmOwfPassword and a NewNtOwfPassword. + // + + // + // Write the new passwords to disk + // + + if (NT_SUCCESS(NtStatus)) { + + // + // We should always have an LM and an NT password to store. + // + + + NtStatus = SampStoreUserPasswords( + AccountContext, + &NewLmOwfPassword, + LmPasswordPresent, + &NewNtOwfPassword, + TRUE, + TRUE + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // We know the password is not expired. + // + + NtStatus = SampStorePasswordExpired( + AccountContext, + FALSE + ); + } + } + + + + // + // if we have a bad password, then increment the bad password + // count and check to see if the account should be locked. + // + + if (NtStatus == STATUS_WRONG_PASSWORD) { + + // + // Get the V1aFixed so we can update the bad password count + // + + + TmpStatus = STATUS_SUCCESS; + if (!V1aFixedRetrieved) { + TmpStatus = SampRetrieveUserV1aFixed( + AccountContext, + &V1aFixed + ); + } + + if (!NT_SUCCESS(TmpStatus)) { + + // + // If we can't update the V1aFixed, then return this + // error so that the user doesn't find out the password + // was not correct. + // + + NtStatus = TmpStatus; + + } else { + + + // + // Increment BadPasswordCount (might lockout account) + // + + + AccountLockedOut = SampIncrementBadPasswordCount( + AccountContext, + &V1aFixed + ); + + V1aFixedModified = TRUE; + + + } + } + + if (V1aFixedModified) { + TmpStatus = SampReplaceUserV1aFixed( + AccountContext, + &V1aFixed + ); + if (!NT_SUCCESS(TmpStatus)) { + NtStatus = TmpStatus; + } + } + + // + // Dereference the account context + // + + if (NT_SUCCESS(NtStatus) || (NtStatus == STATUS_WRONG_PASSWORD)) { + + + + // + // De-reference the object, write out any change to current xaction. + // + + TmpStatus = SampDeReferenceContext( AccountContext, TRUE ); + + // + // retain previous error/success value unless we have + // an over-riding error from our dereference. + // + + if (!NT_SUCCESS(TmpStatus)) { + NtStatus = TmpStatus; + } + + } else { + + // + // De-reference the object, ignore changes + // + + IgnoreStatus = SampDeReferenceContext( AccountContext, FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + } + + // + // Commit changes to disk. + // + + if ( NT_SUCCESS(NtStatus) || NtStatus == STATUS_WRONG_PASSWORD) { + + TmpStatus = SampCommitAndRetainWriteLock(); + + // + // retain previous error/success value unless we have + // an over-riding error from our dereference. + // + + if (!NT_SUCCESS(TmpStatus)) { + NtStatus = TmpStatus; + } + + if ( NT_SUCCESS(TmpStatus) ) { + + SampNotifyNetlogonOfDelta( + SecurityDbChangePassword, + SecurityDbObjectSamUser, + ObjectRid, + (PUNICODE_STRING) NULL, + (DWORD) FALSE, // Don't Replicate immediately + NULL // Delta data + ); + } + } + + if (SampDoAccountAuditing(AccountContext->DomainIndex)) { + + LsaIAuditSamEvent( NtStatus, + SE_AUDITID_USER_PWD_CHANGED, // AuditId + Domain->Sid, // Domain SID + NULL, // Member Rid (not used) + NULL, // Member Sid (not used) + &AccountName, // Account Name + &Domain->ExternalName, // Domain + &UserRid, // Account Rid + NULL // Privileges used + ); + + } + + + + // + // Release the write lock + // + + TmpStatus = SampReleaseWriteLock( FALSE ); + ASSERT(NT_SUCCESS(TmpStatus)); + + SamrCloseHandle(&UserHandle); + + // + // Notify any notification packages that a password has changed. + // + + if (NT_SUCCESS(NtStatus)) { + + IgnoreStatus = SampPasswordChangeNotify( + &AccountName, + UserRid, + &NewClearPassword + ); + + } else { + + // + // Sleep for three seconds to prevent dictionary attacks. + // + + Sleep( 3000 ); + } + + if (NewClearPassword.Buffer != NULL) { + + RtlZeroMemory( + NewClearPassword.Buffer, + NewClearPassword.Length + ); + + } + + if ( Unicode ) { + + SampFreeUnicodeString( &NewClearPassword ); + } else { + + RtlFreeUnicodeString( &NewClearPassword ); + } + + SampFreeUnicodeString( &AccountName ); + + return(NtStatus); +} + + +NTSTATUS +SamrOemChangePasswordUser2( + IN handle_t BindingHandle, + IN PRPC_STRING ServerName, + IN PRPC_STRING UserName, + IN PSAMPR_ENCRYPTED_USER_PASSWORD NewEncryptedWithOldLm, + IN PENCRYPTED_LM_OWF_PASSWORD OldLmOwfEncryptedWithNewLm + ) +/*++ + +Routine Description: + + Server side stub for Unicode password change. + See SampChangePasswordUser2 for details + +Arguments: + + +Return Value: + +--*/ +{ + return(SampChangePasswordUser2( + (PUNICODE_STRING) ServerName, + (PUNICODE_STRING) UserName, + FALSE, // not unicode + FALSE, // NT not present + NULL, // new NT password + NULL, // old NT password + TRUE, // LM present + NewEncryptedWithOldLm, + FALSE, // NT key not used + OldLmOwfEncryptedWithNewLm + ) ); + + + +} + + + + + +NTSTATUS +SamrUnicodeChangePasswordUser2( + IN handle_t BindingHandle, + IN PRPC_UNICODE_STRING ServerName, + IN PRPC_UNICODE_STRING UserName, + IN PSAMPR_ENCRYPTED_USER_PASSWORD NewEncryptedWithOldNt, + IN PENCRYPTED_NT_OWF_PASSWORD OldNtOwfEncryptedWithNewNt, + IN BOOLEAN LmPresent, + IN PSAMPR_ENCRYPTED_USER_PASSWORD NewEncryptedWithOldLm, + IN PENCRYPTED_LM_OWF_PASSWORD OldLmOwfEncryptedWithNewNt + ) +/*++ + +Routine Description: + + Server side stub for Unicode password change. + See SampChangePasswordUser2 for details + +Arguments: + + +Return Value: + +--*/ + +{ + return(SampChangePasswordUser2( + (PUNICODE_STRING) ServerName, + (PUNICODE_STRING) UserName, + TRUE, // unicode + TRUE, // NT present + NewEncryptedWithOldNt, + OldNtOwfEncryptedWithNewNt, + LmPresent, + NewEncryptedWithOldLm, + TRUE, // NT key used + OldLmOwfEncryptedWithNewNt + ) ); +} + + + +NTSTATUS +SamrGetGroupsForUser( + IN SAMPR_HANDLE UserHandle, + OUT PSAMPR_GET_GROUPS_BUFFER *Groups + ) + + +/*++ + +Routine Description: + + This service returns the list of groups that a user is a member of. + It returns a structure for each group that includes the relative ID + of the group, and the attributes of the group that are assigned to + the user. + + This service requires USER_LIST_GROUPS access to the user account + object. + + + + +Parameters: + + UserHandle - The handle of an opened user to operate on. + + Groups - Receives a pointer to a buffer containing a count of members + and a pointer to a second buffer containing an array of + GROUP_MEMBERSHIPs data structures. When this information is + no longer needed, these buffers must be freed using + SamFreeMemory(). + + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_INVALID_HANDLE - The handle passed is invalid. + + + +--*/ +{ + + NTSTATUS NtStatus; + NTSTATUS IgnoreStatus; + PSAMP_OBJECT AccountContext; + SAMP_OBJECT_TYPE FoundType; + + SAMTRACE("SamrGetGroupsForUser"); + + + // + // Make sure we understand what RPC is doing for (to) us. + // + + ASSERT (Groups != NULL); + + if ((*Groups) != NULL) { + return(STATUS_INVALID_PARAMETER); + } + + + + // + // Allocate the first of the return buffers + // + + (*Groups) = MIDL_user_allocate( sizeof(SAMPR_GET_GROUPS_BUFFER) ); + + if ( (*Groups) == NULL) { + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + } + + + + + SampAcquireReadLock(); + + + // + // Validate type of, and access to object. + // + + AccountContext = (PSAMP_OBJECT)UserHandle; + NtStatus = SampLookupContext( + AccountContext, + USER_LIST_GROUPS, + SampUserObjectType, // ExpectedType + &FoundType + ); + + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampRetrieveUserMembership( + AccountContext, + TRUE, // Make copy + &(*Groups)->MembershipCount, + &(*Groups)->Groups + ); + + // + // De-reference the object, discarding changes + // + + IgnoreStatus = SampDeReferenceContext( AccountContext, FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + // + // Free the read lock + // + + SampReleaseReadLock(); + + + if (!NT_SUCCESS(NtStatus)) { + + (*Groups)->MembershipCount = 0; + + MIDL_user_free( (*Groups) ); + (*Groups) = NULL; + } + + return( NtStatus ); +} + + + +NTSTATUS +SamrGetUserDomainPasswordInformation( + IN SAMPR_HANDLE UserHandle, + OUT PUSER_DOMAIN_PASSWORD_INFORMATION PasswordInformation + ) + + +/*++ + +Routine Description: + + Takes a user handle, finds the domain for that user, and returns + password information for the domain. This is so the client\wrappers.c + can get the information to verify the user's password before it is + OWF'd. + + +Parameters: + + UserHandle - The handle of an opened user to operate on. + + PasswordInformation - Receives information about password restrictions + for the user's domain. + + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + Other errors may be returned from SampLookupContext() if the handle + is invalid or does not indicate proper access to the domain's password + inforamtion. + +--*/ +{ + SAMP_OBJECT_TYPE FoundType; + NTSTATUS NtStatus; + NTSTATUS IgnoreStatus; + PSAMP_OBJECT AccountContext; + PSAMP_DEFINED_DOMAINS Domain; + SAMP_V1_0A_FIXED_LENGTH_USER V1aFixed; + + SAMTRACE("SamrGetUserDomainPasswordInformation"); + + SampAcquireReadLock(); + + AccountContext = (PSAMP_OBJECT)UserHandle; + + NtStatus = SampLookupContext( + AccountContext, + 0, + SampUserObjectType, // ExpectedType + &FoundType + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // When the user was opened, we checked to see if the domain handle + // allowed access to the domain password information. Check that here. + // + + if ( !( AccountContext->TypeBody.User.DomainPasswordInformationAccessible ) ) { + + NtStatus = STATUS_ACCESS_DENIED; + + } else { + + Domain = &SampDefinedDomains[ AccountContext->DomainIndex ]; + + // + // If the user account is a machine account, + // then restrictions are generally not enforced. + // This is so that simple initial passwords can be + // established. IT IS EXPECTED THAT COMPLEX PASSWORDS, + // WHICH MEET THE MOST STRINGENT RESTRICTIONS, WILL BE + // AUTOMATICALLY ESTABLISHED AND MAINTAINED ONCE THE MACHINE + // JOINS THE DOMAIN. It is the UI's responsibility to + // maintain this level of complexity. + // + + + NtStatus = SampRetrieveUserV1aFixed( + AccountContext, + &V1aFixed + ); + + if (NT_SUCCESS(NtStatus)) { + if ( (V1aFixed.UserAccountControl & + (USER_WORKSTATION_TRUST_ACCOUNT | USER_SERVER_TRUST_ACCOUNT)) + != 0 ) { + + PasswordInformation->MinPasswordLength = 0; + PasswordInformation->PasswordProperties = 0; + } else { + + PasswordInformation->MinPasswordLength = Domain->UnmodifiedFixed.MinPasswordLength; + PasswordInformation->PasswordProperties = Domain->UnmodifiedFixed.PasswordProperties; + } + } + } + + // + // De-reference the object, discarding changes + // + + IgnoreStatus = SampDeReferenceContext( AccountContext, FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + SampReleaseReadLock(); + + return( NtStatus ); +} + + + +NTSTATUS +SamrGetDomainPasswordInformation( + IN handle_t BindingHandle, + IN OPTIONAL PRPC_UNICODE_STRING ServerName, + OUT PUSER_DOMAIN_PASSWORD_INFORMATION PasswordInformation + ) + + +/*++ + +Routine Description: + + Takes a user handle, finds the domain for that user, and returns + password information for the domain. This is so the client\wrappers.c + can get the information to verify the user's password before it is + OWF'd. + + +Parameters: + + UserHandle - The handle of an opened user to operate on. + + PasswordInformation - Receives information about password restrictions + for the user's domain. + + +Return Values: + + STATUS_SUCCESS - The Service completed successfully. + + Other errors may be returned from SampLookupContext() if the handle + is invalid or does not indicate proper access to the domain's password + inforamtion. + +--*/ +{ + SAMP_OBJECT_TYPE FoundType; + NTSTATUS NtStatus; + NTSTATUS IgnoreStatus; + PSAMP_OBJECT AccountContext; + PSAMP_DEFINED_DOMAINS Domain; + SAMP_V1_0A_FIXED_LENGTH_USER V1aFixed; + SAMPR_HANDLE ServerHandle = NULL; + SAMPR_HANDLE DomainHandle = NULL; + + SAMTRACE("SamrGetDomainPasswordInformation"); + + // + // Connect to the server and open the account domain for + // DOMAIN_READ_PASSWORD_PARAMETERS access. + // + + NtStatus = SamrConnect( + NULL, + &ServerHandle, + SAM_SERVER_LOOKUP_DOMAIN + ); + + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + NtStatus = SamrOpenDomain( + ServerHandle, + DOMAIN_READ_PASSWORD_PARAMETERS, + SampDefinedDomains[1].Sid, + &DomainHandle + ); + + if (!NT_SUCCESS(NtStatus)) { + SamrCloseHandle(&ServerHandle); + return(NtStatus); + } + + + SampAcquireReadLock(); + + + // + // We want to look at the account domain, which is domains[1]. + // + + Domain = &SampDefinedDomains[1]; + + // + // Copy the password properites into the returned structure. + // + + PasswordInformation->MinPasswordLength = Domain->UnmodifiedFixed.MinPasswordLength; + PasswordInformation->PasswordProperties = Domain->UnmodifiedFixed.PasswordProperties; + + + SampReleaseReadLock(); + + SamrCloseHandle(&DomainHandle); + SamrCloseHandle(&ServerHandle); + + + return( NtStatus ); +} + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Services Private to this process // +// // +/////////////////////////////////////////////////////////////////////////////// + + +NTSTATUS +SamIAccountRestrictions( + IN SAM_HANDLE UserHandle, + IN PUNICODE_STRING LogonWorkStation, + IN PUNICODE_STRING WorkStations, + IN PLOGON_HOURS LogonHours, + OUT PLARGE_INTEGER LogoffTime, + OUT PLARGE_INTEGER KickoffTime + ) + +/*++ + +Routine Description: + + Validate a user's ability to logon at this time and at the workstation + being logged onto. + + +Arguments: + + UserHandle - The handle of an opened user to operate on. + + LogonWorkStation - The name of the workstation the logon is being + attempted at. + + WorkStations - The list of workstations the user may logon to. This + information comes from the user's account information. It must + be in API list format. + + LogonHours - The times the user may logon. This information comes + from the user's account information. + + LogoffTime - Receives the time at which the user should logoff the + system. + + KickoffTime - Receives the time at which the user should be kicked + off the system. + + +Return Value: + + + STATUS_SUCCESS - Logon is permitted. + + STATUS_INVALID_LOGON_HOURS - The user is not authorized to logon at + this time. + + STATUS_INVALID_WORKSTATION - The user is not authorized to logon to + the specified workstation. + + +--*/ +{ + +#define MILLISECONDS_PER_WEEK 7 * 24 * 60 * 60 * 1000 + + TIME_FIELDS CurrentTimeFields; + LARGE_INTEGER CurrentTime, CurrentUTCTime; + LARGE_INTEGER MillisecondsIntoWeekXUnitsPerWeek; + LARGE_INTEGER LargeUnitsIntoWeek; + LARGE_INTEGER Delta100Ns; + PSAMP_OBJECT AccountContext; + PSAMP_DEFINED_DOMAINS Domain; + SAMP_OBJECT_TYPE FoundType; + NTSTATUS NtStatus = STATUS_SUCCESS; + NTSTATUS IgnoreStatus; + ULONG CurrentMsIntoWeek; + ULONG LogoffMsIntoWeek; + ULONG DeltaMs; + ULONG MillisecondsPerUnit; + ULONG CurrentUnitsIntoWeek; + ULONG LogoffUnitsIntoWeek; + USHORT i; + TIME_ZONE_INFORMATION TimeZoneInformation; + DWORD TimeZoneId; + LARGE_INTEGER BiasIn100NsUnits; + LONG BiasInMinutes; + SAMP_V1_0A_FIXED_LENGTH_USER V1aFixed; + + SAMTRACE("SamIAccountRestrictions"); + + SampAcquireReadLock(); + + // + // Validate type of, and access to object. + // + + AccountContext = (PSAMP_OBJECT)UserHandle; + + NtStatus = SampLookupContext( + AccountContext, + 0L, + SampUserObjectType, // ExpectedType + &FoundType + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + NtStatus = SampRetrieveUserV1aFixed( + AccountContext, + &V1aFixed + ); + if (NT_SUCCESS(NtStatus)) { + + // + // Only check for users other than the builtin ADMIN + // + + if (V1aFixed.UserId != DOMAIN_USER_RID_ADMIN) { + + // + // Scan to make sure the workstation being logged into is in the + // list of valid workstations - or if the list of valid workstations + // is null, which means that all are valid. + // + + NtStatus = SampMatchworkstation( LogonWorkStation, WorkStations ); + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // Check to make sure that the current time is a valid time to logon + // in the LogonHours. + // + // We need to validate the time taking into account whether we are + // in daylight savings time or standard time. Thus, if the logon + // hours specify that we are able to logon between 9am and 5pm, + // this means 9am to 5pm standard time during the standard time + // period, and 9am to 5pm daylight savings time when in the + // daylight savings time. Since the logon hours stored by SAM are + // independent of daylight savings time, we need to add in the + // difference between standard time and daylight savings time to + // the current time before checking whether this time is a valid + // time to logon. Since this difference (or bias as it is called) + // is actually held in the form + // + // Standard time = Daylight savings time + Bias + // + // the Bias is a negative number. Thus we actually subtract the + // signed Bias from the Current Time. + + // + // First, get the Time Zone Information. + // + + TimeZoneId = GetTimeZoneInformation( + (LPTIME_ZONE_INFORMATION) &TimeZoneInformation + ); + + // + // Next, get the appropriate bias (signed integer in minutes) to subtract from + // the Universal Time Convention (UTC) time returned by NtQuerySystemTime + // to get the local time. The bias to be used depends whether we're + // in Daylight Savings time or Standard Time as indicated by the + // TimeZoneId parameter. + // + // local time = UTC time - bias in 100Ns units + // + + switch (TimeZoneId) { + + case TIME_ZONE_ID_UNKNOWN: + + // + // There is no differentiation between standard and + // daylight savings time. Proceed as for Standard Time + // + + BiasInMinutes = TimeZoneInformation.StandardBias; + break; + + case TIME_ZONE_ID_STANDARD: + + BiasInMinutes = TimeZoneInformation.StandardBias; + break; + + case TIME_ZONE_ID_DAYLIGHT: + + BiasInMinutes = TimeZoneInformation.DaylightBias; + break; + + default: + + // + // Something is wrong with the time zone information. Fail + // the logon request. + // + + NtStatus = STATUS_INVALID_LOGON_HOURS; + break; + } + + if (NT_SUCCESS(NtStatus)) { + + // + // Convert the Bias from minutes to 100ns units + // + + BiasIn100NsUnits.QuadPart = ((LONGLONG)BiasInMinutes) + * 60 * 10000000; + + // + // Get the UTC time in 100Ns units used by Windows Nt. This + // time is GMT. + // + + NtStatus = NtQuerySystemTime( &CurrentUTCTime ); + } + + if ( NT_SUCCESS( NtStatus ) ) { + + CurrentTime.QuadPart = CurrentUTCTime.QuadPart - + BiasIn100NsUnits.QuadPart; + + RtlTimeToTimeFields( &CurrentTime, &CurrentTimeFields ); + + CurrentMsIntoWeek = (((( CurrentTimeFields.Weekday * 24 ) + + CurrentTimeFields.Hour ) * 60 + + CurrentTimeFields.Minute ) * 60 + + CurrentTimeFields.Second ) * 1000 + + CurrentTimeFields.Milliseconds; + + MillisecondsIntoWeekXUnitsPerWeek.QuadPart = + ((LONGLONG)CurrentMsIntoWeek) * + ((LONGLONG)LogonHours->UnitsPerWeek); + + LargeUnitsIntoWeek = RtlExtendedLargeIntegerDivide( + MillisecondsIntoWeekXUnitsPerWeek, + MILLISECONDS_PER_WEEK, + (PULONG)NULL ); + + CurrentUnitsIntoWeek = LargeUnitsIntoWeek.LowPart; + + if ( !( LogonHours->LogonHours[ CurrentUnitsIntoWeek / 8] & + ( 0x01 << ( CurrentUnitsIntoWeek % 8 ) ) ) ) { + + NtStatus = STATUS_INVALID_LOGON_HOURS; + + } else { + + // + // Determine the next time that the user is NOT supposed to be logged + // in, and return that as LogoffTime. + // + + i = 0; + LogoffUnitsIntoWeek = CurrentUnitsIntoWeek; + + do { + + i++; + + LogoffUnitsIntoWeek = ( LogoffUnitsIntoWeek + 1 ) % LogonHours->UnitsPerWeek; + + } while ( ( i <= LogonHours->UnitsPerWeek ) && + ( LogonHours->LogonHours[ LogoffUnitsIntoWeek / 8 ] & + ( 0x01 << ( LogoffUnitsIntoWeek % 8 ) ) ) ); + + if ( i > LogonHours->UnitsPerWeek ) { + + // + // All times are allowed, so there's no logoff + // time. Return forever for both logofftime and + // kickofftime. + // + + LogoffTime->HighPart = 0x7FFFFFFF; + LogoffTime->LowPart = 0xFFFFFFFF; + + KickoffTime->HighPart = 0x7FFFFFFF; + KickoffTime->LowPart = 0xFFFFFFFF; + + } else { + + // + // LogoffUnitsIntoWeek points at which time unit the + // user is to log off. Calculate actual time from + // the unit, and return it. + // + // CurrentTimeFields already holds the current + // time for some time during this week; just adjust + // to the logoff time during this week and convert + // to time format. + // + + MillisecondsPerUnit = MILLISECONDS_PER_WEEK / LogonHours->UnitsPerWeek; + + LogoffMsIntoWeek = MillisecondsPerUnit * LogoffUnitsIntoWeek; + + if ( LogoffMsIntoWeek < CurrentMsIntoWeek ) { + + DeltaMs = MILLISECONDS_PER_WEEK - ( CurrentMsIntoWeek - LogoffMsIntoWeek ); + + } else { + + DeltaMs = LogoffMsIntoWeek - CurrentMsIntoWeek; + } + + Delta100Ns = RtlExtendedIntegerMultiply( + RtlConvertUlongToLargeInteger( DeltaMs ), + 10000 + ); + + LogoffTime->QuadPart = CurrentUTCTime.QuadPart + + Delta100Ns.QuadPart; + + // + // Subtract Domain->ForceLogoff from LogoffTime, and return + // that as KickoffTime. Note that Domain->ForceLogoff is a + // negative delta. If its magnitude is sufficiently large + // (in fact, larger than the difference between LogoffTime + // and the largest positive large integer), we'll get overflow + // resulting in a KickOffTime that is negative. In this + // case, reset the KickOffTime to this largest positive + // large integer (i.e. "never") value. + // + + Domain = &SampDefinedDomains[ AccountContext->DomainIndex ]; + + KickoffTime->QuadPart = LogoffTime->QuadPart - + Domain->UnmodifiedFixed.ForceLogoff.QuadPart; + + if (KickoffTime->QuadPart < 0) { + + KickoffTime->HighPart = 0x7FFFFFFF; + KickoffTime->LowPart = 0xFFFFFFFF; + } + } + } + } + } + + } else { + + // + // Never kick administrators off + // + + LogoffTime->HighPart = 0x7FFFFFFF; + LogoffTime->LowPart = 0xFFFFFFFF; + KickoffTime->HighPart = 0x7FFFFFFF; + KickoffTime->LowPart = 0xFFFFFFFF; + } + } + + // + // De-reference the object, discarding changes + // + + IgnoreStatus = SampDeReferenceContext( AccountContext, FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + SampReleaseReadLock(); + + return( NtStatus ); +} + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Services Private to this file // +// // +/////////////////////////////////////////////////////////////////////////////// + + + +NTSTATUS +SampReplaceUserV1aFixed( + IN PSAMP_OBJECT Context, + IN PSAMP_V1_0A_FIXED_LENGTH_USER V1aFixed + ) + +/*++ + +Routine Description: + + This service replaces the current V1 fixed length information related to + a specified User. + + The change is made to the in-memory object data only. + + +Arguments: + + Context - Points to the account context whose V1_FIXED information is + to be replaced. + + V1aFixed - Is a buffer containing the new V1_FIXED information. + + + +Return Value: + + + STATUS_SUCCESS - The information has been replaced. + + Other status values that may be returned are those returned + by: + + SampSetFixedAttributes() + + + +--*/ +{ + NTSTATUS NtStatus; + + SAMTRACE("SampReplaceUserV1aFixed"); + + NtStatus = SampSetFixedAttributes( + Context, + (PVOID)V1aFixed + ); + + return( NtStatus ); +} + + + +LARGE_INTEGER +SampGetPasswordMustChange( + IN ULONG UserAccountControl, + IN LARGE_INTEGER PasswordLastSet, + IN LARGE_INTEGER MaxPasswordAge + ) + +/*++ + +Routine Description: + + This routine returns the correct value to set the PasswordMustChange time + to depending on the time the password was last set, whether the password + expires on the account, and the maximum password age on the domain. + +Arguments: + + UserAccountControl - The UserAccountControl for the user. The + USER_DONT_EXPIRE_PASSWORD bit is set if the password doesn't expire + for this user. + + PasswordLastSet - Time when the password was last set for this user. + + MaxPasswordAge - Maximum password age for any password in the domain. + + +Return Value: + + Returns the time when the password for this user must change. + +--*/ +{ + LARGE_INTEGER PasswordMustChange; + + SAMTRACE("SampGetPasswordMustChange"); + + // + // If the password never expires for this user, + // return an infinitely large time. + // + + if ( UserAccountControl & USER_DONT_EXPIRE_PASSWORD ) { + + PasswordMustChange = SampWillNeverTime; + + // + // If the password for this account is flagged to expire immediately, + // return a zero time time. + // + // Don't return the current time here. The callers clock might be a + // little off from ours. + // + + } else if ( PasswordLastSet.QuadPart == SampHasNeverTime.QuadPart ) { + + PasswordMustChange = SampHasNeverTime; + + + // + // Otherwise compute the expiration time as the time the password was + // last set plus the maximum age. + // + + } else { + + PasswordMustChange = SampAddDeltaTime( + PasswordLastSet, + MaxPasswordAge); + + } + + return PasswordMustChange; +} + + + +NTSTATUS +SampComputePasswordExpired( + IN BOOLEAN PasswordExpired, + OUT PLARGE_INTEGER PasswordLastSet + ) + +/*++ + +Routine Description: + + This routine returns the correct value to set the PasswordLastSet time + to depending on whether the caller has requested the password to expire. + It does this by setting the PasswordLastSet time to be now (if it's + not expired) or to SampHasNeverTime (if it is expired). + +Arguments: + + PasswordExpired - TRUE if the password should be marked as expired. + + + +Return Value: + + STATUS_SUCCESS - the PasswordLastSet time has been set to indicate + whether or not the password is expired. + + Errors as returned by NtQuerySystemTime. + +--*/ +{ + NTSTATUS NtStatus; + + SAMTRACE("SampComputePasswordExpired"); + + // + // If immediate expiry is required - set this timestamp to the + // beginning of time. This will work if the domain enforces a + // maximum password age. We may have to add a separate flag to + // the database later if immediate expiry is required on a domain + // that doesn't enforce a maximum password age. + // + + if (PasswordExpired) { + + // + // Set password last changed at dawn of time + // + + *PasswordLastSet = SampHasNeverTime; + NtStatus = STATUS_SUCCESS; + + } else { + + // + // Set password last changed 'now' + // + + NtStatus = NtQuerySystemTime( PasswordLastSet ); + } + + return( NtStatus ); +} + + + +NTSTATUS +SampStorePasswordExpired( + IN PSAMP_OBJECT Context, + IN BOOLEAN PasswordExpired + ) + +/*++ + +Routine Description: + + This routine marks the current password as expired, or not expired. + It does this by setting the PasswordLastSet time to be now (if it's + not expired) or to SampHasNeverTime (if it is expired). + +Arguments: + + Context - Points to the user account context. + + PasswordExpired - TRUE if the password should be marked as expired. + +Return Value: + + STATUS_SUCCESS - the PasswordLastSet time has been set to indicate + whether or not the password is expired. + + Errors as returned by Samp{Retrieve|Replace}V1Fixed() + +--*/ +{ + NTSTATUS NtStatus; + SAMP_V1_0A_FIXED_LENGTH_USER V1aFixed; + + SAMTRACE("SampStorePasswordExpired"); + + // + // Get the V1aFixed info for the user + // + + NtStatus = SampRetrieveUserV1aFixed( + Context, + &V1aFixed + ); + + // + // Update the password-last-changed timestamp for the account + // + + if (NT_SUCCESS(NtStatus ) ) { + + NtStatus = SampComputePasswordExpired( + PasswordExpired, + &V1aFixed.PasswordLastSet ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampReplaceUserV1aFixed( + Context, + &V1aFixed + ); + } + } + + return( NtStatus ); +} + + + +NTSTATUS +SampStoreUserPasswords( + IN PSAMP_OBJECT Context, + IN PLM_OWF_PASSWORD LmOwfPassword, + IN BOOLEAN LmPasswordPresent, + IN PNT_OWF_PASSWORD NtOwfPassword, + IN BOOLEAN NtPasswordPresent, + IN BOOLEAN CheckHistory + ) + +/*++ + +Routine Description: + + This service updates the password for the specified user. + + This involves encrypting the one-way-functions of both LM and NT + passwords with a suitable index and writing them into the registry. + + This service checks the new password for legality including history + and UAS compatibilty checks - returns STATUS_PASSWORD_RESTRICTION if + any of these checks fail. + + The password-last-changed time is updated. + + THE CHANGE WILL BE ADDED TO THE CURRENT RXACT TRANSACTION. + + +Arguments: + + Context - Points to the user account context. + + LmOwfPassword - The one-way-function of the LM password. + + LmPasswordPresent - TRUE if the LmOwfPassword contains valid information. + + NtOwfPassword - The one-way-function of the NT password. + + NtPasswordPresent - TRUE if the NtOwfPassword contains valid information. + +Return Value: + + + STATUS_SUCCESS - The passwords have been updated. + + STATUS_PASSWORD_RESTRICTION - The new password is not valid for + for this account at this time. + + Other status values that may be returned are those returned + by: + + NtOpenKey() + RtlAddActionToRXact() + + + +--*/ +{ + NTSTATUS NtStatus; + ULONG ObjectRid = Context->TypeBody.User.Rid; + CRYPT_INDEX CryptIndex; + PSAMP_DEFINED_DOMAINS Domain; + UNICODE_STRING StringBuffer; + UNICODE_STRING NtOwfHistoryBuffer; + UNICODE_STRING LmOwfHistoryBuffer; + ENCRYPTED_LM_OWF_PASSWORD EncryptedLmOwfPassword; + ENCRYPTED_NT_OWF_PASSWORD EncryptedNtOwfPassword; + SAMP_V1_0A_FIXED_LENGTH_USER V1aFixed; + BOOLEAN NtPasswordNull, LmPasswordNull; + + SAMTRACE("SampStoreUserPasswords"); + + // + // Get the V1aFixed info for the user + // + + NtStatus = SampRetrieveUserV1aFixed( + Context, + &V1aFixed + ); + if ( !NT_SUCCESS( NtStatus ) ) { + return (NtStatus); + } + + // + // Get a pointer to the in-memory domain info + // + + Domain = &SampDefinedDomains[ Context->DomainIndex ]; + + + + + // + // Check for a LM Owf of a NULL password. + // + + if (LmPasswordPresent) { + LmPasswordNull = RtlEqualNtOwfPassword(LmOwfPassword, &SampNullLmOwfPassword); + } + + // + // Check for a NT Owf of a NULL password + // + + if (NtPasswordPresent) { + NtPasswordNull = RtlEqualNtOwfPassword(NtOwfPassword, &SampNullNtOwfPassword); + } + + + + // + // Check password against restrictions if this isn't a trusted client + // + + if (NT_SUCCESS(NtStatus) && !Context->TrustedClient) { + + // + // If we have neither an NT or LM password, check it's allowed + // + + if ( ((!LmPasswordPresent) || LmPasswordNull) && + ((!NtPasswordPresent) || NtPasswordNull) ) { + + if ( (!(V1aFixed.UserAccountControl & USER_PASSWORD_NOT_REQUIRED)) + && (Domain->UnmodifiedFixed.MinPasswordLength > 0) ) { + + NtStatus = STATUS_PASSWORD_RESTRICTION; + } + } + + + // + // If we have a complex NT password (no LM equivalent), check it's allowed + // + + if (NT_SUCCESS(NtStatus)) { + + if ((!LmPasswordPresent || LmPasswordNull) && + (NtPasswordPresent && !NtPasswordNull) ) { + + if (Domain->UnmodifiedFixed.UasCompatibilityRequired) { + + NtStatus = STATUS_PASSWORD_RESTRICTION; + } + } + } + } + + + + // + // Reencrypt both OWFs with the key for this user + // so they can be stored on disk + // + // Note we encrypt the NULL OWF if we do not have a + // a particular OWF. This is so we always have something + // to add to the password history. + // + + // + // We'll use the account rid as the encryption index + // + + ASSERT(sizeof(ObjectRid) == sizeof(CryptIndex)); + CryptIndex = ObjectRid; + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = RtlEncryptLmOwfPwdWithIndex( + LmPasswordPresent ? LmOwfPassword : + &SampNullLmOwfPassword, + &CryptIndex, + &EncryptedLmOwfPassword + ); + } + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = RtlEncryptNtOwfPwdWithIndex( + NtPasswordPresent ? NtOwfPassword : + &SampNullNtOwfPassword, + &CryptIndex, + &EncryptedNtOwfPassword + ); + } + + + + // + // Check password against password history IF client isn't trusted. + // If client is trusted, it's not the user changing a password but + // perhaps replication resetting the password from another controller, + // and the password may well be in the password history but we don't + // want to return error. + // + // Note we don't check NULL passwords against history + // + + NtOwfHistoryBuffer.Buffer = NULL; + NtOwfHistoryBuffer.MaximumLength = NtOwfHistoryBuffer.Length = 0; + + LmOwfHistoryBuffer.Buffer = NULL; + LmOwfHistoryBuffer.MaximumLength = LmOwfHistoryBuffer.Length = 0; + + + if (NT_SUCCESS(NtStatus) && !Context->TrustedClient) { + + // + // Always go get the existing password history. + // We'll use these history buffers when we save the new history + // + + NtStatus = SampGetUnicodeStringAttribute( + Context, + SAMP_USER_LM_PWD_HISTORY, + TRUE, // Make copy + &LmOwfHistoryBuffer + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampGetUnicodeStringAttribute( + Context, + SAMP_USER_NT_PWD_HISTORY, + TRUE, // Make copy + &NtOwfHistoryBuffer + ); + } + + + if (NT_SUCCESS(NtStatus) && LmPasswordPresent && !LmPasswordNull) { + + NtStatus = SampCheckPasswordHistory( + &EncryptedLmOwfPassword, + ENCRYPTED_LM_OWF_PASSWORD_LENGTH, + Domain->UnmodifiedFixed.PasswordHistoryLength, + SAMP_USER_LM_PWD_HISTORY, + Context, + CheckHistory, + &LmOwfHistoryBuffer + ); + } + + if (NT_SUCCESS(NtStatus) && NtPasswordPresent && !NtPasswordNull) { + + NtStatus = SampCheckPasswordHistory( + &EncryptedNtOwfPassword, + ENCRYPTED_NT_OWF_PASSWORD_LENGTH, + Domain->UnmodifiedFixed.PasswordHistoryLength, + SAMP_USER_NT_PWD_HISTORY, + Context, + CheckHistory, + &NtOwfHistoryBuffer + ); + } + } + + + + +// if (NT_SUCCESS(NtStatus ) ) { +// +// // +// // If the filter DLL was loaded, get its blessing of this +// // password +// // +// +// UNICODE_STRING AccountName, FullName; +// +// if (SampPasswordFilterDllRoutine != NULL) { +// +// +// UNICODE_STRING AccountName, +// FullName; +// +// // +// // Get the account name and full name +// // +// +// NtStatus = SampGetUnicodeStringAttribute( +// Context, +// SAMP_USER_ACCOUNT_NAME, +// TRUE, // Make copy +// &AccountName +// ); +// +// if (NT_SUCCESS(NtStatus)) { +// +// NtStatus = SampGetUnicodeStringAttribute( +// Context, +// SAMP_USER_FULL_NAME, +// TRUE, // Make copy +// &FullName +// ); +// +// if (NT_SUCCESS(NtStatus)) { +// +// // +// // now see what the filter dll thinks of this password +// // +// +// NtStatus = (SampPasswordFilterDllRoutine)( +// &AccountName, +// &FullName, +// LmOwfPassword, +// LmPasswordPresent, +// NtOwfPassword, +// NtPasswordPresent +// ); +// +// SampFreeUnicodeString(&FullName); +// } +// +// SampFreeUnicodeString(&AccountName); +// } +// } +// } + + + + if (NT_SUCCESS(NtStatus ) ) { + + // + // Write the encrypted LM OWF password into the database + // + + if (!LmPasswordPresent || LmPasswordNull) { + StringBuffer.Buffer = NULL; + StringBuffer.Length = 0; + } else { + StringBuffer.Buffer = (PWCHAR)&EncryptedLmOwfPassword; + StringBuffer.Length = ENCRYPTED_LM_OWF_PASSWORD_LENGTH; + } + StringBuffer.MaximumLength = StringBuffer.Length; + + + // + // Write the encrypted LM OWF password into the registry + // + + NtStatus = SampSetUnicodeStringAttribute( + Context, + SAMP_USER_DBCS_PWD, + &StringBuffer + ); + } + + + + + if (NT_SUCCESS(NtStatus ) ) { + + // + // Write the encrypted NT OWF password into the database + // + + if (!NtPasswordPresent) { + StringBuffer.Buffer = NULL; + StringBuffer.Length = 0; + } else { + StringBuffer.Buffer = (PWCHAR)&EncryptedNtOwfPassword; + StringBuffer.Length = ENCRYPTED_NT_OWF_PASSWORD_LENGTH; + } + StringBuffer.MaximumLength = StringBuffer.Length; + + + // + // Write the encrypted NT OWF password into the registry + // + + NtStatus = SampSetUnicodeStringAttribute( + Context, + SAMP_USER_UNICODE_PWD, + &StringBuffer + ); + } + + // + // Update the password history for this account. + // + // If both passwords are NULL then don't bother adding + // them to the history. Note that if either is non-NULL + // we add both. This is to avoid the weird case where a user + // changes password many times from a LM machine, then tries + // to change password from an NT machine and is told they + // cannot use the password they last set from NT (possibly + // many years ago.) + // + // Also, don't bother with the password history if the client is + // trusted. Trusted clients will set the history via SetPrivateData(). + // Besides, we didn't get the old history buffer in the trusted + // client case above. + // + + if ( (NT_SUCCESS(NtStatus)) && (!Context->TrustedClient) ) { + + if ((LmPasswordPresent && !LmPasswordNull) || + (NtPasswordPresent && !NtPasswordNull)) { + + NtStatus = SampAddPasswordHistory( + Context, + SAMP_USER_LM_PWD_HISTORY, + &LmOwfHistoryBuffer, + &EncryptedLmOwfPassword, + ENCRYPTED_LM_OWF_PASSWORD_LENGTH, + Domain->UnmodifiedFixed.PasswordHistoryLength + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampAddPasswordHistory( + Context, + SAMP_USER_NT_PWD_HISTORY, + &NtOwfHistoryBuffer, + &EncryptedNtOwfPassword, + ENCRYPTED_NT_OWF_PASSWORD_LENGTH, + Domain->UnmodifiedFixed.PasswordHistoryLength + ); + } + } + } + + // + // Clean up our history buffers + // + + if (NtOwfHistoryBuffer.Buffer != NULL ) { + MIDL_user_free(NtOwfHistoryBuffer.Buffer ); + } + if (LmOwfHistoryBuffer.Buffer != NULL ) { + MIDL_user_free(LmOwfHistoryBuffer.Buffer ); + } + + return(NtStatus ); +} + + + +NTSTATUS +SampRetrieveUserPasswords( + IN PSAMP_OBJECT Context, + OUT PLM_OWF_PASSWORD LmOwfPassword, + OUT PBOOLEAN LmPasswordNonNull, + OUT PNT_OWF_PASSWORD NtOwfPassword, + OUT PBOOLEAN NtPasswordPresent, + OUT PBOOLEAN NtPasswordNonNull + ) + +/*++ + +Routine Description: + + This service retrieves the stored OWF passwords for a user. + + +Arguments: + + Context - Points to the user account context. + + LmOwfPassword - The one-way-function of the LM password is returned here. + + LmPasswordNonNull - TRUE if the LmOwfPassword is not the well-known + OWF of a NULL password + + NtOwfPassword - The one-way-function of the NT password is returned here. + + NtPasswordPresent - TRUE if the NtOwfPassword contains valid information. + + +Return Value: + + + STATUS_SUCCESS - The passwords were retrieved successfully. + + Other status values that may be returned are those returned + by: + + NtOpenKey() + RtlAddActionToRXact() + + + +--*/ +{ + NTSTATUS NtStatus; + ULONG ObjectRid = Context->TypeBody.User.Rid; + UNICODE_STRING StringBuffer; + CRYPT_INDEX CryptIndex; + + SAMTRACE("SampRetrieveUserPasswords"); + + // + // The OWF passwords are encrypted with the account index in the registry + // Setup the key we'll use for decryption. + // + + ASSERT(sizeof(ObjectRid) == sizeof(CryptIndex)); + CryptIndex = ObjectRid; + + + + // + // Read the encrypted LM OWF password from the database + // + + NtStatus = SampGetUnicodeStringAttribute( + Context, + SAMP_USER_DBCS_PWD, + TRUE, // Make copy + &StringBuffer + ); + + if ( !NT_SUCCESS( NtStatus ) ) { + return (NtStatus); + } + + // + // Check it is in the expected form + // + + ASSERT( (StringBuffer.Length == 0) || + (StringBuffer.Length == ENCRYPTED_LM_OWF_PASSWORD_LENGTH)); + + // + // Determine if there is an LM password. + // + + *LmPasswordNonNull = (BOOLEAN)(StringBuffer.Length != 0); + + // + // Decrypt the encrypted LM Owf Password + // + + if (*LmPasswordNonNull) { + + NtStatus = RtlDecryptLmOwfPwdWithIndex( + (PENCRYPTED_LM_OWF_PASSWORD)StringBuffer.Buffer, + &CryptIndex, + LmOwfPassword + ); + } else { + + // + // Fill in the NULL password for caller convenience + // + + *LmOwfPassword = SampNullLmOwfPassword; + } + + + // + // Free up the returned string buffer + // + + SampFreeUnicodeString(&StringBuffer); + + + // + // Check if the decryption failed + // + + if ( !NT_SUCCESS( NtStatus ) ) { + return (NtStatus); + } + + + + + // + // Read the encrypted NT OWF password from the database + // + + NtStatus = SampGetUnicodeStringAttribute( + Context, + SAMP_USER_UNICODE_PWD, + TRUE, // Make copy + &StringBuffer + ); + + if ( !NT_SUCCESS( NtStatus ) ) { + return (NtStatus); + } + + // + // Check it is in the expected form + // + + ASSERT( (StringBuffer.Length == 0) || + (StringBuffer.Length == ENCRYPTED_NT_OWF_PASSWORD_LENGTH)); + + // + // Determine if there is an Nt password. + // + + *NtPasswordPresent = (BOOLEAN)(StringBuffer.Length != 0); + + // + // Decrypt the encrypted NT Owf Password + // + + if (*NtPasswordPresent) { + + NtStatus = RtlDecryptNtOwfPwdWithIndex( + (PENCRYPTED_NT_OWF_PASSWORD)StringBuffer.Buffer, + &CryptIndex, + NtOwfPassword + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + *NtPasswordNonNull = (BOOLEAN)!RtlEqualNtOwfPassword( + NtOwfPassword, + &SampNullNtOwfPassword + ); + } + + } else { + + // + // Fill in the NULL password for caller convenience + // + + *NtOwfPassword = SampNullNtOwfPassword; + *NtPasswordNonNull = FALSE; + } + + // + // Free up the returned string buffer + // + + SampFreeUnicodeString(&StringBuffer); + + + return( NtStatus ); +} + + + +NTSTATUS +SampRetrieveUserMembership( + IN PSAMP_OBJECT UserContext, + IN BOOLEAN MakeCopy, + OUT PULONG MembershipCount, + OUT PGROUP_MEMBERSHIP *Membership OPTIONAL + ) + +/*++ +Routine Description: + + This service retrieves the number of groups a user is a member of. + If desired, it will also retrieve an array of RIDs and attributes + of the groups the user is a member of. + + +Arguments: + + UserContext - User context block + + MakeCopy - If FALSE, the Membership pointer returned refers to the + in-memory data for the user. This is only valid as long + as the user context is valid. + If TRUE, memory is allocated and the membership list copied + into it. This buffer should be freed using MIDL_user_free. + + MembershipCount - Receives the number of groups the user is a member of. + + Membership - (Otional) Receives a pointer to a buffer containing an array + of group Relative IDs. If this value is NULL, then this information + is not returned. The returned buffer is allocated using + MIDL_user_allocate() and must be freed using MIDL_user_free() when + no longer needed. + + If MakeCopy = TRUE, the membership buffer returned has extra space + allocated at the end of it for one more membership entry. + + +Return Value: + + + STATUS_SUCCESS - The information has been retrieved. + + STATUS_INSUFFICIENT_RESOURCES - Memory could not be allocated for the + information to be returned in. + + Other status values that may be returned are those returned + by: + + SampGetLargeIntArrayAttribute() + + + +--*/ +{ + + NTSTATUS NtStatus; + PGROUP_MEMBERSHIP MemberArray; + ULONG MemberCount; + + SAMTRACE("SampRetrieveUserMembership"); + + + NtStatus = SampGetLargeIntArrayAttribute( + UserContext, + SAMP_USER_GROUPS, + FALSE, //Reference data directly. + (PLARGE_INTEGER *)&MemberArray, + &MemberCount + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Fill in return info + // + + *MembershipCount = MemberCount; + + if (Membership != NULL) { + + if (MakeCopy) { + + // + // Allocate a buffer large enough to hold the existing + // membership data and one more and copy data into it. + // + + ULONG BytesNow = (*MembershipCount) * sizeof(GROUP_MEMBERSHIP); + ULONG BytesRequired = BytesNow + sizeof(GROUP_MEMBERSHIP); + + *Membership = MIDL_user_allocate(BytesRequired); + + if (*Membership == NULL) { + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + } else { + RtlCopyMemory(*Membership, MemberArray, BytesNow); + } + + } else { + + // + // Reference the data directly + // + + *Membership = (PGROUP_MEMBERSHIP)MemberArray; + } + } + } + + + return( NtStatus ); + +} + + + +NTSTATUS +SampReplaceUserMembership( + IN PSAMP_OBJECT UserContext, + IN ULONG MembershipCount, + IN PGROUP_MEMBERSHIP Membership + ) + +/*++ +Routine Description: + + This service sets the groups a user is a member of. + + The information is updated in the in-memory copy of the user's data only. + The data is not written out by this routine. + + +Arguments: + + UserContext - User context block + + MembershipCount - The number of groups the user is a member of. + + Membership - A pointer to a buffer containing an array of group + membership structures. May be NULL if membership count is zero. + +Return Value: + + + STATUS_SUCCESS - The information has been set. + + Other status values that may be returned are those returned + by: + + SampSetUlongArrayAttribute() + + + +--*/ +{ + + NTSTATUS NtStatus; + + SAMTRACE("SampReplaceUserMembership"); + + NtStatus = SampSetLargeIntArrayAttribute( + UserContext, + SAMP_USER_GROUPS, + (PLARGE_INTEGER)Membership, + MembershipCount + ); + + return( NtStatus ); +} + + + +NTSTATUS +SampRetrieveUserLogonHours( + IN PSAMP_OBJECT Context, + IN PLOGON_HOURS LogonHours + ) + +/*++ +Routine Description: + + This service retrieves a user's logon hours from the registry. + + +Arguments: + + Context - Points to the user account context whose logon hours are + to be retrieved. + + LogonHours - Receives the logon hours information. If necessary, a buffer + containing the logon time restriction bitmap will be allocated using + MIDL_user_allocate(). + +Return Value: + + + STATUS_SUCCESS - The information has been retrieved. + + STATUS_INSUFFICIENT_RESOURCES - Memory could not be allocated for the + information to be returned in. + + Other status values that may be returned are those returned + by: + + NtOpenKey() + NtQueryValueKey() + + + +--*/ +{ + + NTSTATUS NtStatus; + + SAMTRACE("SampRetrieveUserLogonHours"); + + NtStatus = SampGetLogonHoursAttribute( + Context, + SAMP_USER_LOGON_HOURS, + TRUE, // Make copy + LogonHours + ); + + if (NT_SUCCESS(NtStatus)) { + + //////////////////////////////// TEMPORARY MIDL WORKAROUND /////////// + /////////// + if (LogonHours->LogonHours == NULL) { /////////// + /////////// + LogonHours->UnitsPerWeek = SAM_HOURS_PER_WEEK; /////////// + LogonHours->LogonHours = MIDL_user_allocate( 21 ); /////////// + { /////////// + ULONG ijk; /////////// + for ( ijk=0; ijk<21; ijk++ ) { /////////// + LogonHours->LogonHours[ijk] = 0xff; /////////// + } /////////// + } /////////// + } /////////// + /////////// + //////////////////////////////// TEMPORARY MIDL WORKAROUND /////////// + } + + return( NtStatus ); + +} + + + + +NTSTATUS +SampReplaceUserLogonHours( + IN PSAMP_OBJECT Context, + IN PLOGON_HOURS LogonHours + ) + +/*++ +Routine Description: + + This service replaces a user's logon hours in the registry. + + THIS IS DONE BY ADDING AN ACTION TO THE CURRENT RXACT TRANSACTION. + + +Arguments: + + Context - Points to the user account context whose logon hours are + to be replaced. + + LogonHours - Provides the new logon hours. + + +Return Value: + + + STATUS_SUCCESS - The information has been retrieved. + + + Other status values that may be returned are those returned + by: + + RtlAddActionToRXact() + + + +--*/ +{ + NTSTATUS NtStatus; + + SAMTRACE("SampReplaceUserLogonHours"); + + if ( LogonHours->UnitsPerWeek > SAM_MINUTES_PER_WEEK ) { + return(STATUS_INVALID_PARAMETER); + } + + + NtStatus = SampSetLogonHoursAttribute( + Context, + SAMP_USER_LOGON_HOURS, + LogonHours + ); + + return( NtStatus ); + + +} + + + + +NTSTATUS +SampAssignPrimaryGroup( + IN PSAMP_OBJECT Context, + IN ULONG GroupRid + ) + + +/*++ +Routine Description: + + This service ensures a user is a member of the specified group. + + +Arguments: + + Context - Points to the user account context whose primary group is + being changed. + + GroupRid - The RID of the group being assigned as primary group. + The user must be a member of this group. + + +Return Value: + + + STATUS_SUCCESS - The information has been retrieved. + + STATUS_INSUFFICIENT_RESOURCES - Memory could not be allocated to perform + the operation. + + STATUS_MEMBER_NOT_IN_GROUP - The user is not a member of the specified + group. + + Other status values that may be returned are those returned + by: + + SampRetrieveUserMembership() + + + +--*/ +{ + + NTSTATUS NtStatus; + ULONG MembershipCount, i; + PGROUP_MEMBERSHIP Membership; + BOOLEAN Member = FALSE; + + SAMTRACE("SampAssignPrimaryGroup"); + + if (IsDsObject(Context)) + { + NtStatus = SampRetrieveUserMembership( + Context, + FALSE, // Make copy + &MembershipCount, + &Membership + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = STATUS_MEMBER_NOT_IN_GROUP; + for ( i=0; i<MembershipCount; i++) { + if (GroupRid == Membership[i].RelativeId) { + NtStatus = STATUS_SUCCESS; + break; + } + } + } + } + else + { + // + // Query User Membership using Davestr Call. + // and do things + // + + NtStatus = STATUS_NOT_IMPLEMENTED; + } + + return( NtStatus ); +} + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Services Provided for use by other SAM modules // +// // +/////////////////////////////////////////////////////////////////////////////// + + +NTSTATUS +SampRetrieveUserV1aFixed( + IN PSAMP_OBJECT UserContext, + OUT PSAMP_V1_0A_FIXED_LENGTH_USER V1aFixed + ) + +/*++ + +Routine Description: + + This service retrieves the V1 fixed length information related to + a specified User. + + It updates the ACCOUNT_AUTO_LOCKED flag in the AccountControl field + as appropriate while retrieving the data. + + +Arguments: + + UserContext - User context handle + + V1aFixed - Points to a buffer into which V1_FIXED information is to be + retrieved. + + + +Return Value: + + + STATUS_SUCCESS - The information has been retrieved. + + V1aFixed - Is a buffer into which the information is to be returned. + + Other status values that may be returned are those returned + by: + + SampGetFixedAttributes() + + + +--*/ +{ + NTSTATUS NtStatus; + PVOID FixedData; + BOOLEAN IgnoreState; + + SAMTRACE("SampRetrieveUserV1aFixed"); + + + NtStatus = SampGetFixedAttributes( + UserContext, + FALSE, // Don't copy + &FixedData + ); + + if (NT_SUCCESS(NtStatus)) { + + + // + // Copy data into return buffer + // + + RtlMoveMemory( + V1aFixed, + FixedData, + sizeof(SAMP_V1_0A_FIXED_LENGTH_USER) + ); + + // + // Update the account lockout flag (might need to be turned off) + // + + SampUpdateAccountLockedOutFlag( UserContext, + V1aFixed, + &IgnoreState ); + + } + + + + return( NtStatus ); + +} + + +NTSTATUS +SampRetrieveUserGroupAttribute( + IN ULONG UserRid, + IN ULONG GroupRid, + OUT PULONG Attribute + ) + +/*++ + +Routine Description: + + This service retrieves the Attribute of the specified group as assigned + to the specified user account. This routine is used by group apis that + don't have a user context available. + + THIS SERVICE MUST BE CALLED WITH THE TRANSACTION DOMAIN SET. + +Arguments: + + UserRid - The relative ID of the user the group is assigned to. + + GroupRid - The relative ID of the assigned group. + + Attribute - Receives the Attributes of the group as they are assigned + to the user. + + + +Return Value: + + + STATUS_SUCCESS - The information has been retrieved. + + STATUS_INTERNAL_DB_CORRUPTION - The user does not exist or the group + was not in the user's list of memberships. + + Other status values that may be returned are those returned + by: + + NtOpenKey() + NtQueryValueKey() + + + +--*/ +{ + NTSTATUS NtStatus; + PSAMP_OBJECT UserContext; + ULONG MembershipCount; + PGROUP_MEMBERSHIP Membership; + ULONG i; + BOOLEAN AttributeFound; + + SAMTRACE("SampRetrieveUserGroupAttribute"); + + + // + // Get a context handle for the user + // + + NtStatus = SampCreateAccountContext( + SampUserObjectType, + UserRid, + TRUE, // We're trusted + TRUE, // Account exists + &UserContext + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Now we have a user context, get the user's group/alias membership + // + + if (IsDsObject(UserContext)) + { + // + // User is DS Object, then hardwire the attribute to 0 + // + + *Attribute = 0; + } + else + { + NtStatus = SampRetrieveUserMembership( + UserContext, + FALSE, // Make copy + &MembershipCount, + &Membership + ); + + // + // Search the list of groups for a match and return + // the corresponding attribute. + // + + if (NT_SUCCESS(NtStatus)) { + + AttributeFound = FALSE; + for ( i=0; (i<MembershipCount && !AttributeFound); i++) { + if (GroupRid == Membership[i].RelativeId) { + (*Attribute) = Membership[i].Attributes; + AttributeFound = TRUE; + } + } + } + } + + // + // Clean up the user context + // + + SampDeleteContext(UserContext); + } + + + if (NT_SUCCESS(NtStatus) && !AttributeFound) { + NtStatus = STATUS_INTERNAL_DB_CORRUPTION; + } + + + return( NtStatus ); + +} + + +NTSTATUS +SampAddGroupToUserMembership( + IN ULONG GroupRid, + IN ULONG Attributes, + IN ULONG UserRid, + IN SAMP_MEMBERSHIP_DELTA AdminGroup, + IN SAMP_MEMBERSHIP_DELTA OperatorGroup, + OUT PBOOLEAN UserActive + ) + +/*++ + +Routine Description: + + This service adds the specified group to the user's membership + list. It is not assumed that the caller knows anything about + the target user. In particular, the caller doesn't know whether + the user exists or not, nor whether the user is already a member + of the group. + + If the GroupRid is DOMAIN_GROUP_RID_ADMINS, then this service + will also indicate whether the user account is currently active. + + THIS SERVICE MUST BE CALLED WITH THE TRANSACTION DOMAIN SET. + +Arguments: + + GroupRid - The relative ID of the group. + + Attributes - The group attributes as the group is assigned to the + user. + + UserRid - The relative ID of the user. + + AdminGroup - Indicates whether the group the user is being + added to is an administrator group (that is, directly + or indirectly a member of the Administrators alias). + + OperatorGroup - Indicates whether the group the user is being + added to is an operator group (that is, directly + or indirectly a member of the Account Operators, Print + Operators, Backup Operators, or Server Operators aliases) + + UserActive - is the address of a BOOLEAN to be set to indicate + whether the user account is currently active. TRUE indicates + the account is active. This value will only be set if the + GroupRid is DOMAIN_GROUP_RID_ADMINS. + + + + +Return Value: + + + STATUS_SUCCESS - The information has been updated and added to the + RXACT. + + STATUS_NO_SUCH_USER - The user does not exist. + + STATUS_MEMBER_IN_GROUP - The user is already a member of the + specified group. + + Other status values that may be returned are those returned + by: + + NtOpenKey() + NtQueryValueKey() + RtlAddActionToRXact() + + + +--*/ +{ + + NTSTATUS NtStatus; + PSAMP_OBJECT UserContext; + SAMP_V1_0A_FIXED_LENGTH_USER V1aFixed; + ULONG MembershipCount; + PGROUP_MEMBERSHIP Membership; + ULONG i; + + SAMTRACE("SampAddGroupToUserMembership"); + + // + // Get a context handle for the user + // + + NtStatus = SampCreateAccountContext( + SampUserObjectType, + UserRid, + TRUE, // We're trusted + TRUE, // Account exists + &UserContext + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // If this group is in the Administrators alias + // or we are the Domain Administrator group, then + // get the V1aFixed data. + // + + if ((AdminGroup == AddToAdmin) || (OperatorGroup == AddToAdmin)) { + NtStatus = SampRetrieveUserV1aFixed( + UserContext, + &V1aFixed + ); + } + + // + // If necessary, return an indication as to whether this account + // is enabled or not. + // + + if (NT_SUCCESS(NtStatus)) { + + if (GroupRid == DOMAIN_GROUP_RID_ADMINS) { + + ASSERT(AdminGroup == AddToAdmin); // Make sure we retrieved the V1aFixed + + if ((V1aFixed.UserAccountControl & USER_ACCOUNT_DISABLED) == 0) { + (*UserActive) = TRUE; + } else { + (*UserActive) = FALSE; + } + } + } + + if (NT_SUCCESS(NtStatus)) { + + // + // If the user is being added to an ADMIN group, modify + // the user's ACLs so that account operators can once again + // alter the account. This will only occur if the user + // is no longer a member of any admin groups. + // + + if ((AdminGroup == AddToAdmin) || (OperatorGroup == AddToAdmin)) { + NtStatus = SampChangeOperatorAccessToUser2( + UserContext, + &V1aFixed, + AdminGroup, + OperatorGroup + ); + } + } + + + if ((NT_SUCCESS(NtStatus)) && (!IsDsObject(UserContext))) + { + + // + // Get the user membership + // Note the returned buffer already includes space for + // an extra member. For DS case we do not maintain reverse + // membership + // + + NtStatus = SampRetrieveUserMembership( + UserContext, + TRUE, // Make copy + &MembershipCount, + &Membership + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // See if the user is already a member ... + // + + for (i = 0; i<MembershipCount ; i++ ) { + if ( Membership[i].RelativeId == GroupRid ) + { + NtStatus = STATUS_MEMBER_IN_GROUP; + } + } + + if (NT_SUCCESS(NtStatus)) { + + // + // Add the groups's RID to the end. + // + + Membership[MembershipCount].RelativeId = GroupRid; + Membership[MembershipCount].Attributes = Attributes; + MembershipCount += 1; + + // + // Set the user's new membership + // + + NtStatus = SampReplaceUserMembership( + UserContext, + MembershipCount, + Membership + ); + } + + // + // Free up the membership array + // + + MIDL_user_free( Membership ); + } + } + + // + // Write out any changes to the user account + // Don't use the open key handle since we'll be deleting the context. + // + + if (NT_SUCCESS(NtStatus)) { + NtStatus = SampStoreObjectAttributes(UserContext, FALSE); + } + + // + // Clean up the user context + // + + SampDeleteContext(UserContext); + } + + return( NtStatus ); + +} + + + +NTSTATUS +SampRemoveMembershipUser( + IN ULONG GroupRid, + IN ULONG UserRid, + IN SAMP_MEMBERSHIP_DELTA AdminGroup, + IN SAMP_MEMBERSHIP_DELTA OperatorGroup, + OUT PBOOLEAN UserActive + ) + +/*++ + +Routine Description: + + This service removes the specified group from the user's membership + list. It is not assumed that the caller knows anything about + the target user. In particular, the caller doesn't know whether + the user exists or not, nor whether the user is really a member + of the group. + + If the GroupRid is DOMAIN_GROUP_RID_ADMINS, then this service + will also indicate whether the user account is currently active. + + THIS SERVICE MUST BE CALLED WITH THE TRANSACTION DOMAIN SET. + +Arguments: + + GroupRid - The relative ID of the group. + + UserRid - The relative ID of the user. + + AdminGroup - Indicates whether the group the user is being + removed from is an administrator group (that is, directly + or indirectly a member of the Administrators alias). + + OperatorGroup - Indicates whether the group the user is being + added to is an operator group (that is, directly + or indirectly a member of the Account Operators, Print + Operators, Backup Operators, or Server Operators aliases) + + UserActive - is the address of a BOOLEAN to be set to indicate + whether the user account is currently active. TRUE indicates + the account is active. This value will only be set if the + GroupRid is DOMAIN_GROUP_RID_ADMINS. + + + + +Return Value: + + + STATUS_SUCCESS - The information has been updated and added to the + RXACT. + + STATUS_NO_SUCH_USER - The user does not exist. + + STATUS_MEMBER_NOT_IN_GROUP - The user is not a member of the + specified group. + + Other status values that may be returned are those returned + by: + + NtOpenKey() + NtQueryValueKey() + RtlAddActionToRXact() + + + +--*/ +{ + + NTSTATUS NtStatus; + ULONG MembershipCount, i; + PGROUP_MEMBERSHIP MembershipArray; + SAMP_V1_0A_FIXED_LENGTH_USER V1aFixed; + PSAMP_OBJECT UserContext; + + SAMTRACE("SampRemoveMembershipUser"); + + // + // Create a context for the user + // + + NtStatus = SampCreateAccountContext( + SampUserObjectType, + UserRid, + TRUE, // Trusted client + TRUE, // Account exists + &UserContext + ); + + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + // + // Get the v1 fixed information + // (contains primary group value and control flags) + // + + NtStatus = SampRetrieveUserV1aFixed( UserContext, &V1aFixed ); + + if (NT_SUCCESS(NtStatus)) { + + // + // If the user is being removed from an ADMIN group, modify + // the user's ACLs so that account operators can once again + // alter the account. This will only occur if the user + // is no longer a member of any admin groups. + // + + if ((AdminGroup == RemoveFromAdmin) || + (OperatorGroup == RemoveFromAdmin)) { + NtStatus = SampChangeOperatorAccessToUser2( + UserContext, + &V1aFixed, + AdminGroup, + OperatorGroup + ); + } + + if (NT_SUCCESS(NtStatus)) { + + // + // If necessary, return an indication as to whether this account + // is enabled or not. + // + + if (GroupRid == DOMAIN_GROUP_RID_ADMINS) { + + if ((V1aFixed.UserAccountControl & USER_ACCOUNT_DISABLED) == 0) { + (*UserActive) = TRUE; + } else { + (*UserActive) = FALSE; + } + } + + + // + // See if this is the user's primary group... + // + + if (GroupRid == V1aFixed.PrimaryGroupId) { + NtStatus = STATUS_MEMBERS_PRIMARY_GROUP; + } + + + + if ((NT_SUCCESS(NtStatus)) && (!IsDsObject(UserContext))) + { + + // + // Get the user membership, No reverse membership is stored for + // DS Objects + // + + NtStatus = SampRetrieveUserMembership( + UserContext, + TRUE, // Make copy + &MembershipCount, + &MembershipArray + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // See if the user is a member ... + // + + NtStatus = STATUS_MEMBER_NOT_IN_GROUP; + for (i = 0; i<MembershipCount ; i++ ) { + if ( MembershipArray[i].RelativeId == GroupRid ) + { + NtStatus = STATUS_SUCCESS; + break; + } + } + + if (NT_SUCCESS(NtStatus)) { + + // + // Replace the removed group information + // with the last entry's information. + // + + MembershipCount -= 1; + if (MembershipCount > 0) { + MembershipArray[i].RelativeId = + MembershipArray[MembershipCount].RelativeId; + MembershipArray[i].Attributes = + MembershipArray[MembershipCount].Attributes; + } + + // + // Update the object with the new information + // + + NtStatus = SampReplaceUserMembership( + UserContext, + MembershipCount, + MembershipArray + ); + } + + // + // Free up the membership array + // + + MIDL_user_free( MembershipArray ); + } + } + } + } + + + // + // Write out any changes to the user account + // Don't use the open key handle since we'll be deleting the context. + // + + if (NT_SUCCESS(NtStatus)) { + NtStatus = SampStoreObjectAttributes(UserContext, FALSE); + } + + + // + // Clean up the user context + // + + SampDeleteContext(UserContext); + + + return( NtStatus ); + +} + + + +NTSTATUS +SampSetGroupAttributesOfUser( + IN ULONG GroupRid, + IN ULONG Attributes, + IN ULONG UserRid + ) + +/*++ + +Routine Description: + + This service replaces the attributes of a group assigned to a + user. + + The caller does not have to know whether the group is currently + assigned to the user. + + THIS SERVICE MUST BE CALLED WITH THE TRANSACTION DOMAIN SET. + +Arguments: + + GroupRid - The relative ID of the group. + + Attributes - The group attributes as the group is assigned to the + user. + + UserRid - The relative ID of the user. + + + +Return Value: + + + STATUS_SUCCESS - The information has been updated and added to the + RXACT. + + STATUS_NO_SUCH_USER - The user does not exist. + + STATUS_MEMBER_NOT_IN_GROUP - The user is not in the specified group. + + + Other status values that may be returned are those returned + by: + + NtOpenKey() + NtQueryValueKey() + RtlAddActionToRXact() + + + +--*/ +{ + + NTSTATUS NtStatus; + PSAMP_OBJECT UserContext; + ULONG MembershipCount; + PGROUP_MEMBERSHIP Membership; + ULONG i; + + SAMTRACE("SampSetGroupAttributesOfUser"); + + + // + // Get a context handle for the user + // + + NtStatus = SampCreateAccountContext( + SampUserObjectType, + UserRid, + TRUE, // We're trusted + TRUE, // Account exists + &UserContext + ); + + if ((NT_SUCCESS(NtStatus)) && (!IsDsObject(UserContext))) { + + // + // Now we have a user context, get the user's group/alias membership + // For DS case this is a No Op + // + + NtStatus = SampRetrieveUserMembership( + UserContext, + TRUE, // Make copy + &MembershipCount, + &Membership + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // See if the user is a member ... + // + + NtStatus = STATUS_MEMBER_NOT_IN_GROUP; + for (i = 0; i<MembershipCount; i++ ) { + if ( Membership[i].RelativeId == GroupRid ) + { + NtStatus = STATUS_SUCCESS; + break; + } + } + + if (NT_SUCCESS(NtStatus)) { + + // + // Change the groups's attributes. + // + + Membership[i].Attributes = Attributes; + + // + // Update the user's membership + // + + NtStatus = SampReplaceUserMembership( + UserContext, + MembershipCount, + Membership + ); + } + + // + // Free up the membership array + // + + MIDL_user_free(Membership); + } + + // + // Write out any changes to the user account + // Don't use the open key handle since we'll be deleting the context. + // + + if (NT_SUCCESS(NtStatus)) { + NtStatus = SampStoreObjectAttributes(UserContext, FALSE); + } + + // + // Clean up the user context + // + + SampDeleteContext(UserContext); + } + + + return( NtStatus ); +} + + + + +NTSTATUS +SampDeleteUserKeys( + IN PSAMP_OBJECT Context + ) + +/*++ +Routine Description: + + This service deletes all registry keys related to a User object. + + +Arguments: + + Context - Points to the User context whose registry keys are + being deleted. + + +Return Value: + + + STATUS_SUCCESS - The information has been retrieved. + + + Other status values that may be returned by: + + RtlAddActionToRXact() + + + +--*/ +{ + + NTSTATUS NtStatus; + ULONG Rid; + UNICODE_STRING AccountName, KeyName; + + SAMTRACE("SampDeleteUserKeys"); + + + Rid = Context->TypeBody.User.Rid; + + + + + // + // Decrement the User count + // + + NtStatus = SampAdjustAccountCount(SampUserObjectType, FALSE ); + + + + + // + // Delete the registry key that has the User's name to RID mapping. + // + + if (NT_SUCCESS(NtStatus)) { + + // + // Get the name + // + + NtStatus = SampGetUnicodeStringAttribute( + Context, + SAMP_USER_ACCOUNT_NAME, + TRUE, // Make copy + &AccountName + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampBuildAccountKeyName( + SampUserObjectType, + &KeyName, + &AccountName + ); + + SampFreeUnicodeString( &AccountName ); + + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = RtlAddActionToRXact( + SampRXactContext, + RtlRXactOperationDelete, + &KeyName, + 0, + NULL, + 0 + ); + SampFreeUnicodeString( &KeyName ); + } + } + } + + + + // + // Delete the attribute keys + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampDeleteAttributeKeys( + Context + ); + } + + + + + // + // Delete the RID key + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampBuildAccountSubKeyName( + SampUserObjectType, + &KeyName, + Rid, + NULL + ); + + if (NT_SUCCESS(NtStatus)) { + + + NtStatus = RtlAddActionToRXact( + SampRXactContext, + RtlRXactOperationDelete, + &KeyName, + 0, + NULL, + 0 + ); + + SampFreeUnicodeString( &KeyName ); + } + + + } + + + + return( NtStatus ); + +} + + + +NTSTATUS +SampAddPasswordHistory( + IN PSAMP_OBJECT Context, + IN ULONG HistoryAttributeIndex, + IN PUNICODE_STRING NtOwfHistoryBuffer, + IN PVOID EncryptedPassword, + IN ULONG EncryptedPasswordLength, + IN USHORT PasswordHistoryLength + ) + +/*++ + +Routine Description: + + This service adds a password to the given user's password history. + It will work for either NT or Lanman password histories. + + This routine should only be called if the password is actually present. + + +Arguments: + + Context - a pointer to the user context to which changes will be made. + + HistoryAttributeIndex - the attribue index in the user context which + contains the password history. + + NtOwfHistoryBuffer - A pointer to the current password history, as + it was retrieved from the disk - it's encrypted, and pretending + to be in the UNICODE_STRING format. + + EncryptedPasswordLength - ENCRYPTED_NT_OWF_LENGTH or + ENCRYPTED_LM_OWF_LENGTH, depending on which type of password + history is being worked on. + + PasswordHistoryLength - The PasswordHistoryLength for the user's + domain. + + +Return Value: + + + STATUS_SUCCESS - The given password was added to the password history. + + STATUS_INSUFFICIENT_RESOURCES - The user's password history needs to + be expanded, but there isn't enough memory to do so. + + Other errors from building the account subkey name or writing the + password history out to the registry. + + +--*/ +{ + NTSTATUS NtStatus = STATUS_SUCCESS; + PCHAR OldBuffer; + + SAMTRACE("SampAddPasswordHistory"); + + if ( ( NtOwfHistoryBuffer->Length / EncryptedPasswordLength ) < + ( (ULONG)PasswordHistoryLength ) ) { + + // + // Password history buffer can be expanded. + // Allocate a larger buffer, copy the old buffer to the new one + // while leaving room for the new password, and free the old + // buffer. + // + + OldBuffer = (PCHAR)(NtOwfHistoryBuffer->Buffer); + + NtOwfHistoryBuffer->Buffer = MIDL_user_allocate( + NtOwfHistoryBuffer->Length + EncryptedPasswordLength ); + + if ( NtOwfHistoryBuffer->Buffer == NULL ) { + + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + NtOwfHistoryBuffer->Buffer = (PWSTR)OldBuffer; + + } else { + + RtlCopyMemory( + (PVOID)( (PCHAR)(NtOwfHistoryBuffer->Buffer) + EncryptedPasswordLength ), + (PVOID)OldBuffer, + NtOwfHistoryBuffer->Length ); + + MIDL_user_free( OldBuffer ); + + NtOwfHistoryBuffer->Length = (USHORT)(NtOwfHistoryBuffer->Length + + EncryptedPasswordLength); + } + + } else { + + // + // Password history buffer is at its maximum size, or larger (for + // this domain). If it's larger, cut it down to the current maximum. + // + + if ( ( NtOwfHistoryBuffer->Length / EncryptedPasswordLength ) > + ( (ULONG)PasswordHistoryLength ) ) { + + // + // Password history is too large (the password history length must + // have been shortened recently). + // Set length to the proper value, + // + + NtOwfHistoryBuffer->Length = (USHORT)(EncryptedPasswordLength * + PasswordHistoryLength); + } + + // + // Password history buffer is full, at its maximum size. + // Move buffer contents right 16 bytes, which will lose the oldest + // password and make room for the new password at the beginning + // (left). + // Note that we CAN'T move anything if the password history size + // is 0. If it's 1, we could but no need since we'll overwrite + // it below. + // + + if ( PasswordHistoryLength > 1 ) { + + RtlMoveMemory( + (PVOID)( (PCHAR)(NtOwfHistoryBuffer->Buffer) + EncryptedPasswordLength ), + (PVOID)NtOwfHistoryBuffer->Buffer, + NtOwfHistoryBuffer->Length - EncryptedPasswordLength ); + } + } + + + // + // Put the new encrypted OWF at the beginning of the password history + // buffer (unless, of course, the buffer size is 0), and write the password + // history to disk. + // + + if ( NT_SUCCESS( NtStatus ) ) { + + if ( PasswordHistoryLength > 0 ) { + + RtlCopyMemory( + (PVOID)NtOwfHistoryBuffer->Buffer, + (PVOID)EncryptedPassword, + EncryptedPasswordLength ); + } + + + NtStatus = SampSetUnicodeStringAttribute( + Context, + HistoryAttributeIndex, + NtOwfHistoryBuffer + ); + } + + return( NtStatus ); +} + + + +NTSTATUS +SampCheckPasswordHistory( + IN PVOID EncryptedPassword, + IN ULONG EncryptedPasswordLength, + IN USHORT PasswordHistoryLength, + IN ULONG HistoryAttributeIndex, + IN PSAMP_OBJECT Context, + IN BOOLEAN CheckHistory, + IN PUNICODE_STRING OwfHistoryBuffer + ) + +/*++ + +Routine Description: + + This service takes the given password, and optionally checks it against the + password history on the disk. It returns a pointer to the password + history, which will later be passed to SampAddPasswordHistory(). + + This routine should only be called if the password is actually present. + + +Arguments: + + EncryptedPassword - A pointer to the encrypted password that we're + looking for. + + EncryptedPasswordLength - ENCRYPTED_NT_OWF_PASSWORD or + ENCRYPTED_LM_OWF_PASSWORD, depending on the type of password + history to be searched. + + PasswordHistoryLength - the length of the password history for this + domain. + + SubKeyName - a pointer to a unicode string that describes the name + of the password history to be read from the disk. + + Context - a pointer to the user's context. + + CheckHistory - If TRUE, the password is to be checked against + the history to see if it is already present and an error returned + if it is found. If FALSE, the password will not be checked, but a + pointer to the appropriate history buffer will still be returned + because the specified password will be added to the history via + SampAddPasswordHistory. + + NOTE: The purpose of this flag is to allow Administrator to change + a user's password regardless of whether it is already in the history. + + OwfHistoryBuffer - a pointer to a UNICODE_STRING which will be + used to point to the password history. + + NOTE: The caller must free OwfHistoryBuffer.Buffer with + MIDL_user_free(). + + +Return Value: + + + STATUS_SUCCESS - The given password was not found in the password + history. + + STATUS_PASSWORD_RESTRICTION - The given password was found in the + password history. + + Other errors from reading the password history from disk. + + +--*/ +{ + NTSTATUS NtStatus = STATUS_SUCCESS; + PVOID PasswordHistoryEntry; + ULONG i = 0; + BOOLEAN OldPasswordFound = FALSE; + + SAMTRACE("SampCheckPasswordHistory"); + + + if ( ( PasswordHistoryLength > 0 ) && ( OwfHistoryBuffer->Length == 0 ) ) { + + // + // Perhaps the domain's PasswordHistoryLength was raised from 0 + // since the last time this user's password was changed. Try to + // put the current password (if non-null) in the password history. + // + + UNICODE_STRING CurrentPassword; + USHORT PasswordAttributeIndex; + + // + // Initialize the CurrentPassword buffer pointer to NULL (and the + // rest of the structure for consistency. The called routine + // SampGetUnicodeStringAttribute may perform a MIDL_user_allocate + // on a zero buffer length and cannot safely be changed as there are + // many callers. The semantics of a zero-length allocate call are + // not clear. Currently a pointer to a heap block is returned, + // but this might be changed to a NULL being returned. + // + + CurrentPassword.Length = CurrentPassword.MaximumLength = 0; + CurrentPassword.Buffer = NULL; + + + if ( HistoryAttributeIndex == SAMP_USER_LM_PWD_HISTORY ) { + + PasswordAttributeIndex = SAMP_USER_DBCS_PWD; + + } else { + + ASSERT( HistoryAttributeIndex == SAMP_USER_NT_PWD_HISTORY ); + PasswordAttributeIndex = SAMP_USER_UNICODE_PWD; + } + + NtStatus = SampGetUnicodeStringAttribute( + Context, + PasswordAttributeIndex, + TRUE, // Make copy + &CurrentPassword + ); + + if ( ( NT_SUCCESS( NtStatus ) ) && ( CurrentPassword.Length != 0 ) ) { + + ASSERT( (CurrentPassword.Length == ENCRYPTED_NT_OWF_PASSWORD_LENGTH) || + (CurrentPassword.Length == ENCRYPTED_LM_OWF_PASSWORD_LENGTH) ); + + NtStatus = SampAddPasswordHistory( + Context, + HistoryAttributeIndex, + OwfHistoryBuffer, + CurrentPassword.Buffer, + CurrentPassword.Length, + PasswordHistoryLength + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // Free the old password history, and re-read the + // altered password history from the disk. + // + + MIDL_user_free( OwfHistoryBuffer->Buffer ); + + NtStatus = SampGetUnicodeStringAttribute( + Context, + HistoryAttributeIndex, + TRUE, // Make copy + OwfHistoryBuffer + ); + } + } + + // + // If memory was allocated, free it. + // + + if (CurrentPassword.Buffer != NULL) { + + SampFreeUnicodeString( &CurrentPassword ); + } + } + + if ( !NT_SUCCESS( NtStatus ) ) { + + return( NtStatus ); + } + + // + // If requested, check the Password History to see if we can use this + // password. Compare the passed-in password to each of the entries in + // the password history. + // + + if (CheckHistory) { + + PasswordHistoryEntry = (PVOID)(OwfHistoryBuffer->Buffer); + + while ( ( i < (ULONG)PasswordHistoryLength ) && + ( i < ( OwfHistoryBuffer->Length / EncryptedPasswordLength ) ) && + ( OldPasswordFound == FALSE ) ) { + + if ( RtlCompareMemory( + EncryptedPassword, + PasswordHistoryEntry, + EncryptedPasswordLength ) == EncryptedPasswordLength ) { + + OldPasswordFound = TRUE; + + } else { + + i++; + + PasswordHistoryEntry = (PVOID)((PCHAR)(PasswordHistoryEntry) + + EncryptedPasswordLength ); + } + } + + if ( OldPasswordFound ) { + + // + // We did find it in the password history, so return an appropriate + // error. + // + + NtStatus = STATUS_PASSWORD_RESTRICTION; + } + } + + return( NtStatus ); +} + + + +NTSTATUS +SampMatchworkstation( + IN PUNICODE_STRING LogonWorkStation, + IN PUNICODE_STRING WorkStations + ) + +/*++ + +Routine Description: + + Check if the given workstation is a member of the list of workstations + given. + + +Arguments: + + LogonWorkStations - UNICODE name of the workstation that the user is + trying to log into. + + WorkStations - API list of workstations that the user is allowed to + log into. + + +Return Value: + + + STATUS_SUCCESS - The user is allowed to log into the workstation. + + + +--*/ +{ + PWCHAR WorkStationName; + UNICODE_STRING Unicode; + NTSTATUS NtStatus; + WCHAR Buffer[256]; + USHORT LocalBufferLength = 256; + UNICODE_STRING WorkStationsListCopy; + BOOLEAN BufferAllocated = FALSE; + PWCHAR TmpBuffer; + + SAMTRACE("SampMatchWorkstation"); + + // + // Local workstation is always allowed + // If WorkStations field is 0 everybody is allowed + // + + if ( ( LogonWorkStation == NULL ) || + ( LogonWorkStation->Length == 0 ) || + ( WorkStations->Length == 0 ) ) { + + return( STATUS_SUCCESS ); + } + + // + // Assume failure; change status only if we find the string. + // + + NtStatus = STATUS_INVALID_WORKSTATION; + + // + // WorkStationApiList points to our current location in the list of + // WorkStations. + // + + if ( WorkStations->Length > LocalBufferLength ) { + + WorkStationsListCopy.Buffer = RtlAllocateHeap( RtlProcessHeap(), 0, WorkStations->Length ); + BufferAllocated = TRUE; + + if ( WorkStationsListCopy.Buffer == NULL ) { + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + return( NtStatus ); + } + + WorkStationsListCopy.MaximumLength = WorkStations->Length; + + } else { + + WorkStationsListCopy.Buffer = Buffer; + WorkStationsListCopy.MaximumLength = LocalBufferLength; + } + + RtlCopyUnicodeString( &WorkStationsListCopy, WorkStations ); + ASSERT( WorkStationsListCopy.Length == WorkStations->Length ); + + // + // wcstok requires a string the first time it's called, and NULL + // for all subsequent calls. Use a temporary variable so we + // can do this. + // + + TmpBuffer = WorkStationsListCopy.Buffer; + + while( WorkStationName = wcstok(TmpBuffer, L",") ) { + + TmpBuffer = NULL; + RtlInitUnicodeString( &Unicode, WorkStationName ); + if (RtlEqualComputerName( &Unicode, LogonWorkStation )) { + NtStatus = STATUS_SUCCESS; + break; + } + } + + if ( BufferAllocated ) { + RtlFreeHeap( RtlProcessHeap(), 0, WorkStationsListCopy.Buffer ); + } + + return( NtStatus ); +} + + +LARGE_INTEGER +SampAddDeltaTime( + IN LARGE_INTEGER Time, + IN LARGE_INTEGER DeltaTime + ) + +/*++ +Routine Description: + + This service adds a delta time to a time and limits the result to + the maximum legal absolute time value + +Arguments: + + Time - An absolute time + + DeltaTime - A delta time + +Return Value: + + The time modified by delta time. + +--*/ +{ + // + // Check the time and delta time aren't switched + // + + SAMTRACE("SampAddDeleteTime"); + + ASSERT(!(Time.QuadPart < 0)); + ASSERT(!(DeltaTime.QuadPart > 0)); + + try { + + Time.QuadPart = (Time.QuadPart - DeltaTime.QuadPart); + + } except(EXCEPTION_EXECUTE_HANDLER) { + + return( SampWillNeverTime ); + } + + // + // Limit the resultant time to the maximum valid absolute time + // + + if (Time.QuadPart < 0) { + Time = SampWillNeverTime; + } + + return(Time); +} + + + + +NTSTATUS +SampChangeUserAccountName( + IN PSAMP_OBJECT Context, + IN PUNICODE_STRING NewAccountName, + OUT PUNICODE_STRING OldAccountName + ) + +/*++ +Routine Description: + + This routine changes the account name of a user account. + + THIS SERVICE MUST BE CALLED WITH THE TRANSACTION DOMAIN SET. + +Arguments: + + Context - Points to the User context whose name is to be changed. + + NewAccountName - New name to give this account + + OldAccountName - old name is returned here. The buffer should be freed + by calling MIDL_user_free. + +Return Value: + + + STATUS_SUCCESS - The information has been retrieved. + + + Other status values that may be returned by: + + SampGetUnicodeStringAttribute() + SampSetUnicodeStringAttribute() + SampValidateAccountNameChange() + RtlAddActionToRXact() + + + +--*/ +{ + + NTSTATUS NtStatus; + UNICODE_STRING KeyName; + + SAMTRACE("SampChangeUserAccountName"); + + + ///////////////////////////////////////////////////////////// + // There are two copies of the name of each account. // + // one is under the DOMAIN\(domainName)\USER\NAMES key, // + // one is the value of the // + // DOMAIN\(DomainName)\USER\(rid)\NAME key // + ///////////////////////////////////////////////////////////// + + + // + // Get the current name so we can delete the old Name->Rid + // mapping key. + // + + NtStatus = SampGetUnicodeStringAttribute( + Context, + SAMP_USER_ACCOUNT_NAME, + TRUE, // Make copy + OldAccountName + ); + + // + // Make sure the name is valid and not already in use + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampValidateAccountNameChange( + NewAccountName, + OldAccountName + ); + + // + // Delete the old name key + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampBuildAccountKeyName( + SampUserObjectType, + &KeyName, + OldAccountName + ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = RtlAddActionToRXact( + SampRXactContext, + RtlRXactOperationDelete, + &KeyName, + 0, + NULL, + 0 + ); + SampFreeUnicodeString( &KeyName ); + } + + } + + // + // + // Create the new Name->Rid mapping key + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampBuildAccountKeyName( + SampUserObjectType, + &KeyName, + NewAccountName + ); + + if (NT_SUCCESS(NtStatus)) { + + ULONG ObjectRid = Context->TypeBody.User.Rid; + + NtStatus = RtlAddActionToRXact( + SampRXactContext, + RtlRXactOperationSetValue, + &KeyName, + ObjectRid, + (PVOID)NULL, + 0 + ); + + SampFreeUnicodeString( &KeyName ); + } + } + + + + + // + // replace the account's name + // + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampSetUnicodeStringAttribute( + Context, + SAMP_USER_ACCOUNT_NAME, + NewAccountName + ); + } + + // + // Free up the old account name if we failed + // + + if (!NT_SUCCESS(NtStatus)) { + + SampFreeUnicodeString( OldAccountName ); + OldAccountName->Buffer = NULL; + } + + } + + + return(NtStatus); +} + + +USHORT +SampQueryBadPasswordCount( + PSAMP_OBJECT UserContext, + PSAMP_V1_0A_FIXED_LENGTH_USER V1aFixed + ) + +/*++ + +Routine Description: + + This routine is used to retrieve the effective BadPasswordCount + value of a user. + + When querying BadPasswordCount, some quick + analysis has to be done. If the last bad password + was set more than LockoutObservationWindow time ago, + then we re-set the BadPasswordCount. Otherwise, we + return the current value. + + + NOTE: The V1aFixed data for the user object MUST be valid. + This routine does not retrieve the data from disk. + +Arguments: + + UserContext - Points to the object context block of the user whose + bad password count is to be returned. + + V1aFixed - Points to a local copy of the user's V1aFixed data. + + +Return Value: + + + The effective bad password count. + + +--*/ +{ + + SAMTRACE("SampQueryBadPasswordCount"); + + if (SampStillInLockoutObservationWindow( UserContext, V1aFixed ) ) { + return(V1aFixed->BadPasswordCount); + } + + return(0); + +} + + +BOOLEAN +SampStillInLockoutObservationWindow( + PSAMP_OBJECT UserContext, + PSAMP_V1_0A_FIXED_LENGTH_USER V1aFixed + ) +/*++ + +Routine Description: + + This routine returns a boolean indicating whether the provided user + account context is within an account lockout window or not. + + An account lockout window is the time window starting at the + last time a bad password was provided in a logon attempt + (since the last valid logon) and extending for the duration of + time specified in the LockoutObservationWindow field of the + corresponding domain object. + + BY DEFINITION, a user account that has zero bad passwords, is + NOT in an observation window. + + NOTE: The V1aFixed data for the both the user and corresponding + domain objects MUST be valid. This routine does NOT retrieve + data from disk. + +Arguments: + + UserContext - Points to the user object context block. + + V1aFixed - Points to a local copy of the user's V1aFixed data. + + +Return Value: + + + TRUE - the user is in a lockout observation window. + + FALSE - the user is not in a lockout observation window. + + +--*/ +{ + NTSTATUS + NtStatus; + + LARGE_INTEGER + WindowLength, + LastBadPassword, + CurrentTime, + EndOfWindow; + + SAMTRACE("SampStillInLockoutObservationWindow"); + + + if (V1aFixed->BadPasswordCount == 0) { + return(FALSE); + } + + // + // At least one bad password. + // See if we are still in its observation window. + // + + LastBadPassword = V1aFixed->LastBadPasswordTime; + ASSERT( LastBadPassword.HighPart >= 0 ); + + WindowLength = + SampDefinedDomains[UserContext->DomainIndex].CurrentFixed.LockoutObservationWindow; + ASSERT( WindowLength.HighPart <= 0 ); // Must be a delta time + + + NtStatus = NtQuerySystemTime( &CurrentTime ); + ASSERT(NT_SUCCESS(NtStatus)); + + // + // See if current time is outside the observation window. + // * you must subtract a delta time from an absolute time* + // * to end up with a time in the future. * + // + + EndOfWindow = SampAddDeltaTime( LastBadPassword, WindowLength ); + + return(CurrentTime.QuadPart <= EndOfWindow.QuadPart); + +} + + +BOOLEAN +SampIncrementBadPasswordCount( + PSAMP_OBJECT UserContext, + PSAMP_V1_0A_FIXED_LENGTH_USER V1aFixed + ) + +/*++ + +Routine Description: + + This routine increments a user's bad password count. + This may result in the account becoming locked out. + It may also result in the BadPasswordCount being + reduced (because we left one LockoutObservationWindow + and had to start another). + + If (and only if) this call results in the user account + transitioning from not locked out to locked out, a value + of TRUE will be returned. Otherwise, a value of FALSE is + returned. + + + NOTE: The V1aFixed data for the both the user and corresponding + domain objects MUST be valid. This routine does NOT retrieve + data from disk. + +Arguments: + + Context - Points to the user object context block. + + V1aFixed - Points to a local copy of the user's V1aFixed data. + +Return Value: + + + TRUE - the user became locked-out due to this call. + + FALSE - the user was either already locked-out, or did + not become locked out due to this call. + + +--*/ +{ + NTSTATUS + NtStatus; + + BOOLEAN + IsLocked, + WasLocked; + +#if DBG + + TIME_FIELDS + T1; + +#endif //DBG + + SAMTRACE("SampIncrementBadPasswordCount"); + + SampDiagPrint( DISPLAY_LOCKOUT, + ("SAM: IncrementBadPasswordCount: \n" + " User Account: 0x%lx\n", + V1aFixed->UserId)); + + // + // Reset the locked out flag if necessary. + // We might turn right around and set it again below, + // but we need to know when we transition into a locked-out + // state. This is necessary to give us information we + // need to do lockout auditing at some time. Note that + // the lockout flag itself is updated in a very lazy fashion, + // and so its state may or may not be accurate at any point + // in time. You must call SampUpdateAccountLockoutFlag to + // ensure it is up to date. + // + + SampUpdateAccountLockedOutFlag( UserContext, + V1aFixed, + &WasLocked ); + + // + // If we are not in a lockout observation window, then + // reset the bad password count. + // + + if (!SampStillInLockoutObservationWindow( UserContext, V1aFixed )) { + SampDiagPrint( DISPLAY_LOCKOUT, + ("SAM: IncrementBadPasswordCount: \n" + " Starting new observation window.\n" + " Resetting bad password count before increment.\n")); + V1aFixed->BadPasswordCount = 0; // Dirty flag will be set later + } + + V1aFixed->BadPasswordCount++; + + NtStatus = NtQuerySystemTime( &V1aFixed->LastBadPasswordTime ); + ASSERT(NT_SUCCESS(NtStatus)); + +#if DBG + RtlTimeToTimeFields( + &V1aFixed->LastBadPasswordTime, + &T1); + + SampDiagPrint( DISPLAY_LOCKOUT, + (" LastBadPasswordTime: [0x%lx, 0x%lx] %d:%d:%d\n", + V1aFixed->LastBadPasswordTime.HighPart, + V1aFixed->LastBadPasswordTime.LowPart, + T1.Hour, T1.Minute, T1.Second ) + ); +#endif //DBG + + + // + // Update the state of the flag to reflect its new situation + // + + SampUpdateAccountLockedOutFlag( UserContext, + V1aFixed, + &IsLocked ); + + + // + // Now to return our completion value. + // If the user was originally not locked, but now is locked + // then we need to return TRUE to indicate a transition into + // LOCKED occured. Otherwise, return false to indicate we + // did not transition into LOCKED (although we might have + // transitioned out of LOCKED). + // + + if (!WasLocked) { + if (IsLocked) { + return(TRUE); + } + } + + return(FALSE); +} + + + +VOID +SampUpdateAccountLockedOutFlag( + PSAMP_OBJECT Context, + PSAMP_V1_0A_FIXED_LENGTH_USER V1aFixed, + PBOOLEAN IsLocked + ) + +/*++ + +Routine Description: + + This routine checks to see if a user's account should + currently be locked out. If it should, it turns on + the AccountLockedOut flag. If not, it turns the flag + off. + + +Arguments: + + Context - Points to the user object context block. + + V1aFixed - Points to a local copy of the user's V1aFixed data. + + V1aFixedDirty - If any changes are made to V1aFixed, then + V1aFixedDirty will be set to TRUE, otherwise V1aFixedDirty + WILL NOT BE MODIFIED. + + IsState - Indicates whether the account is currently locked + or unlocked. A value of TRUE indicates the account is + locked. A value of false indicates the account is not + locked. + +Return Value: + + + TRUE - the user's lockout status changed. + + FALSE - the user's lockout status did not change. + + +--*/ +{ + USHORT + Threshold; + + LARGE_INTEGER + CurrentTime, + LastBadPassword, + LockoutDuration, + EndOfLockout; + + BOOLEAN + BeyondLockoutDuration; + +#if DBG + + LARGE_INTEGER + TmpTime; + + TIME_FIELDS + AT1, AT2, AT3, DT1; +#endif //DBG + + + + SAMTRACE("SampUpdateAccountLockedOutFlag"); + + + + SampDiagPrint( DISPLAY_LOCKOUT, + ("SAM: UpdateAccountLockedOutFlag: \n" + " User account 0x%lx\n", + V1aFixed->UserId)); + + // + // One of two situations exist: + // + // 1) The account was left in a locked out state. In this + // case we need to see if it should still be locked + // out. + // + // 2) The account was left in a not locked state. In this + // case we need to see if we should lock it. + // + + if ((V1aFixed->UserAccountControl & USER_ACCOUNT_AUTO_LOCKED) !=0) { + + // + // Left locked out - do we need to unlock it? + // + + LastBadPassword = V1aFixed->LastBadPasswordTime; + LockoutDuration = + SampDefinedDomains[Context->DomainIndex].CurrentFixed.LockoutDuration; + + EndOfLockout = + SampAddDeltaTime( LastBadPassword, LockoutDuration ); + + NtQuerySystemTime( &CurrentTime ); + + BeyondLockoutDuration = CurrentTime.QuadPart > EndOfLockout.QuadPart; + +#if DBG + + RtlTimeToTimeFields( &LastBadPassword, &AT1); + RtlTimeToTimeFields( &CurrentTime, &AT2); + RtlTimeToTimeFields( &EndOfLockout, &AT3 ); + + TmpTime.QuadPart = -LockoutDuration.QuadPart; + RtlTimeToElapsedTimeFields( &TmpTime, &DT1 ); + + SampDiagPrint( DISPLAY_LOCKOUT, + (" Account previously locked.\n" + " Current Time : [0x%lx, 0x%lx] %d:%d:%d\n" + " End of Lockout : [0x%lx, 0x%lx] %d:%d:%d\n" + " Lockout Duration : [0x%lx, 0x%lx] %d:%d:%d\n" + " LastBadPasswordTime: [0x%lx, 0x%lx] %d:%d:%d\n", + CurrentTime.HighPart, CurrentTime.LowPart, AT2.Hour, AT2.Minute, AT2.Second, + EndOfLockout.HighPart, EndOfLockout.LowPart, AT3.Hour, AT3.Minute, AT3.Second, + LockoutDuration.HighPart, LockoutDuration.LowPart, DT1.Hour, DT1.Minute, DT1.Second, + V1aFixed->LastBadPasswordTime.HighPart, V1aFixed->LastBadPasswordTime.LowPart, + AT1.Hour, AT1.Minute, AT1.Second) + ); +#endif //DBG + + if (BeyondLockoutDuration) { + + // + // Unlock account + // + + V1aFixed->UserAccountControl &= ~USER_ACCOUNT_AUTO_LOCKED; + V1aFixed->BadPasswordCount = 0; + + + SampDiagPrint( DISPLAY_LOCKOUT, + (" ** unlocking account **\n") ); +#if DBG + } else { + SampDiagPrint( DISPLAY_LOCKOUT, + (" leaving account locked\n") ); +#endif //DBG + + } + + } else { + + SampDiagPrint( DISPLAY_LOCKOUT, + (" Account previously not locked.\n" + " BadPasswordCount: %ld\n", + V1aFixed->BadPasswordCount) ); + + // + // Left in a not locked state. Do we need to lock it? + // + + Threshold = + SampDefinedDomains[Context->DomainIndex].CurrentFixed.LockoutThreshold; + + if (V1aFixed->BadPasswordCount >= Threshold && + Threshold != 0) { // Zero is a special case threshold + + // + // account must be locked. + // + + V1aFixed->UserAccountControl |= USER_ACCOUNT_AUTO_LOCKED; + + + SampDiagPrint( DISPLAY_LOCKOUT, + (" ** locking account **\n") ); +#if DBG + } else { + SampDiagPrint( DISPLAY_LOCKOUT, + (" leaving account unlocked\n") ); +#endif //DBG + + } + } + + + // + // Now return the state of the flag. + // + + if ((V1aFixed->UserAccountControl & USER_ACCOUNT_AUTO_LOCKED) !=0) { + (*IsLocked) = TRUE; + } else { + (*IsLocked) = FALSE; + } + + return; +} diff --git a/private/newsam2/server/utest/alsops.cxx b/private/newsam2/server/utest/alsops.cxx new file mode 100644 index 000000000..5711d8104 --- /dev/null +++ b/private/newsam2/server/utest/alsops.cxx @@ -0,0 +1,2033 @@ +//+----------------------------------------------------------------------- +// +// Microsoft Windows +// +// Copyright (c) Microsoft Corporation 1992 - 1992 +// +// File: secret.cxx +// +// Contents: test program to check the setup of a Cairo installation +// +// +// History: 22-Dec-92 Created MikeSw +// +//------------------------------------------------------------------------ + + +extern "C" +{ +#include <nt.h> +#include <ntrtl.h> +#include <nturtl.h> +#include <ntsam.h> +#include <ntlsa.h> +#include <windows.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <caiseapi.h> +} + +typedef NTSTATUS (TestFunc)( WCHAR *Parameter[]); + +typedef struct _Commands { + PSTR Name; + ULONG Parameter; // TRUE = yes, FALSE = no + TestFunc *Function; +} CommandPair, *PCommandPair; + + +typedef struct _Action { + ULONG CommandNumber; + LPWSTR Parameter[8]; +} Action, *PAction; + + + +TestFunc OpenDomain; +TestFunc EnumDomains; +TestFunc EnumAccounts; +TestFunc QueryDisplay; +TestFunc OpenGroup; +TestFunc GroupMembers; +TestFunc OpenAlias; +TestFunc AliasMembers; +TestFunc GetAliasMembership; +TestFunc OpenUser; +TestFunc GetGroupsForUser; +TestFunc DumpAllUsers; +TestFunc DumpAllGroups; +TestFunc DumpUser; +TestFunc DumpGroup; +TestFunc CreateUser; +TestFunc AddAliasMember; +TestFunc CreateGroup; +TestFunc CreateAlias; +TestFunc DumpDomain; +TestFunc Connect; +TestFunc DelUser; +TestFunc DelAlias; +TestFunc DelGroup; + +CommandPair Commands[] = { + {"-od",1,OpenDomain}, + {"-ed",0,EnumDomains}, + {"-ea",1,EnumAccounts}, + {"-qd",1,QueryDisplay}, + {"-og",1,OpenGroup}, + {"-gm",0,GroupMembers}, + {"-oa",1,OpenAlias}, + {"-am",0,AliasMembers}, + {"-gam",1,GetAliasMembership}, + {"-ou",1,OpenUser}, + {"-ggu",0,GetGroupsForUser}, + {"-dau",0,DumpAllUsers}, + {"-dag",0,DumpAllGroups}, + {"-du",0,DumpUser}, + {"-dg",0,DumpGroup}, + {"-cu",1,CreateUser}, + {"-aam",1,AddAliasMember}, + {"-cg",1,CreateGroup}, + {"-ca",1,CreateAlias}, + {"-dd",0,DumpDomain}, + {"-c",1,Connect}, + {"-delu",0,DelUser}, + {"-dela",0,DelAlias}, + {"-delg",0,DelGroup} + + + +}; + +#define NUM_COMMANDS (sizeof(Commands) / sizeof(CommandPair)) + +SAM_HANDLE SamHandle; +SAM_HANDLE DomainHandle; +SAM_HANDLE GroupHandle; +SAM_HANDLE AliasHandle; +SAM_HANDLE UserHandle; + +UNICODE_STRING ServerName; + +//+------------------------------------------------------------------------- +// +// Function: PrintTime +// +// Synopsis: Prints a text representation of a FILETIME interpreted +// as an absolute date/time +// +// Arguments: [rft] -- time to print +// +// Notes: Used only by dump functions. +// +//-------------------------------------------------------------------------- + +void +PrintTime ( + char * String, + PVOID Time + ) +{ + SYSTEMTIME st; + + FileTimeToSystemTime ( (PFILETIME) Time, & st ); + printf("%s %d-%d-%d %d:%2.2d:%2.2d\n", String, st.wMonth, st.wDay, st.wYear, + st.wHour, st.wMinute, st.wSecond ); +} + +//+------------------------------------------------------------------------- +// +// Function: SpmDbDeltaTimeToString +// +// Synopsis: Converts a time delta to a string. +// +// Effects: +// +// Arguments: +// +// Requires: +// +// Returns: +// +// Notes: +// +// +//-------------------------------------------------------------------------- + + +VOID +PrintDeltaTime( + IN LPSTR Message, + IN PLARGE_INTEGER Time + ) +{ + ULONG Seconds; + ULONG Minutes; + ULONG Hours; + ULONG Days; + ULONG Chars; + CHAR TimeBuffer[256] = ""; + LPSTR TimeString = TimeBuffer; + LARGE_INTEGER DeltaTime; + + DeltaTime.QuadPart = -Time->QuadPart; + + Seconds = (ULONG) (DeltaTime.QuadPart / 10000000); + + Minutes = Seconds / 60; + Hours = Minutes / 60; + Days = Hours / 24; + + Hours = Hours % 24; + Minutes = Minutes % 60; + Seconds = Seconds % 60; + + if (Days >= 1) + { + Chars = sprintf(TimeString,"%d days ",Days); + TimeString += Chars; + } + if (Hours >= 1 ) + { + Chars = sprintf(TimeString,"%d hours ",Hours); + TimeString += Chars; + } + + if (Minutes >= 1 && Days == 0) + { + Chars = sprintf(TimeString,"%d minutes ",Minutes); + TimeString += Chars; + } + + if (Seconds >= 1 && (Days == 0) && (Hours == 0) ) + { + Chars = sprintf(TimeString,"%d seconds ",Seconds); + TimeString += Chars; + } + + printf("%s %s\n",Message,TimeBuffer); + +} + + +NTSTATUS +Connect( LPWSTR * Parameter) +{ + OBJECT_ATTRIBUTES oa; + NTSTATUS Status; + + RtlInitUnicodeString( + &ServerName, + Parameter[0] + ); + + InitializeObjectAttributes(&oa,NULL,0,NULL,NULL); + + Status = SamConnect( + &ServerName, + &SamHandle, + MAXIMUM_ALLOWED, + &oa); + return(Status); +} + +NTSTATUS +CloseSam() +{ + return(SamCloseHandle(SamHandle)); +} + + + +NTSTATUS +EnumDomains( + LPWSTR * Parameter ) +{ + NTSTATUS Status; + SHORT Language; + SAM_ENUMERATE_HANDLE Context = 0; + PSAM_RID_ENUMERATION Buffer = NULL; + ULONG Count = 0; + ULONG i; + + Status = SamEnumerateDomainsInSamServer( + SamHandle, + &Context, + (PVOID *) &Buffer, + 2000, + &Count + ); + + if (!NT_SUCCESS(Status)) + { + return(Status); + } + + + for (i = 0; i < Count ; i++ ) + { + printf("Domain = %wZ\n",&Buffer[i].Name); + } + SamFreeMemory(Buffer); + return(STATUS_SUCCESS); +} + +NTSTATUS +OpenDomain( LPWSTR * Parameter ) +{ + GUID DomainGuid; + BOOLEAN fBuiltin; + NTSTATUS Status; + CAIROSID DomainSid; + SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; + + if (!_wcsicmp(Parameter[0],L"Builtin")) + { + fBuiltin = TRUE; + } + else if (!_wcsicmp(Parameter[0],L"Account")) + { + fBuiltin = FALSE; + } + else + { + printf("Invalid domain to open: %ws\n",Parameter[0]); + return(STATUS_UNSUCCESSFUL); + } + + if (fBuiltin) + { + DomainSid.Revision = SID_REVISION; + DomainSid.SubAuthorityCount = 1; + DomainSid.IdentifierAuthority = NtAuthority; + DomainSid.ZerothSubAuthority = SECURITY_BUILTIN_DOMAIN_RID; + } + else + { + LSA_HANDLE LsaHandle = NULL; + OBJECT_ATTRIBUTES Oa; + PPOLICY_ACCOUNT_DOMAIN_INFO DomainInfo = NULL; + + RtlZeroMemory(&Oa, sizeof(OBJECT_ATTRIBUTES)); + Status = LsaOpenPolicy( + &ServerName, + &Oa, + POLICY_VIEW_LOCAL_INFORMATION, + &LsaHandle + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to open policy: 0x%x\n",Status); + return(Status); + } + Status = LsaQueryInformationPolicy( + LsaHandle, + PolicyAccountDomainInformation, + (PVOID *) &DomainInfo + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query account domain: 0x%x\n",Status); + LsaClose(LsaHandle); + return(Status); + } + RtlCopyMemory( + &DomainSid, + DomainInfo->DomainSid, + RtlLengthSid(DomainInfo->DomainSid) + ); + LsaFreeMemory(DomainInfo); + } + + Status = SamOpenDomain( + SamHandle, + MAXIMUM_ALLOWED, + (PSID) &DomainSid, + &DomainHandle + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to open domain: 0x%x\n",Status); + } + return(Status); + +} + +NTSTATUS +EnumAccounts( LPWSTR * Parameter ) +{ + ULONG PreferedMax = 100; + NTSTATUS Status; + SAM_ENUMERATE_HANDLE EnumContext = 0; + ULONG CountReturned; + + PSAM_RID_ENUMERATION Accounts = NULL; + + swscanf(Parameter[0],L"%d",&PreferedMax); + + printf("EnumAccounts: %d\n",PreferedMax); + + + EnumContext = 0; + ASSERT(DomainHandle != NULL); + do + { + Status = SamEnumerateUsersInDomain( + DomainHandle, + &EnumContext, + 0, + (PVOID *) &Accounts, + PreferedMax, + &CountReturned + ); + + if (NT_SUCCESS(Status) && (Status != STATUS_NO_MORE_ENTRIES)) + { + ULONG Index; + UNICODE_STRING SidString; + + for (Index = 0; Index < CountReturned; Index++) + { + printf("Account : %wZ 0x%x\n",&Accounts[Index].Name, Accounts[Index].RelativeId); + } + SamFreeMemory(Accounts); + } + else printf("Failed to enumerate users: 0x%x\n",Status); + } while (NT_SUCCESS(Status) && (Status != STATUS_SUCCESS) && (CountReturned != 0) ); + + EnumContext = 0; + do + { + Status = SamEnumerateGroupsInDomain( + DomainHandle, + &EnumContext, + (PVOID *) &Accounts, + PreferedMax, + &CountReturned + ); + + if (NT_SUCCESS(Status) && (Status != STATUS_NO_MORE_ENTRIES)) + { + ULONG Index; + UNICODE_STRING SidString; + + for (Index = 0; Index < CountReturned; Index++) + { + printf("Group : %wZ 0x%x\n",&Accounts[Index].Name, Accounts[Index].RelativeId); + } + SamFreeMemory(Accounts); + } + else printf("Failed to enumerate Groups: 0x%x\n",Status); + } while (NT_SUCCESS(Status) && (CountReturned != 0) ); // && (Status != STATUS_SUCCESS) + + + EnumContext = 0; + do + { + Status = SamEnumerateAliasesInDomain( + DomainHandle, + &EnumContext, + (PVOID *) &Accounts, + PreferedMax, + &CountReturned + ); + + if (NT_SUCCESS(Status) && (Status != STATUS_NO_MORE_ENTRIES)) + { + ULONG Index; + UNICODE_STRING SidString; + + for (Index = 0; Index < CountReturned; Index++) + { + printf("Alias : %wZ 0x%x\n",&Accounts[Index].Name, Accounts[Index].RelativeId); + } + SamFreeMemory(Accounts); + } + else printf("Failed to enumerate aliases: 0x%x\n",Status); + } while (NT_SUCCESS(Status) && (CountReturned != 0) ); // && (Status != STATUS_SUCCESS) + + + + return(Status); +} + + +NTSTATUS +QueryDisplay( LPWSTR * Parameter ) +{ + NTSTATUS Status; + DOMAIN_DISPLAY_INFORMATION Type; + PVOID Buffer = NULL; + ULONG TotalAvailable = 0; + ULONG TotalReturned = 0; + ULONG ReturnedCount = 0; + ULONG Index; + ULONG SamIndex = 0; + + if (!_wcsicmp(Parameter[0],L"user")) + { + Type = DomainDisplayUser; + } else if (!_wcsicmp(Parameter[0],L"Machine")) + { + Type = DomainDisplayMachine; + } else if (!_wcsicmp(Parameter[0],L"Group")) + { + Type = DomainDisplayGroup; + } else if (!_wcsicmp(Parameter[0],L"OemUser")) + { + Type = DomainDisplayOemUser; + } else if (!_wcsicmp(Parameter[0],L"OemGroup")) + { + Type = DomainDisplayOemGroup; + } else { + printf("Invalid parameter %ws\n", Parameter[0]); + return(STATUS_INVALID_PARAMETER); + } + + do + { + Status = SamQueryDisplayInformation( + DomainHandle, + Type, + SamIndex, + 5, + 1000, + &TotalAvailable, + &TotalReturned, + &ReturnedCount, + &Buffer + ); + + if (NT_SUCCESS(Status) && (ReturnedCount > 0)) + { + printf("Total returned = %d\t total available = %d\n", + TotalReturned, TotalAvailable); + switch(Type) { + case DomainDisplayUser: + { + PDOMAIN_DISPLAY_USER Users = (PDOMAIN_DISPLAY_USER) Buffer; + for (Index = 0; Index < ReturnedCount ; Index++ ) + { + printf("User %d: Index %d\n Rid 0x%x\n Control 0x%x\n name %wZ\n Comment %wZ\n Full Name %wZ\n", + Index, + Users[Index].Index, + Users[Index].Rid, + Users[Index].AccountControl, + &Users[Index].LogonName, + &Users[Index].AdminComment, + &Users[Index].FullName + ); + } + break; + } + case DomainDisplayGroup: + { + PDOMAIN_DISPLAY_GROUP Groups = (PDOMAIN_DISPLAY_GROUP) Buffer; + for (Index = 0; Index < ReturnedCount ; Index++ ) + { + printf("Group %d\n Index %d\n Rid 0x%x\n Attributes 0x%x\n name %wZ\n Comment %wZ\n", + Index, + Groups[Index].Index, + Groups[Index].Rid, + Groups[Index].Attributes, + &Groups[Index].Group, + &Groups[Index].Comment + ); + + } + break; + } + case DomainDisplayMachine: + { + PDOMAIN_DISPLAY_MACHINE Machines = (PDOMAIN_DISPLAY_MACHINE) Buffer; + for (Index = 0; Index < ReturnedCount ; Index++ ) + { + printf("Machine %d\n Index %d\n Rid 0x%x\n Control 0x%x\n Name %wZ\n Comment %wZ\n", + Index, + Machines[Index].Index, + Machines[Index].Rid, + Machines[Index].AccountControl, + &Machines[Index].Machine, + &Machines[Index].Comment + ); + } + break; + } + case DomainDisplayOemUser: + { + PDOMAIN_DISPLAY_OEM_USER OemUsers = (PDOMAIN_DISPLAY_OEM_USER) Buffer; + for (Index = 0; Index < ReturnedCount ; Index++ ) + { + printf("OemUser %d\n Index %d\n Name %Z\n", + Index, + OemUsers[Index].Index, + &OemUsers[Index].User + ); + } + break; + } + case DomainDisplayOemGroup: + { + PDOMAIN_DISPLAY_OEM_GROUP OemGroups = (PDOMAIN_DISPLAY_OEM_GROUP) Buffer; + for (Index = 0; Index < ReturnedCount ; Index++ ) + { + printf("OemGroup %d\n Index %d\n Name %Z\n", + Index, + OemGroups[Index].Index, + &OemGroups[Index].Group + ); + } + break; + } + + } + SamFreeMemory(Buffer); + SamIndex += ReturnedCount; + } + + + } while (NT_SUCCESS(Status) && (ReturnedCount > 0)); + printf("QDI returned 0x%x\n",Status); + + return(Status); + + +} + +NTSTATUS +OpenGroup( LPWSTR * Parameter) +{ + PSID_NAME_USE Use = NULL; + PULONG Rid = NULL; + NTSTATUS Status; + UNICODE_STRING GroupName; + ULONG RelativeId = 0; + +// swscanf(Parameter[0],L"%x",&RelativeId); + if (RelativeId == 0) + { + RtlInitUnicodeString( + &GroupName, + Parameter[0] + ); + + printf("Looking up group %wZ\n",&GroupName); + + Status = SamLookupNamesInDomain( + DomainHandle, + 1, + &GroupName, + &Rid, + &Use + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to lookup group: 0x%x\n",Status); + return(Status); + } + RelativeId = *Rid; + SamFreeMemory(Rid); + SamFreeMemory(Use); + } + + printf("Opening Group 0x%x\n",RelativeId); + Status= SamOpenGroup( + DomainHandle, + MAXIMUM_ALLOWED, // GROUP_LIST_MEMBERS | GROUP_READ_INFORMATION, + RelativeId, + &GroupHandle + ); + + if (!NT_SUCCESS(Status)) + { + printf("Failed to open group: 0x%x\n",Status); + } + return(Status); +} + +NTSTATUS +GroupMembers(LPWSTR * Parameter) +{ + NTSTATUS Status; + ULONG MembershipCount; + PULONG Attributes = NULL; + PULONG Rids = NULL; + ULONG Index; + + Status = SamGetMembersInGroup( + GroupHandle, + &Rids, + &Attributes, + &MembershipCount + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get members in group: 0x%x\n",Status); + return(Status); + } + + for (Index = 0; Index < MembershipCount ; Index++ ) + { + printf("Member %d: rid 0x%x, attributes 0x%x\n", + Index,Rids[Index],Attributes[Index]); + } + SamFreeMemory(Rids); + SamFreeMemory(Attributes); + return(STATUS_SUCCESS); + +} + + +NTSTATUS +OpenAlias( LPWSTR * Parameter) +{ + PSID_NAME_USE Use = NULL; + PULONG Rid = NULL; + NTSTATUS Status; + UNICODE_STRING AliasName; + ULONG RelativeId; + + swscanf(Parameter[0],L"%x",&RelativeId); + if (RelativeId == 0) + { + RtlInitUnicodeString( + &AliasName, + Parameter[0] + ); + + printf("Looking up Alias %wZ\n",&AliasName); + + Status = SamLookupNamesInDomain( + DomainHandle, + 1, + &AliasName, + &Rid, + &Use + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to lookup Alias: 0x%x\n",Status); + return(Status); + } + RelativeId = *Rid; + SamFreeMemory(Rid); + SamFreeMemory(Use); + } + + + printf("Opening Alias 0x%x\n",RelativeId); + Status= SamOpenAlias( + DomainHandle, + ALIAS_LIST_MEMBERS | ALIAS_ADD_MEMBER, + RelativeId, + &AliasHandle + ); + + if (!NT_SUCCESS(Status)) + { + printf("Failed to open alias: 0x%x\n",Status); + } + return(Status); +} + +NTSTATUS +AliasMembers(LPWSTR * Parameter) +{ + NTSTATUS Status; + ULONG MembershipCount; + PSID * Members = NULL; + ULONG Index; + UNICODE_STRING Sid; + + Status = SamGetMembersInAlias( + AliasHandle, + &Members, + &MembershipCount + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get members in Alias: 0x%x\n",Status); + return(Status); + } + + for (Index = 0; Index < MembershipCount ; Index++ ) + { + RtlConvertSidToUnicodeString( + &Sid, + Members[Index], + TRUE + ); + printf("Member %d: sid %wZ\n", + Index,&Sid); + RtlFreeUnicodeString(&Sid); + } + SamFreeMemory(Members); + return(STATUS_SUCCESS); + +} + + +NTSTATUS +GetAliasMembership(LPWSTR * Parameter) +{ + NTSTATUS Status; + ULONG Index; + UNICODE_STRING Name; + OBJECT_ATTRIBUTES Oa; + LSA_HANDLE LsaHandle = NULL; + CAIROSID Sid; + ULONG SidLength = sizeof(CAIROSID); + WCHAR ReferencedDomainName[100]; + ULONG DomainNameLength = 100; + SID_NAME_USE SidUse; + ULONG MembershipCount; + PULONG AliasList = NULL; + PSID SidAddress = (PSID) &Sid; + + + + printf("Looking up groups for user %ws\n",Parameter[0]); + + if (!LookupAccountNameW( + NULL, + Parameter[0], + SidAddress, + &SidLength, + ReferencedDomainName, + &DomainNameLength, + &SidUse)) + { + printf("Failed to lookup account sid: %d\n",GetLastError()); + return(STATUS_UNSUCCESSFUL); + } + + + + Status = SamGetAliasMembership( + DomainHandle, + 1, + &SidAddress, + &MembershipCount, + &AliasList + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get alises : 0x%x\n",Status); + return(Status); + } + + for (Index = 0; Index < MembershipCount ; Index++ ) + { + printf("Alias Member %d: rid 0x%x\n", + Index,AliasList[Index]); + + } + SamFreeMemory(AliasList); + return(STATUS_SUCCESS); + +} + +NTSTATUS +GetAccountRid( LPWSTR Parameter, + PULONG RelativeId) +{ + + PSID_NAME_USE Use = NULL; + PULONG Rid = NULL; + NTSTATUS Status; + UNICODE_STRING UserName; + + RtlInitUnicodeString( + &UserName, + Parameter + ); + + printf("Looking up User %wZ\n",&UserName); + + Status = SamLookupNamesInDomain( + DomainHandle, + 1, + &UserName, + &Rid, + &Use + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to lookup User: 0x%x\n",Status); + return(Status); + } + *RelativeId = *Rid; + SamFreeMemory(Rid); + SamFreeMemory(Use); + + return(STATUS_SUCCESS); +} +NTSTATUS +OpenUser( LPWSTR * Parameter) +{ + PSID_NAME_USE Use = NULL; + PULONG Rid = NULL; + NTSTATUS Status; + UNICODE_STRING UserName; + ULONG RelativeId = 0; + + Status = GetAccountRid(Parameter[0],&RelativeId); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get account rid: 0x%x\n",Status); + return(Status); + } + + printf("Opening User 0x%x\n",RelativeId); + Status= SamOpenUser( + DomainHandle, + MAXIMUM_ALLOWED, + RelativeId, + &UserHandle + ); + + if (!NT_SUCCESS(Status)) + { + printf("Failed to open User: 0x%x\n",Status); + } + return(Status); +} + +NTSTATUS +DelUser( LPWSTR * Parameter) +{ + NTSTATUS Status; + + Status = SamDeleteUser(UserHandle); + if (!NT_SUCCESS(Status)) + { + printf("Failed to delete user: 0x%x\n",Status); + } + return(Status); +} + +NTSTATUS +DelGroup( LPWSTR * Parameter) +{ + NTSTATUS Status; + + Status = SamDeleteGroup(GroupHandle); + if (!NT_SUCCESS(Status)) + { + printf("Failed to delete user: 0x%x\n",Status); + } + return(Status); +} + +NTSTATUS +DelAlias( LPWSTR * Parameter) +{ + NTSTATUS Status; + + Status = SamDeleteAlias(AliasHandle); + if (!NT_SUCCESS(Status)) + { + printf("Failed to delete Alias: 0x%x\n",Status); + } + return(Status); +} + +NTSTATUS +CreateUser( LPWSTR * Parameter) +{ + PSID_NAME_USE Use = NULL; + PULONG Rid = NULL; + NTSTATUS Status; + UNICODE_STRING UserName; + ULONG RelativeId = 0; + ACCESS_MASK GrantedAccess; + + + RtlInitUnicodeString( + &UserName, + Parameter[0] + ); + + printf("Creating User %wZ\n",&UserName); + + Status= SamCreateUser2InDomain( + DomainHandle, + &UserName, + USER_NORMAL_ACCOUNT, + MAXIMUM_ALLOWED, + &UserHandle, + &GrantedAccess, + &RelativeId + ); + + if (!NT_SUCCESS(Status)) + { + printf("Failed to create User: 0x%x\n",Status); + return(Status); + } + printf("Created user with rid 0x%x, access 0x%x\n", + RelativeId, GrantedAccess); + return(Status); +} + +NTSTATUS +CreateGroup( LPWSTR * Parameter) +{ + PSID_NAME_USE Use = NULL; + PULONG Rid = NULL; + NTSTATUS Status; + UNICODE_STRING GroupName; + ULONG RelativeId = 0; + + + RtlInitUnicodeString( + &GroupName, + Parameter[0] + ); + + printf("Creating Group %wZ\n",&GroupName); + + Status= SamCreateGroupInDomain( + DomainHandle, + &GroupName, + MAXIMUM_ALLOWED, + &GroupHandle, + &RelativeId + ); + + if (!NT_SUCCESS(Status)) + { + printf("Failed to create Group: 0x%x\n",Status); + return(Status); + } + printf("Created Group with rid 0x%x, access 0x%x\n", + RelativeId); + return(Status); +} + +NTSTATUS +CreateAlias( LPWSTR * Parameter) +{ + PSID_NAME_USE Use = NULL; + PULONG Rid = NULL; + NTSTATUS Status; + UNICODE_STRING AliasName; + ULONG RelativeId = 0; + + + RtlInitUnicodeString( + &AliasName, + Parameter[0] + ); + + printf("Creating Alias %wZ\n",&AliasName); + + Status= SamCreateAliasInDomain( + DomainHandle, + &AliasName, + MAXIMUM_ALLOWED, + &AliasHandle, + &RelativeId + ); + + if (!NT_SUCCESS(Status)) + { + printf("Failed to create Alias: 0x%x\n",Status); + return(Status); + } + printf("Created Alias with rid 0x%x, access 0x%x\n", + RelativeId); + return(Status); +} + + + +NTSTATUS +GetGroupsForUser(LPWSTR * Parameter) +{ + NTSTATUS Status; + ULONG MembershipCount; + PULONG Attributes = NULL; + PULONG Rids = NULL; + ULONG Index; + PGROUP_MEMBERSHIP Groups = NULL; + + Status = SamGetGroupsForUser( + UserHandle, + &Groups, + &MembershipCount + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get groups for user: 0x%x\n",Status); + return(Status); + } + + for (Index = 0; Index < MembershipCount ; Index++ ) + { + printf("Member %d: rid 0x%x, attributes 0x%x\n", + Index,Groups[Index].RelativeId, Groups[Index].Attributes ); + } + SamFreeMemory(Groups); + return(STATUS_SUCCESS); + +} + +void +PrintLogonHours( + char * String, + PLOGON_HOURS LogonHours + ) +{ + int Index; + printf("%s",String); + for (Index = 0; Index < (LogonHours->UnitsPerWeek + 7) / 8 ;Index++ ) + { + printf("0x%2.2x ",LogonHours->LogonHours[Index]); + } + printf("\n"); +} + + +NTSTATUS +DumpUser(LPWSTR * Parameter) +{ + NTSTATUS Status; + PUSER_ALL_INFORMATION UserAll = NULL; + PUSER_GENERAL_INFORMATION UserGeneral = NULL; + PUSER_PREFERENCES_INFORMATION UserPreferences = NULL; + PUSER_LOGON_INFORMATION UserLogon = NULL; + PUSER_ACCOUNT_INFORMATION UserAccount = NULL; + PUSER_ACCOUNT_NAME_INFORMATION UserAccountName = NULL; + PUSER_FULL_NAME_INFORMATION UserFullName = NULL; + PUSER_NAME_INFORMATION UserName = NULL; + PUSER_PRIMARY_GROUP_INFORMATION UserPrimary = NULL; + PUSER_HOME_INFORMATION UserHome = NULL; + PUSER_SCRIPT_INFORMATION UserScript = NULL; + PUSER_PROFILE_INFORMATION UserProfile = NULL; + PUSER_ADMIN_COMMENT_INFORMATION UserAdminComment = NULL; + PUSER_WORKSTATIONS_INFORMATION UserWksta = NULL; + PUSER_CONTROL_INFORMATION UserControl = NULL; + PUSER_EXPIRES_INFORMATION UserExpires = NULL; + PUSER_LOGON_HOURS_INFORMATION UserLogonHours = NULL; + + printf("\nDumpUser.\n"); + Status = SamQueryInformationUser( + UserHandle, + UserAllInformation, + (PVOID *) &UserAll + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user all: 0x%x\n",Status); + return(Status); + } + printf("UserAll:\n"); + PrintTime("\tLastLogon = ",&UserAll->LastLogon); + PrintTime("\tLastLogoff = ",&UserAll->LastLogoff); + PrintTime("\tPasswordLastSet = ",&UserAll->PasswordLastSet); + PrintTime("\tAccountExpires = ",&UserAll->AccountExpires); + PrintTime("\tPasswordCanChange = ",&UserAll->PasswordCanChange); + PrintTime("\tPasswordMustChange = ",&UserAll->PasswordMustChange); + printf("\tUserName = %wZ\n",&UserAll->UserName); + printf("\tFullName = %wZ\n",&UserAll->FullName); + printf("\tHomeDirectory = %wZ\n",&UserAll->HomeDirectory); + printf("\tHomeDirectoryDrive = %wZ\n",&UserAll->HomeDirectoryDrive); + printf("\tScriptPath = %wZ\n",&UserAll->ScriptPath); + printf("\tProfilePath = %wZ\n",&UserAll->ProfilePath); + printf("\tAdminComment = %wZ\n",&UserAll->AdminComment); + printf("\tWorkStations = %wZ\n",&UserAll->WorkStations); + printf("\tUserComment = %wZ\n",&UserAll->UserComment); + printf("\tParameters = %wZ\n",&UserAll->Parameters); + printf("\tUserId = 0x%x\n",UserAll->UserId); + printf("\tPrimaryGroupId = 0x%x\n",UserAll->PrimaryGroupId); + printf("\tUserAccountControl = 0x%x\n",UserAll->UserAccountControl); + printf("\tWhichFields = 0x%x\n",UserAll->WhichFields); + PrintLogonHours("\tLogonHours = ",&UserAll->LogonHours); + printf("\tLogonCount = %d\n",UserAll->LogonCount); + printf("\tCountryCode = %d\n",UserAll->CountryCode); + printf("\tCodePage = %d\n",UserAll->CodePage); + + SamFreeMemory(UserAll); + + Status = SamQueryInformationUser( + UserHandle, + UserGeneralInformation, + (PVOID *) &UserGeneral + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user general: 0x%x\n",Status); + return(Status); + } + + printf("UserGeneral:\n"); + printf("\tUserName = %wZ\n",&UserGeneral->UserName); + printf("\tFullName = %wZ\n",&UserGeneral->FullName); + printf("\tPrimaryGroupId = 0x%x\n",UserGeneral->PrimaryGroupId); + printf("\tAdminComment = 0x%x\n",&UserGeneral->AdminComment); + printf("\tUserComment = 0x%x\n",&UserGeneral->UserComment); + + SamFreeMemory(UserGeneral); + + Status = SamQueryInformationUser( + UserHandle, + UserPreferencesInformation, + (PVOID *) &UserPreferences + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user preferences: 0x%x\n",Status); + return(Status); + } + + printf("UserPreferences:\n"); + printf("\tUserComment = %wZ\n",&UserPreferences->UserComment); + printf("\tReserved1 = %wZ\n",&UserPreferences->Reserved1); + printf("\tCountryCode = %d\n",&UserPreferences->CountryCode); + printf("\tCodePage = %d\n",&UserPreferences->CodePage); + + SamFreeMemory(UserPreferences); + + Status = SamQueryInformationUser( + UserHandle, + UserLogonInformation, + (PVOID *) &UserLogon + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user Logon: 0x%x\n",Status); + return(Status); + } + + + printf("UserLogon:\n"); + printf("\tUserName = %wZ\n",&UserLogon->UserName); + printf("\tFullName = %wZ\n",&UserLogon->FullName); + printf("\tUserId = 0x%x\n",UserLogon->UserId); + printf("\tPrimaryGroupId = 0x%x\n",UserLogon->PrimaryGroupId); + printf("\tHomeDirectory = %wZ\n",&UserLogon->HomeDirectory); + printf("\tHomeDirectoryDrive = %wZ\n",&UserLogon->HomeDirectoryDrive); + printf("\tScriptPath = %wZ\n",&UserLogon->ScriptPath); + printf("\tProfilePath = %wZ\n",&UserLogon->ProfilePath); + printf("\tWorkStations = %wZ\n",&UserLogon->WorkStations); + PrintTime("\tLastLogon = ",&UserLogon->LastLogon); + PrintTime("\tLastLogoff = ",&UserLogon->LastLogoff); + PrintTime("\tPasswordLastSet = ",&UserLogon->PasswordLastSet); + PrintTime("\tPasswordCanChange = ",&UserLogon->PasswordCanChange); + PrintTime("\tPasswordMustChange = ",&UserLogon->PasswordMustChange); + PrintLogonHours("\tLogonHours = ",&UserLogon->LogonHours); + printf("\tBadPasswordCount = %d\n",UserLogon->BadPasswordCount); + printf("\tLogonCount = %d\n",UserLogon->LogonCount); + printf("\tUserAccountControl = 0x%x\n",UserLogon->UserAccountControl); + + SamFreeMemory(UserLogon); + + Status = SamQueryInformationUser( + UserHandle, + UserAccountInformation, + (PVOID *) &UserAccount + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user account: 0x%x\n",Status); + return(Status); + } + + printf("UserAccount:\n"); + printf("\tUserName = %wZ\n",&UserAccount->UserName); + printf("\tFullName = %wZ\n",&UserAccount->FullName); + printf("\tUserId = 0x%x\n",UserAccount->UserId); + printf("\tPrimaryGroupId = 0x%x\n",UserAccount->PrimaryGroupId); + printf("\tHomeDirectory = %wZ\n",&UserAccount->HomeDirectory); + printf("\tHomeDirectoryDrive = %wZ\n",&UserAccount->HomeDirectoryDrive); + printf("\tScriptPath = %wZ\n",&UserAccount->ScriptPath); + printf("\tProfilePath = %wZ\n",&UserAccount->ProfilePath); + printf("\tAdminComment = %wZ\n",&UserAccount->AdminComment); + printf("\tWorkStations = %wZ\n",&UserAccount->WorkStations); + PrintTime("\tLastLogon = ",&UserAccount->LastLogon); + PrintTime("\tLastLogoff = ",&UserAccount->LastLogoff); + PrintLogonHours("\tLogonHours = ",&UserAccount->LogonHours); + printf("\tBadPasswordCount = %d\n",UserAccount->BadPasswordCount); + printf("\tLogonCount = %d\n",UserAccount->LogonCount); + PrintTime("\tPasswordLastSet = ",&UserAccount->PasswordLastSet); + PrintTime("\tAccountExpires = ",&UserAccount->AccountExpires); + printf("\tUserAccountControl = 0x%x\n",UserAccount->UserAccountControl); + + SamFreeMemory(UserAccount); + + Status = SamQueryInformationUser( + UserHandle, + UserAccountNameInformation, + (PVOID *) &UserAccountName + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user account name: 0x%x\n",Status); + return(Status); + } + + printf("UserAccountName:\n"); + printf("\tUserName = %wZ\n",&UserAccountName->UserName); + SamFreeMemory(UserAccountName); + + Status = SamQueryInformationUser( + UserHandle, + UserFullNameInformation, + (PVOID *) &UserFullName + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user full name: 0x%x\n",Status); + return(Status); + } + + printf("UserFullName:\n"); + printf("\tFullName = %wZ\n",&UserFullName->FullName); + SamFreeMemory(UserFullName); + + Status = SamQueryInformationUser( + UserHandle, + UserNameInformation, + (PVOID *) &UserName + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user name: 0x%x\n",Status); + return(Status); + } + + printf("UserName:\n"); + printf("\tUserName = %wZ\n",&UserName->UserName); + printf("\tFullName = %wZ\n",&UserName->FullName); + SamFreeMemory(UserName); + + Status = SamQueryInformationUser( + UserHandle, + UserPrimaryGroupInformation, + (PVOID *) &UserPrimary + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user all: 0x%x\n",Status); + return(Status); + } + printf("UserPrimaryGroup:\n"); + printf("PrimaryGroupid = 0x%x\n",UserPrimary->PrimaryGroupId); + SamFreeMemory(UserPrimary); + + Status = SamQueryInformationUser( + UserHandle, + UserHomeInformation, + (PVOID *) &UserHome + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user home: 0x%x\n",Status); + return(Status); + } + printf("UserHome:\n"); + printf("\tHomeDirectory = %wZ\n",&UserHome->HomeDirectory); + printf("\tHomeDirectoryDrive = %wZ\n",&UserHome->HomeDirectoryDrive); + + SamFreeMemory(UserHome); + + Status = SamQueryInformationUser( + UserHandle, + UserScriptInformation, + (PVOID *) &UserScript + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user Script: 0x%x\n",Status); + return(Status); + } + printf("UserScript:\n"); + printf("\tScriptPath = %wZ\n",&UserScript->ScriptPath); + + SamFreeMemory(UserScript); + + Status = SamQueryInformationUser( + UserHandle, + UserProfileInformation, + (PVOID *) &UserProfile + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user Profile: 0x%x\n",Status); + return(Status); + } + printf("UserProfile:\n"); + printf("\tProfilePath = %wZ\n",&UserProfile->ProfilePath); + + SamFreeMemory(UserProfile); + Status = SamQueryInformationUser( + UserHandle, + UserAdminCommentInformation, + (PVOID *) &UserAdminComment + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user AdminComment: 0x%x\n",Status); + return(Status); + } + printf("UserAdminComment:\n"); + printf("\tAdminComment = %wZ\n",&UserAdminComment->AdminComment); + SamFreeMemory(UserAdminComment); + + Status = SamQueryInformationUser( + UserHandle, + UserWorkStationsInformation, + (PVOID *) &UserWksta + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user wksta: 0x%x\n",Status); + return(Status); + } + + printf("UserWorkStations:\n"); + printf("\tWorkStations = %wZ\n",&UserWksta->WorkStations); + SamFreeMemory(UserWksta); + + Status = SamQueryInformationUser( + UserHandle, + UserControlInformation, + (PVOID *) &UserControl + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user Control: 0x%x\n",Status); + return(Status); + } + + printf("UserControl:\n"); + printf("\tUserAccountControl = 0x%x\n",UserControl->UserAccountControl); + SamFreeMemory(UserControl); + + Status = SamQueryInformationUser( + UserHandle, + UserExpiresInformation, + (PVOID *) &UserExpires + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user Expires: 0x%x\n",Status); + return(Status); + } + + printf("UserExpires:\n"); + PrintTime("\tAccountExpires = ",&UserExpires->AccountExpires); + SamFreeMemory(UserExpires); + + Status = SamQueryInformationUser( + UserHandle, + UserLogonHoursInformation, + (PVOID *) &UserLogonHours + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user LogonHours: 0x%x\n",Status); + return(Status); + } + printf("UserLogonHours:\n"); + PrintLogonHours("\tLogonHours = ",&UserLogonHours->LogonHours); + + SamFreeMemory(UserLogonHours); + + + return(STATUS_SUCCESS); +} + +NTSTATUS +DumpGroup(LPWSTR * Parameter) +{ + NTSTATUS Status; + PGROUP_GENERAL_INFORMATION General = NULL; + PGROUP_NAME_INFORMATION Name = NULL; + PGROUP_ATTRIBUTE_INFORMATION Attribute = NULL; + PGROUP_ADM_COMMENT_INFORMATION AdmComment = NULL; + + printf("\nDumpGroup.\n"); + Status = SamQueryInformationGroup( + GroupHandle, + GroupGeneralInformation, + (PVOID *) &General + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get group general information: 0x%x\n",Status); + return(Status); + } + + printf("Group General.Name = %wZ\n",&General->Name); + printf("Group General.Attributes = 0x%x\n",General->Attributes); + printf("Group general.memberCount = %d\n",General->MemberCount); + printf("Group general.AdminComment = %wZ\n",&General->AdminComment); + SamFreeMemory(General); + + Status = SamQueryInformationGroup( + GroupHandle, + GroupNameInformation, + (PVOID *) &Name + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get group name information: 0x%x\n",Status); + return(Status); + } + + printf("Group Name.Name = %wZ\n",&Name->Name); + SamFreeMemory(Name); + + Status = SamQueryInformationGroup( + GroupHandle, + GroupAttributeInformation, + (PVOID *) &Attribute + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get group Attribute information: 0x%x\n",Status); + return(Status); + } + + printf("Group Attribute.Attributes = 0x%x\n",Attribute->Attributes); + SamFreeMemory(Attribute); + + Status = SamQueryInformationGroup( + GroupHandle, + GroupAdminCommentInformation, + (PVOID *) &AdmComment + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get group admin comment information: 0x%x\n",Status); + return(Status); + } + + printf("Group Admin comment.AdminComment = %wZ\n",&AdmComment->AdminComment); + SamFreeMemory(AdmComment); + + return(STATUS_SUCCESS); +} + +NTSTATUS +DumpAllGroups(LPWSTR * Parameter) +{ + ULONG PreferedMax = 1000; + NTSTATUS Status,EnumStatus; + SAM_ENUMERATE_HANDLE EnumContext = 0; + ULONG CountReturned; + LPWSTR GroupName[1]; + + PSAM_RID_ENUMERATION Accounts = NULL; + + + GroupName[0] = (LPWSTR) malloc(128); + + printf("DumpAllGroups:\n"); + + + EnumContext = 0; + ASSERT(DomainHandle != NULL); + do + { + EnumStatus = SamEnumerateGroupsInDomain( + DomainHandle, + &EnumContext, + (PVOID *) &Accounts, + PreferedMax, + &CountReturned + ); + + if (NT_SUCCESS(EnumStatus) && (EnumStatus != STATUS_NO_MORE_ENTRIES)) + { + ULONG Index; + UNICODE_STRING SidString; + + for (Index = 0; Index < CountReturned; Index++) + { + RtlCopyMemory( + GroupName[0], + Accounts[Index].Name.Buffer, + Accounts[Index].Name.Length + ); + GroupName[0][Accounts[Index].Name.Length/sizeof(WCHAR)] = L'\0'; + + Status = OpenGroup(GroupName); + if (!NT_SUCCESS(Status)) + { + break; + } + Status = DumpGroup(NULL); + SamCloseHandle(GroupHandle); + GroupHandle = NULL; + + } + SamFreeMemory(Accounts); + } + else printf("Failed to enumerate Groups: 0x%x\n",Status); + } while (NT_SUCCESS(EnumStatus) && (EnumStatus != STATUS_SUCCESS) && (CountReturned != 0) ); + + free(GroupName[0]); + return(STATUS_SUCCESS); +} + +NTSTATUS +DumpAllUsers(LPWSTR * Parameter) +{ + ULONG PreferedMax = 1000; + NTSTATUS Status,EnumStatus; + SAM_ENUMERATE_HANDLE EnumContext = 0; + ULONG CountReturned; + LPWSTR UserName[1]; + + PSAM_RID_ENUMERATION Accounts = NULL; + + + UserName[0] = (LPWSTR) malloc(128); + + printf("DumpAllUsers:\n"); + + + EnumContext = 0; + ASSERT(DomainHandle != NULL); + do + { + EnumStatus = SamEnumerateUsersInDomain( + DomainHandle, + &EnumContext, + 0, + (PVOID *) &Accounts, + PreferedMax, + &CountReturned + ); + + if (NT_SUCCESS(EnumStatus) && (EnumStatus != STATUS_NO_MORE_ENTRIES)) + { + ULONG Index; + UNICODE_STRING SidString; + + for (Index = 0; Index < CountReturned; Index++) + { + RtlCopyMemory( + UserName[0], + Accounts[Index].Name.Buffer, + Accounts[Index].Name.Length + ); + UserName[0][Accounts[Index].Name.Length/sizeof(WCHAR)] = L'\0'; + + Status = OpenUser(UserName); + if (!NT_SUCCESS(Status)) + { + break; + } + Status = DumpUser(NULL); + Status = GetGroupsForUser(NULL); + SamCloseHandle(UserHandle); + UserHandle = NULL; + + } + SamFreeMemory(Accounts); + } + else printf("Failed to enumerate users: 0x%x\n",Status); + } while (NT_SUCCESS(EnumStatus) && (EnumStatus != STATUS_SUCCESS) && (CountReturned != 0) ); + + free(UserName[0]); + return(STATUS_SUCCESS); +} + +NTSTATUS +AddAliasMember( LPWSTR * Parameter ) +{ + BYTE Buffer[100]; + PSID AccountSid = Buffer; + ULONG SidLen = 100; + SID_NAME_USE Use; + WCHAR ReferencedDomain[100]; + ULONG DomainLen = 100; + NTSTATUS Status; + + printf("Adding account %ws to alias\n",Parameter[0]); + if (!LookupAccountName( + NULL, + Parameter[0], + AccountSid, + &SidLen, + ReferencedDomain, + &DomainLen, + &Use)) + { + printf("Failed to lookup account name: %d\n",GetLastError()); + return(STATUS_UNSUCCESSFUL); + } + + Status = SamAddMemberToAlias( + AliasHandle, + AccountSid + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to add member to alias: 0x%x\n",Status); + } + return(Status); +} + +NTSTATUS +DumpDomain( LPWSTR * Parameter ) +{ + NTSTATUS Status; + PDOMAIN_PASSWORD_INFORMATION Password = NULL; + PDOMAIN_GENERAL_INFORMATION General = NULL; + PDOMAIN_LOGOFF_INFORMATION Logoff = NULL; + PDOMAIN_OEM_INFORMATION Oem = NULL; + PDOMAIN_NAME_INFORMATION Name = NULL; + PDOMAIN_REPLICATION_INFORMATION Replica = NULL; + PDOMAIN_SERVER_ROLE_INFORMATION ServerRole = NULL; + PDOMAIN_MODIFIED_INFORMATION Modified = NULL; + PDOMAIN_STATE_INFORMATION State = NULL; + PDOMAIN_GENERAL_INFORMATION2 General2 = NULL; + PDOMAIN_LOCKOUT_INFORMATION Lockout = NULL; + PDOMAIN_MODIFIED_INFORMATION2 Modified2 = NULL; + + + Status = SamQueryInformationDomain( + DomainHandle, + DomainPasswordInformation, + (PVOID *) &Password + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query password information: 0x%x\n",Status); + return(Status); + } + + printf("Password:\n"); + printf("\tMinPasswordLength = %d\n",Password->MinPasswordLength); + printf("\tPasswordHistoryLength = %d\n",Password->PasswordHistoryLength); + printf("\tPasswordProperties = 0x%x\n",Password->PasswordProperties); + PrintDeltaTime("\tMaxPasswordAge = ",&Password->MaxPasswordAge); + PrintDeltaTime("\tMinPasswordAge = ",&Password->MinPasswordAge); + + SamFreeMemory(Password); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainGeneralInformation, + (PVOID *) &General + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query general: 0x%x\n",Status); + return(Status); + } + + printf("General:\n"); + PrintDeltaTime("\t ForceLogoff = ",&General->ForceLogoff); + printf("\t OemInformation = %wZ\n",&General->OemInformation); + printf("\t DomainName = %wZ\n",&General->DomainName); + printf("\t ReplicaSourceNodeName =%wZ\n",&General->ReplicaSourceNodeName); + printf("\t DomainModifiedCount = 0x%x,0x%x\n", + General->DomainModifiedCount.HighPart, + General->DomainModifiedCount.LowPart ); + printf("\t DomainServerState = %d\n",General->DomainServerState); + printf("\t DomainServerRole = %d\n",General->DomainServerRole); + printf("\t UasCompatibilityRequired = %d\n",General->UasCompatibilityRequired); + printf("\t UserCount = %d\n",General->UserCount); + printf("\t GroupCount = %d\n",General->GroupCount); + printf("\t AliasCount = %d\n",General->AliasCount); + + SamFreeMemory(General); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainLogoffInformation, + (PVOID *) &Logoff + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query logoff: 0x%x\n",Status); + return(Status); + } + + printf("Logoff:\n"); + PrintDeltaTime("\t ForceLogoff = ",&Logoff->ForceLogoff); + SamFreeMemory(Logoff); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainOemInformation, + (PVOID *) &Oem + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query Oem: 0x%x\n",Status); + return(Status); + } + + printf("Oem:\n\t OemInformation = %wZ\n",&Oem->OemInformation); + + SamFreeMemory(Oem); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainNameInformation, + (PVOID *) &Name + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query Name: 0x%x\n",Status); + return(Status); + } + printf("Name:\n\t DomainName = %wZ\n",&Name->DomainName); + + SamFreeMemory(Name); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainReplicationInformation, + (PVOID *) &Replica + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query Replica: 0x%x\n",Status); + return(Status); + } + + printf("Replica:\n\t ReplicaSourceNodeName = %wZ\n", &Replica->ReplicaSourceNodeName); + SamFreeMemory(Replica); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainServerRoleInformation, + (PVOID *) &ServerRole + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query ServerRole: 0x%x\n",Status); + return(Status); + } + + printf("ServerRole:\n\t DomainServerRole = %d\n",ServerRole->DomainServerRole); + SamFreeMemory(ServerRole); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainModifiedInformation, + (PVOID *) &Modified + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query Modified: 0x%x\n",Status); + return(Status); + } + + printf("Modified:\n"); + printf("\t DomainModifiedCount = 0x%x,0x%x\n", + Modified->DomainModifiedCount.HighPart, + Modified->DomainModifiedCount.LowPart ); + PrintTime("\t CreationTime = ",&Modified->CreationTime); + + + + SamFreeMemory(Modified); + + + Status = SamQueryInformationDomain( + DomainHandle, + DomainStateInformation, + (PVOID *) &State + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query State: 0x%x\n",Status); + return(Status); + } + + printf("State:\n\t DomainServerState = %d\n",State->DomainServerState); + SamFreeMemory(State); + + + Status = SamQueryInformationDomain( + DomainHandle, + DomainGeneralInformation2, + (PVOID *) &General2 + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query General2: 0x%x\n",Status); + return(Status); + } + + printf("General2:\n"); + General = &General2->I1; + PrintDeltaTime("\t ForceLogoff = ",&General->ForceLogoff); + printf("\t OemInformation = %wZ\n",&General->OemInformation); + printf("\t DomainName = %wZ\n",&General->DomainName); + printf("\t ReplicaSourceNodeName =%wZ\n",&General->ReplicaSourceNodeName); + printf("\t DomainModifiedCount = 0x%x,0x%x\n", + General->DomainModifiedCount.HighPart, + General->DomainModifiedCount.LowPart ); + printf("\t DomainServerState = %d\n",General->DomainServerState); + printf("\t DomainServerRole = %d\n",General->DomainServerRole); + printf("\t UasCompatibilityRequired = %d\n",General->UasCompatibilityRequired); + printf("\t UserCount = %d\n",General->UserCount); + printf("\t GroupCount = %d\n",General->GroupCount); + printf("\t AliasCount = %d\n",General->AliasCount); + PrintDeltaTime("\t LockoutDuration = ",&General2->LockoutDuration); + PrintDeltaTime("\t LockoutObservationWindow = ",&General2->LockoutObservationWindow); + printf("\t LockoutThreshold = %d\n",General2->LockoutThreshold); + + SamFreeMemory(General2); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainLockoutInformation, + (PVOID *) &Lockout + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query Lockout: 0x%x\n",Status); + return(Status); + } + printf("Lockout:\n"); + PrintDeltaTime("\t LockoutDuration = ",&Lockout->LockoutDuration); + PrintDeltaTime("\t LockoutObservationWindow = ",&Lockout->LockoutObservationWindow); + printf("\t LockoutThreshold = %d\n",Lockout->LockoutThreshold); + + SamFreeMemory(Lockout); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainModifiedInformation2, + (PVOID *) &Modified2 + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query Modified2: 0x%x\n",Status); + return(Status); + } + printf("Modified2:\n"); + PrintTime("\t CreationTime = ",&Modified->CreationTime); + printf("\t DomainModifiedCount = 0x%x,0x%x\n", + Modified2->DomainModifiedCount.HighPart, + Modified2->DomainModifiedCount.LowPart ); + + printf("\t ModifiedCountAtLastPromotion = 0x%x,0x%x\n", + Modified2->ModifiedCountAtLastPromotion.HighPart, + Modified2->ModifiedCountAtLastPromotion.LowPart ); + + SamFreeMemory(Modified2); + + return(STATUS_SUCCESS); + +} + + + +void _cdecl +main(int argc, char *argv[]) +{ + ULONG Command = 0; + ULONG i,j,k; + BOOLEAN Found; + NTSTATUS Status; + Action Actions[20]; + ULONG ActionCount = 0; + + for (i = 1; i < (ULONG) argc ; i++ ) + { + Found = FALSE; + for (j = 0; j < NUM_COMMANDS ; j++ ) + { + if (!_stricmp(argv[i],Commands[j].Name)) + { + Actions[ActionCount].CommandNumber = j; + + if (Commands[j].Parameter != 0) + { + for (k = 0; k < Commands[j].Parameter ;k++ ) + { + Actions[ActionCount].Parameter[k] = (LPWSTR) malloc(128); + if ((ULONG) argc > i) + { + mbstowcs(Actions[ActionCount].Parameter[k],argv[++i],128); + } + else + { + Actions[ActionCount].Parameter[k][0] = L'\0'; + } + } + } + Found = TRUE; + ActionCount++; + break; + } + } + if (!Found) + { + printf("Switch %s not found\n", argv[i]); + return; + } + } + +// Status = OpenSam(); +// if (!NT_SUCCESS(Status)) +// { +// printf("Failed to open sam: 0x%x\n",Status); +// return; +// } + + for (i = 0; i < ActionCount ; i++ ) + { + Status = Commands[Actions[i].CommandNumber].Function(Actions[i].Parameter); + if (!NT_SUCCESS(Status)) + { + printf("Failed test %s : 0x%x\n",Commands[Actions[i].CommandNumber].Name,Status); + goto Cleanup; + + } + } + +Cleanup: + if (DomainHandle != NULL) + { + Status = SamCloseHandle(DomainHandle); + if (!NT_SUCCESS(Status)) + { + printf("Failed to close account: 0x%x\n",Status); + } + } + if (GroupHandle != NULL) + { + Status = SamCloseHandle(GroupHandle); + if (!NT_SUCCESS(Status)) + { + printf("Failed to close account: 0x%x\n",Status); + } + } + if (AliasHandle != NULL) + { + Status = SamCloseHandle(AliasHandle); + if (!NT_SUCCESS(Status)) + { + printf("Failed to close account: 0x%x\n",Status); + } + } + if (UserHandle != NULL) + { + Status = SamCloseHandle(UserHandle); + if (!NT_SUCCESS(Status)) + { + printf("Failed to close account: 0x%x\n",Status); + } + } + Status = CloseSam(); + if (!NT_SUCCESS(Status)) + { + printf("Failed to close lsa: 0x%x\n",Status); + } + + return; + +} diff --git a/private/newsam2/server/utest/grpops.cxx b/private/newsam2/server/utest/grpops.cxx new file mode 100644 index 000000000..5711d8104 --- /dev/null +++ b/private/newsam2/server/utest/grpops.cxx @@ -0,0 +1,2033 @@ +//+----------------------------------------------------------------------- +// +// Microsoft Windows +// +// Copyright (c) Microsoft Corporation 1992 - 1992 +// +// File: secret.cxx +// +// Contents: test program to check the setup of a Cairo installation +// +// +// History: 22-Dec-92 Created MikeSw +// +//------------------------------------------------------------------------ + + +extern "C" +{ +#include <nt.h> +#include <ntrtl.h> +#include <nturtl.h> +#include <ntsam.h> +#include <ntlsa.h> +#include <windows.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <caiseapi.h> +} + +typedef NTSTATUS (TestFunc)( WCHAR *Parameter[]); + +typedef struct _Commands { + PSTR Name; + ULONG Parameter; // TRUE = yes, FALSE = no + TestFunc *Function; +} CommandPair, *PCommandPair; + + +typedef struct _Action { + ULONG CommandNumber; + LPWSTR Parameter[8]; +} Action, *PAction; + + + +TestFunc OpenDomain; +TestFunc EnumDomains; +TestFunc EnumAccounts; +TestFunc QueryDisplay; +TestFunc OpenGroup; +TestFunc GroupMembers; +TestFunc OpenAlias; +TestFunc AliasMembers; +TestFunc GetAliasMembership; +TestFunc OpenUser; +TestFunc GetGroupsForUser; +TestFunc DumpAllUsers; +TestFunc DumpAllGroups; +TestFunc DumpUser; +TestFunc DumpGroup; +TestFunc CreateUser; +TestFunc AddAliasMember; +TestFunc CreateGroup; +TestFunc CreateAlias; +TestFunc DumpDomain; +TestFunc Connect; +TestFunc DelUser; +TestFunc DelAlias; +TestFunc DelGroup; + +CommandPair Commands[] = { + {"-od",1,OpenDomain}, + {"-ed",0,EnumDomains}, + {"-ea",1,EnumAccounts}, + {"-qd",1,QueryDisplay}, + {"-og",1,OpenGroup}, + {"-gm",0,GroupMembers}, + {"-oa",1,OpenAlias}, + {"-am",0,AliasMembers}, + {"-gam",1,GetAliasMembership}, + {"-ou",1,OpenUser}, + {"-ggu",0,GetGroupsForUser}, + {"-dau",0,DumpAllUsers}, + {"-dag",0,DumpAllGroups}, + {"-du",0,DumpUser}, + {"-dg",0,DumpGroup}, + {"-cu",1,CreateUser}, + {"-aam",1,AddAliasMember}, + {"-cg",1,CreateGroup}, + {"-ca",1,CreateAlias}, + {"-dd",0,DumpDomain}, + {"-c",1,Connect}, + {"-delu",0,DelUser}, + {"-dela",0,DelAlias}, + {"-delg",0,DelGroup} + + + +}; + +#define NUM_COMMANDS (sizeof(Commands) / sizeof(CommandPair)) + +SAM_HANDLE SamHandle; +SAM_HANDLE DomainHandle; +SAM_HANDLE GroupHandle; +SAM_HANDLE AliasHandle; +SAM_HANDLE UserHandle; + +UNICODE_STRING ServerName; + +//+------------------------------------------------------------------------- +// +// Function: PrintTime +// +// Synopsis: Prints a text representation of a FILETIME interpreted +// as an absolute date/time +// +// Arguments: [rft] -- time to print +// +// Notes: Used only by dump functions. +// +//-------------------------------------------------------------------------- + +void +PrintTime ( + char * String, + PVOID Time + ) +{ + SYSTEMTIME st; + + FileTimeToSystemTime ( (PFILETIME) Time, & st ); + printf("%s %d-%d-%d %d:%2.2d:%2.2d\n", String, st.wMonth, st.wDay, st.wYear, + st.wHour, st.wMinute, st.wSecond ); +} + +//+------------------------------------------------------------------------- +// +// Function: SpmDbDeltaTimeToString +// +// Synopsis: Converts a time delta to a string. +// +// Effects: +// +// Arguments: +// +// Requires: +// +// Returns: +// +// Notes: +// +// +//-------------------------------------------------------------------------- + + +VOID +PrintDeltaTime( + IN LPSTR Message, + IN PLARGE_INTEGER Time + ) +{ + ULONG Seconds; + ULONG Minutes; + ULONG Hours; + ULONG Days; + ULONG Chars; + CHAR TimeBuffer[256] = ""; + LPSTR TimeString = TimeBuffer; + LARGE_INTEGER DeltaTime; + + DeltaTime.QuadPart = -Time->QuadPart; + + Seconds = (ULONG) (DeltaTime.QuadPart / 10000000); + + Minutes = Seconds / 60; + Hours = Minutes / 60; + Days = Hours / 24; + + Hours = Hours % 24; + Minutes = Minutes % 60; + Seconds = Seconds % 60; + + if (Days >= 1) + { + Chars = sprintf(TimeString,"%d days ",Days); + TimeString += Chars; + } + if (Hours >= 1 ) + { + Chars = sprintf(TimeString,"%d hours ",Hours); + TimeString += Chars; + } + + if (Minutes >= 1 && Days == 0) + { + Chars = sprintf(TimeString,"%d minutes ",Minutes); + TimeString += Chars; + } + + if (Seconds >= 1 && (Days == 0) && (Hours == 0) ) + { + Chars = sprintf(TimeString,"%d seconds ",Seconds); + TimeString += Chars; + } + + printf("%s %s\n",Message,TimeBuffer); + +} + + +NTSTATUS +Connect( LPWSTR * Parameter) +{ + OBJECT_ATTRIBUTES oa; + NTSTATUS Status; + + RtlInitUnicodeString( + &ServerName, + Parameter[0] + ); + + InitializeObjectAttributes(&oa,NULL,0,NULL,NULL); + + Status = SamConnect( + &ServerName, + &SamHandle, + MAXIMUM_ALLOWED, + &oa); + return(Status); +} + +NTSTATUS +CloseSam() +{ + return(SamCloseHandle(SamHandle)); +} + + + +NTSTATUS +EnumDomains( + LPWSTR * Parameter ) +{ + NTSTATUS Status; + SHORT Language; + SAM_ENUMERATE_HANDLE Context = 0; + PSAM_RID_ENUMERATION Buffer = NULL; + ULONG Count = 0; + ULONG i; + + Status = SamEnumerateDomainsInSamServer( + SamHandle, + &Context, + (PVOID *) &Buffer, + 2000, + &Count + ); + + if (!NT_SUCCESS(Status)) + { + return(Status); + } + + + for (i = 0; i < Count ; i++ ) + { + printf("Domain = %wZ\n",&Buffer[i].Name); + } + SamFreeMemory(Buffer); + return(STATUS_SUCCESS); +} + +NTSTATUS +OpenDomain( LPWSTR * Parameter ) +{ + GUID DomainGuid; + BOOLEAN fBuiltin; + NTSTATUS Status; + CAIROSID DomainSid; + SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; + + if (!_wcsicmp(Parameter[0],L"Builtin")) + { + fBuiltin = TRUE; + } + else if (!_wcsicmp(Parameter[0],L"Account")) + { + fBuiltin = FALSE; + } + else + { + printf("Invalid domain to open: %ws\n",Parameter[0]); + return(STATUS_UNSUCCESSFUL); + } + + if (fBuiltin) + { + DomainSid.Revision = SID_REVISION; + DomainSid.SubAuthorityCount = 1; + DomainSid.IdentifierAuthority = NtAuthority; + DomainSid.ZerothSubAuthority = SECURITY_BUILTIN_DOMAIN_RID; + } + else + { + LSA_HANDLE LsaHandle = NULL; + OBJECT_ATTRIBUTES Oa; + PPOLICY_ACCOUNT_DOMAIN_INFO DomainInfo = NULL; + + RtlZeroMemory(&Oa, sizeof(OBJECT_ATTRIBUTES)); + Status = LsaOpenPolicy( + &ServerName, + &Oa, + POLICY_VIEW_LOCAL_INFORMATION, + &LsaHandle + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to open policy: 0x%x\n",Status); + return(Status); + } + Status = LsaQueryInformationPolicy( + LsaHandle, + PolicyAccountDomainInformation, + (PVOID *) &DomainInfo + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query account domain: 0x%x\n",Status); + LsaClose(LsaHandle); + return(Status); + } + RtlCopyMemory( + &DomainSid, + DomainInfo->DomainSid, + RtlLengthSid(DomainInfo->DomainSid) + ); + LsaFreeMemory(DomainInfo); + } + + Status = SamOpenDomain( + SamHandle, + MAXIMUM_ALLOWED, + (PSID) &DomainSid, + &DomainHandle + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to open domain: 0x%x\n",Status); + } + return(Status); + +} + +NTSTATUS +EnumAccounts( LPWSTR * Parameter ) +{ + ULONG PreferedMax = 100; + NTSTATUS Status; + SAM_ENUMERATE_HANDLE EnumContext = 0; + ULONG CountReturned; + + PSAM_RID_ENUMERATION Accounts = NULL; + + swscanf(Parameter[0],L"%d",&PreferedMax); + + printf("EnumAccounts: %d\n",PreferedMax); + + + EnumContext = 0; + ASSERT(DomainHandle != NULL); + do + { + Status = SamEnumerateUsersInDomain( + DomainHandle, + &EnumContext, + 0, + (PVOID *) &Accounts, + PreferedMax, + &CountReturned + ); + + if (NT_SUCCESS(Status) && (Status != STATUS_NO_MORE_ENTRIES)) + { + ULONG Index; + UNICODE_STRING SidString; + + for (Index = 0; Index < CountReturned; Index++) + { + printf("Account : %wZ 0x%x\n",&Accounts[Index].Name, Accounts[Index].RelativeId); + } + SamFreeMemory(Accounts); + } + else printf("Failed to enumerate users: 0x%x\n",Status); + } while (NT_SUCCESS(Status) && (Status != STATUS_SUCCESS) && (CountReturned != 0) ); + + EnumContext = 0; + do + { + Status = SamEnumerateGroupsInDomain( + DomainHandle, + &EnumContext, + (PVOID *) &Accounts, + PreferedMax, + &CountReturned + ); + + if (NT_SUCCESS(Status) && (Status != STATUS_NO_MORE_ENTRIES)) + { + ULONG Index; + UNICODE_STRING SidString; + + for (Index = 0; Index < CountReturned; Index++) + { + printf("Group : %wZ 0x%x\n",&Accounts[Index].Name, Accounts[Index].RelativeId); + } + SamFreeMemory(Accounts); + } + else printf("Failed to enumerate Groups: 0x%x\n",Status); + } while (NT_SUCCESS(Status) && (CountReturned != 0) ); // && (Status != STATUS_SUCCESS) + + + EnumContext = 0; + do + { + Status = SamEnumerateAliasesInDomain( + DomainHandle, + &EnumContext, + (PVOID *) &Accounts, + PreferedMax, + &CountReturned + ); + + if (NT_SUCCESS(Status) && (Status != STATUS_NO_MORE_ENTRIES)) + { + ULONG Index; + UNICODE_STRING SidString; + + for (Index = 0; Index < CountReturned; Index++) + { + printf("Alias : %wZ 0x%x\n",&Accounts[Index].Name, Accounts[Index].RelativeId); + } + SamFreeMemory(Accounts); + } + else printf("Failed to enumerate aliases: 0x%x\n",Status); + } while (NT_SUCCESS(Status) && (CountReturned != 0) ); // && (Status != STATUS_SUCCESS) + + + + return(Status); +} + + +NTSTATUS +QueryDisplay( LPWSTR * Parameter ) +{ + NTSTATUS Status; + DOMAIN_DISPLAY_INFORMATION Type; + PVOID Buffer = NULL; + ULONG TotalAvailable = 0; + ULONG TotalReturned = 0; + ULONG ReturnedCount = 0; + ULONG Index; + ULONG SamIndex = 0; + + if (!_wcsicmp(Parameter[0],L"user")) + { + Type = DomainDisplayUser; + } else if (!_wcsicmp(Parameter[0],L"Machine")) + { + Type = DomainDisplayMachine; + } else if (!_wcsicmp(Parameter[0],L"Group")) + { + Type = DomainDisplayGroup; + } else if (!_wcsicmp(Parameter[0],L"OemUser")) + { + Type = DomainDisplayOemUser; + } else if (!_wcsicmp(Parameter[0],L"OemGroup")) + { + Type = DomainDisplayOemGroup; + } else { + printf("Invalid parameter %ws\n", Parameter[0]); + return(STATUS_INVALID_PARAMETER); + } + + do + { + Status = SamQueryDisplayInformation( + DomainHandle, + Type, + SamIndex, + 5, + 1000, + &TotalAvailable, + &TotalReturned, + &ReturnedCount, + &Buffer + ); + + if (NT_SUCCESS(Status) && (ReturnedCount > 0)) + { + printf("Total returned = %d\t total available = %d\n", + TotalReturned, TotalAvailable); + switch(Type) { + case DomainDisplayUser: + { + PDOMAIN_DISPLAY_USER Users = (PDOMAIN_DISPLAY_USER) Buffer; + for (Index = 0; Index < ReturnedCount ; Index++ ) + { + printf("User %d: Index %d\n Rid 0x%x\n Control 0x%x\n name %wZ\n Comment %wZ\n Full Name %wZ\n", + Index, + Users[Index].Index, + Users[Index].Rid, + Users[Index].AccountControl, + &Users[Index].LogonName, + &Users[Index].AdminComment, + &Users[Index].FullName + ); + } + break; + } + case DomainDisplayGroup: + { + PDOMAIN_DISPLAY_GROUP Groups = (PDOMAIN_DISPLAY_GROUP) Buffer; + for (Index = 0; Index < ReturnedCount ; Index++ ) + { + printf("Group %d\n Index %d\n Rid 0x%x\n Attributes 0x%x\n name %wZ\n Comment %wZ\n", + Index, + Groups[Index].Index, + Groups[Index].Rid, + Groups[Index].Attributes, + &Groups[Index].Group, + &Groups[Index].Comment + ); + + } + break; + } + case DomainDisplayMachine: + { + PDOMAIN_DISPLAY_MACHINE Machines = (PDOMAIN_DISPLAY_MACHINE) Buffer; + for (Index = 0; Index < ReturnedCount ; Index++ ) + { + printf("Machine %d\n Index %d\n Rid 0x%x\n Control 0x%x\n Name %wZ\n Comment %wZ\n", + Index, + Machines[Index].Index, + Machines[Index].Rid, + Machines[Index].AccountControl, + &Machines[Index].Machine, + &Machines[Index].Comment + ); + } + break; + } + case DomainDisplayOemUser: + { + PDOMAIN_DISPLAY_OEM_USER OemUsers = (PDOMAIN_DISPLAY_OEM_USER) Buffer; + for (Index = 0; Index < ReturnedCount ; Index++ ) + { + printf("OemUser %d\n Index %d\n Name %Z\n", + Index, + OemUsers[Index].Index, + &OemUsers[Index].User + ); + } + break; + } + case DomainDisplayOemGroup: + { + PDOMAIN_DISPLAY_OEM_GROUP OemGroups = (PDOMAIN_DISPLAY_OEM_GROUP) Buffer; + for (Index = 0; Index < ReturnedCount ; Index++ ) + { + printf("OemGroup %d\n Index %d\n Name %Z\n", + Index, + OemGroups[Index].Index, + &OemGroups[Index].Group + ); + } + break; + } + + } + SamFreeMemory(Buffer); + SamIndex += ReturnedCount; + } + + + } while (NT_SUCCESS(Status) && (ReturnedCount > 0)); + printf("QDI returned 0x%x\n",Status); + + return(Status); + + +} + +NTSTATUS +OpenGroup( LPWSTR * Parameter) +{ + PSID_NAME_USE Use = NULL; + PULONG Rid = NULL; + NTSTATUS Status; + UNICODE_STRING GroupName; + ULONG RelativeId = 0; + +// swscanf(Parameter[0],L"%x",&RelativeId); + if (RelativeId == 0) + { + RtlInitUnicodeString( + &GroupName, + Parameter[0] + ); + + printf("Looking up group %wZ\n",&GroupName); + + Status = SamLookupNamesInDomain( + DomainHandle, + 1, + &GroupName, + &Rid, + &Use + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to lookup group: 0x%x\n",Status); + return(Status); + } + RelativeId = *Rid; + SamFreeMemory(Rid); + SamFreeMemory(Use); + } + + printf("Opening Group 0x%x\n",RelativeId); + Status= SamOpenGroup( + DomainHandle, + MAXIMUM_ALLOWED, // GROUP_LIST_MEMBERS | GROUP_READ_INFORMATION, + RelativeId, + &GroupHandle + ); + + if (!NT_SUCCESS(Status)) + { + printf("Failed to open group: 0x%x\n",Status); + } + return(Status); +} + +NTSTATUS +GroupMembers(LPWSTR * Parameter) +{ + NTSTATUS Status; + ULONG MembershipCount; + PULONG Attributes = NULL; + PULONG Rids = NULL; + ULONG Index; + + Status = SamGetMembersInGroup( + GroupHandle, + &Rids, + &Attributes, + &MembershipCount + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get members in group: 0x%x\n",Status); + return(Status); + } + + for (Index = 0; Index < MembershipCount ; Index++ ) + { + printf("Member %d: rid 0x%x, attributes 0x%x\n", + Index,Rids[Index],Attributes[Index]); + } + SamFreeMemory(Rids); + SamFreeMemory(Attributes); + return(STATUS_SUCCESS); + +} + + +NTSTATUS +OpenAlias( LPWSTR * Parameter) +{ + PSID_NAME_USE Use = NULL; + PULONG Rid = NULL; + NTSTATUS Status; + UNICODE_STRING AliasName; + ULONG RelativeId; + + swscanf(Parameter[0],L"%x",&RelativeId); + if (RelativeId == 0) + { + RtlInitUnicodeString( + &AliasName, + Parameter[0] + ); + + printf("Looking up Alias %wZ\n",&AliasName); + + Status = SamLookupNamesInDomain( + DomainHandle, + 1, + &AliasName, + &Rid, + &Use + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to lookup Alias: 0x%x\n",Status); + return(Status); + } + RelativeId = *Rid; + SamFreeMemory(Rid); + SamFreeMemory(Use); + } + + + printf("Opening Alias 0x%x\n",RelativeId); + Status= SamOpenAlias( + DomainHandle, + ALIAS_LIST_MEMBERS | ALIAS_ADD_MEMBER, + RelativeId, + &AliasHandle + ); + + if (!NT_SUCCESS(Status)) + { + printf("Failed to open alias: 0x%x\n",Status); + } + return(Status); +} + +NTSTATUS +AliasMembers(LPWSTR * Parameter) +{ + NTSTATUS Status; + ULONG MembershipCount; + PSID * Members = NULL; + ULONG Index; + UNICODE_STRING Sid; + + Status = SamGetMembersInAlias( + AliasHandle, + &Members, + &MembershipCount + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get members in Alias: 0x%x\n",Status); + return(Status); + } + + for (Index = 0; Index < MembershipCount ; Index++ ) + { + RtlConvertSidToUnicodeString( + &Sid, + Members[Index], + TRUE + ); + printf("Member %d: sid %wZ\n", + Index,&Sid); + RtlFreeUnicodeString(&Sid); + } + SamFreeMemory(Members); + return(STATUS_SUCCESS); + +} + + +NTSTATUS +GetAliasMembership(LPWSTR * Parameter) +{ + NTSTATUS Status; + ULONG Index; + UNICODE_STRING Name; + OBJECT_ATTRIBUTES Oa; + LSA_HANDLE LsaHandle = NULL; + CAIROSID Sid; + ULONG SidLength = sizeof(CAIROSID); + WCHAR ReferencedDomainName[100]; + ULONG DomainNameLength = 100; + SID_NAME_USE SidUse; + ULONG MembershipCount; + PULONG AliasList = NULL; + PSID SidAddress = (PSID) &Sid; + + + + printf("Looking up groups for user %ws\n",Parameter[0]); + + if (!LookupAccountNameW( + NULL, + Parameter[0], + SidAddress, + &SidLength, + ReferencedDomainName, + &DomainNameLength, + &SidUse)) + { + printf("Failed to lookup account sid: %d\n",GetLastError()); + return(STATUS_UNSUCCESSFUL); + } + + + + Status = SamGetAliasMembership( + DomainHandle, + 1, + &SidAddress, + &MembershipCount, + &AliasList + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get alises : 0x%x\n",Status); + return(Status); + } + + for (Index = 0; Index < MembershipCount ; Index++ ) + { + printf("Alias Member %d: rid 0x%x\n", + Index,AliasList[Index]); + + } + SamFreeMemory(AliasList); + return(STATUS_SUCCESS); + +} + +NTSTATUS +GetAccountRid( LPWSTR Parameter, + PULONG RelativeId) +{ + + PSID_NAME_USE Use = NULL; + PULONG Rid = NULL; + NTSTATUS Status; + UNICODE_STRING UserName; + + RtlInitUnicodeString( + &UserName, + Parameter + ); + + printf("Looking up User %wZ\n",&UserName); + + Status = SamLookupNamesInDomain( + DomainHandle, + 1, + &UserName, + &Rid, + &Use + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to lookup User: 0x%x\n",Status); + return(Status); + } + *RelativeId = *Rid; + SamFreeMemory(Rid); + SamFreeMemory(Use); + + return(STATUS_SUCCESS); +} +NTSTATUS +OpenUser( LPWSTR * Parameter) +{ + PSID_NAME_USE Use = NULL; + PULONG Rid = NULL; + NTSTATUS Status; + UNICODE_STRING UserName; + ULONG RelativeId = 0; + + Status = GetAccountRid(Parameter[0],&RelativeId); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get account rid: 0x%x\n",Status); + return(Status); + } + + printf("Opening User 0x%x\n",RelativeId); + Status= SamOpenUser( + DomainHandle, + MAXIMUM_ALLOWED, + RelativeId, + &UserHandle + ); + + if (!NT_SUCCESS(Status)) + { + printf("Failed to open User: 0x%x\n",Status); + } + return(Status); +} + +NTSTATUS +DelUser( LPWSTR * Parameter) +{ + NTSTATUS Status; + + Status = SamDeleteUser(UserHandle); + if (!NT_SUCCESS(Status)) + { + printf("Failed to delete user: 0x%x\n",Status); + } + return(Status); +} + +NTSTATUS +DelGroup( LPWSTR * Parameter) +{ + NTSTATUS Status; + + Status = SamDeleteGroup(GroupHandle); + if (!NT_SUCCESS(Status)) + { + printf("Failed to delete user: 0x%x\n",Status); + } + return(Status); +} + +NTSTATUS +DelAlias( LPWSTR * Parameter) +{ + NTSTATUS Status; + + Status = SamDeleteAlias(AliasHandle); + if (!NT_SUCCESS(Status)) + { + printf("Failed to delete Alias: 0x%x\n",Status); + } + return(Status); +} + +NTSTATUS +CreateUser( LPWSTR * Parameter) +{ + PSID_NAME_USE Use = NULL; + PULONG Rid = NULL; + NTSTATUS Status; + UNICODE_STRING UserName; + ULONG RelativeId = 0; + ACCESS_MASK GrantedAccess; + + + RtlInitUnicodeString( + &UserName, + Parameter[0] + ); + + printf("Creating User %wZ\n",&UserName); + + Status= SamCreateUser2InDomain( + DomainHandle, + &UserName, + USER_NORMAL_ACCOUNT, + MAXIMUM_ALLOWED, + &UserHandle, + &GrantedAccess, + &RelativeId + ); + + if (!NT_SUCCESS(Status)) + { + printf("Failed to create User: 0x%x\n",Status); + return(Status); + } + printf("Created user with rid 0x%x, access 0x%x\n", + RelativeId, GrantedAccess); + return(Status); +} + +NTSTATUS +CreateGroup( LPWSTR * Parameter) +{ + PSID_NAME_USE Use = NULL; + PULONG Rid = NULL; + NTSTATUS Status; + UNICODE_STRING GroupName; + ULONG RelativeId = 0; + + + RtlInitUnicodeString( + &GroupName, + Parameter[0] + ); + + printf("Creating Group %wZ\n",&GroupName); + + Status= SamCreateGroupInDomain( + DomainHandle, + &GroupName, + MAXIMUM_ALLOWED, + &GroupHandle, + &RelativeId + ); + + if (!NT_SUCCESS(Status)) + { + printf("Failed to create Group: 0x%x\n",Status); + return(Status); + } + printf("Created Group with rid 0x%x, access 0x%x\n", + RelativeId); + return(Status); +} + +NTSTATUS +CreateAlias( LPWSTR * Parameter) +{ + PSID_NAME_USE Use = NULL; + PULONG Rid = NULL; + NTSTATUS Status; + UNICODE_STRING AliasName; + ULONG RelativeId = 0; + + + RtlInitUnicodeString( + &AliasName, + Parameter[0] + ); + + printf("Creating Alias %wZ\n",&AliasName); + + Status= SamCreateAliasInDomain( + DomainHandle, + &AliasName, + MAXIMUM_ALLOWED, + &AliasHandle, + &RelativeId + ); + + if (!NT_SUCCESS(Status)) + { + printf("Failed to create Alias: 0x%x\n",Status); + return(Status); + } + printf("Created Alias with rid 0x%x, access 0x%x\n", + RelativeId); + return(Status); +} + + + +NTSTATUS +GetGroupsForUser(LPWSTR * Parameter) +{ + NTSTATUS Status; + ULONG MembershipCount; + PULONG Attributes = NULL; + PULONG Rids = NULL; + ULONG Index; + PGROUP_MEMBERSHIP Groups = NULL; + + Status = SamGetGroupsForUser( + UserHandle, + &Groups, + &MembershipCount + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get groups for user: 0x%x\n",Status); + return(Status); + } + + for (Index = 0; Index < MembershipCount ; Index++ ) + { + printf("Member %d: rid 0x%x, attributes 0x%x\n", + Index,Groups[Index].RelativeId, Groups[Index].Attributes ); + } + SamFreeMemory(Groups); + return(STATUS_SUCCESS); + +} + +void +PrintLogonHours( + char * String, + PLOGON_HOURS LogonHours + ) +{ + int Index; + printf("%s",String); + for (Index = 0; Index < (LogonHours->UnitsPerWeek + 7) / 8 ;Index++ ) + { + printf("0x%2.2x ",LogonHours->LogonHours[Index]); + } + printf("\n"); +} + + +NTSTATUS +DumpUser(LPWSTR * Parameter) +{ + NTSTATUS Status; + PUSER_ALL_INFORMATION UserAll = NULL; + PUSER_GENERAL_INFORMATION UserGeneral = NULL; + PUSER_PREFERENCES_INFORMATION UserPreferences = NULL; + PUSER_LOGON_INFORMATION UserLogon = NULL; + PUSER_ACCOUNT_INFORMATION UserAccount = NULL; + PUSER_ACCOUNT_NAME_INFORMATION UserAccountName = NULL; + PUSER_FULL_NAME_INFORMATION UserFullName = NULL; + PUSER_NAME_INFORMATION UserName = NULL; + PUSER_PRIMARY_GROUP_INFORMATION UserPrimary = NULL; + PUSER_HOME_INFORMATION UserHome = NULL; + PUSER_SCRIPT_INFORMATION UserScript = NULL; + PUSER_PROFILE_INFORMATION UserProfile = NULL; + PUSER_ADMIN_COMMENT_INFORMATION UserAdminComment = NULL; + PUSER_WORKSTATIONS_INFORMATION UserWksta = NULL; + PUSER_CONTROL_INFORMATION UserControl = NULL; + PUSER_EXPIRES_INFORMATION UserExpires = NULL; + PUSER_LOGON_HOURS_INFORMATION UserLogonHours = NULL; + + printf("\nDumpUser.\n"); + Status = SamQueryInformationUser( + UserHandle, + UserAllInformation, + (PVOID *) &UserAll + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user all: 0x%x\n",Status); + return(Status); + } + printf("UserAll:\n"); + PrintTime("\tLastLogon = ",&UserAll->LastLogon); + PrintTime("\tLastLogoff = ",&UserAll->LastLogoff); + PrintTime("\tPasswordLastSet = ",&UserAll->PasswordLastSet); + PrintTime("\tAccountExpires = ",&UserAll->AccountExpires); + PrintTime("\tPasswordCanChange = ",&UserAll->PasswordCanChange); + PrintTime("\tPasswordMustChange = ",&UserAll->PasswordMustChange); + printf("\tUserName = %wZ\n",&UserAll->UserName); + printf("\tFullName = %wZ\n",&UserAll->FullName); + printf("\tHomeDirectory = %wZ\n",&UserAll->HomeDirectory); + printf("\tHomeDirectoryDrive = %wZ\n",&UserAll->HomeDirectoryDrive); + printf("\tScriptPath = %wZ\n",&UserAll->ScriptPath); + printf("\tProfilePath = %wZ\n",&UserAll->ProfilePath); + printf("\tAdminComment = %wZ\n",&UserAll->AdminComment); + printf("\tWorkStations = %wZ\n",&UserAll->WorkStations); + printf("\tUserComment = %wZ\n",&UserAll->UserComment); + printf("\tParameters = %wZ\n",&UserAll->Parameters); + printf("\tUserId = 0x%x\n",UserAll->UserId); + printf("\tPrimaryGroupId = 0x%x\n",UserAll->PrimaryGroupId); + printf("\tUserAccountControl = 0x%x\n",UserAll->UserAccountControl); + printf("\tWhichFields = 0x%x\n",UserAll->WhichFields); + PrintLogonHours("\tLogonHours = ",&UserAll->LogonHours); + printf("\tLogonCount = %d\n",UserAll->LogonCount); + printf("\tCountryCode = %d\n",UserAll->CountryCode); + printf("\tCodePage = %d\n",UserAll->CodePage); + + SamFreeMemory(UserAll); + + Status = SamQueryInformationUser( + UserHandle, + UserGeneralInformation, + (PVOID *) &UserGeneral + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user general: 0x%x\n",Status); + return(Status); + } + + printf("UserGeneral:\n"); + printf("\tUserName = %wZ\n",&UserGeneral->UserName); + printf("\tFullName = %wZ\n",&UserGeneral->FullName); + printf("\tPrimaryGroupId = 0x%x\n",UserGeneral->PrimaryGroupId); + printf("\tAdminComment = 0x%x\n",&UserGeneral->AdminComment); + printf("\tUserComment = 0x%x\n",&UserGeneral->UserComment); + + SamFreeMemory(UserGeneral); + + Status = SamQueryInformationUser( + UserHandle, + UserPreferencesInformation, + (PVOID *) &UserPreferences + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user preferences: 0x%x\n",Status); + return(Status); + } + + printf("UserPreferences:\n"); + printf("\tUserComment = %wZ\n",&UserPreferences->UserComment); + printf("\tReserved1 = %wZ\n",&UserPreferences->Reserved1); + printf("\tCountryCode = %d\n",&UserPreferences->CountryCode); + printf("\tCodePage = %d\n",&UserPreferences->CodePage); + + SamFreeMemory(UserPreferences); + + Status = SamQueryInformationUser( + UserHandle, + UserLogonInformation, + (PVOID *) &UserLogon + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user Logon: 0x%x\n",Status); + return(Status); + } + + + printf("UserLogon:\n"); + printf("\tUserName = %wZ\n",&UserLogon->UserName); + printf("\tFullName = %wZ\n",&UserLogon->FullName); + printf("\tUserId = 0x%x\n",UserLogon->UserId); + printf("\tPrimaryGroupId = 0x%x\n",UserLogon->PrimaryGroupId); + printf("\tHomeDirectory = %wZ\n",&UserLogon->HomeDirectory); + printf("\tHomeDirectoryDrive = %wZ\n",&UserLogon->HomeDirectoryDrive); + printf("\tScriptPath = %wZ\n",&UserLogon->ScriptPath); + printf("\tProfilePath = %wZ\n",&UserLogon->ProfilePath); + printf("\tWorkStations = %wZ\n",&UserLogon->WorkStations); + PrintTime("\tLastLogon = ",&UserLogon->LastLogon); + PrintTime("\tLastLogoff = ",&UserLogon->LastLogoff); + PrintTime("\tPasswordLastSet = ",&UserLogon->PasswordLastSet); + PrintTime("\tPasswordCanChange = ",&UserLogon->PasswordCanChange); + PrintTime("\tPasswordMustChange = ",&UserLogon->PasswordMustChange); + PrintLogonHours("\tLogonHours = ",&UserLogon->LogonHours); + printf("\tBadPasswordCount = %d\n",UserLogon->BadPasswordCount); + printf("\tLogonCount = %d\n",UserLogon->LogonCount); + printf("\tUserAccountControl = 0x%x\n",UserLogon->UserAccountControl); + + SamFreeMemory(UserLogon); + + Status = SamQueryInformationUser( + UserHandle, + UserAccountInformation, + (PVOID *) &UserAccount + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user account: 0x%x\n",Status); + return(Status); + } + + printf("UserAccount:\n"); + printf("\tUserName = %wZ\n",&UserAccount->UserName); + printf("\tFullName = %wZ\n",&UserAccount->FullName); + printf("\tUserId = 0x%x\n",UserAccount->UserId); + printf("\tPrimaryGroupId = 0x%x\n",UserAccount->PrimaryGroupId); + printf("\tHomeDirectory = %wZ\n",&UserAccount->HomeDirectory); + printf("\tHomeDirectoryDrive = %wZ\n",&UserAccount->HomeDirectoryDrive); + printf("\tScriptPath = %wZ\n",&UserAccount->ScriptPath); + printf("\tProfilePath = %wZ\n",&UserAccount->ProfilePath); + printf("\tAdminComment = %wZ\n",&UserAccount->AdminComment); + printf("\tWorkStations = %wZ\n",&UserAccount->WorkStations); + PrintTime("\tLastLogon = ",&UserAccount->LastLogon); + PrintTime("\tLastLogoff = ",&UserAccount->LastLogoff); + PrintLogonHours("\tLogonHours = ",&UserAccount->LogonHours); + printf("\tBadPasswordCount = %d\n",UserAccount->BadPasswordCount); + printf("\tLogonCount = %d\n",UserAccount->LogonCount); + PrintTime("\tPasswordLastSet = ",&UserAccount->PasswordLastSet); + PrintTime("\tAccountExpires = ",&UserAccount->AccountExpires); + printf("\tUserAccountControl = 0x%x\n",UserAccount->UserAccountControl); + + SamFreeMemory(UserAccount); + + Status = SamQueryInformationUser( + UserHandle, + UserAccountNameInformation, + (PVOID *) &UserAccountName + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user account name: 0x%x\n",Status); + return(Status); + } + + printf("UserAccountName:\n"); + printf("\tUserName = %wZ\n",&UserAccountName->UserName); + SamFreeMemory(UserAccountName); + + Status = SamQueryInformationUser( + UserHandle, + UserFullNameInformation, + (PVOID *) &UserFullName + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user full name: 0x%x\n",Status); + return(Status); + } + + printf("UserFullName:\n"); + printf("\tFullName = %wZ\n",&UserFullName->FullName); + SamFreeMemory(UserFullName); + + Status = SamQueryInformationUser( + UserHandle, + UserNameInformation, + (PVOID *) &UserName + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user name: 0x%x\n",Status); + return(Status); + } + + printf("UserName:\n"); + printf("\tUserName = %wZ\n",&UserName->UserName); + printf("\tFullName = %wZ\n",&UserName->FullName); + SamFreeMemory(UserName); + + Status = SamQueryInformationUser( + UserHandle, + UserPrimaryGroupInformation, + (PVOID *) &UserPrimary + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user all: 0x%x\n",Status); + return(Status); + } + printf("UserPrimaryGroup:\n"); + printf("PrimaryGroupid = 0x%x\n",UserPrimary->PrimaryGroupId); + SamFreeMemory(UserPrimary); + + Status = SamQueryInformationUser( + UserHandle, + UserHomeInformation, + (PVOID *) &UserHome + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user home: 0x%x\n",Status); + return(Status); + } + printf("UserHome:\n"); + printf("\tHomeDirectory = %wZ\n",&UserHome->HomeDirectory); + printf("\tHomeDirectoryDrive = %wZ\n",&UserHome->HomeDirectoryDrive); + + SamFreeMemory(UserHome); + + Status = SamQueryInformationUser( + UserHandle, + UserScriptInformation, + (PVOID *) &UserScript + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user Script: 0x%x\n",Status); + return(Status); + } + printf("UserScript:\n"); + printf("\tScriptPath = %wZ\n",&UserScript->ScriptPath); + + SamFreeMemory(UserScript); + + Status = SamQueryInformationUser( + UserHandle, + UserProfileInformation, + (PVOID *) &UserProfile + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user Profile: 0x%x\n",Status); + return(Status); + } + printf("UserProfile:\n"); + printf("\tProfilePath = %wZ\n",&UserProfile->ProfilePath); + + SamFreeMemory(UserProfile); + Status = SamQueryInformationUser( + UserHandle, + UserAdminCommentInformation, + (PVOID *) &UserAdminComment + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user AdminComment: 0x%x\n",Status); + return(Status); + } + printf("UserAdminComment:\n"); + printf("\tAdminComment = %wZ\n",&UserAdminComment->AdminComment); + SamFreeMemory(UserAdminComment); + + Status = SamQueryInformationUser( + UserHandle, + UserWorkStationsInformation, + (PVOID *) &UserWksta + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user wksta: 0x%x\n",Status); + return(Status); + } + + printf("UserWorkStations:\n"); + printf("\tWorkStations = %wZ\n",&UserWksta->WorkStations); + SamFreeMemory(UserWksta); + + Status = SamQueryInformationUser( + UserHandle, + UserControlInformation, + (PVOID *) &UserControl + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user Control: 0x%x\n",Status); + return(Status); + } + + printf("UserControl:\n"); + printf("\tUserAccountControl = 0x%x\n",UserControl->UserAccountControl); + SamFreeMemory(UserControl); + + Status = SamQueryInformationUser( + UserHandle, + UserExpiresInformation, + (PVOID *) &UserExpires + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user Expires: 0x%x\n",Status); + return(Status); + } + + printf("UserExpires:\n"); + PrintTime("\tAccountExpires = ",&UserExpires->AccountExpires); + SamFreeMemory(UserExpires); + + Status = SamQueryInformationUser( + UserHandle, + UserLogonHoursInformation, + (PVOID *) &UserLogonHours + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user LogonHours: 0x%x\n",Status); + return(Status); + } + printf("UserLogonHours:\n"); + PrintLogonHours("\tLogonHours = ",&UserLogonHours->LogonHours); + + SamFreeMemory(UserLogonHours); + + + return(STATUS_SUCCESS); +} + +NTSTATUS +DumpGroup(LPWSTR * Parameter) +{ + NTSTATUS Status; + PGROUP_GENERAL_INFORMATION General = NULL; + PGROUP_NAME_INFORMATION Name = NULL; + PGROUP_ATTRIBUTE_INFORMATION Attribute = NULL; + PGROUP_ADM_COMMENT_INFORMATION AdmComment = NULL; + + printf("\nDumpGroup.\n"); + Status = SamQueryInformationGroup( + GroupHandle, + GroupGeneralInformation, + (PVOID *) &General + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get group general information: 0x%x\n",Status); + return(Status); + } + + printf("Group General.Name = %wZ\n",&General->Name); + printf("Group General.Attributes = 0x%x\n",General->Attributes); + printf("Group general.memberCount = %d\n",General->MemberCount); + printf("Group general.AdminComment = %wZ\n",&General->AdminComment); + SamFreeMemory(General); + + Status = SamQueryInformationGroup( + GroupHandle, + GroupNameInformation, + (PVOID *) &Name + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get group name information: 0x%x\n",Status); + return(Status); + } + + printf("Group Name.Name = %wZ\n",&Name->Name); + SamFreeMemory(Name); + + Status = SamQueryInformationGroup( + GroupHandle, + GroupAttributeInformation, + (PVOID *) &Attribute + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get group Attribute information: 0x%x\n",Status); + return(Status); + } + + printf("Group Attribute.Attributes = 0x%x\n",Attribute->Attributes); + SamFreeMemory(Attribute); + + Status = SamQueryInformationGroup( + GroupHandle, + GroupAdminCommentInformation, + (PVOID *) &AdmComment + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get group admin comment information: 0x%x\n",Status); + return(Status); + } + + printf("Group Admin comment.AdminComment = %wZ\n",&AdmComment->AdminComment); + SamFreeMemory(AdmComment); + + return(STATUS_SUCCESS); +} + +NTSTATUS +DumpAllGroups(LPWSTR * Parameter) +{ + ULONG PreferedMax = 1000; + NTSTATUS Status,EnumStatus; + SAM_ENUMERATE_HANDLE EnumContext = 0; + ULONG CountReturned; + LPWSTR GroupName[1]; + + PSAM_RID_ENUMERATION Accounts = NULL; + + + GroupName[0] = (LPWSTR) malloc(128); + + printf("DumpAllGroups:\n"); + + + EnumContext = 0; + ASSERT(DomainHandle != NULL); + do + { + EnumStatus = SamEnumerateGroupsInDomain( + DomainHandle, + &EnumContext, + (PVOID *) &Accounts, + PreferedMax, + &CountReturned + ); + + if (NT_SUCCESS(EnumStatus) && (EnumStatus != STATUS_NO_MORE_ENTRIES)) + { + ULONG Index; + UNICODE_STRING SidString; + + for (Index = 0; Index < CountReturned; Index++) + { + RtlCopyMemory( + GroupName[0], + Accounts[Index].Name.Buffer, + Accounts[Index].Name.Length + ); + GroupName[0][Accounts[Index].Name.Length/sizeof(WCHAR)] = L'\0'; + + Status = OpenGroup(GroupName); + if (!NT_SUCCESS(Status)) + { + break; + } + Status = DumpGroup(NULL); + SamCloseHandle(GroupHandle); + GroupHandle = NULL; + + } + SamFreeMemory(Accounts); + } + else printf("Failed to enumerate Groups: 0x%x\n",Status); + } while (NT_SUCCESS(EnumStatus) && (EnumStatus != STATUS_SUCCESS) && (CountReturned != 0) ); + + free(GroupName[0]); + return(STATUS_SUCCESS); +} + +NTSTATUS +DumpAllUsers(LPWSTR * Parameter) +{ + ULONG PreferedMax = 1000; + NTSTATUS Status,EnumStatus; + SAM_ENUMERATE_HANDLE EnumContext = 0; + ULONG CountReturned; + LPWSTR UserName[1]; + + PSAM_RID_ENUMERATION Accounts = NULL; + + + UserName[0] = (LPWSTR) malloc(128); + + printf("DumpAllUsers:\n"); + + + EnumContext = 0; + ASSERT(DomainHandle != NULL); + do + { + EnumStatus = SamEnumerateUsersInDomain( + DomainHandle, + &EnumContext, + 0, + (PVOID *) &Accounts, + PreferedMax, + &CountReturned + ); + + if (NT_SUCCESS(EnumStatus) && (EnumStatus != STATUS_NO_MORE_ENTRIES)) + { + ULONG Index; + UNICODE_STRING SidString; + + for (Index = 0; Index < CountReturned; Index++) + { + RtlCopyMemory( + UserName[0], + Accounts[Index].Name.Buffer, + Accounts[Index].Name.Length + ); + UserName[0][Accounts[Index].Name.Length/sizeof(WCHAR)] = L'\0'; + + Status = OpenUser(UserName); + if (!NT_SUCCESS(Status)) + { + break; + } + Status = DumpUser(NULL); + Status = GetGroupsForUser(NULL); + SamCloseHandle(UserHandle); + UserHandle = NULL; + + } + SamFreeMemory(Accounts); + } + else printf("Failed to enumerate users: 0x%x\n",Status); + } while (NT_SUCCESS(EnumStatus) && (EnumStatus != STATUS_SUCCESS) && (CountReturned != 0) ); + + free(UserName[0]); + return(STATUS_SUCCESS); +} + +NTSTATUS +AddAliasMember( LPWSTR * Parameter ) +{ + BYTE Buffer[100]; + PSID AccountSid = Buffer; + ULONG SidLen = 100; + SID_NAME_USE Use; + WCHAR ReferencedDomain[100]; + ULONG DomainLen = 100; + NTSTATUS Status; + + printf("Adding account %ws to alias\n",Parameter[0]); + if (!LookupAccountName( + NULL, + Parameter[0], + AccountSid, + &SidLen, + ReferencedDomain, + &DomainLen, + &Use)) + { + printf("Failed to lookup account name: %d\n",GetLastError()); + return(STATUS_UNSUCCESSFUL); + } + + Status = SamAddMemberToAlias( + AliasHandle, + AccountSid + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to add member to alias: 0x%x\n",Status); + } + return(Status); +} + +NTSTATUS +DumpDomain( LPWSTR * Parameter ) +{ + NTSTATUS Status; + PDOMAIN_PASSWORD_INFORMATION Password = NULL; + PDOMAIN_GENERAL_INFORMATION General = NULL; + PDOMAIN_LOGOFF_INFORMATION Logoff = NULL; + PDOMAIN_OEM_INFORMATION Oem = NULL; + PDOMAIN_NAME_INFORMATION Name = NULL; + PDOMAIN_REPLICATION_INFORMATION Replica = NULL; + PDOMAIN_SERVER_ROLE_INFORMATION ServerRole = NULL; + PDOMAIN_MODIFIED_INFORMATION Modified = NULL; + PDOMAIN_STATE_INFORMATION State = NULL; + PDOMAIN_GENERAL_INFORMATION2 General2 = NULL; + PDOMAIN_LOCKOUT_INFORMATION Lockout = NULL; + PDOMAIN_MODIFIED_INFORMATION2 Modified2 = NULL; + + + Status = SamQueryInformationDomain( + DomainHandle, + DomainPasswordInformation, + (PVOID *) &Password + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query password information: 0x%x\n",Status); + return(Status); + } + + printf("Password:\n"); + printf("\tMinPasswordLength = %d\n",Password->MinPasswordLength); + printf("\tPasswordHistoryLength = %d\n",Password->PasswordHistoryLength); + printf("\tPasswordProperties = 0x%x\n",Password->PasswordProperties); + PrintDeltaTime("\tMaxPasswordAge = ",&Password->MaxPasswordAge); + PrintDeltaTime("\tMinPasswordAge = ",&Password->MinPasswordAge); + + SamFreeMemory(Password); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainGeneralInformation, + (PVOID *) &General + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query general: 0x%x\n",Status); + return(Status); + } + + printf("General:\n"); + PrintDeltaTime("\t ForceLogoff = ",&General->ForceLogoff); + printf("\t OemInformation = %wZ\n",&General->OemInformation); + printf("\t DomainName = %wZ\n",&General->DomainName); + printf("\t ReplicaSourceNodeName =%wZ\n",&General->ReplicaSourceNodeName); + printf("\t DomainModifiedCount = 0x%x,0x%x\n", + General->DomainModifiedCount.HighPart, + General->DomainModifiedCount.LowPart ); + printf("\t DomainServerState = %d\n",General->DomainServerState); + printf("\t DomainServerRole = %d\n",General->DomainServerRole); + printf("\t UasCompatibilityRequired = %d\n",General->UasCompatibilityRequired); + printf("\t UserCount = %d\n",General->UserCount); + printf("\t GroupCount = %d\n",General->GroupCount); + printf("\t AliasCount = %d\n",General->AliasCount); + + SamFreeMemory(General); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainLogoffInformation, + (PVOID *) &Logoff + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query logoff: 0x%x\n",Status); + return(Status); + } + + printf("Logoff:\n"); + PrintDeltaTime("\t ForceLogoff = ",&Logoff->ForceLogoff); + SamFreeMemory(Logoff); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainOemInformation, + (PVOID *) &Oem + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query Oem: 0x%x\n",Status); + return(Status); + } + + printf("Oem:\n\t OemInformation = %wZ\n",&Oem->OemInformation); + + SamFreeMemory(Oem); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainNameInformation, + (PVOID *) &Name + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query Name: 0x%x\n",Status); + return(Status); + } + printf("Name:\n\t DomainName = %wZ\n",&Name->DomainName); + + SamFreeMemory(Name); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainReplicationInformation, + (PVOID *) &Replica + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query Replica: 0x%x\n",Status); + return(Status); + } + + printf("Replica:\n\t ReplicaSourceNodeName = %wZ\n", &Replica->ReplicaSourceNodeName); + SamFreeMemory(Replica); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainServerRoleInformation, + (PVOID *) &ServerRole + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query ServerRole: 0x%x\n",Status); + return(Status); + } + + printf("ServerRole:\n\t DomainServerRole = %d\n",ServerRole->DomainServerRole); + SamFreeMemory(ServerRole); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainModifiedInformation, + (PVOID *) &Modified + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query Modified: 0x%x\n",Status); + return(Status); + } + + printf("Modified:\n"); + printf("\t DomainModifiedCount = 0x%x,0x%x\n", + Modified->DomainModifiedCount.HighPart, + Modified->DomainModifiedCount.LowPart ); + PrintTime("\t CreationTime = ",&Modified->CreationTime); + + + + SamFreeMemory(Modified); + + + Status = SamQueryInformationDomain( + DomainHandle, + DomainStateInformation, + (PVOID *) &State + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query State: 0x%x\n",Status); + return(Status); + } + + printf("State:\n\t DomainServerState = %d\n",State->DomainServerState); + SamFreeMemory(State); + + + Status = SamQueryInformationDomain( + DomainHandle, + DomainGeneralInformation2, + (PVOID *) &General2 + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query General2: 0x%x\n",Status); + return(Status); + } + + printf("General2:\n"); + General = &General2->I1; + PrintDeltaTime("\t ForceLogoff = ",&General->ForceLogoff); + printf("\t OemInformation = %wZ\n",&General->OemInformation); + printf("\t DomainName = %wZ\n",&General->DomainName); + printf("\t ReplicaSourceNodeName =%wZ\n",&General->ReplicaSourceNodeName); + printf("\t DomainModifiedCount = 0x%x,0x%x\n", + General->DomainModifiedCount.HighPart, + General->DomainModifiedCount.LowPart ); + printf("\t DomainServerState = %d\n",General->DomainServerState); + printf("\t DomainServerRole = %d\n",General->DomainServerRole); + printf("\t UasCompatibilityRequired = %d\n",General->UasCompatibilityRequired); + printf("\t UserCount = %d\n",General->UserCount); + printf("\t GroupCount = %d\n",General->GroupCount); + printf("\t AliasCount = %d\n",General->AliasCount); + PrintDeltaTime("\t LockoutDuration = ",&General2->LockoutDuration); + PrintDeltaTime("\t LockoutObservationWindow = ",&General2->LockoutObservationWindow); + printf("\t LockoutThreshold = %d\n",General2->LockoutThreshold); + + SamFreeMemory(General2); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainLockoutInformation, + (PVOID *) &Lockout + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query Lockout: 0x%x\n",Status); + return(Status); + } + printf("Lockout:\n"); + PrintDeltaTime("\t LockoutDuration = ",&Lockout->LockoutDuration); + PrintDeltaTime("\t LockoutObservationWindow = ",&Lockout->LockoutObservationWindow); + printf("\t LockoutThreshold = %d\n",Lockout->LockoutThreshold); + + SamFreeMemory(Lockout); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainModifiedInformation2, + (PVOID *) &Modified2 + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query Modified2: 0x%x\n",Status); + return(Status); + } + printf("Modified2:\n"); + PrintTime("\t CreationTime = ",&Modified->CreationTime); + printf("\t DomainModifiedCount = 0x%x,0x%x\n", + Modified2->DomainModifiedCount.HighPart, + Modified2->DomainModifiedCount.LowPart ); + + printf("\t ModifiedCountAtLastPromotion = 0x%x,0x%x\n", + Modified2->ModifiedCountAtLastPromotion.HighPart, + Modified2->ModifiedCountAtLastPromotion.LowPart ); + + SamFreeMemory(Modified2); + + return(STATUS_SUCCESS); + +} + + + +void _cdecl +main(int argc, char *argv[]) +{ + ULONG Command = 0; + ULONG i,j,k; + BOOLEAN Found; + NTSTATUS Status; + Action Actions[20]; + ULONG ActionCount = 0; + + for (i = 1; i < (ULONG) argc ; i++ ) + { + Found = FALSE; + for (j = 0; j < NUM_COMMANDS ; j++ ) + { + if (!_stricmp(argv[i],Commands[j].Name)) + { + Actions[ActionCount].CommandNumber = j; + + if (Commands[j].Parameter != 0) + { + for (k = 0; k < Commands[j].Parameter ;k++ ) + { + Actions[ActionCount].Parameter[k] = (LPWSTR) malloc(128); + if ((ULONG) argc > i) + { + mbstowcs(Actions[ActionCount].Parameter[k],argv[++i],128); + } + else + { + Actions[ActionCount].Parameter[k][0] = L'\0'; + } + } + } + Found = TRUE; + ActionCount++; + break; + } + } + if (!Found) + { + printf("Switch %s not found\n", argv[i]); + return; + } + } + +// Status = OpenSam(); +// if (!NT_SUCCESS(Status)) +// { +// printf("Failed to open sam: 0x%x\n",Status); +// return; +// } + + for (i = 0; i < ActionCount ; i++ ) + { + Status = Commands[Actions[i].CommandNumber].Function(Actions[i].Parameter); + if (!NT_SUCCESS(Status)) + { + printf("Failed test %s : 0x%x\n",Commands[Actions[i].CommandNumber].Name,Status); + goto Cleanup; + + } + } + +Cleanup: + if (DomainHandle != NULL) + { + Status = SamCloseHandle(DomainHandle); + if (!NT_SUCCESS(Status)) + { + printf("Failed to close account: 0x%x\n",Status); + } + } + if (GroupHandle != NULL) + { + Status = SamCloseHandle(GroupHandle); + if (!NT_SUCCESS(Status)) + { + printf("Failed to close account: 0x%x\n",Status); + } + } + if (AliasHandle != NULL) + { + Status = SamCloseHandle(AliasHandle); + if (!NT_SUCCESS(Status)) + { + printf("Failed to close account: 0x%x\n",Status); + } + } + if (UserHandle != NULL) + { + Status = SamCloseHandle(UserHandle); + if (!NT_SUCCESS(Status)) + { + printf("Failed to close account: 0x%x\n",Status); + } + } + Status = CloseSam(); + if (!NT_SUCCESS(Status)) + { + printf("Failed to close lsa: 0x%x\n",Status); + } + + return; + +} diff --git a/private/newsam2/server/utest/makefile b/private/newsam2/server/utest/makefile new file mode 100644 index 000000000..bdd684681 --- /dev/null +++ b/private/newsam2/server/utest/makefile @@ -0,0 +1,29 @@ +############################################################################ +# +# Copyright (C) 1992, Microsoft Corporation. +# +# All rights reserved. +# +############################################################################ + +!if "$(NTMAKEENV)" != "" + +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def + +!else + + +default: all + +!include filelist.mk +!include $(SECURITY)\security.mk +!include $(COMMON)\src\win40.mk +!include depend.mk + + +!endif diff --git a/private/newsam2/server/utest/samtest.cxx b/private/newsam2/server/utest/samtest.cxx new file mode 100644 index 000000000..5711d8104 --- /dev/null +++ b/private/newsam2/server/utest/samtest.cxx @@ -0,0 +1,2033 @@ +//+----------------------------------------------------------------------- +// +// Microsoft Windows +// +// Copyright (c) Microsoft Corporation 1992 - 1992 +// +// File: secret.cxx +// +// Contents: test program to check the setup of a Cairo installation +// +// +// History: 22-Dec-92 Created MikeSw +// +//------------------------------------------------------------------------ + + +extern "C" +{ +#include <nt.h> +#include <ntrtl.h> +#include <nturtl.h> +#include <ntsam.h> +#include <ntlsa.h> +#include <windows.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <caiseapi.h> +} + +typedef NTSTATUS (TestFunc)( WCHAR *Parameter[]); + +typedef struct _Commands { + PSTR Name; + ULONG Parameter; // TRUE = yes, FALSE = no + TestFunc *Function; +} CommandPair, *PCommandPair; + + +typedef struct _Action { + ULONG CommandNumber; + LPWSTR Parameter[8]; +} Action, *PAction; + + + +TestFunc OpenDomain; +TestFunc EnumDomains; +TestFunc EnumAccounts; +TestFunc QueryDisplay; +TestFunc OpenGroup; +TestFunc GroupMembers; +TestFunc OpenAlias; +TestFunc AliasMembers; +TestFunc GetAliasMembership; +TestFunc OpenUser; +TestFunc GetGroupsForUser; +TestFunc DumpAllUsers; +TestFunc DumpAllGroups; +TestFunc DumpUser; +TestFunc DumpGroup; +TestFunc CreateUser; +TestFunc AddAliasMember; +TestFunc CreateGroup; +TestFunc CreateAlias; +TestFunc DumpDomain; +TestFunc Connect; +TestFunc DelUser; +TestFunc DelAlias; +TestFunc DelGroup; + +CommandPair Commands[] = { + {"-od",1,OpenDomain}, + {"-ed",0,EnumDomains}, + {"-ea",1,EnumAccounts}, + {"-qd",1,QueryDisplay}, + {"-og",1,OpenGroup}, + {"-gm",0,GroupMembers}, + {"-oa",1,OpenAlias}, + {"-am",0,AliasMembers}, + {"-gam",1,GetAliasMembership}, + {"-ou",1,OpenUser}, + {"-ggu",0,GetGroupsForUser}, + {"-dau",0,DumpAllUsers}, + {"-dag",0,DumpAllGroups}, + {"-du",0,DumpUser}, + {"-dg",0,DumpGroup}, + {"-cu",1,CreateUser}, + {"-aam",1,AddAliasMember}, + {"-cg",1,CreateGroup}, + {"-ca",1,CreateAlias}, + {"-dd",0,DumpDomain}, + {"-c",1,Connect}, + {"-delu",0,DelUser}, + {"-dela",0,DelAlias}, + {"-delg",0,DelGroup} + + + +}; + +#define NUM_COMMANDS (sizeof(Commands) / sizeof(CommandPair)) + +SAM_HANDLE SamHandle; +SAM_HANDLE DomainHandle; +SAM_HANDLE GroupHandle; +SAM_HANDLE AliasHandle; +SAM_HANDLE UserHandle; + +UNICODE_STRING ServerName; + +//+------------------------------------------------------------------------- +// +// Function: PrintTime +// +// Synopsis: Prints a text representation of a FILETIME interpreted +// as an absolute date/time +// +// Arguments: [rft] -- time to print +// +// Notes: Used only by dump functions. +// +//-------------------------------------------------------------------------- + +void +PrintTime ( + char * String, + PVOID Time + ) +{ + SYSTEMTIME st; + + FileTimeToSystemTime ( (PFILETIME) Time, & st ); + printf("%s %d-%d-%d %d:%2.2d:%2.2d\n", String, st.wMonth, st.wDay, st.wYear, + st.wHour, st.wMinute, st.wSecond ); +} + +//+------------------------------------------------------------------------- +// +// Function: SpmDbDeltaTimeToString +// +// Synopsis: Converts a time delta to a string. +// +// Effects: +// +// Arguments: +// +// Requires: +// +// Returns: +// +// Notes: +// +// +//-------------------------------------------------------------------------- + + +VOID +PrintDeltaTime( + IN LPSTR Message, + IN PLARGE_INTEGER Time + ) +{ + ULONG Seconds; + ULONG Minutes; + ULONG Hours; + ULONG Days; + ULONG Chars; + CHAR TimeBuffer[256] = ""; + LPSTR TimeString = TimeBuffer; + LARGE_INTEGER DeltaTime; + + DeltaTime.QuadPart = -Time->QuadPart; + + Seconds = (ULONG) (DeltaTime.QuadPart / 10000000); + + Minutes = Seconds / 60; + Hours = Minutes / 60; + Days = Hours / 24; + + Hours = Hours % 24; + Minutes = Minutes % 60; + Seconds = Seconds % 60; + + if (Days >= 1) + { + Chars = sprintf(TimeString,"%d days ",Days); + TimeString += Chars; + } + if (Hours >= 1 ) + { + Chars = sprintf(TimeString,"%d hours ",Hours); + TimeString += Chars; + } + + if (Minutes >= 1 && Days == 0) + { + Chars = sprintf(TimeString,"%d minutes ",Minutes); + TimeString += Chars; + } + + if (Seconds >= 1 && (Days == 0) && (Hours == 0) ) + { + Chars = sprintf(TimeString,"%d seconds ",Seconds); + TimeString += Chars; + } + + printf("%s %s\n",Message,TimeBuffer); + +} + + +NTSTATUS +Connect( LPWSTR * Parameter) +{ + OBJECT_ATTRIBUTES oa; + NTSTATUS Status; + + RtlInitUnicodeString( + &ServerName, + Parameter[0] + ); + + InitializeObjectAttributes(&oa,NULL,0,NULL,NULL); + + Status = SamConnect( + &ServerName, + &SamHandle, + MAXIMUM_ALLOWED, + &oa); + return(Status); +} + +NTSTATUS +CloseSam() +{ + return(SamCloseHandle(SamHandle)); +} + + + +NTSTATUS +EnumDomains( + LPWSTR * Parameter ) +{ + NTSTATUS Status; + SHORT Language; + SAM_ENUMERATE_HANDLE Context = 0; + PSAM_RID_ENUMERATION Buffer = NULL; + ULONG Count = 0; + ULONG i; + + Status = SamEnumerateDomainsInSamServer( + SamHandle, + &Context, + (PVOID *) &Buffer, + 2000, + &Count + ); + + if (!NT_SUCCESS(Status)) + { + return(Status); + } + + + for (i = 0; i < Count ; i++ ) + { + printf("Domain = %wZ\n",&Buffer[i].Name); + } + SamFreeMemory(Buffer); + return(STATUS_SUCCESS); +} + +NTSTATUS +OpenDomain( LPWSTR * Parameter ) +{ + GUID DomainGuid; + BOOLEAN fBuiltin; + NTSTATUS Status; + CAIROSID DomainSid; + SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; + + if (!_wcsicmp(Parameter[0],L"Builtin")) + { + fBuiltin = TRUE; + } + else if (!_wcsicmp(Parameter[0],L"Account")) + { + fBuiltin = FALSE; + } + else + { + printf("Invalid domain to open: %ws\n",Parameter[0]); + return(STATUS_UNSUCCESSFUL); + } + + if (fBuiltin) + { + DomainSid.Revision = SID_REVISION; + DomainSid.SubAuthorityCount = 1; + DomainSid.IdentifierAuthority = NtAuthority; + DomainSid.ZerothSubAuthority = SECURITY_BUILTIN_DOMAIN_RID; + } + else + { + LSA_HANDLE LsaHandle = NULL; + OBJECT_ATTRIBUTES Oa; + PPOLICY_ACCOUNT_DOMAIN_INFO DomainInfo = NULL; + + RtlZeroMemory(&Oa, sizeof(OBJECT_ATTRIBUTES)); + Status = LsaOpenPolicy( + &ServerName, + &Oa, + POLICY_VIEW_LOCAL_INFORMATION, + &LsaHandle + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to open policy: 0x%x\n",Status); + return(Status); + } + Status = LsaQueryInformationPolicy( + LsaHandle, + PolicyAccountDomainInformation, + (PVOID *) &DomainInfo + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query account domain: 0x%x\n",Status); + LsaClose(LsaHandle); + return(Status); + } + RtlCopyMemory( + &DomainSid, + DomainInfo->DomainSid, + RtlLengthSid(DomainInfo->DomainSid) + ); + LsaFreeMemory(DomainInfo); + } + + Status = SamOpenDomain( + SamHandle, + MAXIMUM_ALLOWED, + (PSID) &DomainSid, + &DomainHandle + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to open domain: 0x%x\n",Status); + } + return(Status); + +} + +NTSTATUS +EnumAccounts( LPWSTR * Parameter ) +{ + ULONG PreferedMax = 100; + NTSTATUS Status; + SAM_ENUMERATE_HANDLE EnumContext = 0; + ULONG CountReturned; + + PSAM_RID_ENUMERATION Accounts = NULL; + + swscanf(Parameter[0],L"%d",&PreferedMax); + + printf("EnumAccounts: %d\n",PreferedMax); + + + EnumContext = 0; + ASSERT(DomainHandle != NULL); + do + { + Status = SamEnumerateUsersInDomain( + DomainHandle, + &EnumContext, + 0, + (PVOID *) &Accounts, + PreferedMax, + &CountReturned + ); + + if (NT_SUCCESS(Status) && (Status != STATUS_NO_MORE_ENTRIES)) + { + ULONG Index; + UNICODE_STRING SidString; + + for (Index = 0; Index < CountReturned; Index++) + { + printf("Account : %wZ 0x%x\n",&Accounts[Index].Name, Accounts[Index].RelativeId); + } + SamFreeMemory(Accounts); + } + else printf("Failed to enumerate users: 0x%x\n",Status); + } while (NT_SUCCESS(Status) && (Status != STATUS_SUCCESS) && (CountReturned != 0) ); + + EnumContext = 0; + do + { + Status = SamEnumerateGroupsInDomain( + DomainHandle, + &EnumContext, + (PVOID *) &Accounts, + PreferedMax, + &CountReturned + ); + + if (NT_SUCCESS(Status) && (Status != STATUS_NO_MORE_ENTRIES)) + { + ULONG Index; + UNICODE_STRING SidString; + + for (Index = 0; Index < CountReturned; Index++) + { + printf("Group : %wZ 0x%x\n",&Accounts[Index].Name, Accounts[Index].RelativeId); + } + SamFreeMemory(Accounts); + } + else printf("Failed to enumerate Groups: 0x%x\n",Status); + } while (NT_SUCCESS(Status) && (CountReturned != 0) ); // && (Status != STATUS_SUCCESS) + + + EnumContext = 0; + do + { + Status = SamEnumerateAliasesInDomain( + DomainHandle, + &EnumContext, + (PVOID *) &Accounts, + PreferedMax, + &CountReturned + ); + + if (NT_SUCCESS(Status) && (Status != STATUS_NO_MORE_ENTRIES)) + { + ULONG Index; + UNICODE_STRING SidString; + + for (Index = 0; Index < CountReturned; Index++) + { + printf("Alias : %wZ 0x%x\n",&Accounts[Index].Name, Accounts[Index].RelativeId); + } + SamFreeMemory(Accounts); + } + else printf("Failed to enumerate aliases: 0x%x\n",Status); + } while (NT_SUCCESS(Status) && (CountReturned != 0) ); // && (Status != STATUS_SUCCESS) + + + + return(Status); +} + + +NTSTATUS +QueryDisplay( LPWSTR * Parameter ) +{ + NTSTATUS Status; + DOMAIN_DISPLAY_INFORMATION Type; + PVOID Buffer = NULL; + ULONG TotalAvailable = 0; + ULONG TotalReturned = 0; + ULONG ReturnedCount = 0; + ULONG Index; + ULONG SamIndex = 0; + + if (!_wcsicmp(Parameter[0],L"user")) + { + Type = DomainDisplayUser; + } else if (!_wcsicmp(Parameter[0],L"Machine")) + { + Type = DomainDisplayMachine; + } else if (!_wcsicmp(Parameter[0],L"Group")) + { + Type = DomainDisplayGroup; + } else if (!_wcsicmp(Parameter[0],L"OemUser")) + { + Type = DomainDisplayOemUser; + } else if (!_wcsicmp(Parameter[0],L"OemGroup")) + { + Type = DomainDisplayOemGroup; + } else { + printf("Invalid parameter %ws\n", Parameter[0]); + return(STATUS_INVALID_PARAMETER); + } + + do + { + Status = SamQueryDisplayInformation( + DomainHandle, + Type, + SamIndex, + 5, + 1000, + &TotalAvailable, + &TotalReturned, + &ReturnedCount, + &Buffer + ); + + if (NT_SUCCESS(Status) && (ReturnedCount > 0)) + { + printf("Total returned = %d\t total available = %d\n", + TotalReturned, TotalAvailable); + switch(Type) { + case DomainDisplayUser: + { + PDOMAIN_DISPLAY_USER Users = (PDOMAIN_DISPLAY_USER) Buffer; + for (Index = 0; Index < ReturnedCount ; Index++ ) + { + printf("User %d: Index %d\n Rid 0x%x\n Control 0x%x\n name %wZ\n Comment %wZ\n Full Name %wZ\n", + Index, + Users[Index].Index, + Users[Index].Rid, + Users[Index].AccountControl, + &Users[Index].LogonName, + &Users[Index].AdminComment, + &Users[Index].FullName + ); + } + break; + } + case DomainDisplayGroup: + { + PDOMAIN_DISPLAY_GROUP Groups = (PDOMAIN_DISPLAY_GROUP) Buffer; + for (Index = 0; Index < ReturnedCount ; Index++ ) + { + printf("Group %d\n Index %d\n Rid 0x%x\n Attributes 0x%x\n name %wZ\n Comment %wZ\n", + Index, + Groups[Index].Index, + Groups[Index].Rid, + Groups[Index].Attributes, + &Groups[Index].Group, + &Groups[Index].Comment + ); + + } + break; + } + case DomainDisplayMachine: + { + PDOMAIN_DISPLAY_MACHINE Machines = (PDOMAIN_DISPLAY_MACHINE) Buffer; + for (Index = 0; Index < ReturnedCount ; Index++ ) + { + printf("Machine %d\n Index %d\n Rid 0x%x\n Control 0x%x\n Name %wZ\n Comment %wZ\n", + Index, + Machines[Index].Index, + Machines[Index].Rid, + Machines[Index].AccountControl, + &Machines[Index].Machine, + &Machines[Index].Comment + ); + } + break; + } + case DomainDisplayOemUser: + { + PDOMAIN_DISPLAY_OEM_USER OemUsers = (PDOMAIN_DISPLAY_OEM_USER) Buffer; + for (Index = 0; Index < ReturnedCount ; Index++ ) + { + printf("OemUser %d\n Index %d\n Name %Z\n", + Index, + OemUsers[Index].Index, + &OemUsers[Index].User + ); + } + break; + } + case DomainDisplayOemGroup: + { + PDOMAIN_DISPLAY_OEM_GROUP OemGroups = (PDOMAIN_DISPLAY_OEM_GROUP) Buffer; + for (Index = 0; Index < ReturnedCount ; Index++ ) + { + printf("OemGroup %d\n Index %d\n Name %Z\n", + Index, + OemGroups[Index].Index, + &OemGroups[Index].Group + ); + } + break; + } + + } + SamFreeMemory(Buffer); + SamIndex += ReturnedCount; + } + + + } while (NT_SUCCESS(Status) && (ReturnedCount > 0)); + printf("QDI returned 0x%x\n",Status); + + return(Status); + + +} + +NTSTATUS +OpenGroup( LPWSTR * Parameter) +{ + PSID_NAME_USE Use = NULL; + PULONG Rid = NULL; + NTSTATUS Status; + UNICODE_STRING GroupName; + ULONG RelativeId = 0; + +// swscanf(Parameter[0],L"%x",&RelativeId); + if (RelativeId == 0) + { + RtlInitUnicodeString( + &GroupName, + Parameter[0] + ); + + printf("Looking up group %wZ\n",&GroupName); + + Status = SamLookupNamesInDomain( + DomainHandle, + 1, + &GroupName, + &Rid, + &Use + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to lookup group: 0x%x\n",Status); + return(Status); + } + RelativeId = *Rid; + SamFreeMemory(Rid); + SamFreeMemory(Use); + } + + printf("Opening Group 0x%x\n",RelativeId); + Status= SamOpenGroup( + DomainHandle, + MAXIMUM_ALLOWED, // GROUP_LIST_MEMBERS | GROUP_READ_INFORMATION, + RelativeId, + &GroupHandle + ); + + if (!NT_SUCCESS(Status)) + { + printf("Failed to open group: 0x%x\n",Status); + } + return(Status); +} + +NTSTATUS +GroupMembers(LPWSTR * Parameter) +{ + NTSTATUS Status; + ULONG MembershipCount; + PULONG Attributes = NULL; + PULONG Rids = NULL; + ULONG Index; + + Status = SamGetMembersInGroup( + GroupHandle, + &Rids, + &Attributes, + &MembershipCount + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get members in group: 0x%x\n",Status); + return(Status); + } + + for (Index = 0; Index < MembershipCount ; Index++ ) + { + printf("Member %d: rid 0x%x, attributes 0x%x\n", + Index,Rids[Index],Attributes[Index]); + } + SamFreeMemory(Rids); + SamFreeMemory(Attributes); + return(STATUS_SUCCESS); + +} + + +NTSTATUS +OpenAlias( LPWSTR * Parameter) +{ + PSID_NAME_USE Use = NULL; + PULONG Rid = NULL; + NTSTATUS Status; + UNICODE_STRING AliasName; + ULONG RelativeId; + + swscanf(Parameter[0],L"%x",&RelativeId); + if (RelativeId == 0) + { + RtlInitUnicodeString( + &AliasName, + Parameter[0] + ); + + printf("Looking up Alias %wZ\n",&AliasName); + + Status = SamLookupNamesInDomain( + DomainHandle, + 1, + &AliasName, + &Rid, + &Use + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to lookup Alias: 0x%x\n",Status); + return(Status); + } + RelativeId = *Rid; + SamFreeMemory(Rid); + SamFreeMemory(Use); + } + + + printf("Opening Alias 0x%x\n",RelativeId); + Status= SamOpenAlias( + DomainHandle, + ALIAS_LIST_MEMBERS | ALIAS_ADD_MEMBER, + RelativeId, + &AliasHandle + ); + + if (!NT_SUCCESS(Status)) + { + printf("Failed to open alias: 0x%x\n",Status); + } + return(Status); +} + +NTSTATUS +AliasMembers(LPWSTR * Parameter) +{ + NTSTATUS Status; + ULONG MembershipCount; + PSID * Members = NULL; + ULONG Index; + UNICODE_STRING Sid; + + Status = SamGetMembersInAlias( + AliasHandle, + &Members, + &MembershipCount + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get members in Alias: 0x%x\n",Status); + return(Status); + } + + for (Index = 0; Index < MembershipCount ; Index++ ) + { + RtlConvertSidToUnicodeString( + &Sid, + Members[Index], + TRUE + ); + printf("Member %d: sid %wZ\n", + Index,&Sid); + RtlFreeUnicodeString(&Sid); + } + SamFreeMemory(Members); + return(STATUS_SUCCESS); + +} + + +NTSTATUS +GetAliasMembership(LPWSTR * Parameter) +{ + NTSTATUS Status; + ULONG Index; + UNICODE_STRING Name; + OBJECT_ATTRIBUTES Oa; + LSA_HANDLE LsaHandle = NULL; + CAIROSID Sid; + ULONG SidLength = sizeof(CAIROSID); + WCHAR ReferencedDomainName[100]; + ULONG DomainNameLength = 100; + SID_NAME_USE SidUse; + ULONG MembershipCount; + PULONG AliasList = NULL; + PSID SidAddress = (PSID) &Sid; + + + + printf("Looking up groups for user %ws\n",Parameter[0]); + + if (!LookupAccountNameW( + NULL, + Parameter[0], + SidAddress, + &SidLength, + ReferencedDomainName, + &DomainNameLength, + &SidUse)) + { + printf("Failed to lookup account sid: %d\n",GetLastError()); + return(STATUS_UNSUCCESSFUL); + } + + + + Status = SamGetAliasMembership( + DomainHandle, + 1, + &SidAddress, + &MembershipCount, + &AliasList + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get alises : 0x%x\n",Status); + return(Status); + } + + for (Index = 0; Index < MembershipCount ; Index++ ) + { + printf("Alias Member %d: rid 0x%x\n", + Index,AliasList[Index]); + + } + SamFreeMemory(AliasList); + return(STATUS_SUCCESS); + +} + +NTSTATUS +GetAccountRid( LPWSTR Parameter, + PULONG RelativeId) +{ + + PSID_NAME_USE Use = NULL; + PULONG Rid = NULL; + NTSTATUS Status; + UNICODE_STRING UserName; + + RtlInitUnicodeString( + &UserName, + Parameter + ); + + printf("Looking up User %wZ\n",&UserName); + + Status = SamLookupNamesInDomain( + DomainHandle, + 1, + &UserName, + &Rid, + &Use + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to lookup User: 0x%x\n",Status); + return(Status); + } + *RelativeId = *Rid; + SamFreeMemory(Rid); + SamFreeMemory(Use); + + return(STATUS_SUCCESS); +} +NTSTATUS +OpenUser( LPWSTR * Parameter) +{ + PSID_NAME_USE Use = NULL; + PULONG Rid = NULL; + NTSTATUS Status; + UNICODE_STRING UserName; + ULONG RelativeId = 0; + + Status = GetAccountRid(Parameter[0],&RelativeId); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get account rid: 0x%x\n",Status); + return(Status); + } + + printf("Opening User 0x%x\n",RelativeId); + Status= SamOpenUser( + DomainHandle, + MAXIMUM_ALLOWED, + RelativeId, + &UserHandle + ); + + if (!NT_SUCCESS(Status)) + { + printf("Failed to open User: 0x%x\n",Status); + } + return(Status); +} + +NTSTATUS +DelUser( LPWSTR * Parameter) +{ + NTSTATUS Status; + + Status = SamDeleteUser(UserHandle); + if (!NT_SUCCESS(Status)) + { + printf("Failed to delete user: 0x%x\n",Status); + } + return(Status); +} + +NTSTATUS +DelGroup( LPWSTR * Parameter) +{ + NTSTATUS Status; + + Status = SamDeleteGroup(GroupHandle); + if (!NT_SUCCESS(Status)) + { + printf("Failed to delete user: 0x%x\n",Status); + } + return(Status); +} + +NTSTATUS +DelAlias( LPWSTR * Parameter) +{ + NTSTATUS Status; + + Status = SamDeleteAlias(AliasHandle); + if (!NT_SUCCESS(Status)) + { + printf("Failed to delete Alias: 0x%x\n",Status); + } + return(Status); +} + +NTSTATUS +CreateUser( LPWSTR * Parameter) +{ + PSID_NAME_USE Use = NULL; + PULONG Rid = NULL; + NTSTATUS Status; + UNICODE_STRING UserName; + ULONG RelativeId = 0; + ACCESS_MASK GrantedAccess; + + + RtlInitUnicodeString( + &UserName, + Parameter[0] + ); + + printf("Creating User %wZ\n",&UserName); + + Status= SamCreateUser2InDomain( + DomainHandle, + &UserName, + USER_NORMAL_ACCOUNT, + MAXIMUM_ALLOWED, + &UserHandle, + &GrantedAccess, + &RelativeId + ); + + if (!NT_SUCCESS(Status)) + { + printf("Failed to create User: 0x%x\n",Status); + return(Status); + } + printf("Created user with rid 0x%x, access 0x%x\n", + RelativeId, GrantedAccess); + return(Status); +} + +NTSTATUS +CreateGroup( LPWSTR * Parameter) +{ + PSID_NAME_USE Use = NULL; + PULONG Rid = NULL; + NTSTATUS Status; + UNICODE_STRING GroupName; + ULONG RelativeId = 0; + + + RtlInitUnicodeString( + &GroupName, + Parameter[0] + ); + + printf("Creating Group %wZ\n",&GroupName); + + Status= SamCreateGroupInDomain( + DomainHandle, + &GroupName, + MAXIMUM_ALLOWED, + &GroupHandle, + &RelativeId + ); + + if (!NT_SUCCESS(Status)) + { + printf("Failed to create Group: 0x%x\n",Status); + return(Status); + } + printf("Created Group with rid 0x%x, access 0x%x\n", + RelativeId); + return(Status); +} + +NTSTATUS +CreateAlias( LPWSTR * Parameter) +{ + PSID_NAME_USE Use = NULL; + PULONG Rid = NULL; + NTSTATUS Status; + UNICODE_STRING AliasName; + ULONG RelativeId = 0; + + + RtlInitUnicodeString( + &AliasName, + Parameter[0] + ); + + printf("Creating Alias %wZ\n",&AliasName); + + Status= SamCreateAliasInDomain( + DomainHandle, + &AliasName, + MAXIMUM_ALLOWED, + &AliasHandle, + &RelativeId + ); + + if (!NT_SUCCESS(Status)) + { + printf("Failed to create Alias: 0x%x\n",Status); + return(Status); + } + printf("Created Alias with rid 0x%x, access 0x%x\n", + RelativeId); + return(Status); +} + + + +NTSTATUS +GetGroupsForUser(LPWSTR * Parameter) +{ + NTSTATUS Status; + ULONG MembershipCount; + PULONG Attributes = NULL; + PULONG Rids = NULL; + ULONG Index; + PGROUP_MEMBERSHIP Groups = NULL; + + Status = SamGetGroupsForUser( + UserHandle, + &Groups, + &MembershipCount + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get groups for user: 0x%x\n",Status); + return(Status); + } + + for (Index = 0; Index < MembershipCount ; Index++ ) + { + printf("Member %d: rid 0x%x, attributes 0x%x\n", + Index,Groups[Index].RelativeId, Groups[Index].Attributes ); + } + SamFreeMemory(Groups); + return(STATUS_SUCCESS); + +} + +void +PrintLogonHours( + char * String, + PLOGON_HOURS LogonHours + ) +{ + int Index; + printf("%s",String); + for (Index = 0; Index < (LogonHours->UnitsPerWeek + 7) / 8 ;Index++ ) + { + printf("0x%2.2x ",LogonHours->LogonHours[Index]); + } + printf("\n"); +} + + +NTSTATUS +DumpUser(LPWSTR * Parameter) +{ + NTSTATUS Status; + PUSER_ALL_INFORMATION UserAll = NULL; + PUSER_GENERAL_INFORMATION UserGeneral = NULL; + PUSER_PREFERENCES_INFORMATION UserPreferences = NULL; + PUSER_LOGON_INFORMATION UserLogon = NULL; + PUSER_ACCOUNT_INFORMATION UserAccount = NULL; + PUSER_ACCOUNT_NAME_INFORMATION UserAccountName = NULL; + PUSER_FULL_NAME_INFORMATION UserFullName = NULL; + PUSER_NAME_INFORMATION UserName = NULL; + PUSER_PRIMARY_GROUP_INFORMATION UserPrimary = NULL; + PUSER_HOME_INFORMATION UserHome = NULL; + PUSER_SCRIPT_INFORMATION UserScript = NULL; + PUSER_PROFILE_INFORMATION UserProfile = NULL; + PUSER_ADMIN_COMMENT_INFORMATION UserAdminComment = NULL; + PUSER_WORKSTATIONS_INFORMATION UserWksta = NULL; + PUSER_CONTROL_INFORMATION UserControl = NULL; + PUSER_EXPIRES_INFORMATION UserExpires = NULL; + PUSER_LOGON_HOURS_INFORMATION UserLogonHours = NULL; + + printf("\nDumpUser.\n"); + Status = SamQueryInformationUser( + UserHandle, + UserAllInformation, + (PVOID *) &UserAll + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user all: 0x%x\n",Status); + return(Status); + } + printf("UserAll:\n"); + PrintTime("\tLastLogon = ",&UserAll->LastLogon); + PrintTime("\tLastLogoff = ",&UserAll->LastLogoff); + PrintTime("\tPasswordLastSet = ",&UserAll->PasswordLastSet); + PrintTime("\tAccountExpires = ",&UserAll->AccountExpires); + PrintTime("\tPasswordCanChange = ",&UserAll->PasswordCanChange); + PrintTime("\tPasswordMustChange = ",&UserAll->PasswordMustChange); + printf("\tUserName = %wZ\n",&UserAll->UserName); + printf("\tFullName = %wZ\n",&UserAll->FullName); + printf("\tHomeDirectory = %wZ\n",&UserAll->HomeDirectory); + printf("\tHomeDirectoryDrive = %wZ\n",&UserAll->HomeDirectoryDrive); + printf("\tScriptPath = %wZ\n",&UserAll->ScriptPath); + printf("\tProfilePath = %wZ\n",&UserAll->ProfilePath); + printf("\tAdminComment = %wZ\n",&UserAll->AdminComment); + printf("\tWorkStations = %wZ\n",&UserAll->WorkStations); + printf("\tUserComment = %wZ\n",&UserAll->UserComment); + printf("\tParameters = %wZ\n",&UserAll->Parameters); + printf("\tUserId = 0x%x\n",UserAll->UserId); + printf("\tPrimaryGroupId = 0x%x\n",UserAll->PrimaryGroupId); + printf("\tUserAccountControl = 0x%x\n",UserAll->UserAccountControl); + printf("\tWhichFields = 0x%x\n",UserAll->WhichFields); + PrintLogonHours("\tLogonHours = ",&UserAll->LogonHours); + printf("\tLogonCount = %d\n",UserAll->LogonCount); + printf("\tCountryCode = %d\n",UserAll->CountryCode); + printf("\tCodePage = %d\n",UserAll->CodePage); + + SamFreeMemory(UserAll); + + Status = SamQueryInformationUser( + UserHandle, + UserGeneralInformation, + (PVOID *) &UserGeneral + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user general: 0x%x\n",Status); + return(Status); + } + + printf("UserGeneral:\n"); + printf("\tUserName = %wZ\n",&UserGeneral->UserName); + printf("\tFullName = %wZ\n",&UserGeneral->FullName); + printf("\tPrimaryGroupId = 0x%x\n",UserGeneral->PrimaryGroupId); + printf("\tAdminComment = 0x%x\n",&UserGeneral->AdminComment); + printf("\tUserComment = 0x%x\n",&UserGeneral->UserComment); + + SamFreeMemory(UserGeneral); + + Status = SamQueryInformationUser( + UserHandle, + UserPreferencesInformation, + (PVOID *) &UserPreferences + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user preferences: 0x%x\n",Status); + return(Status); + } + + printf("UserPreferences:\n"); + printf("\tUserComment = %wZ\n",&UserPreferences->UserComment); + printf("\tReserved1 = %wZ\n",&UserPreferences->Reserved1); + printf("\tCountryCode = %d\n",&UserPreferences->CountryCode); + printf("\tCodePage = %d\n",&UserPreferences->CodePage); + + SamFreeMemory(UserPreferences); + + Status = SamQueryInformationUser( + UserHandle, + UserLogonInformation, + (PVOID *) &UserLogon + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user Logon: 0x%x\n",Status); + return(Status); + } + + + printf("UserLogon:\n"); + printf("\tUserName = %wZ\n",&UserLogon->UserName); + printf("\tFullName = %wZ\n",&UserLogon->FullName); + printf("\tUserId = 0x%x\n",UserLogon->UserId); + printf("\tPrimaryGroupId = 0x%x\n",UserLogon->PrimaryGroupId); + printf("\tHomeDirectory = %wZ\n",&UserLogon->HomeDirectory); + printf("\tHomeDirectoryDrive = %wZ\n",&UserLogon->HomeDirectoryDrive); + printf("\tScriptPath = %wZ\n",&UserLogon->ScriptPath); + printf("\tProfilePath = %wZ\n",&UserLogon->ProfilePath); + printf("\tWorkStations = %wZ\n",&UserLogon->WorkStations); + PrintTime("\tLastLogon = ",&UserLogon->LastLogon); + PrintTime("\tLastLogoff = ",&UserLogon->LastLogoff); + PrintTime("\tPasswordLastSet = ",&UserLogon->PasswordLastSet); + PrintTime("\tPasswordCanChange = ",&UserLogon->PasswordCanChange); + PrintTime("\tPasswordMustChange = ",&UserLogon->PasswordMustChange); + PrintLogonHours("\tLogonHours = ",&UserLogon->LogonHours); + printf("\tBadPasswordCount = %d\n",UserLogon->BadPasswordCount); + printf("\tLogonCount = %d\n",UserLogon->LogonCount); + printf("\tUserAccountControl = 0x%x\n",UserLogon->UserAccountControl); + + SamFreeMemory(UserLogon); + + Status = SamQueryInformationUser( + UserHandle, + UserAccountInformation, + (PVOID *) &UserAccount + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user account: 0x%x\n",Status); + return(Status); + } + + printf("UserAccount:\n"); + printf("\tUserName = %wZ\n",&UserAccount->UserName); + printf("\tFullName = %wZ\n",&UserAccount->FullName); + printf("\tUserId = 0x%x\n",UserAccount->UserId); + printf("\tPrimaryGroupId = 0x%x\n",UserAccount->PrimaryGroupId); + printf("\tHomeDirectory = %wZ\n",&UserAccount->HomeDirectory); + printf("\tHomeDirectoryDrive = %wZ\n",&UserAccount->HomeDirectoryDrive); + printf("\tScriptPath = %wZ\n",&UserAccount->ScriptPath); + printf("\tProfilePath = %wZ\n",&UserAccount->ProfilePath); + printf("\tAdminComment = %wZ\n",&UserAccount->AdminComment); + printf("\tWorkStations = %wZ\n",&UserAccount->WorkStations); + PrintTime("\tLastLogon = ",&UserAccount->LastLogon); + PrintTime("\tLastLogoff = ",&UserAccount->LastLogoff); + PrintLogonHours("\tLogonHours = ",&UserAccount->LogonHours); + printf("\tBadPasswordCount = %d\n",UserAccount->BadPasswordCount); + printf("\tLogonCount = %d\n",UserAccount->LogonCount); + PrintTime("\tPasswordLastSet = ",&UserAccount->PasswordLastSet); + PrintTime("\tAccountExpires = ",&UserAccount->AccountExpires); + printf("\tUserAccountControl = 0x%x\n",UserAccount->UserAccountControl); + + SamFreeMemory(UserAccount); + + Status = SamQueryInformationUser( + UserHandle, + UserAccountNameInformation, + (PVOID *) &UserAccountName + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user account name: 0x%x\n",Status); + return(Status); + } + + printf("UserAccountName:\n"); + printf("\tUserName = %wZ\n",&UserAccountName->UserName); + SamFreeMemory(UserAccountName); + + Status = SamQueryInformationUser( + UserHandle, + UserFullNameInformation, + (PVOID *) &UserFullName + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user full name: 0x%x\n",Status); + return(Status); + } + + printf("UserFullName:\n"); + printf("\tFullName = %wZ\n",&UserFullName->FullName); + SamFreeMemory(UserFullName); + + Status = SamQueryInformationUser( + UserHandle, + UserNameInformation, + (PVOID *) &UserName + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user name: 0x%x\n",Status); + return(Status); + } + + printf("UserName:\n"); + printf("\tUserName = %wZ\n",&UserName->UserName); + printf("\tFullName = %wZ\n",&UserName->FullName); + SamFreeMemory(UserName); + + Status = SamQueryInformationUser( + UserHandle, + UserPrimaryGroupInformation, + (PVOID *) &UserPrimary + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user all: 0x%x\n",Status); + return(Status); + } + printf("UserPrimaryGroup:\n"); + printf("PrimaryGroupid = 0x%x\n",UserPrimary->PrimaryGroupId); + SamFreeMemory(UserPrimary); + + Status = SamQueryInformationUser( + UserHandle, + UserHomeInformation, + (PVOID *) &UserHome + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user home: 0x%x\n",Status); + return(Status); + } + printf("UserHome:\n"); + printf("\tHomeDirectory = %wZ\n",&UserHome->HomeDirectory); + printf("\tHomeDirectoryDrive = %wZ\n",&UserHome->HomeDirectoryDrive); + + SamFreeMemory(UserHome); + + Status = SamQueryInformationUser( + UserHandle, + UserScriptInformation, + (PVOID *) &UserScript + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user Script: 0x%x\n",Status); + return(Status); + } + printf("UserScript:\n"); + printf("\tScriptPath = %wZ\n",&UserScript->ScriptPath); + + SamFreeMemory(UserScript); + + Status = SamQueryInformationUser( + UserHandle, + UserProfileInformation, + (PVOID *) &UserProfile + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user Profile: 0x%x\n",Status); + return(Status); + } + printf("UserProfile:\n"); + printf("\tProfilePath = %wZ\n",&UserProfile->ProfilePath); + + SamFreeMemory(UserProfile); + Status = SamQueryInformationUser( + UserHandle, + UserAdminCommentInformation, + (PVOID *) &UserAdminComment + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user AdminComment: 0x%x\n",Status); + return(Status); + } + printf("UserAdminComment:\n"); + printf("\tAdminComment = %wZ\n",&UserAdminComment->AdminComment); + SamFreeMemory(UserAdminComment); + + Status = SamQueryInformationUser( + UserHandle, + UserWorkStationsInformation, + (PVOID *) &UserWksta + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user wksta: 0x%x\n",Status); + return(Status); + } + + printf("UserWorkStations:\n"); + printf("\tWorkStations = %wZ\n",&UserWksta->WorkStations); + SamFreeMemory(UserWksta); + + Status = SamQueryInformationUser( + UserHandle, + UserControlInformation, + (PVOID *) &UserControl + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user Control: 0x%x\n",Status); + return(Status); + } + + printf("UserControl:\n"); + printf("\tUserAccountControl = 0x%x\n",UserControl->UserAccountControl); + SamFreeMemory(UserControl); + + Status = SamQueryInformationUser( + UserHandle, + UserExpiresInformation, + (PVOID *) &UserExpires + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user Expires: 0x%x\n",Status); + return(Status); + } + + printf("UserExpires:\n"); + PrintTime("\tAccountExpires = ",&UserExpires->AccountExpires); + SamFreeMemory(UserExpires); + + Status = SamQueryInformationUser( + UserHandle, + UserLogonHoursInformation, + (PVOID *) &UserLogonHours + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user LogonHours: 0x%x\n",Status); + return(Status); + } + printf("UserLogonHours:\n"); + PrintLogonHours("\tLogonHours = ",&UserLogonHours->LogonHours); + + SamFreeMemory(UserLogonHours); + + + return(STATUS_SUCCESS); +} + +NTSTATUS +DumpGroup(LPWSTR * Parameter) +{ + NTSTATUS Status; + PGROUP_GENERAL_INFORMATION General = NULL; + PGROUP_NAME_INFORMATION Name = NULL; + PGROUP_ATTRIBUTE_INFORMATION Attribute = NULL; + PGROUP_ADM_COMMENT_INFORMATION AdmComment = NULL; + + printf("\nDumpGroup.\n"); + Status = SamQueryInformationGroup( + GroupHandle, + GroupGeneralInformation, + (PVOID *) &General + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get group general information: 0x%x\n",Status); + return(Status); + } + + printf("Group General.Name = %wZ\n",&General->Name); + printf("Group General.Attributes = 0x%x\n",General->Attributes); + printf("Group general.memberCount = %d\n",General->MemberCount); + printf("Group general.AdminComment = %wZ\n",&General->AdminComment); + SamFreeMemory(General); + + Status = SamQueryInformationGroup( + GroupHandle, + GroupNameInformation, + (PVOID *) &Name + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get group name information: 0x%x\n",Status); + return(Status); + } + + printf("Group Name.Name = %wZ\n",&Name->Name); + SamFreeMemory(Name); + + Status = SamQueryInformationGroup( + GroupHandle, + GroupAttributeInformation, + (PVOID *) &Attribute + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get group Attribute information: 0x%x\n",Status); + return(Status); + } + + printf("Group Attribute.Attributes = 0x%x\n",Attribute->Attributes); + SamFreeMemory(Attribute); + + Status = SamQueryInformationGroup( + GroupHandle, + GroupAdminCommentInformation, + (PVOID *) &AdmComment + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get group admin comment information: 0x%x\n",Status); + return(Status); + } + + printf("Group Admin comment.AdminComment = %wZ\n",&AdmComment->AdminComment); + SamFreeMemory(AdmComment); + + return(STATUS_SUCCESS); +} + +NTSTATUS +DumpAllGroups(LPWSTR * Parameter) +{ + ULONG PreferedMax = 1000; + NTSTATUS Status,EnumStatus; + SAM_ENUMERATE_HANDLE EnumContext = 0; + ULONG CountReturned; + LPWSTR GroupName[1]; + + PSAM_RID_ENUMERATION Accounts = NULL; + + + GroupName[0] = (LPWSTR) malloc(128); + + printf("DumpAllGroups:\n"); + + + EnumContext = 0; + ASSERT(DomainHandle != NULL); + do + { + EnumStatus = SamEnumerateGroupsInDomain( + DomainHandle, + &EnumContext, + (PVOID *) &Accounts, + PreferedMax, + &CountReturned + ); + + if (NT_SUCCESS(EnumStatus) && (EnumStatus != STATUS_NO_MORE_ENTRIES)) + { + ULONG Index; + UNICODE_STRING SidString; + + for (Index = 0; Index < CountReturned; Index++) + { + RtlCopyMemory( + GroupName[0], + Accounts[Index].Name.Buffer, + Accounts[Index].Name.Length + ); + GroupName[0][Accounts[Index].Name.Length/sizeof(WCHAR)] = L'\0'; + + Status = OpenGroup(GroupName); + if (!NT_SUCCESS(Status)) + { + break; + } + Status = DumpGroup(NULL); + SamCloseHandle(GroupHandle); + GroupHandle = NULL; + + } + SamFreeMemory(Accounts); + } + else printf("Failed to enumerate Groups: 0x%x\n",Status); + } while (NT_SUCCESS(EnumStatus) && (EnumStatus != STATUS_SUCCESS) && (CountReturned != 0) ); + + free(GroupName[0]); + return(STATUS_SUCCESS); +} + +NTSTATUS +DumpAllUsers(LPWSTR * Parameter) +{ + ULONG PreferedMax = 1000; + NTSTATUS Status,EnumStatus; + SAM_ENUMERATE_HANDLE EnumContext = 0; + ULONG CountReturned; + LPWSTR UserName[1]; + + PSAM_RID_ENUMERATION Accounts = NULL; + + + UserName[0] = (LPWSTR) malloc(128); + + printf("DumpAllUsers:\n"); + + + EnumContext = 0; + ASSERT(DomainHandle != NULL); + do + { + EnumStatus = SamEnumerateUsersInDomain( + DomainHandle, + &EnumContext, + 0, + (PVOID *) &Accounts, + PreferedMax, + &CountReturned + ); + + if (NT_SUCCESS(EnumStatus) && (EnumStatus != STATUS_NO_MORE_ENTRIES)) + { + ULONG Index; + UNICODE_STRING SidString; + + for (Index = 0; Index < CountReturned; Index++) + { + RtlCopyMemory( + UserName[0], + Accounts[Index].Name.Buffer, + Accounts[Index].Name.Length + ); + UserName[0][Accounts[Index].Name.Length/sizeof(WCHAR)] = L'\0'; + + Status = OpenUser(UserName); + if (!NT_SUCCESS(Status)) + { + break; + } + Status = DumpUser(NULL); + Status = GetGroupsForUser(NULL); + SamCloseHandle(UserHandle); + UserHandle = NULL; + + } + SamFreeMemory(Accounts); + } + else printf("Failed to enumerate users: 0x%x\n",Status); + } while (NT_SUCCESS(EnumStatus) && (EnumStatus != STATUS_SUCCESS) && (CountReturned != 0) ); + + free(UserName[0]); + return(STATUS_SUCCESS); +} + +NTSTATUS +AddAliasMember( LPWSTR * Parameter ) +{ + BYTE Buffer[100]; + PSID AccountSid = Buffer; + ULONG SidLen = 100; + SID_NAME_USE Use; + WCHAR ReferencedDomain[100]; + ULONG DomainLen = 100; + NTSTATUS Status; + + printf("Adding account %ws to alias\n",Parameter[0]); + if (!LookupAccountName( + NULL, + Parameter[0], + AccountSid, + &SidLen, + ReferencedDomain, + &DomainLen, + &Use)) + { + printf("Failed to lookup account name: %d\n",GetLastError()); + return(STATUS_UNSUCCESSFUL); + } + + Status = SamAddMemberToAlias( + AliasHandle, + AccountSid + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to add member to alias: 0x%x\n",Status); + } + return(Status); +} + +NTSTATUS +DumpDomain( LPWSTR * Parameter ) +{ + NTSTATUS Status; + PDOMAIN_PASSWORD_INFORMATION Password = NULL; + PDOMAIN_GENERAL_INFORMATION General = NULL; + PDOMAIN_LOGOFF_INFORMATION Logoff = NULL; + PDOMAIN_OEM_INFORMATION Oem = NULL; + PDOMAIN_NAME_INFORMATION Name = NULL; + PDOMAIN_REPLICATION_INFORMATION Replica = NULL; + PDOMAIN_SERVER_ROLE_INFORMATION ServerRole = NULL; + PDOMAIN_MODIFIED_INFORMATION Modified = NULL; + PDOMAIN_STATE_INFORMATION State = NULL; + PDOMAIN_GENERAL_INFORMATION2 General2 = NULL; + PDOMAIN_LOCKOUT_INFORMATION Lockout = NULL; + PDOMAIN_MODIFIED_INFORMATION2 Modified2 = NULL; + + + Status = SamQueryInformationDomain( + DomainHandle, + DomainPasswordInformation, + (PVOID *) &Password + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query password information: 0x%x\n",Status); + return(Status); + } + + printf("Password:\n"); + printf("\tMinPasswordLength = %d\n",Password->MinPasswordLength); + printf("\tPasswordHistoryLength = %d\n",Password->PasswordHistoryLength); + printf("\tPasswordProperties = 0x%x\n",Password->PasswordProperties); + PrintDeltaTime("\tMaxPasswordAge = ",&Password->MaxPasswordAge); + PrintDeltaTime("\tMinPasswordAge = ",&Password->MinPasswordAge); + + SamFreeMemory(Password); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainGeneralInformation, + (PVOID *) &General + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query general: 0x%x\n",Status); + return(Status); + } + + printf("General:\n"); + PrintDeltaTime("\t ForceLogoff = ",&General->ForceLogoff); + printf("\t OemInformation = %wZ\n",&General->OemInformation); + printf("\t DomainName = %wZ\n",&General->DomainName); + printf("\t ReplicaSourceNodeName =%wZ\n",&General->ReplicaSourceNodeName); + printf("\t DomainModifiedCount = 0x%x,0x%x\n", + General->DomainModifiedCount.HighPart, + General->DomainModifiedCount.LowPart ); + printf("\t DomainServerState = %d\n",General->DomainServerState); + printf("\t DomainServerRole = %d\n",General->DomainServerRole); + printf("\t UasCompatibilityRequired = %d\n",General->UasCompatibilityRequired); + printf("\t UserCount = %d\n",General->UserCount); + printf("\t GroupCount = %d\n",General->GroupCount); + printf("\t AliasCount = %d\n",General->AliasCount); + + SamFreeMemory(General); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainLogoffInformation, + (PVOID *) &Logoff + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query logoff: 0x%x\n",Status); + return(Status); + } + + printf("Logoff:\n"); + PrintDeltaTime("\t ForceLogoff = ",&Logoff->ForceLogoff); + SamFreeMemory(Logoff); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainOemInformation, + (PVOID *) &Oem + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query Oem: 0x%x\n",Status); + return(Status); + } + + printf("Oem:\n\t OemInformation = %wZ\n",&Oem->OemInformation); + + SamFreeMemory(Oem); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainNameInformation, + (PVOID *) &Name + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query Name: 0x%x\n",Status); + return(Status); + } + printf("Name:\n\t DomainName = %wZ\n",&Name->DomainName); + + SamFreeMemory(Name); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainReplicationInformation, + (PVOID *) &Replica + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query Replica: 0x%x\n",Status); + return(Status); + } + + printf("Replica:\n\t ReplicaSourceNodeName = %wZ\n", &Replica->ReplicaSourceNodeName); + SamFreeMemory(Replica); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainServerRoleInformation, + (PVOID *) &ServerRole + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query ServerRole: 0x%x\n",Status); + return(Status); + } + + printf("ServerRole:\n\t DomainServerRole = %d\n",ServerRole->DomainServerRole); + SamFreeMemory(ServerRole); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainModifiedInformation, + (PVOID *) &Modified + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query Modified: 0x%x\n",Status); + return(Status); + } + + printf("Modified:\n"); + printf("\t DomainModifiedCount = 0x%x,0x%x\n", + Modified->DomainModifiedCount.HighPart, + Modified->DomainModifiedCount.LowPart ); + PrintTime("\t CreationTime = ",&Modified->CreationTime); + + + + SamFreeMemory(Modified); + + + Status = SamQueryInformationDomain( + DomainHandle, + DomainStateInformation, + (PVOID *) &State + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query State: 0x%x\n",Status); + return(Status); + } + + printf("State:\n\t DomainServerState = %d\n",State->DomainServerState); + SamFreeMemory(State); + + + Status = SamQueryInformationDomain( + DomainHandle, + DomainGeneralInformation2, + (PVOID *) &General2 + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query General2: 0x%x\n",Status); + return(Status); + } + + printf("General2:\n"); + General = &General2->I1; + PrintDeltaTime("\t ForceLogoff = ",&General->ForceLogoff); + printf("\t OemInformation = %wZ\n",&General->OemInformation); + printf("\t DomainName = %wZ\n",&General->DomainName); + printf("\t ReplicaSourceNodeName =%wZ\n",&General->ReplicaSourceNodeName); + printf("\t DomainModifiedCount = 0x%x,0x%x\n", + General->DomainModifiedCount.HighPart, + General->DomainModifiedCount.LowPart ); + printf("\t DomainServerState = %d\n",General->DomainServerState); + printf("\t DomainServerRole = %d\n",General->DomainServerRole); + printf("\t UasCompatibilityRequired = %d\n",General->UasCompatibilityRequired); + printf("\t UserCount = %d\n",General->UserCount); + printf("\t GroupCount = %d\n",General->GroupCount); + printf("\t AliasCount = %d\n",General->AliasCount); + PrintDeltaTime("\t LockoutDuration = ",&General2->LockoutDuration); + PrintDeltaTime("\t LockoutObservationWindow = ",&General2->LockoutObservationWindow); + printf("\t LockoutThreshold = %d\n",General2->LockoutThreshold); + + SamFreeMemory(General2); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainLockoutInformation, + (PVOID *) &Lockout + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query Lockout: 0x%x\n",Status); + return(Status); + } + printf("Lockout:\n"); + PrintDeltaTime("\t LockoutDuration = ",&Lockout->LockoutDuration); + PrintDeltaTime("\t LockoutObservationWindow = ",&Lockout->LockoutObservationWindow); + printf("\t LockoutThreshold = %d\n",Lockout->LockoutThreshold); + + SamFreeMemory(Lockout); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainModifiedInformation2, + (PVOID *) &Modified2 + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query Modified2: 0x%x\n",Status); + return(Status); + } + printf("Modified2:\n"); + PrintTime("\t CreationTime = ",&Modified->CreationTime); + printf("\t DomainModifiedCount = 0x%x,0x%x\n", + Modified2->DomainModifiedCount.HighPart, + Modified2->DomainModifiedCount.LowPart ); + + printf("\t ModifiedCountAtLastPromotion = 0x%x,0x%x\n", + Modified2->ModifiedCountAtLastPromotion.HighPart, + Modified2->ModifiedCountAtLastPromotion.LowPart ); + + SamFreeMemory(Modified2); + + return(STATUS_SUCCESS); + +} + + + +void _cdecl +main(int argc, char *argv[]) +{ + ULONG Command = 0; + ULONG i,j,k; + BOOLEAN Found; + NTSTATUS Status; + Action Actions[20]; + ULONG ActionCount = 0; + + for (i = 1; i < (ULONG) argc ; i++ ) + { + Found = FALSE; + for (j = 0; j < NUM_COMMANDS ; j++ ) + { + if (!_stricmp(argv[i],Commands[j].Name)) + { + Actions[ActionCount].CommandNumber = j; + + if (Commands[j].Parameter != 0) + { + for (k = 0; k < Commands[j].Parameter ;k++ ) + { + Actions[ActionCount].Parameter[k] = (LPWSTR) malloc(128); + if ((ULONG) argc > i) + { + mbstowcs(Actions[ActionCount].Parameter[k],argv[++i],128); + } + else + { + Actions[ActionCount].Parameter[k][0] = L'\0'; + } + } + } + Found = TRUE; + ActionCount++; + break; + } + } + if (!Found) + { + printf("Switch %s not found\n", argv[i]); + return; + } + } + +// Status = OpenSam(); +// if (!NT_SUCCESS(Status)) +// { +// printf("Failed to open sam: 0x%x\n",Status); +// return; +// } + + for (i = 0; i < ActionCount ; i++ ) + { + Status = Commands[Actions[i].CommandNumber].Function(Actions[i].Parameter); + if (!NT_SUCCESS(Status)) + { + printf("Failed test %s : 0x%x\n",Commands[Actions[i].CommandNumber].Name,Status); + goto Cleanup; + + } + } + +Cleanup: + if (DomainHandle != NULL) + { + Status = SamCloseHandle(DomainHandle); + if (!NT_SUCCESS(Status)) + { + printf("Failed to close account: 0x%x\n",Status); + } + } + if (GroupHandle != NULL) + { + Status = SamCloseHandle(GroupHandle); + if (!NT_SUCCESS(Status)) + { + printf("Failed to close account: 0x%x\n",Status); + } + } + if (AliasHandle != NULL) + { + Status = SamCloseHandle(AliasHandle); + if (!NT_SUCCESS(Status)) + { + printf("Failed to close account: 0x%x\n",Status); + } + } + if (UserHandle != NULL) + { + Status = SamCloseHandle(UserHandle); + if (!NT_SUCCESS(Status)) + { + printf("Failed to close account: 0x%x\n",Status); + } + } + Status = CloseSam(); + if (!NT_SUCCESS(Status)) + { + printf("Failed to close lsa: 0x%x\n",Status); + } + + return; + +} diff --git a/private/newsam2/server/utest/sources b/private/newsam2/server/utest/sources new file mode 100644 index 000000000..3d54889d7 --- /dev/null +++ b/private/newsam2/server/utest/sources @@ -0,0 +1,46 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + + +TARGETNAME=samtest +TARGETPATH=obj +TARGETTYPE=PROGRAM + +UMTYPE=console + +INCLUDES=..\..\..\inc + +SOURCES= samtest.cxx + + +TARGETLIBS= \ + $(BASEDIR)\public\sdk\lib\*\advapi32.lib \ + $(BASEDIR)\public\sdk\lib\*\samlib.lib \ + $(BASEDIR)\public\sdk\lib\*\ntdll.lib + + +C_DEFINES=$(C_DEFINES) -DRPC_NO_WINDOWS_H -DUNICODE + +USE_CRTDLL=1 + diff --git a/private/newsam2/server/utest/usrops.cxx b/private/newsam2/server/utest/usrops.cxx new file mode 100644 index 000000000..5711d8104 --- /dev/null +++ b/private/newsam2/server/utest/usrops.cxx @@ -0,0 +1,2033 @@ +//+----------------------------------------------------------------------- +// +// Microsoft Windows +// +// Copyright (c) Microsoft Corporation 1992 - 1992 +// +// File: secret.cxx +// +// Contents: test program to check the setup of a Cairo installation +// +// +// History: 22-Dec-92 Created MikeSw +// +//------------------------------------------------------------------------ + + +extern "C" +{ +#include <nt.h> +#include <ntrtl.h> +#include <nturtl.h> +#include <ntsam.h> +#include <ntlsa.h> +#include <windows.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <caiseapi.h> +} + +typedef NTSTATUS (TestFunc)( WCHAR *Parameter[]); + +typedef struct _Commands { + PSTR Name; + ULONG Parameter; // TRUE = yes, FALSE = no + TestFunc *Function; +} CommandPair, *PCommandPair; + + +typedef struct _Action { + ULONG CommandNumber; + LPWSTR Parameter[8]; +} Action, *PAction; + + + +TestFunc OpenDomain; +TestFunc EnumDomains; +TestFunc EnumAccounts; +TestFunc QueryDisplay; +TestFunc OpenGroup; +TestFunc GroupMembers; +TestFunc OpenAlias; +TestFunc AliasMembers; +TestFunc GetAliasMembership; +TestFunc OpenUser; +TestFunc GetGroupsForUser; +TestFunc DumpAllUsers; +TestFunc DumpAllGroups; +TestFunc DumpUser; +TestFunc DumpGroup; +TestFunc CreateUser; +TestFunc AddAliasMember; +TestFunc CreateGroup; +TestFunc CreateAlias; +TestFunc DumpDomain; +TestFunc Connect; +TestFunc DelUser; +TestFunc DelAlias; +TestFunc DelGroup; + +CommandPair Commands[] = { + {"-od",1,OpenDomain}, + {"-ed",0,EnumDomains}, + {"-ea",1,EnumAccounts}, + {"-qd",1,QueryDisplay}, + {"-og",1,OpenGroup}, + {"-gm",0,GroupMembers}, + {"-oa",1,OpenAlias}, + {"-am",0,AliasMembers}, + {"-gam",1,GetAliasMembership}, + {"-ou",1,OpenUser}, + {"-ggu",0,GetGroupsForUser}, + {"-dau",0,DumpAllUsers}, + {"-dag",0,DumpAllGroups}, + {"-du",0,DumpUser}, + {"-dg",0,DumpGroup}, + {"-cu",1,CreateUser}, + {"-aam",1,AddAliasMember}, + {"-cg",1,CreateGroup}, + {"-ca",1,CreateAlias}, + {"-dd",0,DumpDomain}, + {"-c",1,Connect}, + {"-delu",0,DelUser}, + {"-dela",0,DelAlias}, + {"-delg",0,DelGroup} + + + +}; + +#define NUM_COMMANDS (sizeof(Commands) / sizeof(CommandPair)) + +SAM_HANDLE SamHandle; +SAM_HANDLE DomainHandle; +SAM_HANDLE GroupHandle; +SAM_HANDLE AliasHandle; +SAM_HANDLE UserHandle; + +UNICODE_STRING ServerName; + +//+------------------------------------------------------------------------- +// +// Function: PrintTime +// +// Synopsis: Prints a text representation of a FILETIME interpreted +// as an absolute date/time +// +// Arguments: [rft] -- time to print +// +// Notes: Used only by dump functions. +// +//-------------------------------------------------------------------------- + +void +PrintTime ( + char * String, + PVOID Time + ) +{ + SYSTEMTIME st; + + FileTimeToSystemTime ( (PFILETIME) Time, & st ); + printf("%s %d-%d-%d %d:%2.2d:%2.2d\n", String, st.wMonth, st.wDay, st.wYear, + st.wHour, st.wMinute, st.wSecond ); +} + +//+------------------------------------------------------------------------- +// +// Function: SpmDbDeltaTimeToString +// +// Synopsis: Converts a time delta to a string. +// +// Effects: +// +// Arguments: +// +// Requires: +// +// Returns: +// +// Notes: +// +// +//-------------------------------------------------------------------------- + + +VOID +PrintDeltaTime( + IN LPSTR Message, + IN PLARGE_INTEGER Time + ) +{ + ULONG Seconds; + ULONG Minutes; + ULONG Hours; + ULONG Days; + ULONG Chars; + CHAR TimeBuffer[256] = ""; + LPSTR TimeString = TimeBuffer; + LARGE_INTEGER DeltaTime; + + DeltaTime.QuadPart = -Time->QuadPart; + + Seconds = (ULONG) (DeltaTime.QuadPart / 10000000); + + Minutes = Seconds / 60; + Hours = Minutes / 60; + Days = Hours / 24; + + Hours = Hours % 24; + Minutes = Minutes % 60; + Seconds = Seconds % 60; + + if (Days >= 1) + { + Chars = sprintf(TimeString,"%d days ",Days); + TimeString += Chars; + } + if (Hours >= 1 ) + { + Chars = sprintf(TimeString,"%d hours ",Hours); + TimeString += Chars; + } + + if (Minutes >= 1 && Days == 0) + { + Chars = sprintf(TimeString,"%d minutes ",Minutes); + TimeString += Chars; + } + + if (Seconds >= 1 && (Days == 0) && (Hours == 0) ) + { + Chars = sprintf(TimeString,"%d seconds ",Seconds); + TimeString += Chars; + } + + printf("%s %s\n",Message,TimeBuffer); + +} + + +NTSTATUS +Connect( LPWSTR * Parameter) +{ + OBJECT_ATTRIBUTES oa; + NTSTATUS Status; + + RtlInitUnicodeString( + &ServerName, + Parameter[0] + ); + + InitializeObjectAttributes(&oa,NULL,0,NULL,NULL); + + Status = SamConnect( + &ServerName, + &SamHandle, + MAXIMUM_ALLOWED, + &oa); + return(Status); +} + +NTSTATUS +CloseSam() +{ + return(SamCloseHandle(SamHandle)); +} + + + +NTSTATUS +EnumDomains( + LPWSTR * Parameter ) +{ + NTSTATUS Status; + SHORT Language; + SAM_ENUMERATE_HANDLE Context = 0; + PSAM_RID_ENUMERATION Buffer = NULL; + ULONG Count = 0; + ULONG i; + + Status = SamEnumerateDomainsInSamServer( + SamHandle, + &Context, + (PVOID *) &Buffer, + 2000, + &Count + ); + + if (!NT_SUCCESS(Status)) + { + return(Status); + } + + + for (i = 0; i < Count ; i++ ) + { + printf("Domain = %wZ\n",&Buffer[i].Name); + } + SamFreeMemory(Buffer); + return(STATUS_SUCCESS); +} + +NTSTATUS +OpenDomain( LPWSTR * Parameter ) +{ + GUID DomainGuid; + BOOLEAN fBuiltin; + NTSTATUS Status; + CAIROSID DomainSid; + SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; + + if (!_wcsicmp(Parameter[0],L"Builtin")) + { + fBuiltin = TRUE; + } + else if (!_wcsicmp(Parameter[0],L"Account")) + { + fBuiltin = FALSE; + } + else + { + printf("Invalid domain to open: %ws\n",Parameter[0]); + return(STATUS_UNSUCCESSFUL); + } + + if (fBuiltin) + { + DomainSid.Revision = SID_REVISION; + DomainSid.SubAuthorityCount = 1; + DomainSid.IdentifierAuthority = NtAuthority; + DomainSid.ZerothSubAuthority = SECURITY_BUILTIN_DOMAIN_RID; + } + else + { + LSA_HANDLE LsaHandle = NULL; + OBJECT_ATTRIBUTES Oa; + PPOLICY_ACCOUNT_DOMAIN_INFO DomainInfo = NULL; + + RtlZeroMemory(&Oa, sizeof(OBJECT_ATTRIBUTES)); + Status = LsaOpenPolicy( + &ServerName, + &Oa, + POLICY_VIEW_LOCAL_INFORMATION, + &LsaHandle + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to open policy: 0x%x\n",Status); + return(Status); + } + Status = LsaQueryInformationPolicy( + LsaHandle, + PolicyAccountDomainInformation, + (PVOID *) &DomainInfo + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query account domain: 0x%x\n",Status); + LsaClose(LsaHandle); + return(Status); + } + RtlCopyMemory( + &DomainSid, + DomainInfo->DomainSid, + RtlLengthSid(DomainInfo->DomainSid) + ); + LsaFreeMemory(DomainInfo); + } + + Status = SamOpenDomain( + SamHandle, + MAXIMUM_ALLOWED, + (PSID) &DomainSid, + &DomainHandle + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to open domain: 0x%x\n",Status); + } + return(Status); + +} + +NTSTATUS +EnumAccounts( LPWSTR * Parameter ) +{ + ULONG PreferedMax = 100; + NTSTATUS Status; + SAM_ENUMERATE_HANDLE EnumContext = 0; + ULONG CountReturned; + + PSAM_RID_ENUMERATION Accounts = NULL; + + swscanf(Parameter[0],L"%d",&PreferedMax); + + printf("EnumAccounts: %d\n",PreferedMax); + + + EnumContext = 0; + ASSERT(DomainHandle != NULL); + do + { + Status = SamEnumerateUsersInDomain( + DomainHandle, + &EnumContext, + 0, + (PVOID *) &Accounts, + PreferedMax, + &CountReturned + ); + + if (NT_SUCCESS(Status) && (Status != STATUS_NO_MORE_ENTRIES)) + { + ULONG Index; + UNICODE_STRING SidString; + + for (Index = 0; Index < CountReturned; Index++) + { + printf("Account : %wZ 0x%x\n",&Accounts[Index].Name, Accounts[Index].RelativeId); + } + SamFreeMemory(Accounts); + } + else printf("Failed to enumerate users: 0x%x\n",Status); + } while (NT_SUCCESS(Status) && (Status != STATUS_SUCCESS) && (CountReturned != 0) ); + + EnumContext = 0; + do + { + Status = SamEnumerateGroupsInDomain( + DomainHandle, + &EnumContext, + (PVOID *) &Accounts, + PreferedMax, + &CountReturned + ); + + if (NT_SUCCESS(Status) && (Status != STATUS_NO_MORE_ENTRIES)) + { + ULONG Index; + UNICODE_STRING SidString; + + for (Index = 0; Index < CountReturned; Index++) + { + printf("Group : %wZ 0x%x\n",&Accounts[Index].Name, Accounts[Index].RelativeId); + } + SamFreeMemory(Accounts); + } + else printf("Failed to enumerate Groups: 0x%x\n",Status); + } while (NT_SUCCESS(Status) && (CountReturned != 0) ); // && (Status != STATUS_SUCCESS) + + + EnumContext = 0; + do + { + Status = SamEnumerateAliasesInDomain( + DomainHandle, + &EnumContext, + (PVOID *) &Accounts, + PreferedMax, + &CountReturned + ); + + if (NT_SUCCESS(Status) && (Status != STATUS_NO_MORE_ENTRIES)) + { + ULONG Index; + UNICODE_STRING SidString; + + for (Index = 0; Index < CountReturned; Index++) + { + printf("Alias : %wZ 0x%x\n",&Accounts[Index].Name, Accounts[Index].RelativeId); + } + SamFreeMemory(Accounts); + } + else printf("Failed to enumerate aliases: 0x%x\n",Status); + } while (NT_SUCCESS(Status) && (CountReturned != 0) ); // && (Status != STATUS_SUCCESS) + + + + return(Status); +} + + +NTSTATUS +QueryDisplay( LPWSTR * Parameter ) +{ + NTSTATUS Status; + DOMAIN_DISPLAY_INFORMATION Type; + PVOID Buffer = NULL; + ULONG TotalAvailable = 0; + ULONG TotalReturned = 0; + ULONG ReturnedCount = 0; + ULONG Index; + ULONG SamIndex = 0; + + if (!_wcsicmp(Parameter[0],L"user")) + { + Type = DomainDisplayUser; + } else if (!_wcsicmp(Parameter[0],L"Machine")) + { + Type = DomainDisplayMachine; + } else if (!_wcsicmp(Parameter[0],L"Group")) + { + Type = DomainDisplayGroup; + } else if (!_wcsicmp(Parameter[0],L"OemUser")) + { + Type = DomainDisplayOemUser; + } else if (!_wcsicmp(Parameter[0],L"OemGroup")) + { + Type = DomainDisplayOemGroup; + } else { + printf("Invalid parameter %ws\n", Parameter[0]); + return(STATUS_INVALID_PARAMETER); + } + + do + { + Status = SamQueryDisplayInformation( + DomainHandle, + Type, + SamIndex, + 5, + 1000, + &TotalAvailable, + &TotalReturned, + &ReturnedCount, + &Buffer + ); + + if (NT_SUCCESS(Status) && (ReturnedCount > 0)) + { + printf("Total returned = %d\t total available = %d\n", + TotalReturned, TotalAvailable); + switch(Type) { + case DomainDisplayUser: + { + PDOMAIN_DISPLAY_USER Users = (PDOMAIN_DISPLAY_USER) Buffer; + for (Index = 0; Index < ReturnedCount ; Index++ ) + { + printf("User %d: Index %d\n Rid 0x%x\n Control 0x%x\n name %wZ\n Comment %wZ\n Full Name %wZ\n", + Index, + Users[Index].Index, + Users[Index].Rid, + Users[Index].AccountControl, + &Users[Index].LogonName, + &Users[Index].AdminComment, + &Users[Index].FullName + ); + } + break; + } + case DomainDisplayGroup: + { + PDOMAIN_DISPLAY_GROUP Groups = (PDOMAIN_DISPLAY_GROUP) Buffer; + for (Index = 0; Index < ReturnedCount ; Index++ ) + { + printf("Group %d\n Index %d\n Rid 0x%x\n Attributes 0x%x\n name %wZ\n Comment %wZ\n", + Index, + Groups[Index].Index, + Groups[Index].Rid, + Groups[Index].Attributes, + &Groups[Index].Group, + &Groups[Index].Comment + ); + + } + break; + } + case DomainDisplayMachine: + { + PDOMAIN_DISPLAY_MACHINE Machines = (PDOMAIN_DISPLAY_MACHINE) Buffer; + for (Index = 0; Index < ReturnedCount ; Index++ ) + { + printf("Machine %d\n Index %d\n Rid 0x%x\n Control 0x%x\n Name %wZ\n Comment %wZ\n", + Index, + Machines[Index].Index, + Machines[Index].Rid, + Machines[Index].AccountControl, + &Machines[Index].Machine, + &Machines[Index].Comment + ); + } + break; + } + case DomainDisplayOemUser: + { + PDOMAIN_DISPLAY_OEM_USER OemUsers = (PDOMAIN_DISPLAY_OEM_USER) Buffer; + for (Index = 0; Index < ReturnedCount ; Index++ ) + { + printf("OemUser %d\n Index %d\n Name %Z\n", + Index, + OemUsers[Index].Index, + &OemUsers[Index].User + ); + } + break; + } + case DomainDisplayOemGroup: + { + PDOMAIN_DISPLAY_OEM_GROUP OemGroups = (PDOMAIN_DISPLAY_OEM_GROUP) Buffer; + for (Index = 0; Index < ReturnedCount ; Index++ ) + { + printf("OemGroup %d\n Index %d\n Name %Z\n", + Index, + OemGroups[Index].Index, + &OemGroups[Index].Group + ); + } + break; + } + + } + SamFreeMemory(Buffer); + SamIndex += ReturnedCount; + } + + + } while (NT_SUCCESS(Status) && (ReturnedCount > 0)); + printf("QDI returned 0x%x\n",Status); + + return(Status); + + +} + +NTSTATUS +OpenGroup( LPWSTR * Parameter) +{ + PSID_NAME_USE Use = NULL; + PULONG Rid = NULL; + NTSTATUS Status; + UNICODE_STRING GroupName; + ULONG RelativeId = 0; + +// swscanf(Parameter[0],L"%x",&RelativeId); + if (RelativeId == 0) + { + RtlInitUnicodeString( + &GroupName, + Parameter[0] + ); + + printf("Looking up group %wZ\n",&GroupName); + + Status = SamLookupNamesInDomain( + DomainHandle, + 1, + &GroupName, + &Rid, + &Use + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to lookup group: 0x%x\n",Status); + return(Status); + } + RelativeId = *Rid; + SamFreeMemory(Rid); + SamFreeMemory(Use); + } + + printf("Opening Group 0x%x\n",RelativeId); + Status= SamOpenGroup( + DomainHandle, + MAXIMUM_ALLOWED, // GROUP_LIST_MEMBERS | GROUP_READ_INFORMATION, + RelativeId, + &GroupHandle + ); + + if (!NT_SUCCESS(Status)) + { + printf("Failed to open group: 0x%x\n",Status); + } + return(Status); +} + +NTSTATUS +GroupMembers(LPWSTR * Parameter) +{ + NTSTATUS Status; + ULONG MembershipCount; + PULONG Attributes = NULL; + PULONG Rids = NULL; + ULONG Index; + + Status = SamGetMembersInGroup( + GroupHandle, + &Rids, + &Attributes, + &MembershipCount + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get members in group: 0x%x\n",Status); + return(Status); + } + + for (Index = 0; Index < MembershipCount ; Index++ ) + { + printf("Member %d: rid 0x%x, attributes 0x%x\n", + Index,Rids[Index],Attributes[Index]); + } + SamFreeMemory(Rids); + SamFreeMemory(Attributes); + return(STATUS_SUCCESS); + +} + + +NTSTATUS +OpenAlias( LPWSTR * Parameter) +{ + PSID_NAME_USE Use = NULL; + PULONG Rid = NULL; + NTSTATUS Status; + UNICODE_STRING AliasName; + ULONG RelativeId; + + swscanf(Parameter[0],L"%x",&RelativeId); + if (RelativeId == 0) + { + RtlInitUnicodeString( + &AliasName, + Parameter[0] + ); + + printf("Looking up Alias %wZ\n",&AliasName); + + Status = SamLookupNamesInDomain( + DomainHandle, + 1, + &AliasName, + &Rid, + &Use + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to lookup Alias: 0x%x\n",Status); + return(Status); + } + RelativeId = *Rid; + SamFreeMemory(Rid); + SamFreeMemory(Use); + } + + + printf("Opening Alias 0x%x\n",RelativeId); + Status= SamOpenAlias( + DomainHandle, + ALIAS_LIST_MEMBERS | ALIAS_ADD_MEMBER, + RelativeId, + &AliasHandle + ); + + if (!NT_SUCCESS(Status)) + { + printf("Failed to open alias: 0x%x\n",Status); + } + return(Status); +} + +NTSTATUS +AliasMembers(LPWSTR * Parameter) +{ + NTSTATUS Status; + ULONG MembershipCount; + PSID * Members = NULL; + ULONG Index; + UNICODE_STRING Sid; + + Status = SamGetMembersInAlias( + AliasHandle, + &Members, + &MembershipCount + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get members in Alias: 0x%x\n",Status); + return(Status); + } + + for (Index = 0; Index < MembershipCount ; Index++ ) + { + RtlConvertSidToUnicodeString( + &Sid, + Members[Index], + TRUE + ); + printf("Member %d: sid %wZ\n", + Index,&Sid); + RtlFreeUnicodeString(&Sid); + } + SamFreeMemory(Members); + return(STATUS_SUCCESS); + +} + + +NTSTATUS +GetAliasMembership(LPWSTR * Parameter) +{ + NTSTATUS Status; + ULONG Index; + UNICODE_STRING Name; + OBJECT_ATTRIBUTES Oa; + LSA_HANDLE LsaHandle = NULL; + CAIROSID Sid; + ULONG SidLength = sizeof(CAIROSID); + WCHAR ReferencedDomainName[100]; + ULONG DomainNameLength = 100; + SID_NAME_USE SidUse; + ULONG MembershipCount; + PULONG AliasList = NULL; + PSID SidAddress = (PSID) &Sid; + + + + printf("Looking up groups for user %ws\n",Parameter[0]); + + if (!LookupAccountNameW( + NULL, + Parameter[0], + SidAddress, + &SidLength, + ReferencedDomainName, + &DomainNameLength, + &SidUse)) + { + printf("Failed to lookup account sid: %d\n",GetLastError()); + return(STATUS_UNSUCCESSFUL); + } + + + + Status = SamGetAliasMembership( + DomainHandle, + 1, + &SidAddress, + &MembershipCount, + &AliasList + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get alises : 0x%x\n",Status); + return(Status); + } + + for (Index = 0; Index < MembershipCount ; Index++ ) + { + printf("Alias Member %d: rid 0x%x\n", + Index,AliasList[Index]); + + } + SamFreeMemory(AliasList); + return(STATUS_SUCCESS); + +} + +NTSTATUS +GetAccountRid( LPWSTR Parameter, + PULONG RelativeId) +{ + + PSID_NAME_USE Use = NULL; + PULONG Rid = NULL; + NTSTATUS Status; + UNICODE_STRING UserName; + + RtlInitUnicodeString( + &UserName, + Parameter + ); + + printf("Looking up User %wZ\n",&UserName); + + Status = SamLookupNamesInDomain( + DomainHandle, + 1, + &UserName, + &Rid, + &Use + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to lookup User: 0x%x\n",Status); + return(Status); + } + *RelativeId = *Rid; + SamFreeMemory(Rid); + SamFreeMemory(Use); + + return(STATUS_SUCCESS); +} +NTSTATUS +OpenUser( LPWSTR * Parameter) +{ + PSID_NAME_USE Use = NULL; + PULONG Rid = NULL; + NTSTATUS Status; + UNICODE_STRING UserName; + ULONG RelativeId = 0; + + Status = GetAccountRid(Parameter[0],&RelativeId); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get account rid: 0x%x\n",Status); + return(Status); + } + + printf("Opening User 0x%x\n",RelativeId); + Status= SamOpenUser( + DomainHandle, + MAXIMUM_ALLOWED, + RelativeId, + &UserHandle + ); + + if (!NT_SUCCESS(Status)) + { + printf("Failed to open User: 0x%x\n",Status); + } + return(Status); +} + +NTSTATUS +DelUser( LPWSTR * Parameter) +{ + NTSTATUS Status; + + Status = SamDeleteUser(UserHandle); + if (!NT_SUCCESS(Status)) + { + printf("Failed to delete user: 0x%x\n",Status); + } + return(Status); +} + +NTSTATUS +DelGroup( LPWSTR * Parameter) +{ + NTSTATUS Status; + + Status = SamDeleteGroup(GroupHandle); + if (!NT_SUCCESS(Status)) + { + printf("Failed to delete user: 0x%x\n",Status); + } + return(Status); +} + +NTSTATUS +DelAlias( LPWSTR * Parameter) +{ + NTSTATUS Status; + + Status = SamDeleteAlias(AliasHandle); + if (!NT_SUCCESS(Status)) + { + printf("Failed to delete Alias: 0x%x\n",Status); + } + return(Status); +} + +NTSTATUS +CreateUser( LPWSTR * Parameter) +{ + PSID_NAME_USE Use = NULL; + PULONG Rid = NULL; + NTSTATUS Status; + UNICODE_STRING UserName; + ULONG RelativeId = 0; + ACCESS_MASK GrantedAccess; + + + RtlInitUnicodeString( + &UserName, + Parameter[0] + ); + + printf("Creating User %wZ\n",&UserName); + + Status= SamCreateUser2InDomain( + DomainHandle, + &UserName, + USER_NORMAL_ACCOUNT, + MAXIMUM_ALLOWED, + &UserHandle, + &GrantedAccess, + &RelativeId + ); + + if (!NT_SUCCESS(Status)) + { + printf("Failed to create User: 0x%x\n",Status); + return(Status); + } + printf("Created user with rid 0x%x, access 0x%x\n", + RelativeId, GrantedAccess); + return(Status); +} + +NTSTATUS +CreateGroup( LPWSTR * Parameter) +{ + PSID_NAME_USE Use = NULL; + PULONG Rid = NULL; + NTSTATUS Status; + UNICODE_STRING GroupName; + ULONG RelativeId = 0; + + + RtlInitUnicodeString( + &GroupName, + Parameter[0] + ); + + printf("Creating Group %wZ\n",&GroupName); + + Status= SamCreateGroupInDomain( + DomainHandle, + &GroupName, + MAXIMUM_ALLOWED, + &GroupHandle, + &RelativeId + ); + + if (!NT_SUCCESS(Status)) + { + printf("Failed to create Group: 0x%x\n",Status); + return(Status); + } + printf("Created Group with rid 0x%x, access 0x%x\n", + RelativeId); + return(Status); +} + +NTSTATUS +CreateAlias( LPWSTR * Parameter) +{ + PSID_NAME_USE Use = NULL; + PULONG Rid = NULL; + NTSTATUS Status; + UNICODE_STRING AliasName; + ULONG RelativeId = 0; + + + RtlInitUnicodeString( + &AliasName, + Parameter[0] + ); + + printf("Creating Alias %wZ\n",&AliasName); + + Status= SamCreateAliasInDomain( + DomainHandle, + &AliasName, + MAXIMUM_ALLOWED, + &AliasHandle, + &RelativeId + ); + + if (!NT_SUCCESS(Status)) + { + printf("Failed to create Alias: 0x%x\n",Status); + return(Status); + } + printf("Created Alias with rid 0x%x, access 0x%x\n", + RelativeId); + return(Status); +} + + + +NTSTATUS +GetGroupsForUser(LPWSTR * Parameter) +{ + NTSTATUS Status; + ULONG MembershipCount; + PULONG Attributes = NULL; + PULONG Rids = NULL; + ULONG Index; + PGROUP_MEMBERSHIP Groups = NULL; + + Status = SamGetGroupsForUser( + UserHandle, + &Groups, + &MembershipCount + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get groups for user: 0x%x\n",Status); + return(Status); + } + + for (Index = 0; Index < MembershipCount ; Index++ ) + { + printf("Member %d: rid 0x%x, attributes 0x%x\n", + Index,Groups[Index].RelativeId, Groups[Index].Attributes ); + } + SamFreeMemory(Groups); + return(STATUS_SUCCESS); + +} + +void +PrintLogonHours( + char * String, + PLOGON_HOURS LogonHours + ) +{ + int Index; + printf("%s",String); + for (Index = 0; Index < (LogonHours->UnitsPerWeek + 7) / 8 ;Index++ ) + { + printf("0x%2.2x ",LogonHours->LogonHours[Index]); + } + printf("\n"); +} + + +NTSTATUS +DumpUser(LPWSTR * Parameter) +{ + NTSTATUS Status; + PUSER_ALL_INFORMATION UserAll = NULL; + PUSER_GENERAL_INFORMATION UserGeneral = NULL; + PUSER_PREFERENCES_INFORMATION UserPreferences = NULL; + PUSER_LOGON_INFORMATION UserLogon = NULL; + PUSER_ACCOUNT_INFORMATION UserAccount = NULL; + PUSER_ACCOUNT_NAME_INFORMATION UserAccountName = NULL; + PUSER_FULL_NAME_INFORMATION UserFullName = NULL; + PUSER_NAME_INFORMATION UserName = NULL; + PUSER_PRIMARY_GROUP_INFORMATION UserPrimary = NULL; + PUSER_HOME_INFORMATION UserHome = NULL; + PUSER_SCRIPT_INFORMATION UserScript = NULL; + PUSER_PROFILE_INFORMATION UserProfile = NULL; + PUSER_ADMIN_COMMENT_INFORMATION UserAdminComment = NULL; + PUSER_WORKSTATIONS_INFORMATION UserWksta = NULL; + PUSER_CONTROL_INFORMATION UserControl = NULL; + PUSER_EXPIRES_INFORMATION UserExpires = NULL; + PUSER_LOGON_HOURS_INFORMATION UserLogonHours = NULL; + + printf("\nDumpUser.\n"); + Status = SamQueryInformationUser( + UserHandle, + UserAllInformation, + (PVOID *) &UserAll + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user all: 0x%x\n",Status); + return(Status); + } + printf("UserAll:\n"); + PrintTime("\tLastLogon = ",&UserAll->LastLogon); + PrintTime("\tLastLogoff = ",&UserAll->LastLogoff); + PrintTime("\tPasswordLastSet = ",&UserAll->PasswordLastSet); + PrintTime("\tAccountExpires = ",&UserAll->AccountExpires); + PrintTime("\tPasswordCanChange = ",&UserAll->PasswordCanChange); + PrintTime("\tPasswordMustChange = ",&UserAll->PasswordMustChange); + printf("\tUserName = %wZ\n",&UserAll->UserName); + printf("\tFullName = %wZ\n",&UserAll->FullName); + printf("\tHomeDirectory = %wZ\n",&UserAll->HomeDirectory); + printf("\tHomeDirectoryDrive = %wZ\n",&UserAll->HomeDirectoryDrive); + printf("\tScriptPath = %wZ\n",&UserAll->ScriptPath); + printf("\tProfilePath = %wZ\n",&UserAll->ProfilePath); + printf("\tAdminComment = %wZ\n",&UserAll->AdminComment); + printf("\tWorkStations = %wZ\n",&UserAll->WorkStations); + printf("\tUserComment = %wZ\n",&UserAll->UserComment); + printf("\tParameters = %wZ\n",&UserAll->Parameters); + printf("\tUserId = 0x%x\n",UserAll->UserId); + printf("\tPrimaryGroupId = 0x%x\n",UserAll->PrimaryGroupId); + printf("\tUserAccountControl = 0x%x\n",UserAll->UserAccountControl); + printf("\tWhichFields = 0x%x\n",UserAll->WhichFields); + PrintLogonHours("\tLogonHours = ",&UserAll->LogonHours); + printf("\tLogonCount = %d\n",UserAll->LogonCount); + printf("\tCountryCode = %d\n",UserAll->CountryCode); + printf("\tCodePage = %d\n",UserAll->CodePage); + + SamFreeMemory(UserAll); + + Status = SamQueryInformationUser( + UserHandle, + UserGeneralInformation, + (PVOID *) &UserGeneral + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user general: 0x%x\n",Status); + return(Status); + } + + printf("UserGeneral:\n"); + printf("\tUserName = %wZ\n",&UserGeneral->UserName); + printf("\tFullName = %wZ\n",&UserGeneral->FullName); + printf("\tPrimaryGroupId = 0x%x\n",UserGeneral->PrimaryGroupId); + printf("\tAdminComment = 0x%x\n",&UserGeneral->AdminComment); + printf("\tUserComment = 0x%x\n",&UserGeneral->UserComment); + + SamFreeMemory(UserGeneral); + + Status = SamQueryInformationUser( + UserHandle, + UserPreferencesInformation, + (PVOID *) &UserPreferences + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user preferences: 0x%x\n",Status); + return(Status); + } + + printf("UserPreferences:\n"); + printf("\tUserComment = %wZ\n",&UserPreferences->UserComment); + printf("\tReserved1 = %wZ\n",&UserPreferences->Reserved1); + printf("\tCountryCode = %d\n",&UserPreferences->CountryCode); + printf("\tCodePage = %d\n",&UserPreferences->CodePage); + + SamFreeMemory(UserPreferences); + + Status = SamQueryInformationUser( + UserHandle, + UserLogonInformation, + (PVOID *) &UserLogon + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user Logon: 0x%x\n",Status); + return(Status); + } + + + printf("UserLogon:\n"); + printf("\tUserName = %wZ\n",&UserLogon->UserName); + printf("\tFullName = %wZ\n",&UserLogon->FullName); + printf("\tUserId = 0x%x\n",UserLogon->UserId); + printf("\tPrimaryGroupId = 0x%x\n",UserLogon->PrimaryGroupId); + printf("\tHomeDirectory = %wZ\n",&UserLogon->HomeDirectory); + printf("\tHomeDirectoryDrive = %wZ\n",&UserLogon->HomeDirectoryDrive); + printf("\tScriptPath = %wZ\n",&UserLogon->ScriptPath); + printf("\tProfilePath = %wZ\n",&UserLogon->ProfilePath); + printf("\tWorkStations = %wZ\n",&UserLogon->WorkStations); + PrintTime("\tLastLogon = ",&UserLogon->LastLogon); + PrintTime("\tLastLogoff = ",&UserLogon->LastLogoff); + PrintTime("\tPasswordLastSet = ",&UserLogon->PasswordLastSet); + PrintTime("\tPasswordCanChange = ",&UserLogon->PasswordCanChange); + PrintTime("\tPasswordMustChange = ",&UserLogon->PasswordMustChange); + PrintLogonHours("\tLogonHours = ",&UserLogon->LogonHours); + printf("\tBadPasswordCount = %d\n",UserLogon->BadPasswordCount); + printf("\tLogonCount = %d\n",UserLogon->LogonCount); + printf("\tUserAccountControl = 0x%x\n",UserLogon->UserAccountControl); + + SamFreeMemory(UserLogon); + + Status = SamQueryInformationUser( + UserHandle, + UserAccountInformation, + (PVOID *) &UserAccount + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user account: 0x%x\n",Status); + return(Status); + } + + printf("UserAccount:\n"); + printf("\tUserName = %wZ\n",&UserAccount->UserName); + printf("\tFullName = %wZ\n",&UserAccount->FullName); + printf("\tUserId = 0x%x\n",UserAccount->UserId); + printf("\tPrimaryGroupId = 0x%x\n",UserAccount->PrimaryGroupId); + printf("\tHomeDirectory = %wZ\n",&UserAccount->HomeDirectory); + printf("\tHomeDirectoryDrive = %wZ\n",&UserAccount->HomeDirectoryDrive); + printf("\tScriptPath = %wZ\n",&UserAccount->ScriptPath); + printf("\tProfilePath = %wZ\n",&UserAccount->ProfilePath); + printf("\tAdminComment = %wZ\n",&UserAccount->AdminComment); + printf("\tWorkStations = %wZ\n",&UserAccount->WorkStations); + PrintTime("\tLastLogon = ",&UserAccount->LastLogon); + PrintTime("\tLastLogoff = ",&UserAccount->LastLogoff); + PrintLogonHours("\tLogonHours = ",&UserAccount->LogonHours); + printf("\tBadPasswordCount = %d\n",UserAccount->BadPasswordCount); + printf("\tLogonCount = %d\n",UserAccount->LogonCount); + PrintTime("\tPasswordLastSet = ",&UserAccount->PasswordLastSet); + PrintTime("\tAccountExpires = ",&UserAccount->AccountExpires); + printf("\tUserAccountControl = 0x%x\n",UserAccount->UserAccountControl); + + SamFreeMemory(UserAccount); + + Status = SamQueryInformationUser( + UserHandle, + UserAccountNameInformation, + (PVOID *) &UserAccountName + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user account name: 0x%x\n",Status); + return(Status); + } + + printf("UserAccountName:\n"); + printf("\tUserName = %wZ\n",&UserAccountName->UserName); + SamFreeMemory(UserAccountName); + + Status = SamQueryInformationUser( + UserHandle, + UserFullNameInformation, + (PVOID *) &UserFullName + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user full name: 0x%x\n",Status); + return(Status); + } + + printf("UserFullName:\n"); + printf("\tFullName = %wZ\n",&UserFullName->FullName); + SamFreeMemory(UserFullName); + + Status = SamQueryInformationUser( + UserHandle, + UserNameInformation, + (PVOID *) &UserName + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user name: 0x%x\n",Status); + return(Status); + } + + printf("UserName:\n"); + printf("\tUserName = %wZ\n",&UserName->UserName); + printf("\tFullName = %wZ\n",&UserName->FullName); + SamFreeMemory(UserName); + + Status = SamQueryInformationUser( + UserHandle, + UserPrimaryGroupInformation, + (PVOID *) &UserPrimary + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user all: 0x%x\n",Status); + return(Status); + } + printf("UserPrimaryGroup:\n"); + printf("PrimaryGroupid = 0x%x\n",UserPrimary->PrimaryGroupId); + SamFreeMemory(UserPrimary); + + Status = SamQueryInformationUser( + UserHandle, + UserHomeInformation, + (PVOID *) &UserHome + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user home: 0x%x\n",Status); + return(Status); + } + printf("UserHome:\n"); + printf("\tHomeDirectory = %wZ\n",&UserHome->HomeDirectory); + printf("\tHomeDirectoryDrive = %wZ\n",&UserHome->HomeDirectoryDrive); + + SamFreeMemory(UserHome); + + Status = SamQueryInformationUser( + UserHandle, + UserScriptInformation, + (PVOID *) &UserScript + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user Script: 0x%x\n",Status); + return(Status); + } + printf("UserScript:\n"); + printf("\tScriptPath = %wZ\n",&UserScript->ScriptPath); + + SamFreeMemory(UserScript); + + Status = SamQueryInformationUser( + UserHandle, + UserProfileInformation, + (PVOID *) &UserProfile + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user Profile: 0x%x\n",Status); + return(Status); + } + printf("UserProfile:\n"); + printf("\tProfilePath = %wZ\n",&UserProfile->ProfilePath); + + SamFreeMemory(UserProfile); + Status = SamQueryInformationUser( + UserHandle, + UserAdminCommentInformation, + (PVOID *) &UserAdminComment + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user AdminComment: 0x%x\n",Status); + return(Status); + } + printf("UserAdminComment:\n"); + printf("\tAdminComment = %wZ\n",&UserAdminComment->AdminComment); + SamFreeMemory(UserAdminComment); + + Status = SamQueryInformationUser( + UserHandle, + UserWorkStationsInformation, + (PVOID *) &UserWksta + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user wksta: 0x%x\n",Status); + return(Status); + } + + printf("UserWorkStations:\n"); + printf("\tWorkStations = %wZ\n",&UserWksta->WorkStations); + SamFreeMemory(UserWksta); + + Status = SamQueryInformationUser( + UserHandle, + UserControlInformation, + (PVOID *) &UserControl + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user Control: 0x%x\n",Status); + return(Status); + } + + printf("UserControl:\n"); + printf("\tUserAccountControl = 0x%x\n",UserControl->UserAccountControl); + SamFreeMemory(UserControl); + + Status = SamQueryInformationUser( + UserHandle, + UserExpiresInformation, + (PVOID *) &UserExpires + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user Expires: 0x%x\n",Status); + return(Status); + } + + printf("UserExpires:\n"); + PrintTime("\tAccountExpires = ",&UserExpires->AccountExpires); + SamFreeMemory(UserExpires); + + Status = SamQueryInformationUser( + UserHandle, + UserLogonHoursInformation, + (PVOID *) &UserLogonHours + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query user LogonHours: 0x%x\n",Status); + return(Status); + } + printf("UserLogonHours:\n"); + PrintLogonHours("\tLogonHours = ",&UserLogonHours->LogonHours); + + SamFreeMemory(UserLogonHours); + + + return(STATUS_SUCCESS); +} + +NTSTATUS +DumpGroup(LPWSTR * Parameter) +{ + NTSTATUS Status; + PGROUP_GENERAL_INFORMATION General = NULL; + PGROUP_NAME_INFORMATION Name = NULL; + PGROUP_ATTRIBUTE_INFORMATION Attribute = NULL; + PGROUP_ADM_COMMENT_INFORMATION AdmComment = NULL; + + printf("\nDumpGroup.\n"); + Status = SamQueryInformationGroup( + GroupHandle, + GroupGeneralInformation, + (PVOID *) &General + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get group general information: 0x%x\n",Status); + return(Status); + } + + printf("Group General.Name = %wZ\n",&General->Name); + printf("Group General.Attributes = 0x%x\n",General->Attributes); + printf("Group general.memberCount = %d\n",General->MemberCount); + printf("Group general.AdminComment = %wZ\n",&General->AdminComment); + SamFreeMemory(General); + + Status = SamQueryInformationGroup( + GroupHandle, + GroupNameInformation, + (PVOID *) &Name + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get group name information: 0x%x\n",Status); + return(Status); + } + + printf("Group Name.Name = %wZ\n",&Name->Name); + SamFreeMemory(Name); + + Status = SamQueryInformationGroup( + GroupHandle, + GroupAttributeInformation, + (PVOID *) &Attribute + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get group Attribute information: 0x%x\n",Status); + return(Status); + } + + printf("Group Attribute.Attributes = 0x%x\n",Attribute->Attributes); + SamFreeMemory(Attribute); + + Status = SamQueryInformationGroup( + GroupHandle, + GroupAdminCommentInformation, + (PVOID *) &AdmComment + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to get group admin comment information: 0x%x\n",Status); + return(Status); + } + + printf("Group Admin comment.AdminComment = %wZ\n",&AdmComment->AdminComment); + SamFreeMemory(AdmComment); + + return(STATUS_SUCCESS); +} + +NTSTATUS +DumpAllGroups(LPWSTR * Parameter) +{ + ULONG PreferedMax = 1000; + NTSTATUS Status,EnumStatus; + SAM_ENUMERATE_HANDLE EnumContext = 0; + ULONG CountReturned; + LPWSTR GroupName[1]; + + PSAM_RID_ENUMERATION Accounts = NULL; + + + GroupName[0] = (LPWSTR) malloc(128); + + printf("DumpAllGroups:\n"); + + + EnumContext = 0; + ASSERT(DomainHandle != NULL); + do + { + EnumStatus = SamEnumerateGroupsInDomain( + DomainHandle, + &EnumContext, + (PVOID *) &Accounts, + PreferedMax, + &CountReturned + ); + + if (NT_SUCCESS(EnumStatus) && (EnumStatus != STATUS_NO_MORE_ENTRIES)) + { + ULONG Index; + UNICODE_STRING SidString; + + for (Index = 0; Index < CountReturned; Index++) + { + RtlCopyMemory( + GroupName[0], + Accounts[Index].Name.Buffer, + Accounts[Index].Name.Length + ); + GroupName[0][Accounts[Index].Name.Length/sizeof(WCHAR)] = L'\0'; + + Status = OpenGroup(GroupName); + if (!NT_SUCCESS(Status)) + { + break; + } + Status = DumpGroup(NULL); + SamCloseHandle(GroupHandle); + GroupHandle = NULL; + + } + SamFreeMemory(Accounts); + } + else printf("Failed to enumerate Groups: 0x%x\n",Status); + } while (NT_SUCCESS(EnumStatus) && (EnumStatus != STATUS_SUCCESS) && (CountReturned != 0) ); + + free(GroupName[0]); + return(STATUS_SUCCESS); +} + +NTSTATUS +DumpAllUsers(LPWSTR * Parameter) +{ + ULONG PreferedMax = 1000; + NTSTATUS Status,EnumStatus; + SAM_ENUMERATE_HANDLE EnumContext = 0; + ULONG CountReturned; + LPWSTR UserName[1]; + + PSAM_RID_ENUMERATION Accounts = NULL; + + + UserName[0] = (LPWSTR) malloc(128); + + printf("DumpAllUsers:\n"); + + + EnumContext = 0; + ASSERT(DomainHandle != NULL); + do + { + EnumStatus = SamEnumerateUsersInDomain( + DomainHandle, + &EnumContext, + 0, + (PVOID *) &Accounts, + PreferedMax, + &CountReturned + ); + + if (NT_SUCCESS(EnumStatus) && (EnumStatus != STATUS_NO_MORE_ENTRIES)) + { + ULONG Index; + UNICODE_STRING SidString; + + for (Index = 0; Index < CountReturned; Index++) + { + RtlCopyMemory( + UserName[0], + Accounts[Index].Name.Buffer, + Accounts[Index].Name.Length + ); + UserName[0][Accounts[Index].Name.Length/sizeof(WCHAR)] = L'\0'; + + Status = OpenUser(UserName); + if (!NT_SUCCESS(Status)) + { + break; + } + Status = DumpUser(NULL); + Status = GetGroupsForUser(NULL); + SamCloseHandle(UserHandle); + UserHandle = NULL; + + } + SamFreeMemory(Accounts); + } + else printf("Failed to enumerate users: 0x%x\n",Status); + } while (NT_SUCCESS(EnumStatus) && (EnumStatus != STATUS_SUCCESS) && (CountReturned != 0) ); + + free(UserName[0]); + return(STATUS_SUCCESS); +} + +NTSTATUS +AddAliasMember( LPWSTR * Parameter ) +{ + BYTE Buffer[100]; + PSID AccountSid = Buffer; + ULONG SidLen = 100; + SID_NAME_USE Use; + WCHAR ReferencedDomain[100]; + ULONG DomainLen = 100; + NTSTATUS Status; + + printf("Adding account %ws to alias\n",Parameter[0]); + if (!LookupAccountName( + NULL, + Parameter[0], + AccountSid, + &SidLen, + ReferencedDomain, + &DomainLen, + &Use)) + { + printf("Failed to lookup account name: %d\n",GetLastError()); + return(STATUS_UNSUCCESSFUL); + } + + Status = SamAddMemberToAlias( + AliasHandle, + AccountSid + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to add member to alias: 0x%x\n",Status); + } + return(Status); +} + +NTSTATUS +DumpDomain( LPWSTR * Parameter ) +{ + NTSTATUS Status; + PDOMAIN_PASSWORD_INFORMATION Password = NULL; + PDOMAIN_GENERAL_INFORMATION General = NULL; + PDOMAIN_LOGOFF_INFORMATION Logoff = NULL; + PDOMAIN_OEM_INFORMATION Oem = NULL; + PDOMAIN_NAME_INFORMATION Name = NULL; + PDOMAIN_REPLICATION_INFORMATION Replica = NULL; + PDOMAIN_SERVER_ROLE_INFORMATION ServerRole = NULL; + PDOMAIN_MODIFIED_INFORMATION Modified = NULL; + PDOMAIN_STATE_INFORMATION State = NULL; + PDOMAIN_GENERAL_INFORMATION2 General2 = NULL; + PDOMAIN_LOCKOUT_INFORMATION Lockout = NULL; + PDOMAIN_MODIFIED_INFORMATION2 Modified2 = NULL; + + + Status = SamQueryInformationDomain( + DomainHandle, + DomainPasswordInformation, + (PVOID *) &Password + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query password information: 0x%x\n",Status); + return(Status); + } + + printf("Password:\n"); + printf("\tMinPasswordLength = %d\n",Password->MinPasswordLength); + printf("\tPasswordHistoryLength = %d\n",Password->PasswordHistoryLength); + printf("\tPasswordProperties = 0x%x\n",Password->PasswordProperties); + PrintDeltaTime("\tMaxPasswordAge = ",&Password->MaxPasswordAge); + PrintDeltaTime("\tMinPasswordAge = ",&Password->MinPasswordAge); + + SamFreeMemory(Password); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainGeneralInformation, + (PVOID *) &General + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query general: 0x%x\n",Status); + return(Status); + } + + printf("General:\n"); + PrintDeltaTime("\t ForceLogoff = ",&General->ForceLogoff); + printf("\t OemInformation = %wZ\n",&General->OemInformation); + printf("\t DomainName = %wZ\n",&General->DomainName); + printf("\t ReplicaSourceNodeName =%wZ\n",&General->ReplicaSourceNodeName); + printf("\t DomainModifiedCount = 0x%x,0x%x\n", + General->DomainModifiedCount.HighPart, + General->DomainModifiedCount.LowPart ); + printf("\t DomainServerState = %d\n",General->DomainServerState); + printf("\t DomainServerRole = %d\n",General->DomainServerRole); + printf("\t UasCompatibilityRequired = %d\n",General->UasCompatibilityRequired); + printf("\t UserCount = %d\n",General->UserCount); + printf("\t GroupCount = %d\n",General->GroupCount); + printf("\t AliasCount = %d\n",General->AliasCount); + + SamFreeMemory(General); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainLogoffInformation, + (PVOID *) &Logoff + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query logoff: 0x%x\n",Status); + return(Status); + } + + printf("Logoff:\n"); + PrintDeltaTime("\t ForceLogoff = ",&Logoff->ForceLogoff); + SamFreeMemory(Logoff); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainOemInformation, + (PVOID *) &Oem + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query Oem: 0x%x\n",Status); + return(Status); + } + + printf("Oem:\n\t OemInformation = %wZ\n",&Oem->OemInformation); + + SamFreeMemory(Oem); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainNameInformation, + (PVOID *) &Name + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query Name: 0x%x\n",Status); + return(Status); + } + printf("Name:\n\t DomainName = %wZ\n",&Name->DomainName); + + SamFreeMemory(Name); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainReplicationInformation, + (PVOID *) &Replica + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query Replica: 0x%x\n",Status); + return(Status); + } + + printf("Replica:\n\t ReplicaSourceNodeName = %wZ\n", &Replica->ReplicaSourceNodeName); + SamFreeMemory(Replica); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainServerRoleInformation, + (PVOID *) &ServerRole + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query ServerRole: 0x%x\n",Status); + return(Status); + } + + printf("ServerRole:\n\t DomainServerRole = %d\n",ServerRole->DomainServerRole); + SamFreeMemory(ServerRole); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainModifiedInformation, + (PVOID *) &Modified + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query Modified: 0x%x\n",Status); + return(Status); + } + + printf("Modified:\n"); + printf("\t DomainModifiedCount = 0x%x,0x%x\n", + Modified->DomainModifiedCount.HighPart, + Modified->DomainModifiedCount.LowPart ); + PrintTime("\t CreationTime = ",&Modified->CreationTime); + + + + SamFreeMemory(Modified); + + + Status = SamQueryInformationDomain( + DomainHandle, + DomainStateInformation, + (PVOID *) &State + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query State: 0x%x\n",Status); + return(Status); + } + + printf("State:\n\t DomainServerState = %d\n",State->DomainServerState); + SamFreeMemory(State); + + + Status = SamQueryInformationDomain( + DomainHandle, + DomainGeneralInformation2, + (PVOID *) &General2 + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query General2: 0x%x\n",Status); + return(Status); + } + + printf("General2:\n"); + General = &General2->I1; + PrintDeltaTime("\t ForceLogoff = ",&General->ForceLogoff); + printf("\t OemInformation = %wZ\n",&General->OemInformation); + printf("\t DomainName = %wZ\n",&General->DomainName); + printf("\t ReplicaSourceNodeName =%wZ\n",&General->ReplicaSourceNodeName); + printf("\t DomainModifiedCount = 0x%x,0x%x\n", + General->DomainModifiedCount.HighPart, + General->DomainModifiedCount.LowPart ); + printf("\t DomainServerState = %d\n",General->DomainServerState); + printf("\t DomainServerRole = %d\n",General->DomainServerRole); + printf("\t UasCompatibilityRequired = %d\n",General->UasCompatibilityRequired); + printf("\t UserCount = %d\n",General->UserCount); + printf("\t GroupCount = %d\n",General->GroupCount); + printf("\t AliasCount = %d\n",General->AliasCount); + PrintDeltaTime("\t LockoutDuration = ",&General2->LockoutDuration); + PrintDeltaTime("\t LockoutObservationWindow = ",&General2->LockoutObservationWindow); + printf("\t LockoutThreshold = %d\n",General2->LockoutThreshold); + + SamFreeMemory(General2); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainLockoutInformation, + (PVOID *) &Lockout + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query Lockout: 0x%x\n",Status); + return(Status); + } + printf("Lockout:\n"); + PrintDeltaTime("\t LockoutDuration = ",&Lockout->LockoutDuration); + PrintDeltaTime("\t LockoutObservationWindow = ",&Lockout->LockoutObservationWindow); + printf("\t LockoutThreshold = %d\n",Lockout->LockoutThreshold); + + SamFreeMemory(Lockout); + + Status = SamQueryInformationDomain( + DomainHandle, + DomainModifiedInformation2, + (PVOID *) &Modified2 + ); + if (!NT_SUCCESS(Status)) + { + printf("Failed to query Modified2: 0x%x\n",Status); + return(Status); + } + printf("Modified2:\n"); + PrintTime("\t CreationTime = ",&Modified->CreationTime); + printf("\t DomainModifiedCount = 0x%x,0x%x\n", + Modified2->DomainModifiedCount.HighPart, + Modified2->DomainModifiedCount.LowPart ); + + printf("\t ModifiedCountAtLastPromotion = 0x%x,0x%x\n", + Modified2->ModifiedCountAtLastPromotion.HighPart, + Modified2->ModifiedCountAtLastPromotion.LowPart ); + + SamFreeMemory(Modified2); + + return(STATUS_SUCCESS); + +} + + + +void _cdecl +main(int argc, char *argv[]) +{ + ULONG Command = 0; + ULONG i,j,k; + BOOLEAN Found; + NTSTATUS Status; + Action Actions[20]; + ULONG ActionCount = 0; + + for (i = 1; i < (ULONG) argc ; i++ ) + { + Found = FALSE; + for (j = 0; j < NUM_COMMANDS ; j++ ) + { + if (!_stricmp(argv[i],Commands[j].Name)) + { + Actions[ActionCount].CommandNumber = j; + + if (Commands[j].Parameter != 0) + { + for (k = 0; k < Commands[j].Parameter ;k++ ) + { + Actions[ActionCount].Parameter[k] = (LPWSTR) malloc(128); + if ((ULONG) argc > i) + { + mbstowcs(Actions[ActionCount].Parameter[k],argv[++i],128); + } + else + { + Actions[ActionCount].Parameter[k][0] = L'\0'; + } + } + } + Found = TRUE; + ActionCount++; + break; + } + } + if (!Found) + { + printf("Switch %s not found\n", argv[i]); + return; + } + } + +// Status = OpenSam(); +// if (!NT_SUCCESS(Status)) +// { +// printf("Failed to open sam: 0x%x\n",Status); +// return; +// } + + for (i = 0; i < ActionCount ; i++ ) + { + Status = Commands[Actions[i].CommandNumber].Function(Actions[i].Parameter); + if (!NT_SUCCESS(Status)) + { + printf("Failed test %s : 0x%x\n",Commands[Actions[i].CommandNumber].Name,Status); + goto Cleanup; + + } + } + +Cleanup: + if (DomainHandle != NULL) + { + Status = SamCloseHandle(DomainHandle); + if (!NT_SUCCESS(Status)) + { + printf("Failed to close account: 0x%x\n",Status); + } + } + if (GroupHandle != NULL) + { + Status = SamCloseHandle(GroupHandle); + if (!NT_SUCCESS(Status)) + { + printf("Failed to close account: 0x%x\n",Status); + } + } + if (AliasHandle != NULL) + { + Status = SamCloseHandle(AliasHandle); + if (!NT_SUCCESS(Status)) + { + printf("Failed to close account: 0x%x\n",Status); + } + } + if (UserHandle != NULL) + { + Status = SamCloseHandle(UserHandle); + if (!NT_SUCCESS(Status)) + { + printf("Failed to close account: 0x%x\n",Status); + } + } + Status = CloseSam(); + if (!NT_SUCCESS(Status)) + { + printf("Failed to close lsa: 0x%x\n",Status); + } + + return; + +} diff --git a/private/newsam2/server/utility.c b/private/newsam2/server/utility.c new file mode 100644 index 000000000..41c4cd12d --- /dev/null +++ b/private/newsam2/server/utility.c @@ -0,0 +1,7253 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + utility.c + +Abstract: + + This file contains utility services used by several other SAM files. + +Author: + + Jim Kelly (JimK) 4-July-1991 + +Environment: + + User Mode - Win32 + +Revision History: + + 6-11-96: MURLIS Added logic to branch between registry/ DS cases + 6-16-96: MURLIS Added Logic to Open Account/ Adjust Account counts + + +--*/ + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + +#include <samsrvp.h> +#include <dslayer.h> +#include <mappings.h> +#include <ntlsa.h> +#include <nlrepl.h> + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// private service prototypes // +// // +/////////////////////////////////////////////////////////////////////////////// + + +#define VERBOSE_FLUSH 0 + +#if VERBOSE_FLUSH +#define VerbosePrint(s) KdPrint(s) +#else +#define VerbosePrint(s) +#endif + +NTSTATUS +SampCommitChangesToRegistry( + BOOLEAN * AbortDone + ); + +NTSTATUS +SampRefreshRegistry( + VOID + ); + +NTSTATUS +SampRetrieveAccountCountsRegistry( + OUT PULONG UserCount, + OUT PULONG GroupCount, + OUT PULONG AliasCount + ); + +NTSTATUS +SampRetrieveAccountCountsDs( + OUT PULONG UserCount, + OUT PULONG GroupCount, + OUT PULONG AliasCount + ); + +NTSTATUS +SampAdjustAccountCountDs( + IN SAMP_OBJECT_TYPE ObjectType, + IN BOOLEAN Increment + ); + +NTSTATUS +SampAdjustAccountCountRegistry( + IN SAMP_OBJECT_TYPE ObjectType, + IN BOOLEAN Increment + ); + + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Database/registry access lock services // +// // +/////////////////////////////////////////////////////////////////////////////// + + +VOID +SampAcquireReadLock( + VOID + ) + +/*++ + +Routine Description: + + This routine obtains read access to the SAM data structures and + backing store. + + Despite its apparent implications, read access is an exclusive access. + This is to support the model set up in which global variables are used + to track the "current" domain. In the future, if performance warrants, + a read lock could imply shared access to SAM data structures. + + The primary implication of a read lock at this time is that no + changes to the SAM database will be made which require a backing + store update. + + +Arguments: + + None. + +Return Value: + + + None. + + +--*/ +{ + BOOLEAN Success; + + SAMTRACE("SampAcquireReadLock"); + + // + // Before changing this to a non-exclusive lock, the display information + // module must be changed to use a separate locking mechanism. Davidc 5/12/92 + // + + Success = RtlAcquireResourceExclusive( &SampLock, TRUE ); + ASSERT(Success); + + SampWriteLock = FALSE; + + // + // Allow LSA a chance to perform an integrity check + // + + LsaIHealthCheck( LsaIHealthSamJustLocked ); + + return; +} + + +VOID +SampReleaseReadLock( + VOID + ) + +/*++ + +Routine Description: + + This routine releases shared read access to the SAM data structures and + backing store. + + +Arguments: + + None. + +Return Value: + + + None. + + +--*/ +{ + + SAMTRACE("SampReleaseReadLock"); + + // + // Allow LSA a chance to perform an integrity check + // + + LsaIHealthCheck( LsaIHealthSamAboutToFree ); + + + SampTransactionWithinDomain = FALSE; + RtlReleaseResource( &SampLock ); + + return; +} + + +NTSTATUS +SampAcquireWriteLock( + VOID + ) + +/*++ + +Routine Description: + + This routine acquires exclusive access to the SAM data structures and + backing store. + + This access is needed to perform a write operation. + + This routine also initiates a new transaction for the write operation. + + + NOTE: It is not acceptable to acquire this lock recursively. An + attempt to do so will fail. + + +Arguments: + + None. + +Return Value: + + STATUS_SUCCESS - Indicates the write lock was acquired and the transaction + was successfully started. + + Other values may be returned as a result of failure to initiate the + transaction. These include any values returned by RtlStartRXact(). + + + +--*/ +{ + NTSTATUS NtStatus; + + SAMTRACE("SampAcquireWriteLock"); + + (VOID)RtlAcquireResourceExclusive( &SampLock, TRUE ); + + SampWriteLock = TRUE; + + SampTransactionWithinDomain = FALSE; + + // Start the registry Transaction + NtStatus = RtlStartRXact( SampRXactContext ); + + if (!NT_SUCCESS(NtStatus)) + goto Error; + + + // + // Allow LSA a chance to perform an integrity check + // + + LsaIHealthCheck( LsaIHealthSamJustLocked ); + + return(NtStatus); + + +Error: + + // + // If the transaction failed, release the lock. + // + + (VOID)RtlReleaseResource( &SampLock ); + + DbgPrint("SAM: StartRxact failed, status = 0x%lx\n", NtStatus); + + return(NtStatus); +} + + +VOID +SampSetTransactionDomain( + IN ULONG DomainIndex + ) + +/*++ + +Routine Description: + + This routine sets a domain for a transaction. This must be done + if any domain-specific information is to be modified during a transaction. + In this case, the domain modification count will be updated upon commit. + + This causes the UnmodifiedFixed information for the specified domain to + be copied to the CurrentFixed field for the in-memory representation of + that domain. + + +Arguments: + + DomainIndex - Index of the domain within which this transaction + will occur. + + +Return Value: + + STATUS_SUCCESS - Indicates the write lock was acquired and the transaction + was successfully started. + + Other values may be returned as a result of failure to initiate the + transaction. These include any values returned by RtlStartRXact(). + + + +--*/ +{ + + SAMTRACE("SampSetTransactionDomain"); + + ASSERT(SampTransactionWithinDomain == FALSE); + + SampTransactionWithinDomain = TRUE; + SampTransactionDomainIndex = DomainIndex; + + SampDefinedDomains[SampTransactionDomainIndex].CurrentFixed = + SampDefinedDomains[SampTransactionDomainIndex].UnmodifiedFixed; + + + return; + +} + + + +NTSTATUS +SampFlushThread( + IN PVOID ThreadParameter + ) + +/*++ + +Routine Description: + + This thread is created when SAM's registry tree is changed. + It will sleep for a while, and if no other changes occur, + flush the changes to disk. If other changes keep occurring, + it will wait for a certain amount of time and then flush + anyway. + + After flushing, the thread will wait a while longer. If no + other changes occur, it will exit. + + Note that if any errors occur, this thread will simply exit + without flushing. The mainline code should create another thread, + and hopefully it will be luckier. Unfortunately, the error is lost + since there's nobody to give it to that will be able to do anything + about it. + +Arguments: + + ThreadParameter - not used. + +Return Value: + + None. + +--*/ + +{ + TIME minDelayTime, maxDelayTime, exitDelayTime; + LARGE_INTEGER startedWaitLoop; + LARGE_INTEGER currentTime; + NTSTATUS NtStatus; + BOOLEAN Finished = FALSE; + + UNREFERENCED_PARAMETER( ThreadParameter ); + + SAMTRACE("SampFlushThread"); + + NtQuerySystemTime( &startedWaitLoop ); + + // + // It would be more efficient to use constants here, but for now + // we'll recalculate the times each time we start the thread + // so that somebody playing with us can change the global + // time variables to affect performance. + // + + minDelayTime.QuadPart = -1000 * 1000 * 10 * + ((LONGLONG)SampFlushThreadMinWaitSeconds); + + maxDelayTime.QuadPart = -1000 * 1000 * 10 * + ((LONGLONG)SampFlushThreadMaxWaitSeconds); + + exitDelayTime.QuadPart = -1000 * 1000 * 10 * + ((LONGLONG)SampFlushThreadExitDelaySeconds); + + do { + + VerbosePrint(("SAM: Flush thread sleeping\n")); + + NtDelayExecution( FALSE, &minDelayTime ); + + VerbosePrint(("SAM: Flush thread woke up\n")); + + NtStatus = SampAcquireWriteLock(); + + if ( NT_SUCCESS( NtStatus ) ) { + +#ifdef SAMP_DBG_CONTEXT_TRACKING + SampDumpContexts(); +#endif + + NtQuerySystemTime( ¤tTime ); + + if ( LastUnflushedChange.QuadPart == SampHasNeverTime.QuadPart ) { + + LARGE_INTEGER exitBecauseNoWorkRecentlyTime; + + // + // No changes to flush. See if we should stick around. + // + + exitBecauseNoWorkRecentlyTime = SampAddDeltaTime( + startedWaitLoop, + exitDelayTime + ); + + if ( exitBecauseNoWorkRecentlyTime.QuadPart < currentTime.QuadPart ) { + + // + // We've waited for changes long enough; note that + // the thread is exiting. + // + + FlushThreadCreated = FALSE; + Finished = TRUE; + } + + } else { + + LARGE_INTEGER noRecentChangesTime; + LARGE_INTEGER tooLongSinceFlushTime; + + // + // There are changes to flush. See if it's time to do so. + // + + noRecentChangesTime = SampAddDeltaTime( + LastUnflushedChange, + minDelayTime + ); + + tooLongSinceFlushTime = SampAddDeltaTime( + startedWaitLoop, + maxDelayTime + ); + + if ( (noRecentChangesTime.QuadPart < currentTime.QuadPart) || + (tooLongSinceFlushTime.QuadPart < currentTime.QuadPart) ) { + + // + // Min time has passed since last change, or Max time + // has passed since last flush. Let's flush. + // + + NtStatus = NtFlushKey( SampKey ); + +#if SAMP_DIAGNOSTICS + if (!NT_SUCCESS(NtStatus)) { + SampDiagPrint( DISPLAY_STORAGE_FAIL, + ("SAM: Failed to flush RXact (0x%lx)\n", + NtStatus) ); + IF_SAMP_GLOBAL( BREAK_ON_STORAGE_FAIL ) { + ASSERT(NT_SUCCESS(NtStatus)); // See following comment + } + } +#endif //SAMP_DIAGNOSTICS + + // + // Under normal conditions, we would have an + // ASSERT(NT_SUCCESS(NtStatus)) here. However, + // Because system shutdown can occur while we + // are waiting to flush, we have a race condition. + // When shutdown is made, another thread will be + // notified and perform a flush. That leaves this + // flush to potentially occur after the registry + // has been notified of system shutdown - which + // causes and error to be returned. Unfortunately, + // the error is REGISTRY_IO_FAILED - a great help. + // + // Despite this, we will only exit this loop only + // if we have success. This may cause us to enter + // into another wait and attempt another hive flush + // during shutdown, but the wait should never finish + // (unless shutdown takes more than 30 seconds). In + // other error situations though, we want to keep + // trying the flush until we succeed. Jim Kelly + // + + + if ( NT_SUCCESS(NtStatus) ) { + + LastUnflushedChange = SampHasNeverTime; + NtQuerySystemTime( &startedWaitLoop ); + + FlushThreadCreated = FALSE; + Finished = TRUE; + } + } + } + + SampReleaseWriteLock( FALSE ); + + } else { + + DbgPrint("SAM: Thread failed to get write lock, status = 0x%lx\n", NtStatus); + ASSERT( NT_SUCCESS(NtStatus) ); + + FlushThreadCreated = FALSE; + Finished = TRUE; + } + + } while ( !Finished ); + + return( STATUS_SUCCESS ); +} + + + +NTSTATUS +SampCommitChanges( + ) + +/*++ + +Routine Description: + + Thie service commits any changes made to the backstore while exclusive + access was held. + + If the operation was within a domain (which would have been indicated + via the SampSetTransactionDomain() api), then the CurrentFixed field for + that domain is added to the transaction before the transaction is + committed. + + NOTE: Write operations within a domain do not have to worry about + updating the modified count for that domain. This routine + will automatically increment the ModifiedCount for a domain + when a commit is requested within that domain. + + NOTE: When this routine returns any transaction will have either been + committed or aborted. i.e. there will be no transaction in progress. + +Arguments: + + None. + +Return Value: + + STATUS_SUCCESS - Indicates the transaction was successfully commited. + + Other values may be returned as a result of commital failure. + +--*/ + +{ + NTSTATUS NtStatus, IgnoreStatus; + BOOLEAN DomainInfoChanged = FALSE; + BOOLEAN AbortDone = FALSE; + BOOLEAN DsTransaction = FALSE; + + + SAMTRACE("SampCommitChanges"); + + NtStatus = STATUS_SUCCESS; + + // + // If this transaction was within a domain then we have to: + // + // (1) Update the ModifiedCount of that domain, + // + // (2) Write out the CurrentFixed field for that + // domain (using RtlAddActionToRXact(), so that it + // is part of the current transaction). + // + // (3) Commit the RXACT. + // + // (4) If the commit is successful, then update the + // in-memory copy of the un-modified fixed-length data. + // + // Otherwise, we just do the commit. + // + + if (SampTransactionWithinDomain == TRUE) { + + + // It is a DS transaction if Its a Transaction within Domain + // and the Domain object is a DS Object + + DsTransaction = IsDsObject(((SampDefinedDomains[SampTransactionDomainIndex]).Context)); + + if (SampDefinedDomains[SampTransactionDomainIndex].CurrentFixed.ServerRole + != DomainServerRoleBackup) { + + // + // Don't update the modified count on backup controllers; + // the replicator will explicitly set the modified count. + // + + SampDefinedDomains[SampTransactionDomainIndex].CurrentFixed.ModifiedCount.QuadPart = + SampDefinedDomains[SampTransactionDomainIndex].CurrentFixed.ModifiedCount.QuadPart + + 1; + + DomainInfoChanged = TRUE; + + } else { + + // + // See if the domain information changed - if it did, we + // need to add the change to the RXACT. + // + + if ( RtlCompareMemory( + &SampDefinedDomains[SampTransactionDomainIndex].CurrentFixed, + &SampDefinedDomains[SampTransactionDomainIndex].UnmodifiedFixed, + sizeof(SAMP_V1_0A_FIXED_LENGTH_DOMAIN) ) != + sizeof( SAMP_V1_0A_FIXED_LENGTH_DOMAIN) ) { + + DomainInfoChanged = TRUE; + } + } + + if ( DomainInfoChanged ) { + + // + // The domain object's fixed information has changed, so set + // the changes in the domain object's private data. + // + + NtStatus = SampSetFixedAttributes( + SampDefinedDomains[SampTransactionDomainIndex]. + Context, + &SampDefinedDomains[SampTransactionDomainIndex]. + CurrentFixed + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // Normally when we dereference the context, + // SampStoreObjectAttributes() is called to add the + // latest change to the RXACT. But that won't happen + // for the domain object's change since this is the + // commit code, so we have to flush it by hand here. + // + + NtStatus = SampStoreObjectAttributes( + SampDefinedDomains[SampTransactionDomainIndex].Context, + TRUE // Use the existing key handle + ); + } + } + } + + + // + // If we still have no errors, try to commit the whole mess + + if ( NT_SUCCESS(NtStatus)) + { + + // We have the following cases here + // 1. Transaction spans multiple Domains + // 2. Transaction is confined to a single domain in DS + // 3. Transaction is confined to a single Domain in Registry + + if (!SampTransactionWithinDomain) + { + // Case 1 + + + // + // Assert as this case should never occur. Transaction + // never spans multiple domains. Rollback in both DS and + // Registry. + // + + ASSERT(FALSE && "Illegal combination"); + + NtStatus = STATUS_UNSUCCESSFUL; + goto Rollback; + + } + else if (DsTransaction) + { + // case 2 , commit the DS transaction, Rollback the + // Registry Transaction + + NtStatus = RtlAbortRXact( SampRXactContext ); + ASSERT(NT_SUCCESS(NtStatus)); + + ASSERT(SampExistsDsTransaction); + + NtStatus = SampMaybeEndDsTransaction(FALSE); + + } + else + { + // case 3, commit the Registry Transaction + // No need to Abort DS transaction as it was never started + // due to lazy DS transaction begin mechanism in dslayer.c + + ASSERT(!SampExistsDsTransaction()); + + NtStatus = SampCommitChangesToRegistry(&AbortDone); + } + } + + // + // Always abort the transaction on failure + // + +Rollback: + + if ( !NT_SUCCESS(NtStatus) && !AbortDone) { + + // Ds transaction, if there is one, is implicitly aborted + // when the global lock is released if it hasn't been committed. + // Thus only need to explicitly roll back registry transactions. + + NtStatus = RtlAbortRXact( SampRXactContext ); + ASSERT(NT_SUCCESS(NtStatus)); + } + + return( NtStatus ); +} + + +NTSTATUS +SampCommitChangesToRegistry( + BOOLEAN * AbortDone + ) +/*++ + Description: + + Commits the changes to the Registry. + + Parameters + + AbortDone -- Indicates that an error ocurred and in + the process of error handling aborted the + transaction +--*/ +{ + NTSTATUS NtStatus = STATUS_SUCCESS; + NTSTATUS IgnoreStatus = STATUS_SUCCESS; + + + if ( ( !FlushImmediately ) && ( !FlushThreadCreated ) ) + { + + HANDLE Thread; + DWORD Ignore; + + // + // If we can't create the flush thread, ignore error and + // just flush by hand below. + // + + Thread = CreateThread( + NULL, + 0L, + (LPTHREAD_START_ROUTINE)SampFlushThread, + NULL, + 0L, + &Ignore + ); + + if ( Thread != NULL ) + { + + FlushThreadCreated = TRUE; + VerbosePrint(("Flush thread created, handle = 0x%lx\n", Thread)); + CloseHandle(Thread); + } + } + + NtStatus = RtlApplyRXactNoFlush( SampRXactContext ); + +#if SAMP_DIAGNOSTICS + if (!NT_SUCCESS(NtStatus)) + { + SampDiagPrint( DISPLAY_STORAGE_FAIL, + ("SAM: Failed to apply RXact without flush (0x%lx)\n", + NtStatus) ); + IF_SAMP_GLOBAL( BREAK_ON_STORAGE_FAIL ) + { + ASSERT(NT_SUCCESS(NtStatus)); + + } + } +#endif //SAMP_DIAGNOSTICS + + + if ( NT_SUCCESS(NtStatus) ) + { + + if ( ( FlushImmediately ) || ( !FlushThreadCreated ) ) + { + + NtStatus = NtFlushKey( SampKey ); + +#if SAMP_DIAGNOSTICS + if (!NT_SUCCESS(NtStatus)) + { + SampDiagPrint( DISPLAY_STORAGE_FAIL, + ("SAM: Failed to flush RXact (0x%lx)\n", + NtStatus) ); + IF_SAMP_GLOBAL( BREAK_ON_STORAGE_FAIL ) + { + ASSERT(NT_SUCCESS(NtStatus)); + } + } +#endif //SAMP_DIAGNOSTICS + + if ( NT_SUCCESS( NtStatus ) ) + { + FlushImmediately = FALSE; + LastUnflushedChange = SampHasNeverTime; + } + + } + else + { + NtQuerySystemTime( &LastUnflushedChange ); + } + + + // + // Commit successful, set our unmodified to now be the current... + // + + if (NT_SUCCESS(NtStatus)) + { + if (SampTransactionWithinDomain == TRUE) + { + SampDefinedDomains[SampTransactionDomainIndex].UnmodifiedFixed = + SampDefinedDomains[SampTransactionDomainIndex].CurrentFixed; + } + } + + } + else + { + + KdPrint(("SAM: Failed to commit changes to registry, status = 0x%lx\n", NtStatus)); + KdPrint(("SAM: Restoring database to earlier consistent state\n")); + + // + // Add an entry to the event log + // + + SampWriteEventLog( + EVENTLOG_ERROR_TYPE, + 0, // Category + SAMMSG_COMMIT_FAILED, + NULL, // User Sid + 0, // Num strings + sizeof(NTSTATUS), // Data size + NULL, // String array + (PVOID)&NtStatus // Data + ); + + // + // The Rxact commital failed. We don't know how many registry + // writes were done for this transaction. We can't guarantee + // to successfully back them out anyway so all we can do is + // back out all changes since the last flush. When this is done + // we'll be back to a consistent database state although recent + // apis that were reported as succeeding will be 'undone'. + // + + IgnoreStatus = SampRefreshRegistry(); + + if (!NT_SUCCESS(IgnoreStatus)) + { + + // + // This is very serious. We failed to revert to a previous + // database state and we can't proceed. + // Shutdown SAM operations. + // + + SampServiceState = SampServiceTerminating; + + KdPrint(("SAM: Failed to refresh registry, SAM has shutdown\n")); + + // + // Add an entry to the event log + // + + SampWriteEventLog( + EVENTLOG_ERROR_TYPE, + 0, // Category + SAMMSG_REFRESH_FAILED, + NULL, // User Sid + 0, // Num strings + sizeof(NTSTATUS), // Data size + NULL, // String array + (PVOID)&IgnoreStatus // Data + ); + + } + + + // + // Now all open contexts are invalid (contain invalid registry + // handles). The in memory registry handles have been + // re-opened so any new contexts should work ok. + // + + + // + // All unflushed changes have just been erased. + // There is nothing to flush + // + // If the refresh failed it is important to prevent any further + // registry flushes until the system is rebooted + // + + FlushImmediately = FALSE; + LastUnflushedChange = SampHasNeverTime; + + // + // The refresh effectively aborted the transaction + // + *AbortDone = TRUE; + + } + + return NtStatus; +} + + + +NTSTATUS +SampRefreshRegistry( + VOID + ) + +/*++ + +Routine Description: + + This routine backs out all unflushed changes in the registry. + This operation invalidates any open handles to the SAM hive. + Global handles that we keep around are closed and re-opened by + this routine. The net result of this call will be that the database + is taken back to a previous consistent state. All open SAM contexts + are invalidated since they have invalid registry handles in them. + +Arguments: + + STATUS_SUCCESS : Operation completed successfully + + Failure returns: We are in deep trouble. Normal operations can + not be resumed. SAM should be shutdown. + +Return Value: + + None + +--*/ +{ + NTSTATUS NtStatus; + NTSTATUS IgnoreStatus; + HANDLE HiveKey; + BOOLEAN WasEnabled; + OBJECT_ATTRIBUTES ObjectAttributes; + UNICODE_STRING String; + ULONG i; + + SAMTRACE("SampRefreshRegistry"); + + // + // Get a key handle to the root of the SAM hive + // + +#ifdef USER_MODE_SAM + + RtlInitUnicodeString( &String, L"\\Registry\\Machine\\Software\\SAM" ); +#else + + RtlInitUnicodeString( &String, L"\\Registry\\Machine\\SAM" ); +#endif + + InitializeObjectAttributes( + &ObjectAttributes, + &String, + OBJ_CASE_INSENSITIVE, + 0, + NULL + ); + + SampDumpNtOpenKey((KEY_QUERY_VALUE), &ObjectAttributes, 0); + + NtStatus = RtlpNtOpenKey( + &HiveKey, + KEY_QUERY_VALUE, + &ObjectAttributes, + 0 + ); + + if (!NT_SUCCESS(NtStatus)) { + KdPrint(("SAM: Failed to open SAM hive root key for refresh, status = 0x%lx\n", NtStatus)); + return(NtStatus); + } + + + // + // Enable restore privilege in preparation for the refresh + // + + NtStatus = RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, TRUE, FALSE, &WasEnabled); + if (!NT_SUCCESS(NtStatus)) { + KdPrint(("SAM: Failed to enable restore privilege to refresh registry, status = 0x%lx\n", NtStatus)); + + IgnoreStatus = NtClose(HiveKey); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + return(NtStatus); + } + + + // + // Refresh the SAM hive + // This should not fail unless there is volatile storage in the + // hive or we don't have TCB privilege + // + + + NtStatus = NtRestoreKey(HiveKey, NULL, REG_REFRESH_HIVE); + + + IgnoreStatus = RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, WasEnabled, FALSE, &WasEnabled); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + IgnoreStatus = NtClose(HiveKey); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + + if (!NT_SUCCESS(NtStatus)) { + KdPrint(("SAM: Failed to refresh registry, status = 0x%lx\n", NtStatus)); + return(NtStatus); + } + + + + + // + // Now close the registry handles we keep in memory at all times + // This effectively closes all server and domain context keys + // since they are shared. + // + + NtStatus = NtClose(SampKey); + ASSERT(NT_SUCCESS(NtStatus)); + SampKey = INVALID_HANDLE_VALUE; + + for (i = 0; i<SampDefinedDomainsCount; i++ ) { + NtStatus = NtClose(SampDefinedDomains[i].Context->RootKey); + ASSERT(NT_SUCCESS(NtStatus)); + SampDefinedDomains[i].Context->RootKey = INVALID_HANDLE_VALUE; + } + + // + // Mark all domain and server context handles as invalid since they've + // now been closed + // + + SampInvalidateContextListKeys(&SampContextListHead, FALSE); + + // + // Re-open the SAM root key + // + + RtlInitUnicodeString( &String, L"\\Registry\\Machine\\Security\\SAM" ); + + InitializeObjectAttributes( + &ObjectAttributes, + &String, + OBJ_CASE_INSENSITIVE, + 0, + NULL + ); + + SampDumpNtOpenKey((KEY_READ | KEY_WRITE), &ObjectAttributes, 0); + + NtStatus = RtlpNtOpenKey( + &SampKey, + (KEY_READ | KEY_WRITE), + &ObjectAttributes, + 0 + ); + + if (!NT_SUCCESS(NtStatus)) { + KdPrint(("SAM: Failed to re-open SAM root key after registry refresh, status = 0x%lx\n", NtStatus)); + ASSERT(FALSE); + return(NtStatus); + } + + // + // Re-initialize the in-memory domain contexts + // Each domain will re-initialize it's open user/group/alias contexts + // + + for (i = 0; i<SampDefinedDomainsCount; i++ ) { + + NtStatus = SampReInitializeSingleDomain(i); + + if (!NT_SUCCESS(NtStatus)) { + KdPrint(("SAM: Failed to re-initialize domain %d context after registry refresh, status = 0x%lx\n", i, NtStatus)); + return(NtStatus); + } + } + + // + // Cleanup the current transcation context + // (It would be nice if there were a RtlDeleteRXactContext()) + // + // Note we don't have to close the rootregistrykey in the + // xact context since it was SampKey which we've already closed. + // + + NtStatus = RtlAbortRXact( SampRXactContext ); + ASSERT(NT_SUCCESS(NtStatus)); + + NtStatus = NtClose(SampRXactContext->RXactKey); + ASSERT(NT_SUCCESS(NtStatus)); + + // + // Re-initialize the transaction context. + // We don't expect there to be a partially commited transaction + // since we're reverting to a previously consistent and committed + // database. + // + + NtStatus = RtlInitializeRXact( SampKey, FALSE, &SampRXactContext ); + if (!NT_SUCCESS(NtStatus)) { + KdPrint(("SAM: Failed to re-initialize rxact context registry refresh, status = 0x%lx\n", NtStatus)); + return(NtStatus); + } + + ASSERT(NtStatus != STATUS_UNKNOWN_REVISION); + ASSERT(NtStatus != STATUS_RXACT_STATE_CREATED); + ASSERT(NtStatus != STATUS_RXACT_COMMIT_NECESSARY); + ASSERT(NtStatus != STATUS_RXACT_INVALID_STATE); + + return(STATUS_SUCCESS); +} + + + +NTSTATUS +SampReleaseWriteLock( + IN BOOLEAN Commit + ) + +/*++ + +Routine Description: + + This routine releases exclusive access to the SAM data structures and + backing store. + + If any changes were made to the backstore while exclusive access + was held, then this service commits those changes. Otherwise, the + transaction started when exclusive access was obtained is rolled back. + + If the operation was within a domain (which would have been indicated + via the SampSetTransactionDomain() api), then the CurrentFixed field for + that domain is added to the transaction before the transaction is + committed. + + NOTE: Write operations within a domain do not have to worry about + updating the modified count for that domain. This routine + will automatically increment the ModifiedCount for a domain + when a commit is requested within that domain. + + + +Arguments: + + Commit - A boolean value indicating whether modifications need to be + committed in the backing store. A value of TRUE indicates the + transaction should be committed. A value of FALSE indicates the + transaction should be aborted (rolled-back). + +Return Value: + + STATUS_SUCCESS - Indicates the write lock was released and the transaction + was successfully commited or rolled back. + + Other values may be returned as a result of failure to commit or + roll-back the transaction. These include any values returned by + RtlApplyRXact() or RtlAbortRXact(). In the case of a commit, it + may also represent errors returned by RtlAddActionToRXact(). + + + + +--*/ +{ + NTSTATUS NtStatus; + + SAMTRACE("SampReleaseWriteLock"); + + // + // Commit or rollback the transaction based upon the Commit parameter... + // + + if (Commit == TRUE) { + + NtStatus = SampCommitChanges(); + + } else { + + // Rollback in both DS and registry + NtStatus = SampMaybeEndDsTransaction(TRUE); + ASSERT(NT_SUCCESS(NtStatus)); + + NtStatus = RtlAbortRXact( SampRXactContext ); + ASSERT(NT_SUCCESS(NtStatus)); + } + + SampTransactionWithinDomain = FALSE; + + + // + // Allow LSA a chance to perform an integrity check + // + + LsaIHealthCheck( LsaIHealthSamAboutToFree ); + + + // + // And free the lock... + // + + (VOID)RtlReleaseResource( &SampLock ); + + return(NtStatus); +} + + + +NTSTATUS +SampCommitAndRetainWriteLock( + VOID + ) + +/*++ + +Routine Description: + + This routine attempts to commit all changes made so far. The write-lock + is held for the duration of the commit and is retained by the caller upon + return. + + The transaction domain is left intact as well. + + NOTE: Write operations within a domain do not have to worry about + updating the modified count for that domain. This routine + will automatically increment the ModifiedCount for a domain + when a commit is requested within that domain. + + + +Arguments: + + None. + +Return Value: + + STATUS_SUCCESS - Indicates the transaction was successfully commited. + + + Other values may be returned as a result of failure to commit or + roll-back the transaction. These include any values returned by + RtlApplyRXact() or RtlAddActionToRXact(). + + + + +--*/ + +{ + NTSTATUS NtStatus; + NTSTATUS TempStatus; + + SAMTRACE("SampCommitAndRetainWriteLock"); + + NtStatus = SampCommitChanges(); + + // + // Start another transaction, since we're retaining the write lock. + // Note we do this even if the commit failed so that cleanup code + // won't get confused by the lack of a transaction. This is true + // for the registry transaction but not for DS transactions. In + // the DS case, once you commit, the transaction and threads state + // are gone for good. + // + + TempStatus = RtlStartRXact( SampRXactContext ); + ASSERT(NT_SUCCESS(TempStatus)); + + // + // Return the worst status + // + + if (NT_SUCCESS(NtStatus)) { + NtStatus = TempStatus; + } + + return(NtStatus); +} + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Unicode registry key manipulation services // +// // +/////////////////////////////////////////////////////////////////////////////// + + + +NTSTATUS +SampRetrieveStringFromRegistry( + IN HANDLE ParentKey, + IN PUNICODE_STRING SubKeyName, + OUT PUNICODE_STRING Body + ) + +/*++ + +Routine Description: + + This routine retrieves a unicode string buffer from the specified registry + sub-key and sets the output parameter "Body" to be that unicode string. + + If the specified sub-key does not exist, then a null string will be + returned. + + The string buffer is returned in a block of memory which the caller is + responsible for deallocating (using MIDL_user_free). + + + +Arguments: + + ParentKey - Key to the parent registry key of the registry key + containing the unicode string. For example, to retrieve + the unicode string for a key called ALPHA\BETA\GAMMA, this + is the key to ALPHA\BETA. + + SubKeyName - The name of the sub-key whose value contains + a unicode string to retrieve. This field should not begin with + a back-slash (\). For example, to retrieve the unicode string + for a key called ALPHA\BETA\GAMMA, the name specified by this + field would be "BETA". + + Body - The address of a UNICODE_STRING whose fields are to be filled + in with the information retrieved from the sub-key. The Buffer + field of this argument will be set to point to an allocated buffer + containing the unicode string characters. + + +Return Value: + + + STATUS_SUCCESS - The string was retrieved successfully. + + STATUS_INSUFFICIENT_RESOURCES - Memory could not be allocated for the + string to be returned in. + + Other errors as may be returned by: + + NtOpenKey() + NtQueryInformationKey() + + + +--*/ +{ + + NTSTATUS NtStatus, IgnoreStatus; + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE SubKeyHandle; + ULONG IgnoreKeyType, KeyValueLength; + LARGE_INTEGER IgnoreLastWriteTime; + + SAMTRACE("SampRetrieveStringFromRegistry"); + + + ASSERT(Body != NULL); + + // + // Get a handle to the sub-key ... + // + + InitializeObjectAttributes( + &ObjectAttributes, + SubKeyName, + OBJ_CASE_INSENSITIVE, + ParentKey, + NULL + ); + + SampDumpNtOpenKey((KEY_READ), &ObjectAttributes, 0); + + NtStatus = RtlpNtOpenKey( + &SubKeyHandle, + (KEY_READ), + &ObjectAttributes, + 0 + ); + + if (!NT_SUCCESS(NtStatus)) { + + // + // Couldn't open the sub-key + // If it is OBJECT_NAME_NOT_FOUND, then build a null string + // to return. Otherwise, return nothing. + // + + if (NtStatus == STATUS_OBJECT_NAME_NOT_FOUND) { + + Body->Buffer = MIDL_user_allocate( sizeof(UNICODE_NULL) ); + if (Body->Buffer == NULL) { + return(STATUS_INSUFFICIENT_RESOURCES); + } + Body->Length = 0; + Body->MaximumLength = sizeof(UNICODE_NULL); + Body->Buffer[0] = 0; + + return( STATUS_SUCCESS ); + + } else { + return(NtStatus); + } + + } + + + + // + // Get the length of the unicode string + // We expect one of two things to come back here: + // + // 1) STATUS_BUFFER_OVERFLOW - In which case the KeyValueLength + // contains the length of the string. + // + // 2) STATUS_SUCCESS - In which case there is no string out there + // and we need to build an empty string for return. + // + + KeyValueLength = 0; + NtStatus = RtlpNtQueryValueKey( + SubKeyHandle, + &IgnoreKeyType, + NULL, + &KeyValueLength, + &IgnoreLastWriteTime + ); + + SampDumpRtlpNtQueryValueKey(&IgnoreKeyType, + NULL, + &KeyValueLength, + &IgnoreLastWriteTime); + + if (NT_SUCCESS(NtStatus)) { + + KeyValueLength = 0; + Body->Buffer = MIDL_user_allocate( KeyValueLength + sizeof(WCHAR) ); // Length of null string + if (Body->Buffer == NULL) { + IgnoreStatus = NtClose( SubKeyHandle ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + return(STATUS_INSUFFICIENT_RESOURCES); + } + Body->Buffer[0] = 0; + + } else { + + if (NtStatus == STATUS_BUFFER_OVERFLOW) { + Body->Buffer = MIDL_user_allocate( KeyValueLength + sizeof(WCHAR) ); + if (Body->Buffer == NULL) { + IgnoreStatus = NtClose( SubKeyHandle ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + return(STATUS_INSUFFICIENT_RESOURCES); + } + NtStatus = RtlpNtQueryValueKey( + SubKeyHandle, + &IgnoreKeyType, + Body->Buffer, + &KeyValueLength, + &IgnoreLastWriteTime + ); + + SampDumpRtlpNtQueryValueKey(&IgnoreKeyType, + Body->Buffer, + &KeyValueLength, + &IgnoreLastWriteTime); + + } else { + IgnoreStatus = NtClose( SubKeyHandle ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + return(NtStatus); + } + } + + if (!NT_SUCCESS(NtStatus)) { + MIDL_user_free( Body->Buffer ); + IgnoreStatus = NtClose( SubKeyHandle ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + return(NtStatus); + } + + Body->Length = (USHORT)(KeyValueLength); + Body->MaximumLength = (USHORT)(KeyValueLength) + (USHORT)sizeof(WCHAR); + UnicodeTerminate(Body); + + + IgnoreStatus = NtClose( SubKeyHandle ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + return( STATUS_SUCCESS ); + + +} + + +NTSTATUS +SampPutStringToRegistry( + IN BOOLEAN RelativeToDomain, + IN PUNICODE_STRING SubKeyName, + IN PUNICODE_STRING Body + ) + +/*++ + +Routine Description: + + This routine puts a unicode string into the specified registry + sub-key. + + If the specified sub-key does not exist, then it is created. + + NOTE: The string is assigned via the RXACT mechanism. Therefore, + it won't actually reside in the registry key until a commit + is performed. + + + + +Arguments: + + RelativeToDomain - This boolean indicates whether or not the name + of the sub-key provide via the SubKeyName parameter is relative + to the current domain or to the top of the SAM registry tree. + If the name is relative to the current domain, then this value + is set to TRUE. Otherwise this value is set to FALSE. + + SubKeyName - The name of the sub-key to be assigned the unicode string. + This field should not begin with a back-slash (\). For example, + to put a unicode string into a key called ALPHA\BETA\GAMMA, the + name specified by this field would be "BETA". + + Body - The address of a UNICODE_STRING to be placed in the registry. + + +Return Value: + + + STATUS_SUCCESS - The string was added to the RXACT transaction + successfully. + + STATUS_INSUFFICIENT_RESOURCES - There was not enough heap memory + or other limited resource available to fullfil the request. + + Other errors as may be returned by: + + RtlAddActionToRXact() + + + +--*/ +{ + + NTSTATUS NtStatus; + UNICODE_STRING KeyName; + + SAMTRACE("SampPutStringToRegsitry"); + + + // + // Need to build up the name of the key from the root of the RXACT + // registry key. That is the root of the SAM registry database + // in our case. If RelativeToDomain is FALSE, then the name passed + // is already relative to the SAM registry database root. + // + + if (RelativeToDomain == TRUE) { + + + NtStatus = SampBuildDomainSubKeyName( + &KeyName, + SubKeyName + ); + if (!NT_SUCCESS(NtStatus)) { + SampFreeUnicodeString( &KeyName ); + return(NtStatus); + } + + + } else { + KeyName = (*SubKeyName); + } + + + NtStatus = RtlAddActionToRXact( + SampRXactContext, + RtlRXactOperationSetValue, + &KeyName, + 0, // No KeyValueType + Body->Buffer, + Body->Length + ); + + + + // + // free the KeyName buffer if necessary + // + + if (RelativeToDomain) { + SampFreeUnicodeString( &KeyName ); + } + + + return( STATUS_SUCCESS ); + + +} + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Unicode String related services - These use MIDL_user_allocate and // +// MIDL_user_free so that the resultant strings can be given to the // +// RPC runtime. // +// // +/////////////////////////////////////////////////////////////////////////////// + + +NTSTATUS +SampInitUnicodeString( + IN OUT PUNICODE_STRING String, + IN USHORT MaximumLength + ) + +/*++ + +Routine Description: + + This routine initializes a unicode string to have zero length and + no initial buffer. + + + All allocation for this string will be done using MIDL_user_allocate. + +Arguments: + + String - The address of a unicode string to initialize. + + MaximumLength - The maximum length (in bytes) the string will need + to grow to. The buffer associated with the string is allocated + to be this size. Don't forget to allow 2 bytes for null termination. + + +Return Value: + + + STATUS_SUCCESS - Successful completion. + +--*/ + +{ + SAMTRACE("SampInitUnicodeString"); + + String->Length = 0; + String->MaximumLength = MaximumLength; + + String->Buffer = MIDL_user_allocate(MaximumLength); + + if (String->Buffer != NULL) { + String->Buffer[0] = 0; + + return(STATUS_SUCCESS); + } else { + return(STATUS_INSUFFICIENT_RESOURCES); + } +} + + + +NTSTATUS +SampAppendUnicodeString( + IN OUT PUNICODE_STRING Target, + IN PUNICODE_STRING StringToAdd + ) + +/*++ + +Routine Description: + + This routine appends the string pointed to by StringToAdd to the + string pointed to by Target. The contents of Target are replaced + by the result. + + + All allocation for this string will be done using MIDL_user_allocate. + Any deallocations will be done using MIDL_user_free. + +Arguments: + + Target - The address of a unicode string to initialize to be appended to. + + StringToAdd - The address of a unicode string to be added to the + end of Target. + + +Return Value: + + + STATUS_SUCCESS - Successful completion. + + STATUS_INSUFFICIENT_RESOURCES - There was not sufficient heap to fullfil + the requested operation. + + +--*/ +{ + + USHORT TotalLength; + PWSTR NewBuffer; + + SAMTRACE("SampAppendUnicodeString"); + + + TotalLength = Target->Length + StringToAdd->Length + (USHORT)(sizeof(UNICODE_NULL)); + + // + // If there isn't room in the target to append the new string, + // allocate a buffer that is large enough and move the current + // target into it. + // + + if (TotalLength > Target->MaximumLength) { + + NewBuffer = MIDL_user_allocate( (ULONG)TotalLength ); + if (NewBuffer == NULL) { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + RtlCopyMemory( NewBuffer, Target->Buffer, (ULONG)(Target->Length) ); + + MIDL_user_free( Target->Buffer ); + Target->Buffer = NewBuffer; + Target->MaximumLength = TotalLength; + + } else { + NewBuffer = Target->Buffer; + } + + + // + // There's now room in the target to append the string. + // + + (PCHAR)NewBuffer += Target->Length; + + RtlCopyMemory( NewBuffer, StringToAdd->Buffer, (ULONG)(StringToAdd->Length) ); + + + Target->Length = TotalLength - (USHORT)(sizeof(UNICODE_NULL)); + + + // + // Null terminate the resultant string + // + + UnicodeTerminate(Target); + + return(STATUS_SUCCESS); + +} + + + +VOID +SampFreeUnicodeString( + IN PUNICODE_STRING String + ) + +/*++ + +Routine Description: + + This routine frees the buffer associated with a unicode string + (using MIDL_user_free()). + + +Arguments: + + Target - The address of a unicode string to free. + + +Return Value: + + None. + +--*/ +{ + + SAMTRACE("SampFreeUnicodeString"); + + if (String->Buffer != NULL) { + MIDL_user_free( String->Buffer ); + } + + return; +} + + +VOID +SampFreeOemString( + IN POEM_STRING String + ) + +/*++ + +Routine Description: + + This routine frees the buffer associated with an OEM string + (using MIDL_user_free()). + + + +Arguments: + + Target - The address of an OEM string to free. + + +Return Value: + + None. + +--*/ +{ + + SAMTRACE("SampFreeOemString"); + + if (String->Buffer != NULL) { + MIDL_user_free( String->Buffer ); + } + + return; +} + + +NTSTATUS +SampBuildDomainSubKeyName( + OUT PUNICODE_STRING KeyName, + IN PUNICODE_STRING SubKeyName OPTIONAL + ) + +/*++ + +Routine Description: + + This routine builds a unicode string name of the string passed + via the SubKeyName argument. The resultant name is relative to + the root of the SAM root registry key. + + Note: THIS ROUTINE REFERENCES THE CURRENT TRANSACTION DOMAIN + (ESTABLISHED USING SampSetTransactioDomain()). THIS + SERVICE MAY ONLY BE CALLED AFTER SampSetTransactionDomain() + AND BEFORE SampReleaseWriteLock(). + + + The name built up is comprized of three components: + + 1) The constant named domain parent key name ("DOMAINS"). + + 2) A backslash + + 3) The name of the current transaction domain. + + (optionally) + + 4) A backslash + + 5) The name of the domain's sub-key (specified by the SubKeyName + argument). + + + For example, if the current domain is called "MY_DOMAIN", then + the relative name of the sub-key named "FRAMITZ" is : + + "DOMAINS\MY_DOMAIN\FRAMITZ" + + + All allocation for this string will be done using MIDL_user_allocate. + Any deallocations will be done using MIDL_user_free. + + + +Arguments: + + KeyName - The address of a unicode string whose buffer is to be filled + in with the full name of the registry key. If successfully created, + this string must be released with SampFreeUnicodeString() when no + longer needed. + + + SubKeyName - (optional) The name of the domain sub-key. If this parameter + is not provided, then only the domain's name is produced. + This string is not modified. + + + + +Return Value: + + + + + +--*/ +{ + NTSTATUS NtStatus; + USHORT TotalLength, SubKeyNameLength; + + SAMTRACE("SampBuildDomainSubKeyName"); + + + ASSERT(SampTransactionWithinDomain == TRUE); + + + // + // Initialize a string large enough to hold the name + // + + if (ARGUMENT_PRESENT(SubKeyName)) { + SubKeyNameLength = SampBackSlash.Length + SubKeyName->Length; + } else { + SubKeyNameLength = 0; + } + + TotalLength = SampNameDomains.Length + + SampBackSlash.Length + + SampDefinedDomains[SampTransactionDomainIndex].InternalName.Length + + SubKeyNameLength + + (USHORT)(sizeof(UNICODE_NULL)); // for null terminator + + NtStatus = SampInitUnicodeString( KeyName, TotalLength ); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + // + // "DOMAINS" + // + + NtStatus = SampAppendUnicodeString( KeyName, &SampNameDomains); + if (!NT_SUCCESS(NtStatus)) { + SampFreeUnicodeString( KeyName ); + return(NtStatus); + } + + // + // "DOMAINS\" + // + + NtStatus = SampAppendUnicodeString( KeyName, &SampBackSlash ); + if (!NT_SUCCESS(NtStatus)) { + SampFreeUnicodeString( KeyName ); + return(NtStatus); + } + + + // + // "DOMAINS\(domain name)" + // + + NtStatus = SampAppendUnicodeString( + KeyName, + &SampDefinedDomains[SampTransactionDomainIndex].InternalName + ); + if (!NT_SUCCESS(NtStatus)) { + SampFreeUnicodeString( KeyName ); + return(NtStatus); + } + + + if (ARGUMENT_PRESENT(SubKeyName)) { + + // + // "DOMAINS\(domain name)\" + // + + + + NtStatus = SampAppendUnicodeString( KeyName, &SampBackSlash ); + if (!NT_SUCCESS(NtStatus)) { + SampFreeUnicodeString( KeyName ); + return(NtStatus); + } + + + // + // "DOMAINS\(domain name)\(sub key name)" + // + + NtStatus = SampAppendUnicodeString( KeyName, SubKeyName ); + if (!NT_SUCCESS(NtStatus)) { + SampFreeUnicodeString( KeyName ); + return(NtStatus); + } + + } + return(NtStatus); + +} + + +NTSTATUS +SampBuildAccountKeyName( + IN SAMP_OBJECT_TYPE ObjectType, + OUT PUNICODE_STRING AccountKeyName, + IN PUNICODE_STRING AccountName OPTIONAL + ) + +/*++ + +Routine Description: + + This routine builds the name of either a group or user registry key. + The name produced is relative to the SAM root and will be the name of + key whose name is the name of the account. + + + Note: THIS ROUTINE REFERENCES THE CURRENT TRANSACTION DOMAIN + (ESTABLISHED USING SampSetTransactioDomain()). THIS + SERVICE MAY ONLY BE CALLED AFTER SampSetTransactionDomain() + AND BEFORE SampReleaseWriteLock(). + + + The name built up is comprized of the following components: + + 1) The constant named domain parent key name ("DOMAINS"). + + 2) A backslash + + 3) The name of the current transaction domain. + + 4) A backslash + + 5) The constant name of the group or user registry key + ("GROUPS" or "USERS"). + + 6) A backslash + + 7) The constant name of the registry key containing the + account names ("NAMES"). + + and, if the AccountName is specified, + + 8) A backslash + + 9) The account name specified by the AccountName argument. + + + For example, given a AccountName of "XYZ_GROUP" and the current domain + is "ALPHA_DOMAIN", this would yield a resultant AccountKeyName of + "DOMAINS\ALPHA_DOMAIN\GROUPS\NAMES\XYZ_GROUP". + + + + All allocation for this string will be done using MIDL_user_allocate. + Any deallocations will be done using MIDL_user_free. + + + +Arguments: + + ObjectType - Indicates whether the account is a user or group account. + + AccountKeyName - The address of a unicode string whose buffer is to be + filled in with the full name of the registry key. If successfully + created, this string must be released with SampFreeUnicodeString() + when no longer needed. + + + AccountName - The name of the account. This string is not + modified. + + + + +Return Value: + + + STATUS_SUCCESS - The name has been built. + + STATUS_INVALID_ACCOUNT_NAME - The name specified is not legitimate. + + + + +--*/ +{ + NTSTATUS NtStatus; + USHORT TotalLength, AccountNameLength; + PUNICODE_STRING AccountTypeKeyName; + PUNICODE_STRING NamesSubKeyName; + + SAMTRACE("SampBuildAccountKeyName"); + + + ASSERT(SampTransactionWithinDomain == TRUE); + ASSERT( (ObjectType == SampGroupObjectType) || + (ObjectType == SampAliasObjectType) || + (ObjectType == SampUserObjectType) ); + + + // + // If an account name was provided, then it must meet certain + // criteria. + // + + if (ARGUMENT_PRESENT(AccountName)) { + if ( + // + // Length must be legitimate + // + + (AccountName->Length == 0) || + (AccountName->Length > AccountName->MaximumLength) || + + // + // Buffer pointer is available + // + + (AccountName->Buffer == NULL) + + + ) { + return(STATUS_INVALID_ACCOUNT_NAME); + } + } + + + + + switch (ObjectType) { + case SampGroupObjectType: + AccountTypeKeyName = &SampNameDomainGroups; + NamesSubKeyName = &SampNameDomainGroupsNames; + break; + case SampAliasObjectType: + AccountTypeKeyName = &SampNameDomainAliases; + NamesSubKeyName = &SampNameDomainAliasesNames; + break; + case SampUserObjectType: + AccountTypeKeyName = &SampNameDomainUsers; + NamesSubKeyName = &SampNameDomainUsersNames; + break; + } + + + + + // + // Allocate a buffer large enough to hold the entire name. + // Only count the account name if it is passed. + // + + AccountNameLength = 0; + if (ARGUMENT_PRESENT(AccountName)) { + AccountNameLength = AccountName->Length + SampBackSlash.Length; + } + + TotalLength = SampNameDomains.Length + + SampBackSlash.Length + + SampDefinedDomains[SampTransactionDomainIndex].InternalName.Length + + SampBackSlash.Length + + AccountTypeKeyName->Length + + SampBackSlash.Length + + NamesSubKeyName->Length + + AccountNameLength + + (USHORT)(sizeof(UNICODE_NULL)); // for null terminator + + NtStatus = SampInitUnicodeString( AccountKeyName, TotalLength ); + if (NT_SUCCESS(NtStatus)) { + + // + // "DOMAINS" + // + + NtStatus = SampAppendUnicodeString( AccountKeyName, &SampNameDomains); + if (NT_SUCCESS(NtStatus)) { + + // + // "DOMAINS\" + // + + NtStatus = SampAppendUnicodeString( AccountKeyName, &SampBackSlash ); + if (NT_SUCCESS(NtStatus)) { + + // + // "DOMAINS\(domain name)" + // + + + NtStatus = SampAppendUnicodeString( + AccountKeyName, + &SampDefinedDomains[SampTransactionDomainIndex].InternalName + ); + if (NT_SUCCESS(NtStatus)) { + + // + // "DOMAINS\(domain name)\" + // + + NtStatus = SampAppendUnicodeString( AccountKeyName, &SampBackSlash ); + if (NT_SUCCESS(NtStatus)) { + + // + // "DOMAINS\(domain name)\GROUPS" + // or + // "DOMAINS\(domain name)\USERS" + // + + NtStatus = SampAppendUnicodeString( AccountKeyName, AccountTypeKeyName ); + if (NT_SUCCESS(NtStatus)) { + + // + // "DOMAINS\(domain name)\GROUPS\" + // or + // "DOMAINS\(domain name)\USERS\" + // + + NtStatus = SampAppendUnicodeString( AccountKeyName, &SampBackSlash ); + if (NT_SUCCESS(NtStatus)) { + + // + // "DOMAINS\(domain name)\GROUPS\NAMES" + // or + // "DOMAINS\(domain name)\USERS\NAMES" + // + + NtStatus = SampAppendUnicodeString( AccountKeyName, NamesSubKeyName ); + if (NT_SUCCESS(NtStatus) && ARGUMENT_PRESENT(AccountName)) { + // + // "DOMAINS\(domain name)\GROUPS\NAMES\" + // or + // "DOMAINS\(domain name)\USERS\NAMES\" + // + + NtStatus = SampAppendUnicodeString( AccountKeyName, &SampBackSlash ); + if (NT_SUCCESS(NtStatus)) { + + // + // "DOMAINS\(domain name)\GROUPS\(account name)" + // or + // "DOMAINS\(domain name)\USERS\(account name)" + // + + NtStatus = SampAppendUnicodeString( AccountKeyName, AccountName ); + + } + } + } + } + } + } + } + } + + } + + + return(NtStatus); + +} + + + +NTSTATUS +SampBuildAccountSubKeyName( + IN SAMP_OBJECT_TYPE ObjectType, + OUT PUNICODE_STRING AccountKeyName, + IN ULONG AccountRid, + IN PUNICODE_STRING SubKeyName OPTIONAL + ) + +/*++ + +Routine Description: + + This routine builds the name of a key for one of the fields of either + a user or a group. + + The name produced is relative to the SAM root. + + + Note: THIS ROUTINE REFERENCES THE CURRENT TRANSACTION DOMAIN + (ESTABLISHED USING SampSetTransactioDomain()). THIS + SERVICE MAY ONLY BE CALLED AFTER SampSetTransactionDomain() + AND BEFORE SampReleaseWriteLock(). + + + The name built up is comprized of the following components: + + 1) The constant named domain parent key name ("DOMAINS"). + + 2) A backslash + + 3) The name of the current transaction domain. + + 4) A backslash + + 5) The constant name of the group or user registry key + ("Groups" or "Users"). + + 6) A unicode representation of the reltive ID of the account + + and if the optional SubKeyName is provided: + + 7) A backslash + + 8) the sub key's name. + 4) The account name specified by the AccountName argument. + + + For example, given a AccountRid of 3187, a SubKeyName of "AdminComment" + and the current domain is "ALPHA_DOMAIN", this would yield a resultant + AccountKeyName of: + + "DOMAINS\ALPHA_DOMAIN\GROUPS\00003187\AdminComment". + + + + All allocation for this string will be done using MIDL_user_allocate. + Any deallocations will be done using MIDL_user_free. + + + +Arguments: + + ObjectType - Indicates whether the account is a user or group account. + + AccountKeyName - The address of a unicode string whose buffer is to be + filled in with the full name of the registry key. If successfully + created, this string must be released with SampFreeUnicodeString() + when no longer needed. + + + AccountName - The name of the account. This string is not + modified. + +Return Value: + +--*/ + +{ + NTSTATUS NtStatus; + USHORT TotalLength, SubKeyNameLength; + PUNICODE_STRING AccountTypeKeyName; + UNICODE_STRING RidNameU; + + SAMTRACE("SampBuildAccountSubKeyName"); + + ASSERT(SampTransactionWithinDomain == TRUE); + ASSERT( (ObjectType == SampGroupObjectType) || + (ObjectType == SampAliasObjectType) || + (ObjectType == SampUserObjectType) ); + + + switch (ObjectType) { + case SampGroupObjectType: + AccountTypeKeyName = &SampNameDomainGroups; + break; + case SampAliasObjectType: + AccountTypeKeyName = &SampNameDomainAliases; + break; + case SampUserObjectType: + AccountTypeKeyName = &SampNameDomainUsers; + break; + } + + // + // Determine how much space will be needed in the resultant name + // buffer to allow for the sub-key-name. + // + + if (ARGUMENT_PRESENT(SubKeyName)) { + SubKeyNameLength = SubKeyName->Length + SampBackSlash.Length; + } else { + SubKeyNameLength = 0; + } + + // + // Convert the account Rid to Unicode. + // + + NtStatus = SampRtlConvertUlongToUnicodeString( + AccountRid, + 16, + 8, + TRUE, + &RidNameU + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // allocate a buffer large enough to hold the entire name + // + + TotalLength = SampNameDomains.Length + + SampBackSlash.Length + + SampDefinedDomains[SampTransactionDomainIndex].InternalName.Length + + SampBackSlash.Length + + AccountTypeKeyName->Length + + RidNameU.Length + + SubKeyNameLength + + (USHORT)(sizeof(UNICODE_NULL)); // for null terminator + + NtStatus = SampInitUnicodeString( AccountKeyName, TotalLength ); + if (NT_SUCCESS(NtStatus)) { + + + // + // "DOMAINS" + // + + NtStatus = SampAppendUnicodeString( AccountKeyName, &SampNameDomains); + if (NT_SUCCESS(NtStatus)) { + + // + // "DOMAINS\" + // + + NtStatus = SampAppendUnicodeString( AccountKeyName, &SampBackSlash ); + if (NT_SUCCESS(NtStatus)) { + + // + // "DOMAINS\(domain name)" + // + + + NtStatus = SampAppendUnicodeString( + AccountKeyName, + &SampDefinedDomains[SampTransactionDomainIndex].InternalName + ); + if (NT_SUCCESS(NtStatus)) { + + // + // "DOMAINS\(domain name)\" + // + + NtStatus = SampAppendUnicodeString( AccountKeyName, &SampBackSlash ); + if (NT_SUCCESS(NtStatus)) { + + // + // "DOMAINS\(domain name)\GROUPS" + // or + // "DOMAINS\(domain name)\USERS" + // + + NtStatus = SampAppendUnicodeString( AccountKeyName, AccountTypeKeyName ); + if (NT_SUCCESS(NtStatus)) { + + // + // "DOMAINS\(domain name)\GROUPS\" + // or + // "DOMAINS\(domain name)\USERS\" + // + + NtStatus = SampAppendUnicodeString( AccountKeyName, &SampBackSlash ); + if (NT_SUCCESS(NtStatus)) { + + // + // "DOMAINS\(domain name)\GROUPS\(rid)" + // or + // "DOMAINS\(domain name)\USERS\(rid)" + // + + NtStatus = SampAppendUnicodeString( AccountKeyName, &RidNameU ); + + if (NT_SUCCESS(NtStatus) && ARGUMENT_PRESENT(SubKeyName)) { + + // + // "DOMAINS\(domain name)\GROUPS\(rid)\" + // or + // "DOMAINS\(domain name)\USERS\(rid)\" + // + + NtStatus = SampAppendUnicodeString( AccountKeyName, &SampBackSlash ); + if (NT_SUCCESS(NtStatus)) { + + // + // "DOMAINS\(domain name)\GROUPS\(rid)\(sub-key-name)" + // or + // "DOMAINS\(domain name)\USERS\(rid)\(sub-key-name)" + // + + NtStatus = SampAppendUnicodeString( AccountKeyName, SubKeyName ); + } + } + } + } + } + } + } + } + + } + + MIDL_user_free(RidNameU.Buffer); + } + + return(NtStatus); +} + + + +NTSTATUS +SampBuildAliasMembersKeyName( + IN PSID AccountSid, + OUT PUNICODE_STRING DomainKeyName, + OUT PUNICODE_STRING AccountKeyName + ) + +/*++ + +Routine Description: + + This routine builds the name of a key for the alias membership for an + arbitrary account sid. Also produced is the name of the key for the + domain of the account. This is the account key name without the last + account rid component. + + The names produced is relative to the SAM root. + + + Note: THIS ROUTINE REFERENCES THE CURRENT TRANSACTION DOMAIN + (ESTABLISHED USING SampSetTransactioDomain()). THIS + SERVICE MAY ONLY BE CALLED AFTER SampSetTransactionDomain() + AND BEFORE SampReleaseWriteLock(). + + + The names built up are comprised of the following components: + + 1) The constant named domain parent key name ("DOMAINS"). + + 2) A backslash + + 3) The name of the current transaction domain. + + 4) A backslash + + 5) The constant name of the alias registry key ("Aliases"). + + 6) A backslash + + 7) The constant name of the alias members registry key ("Members"). + + 8) A backslash + + 9) A unicode representation of the SID of the account domain + + and for the AccountKeyName only + + 10) A backslash + + 11) A unicode representation of the RID of the account + + + For example, given a Account Sid of 1-2-3-3187 + and the current domain is "ALPHA_DOMAIN", + this would yield a resultant AcccountKeyName of: + + "DOMAINS\ALPHA_DOMAIN\ALIASES\MEMBERS\1-2-3\00003187". + + and a DomainKeyName of: + + "DOMAINS\ALPHA_DOMAIN\ALIASES\MEMBERS\1-2-3". + + + + All allocation for these strings will be done using MIDL_user_allocate. + Any deallocations will be done using MIDL_user_free. + + + +Arguments: + + AccountSid - The account whose alias membership in the current domain + is to be determined. + + DomainKeyName - The address of a unicode string whose + buffer is to be filled in with the full name of the domain registry key. + If successfully created, this string must be released with + SampFreeUnicodeString() when no longer needed. + + AccountKeyName - The address of a unicode string whose + buffer is to be filled in with the full name of the account registry key. + If successfully created, this string must be released with + SampFreeUnicodeString() when no longer needed. + + + + +Return Value: + + STATUS_SUCCESS - the domain and account key names are valid. + + STATUS_INVALID_SID - the AccountSid is not valid. AccountSids must have + a sub-authority count > 0 + +--*/ + +{ + NTSTATUS NtStatus; + USHORT DomainTotalLength; + USHORT AccountTotalLength; + UNICODE_STRING DomainNameU, TempStringU; + UNICODE_STRING RidNameU; + PSID DomainSid = NULL; + ULONG AccountRid; + ULONG AccountSubAuthorities; + + SAMTRACE("SampBuildAliasMembersKeyName"); + + DomainNameU.Buffer = TempStringU.Buffer = RidNameU.Buffer = NULL; + + ASSERT(SampTransactionWithinDomain == TRUE); + + ASSERT(AccountSid != NULL); + ASSERT(DomainKeyName != NULL); + ASSERT(AccountKeyName != NULL); + + // + // Split the account sid into domain sid and account rid + // + + AccountSubAuthorities = (ULONG)*RtlSubAuthorityCountSid(AccountSid); + + // + // Check for at least one sub-authority + // + + if (AccountSubAuthorities < 1) { + + return (STATUS_INVALID_SID); + } + + // + // Allocate space for the domain sid + // + + DomainSid = MIDL_user_allocate(RtlLengthSid(AccountSid)); + + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + + if (DomainSid == NULL) { + + return(NtStatus); + } + + // + // Initialize the domain sid + // + + NtStatus = RtlCopySid(RtlLengthSid(AccountSid), DomainSid, AccountSid); + ASSERT(NT_SUCCESS(NtStatus)); + + *RtlSubAuthorityCountSid(DomainSid) = (UCHAR)(AccountSubAuthorities - 1); + + // + // Initialize the account rid + // + + AccountRid = *RtlSubAuthoritySid(AccountSid, AccountSubAuthorities - 1); + + // + // Convert the domain sid into a registry key name string + // + + NtStatus = RtlConvertSidToUnicodeString( &DomainNameU, DomainSid, TRUE); + + if (!NT_SUCCESS(NtStatus)) { + DomainNameU.Buffer = NULL; + goto BuildAliasMembersKeyNameError; + } + + // + // Convert the account rid into a registry key name string with + // leading zeros. + // + + NtStatus = SampRtlConvertUlongToUnicodeString( + AccountRid, + 16, + 8, + TRUE, + &RidNameU + ); + + if (!NT_SUCCESS(NtStatus)) { + + goto BuildAliasMembersKeyNameError; + } + + if (NT_SUCCESS(NtStatus)) { + + // + // allocate a buffer large enough to hold the entire name + // + + DomainTotalLength = + SampNameDomains.Length + + SampBackSlash.Length + + SampDefinedDomains[SampTransactionDomainIndex].InternalName.Length + + SampBackSlash.Length + + SampNameDomainAliases.Length + + SampBackSlash.Length + + SampNameDomainAliasesMembers.Length + + SampBackSlash.Length + + DomainNameU.Length + + (USHORT)(sizeof(UNICODE_NULL)); // for null terminator + + AccountTotalLength = DomainTotalLength + + SampBackSlash.Length + + RidNameU.Length; + + // + // First build the domain key name + // + + + NtStatus = SampInitUnicodeString( DomainKeyName, DomainTotalLength ); + + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampInitUnicodeString( AccountKeyName, AccountTotalLength ); + + if (!NT_SUCCESS(NtStatus)) { + + SampFreeUnicodeString(DomainKeyName); + + } else { + + // + // "DOMAINS" + // + + NtStatus = SampAppendUnicodeString( DomainKeyName, &SampNameDomains); + ASSERT(NT_SUCCESS(NtStatus)); + + + // + // "DOMAINS\" + // + + NtStatus = SampAppendUnicodeString( DomainKeyName, &SampBackSlash ); + ASSERT(NT_SUCCESS(NtStatus)); + + + // + // "DOMAINS\(domain name)" + // + + NtStatus = SampAppendUnicodeString( + DomainKeyName, + &SampDefinedDomains[SampTransactionDomainIndex].InternalName + ); + ASSERT(NT_SUCCESS(NtStatus)); + + + // + // "DOMAINS\(domain name)\" + // + + NtStatus = SampAppendUnicodeString( DomainKeyName, &SampBackSlash ); + ASSERT(NT_SUCCESS(NtStatus)); + + + // + // "DOMAINS\(domain name)\ALIASES" + // + + NtStatus = SampAppendUnicodeString( DomainKeyName, &SampNameDomainAliases); + ASSERT(NT_SUCCESS(NtStatus)); + + + // + // "DOMAINS\(domain name)\ALIASES\" + // + + NtStatus = SampAppendUnicodeString( DomainKeyName, &SampBackSlash ); + ASSERT(NT_SUCCESS(NtStatus)); + + + // + // "DOMAINS\(domain name)\ALIASES\MEMBERS" + // + + NtStatus = SampAppendUnicodeString( DomainKeyName, &SampNameDomainAliasesMembers); + ASSERT(NT_SUCCESS(NtStatus)); + + + // + // "DOMAINS\(domain name)\ALIASES\MEMBERS\" + // + + NtStatus = SampAppendUnicodeString( DomainKeyName, &SampBackSlash ); + ASSERT(NT_SUCCESS(NtStatus)); + + // + // "DOMAINS\(domain name)\ALIASES\MEMBERS\(DomainSid)" + // + + NtStatus = SampAppendUnicodeString( DomainKeyName, &DomainNameU ); + ASSERT(NT_SUCCESS(NtStatus)); + + // + // Now build the account name by copying the domain name + // and suffixing the account Rid + // + + RtlCopyUnicodeString(AccountKeyName, DomainKeyName); + ASSERT(AccountKeyName->Length = DomainKeyName->Length); + + // + // "DOMAINS\(domain name)\ALIASES\MEMBERS\(DomainSid)\" + // + + NtStatus = SampAppendUnicodeString( AccountKeyName, &SampBackSlash ); + ASSERT(NT_SUCCESS(NtStatus)); + + // + // "DOMAINS\(domain name)\ALIASES\MEMBERS\(DomainSid)\(AccountRid)" + // + + NtStatus = SampAppendUnicodeString( AccountKeyName, &RidNameU ); + ASSERT(NT_SUCCESS(NtStatus)); + } + } + + MIDL_user_free(RidNameU.Buffer); + } + +BuildAliasMembersKeyNameFinish: + + // + // If necessary, free memory allocated for the DomainSid. + // + + if (DomainSid != NULL) { + + MIDL_user_free(DomainSid); + DomainSid = NULL; + } + if ( DomainNameU.Buffer != NULL ) { + RtlFreeUnicodeString( &DomainNameU ); + } + + return(NtStatus); + +BuildAliasMembersKeyNameError: + + goto BuildAliasMembersKeyNameFinish; +} + + +NTSTATUS +SampValidateNewAccountName( + PUNICODE_STRING NewAccountName + ) + +/*++ + +Routine Description: + + This routine validates a new user, alias or group account name. + This routine: + + 1) Validates that the name is properly constructed. + + 2) Is not already in use as a user, alias or group account name + in any of the local SAM domains. + + +Arguments: + + Name - The address of a unicode string containing the name to be + looked for. + +Return Value: + + STATUS_SUCCESS - The new account name is valid, and not yet in use. + + STATUS_ALIAS_EXISTS - The account name is already in use as a + alias account name. + + STATUS_GROUP_EXISTS - The account name is already in use as a + group account name. + + STATUS_USER_EXISTS - The account name is already in use as a user + account name. + +--*/ + +{ + NTSTATUS NtStatus; + SID_NAME_USE Use; + ULONG Rid; + ULONG DomainIndex, CurrentTransactionDomainIndex; + + SAMTRACE("SampValidateNewAccountName"); + + // + // Save the current transaction domain indicator + // + + CurrentTransactionDomainIndex = SampTransactionDomainIndex; + + // + // Lookup the account in each of the local SAM domains + // + + NtStatus = STATUS_SUCCESS; + for (DomainIndex = 0; + ( (DomainIndex < SampDefinedDomainsCount) && NT_SUCCESS(NtStatus) ); + DomainIndex++) { + + SampTransactionWithinDomain = FALSE; + SampSetTransactionDomain( DomainIndex ); + + NtStatus = SampLookupAccountRid( + SampDefinedDomains[DomainIndex].Context, + SampUnknownObjectType, + NewAccountName, + STATUS_NO_SUCH_USER, + &Rid, + &Use + ); + + if (!NT_SUCCESS(NtStatus)) { + + // + // The only error allowed is that the account was not found. + // Convert this to success, and continue searching SAM domains. + // Propagate any other error. + // + + if (NtStatus != STATUS_NO_SUCH_USER) { + + break; + } + + NtStatus = STATUS_SUCCESS; + + } else { + + // + // An account with the given Rid already exists. Return status + // indicating the type of the conflicting account. + // + + switch (Use) { + + case SidTypeUser: + + NtStatus = STATUS_USER_EXISTS; + break; + + case SidTypeGroup: + + NtStatus = STATUS_GROUP_EXISTS; + break; + + case SidTypeDomain: + + NtStatus = STATUS_DOMAIN_EXISTS; + break; + + case SidTypeAlias: + + NtStatus = STATUS_ALIAS_EXISTS; + break; + + case SidTypeWellKnownGroup: + + NtStatus = STATUS_GROUP_EXISTS; + break; + + case SidTypeDeletedAccount: + + NtStatus = STATUS_INVALID_PARAMETER; + break; + + case SidTypeInvalid: + + NtStatus = STATUS_INVALID_PARAMETER; + break; + + default: + + NtStatus = STATUS_INTERNAL_DB_CORRUPTION; + break; + } + } + } + + // + // Restore the Current Transaction Domain + // + + SampTransactionWithinDomain = FALSE; + SampSetTransactionDomain( CurrentTransactionDomainIndex ); + + return(NtStatus); +} + + +NTSTATUS +SampValidateAccountNameChange( + IN PUNICODE_STRING NewAccountName, + IN PUNICODE_STRING OldAccountName + ) + +/*++ + +Routine Description: + + This routine validates a user, group or alias account name that is + to be set on an account. This routine: + + 1) Returns success if the name is the same as the existing name, + except with a different case + + 1) Otherwise calls SampValidateNewAccountName to verify that the + name is properly constructed and is not already in use as a + user, alias or group account name. + +Arguments: + + NewAccountName - The address of a unicode string containing the new + name. + + OldAccountName - The address of a unicode string containing the old + name. + + +Return Value: + + STATUS_SUCCESS - The account's name may be changed to the new + account name + + STATUS_ALIAS_EXISTS - The account name is already in use as a + alias account name. + + STATUS_GROUP_EXISTS - The account name is already in use as a + group account name. + + STATUS_USER_EXISTS - The account name is already in use as a user + account name. + + + +--*/ + +{ + // + // Compare the old and new names without regard for case. If they + // are the same, return success because the name was checked when we + // first added it; we don't care about case changes. + // + + SAMTRACE("SampValidateAccountNameChange"); + + if ( 0 == RtlCompareUnicodeString( + NewAccountName, + OldAccountName, + TRUE ) ) { + + return( STATUS_SUCCESS ); + } + + // + // Not just a case change; this is a different name. Validate it as + // any new name. + // + + return( SampValidateNewAccountName( NewAccountName ) ); +} + + + +NTSTATUS +SampRetrieveAccountCounts( + OUT PULONG UserCount, + OUT PULONG GroupCount, + OUT PULONG AliasCount + ) + + +/*++ + +Routine Description: + + This routine retrieve the number of user and group accounts in a domain. + + + + Note: THIS ROUTINE REFERENCES THE CURRENT TRANSACTION DOMAIN + (ESTABLISHED USING SampSetTransactioDomain()). THIS + SERVICE MAY ONLY BE CALLED AFTER SampSetTransactionDomain() + AND BEFORE SampReleaseReadLock(). + + + +Arguments: + + UserCount - Receives the number of user accounts in the domain. + + GroupCount - Receives the number of group accounts in the domain. + + AliasCount - Receives the number of alias accounts in the domain. + + +Return Value: + + STATUS_SUCCESS - The values have been retrieved. + + STATUS_INSUFFICIENT_RESOURCES - Not enough memory could be allocated + to perform the requested operation. + + Other values are unexpected errors. These may originate from + internal calls to: + SampRetrieveAccountsRegistry + SampRetrieveAcountsDs + + + +--*/ +{ + NTSTATUS NtStatus = STATUS_SUCCESS; + + + SAMTRACE("SampRetrieveAccountCount"); + + + ASSERT(SampTransactionWithinDomain == TRUE); + + // Check if a Ds Object + if (IsDsObject(SampDefinedDomains[SampTransactionDomainIndex].Context)) + NtStatus = SampRetrieveAccountCountsDs( + UserCount, + GroupCount, + AliasCount + ); + else + NtStatus = SampRetrieveAccountCountsRegistry( + UserCount, + GroupCount, + AliasCount + ); + return NtStatus; + +} + + + +NTSTATUS +SampRetrieveAccountCountsDs( + OUT PULONG UserCount, + OUT PULONG GroupCount, + OUT PULONG AliasCount + ) +/*++ + + Retrieve Account Counts from the DS. + + + PRE M1 -- we will retrieve counts from the Domain Object in the DS. + Adjust AccountCount will modify Domain Object in the DS for correct Account Count + + POST M1 -- Retrieve is as above + Adjust Account Count will be a No Op. Core DS will will update the Domain counts +--*/ + +{ + + NTSTATUS NtStatus = STATUS_SUCCESS; + + // Define an AttrBlock for reading User Group and Alias Counts + + // Fill values as NULL. DS will fill it out correctly for + // us upon a read. + ATTRVAL CountValues []= + { + { 0, NULL}, + { 0, NULL}, + { 0, NULL} + }; + // declare attribute types + ATTRTYP CountTypes[]= + { + SAMP_DOMAIN_USERCOUNT, + SAMP_DOMAIN_GROUPCOUNT, + SAMP_DOMAIN_ALIASCOUNT + }; + // declare the entire attrblock + DEFINE_ATTRBLOCK3(CountAttrs, CountTypes, CountValues); + ATTRBLOCK CountResult; + DSNAME * DomainObjectName; + + + + SAMTRACE("SampretrieveAccountCountsDs"); + + ASSERT(UserCount); + ASSERT(GroupCount); + ASSERT(AliasCount); + + + // Make the call to read from the DS + DomainObjectName = SampDefinedDomains[SampTransactionDomainIndex].Context->ObjectNameInDs; + + ASSERT(DomainObjectName); + + NtStatus = SampDsRead(DomainObjectName,0,SampDomainObjectType,&CountAttrs,&CountResult); + if (!NT_SUCCESS(NtStatus)) + goto Error; + + // Assert on attr types + ASSERT(CountResult.pAttr[0].attrTyp == SAMP_DOMAIN_USERCOUNT); + ASSERT(CountResult.pAttr[1].attrTyp == SAMP_DOMAIN_GROUPCOUNT); + ASSERT(CountResult.pAttr[2].attrTyp == SAMP_DOMAIN_ALIASCOUNT); + + // Assert on number of attr values + ASSERT(CountResult.pAttr[0].AttrVal.valCount == 1); + ASSERT(CountResult.pAttr[1].AttrVal.valCount == 1); + ASSERT(CountResult.pAttr[2].AttrVal.valCount == 1); + + // Assert on Attribute Lengths + ASSERT(CountResult.pAttr[0].AttrVal.pAVal[0].valLen == sizeof(ULONG)); + ASSERT(CountResult.pAttr[1].AttrVal.pAVal[0].valLen == sizeof(ULONG)); + ASSERT(CountResult.pAttr[2].AttrVal.pAVal[0].valLen == sizeof(ULONG)); + + // Fill the counts + *UserCount = *(UNALIGNED ULONG *)CountResult.pAttr[0].AttrVal.pAVal[0].pVal; + *GroupCount = *(UNALIGNED ULONG *)CountResult.pAttr[1].AttrVal.pAVal[0].pVal; + *AliasCount = *(UNALIGNED ULONG *)CountResult.pAttr[2].AttrVal.pAVal[0].pVal; + +Error: + return NtStatus; +} + + + +NTSTATUS +SampRetrieveAccountCountsRegistry( + OUT PULONG UserCount, + OUT PULONG GroupCount, + OUT PULONG AliasCount + ) +/*++ + +Routine Description: + + This routine retrieve the number of user and group accounts in a domain + in the registry.Called in by SampRetrieveAccountCounts if its the Registry + case + + + + Note: THIS ROUTINE REFERENCES THE CURRENT TRANSACTION DOMAIN + (ESTABLISHED USING SampSetTransactioDomain()). THIS + SERVICE MAY ONLY BE CALLED AFTER SampSetTransactionDomain() + AND BEFORE SampReleaseReadLock(). + + + +Arguments: + + UserCount - Receives the number of user accounts in the domain. + + GroupCount - Receives the number of group accounts in the domain. + + AliasCount - Receives the number of alias accounts in the domain. + + +Return Value: + + STATUS_SUCCESS - The values have been retrieved. + + STATUS_INSUFFICIENT_RESOURCES - Not enough memory could be allocated + to perform the requested operation. + + Other values are unexpected errors. These may originate from + internal calls to: + + NtOpenKey() + NtQueryInformationKey() + + +--*/ + +{ + NTSTATUS NtStatus, IgnoreStatus; + UNICODE_STRING KeyName; + PUNICODE_STRING AccountTypeKeyName; + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE AccountHandle; + ULONG KeyValueLength; + LARGE_INTEGER IgnoreLastWriteTime; + + + + SAMTRACE("SampRetrieveAccountCountsRegistry"); + + // + // Get the user count first + // + + AccountTypeKeyName = &SampNameDomainUsers; + NtStatus = SampBuildDomainSubKeyName( &KeyName, AccountTypeKeyName ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Open this key and get its current value + // + + InitializeObjectAttributes( + &ObjectAttributes, + &KeyName, + OBJ_CASE_INSENSITIVE, + SampKey, + NULL + ); + + SampDumpNtOpenKey((KEY_READ), &ObjectAttributes, 0); + + NtStatus = RtlpNtOpenKey( + &AccountHandle, + (KEY_READ), + &ObjectAttributes, + 0 + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // The count is stored as the KeyValueType + // + + KeyValueLength = 0; + NtStatus = RtlpNtQueryValueKey( + AccountHandle, + UserCount, + NULL, + &KeyValueLength, + &IgnoreLastWriteTime + ); + + SampDumpRtlpNtQueryValueKey(UserCount, + NULL, + &KeyValueLength, + &IgnoreLastWriteTime); + + + + IgnoreStatus = NtClose( AccountHandle ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + } + + SampFreeUnicodeString( &KeyName ); + + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + } + + // + // Now get the group count + // + + AccountTypeKeyName = &SampNameDomainGroups; + NtStatus = SampBuildDomainSubKeyName( &KeyName, AccountTypeKeyName ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Open this key and get its current value + // + + InitializeObjectAttributes( + &ObjectAttributes, + &KeyName, + OBJ_CASE_INSENSITIVE, + SampKey, + NULL + ); + + SampDumpNtOpenKey((KEY_READ), &ObjectAttributes, 0); + + NtStatus = RtlpNtOpenKey( + &AccountHandle, + (KEY_READ), + &ObjectAttributes, + 0 + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // The count is stored as the KeyValueType + // + + KeyValueLength = 0; + NtStatus = RtlpNtQueryValueKey( + AccountHandle, + GroupCount, + NULL, + &KeyValueLength, + &IgnoreLastWriteTime + ); + + SampDumpRtlpNtQueryValueKey(GroupCount, + NULL, + &KeyValueLength, + &IgnoreLastWriteTime); + + + + IgnoreStatus = NtClose( AccountHandle ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + } + + SampFreeUnicodeString( &KeyName ); + + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + } + + // + // Now get the alias count + // + + AccountTypeKeyName = &SampNameDomainAliases; + NtStatus = SampBuildDomainSubKeyName( &KeyName, AccountTypeKeyName ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Open this key and get its current value + // + + InitializeObjectAttributes( + &ObjectAttributes, + &KeyName, + OBJ_CASE_INSENSITIVE, + SampKey, + NULL + ); + + SampDumpNtOpenKey((KEY_READ), &ObjectAttributes, 0); + + NtStatus = RtlpNtOpenKey( + &AccountHandle, + (KEY_READ), + &ObjectAttributes, + 0 + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // The count is stored as the KeyValueType + // + + KeyValueLength = 0; + NtStatus = RtlpNtQueryValueKey( + AccountHandle, + AliasCount, + NULL, + &KeyValueLength, + &IgnoreLastWriteTime + ); + + SampDumpRtlpNtQueryValueKey(AliasCount, + NULL, + &KeyValueLength, + &IgnoreLastWriteTime); + + + + IgnoreStatus = NtClose( AccountHandle ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + } + + SampFreeUnicodeString( &KeyName ); + } + + return( NtStatus); + +} + + + +NTSTATUS +SampAdjustAccountCount( + IN SAMP_OBJECT_TYPE ObjectType, + IN BOOLEAN Increment + ) + +/*++ + +Routine Description: + + This is the main wrapper routine for Adjusting account counts + This routine figures out wether the object is in the Ds or the + registry and then calls one of the two routines + + Note: THIS ROUTINE REFERENCES THE CURRENT TRANSACTION DOMAIN + (ESTABLISHED USING SampSetTransactioDomain()). THIS + SERVICE MAY ONLY BE CALLED AFTER SampSetTransactionDomain() + AND BEFORE SampReleaseWriteLock(). + Arguments: + + ObjectType - Indicates whether the account is a user or group account. + + Increment - a BOOLEAN value indicating whether the user or group + count is to be incremented or decremented. A value of TRUE + will cause the count to be incremented. A value of FALSE will + cause the value to be decremented. + + +Return Value: + + STATUS_SUCCESS - The value has been adjusted and the new value added + to the current RXACT transaction. + + Other values are unexpected errors. These may originate from + internal calls to sampAdjustAccountCountInRegistry, SampAdjustAccountCount + inDs + +--*/ +{ + NTSTATUS NtStatus = STATUS_SUCCESS; + + + SAMTRACE("SampAdjustAccountCount"); + + + ASSERT(SampTransactionWithinDomain == TRUE); + ASSERT( (ObjectType == SampGroupObjectType) || + (ObjectType == SampAliasObjectType) || + (ObjectType == SampUserObjectType) ); + + // + // Check if the current transaction Domain is in the DS or in the + // Registry. Accordingly the DS or registry version is called. + // + if (IsDsObject(SampDefinedDomains[SampTransactionDomainIndex].Context)) + NtStatus = SampAdjustAccountCountDs( + ObjectType, + Increment + ); + else + NtStatus = SampAdjustAccountCountRegistry( + ObjectType, + Increment + ); + return NtStatus; + +} + + +NTSTATUS +SampAdjustAccountCountDs( + IN SAMP_OBJECT_TYPE ObjectType, + IN BOOLEAN Increment + ) +/*++ + +Routine Description + + This routine increments or decrements the count of users + or groups in a domain. + + PRE M1 -- This reads the current count and then increments + or decremebts. + + POST M1 -- No Op ,as the core DS will have the required support + Note: THIS ROUTINE REFERENCES THE CURRENT TRANSACTION DOMAIN + (ESTABLISHED USING SampSetTransactioDomain()). THIS + SERVICE MAY ONLY BE CALLED AFTER SampSetTransactionDomain() + AND BEFORE SampReleaseWriteLock(). + + + +Arguments: + + ObjectType - Indicates whether the account is a user or group account. + + Increment - a BOOLEAN value indicating whether the user or group + count is to be incremented or decremented. A value of TRUE + will cause the count to be incremented. A value of FALSE will + cause the value to be decremented. + + +Return Value: + + STATUS_SUCCESS - The value has been adjusted and the new value added + + Any other return values from calls into Ds Layer +--*/ +{ + NTSTATUS NtStatus = STATUS_SUCCESS; + ULONG Count; + ULONG UserCount; + ULONG GroupCount; + ULONG AliasCount; + // Define an Attr block for writing count + ATTRVAL CountValues []= + { + {sizeof(ULONG), (UCHAR *) &Count} + }; + ATTRTYP CountTypes[]= // declare attribute type as 0, later we will fill in + { // depending upon object Type + 0 + }; + DEFINE_ATTRBLOCK1(CountAttrs, CountTypes, CountValues); + DSNAME * DomainObjectName; + + + + + SAMTRACE("SampAdjustAccountCountsDs"); + + ASSERT(SampTransactionWithinDomain == TRUE); + ASSERT( (ObjectType == SampGroupObjectType) || + (ObjectType == SampAliasObjectType) || + (ObjectType == SampUserObjectType) ); + + + // + // Get the current values of account counts + // Retrieve all the three values + // + + NtStatus = SampRetrieveAccountCountsDs(&UserCount,&GroupCount,&AliasCount); + if (!NT_SUCCESS(NtStatus)) + goto Error; + + // Set the count and attribute type by object + switch(ObjectType) + { + case SampGroupObjectType: + Count = GroupCount; + CountAttrs.pAttr[0].attrTyp = SAMP_DOMAIN_GROUPCOUNT; + break; + + case SampAliasObjectType: + Count = AliasCount; + CountAttrs.pAttr[0].attrTyp = SAMP_DOMAIN_ALIASCOUNT; + break; + + case SampUserObjectType: + Count = UserCount; + CountAttrs.pAttr[0].attrTyp = SAMP_DOMAIN_USERCOUNT; + break; + + default: + NtStatus = STATUS_UNSUCCESSFUL; + goto Error; + } + + // Get the DomainObjectName + DomainObjectName = SampDefinedDomains[SampTransactionDomainIndex].Context->ObjectNameInDs; + + ASSERT(DomainObjectName); + + // Increment or decrement the count + if (Increment) + Count++; + else + Count--; + + // Make the DS Call to set the new count + NtStatus = SampDsSetAttributes(DomainObjectName,0, SampDomainObjectType,&CountAttrs); + +Error: + + return NtStatus; + +} + + + + + + +NTSTATUS +SampAdjustAccountCountRegistry( + IN SAMP_OBJECT_TYPE ObjectType, + IN BOOLEAN Increment + ) + +/*++ + +Routine Description: + + This routine increments or decrements the count of either + users or groups in a domain. + + + + Note: THIS ROUTINE REFERENCES THE CURRENT TRANSACTION DOMAIN + (ESTABLISHED USING SampSetTransactioDomain()). THIS + SERVICE MAY ONLY BE CALLED AFTER SampSetTransactionDomain() + AND BEFORE SampReleaseWriteLock(). + + + +Arguments: + + ObjectType - Indicates whether the account is a user or group account. + + Increment - a BOOLEAN value indicating whether the user or group + count is to be incremented or decremented. A value of TRUE + will cause the count to be incremented. A value of FALSE will + cause the value to be decremented. + + +Return Value: + + STATUS_SUCCESS - The value has been adjusted and the new value added + to the current RXACT transaction. + + STATUS_INSUFFICIENT_RESOURCES - Not enough memory could be allocated + to perform the requested operation. + + Other values are unexpected errors. These may originate from + internal calls to: + + NtOpenKey() + NtQueryInformationKey() + RtlAddActionToRXact() + + +--*/ +{ + NTSTATUS NtStatus, IgnoreStatus; + UNICODE_STRING KeyName; + PUNICODE_STRING AccountTypeKeyName; + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE AccountHandle; + ULONG Count, KeyValueLength; + LARGE_INTEGER IgnoreLastWriteTime; + + SAMTRACE("SampAdjustAccountCount"); + + + ASSERT(SampTransactionWithinDomain == TRUE); + ASSERT( (ObjectType == SampGroupObjectType) || + (ObjectType == SampAliasObjectType) || + (ObjectType == SampUserObjectType) ); + + + // + // Build the name of the key whose count is to be incremented or + // decremented. + // + + switch (ObjectType) { + case SampGroupObjectType: + AccountTypeKeyName = &SampNameDomainGroups; + break; + case SampAliasObjectType: + AccountTypeKeyName = &SampNameDomainAliases; + break; + case SampUserObjectType: + AccountTypeKeyName = &SampNameDomainUsers; + break; + } + + NtStatus = SampBuildDomainSubKeyName( &KeyName, AccountTypeKeyName ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Open this key and get its current value + // + + InitializeObjectAttributes( + &ObjectAttributes, + &KeyName, + OBJ_CASE_INSENSITIVE, + SampKey, + NULL + ); + + SampDumpNtOpenKey((KEY_READ), &ObjectAttributes, 0); + + NtStatus = RtlpNtOpenKey( + &AccountHandle, + (KEY_READ), + &ObjectAttributes, + 0 + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // The count is stored as the KeyValueType + // + + KeyValueLength = 0; + NtStatus = RtlpNtQueryValueKey( + AccountHandle, + &Count, + NULL, + &KeyValueLength, + &IgnoreLastWriteTime + ); + + SampDumpRtlpNtQueryValueKey(&Count, + NULL, + &KeyValueLength, + &IgnoreLastWriteTime); + + if (NT_SUCCESS(NtStatus)) { + + if (Increment == TRUE) { + Count += 1; + } else { + ASSERT( Count != 0 ); + Count -= 1; + } + + NtStatus = RtlAddActionToRXact( + SampRXactContext, + RtlRXactOperationSetValue, + &KeyName, + Count, + NULL, + 0 + ); + } + + + IgnoreStatus = NtClose( AccountHandle ); + ASSERT( NT_SUCCESS(IgnoreStatus) ); + } + + SampFreeUnicodeString( &KeyName ); + } + + + return( STATUS_SUCCESS ); + + +} + + + + +NTSTATUS +SampLookupAccountRid( + IN PSAMP_OBJECT DomainContext, + IN SAMP_OBJECT_TYPE ObjectType, + IN PUNICODE_STRING Name, + IN NTSTATUS NotFoundStatus, + OUT PULONG Rid, + OUT PSID_NAME_USE Use + ) + +/*++ + +Routine Description: + + + + +Arguments: + + DomainContext - Indicates the domain in which the account lookup is being done + + ObjectType - Indicates whether the name is a user, group or unknown + type of object. + + Name - The name of the account being looked up. + + NotFoundStatus - Receives a status value to be returned if no name is + found. + + Rid - Receives the relative ID of account with the specified name. + + Use - Receives an indication of the type of account. + + +Return Value: + + STATUS_SUCCESS - The Service completed successfully. + + (NotFoundStatus) - No name by the specified name and type could be + found. This value is passed to this routine. + + Other values that may be returned by: + + SampBuildAccountKeyName() + NtOpenKey() + NtQueryValueKey() + DsLayer + +--*/ +{ + + NTSTATUS NtStatus = STATUS_SUCCESS; + DSNAME * ObjectName; + + ASSERT(DomainContext); + + if (IsDsObject(DomainContext)) + { + // Do the DS Thing + NtStatus = SampDsLookupObjectByName(DomainContext->ObjectNameInDs, ObjectType, Name, &ObjectName); + if NT_SUCCESS(NtStatus) + { + ULONG ObjectClass; + + // We found the object, lookup its class and its Rid + + // Define an Attrblock structure to do so. Fill in values + // field as NULL. DS will fill them out correctly for us upon + // a Read + + ATTRVAL ValuesDesired[] = + { + { sizeof(ULONG), NULL }, + { sizeof(ULONG), NULL } + }; + + ATTRTYP TypesDesired[]= + { + SAMP_UNKNOWN_OBJECTRID, + SAMP_UNKNOWN_OBJECTCLASS, + }; + ATTRBLOCK AttrsRead; + DEFINE_ATTRBLOCK2(AttrsDesired,TypesDesired,ValuesDesired); + + NtStatus = SampDsRead(ObjectName, 0, SampUnknownObjectType, & AttrsDesired, &AttrsRead); + + + if NT_SUCCESS(NtStatus) + { + + *Rid = *((UNALIGNED ULONG *) AttrsRead.pAttr[0].AttrVal.pAVal[0].pVal); + ObjectClass = *((UNALIGNED ULONG *) AttrsRead.pAttr[1].AttrVal.pAVal[0].pVal); + + // + // BUG: Need to do something for groups and Aliases + // mapping to the same object + // + + switch(SampSamObjectTypeFromDsClass(ObjectClass)) + { + case SampDomainObjectType: + *Use=SidTypeDomain; + break; + case SampGroupObjectType: + *Use=SidTypeGroup; + break; + case SampAliasObjectType: + *Use=SidTypeAlias; + break; + case SampUserObjectType: + *Use=SidTypeUser; + break; + case SampUnknownObjectType: + *Use=SidTypeUnknown; + break; + default: + *Use=SidTypeInvalid; + break; + } + } + } + + } + else + { + NtStatus = SampLookupAccountRidRegistry( + ObjectType, + Name, + NotFoundStatus, + Rid, + Use + ); + } + + return NtStatus; +} + + +NTSTATUS +SampLookupAccountRidRegistry( + IN SAMP_OBJECT_TYPE ObjectType, + IN PUNICODE_STRING Name, + IN NTSTATUS NotFoundStatus, + OUT PULONG Rid, + OUT PSID_NAME_USE Use + ) + +/*++ + +Routine Description: + + + + +Arguments: + + ObjectType - Indicates whether the name is a user, group or unknown + type of object. + + Name - The name of the account being looked up. + + NotFoundStatus - Receives a status value to be returned if no name is + found. + + Rid - Receives the relative ID of account with the specified name. + + Use - Receives an indication of the type of account. + + +Return Value: + + STATUS_SUCCESS - The Service completed successfully. + + (NotFoundStatus) - No name by the specified name and type could be + found. This value is passed to this routine. + + Other values that may be returned by: + + SampBuildAccountKeyName() + NtOpenKey() + NtQueryValueKey() + +--*/ + + +{ + NTSTATUS NtStatus, TmpStatus; + UNICODE_STRING KeyName; + OBJECT_ATTRIBUTES ObjectAttributes; + HANDLE TempHandle; + ULONG KeyValueLength; + LARGE_INTEGER IgnoreLastWriteTime; + + + SAMTRACE("SampLookupAccountRid"); + + + if ( (ObjectType == SampGroupObjectType ) || + (ObjectType == SampUnknownObjectType) ) { + + // + // Search the groups for a match + // + + NtStatus = SampBuildAccountKeyName( + SampGroupObjectType, + &KeyName, + Name + ); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + InitializeObjectAttributes( + &ObjectAttributes, + &KeyName, + OBJ_CASE_INSENSITIVE, + SampKey, + NULL + ); + + SampDumpNtOpenKey((KEY_READ), &ObjectAttributes, 0); + + NtStatus = RtlpNtOpenKey( + &TempHandle, + (KEY_READ), + &ObjectAttributes, + 0 + ); + SampFreeUnicodeString( &KeyName ); + + if (NT_SUCCESS(NtStatus)) { + + (*Use) = SidTypeGroup; + + KeyValueLength = 0; + NtStatus = RtlpNtQueryValueKey( + TempHandle, + Rid, + NULL, + &KeyValueLength, + &IgnoreLastWriteTime + ); + + SampDumpRtlpNtQueryValueKey(Rid, + NULL, + &KeyValueLength, + &IgnoreLastWriteTime); + + TmpStatus = NtClose( TempHandle ); + ASSERT( NT_SUCCESS(TmpStatus) ); + + return( NtStatus ); + } + + + } + + // + // No group (or not group type) + // Try an alias if appropriate + // + + if ( (ObjectType == SampAliasObjectType ) || + (ObjectType == SampUnknownObjectType) ) { + + // + // Search the aliases for a match + // + + NtStatus = SampBuildAccountKeyName( + SampAliasObjectType, + &KeyName, + Name + ); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + InitializeObjectAttributes( + &ObjectAttributes, + &KeyName, + OBJ_CASE_INSENSITIVE, + SampKey, + NULL + ); + + SampDumpNtOpenKey((KEY_READ), &ObjectAttributes, 0); + + NtStatus = RtlpNtOpenKey( + &TempHandle, + (KEY_READ), + &ObjectAttributes, + 0 + ); + SampFreeUnicodeString( &KeyName ); + + if (NT_SUCCESS(NtStatus)) { + + (*Use) = SidTypeAlias; + + KeyValueLength = 0; + NtStatus = RtlpNtQueryValueKey( + TempHandle, + Rid, + NULL, + &KeyValueLength, + &IgnoreLastWriteTime + ); + + SampDumpRtlpNtQueryValueKey(Rid, + NULL, + &KeyValueLength, + &IgnoreLastWriteTime); + + TmpStatus = NtClose( TempHandle ); + ASSERT( NT_SUCCESS(TmpStatus) ); + + return( NtStatus ); + } + + + } + + + // + // No group (or not group type) nor alias (or not alias type) + // Try a user if appropriate + // + + + if ( (ObjectType == SampUserObjectType ) || + (ObjectType == SampUnknownObjectType) ) { + + // + // Search the Users for a match + // + + NtStatus = SampBuildAccountKeyName( + SampUserObjectType, + &KeyName, + Name + ); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + InitializeObjectAttributes( + &ObjectAttributes, + &KeyName, + OBJ_CASE_INSENSITIVE, + SampKey, + NULL + ); + + SampDumpNtOpenKey((KEY_READ), &ObjectAttributes, 0); + + NtStatus = RtlpNtOpenKey( + &TempHandle, + (KEY_READ), + &ObjectAttributes, + 0 + ); + SampFreeUnicodeString( &KeyName ); + + if (NT_SUCCESS(NtStatus)) { + + (*Use) = SidTypeUser; + + KeyValueLength = 0; + NtStatus = RtlpNtQueryValueKey( + TempHandle, + Rid, + NULL, + &KeyValueLength, + &IgnoreLastWriteTime + ); + + SampDumpRtlpNtQueryValueKey(Rid, + NULL, + &KeyValueLength, + &IgnoreLastWriteTime); + + + TmpStatus = NtClose( TempHandle ); + ASSERT( NT_SUCCESS(TmpStatus) ); + + return( NtStatus ); + } + + + } + + if (NtStatus == STATUS_OBJECT_NAME_NOT_FOUND) { + NtStatus = NotFoundStatus; + } + + return(NtStatus); +} + + + +NTSTATUS +SampLookupAccountName( + IN ULONG Rid, + OUT PUNICODE_STRING Name OPTIONAL, + OUT PSAMP_OBJECT_TYPE ObjectType + ) + +/*++ + +Routine Description: + + Looks up the specified rid in the current transaction domain. + Returns its name and account type. + + +Arguments: + + Rid - The relative ID of account + + Name - Receives the name of the account if ObjectType != UnknownObjectType + The name buffer can be freed using MIDL_user_free + + ObjectType - Receives the type of account this rid represents + + SampUnknownObjectType - the account doesn't exist + SampUserObjectType + SampGroupObjectType + SampAliasObjectType + +Return Value: + + STATUS_SUCCESS - The Service completed successfully, object type contains + the type of object this rid represents. + + Other values that may be returned by: + + SampBuildAccountKeyName() + NtOpenKey() + NtQueryValueKey() + +--*/ +{ + NTSTATUS NtStatus; + PSAMP_OBJECT AccountContext; + + SAMTRACE("SampLookupAccountName"); + + // + // Search the groups for a match + // + + NtStatus = SampCreateAccountContext( + SampGroupObjectType, + Rid, + TRUE, // Trusted client + TRUE, // Account exists + &AccountContext + ); + + + if (NT_SUCCESS(NtStatus)) { + + *ObjectType = SampGroupObjectType; + + if (ARGUMENT_PRESENT(Name)) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_GROUP_NAME, + TRUE, // Make copy + Name + ); + } + + SampDeleteContext(AccountContext); + + return (NtStatus); + + } + + // + // Search the aliases for a match + // + + NtStatus = SampCreateAccountContext( + SampAliasObjectType, + Rid, + TRUE, // Trusted client + TRUE, // Account exists + &AccountContext + ); + + + if (NT_SUCCESS(NtStatus)) { + + *ObjectType = SampAliasObjectType; + + if (ARGUMENT_PRESENT(Name)) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_ALIAS_NAME, + TRUE, // Make copy + Name + ); + } + + SampDeleteContext(AccountContext); + + return (NtStatus); + + } + + + // + // Search the users for a match + // + + NtStatus = SampCreateAccountContext( + SampUserObjectType, + Rid, + TRUE, // Trusted client + TRUE, // Account exists + &AccountContext + ); + + + if (NT_SUCCESS(NtStatus)) { + + *ObjectType = SampUserObjectType; + + if (ARGUMENT_PRESENT(Name)) { + + NtStatus = SampGetUnicodeStringAttribute( + AccountContext, + SAMP_USER_ACCOUNT_NAME, + TRUE, // Make copy + Name + ); + } + + SampDeleteContext(AccountContext); + + return (NtStatus); + + } + + + + + // + // This account doesn't exist + // + + *ObjectType = SampUnknownObjectType; + + return(STATUS_SUCCESS); +} + + + +NTSTATUS +SampOpenAccount( + IN SAMP_OBJECT_TYPE ObjectType, + IN SAMPR_HANDLE DomainHandle, + IN ACCESS_MASK DesiredAccess, + IN ULONG AccountId, + IN BOOLEAN WriteLockHeld, + OUT SAMPR_HANDLE *AccountHandle + ) + +/*++ + +Routine Description: + + This API opens an existing user, group or alias account in the account database. + The account is specified by a ID value that is relative to the SID of the + domain. The operations that will be performed on the group must be + declared at this time. + + This call returns a handle to the newly opened account that may be + used for successive operations on the account. This handle may be + closed with the SamCloseHandle API. + + + +Parameters: + + DomainHandle - A domain handle returned from a previous call to + SamOpenDomain. + + DesiredAccess - Is an access mask indicating which access types + are desired to the account. These access types are reconciled + with the Discretionary Access Control list of the account to + determine whether the accesses will be granted or denied. + + GroupId - Specifies the relative ID value of the user or group to be + opened. + + GroupHandle - Receives a handle referencing the newly opened + user or group. This handle will be required in successive calls to + operate on the account. + + WriteLockHeld - if TRUE, the caller holds SAM's SampLock for WRITE + access, so this routine does not have to obtain it. + +Return Values: + + STATUS_SUCCESS - The account was successfully opened. + + STATUS_ACCESS_DENIED - Caller does not have the appropriate + access to complete the operation. + + STATUS_NO_SUCH_GROUP - The specified group does not exist. + + STATUS_NO_SUCH_USER - The specified user does not exist. + + STATUS_NO_SUCH_ALIAS - The specified alias does not exist. + + STATUS_INVALID_HANDLE - The domain handle passed is invalid. + +--*/ +{ + + NTSTATUS NtStatus; + NTSTATUS IgnoreStatus; + PSAMP_OBJECT DomainContext, NewContext; + SAMP_OBJECT_TYPE FoundType; + + SAMTRACE("SampOpenAccount"); + + + // + // Grab a read lock, if a lock isn't already held. + // + + if ( !WriteLockHeld ) { + SampAcquireReadLock(); + } + + + // + // Validate type of, and access to domain object. + // + + DomainContext = (PSAMP_OBJECT)DomainHandle; + NtStatus = SampLookupContext( + DomainContext, + DOMAIN_LOOKUP, // DesiredAccess + SampDomainObjectType, // ExpectedType + &FoundType + ); + + + + if (NT_SUCCESS(NtStatus)) { + + // + // Try to create a context for the account. + // + + NtStatus = SampCreateAccountContext( + ObjectType, + AccountId, + DomainContext->TrustedClient, + TRUE, // Account exists + &NewContext + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Reference the object for the validation + // + + SampReferenceContext(NewContext); + + // + // Validate the caller's access. + // + + NtStatus = SampValidateObjectAccess( + NewContext, //Context + DesiredAccess, //DesiredAccess + FALSE //ObjectCreation + ); + + // + // Dereference object, discarding any changes + // + + IgnoreStatus = SampDeReferenceContext(NewContext, FALSE); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + // + // Clean up the new context if we didn't succeed. + // + + if (!NT_SUCCESS(NtStatus)) { + SampDeleteContext( NewContext ); + } + + } + + + // + // De-reference the object, discarding changes + // + + IgnoreStatus = SampDeReferenceContext( DomainContext, FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + } + + + // + // Return the account handle + // + + if (!NT_SUCCESS(NtStatus)) { + (*AccountHandle) = 0; + } else { + (*AccountHandle) = NewContext; + } + + + // + // Free the lock, if we obtained it. + // + + if ( !WriteLockHeld ) { + SampReleaseReadLock(); + } + + return(NtStatus); +} + + + +NTSTATUS +SampCreateAccountContext( + IN SAMP_OBJECT_TYPE ObjectType, + IN ULONG AccountId, + IN BOOLEAN TrustedClient, + IN BOOLEAN AccountExists, + OUT PSAMP_OBJECT *AccountContext + ) + +/*++ + +Routine Description: + + This API creates a context for an account object. (User group or alias). + If the account exists flag is specified, an attempt is made to open + the object in the database and this api fails if it doesn't exist. + If AccountExists = FALSE, this routine setups up the context such that + data can be written into the context and the object will be created + when they are committed. + + The account is specified by a ID value that is relative to the SID of the + current transaction domain. + + This call returns a context handle for the newly opened account. + This handle may be closed with the SampDeleteContext API. + + No access check is performed by this function. + + Note: THIS ROUTINE REFERENCES THE CURRENT TRANSACTION DOMAIN + (ESTABLISHED USING SampSetTransactioDomain()). THIS + SERVICE MAY ONLY BE CALLED AFTER SampSetTransactionDomain() + AND BEFORE SampReleaseReadLock(). + + + +Parameters: + + ObjectType - the type of object to open + + AccountId - the id of the account in the current transaction domain + + TrustedClient - TRUE if client is trusted - i.e. server side process. + + AccountExists - specifies whether the account already exists. + + AccountContext - Receives context pointer referencing the newly opened account. + + +Return Values: + + STATUS_SUCCESS - The account was successfully opened. + + STATUS_NO_SUCH_GROUP - The specified group does not exist. + + STATUS_NO_SUCH_USER - The specified user does not exist. + + STATUS_NO_SUCH_ALIAS - The specified alias does not exist. + +--*/ +{ + + NTSTATUS NtStatus, NotFoundStatus; + OBJECT_ATTRIBUTES ObjectAttributes; + PSAMP_OBJECT NewContext; + PSAMP_OBJECT DomainContext; + + SAMTRACE("SampCreateAccountContext"); + + + // + // Establish type-specific information + // + + ASSERT( (ObjectType == SampGroupObjectType) || + (ObjectType == SampAliasObjectType) || + (ObjectType == SampUserObjectType) ); + + switch (ObjectType) { + case SampGroupObjectType: + NotFoundStatus = STATUS_NO_SUCH_GROUP; + break; + case SampAliasObjectType: + NotFoundStatus = STATUS_NO_SUCH_ALIAS; + break; + case SampUserObjectType: + NotFoundStatus = STATUS_NO_SUCH_USER; + break; + } + + // + // Get the Domain Context + // + + DomainContext = SampDefinedDomains[SampTransactionDomainIndex].Context; + + // + // Try to create a context for the account. + // + + NewContext = SampCreateContext( + ObjectType, + TrustedClient + ); + + if (NewContext != NULL) { + + + // + // Set the account's rid + // + + switch (ObjectType) { + case SampGroupObjectType: + NewContext->TypeBody.Group.Rid = AccountId; + break; + case SampAliasObjectType: + NewContext->TypeBody.Alias.Rid = AccountId; + break; + case SampUserObjectType: + NewContext->TypeBody.User.Rid = AccountId; + break; + } + + + + // Check if registry or DS. We have to do different things depending + // upon Registry or Ds. + if (IsDsObject(DomainContext)) + { + // + // Domain to which the object belongs ( supposedly) Exists in the DS + // + + if (AccountExists) + { + + // + // Caller Specified that this is an existing Account. + // In this case we do the following + // + // 1. Lookup the account in the DS using the Rid and the Domain Object + // This should give us the DS Name of the object in question if + // the object exisits + // + // 2. Get the Object Class and verify if the Object is the same type as + // specified by the caller + + + // Locate the account + NtStatus = SampDsLookupObjectByRid( + DomainContext->ObjectNameInDs, + AccountId, + &(NewContext->ObjectNameInDs) + ); + if (NT_SUCCESS(NtStatus)) + { + // + // Validate the type of account, by getting Object Class + // + + ULONG ObjectClass; + + // Declare the ATTR Block to retrieve the object class + // Fill the value field as NULL, DS will fill it out correctly + // for us upon the DsRead + + ATTRVAL ValuesDesired[] = + { + { sizeof(ULONG), NULL } + }; + + ATTRTYP TypesDesired[]= + { + SAMP_UNKNOWN_OBJECTCLASS, + }; + ATTRBLOCK AttrsRead; + DEFINE_ATTRBLOCK1(AttrsDesired,TypesDesired,ValuesDesired); + + // Do the DS read + NtStatus = SampDsRead( + NewContext->ObjectNameInDs, + 0, + SampUnknownObjectType, + & AttrsDesired, + & AttrsRead + ); + + if NT_SUCCESS(NtStatus) + { + // Fish out the object class. + // BUG: Need to do something about Groups and Aliases Mapping to + // the Same thing. For Now we will keep groups and aliases to be + // interchangeable + + ULONG ObjectTypeStoredInDs; + ObjectClass = *((UNALIGNED ULONG *) AttrsRead.pAttr[0].AttrVal.pAVal[0].pVal); + ObjectTypeStoredInDs = SampSamObjectTypeFromDsClass(ObjectClass); + + if ((ULONG) ObjectType != ObjectTypeStoredInDs) + { + // + // If we asked for an Alias Object, and got back a group object + // then pass the call + // + + if (!((ObjectType==SampAliasObjectType) + && (ObjectTypeStoredInDs == SampGroupObjectType))) + + NtStatus = NotFoundStatus; + } + } + } + } + else + { + // + // Caller Wants to create a new Account. Therefore ..... + // Create a DSNAME for the object that caller plans on creating in the + // specified Domain. + // + + NtStatus = SampDsCreateDsName( + DomainContext->ObjectNameInDs, + AccountId, + &(NewContext->ObjectNameInDs) + ); + } + + } + else + { + // + // The Domain in which the account is supposed to exist/ or to + // be created is in the registry + // + + // + // Create the specified acocunt's root key name + // and store it in the context. + // This name remains around until the context is deleted. + // + + NtStatus = SampBuildAccountSubKeyName( + ObjectType, + &NewContext->RootName, + AccountId, + NULL // Don't give a sub-key name + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // If the account should exist, try and open the root key + // to the object - fail if it doesn't exist. + // + + if (AccountExists) { + + InitializeObjectAttributes( + &ObjectAttributes, + &NewContext->RootName, + OBJ_CASE_INSENSITIVE, + SampKey, + NULL + ); + + SampDumpNtOpenKey((KEY_READ | KEY_WRITE), &ObjectAttributes, 0); + + NtStatus = RtlpNtOpenKey( + &NewContext->RootKey, + (KEY_READ | KEY_WRITE), + &ObjectAttributes, + 0 + ); + + if ( !NT_SUCCESS(NtStatus) ) { + NewContext->RootKey = INVALID_HANDLE_VALUE; + NtStatus = NotFoundStatus; + } + } + + } else { + RtlInitUnicodeString(&NewContext->RootName, NULL); + } + } + + // + // Clean up the account context if we failed + // + + if (!NT_SUCCESS(NtStatus)) { + SampDeleteContext( NewContext ); + NewContext = NULL; + } + + } else { + + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + + } + + + // + // Return the context pointer + // + + *AccountContext = NewContext; + + + return(NtStatus); +} + + + +NTSTATUS +SampIsAccountBuiltIn( + IN ULONG Rid + ) + +/*++ + +Routine Description: + + This routine checks to see if a specified account name is a well-known + (built-in) account. Some restrictions apply to such accounts, such as + they can not be deleted or renamed. + + +Parameters: + + Rid - The RID of the account. + + +Return Values: + + STATUS_SUCCESS - The account is not a well-known (restricted) account. + + STATUS_SPECIAL_ACCOUNT - Indicates the account is a restricted + account. This is an error status, based upon the assumption that + this service will primarily be utilized to determine if an + operation may allowed on an account. + + +--*/ +{ + SAMTRACE("SampIsAccountBuiltIn"); + + if (Rid < SAMP_RESTRICTED_ACCOUNT_COUNT) { + + return(STATUS_SPECIAL_ACCOUNT); + + } else { + + return(STATUS_SUCCESS); + } +} + + + +NTSTATUS +SampCreateFullSid( + IN PSID DomainSid, + IN ULONG Rid, + OUT PSID *AccountSid + ) + +/*++ + +Routine Description: + + This function creates a domain account sid given a domain sid and + the relative id of the account within the domain. + + The returned Sid may be freed with MIDL_user_free. + +Arguments: + + None. + +Return Value: + + STATUS_SUCCESS + +--*/ +{ + + NTSTATUS NtStatus, IgnoreStatus; + UCHAR AccountSubAuthorityCount; + ULONG AccountSidLength; + PULONG RidLocation; + + SAMTRACE("SampCreateFullSid"); + + // + // Calculate the size of the new sid + // + + AccountSubAuthorityCount = *RtlSubAuthorityCountSid(DomainSid) + (UCHAR)1; + AccountSidLength = RtlLengthRequiredSid(AccountSubAuthorityCount); + + // + // Allocate space for the account sid + // + + *AccountSid = MIDL_user_allocate(AccountSidLength); + + if (*AccountSid == NULL) { + + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + + } else { + + // + // Copy the domain sid into the first part of the account sid + // + + IgnoreStatus = RtlCopySid(AccountSidLength, *AccountSid, DomainSid); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + // + // Increment the account sid sub-authority count + // + + *RtlSubAuthorityCountSid(*AccountSid) = AccountSubAuthorityCount; + + // + // Add the rid as the final sub-authority + // + + RidLocation = RtlSubAuthoritySid(*AccountSid, AccountSubAuthorityCount-1); + *RidLocation = Rid; + + NtStatus = STATUS_SUCCESS; + } + + return(NtStatus); +} + + + +NTSTATUS +SampCreateAccountSid( + IN PSAMP_OBJECT AccountContext, + OUT PSID *AccountSid + ) + +/*++ + +Routine Description: + + This function creates the sid for an account object. + + The returned Sid may be freed with MIDL_user_free. + +Arguments: + + None. + +Return Value: + + STATUS_SUCCESS + +--*/ +{ + NTSTATUS NtStatus; + PSID DomainSid; + ULONG AccountRid; + + SAMTRACE("SampCreateAccountSid"); + + + // + // Get the Sid for the domain this object is in + // + + + DomainSid = SampDefinedDomains[AccountContext->DomainIndex].Sid; + + // + // Get the account Rid + // + + switch (AccountContext->ObjectType) { + + case SampGroupObjectType: + AccountRid = AccountContext->TypeBody.Group.Rid; + break; + case SampAliasObjectType: + AccountRid = AccountContext->TypeBody.Alias.Rid; + break; + case SampUserObjectType: + AccountRid = AccountContext->TypeBody.User.Rid; + break; + default: + ASSERT(FALSE); + } + + + // + // Build a full sid from the domain sid and the account rid + // + + NtStatus = SampCreateFullSid(DomainSid, AccountRid, AccountSid); + + return(NtStatus); +} + + +VOID +SampNotifyNetlogonOfDelta( + IN SECURITY_DB_DELTA_TYPE DeltaType, + IN SECURITY_DB_OBJECT_TYPE ObjectType, + IN ULONG ObjectRid, + IN PUNICODE_STRING ObjectName, + IN DWORD ReplicateImmediately, + IN PSAM_DELTA_DATA DeltaData OPTIONAL + ) + +/*++ + +Routine Description: + + This routine is called after any change is made to the SAM database + on a PDC. It will pass the parameters, along with the database type + and ModifiedCount to I_NetNotifyDelta() so that Netlogon will know + that the database has been changed. + + This routine MUST be called with SAM's write lock held; however, any + changes must have already been committed to disk. That is, call + SampCommitAndRetainWriteLock() first, then this routine, then + SampReleaseWriteLock(). + +Arguments: + + DeltaType - Type of modification that has been made on the object. + + ObjectType - Type of object that has been modified. + + ObjectRid - The relative ID of the object that has been modified. + This parameter is valid only when the object type specified is + either SecurityDbObjectSamUser, SecurityDbObjectSamGroup or + SecurityDbObjectSamAlias otherwise this parameter is set to + zero. + + ObjectName - The old name of the object when the object type specified + is either SecurityDbObjectSamUser, SecurityDbObjectSamGroup or + SecurityDbObjectSamAlias and the delta type is SecurityDbRename + otherwise this parameter is set to zero. + + ReplicateImmediately - TRUE if the change should be immediately + replicated to all BDCs. A password change should set the flag + TRUE. + + DeltaData - pointer to delta-type specific structure to be passed + - to netlogon. + +Return Value: + + None. + + +--*/ +{ + SAMTRACE("SampNotifyNetlogonOfDelta"); + + // + // Only make the call if this is not a backup domain controller. + // + + if ( SampDefinedDomains[SampTransactionDomainIndex].CurrentFixed.ServerRole + != DomainServerRoleBackup ) { + + I_NetNotifyDelta( + SecurityDbSam, + SampDefinedDomains[SampTransactionDomainIndex].CurrentFixed.ModifiedCount, + DeltaType, + ObjectType, + ObjectRid, + SampDefinedDomains[SampTransactionDomainIndex].Sid, + ObjectName, + ReplicateImmediately, + DeltaData + ); + + // + // Let any notification packages know about the delta. + // + + SampDeltaChangeNotify( + SampDefinedDomains[SampTransactionDomainIndex].Sid, + DeltaType, + ObjectType, + ObjectRid, + ObjectName, + &SampDefinedDomains[SampTransactionDomainIndex].CurrentFixed.ModifiedCount, + DeltaData + ); + + } +} + + + +NTSTATUS +SampSplitSid( + IN PSID AccountSid, + IN OUT PSID *DomainSid OPTIONAL, + OUT ULONG *Rid + ) + +/*++ + +Routine Description: + + This function splits a sid into its domain sid and rid. The caller + can either provide a memory buffer for the returned DomainSid, or + request that one be allocated. If the caller provides a buffer, the buffer + is assumed to be of sufficient size. If allocated on the caller's behalf, + the buffer must be freed when no longer required via MIDL_user_free. + +Arguments: + + AccountSid - Specifies the Sid to be split. The Sid is assumed to be + syntactically valid. Sids with zero subauthorities cannot be split. + + DomainSid - Pointer to location containing either NULL or a pointer to + a buffer in which the Domain Sid will be returned. If NULL is + specified, memory will be allocated on behalf of the caller. If this + paramter is NULL, only the account Rid is returned + +Return Value: + + NTSTATUS - Standard Nt Result Code + + STATUS_SUCCESS - The call completed successfully. + + STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources, + such as memory, to complete the call successfully. + + STATUS_INVALID_SID - The Sid is has a subauthority count of 0. +--*/ + +{ + NTSTATUS NtStatus; + UCHAR AccountSubAuthorityCount; + ULONG AccountSidLength; + + SAMTRACE("SampSplitSid"); + + // + // Calculate the size of the domain sid + // + + AccountSubAuthorityCount = *RtlSubAuthorityCountSid(AccountSid); + + + if (AccountSubAuthorityCount < 1) { + + NtStatus = STATUS_INVALID_SID; + goto SplitSidError; + } + + AccountSidLength = RtlLengthSid(AccountSid); + + + // + // Get Domain Sid if caller is intersted in it. + // + + if (DomainSid) + { + + // + // If no buffer is required for the Domain Sid, we have to allocate one. + // + + if (*DomainSid == NULL) { + + // + // Allocate space for the domain sid (allocate the same size as the + // account sid so we can use RtlCopySid) + // + + *DomainSid = MIDL_user_allocate(AccountSidLength); + + + if (*DomainSid == NULL) { + + NtStatus = STATUS_INSUFFICIENT_RESOURCES; + goto SplitSidError; + } + } + + // + // Copy the Account sid into the Domain sid + // + + RtlMoveMemory(*DomainSid, AccountSid, AccountSidLength); + + // + // Decrement the domain sid sub-authority count + // + + (*RtlSubAuthorityCountSid(*DomainSid))--; + } + + + // + // Copy the rid out of the account sid + // + + *Rid = *RtlSubAuthoritySid(AccountSid, AccountSubAuthorityCount-1); + + NtStatus = STATUS_SUCCESS; + +SplitSidFinish: + + return(NtStatus); + +SplitSidError: + + goto SplitSidFinish; +} + + + +NTSTATUS +SampDuplicateUnicodeString( + IN PUNICODE_STRING OutString, + IN PUNICODE_STRING InString + ) + +/*++ + +Routine Description: + + This routine allocates memory for a new OutString and copies the + InString string to it. + +Parameters: + + OutString - A pointer to a destination unicode string + + InString - A pointer to an unicode string to be copied + +Return Values: + + None. + +--*/ + +{ + SAMTRACE("SampDuplicateUnicodeString"); + + ASSERT( OutString != NULL ); + ASSERT( InString != NULL ); + + if ( InString->Length > 0 ) { + + OutString->Buffer = MIDL_user_allocate( InString->Length ); + + if (OutString->Buffer == NULL) { + return(STATUS_INSUFFICIENT_RESOURCES); + } + + OutString->MaximumLength = InString->Length; + + RtlCopyUnicodeString(OutString, InString); + + } else { + + RtlInitUnicodeString(OutString, NULL); + } + + return(STATUS_SUCCESS); +} + + +NTSTATUS +SampUnicodeToOemString( + IN POEM_STRING OutString, + IN PUNICODE_STRING InString + ) + +/*++ + +Routine Description: + + This routine allocates memory for a new OutString and copies the + InString string to it, converting to OEM string in the process. + +Parameters: + + OutString - A pointer to a destination OEM string. + + InString - A pointer to a unicode string to be copied + +Return Values: + + None. + +--*/ + +{ + ULONG + OemLength, + Index; + + NTSTATUS + NtStatus; + + SAMTRACE("SampUnicodeToOemString"); + + ASSERT( OutString != NULL ); + ASSERT( InString != NULL ); + + if ( InString->Length > 0 ) { + + OemLength = RtlUnicodeStringToOemSize(InString); + if ( OemLength > MAXUSHORT ) { + return STATUS_INVALID_PARAMETER_2; + } + + OutString->Length = (USHORT)(OemLength - 1); + OutString->MaximumLength = (USHORT)OemLength; + OutString->Buffer = MIDL_user_allocate(OemLength); + if ( !OutString->Buffer ) { + return STATUS_NO_MEMORY; + } + + NtStatus = RtlUnicodeToOemN( + OutString->Buffer, + OutString->Length, + &Index, + InString->Buffer, + InString->Length + ); + + if (!NT_SUCCESS(NtStatus)) { + MIDL_user_free(OutString->Buffer); + return NtStatus; + } + + OutString->Buffer[Index] = '\0'; + + + } else { + + RtlInitString(OutString, NULL); + } + + return(STATUS_SUCCESS); +} + + + +NTSTATUS +SampChangeAccountOperatorAccessToMember( + IN PRPC_SID MemberSid, + IN SAMP_MEMBERSHIP_DELTA ChangingToAdmin, + IN SAMP_MEMBERSHIP_DELTA ChangingToOperator + ) + +/*++ + +Routine Description: + + This routine is called when a member is added to or removed from an + ADMIN alias. If the member is from the BUILTIN or ACCOUNT domain, + it will change the ACL(s) of the member to allow or disallow access + by account operators if necessary. + + This must be called BEFORE the member is actually added to the + alias, and AFTER the member is actually removed from the alias to + avoid security holes in the event that we are unable to complete the + entire task. + + When this routine is called, the transaction domain is alredy set + to that of the alias. Note, however, that the member might be in a + different domain, so the transaction domain may be adjusted in this + routine. + + THIS SERVICE MUST BE CALLED WITH THE SampLock HELD FOR WRITE ACCESS. + + +Arguments: + + MemberSid - The full ID of the member being added to/ deleted from + an ADMIN alias. + + ChangingToAdmin - AddToAdmin if Member is being added to an ADMIN alias, + RemoveFromAdmin if it's being removed. + + ChangingToOperator - AddToAdmin if Member is being added to an OPERATOR + alias, RemoveFromAdmin if it's being removed. + + +Return Value: + + STATUS_SUCCESS - either the ACL(s) was modified, or it didn't need + to be. + +--*/ +{ + SAMP_V1_0A_FIXED_LENGTH_GROUP GroupV1Fixed; + PSID MemberDomainSid = NULL; + PULONG UsersInGroup = NULL; + NTSTATUS NtStatus; + ULONG MemberRid; + ULONG OldTransactionDomainIndex = SampDefinedDomainsCount; + ULONG NumberOfUsersInGroup; + ULONG i; + ULONG MemberDomainIndex; + SAMP_OBJECT_TYPE MemberType; + PSECURITY_DESCRIPTOR SecurityDescriptor; + PSECURITY_DESCRIPTOR OldDescriptor; + ULONG SecurityDescriptorLength; + ULONG Revision; + + SAMTRACE("SampChangeAccountOperatorAccessToMember"); + + ASSERT( SampTransactionWithinDomain ); + + // + // See if the SID is from one of the local domains (BUILTIN or ACCOUNT). + // If it's not, we don't have to worry about modifying ACLs. + // + + NtStatus = SampSplitSid( MemberSid, &MemberDomainSid, &MemberRid ); + + if ( !NT_SUCCESS( NtStatus ) ) { + + return( NtStatus ); + } + + for ( MemberDomainIndex = 0; + MemberDomainIndex < SampDefinedDomainsCount; + MemberDomainIndex++ ) { + + if ( RtlEqualSid( + MemberDomainSid, + SampDefinedDomains[MemberDomainIndex].Sid ) ) { + + break; + } + } + + if ( MemberDomainIndex < SampDefinedDomainsCount ) { + + // + // The member is from one of the local domains. MemberDomainIndex + // indexes that domain. First, check to see if the alias and member + // are in the same domain. + // + + if ( MemberDomainIndex != SampTransactionDomainIndex ) { + + // + // The transaction domain is set to that of the alias, but + // we need to set it to that of the member while we modify + // the member. + // + + SampTransactionWithinDomain = FALSE; + + OldTransactionDomainIndex = SampTransactionDomainIndex; + + SampSetTransactionDomain( MemberDomainIndex ); + } + + // + // Now we need to change the member ACL(s), IF the member is being + // added to an admin alias for the first time. Find out whether + // the member is a user or a group, and attack accordingly. + // + + NtStatus = SampLookupAccountName( + MemberRid, + NULL, + &MemberType + ); + + if (NT_SUCCESS(NtStatus)) { + + switch (MemberType) { + + case SampUserObjectType: { + + NtStatus = SampChangeOperatorAccessToUser( + MemberRid, + ChangingToAdmin, + ChangingToOperator + ); + + break; + } + + case SampGroupObjectType: { + + PSAMP_OBJECT GroupContext; + + // + // Change ACL for every user in this group. + // First get group member list. + // + + // + // Try to create a context for the account. + // + + NtStatus = SampCreateAccountContext( + SampGroupObjectType, + MemberRid, + TRUE, // Trusted client + TRUE, // Account exists + &GroupContext + ); + if (NT_SUCCESS(NtStatus)) { + + + // + // Now set a flag in the group itself, + // so that when users are added and removed + // in the future it is known whether this + // group is in an ADMIN alias or not. + // + + NtStatus = SampRetrieveGroupV1Fixed( + GroupContext, + &GroupV1Fixed + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + ULONG OldAdminStatus = 0; + ULONG NewAdminStatus; + + // SAM BUG 42367 FIX - ChrisMay 7/1/96 + + SAMP_MEMBERSHIP_DELTA AdminChange = NoChange; + SAMP_MEMBERSHIP_DELTA OperatorChange = NoChange; + + if (GroupV1Fixed.AdminCount != 0 ) { + OldAdminStatus++; + } + if (GroupV1Fixed.OperatorCount != 0) { + OldAdminStatus++; + } + NewAdminStatus = OldAdminStatus; + + // + // Update the admin count. If we added one and the + // count is now 1, then the group became administrative. + // If we subtracted one and the count is zero, + // then the group lost its administrive membership. + // + + if (ChangingToAdmin == AddToAdmin) { + if (++GroupV1Fixed.AdminCount == 1) { + NewAdminStatus++; + + // SAM BUG 42367 FIX - ChrisMay 7/1/96 + + AdminChange = AddToAdmin; + } + } else if (ChangingToAdmin == RemoveFromAdmin) { + + + // + // For removing an admin count, we need to make + // sure there is at least one. In the upgrade + // case there may not be, since prior versions + // of NT only had a boolean. + // + if (GroupV1Fixed.AdminCount > 0) { + if (--GroupV1Fixed.AdminCount == 0) { + NewAdminStatus --; + + // SAM BUG 42367 FIX - ChrisMay 7/1/96 + + AdminChange = RemoveFromAdmin; + } + } + + } + + // + // Update the operator count + // + + if (ChangingToOperator == AddToAdmin) { + if (++GroupV1Fixed.OperatorCount == 1) { + NewAdminStatus++; + + // SAM BUG 42367 FIX - ChrisMay 7/1/96 + + OperatorChange = AddToAdmin; + } + } else if (ChangingToOperator == RemoveFromAdmin) { + + + // + // For removing an Operator count, we need to make + // sure there is at least one. In the upgrade + // case there may not be, since prior versions + // of NT only had a boolean. + // + if (GroupV1Fixed.OperatorCount > 0) { + if (--GroupV1Fixed.OperatorCount == 0) { + NewAdminStatus --; + + // SAM BUG 42367 FIX - ChrisMay 7/1/96 + + OperatorChange = RemoveFromAdmin; + } + } + + } + + + NtStatus = SampReplaceGroupV1Fixed( + GroupContext, + &GroupV1Fixed + ); + // + // If the status of the group changed, + // modify the security descriptor to + // prevent account operators from adding + // anybody to this group + // + + if ( NT_SUCCESS( NtStatus ) && + ((NewAdminStatus != 0) != (OldAdminStatus != 0)) ) { + + // + // Get the old security descriptor so we can + // modify it. + // + + NtStatus = SampGetAccessAttribute( + GroupContext, + SAMP_GROUP_SECURITY_DESCRIPTOR, + FALSE, // don't make copy + &Revision, + &OldDescriptor + ); + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampModifyAccountSecurity( + SampGroupObjectType, + (BOOLEAN) ((NewAdminStatus != 0) ? TRUE : FALSE), + OldDescriptor, + &SecurityDescriptor, + &SecurityDescriptorLength + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // Write the new security descriptor into the object + // + + NtStatus = SampSetAccessAttribute( + GroupContext, + SAMP_GROUP_SECURITY_DESCRIPTOR, + SecurityDescriptor, + SecurityDescriptorLength + ); + + RtlDeleteSecurityObject( &SecurityDescriptor ); + } + } + } + + // + // Update all the members of this group so that + // their security descriptors are changed. + // + + // SAM BUG 42367 FIX - ChrisMay 7/1/96 + + #if 0 + + if ( NT_SUCCESS( NtStatus ) ) { + + NtStatus = SampRetrieveGroupMembers( + GroupContext, + &NumberOfUsersInGroup, + &UsersInGroup + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + for ( i = 0; i < NumberOfUsersInGroup; i++ ) { + + NtStatus = SampChangeOperatorAccessToUser( + UsersInGroup[i], + ChangingToAdmin, + ChangingToOperator + ); + + if ( !( NT_SUCCESS( NtStatus ) ) ) { + + break; + } + } + + MIDL_user_free( UsersInGroup ); + + } + + } + + #endif + + if (NT_SUCCESS(NtStatus) && + ((AdminChange != NoChange) || + (OperatorChange != NoChange))) { + + NtStatus = SampRetrieveGroupMembers( + GroupContext, + &NumberOfUsersInGroup, + &UsersInGroup + ); + + if (NT_SUCCESS(NtStatus)) { + + for (i = 0; i < NumberOfUsersInGroup; i++) { + + NtStatus = SampChangeOperatorAccessToUser( + UsersInGroup[i], + AdminChange, + OperatorChange + ); + + if (!(NT_SUCCESS(NtStatus))){ + + break; + } + } + + MIDL_user_free(UsersInGroup); + + } + + } + + if (NT_SUCCESS(NtStatus)) { + + // + // Add the modified group to the current transaction + // Don't use the open key handle since we'll be deleting the context. + // + + NtStatus = SampStoreObjectAttributes(GroupContext, FALSE); + } + + } + + + + // + // Clean up the group context + // + + SampDeleteContext(GroupContext); + } + + break; + } + + default: { + + // + // A bad RID from a domain other than the domain + // current at the time of the call could slip through + // to this point. Return error. + // + + // + // If the account is in a different domain than the alias, + // don't report an error if we're removing the member and + // the member no longer exists. + // + // Possibly caused by deleting the object before deleting + // the membership in the alias. + // + + // SAM BUG 42367 FIX - ChrisMay 7/1/96 + + #if 0 + + if ( (ChangingToAdmin == AddToAdmin) || + (ChangingToOperator == AddToAdmin) || + OldTransactionDomainIndex == SampDefinedDomainsCount ){ + NtStatus = STATUS_INVALID_MEMBER; + } else { + NtStatus = STATUS_SUCCESS; + } + + #endif + + NtStatus = STATUS_SUCCESS; + } + } + } + + if ( OldTransactionDomainIndex != SampDefinedDomainsCount ) { + + // + // The transaction domain should be set to that of the alias, but + // we switched it above to that of the member while we modified + // the member. Now we need to switch it back. + // + + SampTransactionWithinDomain = FALSE; + + SampSetTransactionDomain( OldTransactionDomainIndex ); + } + } + + MIDL_user_free( MemberDomainSid ); + + return( NtStatus ); +} + + +NTSTATUS +SampChangeOperatorAccessToUser( + IN ULONG UserRid, + IN SAMP_MEMBERSHIP_DELTA ChangingToAdmin, + IN SAMP_MEMBERSHIP_DELTA ChangingToOperator + ) + +/*++ + +Routine Description: + + This routine adjusts the user's AdminCount field as appropriate, and + if the user is being removed from it's last ADMIN alias or added to + its first ADMIN alias, the ACL is adjusted to allow/disallow access + by account operators as appropriate. + + This routine will also increment or decrement the domain's admin count, + if this operation changes that. + + NOTE: + This routine is similar to SampChangeOperatorAccessToUser2(). + This routine should be used in cases where a user context does NOT + already exist (and won't later on). You must be careful not to + create two contexts, since they will be independently applied back + to the registry, and the last one there will win. + + THIS SERVICE MUST BE CALLED WITH THE SampLock HELD FOR WRITE ACCESS. + +Arguments: + + UserRid - The transaction-domain-relative ID of the user that is + being added to or removed from an ADMIN alias. + + ChangingToAdmin - AddToAdmin if Member is being added to an ADMIN alias, + RemoveFromAdmin if it's being removed. + + ChangingToOperator - AddToAdmin if Member is being added to an OPERATOR + alias, RemoveFromAdmin if it's being removed. + + +Return Value: + + STATUS_SUCCESS - either the ACL was modified, or it didn't need + to be. + +--*/ +{ + SAMP_V1_0A_FIXED_LENGTH_USER UserV1aFixed; + NTSTATUS NtStatus; + PSAMP_OBJECT UserContext; + PSECURITY_DESCRIPTOR SecurityDescriptor; + ULONG SecurityDescriptorLength; + + SAMTRACE("SampChangeOperatorAccessToUser"); + + // + // Get the user's fixed data, and adjust the AdminCount. + // + + NtStatus = SampCreateAccountContext( + SampUserObjectType, + UserRid, + TRUE, // Trusted client + TRUE, // Account exists + &UserContext + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + NtStatus = SampRetrieveUserV1aFixed( + UserContext, + &UserV1aFixed + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + NtStatus = SampChangeOperatorAccessToUser2( + UserContext, + &UserV1aFixed, + ChangingToAdmin, + ChangingToOperator + ); + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // If we've succeeded (at changing the admin count, and + // the ACL if necessary) then write out the new admin + // count. + // + + NtStatus = SampReplaceUserV1aFixed( + UserContext, + &UserV1aFixed + ); + } + } + + if (NT_SUCCESS(NtStatus)) { + + // + // Add the modified user context to the current transaction + // Don't use the open key handle since we'll be deleting the context. + // + + NtStatus = SampStoreObjectAttributes(UserContext, FALSE); + } + + + // + // Clean up account context + // + + SampDeleteContext(UserContext); + } + + if ( ( !NT_SUCCESS( NtStatus ) ) && + (( ChangingToAdmin == RemoveFromAdmin ) || + ( ChangingToOperator == RemoveFromAdmin )) && + ( NtStatus != STATUS_SPECIAL_ACCOUNT ) ) { + + // + // When an account is *removed* from admin groups, we can + // ignore errors from this routine. This routine is just + // making the account accessible to account operators, but + // it's no big deal if that doesn't work. The administrator + // can still get at it, so we should proceed with the calling + // operation. + // + // Obviously, we can't ignore errors if we're being added + // to an admin group, because that could be a security hole. + // + // Also, we want to make sure that the Administrator is + // never removed, so we DO propogate STATUS_SPECIAL_ACCOUNT. + // + + NtStatus = STATUS_SUCCESS; + } + + return( NtStatus ); +} + + +NTSTATUS +SampChangeOperatorAccessToUser2( + IN PSAMP_OBJECT UserContext, + IN PSAMP_V1_0A_FIXED_LENGTH_USER V1aFixed, + IN SAMP_MEMBERSHIP_DELTA AddingToAdmin, + IN SAMP_MEMBERSHIP_DELTA AddingToOperator + ) + +/*++ + +Routine Description: + + This routine adjusts the user's AdminCount field as appropriate, and + if the user is being removed from it's last ADMIN alias or added to + its first ADMIN alias, the ACL is adjusted to allow/disallow access + by account operators as appropriate. + + This routine will also increment or decrement the domain's admin count, + if this operation changes that. + + NOTE: + This routine is similar to SampAccountOperatorAccessToUser(). + This routine should be used in cases where a user account context + already exists. You must be careful not to create two contexts, + since they will be independently applied back to the registry, and + the last one there will win. + + THIS SERVICE MUST BE CALLED WITH THE SampLock HELD FOR WRITE ACCESS. + +Arguments: + + UserContext - Context of user whose access is to be updated. + + V1aFixed - Pointer to the V1aFixed length data for the user. + The caller of this routine must ensure that this value is + stored back out to disk on successful completion of this + routine. + + AddingToAdmin - AddToAdmin if Member is being added to an ADMIN alias, + RemoveFromAdmin if it's being removed. + + AddingToOperator - AddToAdmin if Member is being added to an OPERATOR + alias, RemoveFromAdmin if it's being removed. + + +Return Value: + + STATUS_SUCCESS - either the ACL(s) was modified, or it didn't need + to be. + +--*/ +{ + NTSTATUS NtStatus; + PSECURITY_DESCRIPTOR OldDescriptor; + PSECURITY_DESCRIPTOR SecurityDescriptor; + ULONG SecurityDescriptorLength; + ULONG OldAdminStatus = 0, NewAdminStatus = 0; + ULONG Revision; + + SAMTRACE("SampChangeOperatorAccessToUser2"); + + // + // Compute whether we are an admin now. From that we will figure + // out how many times we were may not an admin to tell if we need + // to update the security descriptor. + // + + if (V1aFixed->AdminCount != 0) { + OldAdminStatus++; + } + if (V1aFixed->OperatorCount != 0) { + OldAdminStatus++; + } + + NewAdminStatus = OldAdminStatus; + + + + if ( AddingToAdmin == AddToAdmin ) { + + V1aFixed->AdminCount++; + NewAdminStatus++; + SampDiagPrint( DISPLAY_ADMIN_CHANGES, + ("SAM DIAG: Incrementing admin count for user %d\n" + " New admin count: %d\n", + V1aFixed->UserId, V1aFixed->AdminCount ) ); + } else if (AddingToAdmin == RemoveFromAdmin) { + + V1aFixed->AdminCount--; + + if (V1aFixed->AdminCount == 0) { + NewAdminStatus--; + } + + SampDiagPrint( DISPLAY_ADMIN_CHANGES, + ("SAM DIAG: Decrementing admin count for user %d\n" + " New admin count: %d\n", + V1aFixed->UserId, V1aFixed->AdminCount ) ); + + if ( V1aFixed->AdminCount == 0 ) { + + // + // Don't allow the Administrator account to lose + // administrative power. + // + + if ( V1aFixed->UserId == DOMAIN_USER_RID_ADMIN ) { + + NtStatus = STATUS_SPECIAL_ACCOUNT; + } + } + } + if ( AddingToOperator == AddToAdmin ) { + + V1aFixed->OperatorCount++; + NewAdminStatus++; + SampDiagPrint( DISPLAY_ADMIN_CHANGES, + ("SAM DIAG: Incrementing operator count for user %d\n" + " New admin count: %d\n", + V1aFixed->UserId, V1aFixed->OperatorCount ) ); + + } else if (AddingToOperator == RemoveFromAdmin) { + + // + // Only decrement if the count is > 0, since in the upgrade case + // this field we start out zero. + // + + if (V1aFixed->OperatorCount > 0) { + V1aFixed->OperatorCount--; + + if (V1aFixed->OperatorCount == 0) { + NewAdminStatus--; + } + } + + SampDiagPrint( DISPLAY_ADMIN_CHANGES, + ("SAM DIAG: Decrementing operator count for user %d\n" + " New admin count: %d\n", + V1aFixed->UserId, V1aFixed->OperatorCount ) ); + } + + + if (NT_SUCCESS(NtStatus)) { + + if ( ( NewAdminStatus != 0 ) != ( OldAdminStatus != 0 ) ) { + + // + // User's admin status is changing. We must change the + // ACL. + // + +#ifdef SAMP_DIAGNOSTICS + if (AddingToAdmin) { + SampDiagPrint( DISPLAY_ADMIN_CHANGES, + ("SAM DIAG: Protecting user %d as ADMIN account\n", + V1aFixed->UserId ) ); + } else { + SampDiagPrint( DISPLAY_ADMIN_CHANGES, + ("SAM DIAG: Protecting user %d as non-admin account\n", + V1aFixed->UserId ) ); + } +#endif // SAMP_DIAGNOSTICS + + // + // Get the old security descriptor so we can + // modify it. + // + + NtStatus = SampGetAccessAttribute( + UserContext, + SAMP_USER_SECURITY_DESCRIPTOR, + FALSE, // don't make copy + &Revision, + &OldDescriptor + ); + if (NT_SUCCESS(NtStatus)) { + + NtStatus = SampModifyAccountSecurity( + SampUserObjectType, + (BOOLEAN) ((NewAdminStatus != 0) ? TRUE : FALSE), + OldDescriptor, + &SecurityDescriptor, + &SecurityDescriptorLength + ); + } + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // Write the new security descriptor into the object + // + + NtStatus = SampSetAccessAttribute( + UserContext, + SAMP_USER_SECURITY_DESCRIPTOR, + SecurityDescriptor, + SecurityDescriptorLength + ); + + RtlDeleteSecurityObject( &SecurityDescriptor ); + } + } + } + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // Save the fixed-length attributes + // + + NtStatus = SampReplaceUserV1aFixed( + UserContext, + V1aFixed + ); + } + + + if ( ( !NT_SUCCESS( NtStatus ) ) && + ( AddingToAdmin != AddToAdmin ) && + ( NtStatus != STATUS_SPECIAL_ACCOUNT ) ) { + + // + // When an account is *removed* from admin groups, we can + // ignore errors from this routine. This routine is just + // making the account accessible to account operators, but + // it's no big deal if that doesn't work. The administrator + // can still get at it, so we should proceed with the calling + // operation. + // + // Obviously, we can't ignore errors if we're being added + // to an admin group, because that could be a security hole. + // + // Also, we want to make sure that the Administrator is + // never removed, so we DO propogate STATUS_SPECIAL_ACCOUNT. + // + + NtStatus = STATUS_SUCCESS; + } + + return( NtStatus ); +} + + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Services Private to this process // +// // +/////////////////////////////////////////////////////////////////////////////// + + +NTSTATUS +SamINotifyDelta ( + IN SAMPR_HANDLE DomainHandle, + IN SECURITY_DB_DELTA_TYPE DeltaType, + IN SECURITY_DB_OBJECT_TYPE ObjectType, + IN ULONG ObjectRid, + IN PUNICODE_STRING ObjectName, + IN DWORD ReplicateImmediately, + IN PSAM_DELTA_DATA DeltaData OPTIONAL + ) + +/*++ + +Routine Description: + + Performs a change to some 'virtual' data in a domain. This is used by + netlogon to get the domain modification count updated for cases where + fields stored in the database replicated to a down-level machine have + changed. These fields don't exist in the NT SAM database but netlogon + needs to keep the SAM database and the down-level database modification + counts in sync. + +Arguments: + + DomainHandle - The handle of an opened domain to operate on. + + All other parameters match those in I_NetNotifyDelta. + + +Return Value: + + + STATUS_SUCCESS - Domain modification count updated successfully. + + +--*/ +{ + NTSTATUS NtStatus, IgnoreStatus; + PSAMP_OBJECT DomainContext; + SAMP_OBJECT_TYPE FoundType; + + SAMTRACE("SamINotifyDelta"); + + + NtStatus = SampAcquireWriteLock(); + if (!NT_SUCCESS(NtStatus)) { + return(NtStatus); + } + + + // + // Validate type of, and access to object. + // + + DomainContext = (PSAMP_OBJECT)DomainHandle; + NtStatus = SampLookupContext( + DomainContext, + DOMAIN_ALL_ACCESS, // Trusted client should succeed + SampDomainObjectType, // ExpectedType + &FoundType + ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Dump the context - don't save the non-existent changes + // + + NtStatus = SampDeReferenceContext( DomainContext, FALSE ); + } + + + + + + // + // Commit changes, if successful, and notify Netlogon of changes. + // + + if ( NT_SUCCESS(NtStatus) ) { + + // + // This will increment domain count and write it out + // + + NtStatus = SampCommitAndRetainWriteLock(); + + if ( NT_SUCCESS( NtStatus ) ) { + + SampNotifyNetlogonOfDelta( + DeltaType, + ObjectType, + ObjectRid, + ObjectName, + ReplicateImmediately, + DeltaData + ); + } + } + + IgnoreStatus = SampReleaseWriteLock( FALSE ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + + return(NtStatus); +} + + +NTSTATUS +SamISetAuditingInformation( + IN PPOLICY_AUDIT_EVENTS_INFO PolicyAuditEventsInfo + ) + +/*++ + +Routine Description: + + This function sets Policy Audit Event Info relevant to SAM Auditing + +Arguments: + + PolicyAuditEventsInfo - Pointer to structure containing the + current Audit Events Information. SAM extracts values of + relevance. + +Return Value: + + NTSTATUS - Standard Nt Result Code + + STATUS_SUCCESSFUL - The call completed successfully. + + STATUS_UNSUCCESSFUL - The call was not successful because the + SAM lock was not acquired. +--*/ + +{ + NTSTATUS NtStatus; + + SAMTRACE("SamISetAuditingInformation"); + + // + // Acquire the SAM Database Write Lock. + // + + NtStatus = SampAcquireWriteLock(); + + if (NT_SUCCESS(NtStatus)) { + + // + // Set boolean if Auditing is on for Account Management + // + + SampSetAuditingInformation( PolicyAuditEventsInfo ); + + // + // Release the SAM Database Write Lock. No need to commit + // the database transaction as there are no entries in the + // transaction log. + // + + NtStatus = SampReleaseWriteLock( FALSE ); + } + + return(NtStatus); +} + + +NTSTATUS +SampRtlConvertUlongToUnicodeString( + IN ULONG Value, + IN ULONG Base OPTIONAL, + IN ULONG DigitCount, + IN BOOLEAN AllocateDestinationString, + OUT PUNICODE_STRING UnicodeString + ) + +/*++ + +Routine Description: + + This function converts an unsigned long integer a Unicode String. + The string contains leading zeros and is Unicode-NULL terminated. + Memory for the output buffer can optionally be allocated by the routine. + + NOTE: This routine may be eligible for inclusion in the Rtl library + (possibly after modification). It is modeled on + RtlIntegerToUnicodeString + +Arguments: + + Value - The unsigned long value to be converted. + + Base - Specifies the radix that the converted string is to be + converted to. + + DigitCount - Specifies the number of digits, including leading zeros + required for the result. + + AllocateDestinationString - Specifies whether memory of the string + buffer is to be allocated by this routine. If TRUE is specified, + memory will be allocated via MIDL_user_allocate(). When this memory + is no longer required, it must be freed via MIDL_user_free. If + FALSE is specified, the string will be appended to the output + at the point marked by the Length field onwards. + + UnicodeString - Pointer to UNICODE_STRING structure which will receive + the output string. The Length field will be set equal to the + number of bytes occupied by the string (excluding NULL terminator). + If memory for the destination string is being allocated by + the routine, the MaximumLength field will be set equal to the + length of the string in bytes including the null terminator. + +Return Values: + + NTSTATUS - Standard Nt Result Code. + + STATUS_SUCCESS - The call completed successfully. + + STATUS_NO_MEMORY - Insufficient memory for the output string buffer. + + STATUS_BUFFER_OVERFLOW - Buffer supplied is too small to contain the + output null-terminated string. + + STATUS_INVALID_PARAMETER_MIX - One or more parameters are + invalid in combination. + + - The specified Relative Id is too large to fit when converted + into an integer with DigitCount digits. + + STATUS_INVALID_PARAMETER - One or more parameters are invalid. + + - DigitCount specifies too large a number of digits. +--*/ + +{ + NTSTATUS NtStatus; + UNICODE_STRING TempStringU, NumericStringU, OutputUnicodeStringU; + USHORT OutputLengthAvailable, OutputLengthRequired, LeadingZerosLength; + + SAMTRACE("SamRtlConvertUlongToUnicodeString"); + + OutputUnicodeStringU = *UnicodeString; + + // + // Verify that the maximum number of digits rquested has not been + // exceeded. + // + + if (DigitCount > SAMP_MAXIMUM_ACCOUNT_RID_DIGITS) { + + goto ConvertUlongToUnicodeStringError; + } + + OutputLengthRequired = (USHORT)((DigitCount + 1) * sizeof(WCHAR)); + + // + // Allocate the Destination String Buffer if requested + // + + if (AllocateDestinationString) { + + NtStatus = STATUS_NO_MEMORY; + OutputUnicodeStringU.MaximumLength = OutputLengthRequired; + OutputUnicodeStringU.Length = (USHORT) 0; + + OutputUnicodeStringU.Buffer = MIDL_user_allocate( + OutputUnicodeStringU.MaximumLength + ); + + if (OutputUnicodeStringU.Buffer == NULL) { + + goto ConvertUlongToUnicodeStringError; + } + } + + // + // Compute the length available in the output string and compare it with + // the length required. + // + + OutputLengthAvailable = OutputUnicodeStringU.MaximumLength - + OutputUnicodeStringU.Length; + + + NtStatus = STATUS_BUFFER_OVERFLOW; + + if (OutputLengthRequired > OutputLengthAvailable) { + + goto ConvertUlongToUnicodeStringError; + } + + // + // Create a Unicode String with capacity equal to the required + // converted Rid Length + // + + TempStringU.MaximumLength = OutputLengthRequired; + + TempStringU.Buffer = MIDL_user_allocate( TempStringU.MaximumLength ); + + NtStatus = STATUS_NO_MEMORY; + + if (TempStringU.Buffer == NULL) { + + goto ConvertUlongToUnicodeStringError; + } + + // + // Convert the unsigned long value to a hexadecimal Unicode String. + // + + NtStatus = RtlIntegerToUnicodeString( Value, Base, &TempStringU ); + + if (!NT_SUCCESS(NtStatus)) { + + goto ConvertUlongToUnicodeStringError; + } + + // + // Prepend the requisite number of Unicode Zeros. + // + + LeadingZerosLength = OutputLengthRequired - sizeof(WCHAR) - TempStringU.Length; + + if (LeadingZerosLength > 0) { + + RtlInitUnicodeString( &NumericStringU, L"00000000000000000000000000000000" ); + + RtlCopyMemory( + ((PUCHAR)OutputUnicodeStringU.Buffer) + OutputUnicodeStringU.Length, + NumericStringU.Buffer, + LeadingZerosLength + ); + + OutputUnicodeStringU.Length += LeadingZerosLength; + } + + // + // Append the converted string + // + + RtlAppendUnicodeStringToString( &OutputUnicodeStringU, &TempStringU); + + *UnicodeString = OutputUnicodeStringU; + NtStatus = STATUS_SUCCESS; + +ConvertUlongToUnicodeStringFinish: + + if (TempStringU.Buffer != NULL) { + + MIDL_user_free( TempStringU.Buffer); + } + + return(NtStatus); + +ConvertUlongToUnicodeStringError: + + if (AllocateDestinationString) { + + if (OutputUnicodeStringU.Buffer != NULL) { + + MIDL_user_free( OutputUnicodeStringU.Buffer); + } + } + + goto ConvertUlongToUnicodeStringFinish; +} + + +NTSTATUS +SampRtlWellKnownPrivilegeCheck( + BOOLEAN ImpersonateClient, + IN ULONG PrivilegeId, + IN OPTIONAL PCLIENT_ID ClientId + ) + +/*++ + +Routine Description: + + This function checks if the given well known privilege is enabled for an + impersonated client or for the current process. + +Arguments: + + ImpersonateClient - If TRUE, impersonate the client. If FALSE, don't + impersonate the client (we may already be doing so). + + PrivilegeId - Specifies the well known Privilege Id + + ClientId - Specifies the client process/thread Id. If already + impersonating the client, or impersonation is requested, this + parameter should be omitted. + +Return Value: + + NTSTATUS - Standard Nt Result Code + + STATUS_SUCCESS - The call completed successfully and the client + is either trusted or has the necessary privilege enabled. + +--*/ + +{ + NTSTATUS Status, SecondaryStatus; + BOOLEAN PrivilegeHeld = FALSE; + HANDLE ClientThread = NULL, ClientProcess = NULL, ClientToken = NULL; + OBJECT_ATTRIBUTES NullAttributes; + PRIVILEGE_SET Privilege; + BOOLEAN ClientImpersonatedHere = FALSE; + + SAMTRACE("SampRtlWellKnownPrivilegeCheck"); + + InitializeObjectAttributes( &NullAttributes, NULL, 0, NULL, NULL ); + + // + // If requested, impersonate the client. + // + + if (ImpersonateClient) { + + Status = I_RpcMapWin32Status(RpcImpersonateClient( NULL )); + + if ( !NT_SUCCESS(Status) ) { + + goto WellKnownPrivilegeCheckError; + } + + ClientImpersonatedHere = TRUE; + } + + // + // If a client process other than ourself has been specified , open it + // for query information access. + // + + if (ARGUMENT_PRESENT(ClientId)) { + + if (ClientId->UniqueProcess != NtCurrentProcess()) { + + Status = NtOpenProcess( + &ClientProcess, + PROCESS_QUERY_INFORMATION, // To open primary token + &NullAttributes, + ClientId + ); + + if ( !NT_SUCCESS(Status) ) { + + goto WellKnownPrivilegeCheckError; + } + + } else { + + ClientProcess = NtCurrentProcess(); + } + } + + // + // If a client thread other than ourself has been specified , open it + // for query information access. + // + + if (ARGUMENT_PRESENT(ClientId)) { + + if (ClientId->UniqueThread != NtCurrentThread()) { + + Status = NtOpenThread( + &ClientThread, + THREAD_QUERY_INFORMATION, + &NullAttributes, + ClientId + ); + + if ( !NT_SUCCESS(Status) ) { + + goto WellKnownPrivilegeCheckError; + } + + } else { + + ClientThread = NtCurrentThread(); + } + + } else { + + ClientThread = NtCurrentThread(); + } + + // + // Open the specified or current thread's impersonation token (if any). + // + + Status = NtOpenThreadToken( + ClientThread, + TOKEN_QUERY, + TRUE, + &ClientToken + ); + + + // + // Make sure that we did not get any error in opening the impersonation + // token other than that the token doesn't exist. + // + + if ( !NT_SUCCESS(Status) ) { + + if ( Status != STATUS_NO_TOKEN ) { + + goto WellKnownPrivilegeCheckError; + } + + // + // The thread isn't impersonating...open the process's token. + // A process Id must have been specified in the ClientId information + // in this case. + // + + if (ClientProcess == NULL) { + + Status = STATUS_INVALID_PARAMETER; + goto WellKnownPrivilegeCheckError; + } + + Status = NtOpenProcessToken( + ClientProcess, + TOKEN_QUERY, + &ClientToken + ); + + // + // Make sure we succeeded in opening the token + // + + if ( !NT_SUCCESS(Status) ) { + + goto WellKnownPrivilegeCheckError; + } + } + + // + // OK, we have a token open. Now check for the privilege to execute this + // service. + // + + Privilege.PrivilegeCount = 1; + Privilege.Control = PRIVILEGE_SET_ALL_NECESSARY; + Privilege.Privilege[0].Luid = RtlConvertLongToLuid(PrivilegeId); + Privilege.Privilege[0].Attributes = 0; + + Status = NtPrivilegeCheck( + ClientToken, + &Privilege, + &PrivilegeHeld + ); + + if (!NT_SUCCESS(Status)) { + + goto WellKnownPrivilegeCheckError; + } + + // + // Generate any necessary audits + // + + SecondaryStatus = NtPrivilegedServiceAuditAlarm ( + &SampSamSubsystem, + &SampSamSubsystem, + ClientToken, + &Privilege, + PrivilegeHeld + ); + // ASSERT( NT_SUCCESS(SecondaryStatus) ); + + + if ( !PrivilegeHeld ) { + + Status = STATUS_PRIVILEGE_NOT_HELD; + goto WellKnownPrivilegeCheckError; + } + +WellKnownPrivilegeCheckFinish: + + // + // If we impersonated the client, revert to ourself. + // + + if (ClientImpersonatedHere) { + + SecondaryStatus = I_RpcMapWin32Status(RpcRevertToSelf()); + } + + // + // If necessary, close the client Process. + // + + if ((ARGUMENT_PRESENT(ClientId)) && + (ClientId->UniqueProcess != NtCurrentProcess()) && + (ClientProcess != NULL)) { + + SecondaryStatus = NtClose( ClientProcess ); + ASSERT(NT_SUCCESS(SecondaryStatus)); + ClientProcess = NULL; + } + + // + // If necessary, close the client token. + // + + if (ClientToken != NULL) { + + SecondaryStatus = NtClose( ClientToken ); + ASSERT(NT_SUCCESS(SecondaryStatus)); + ClientToken = NULL; + } + + // + // If necessary, close the client thread + // + + if ((ARGUMENT_PRESENT(ClientId)) && + (ClientId->UniqueThread != NtCurrentThread()) && + (ClientThread != NULL)) { + + SecondaryStatus = NtClose( ClientThread ); + ASSERT(NT_SUCCESS(SecondaryStatus)); + ClientThread = NULL; + } + + return(Status); + +WellKnownPrivilegeCheckError: + + goto WellKnownPrivilegeCheckFinish; +} + + +VOID +SampWriteEventLog ( + IN USHORT EventType, + IN USHORT EventCategory OPTIONAL, + IN ULONG EventID, + IN PSID UserSid OPTIONAL, + IN USHORT NumStrings, + IN ULONG DataSize, + IN PUNICODE_STRING *Strings OPTIONAL, + IN PVOID Data OPTIONAL + ) + +/*++ + +Routine Description: + + Routine that adds an entry to the event log + +Arguments: + + EventType - Type of event. + + EventCategory - EventCategory + + EventID - event log ID. + + UserSid - SID of user involved. + + NumStrings - Number of strings in Strings array + + DataSize - Number of bytes in Data buffer + + Strings - Array of unicode strings + + Data - Pointer to data buffer + +Return Value: + + None. + +--*/ + +{ + NTSTATUS NtStatus; + UNICODE_STRING Source; + HANDLE LogHandle; + + SAMTRACE("SampWriteEventLog"); + + RtlInitUnicodeString(&Source, L"SAM"); + + // + // Open the log + // + + NtStatus = ElfRegisterEventSourceW ( + NULL, // Server + &Source, + &LogHandle + ); + if (!NT_SUCCESS(NtStatus)) { + KdPrint(("SAM: Failed to registry event source with event log, status = 0x%lx\n", NtStatus)); + return; + } + + + + // + // Write out the event + // + + NtStatus = ElfReportEventW ( + LogHandle, + EventType, + EventCategory, + EventID, + UserSid, + NumStrings, + DataSize, + Strings, + Data, + 0, // Flags + NULL, // Record Number + NULL // Time written + ); + + if (!NT_SUCCESS(NtStatus)) { + KdPrint(("SAM: Failed to report event to event log, status = 0x%lx\n", NtStatus)); + } + + + + // + // Close the event log + // + + NtStatus = ElfDeregisterEventSource (LogHandle); + + if (!NT_SUCCESS(NtStatus)) { + KdPrint(("SAM: Failed to de-register event source with event log, status = 0x%lx\n", NtStatus)); + } +} + + +BOOL +SampShutdownNotification( + DWORD dwCtrlType + ) + +/*++ + +Routine Description: + + This routine is called by the system when system shutdown is occuring. + + It causes the SAM registry to be flushed if necessary. + +Arguments: + + + +Return Value: + + FALSE - to allow any other shutdown routines in this process to + also be called. + + + +--*/ +{ + NTSTATUS + NtStatus; + + SAMTRACE("SampShutdownNotification"); + + + if (dwCtrlType == CTRL_SHUTDOWN_EVENT) { + + + // + // Don't wait for the flush thread to wake up. + // Flush the registry now if necessary ... + // + + NtStatus = SampAcquireWriteLock(); + ASSERT( NT_SUCCESS(NtStatus) ); //Nothing we can do if this fails + + if ( NT_SUCCESS( NtStatus ) ) { + + // + // This flush use to be done only if FlushThreadCreated + // was true. However, we seem to have a race condition + // at setup that causes an initial replication to be + // lost (resulting in an additional replication). + // Until we resolve this problem, always flush on + // shutdown. + // + + NtStatus = NtFlushKey( SampKey ); + + if (!NT_SUCCESS( NtStatus )) { + DbgPrint("NtFlushKey failed, Status = %X\n",NtStatus); +// ASSERT( NT_SUCCESS(NtStatus) ); + } + + SampReleaseWriteLock( FALSE ); + } + + + } + return(FALSE); +} + + +NTSTATUS +SampGetAccountDomainInfo( + PPOLICY_ACCOUNT_DOMAIN_INFO *PolicyAccountDomainInfo + ) + +/*++ + +Routine Description: + + This routine retrieves ACCOUNT domain information from the LSA + policy database. + + +Arguments: + + PolicyAccountDomainInfo - Receives a pointer to a + POLICY_ACCOUNT_DOMAIN_INFO structure containing the account + domain info. + + + +Return Value: + + STATUS_SUCCESS - Succeeded. + + Other status values that may be returned from: + + LsarQueryInformationPolicy() +--*/ + +{ + NTSTATUS + NtStatus, + IgnoreStatus; + + LSAPR_HANDLE + PolicyHandle; + + SAMTRACE("SampGetAccountDomainInfo"); + + + NtStatus = LsaIOpenPolicyTrusted( &PolicyHandle ); + + if (NT_SUCCESS(NtStatus)) { + + // + // Query the account domain information + // + + NtStatus = LsarQueryInformationPolicy( + PolicyHandle, + PolicyAccountDomainInformation, + (PLSAPR_POLICY_INFORMATION *)PolicyAccountDomainInfo + ); + + if (NT_SUCCESS(NtStatus)) { + + if ( (*PolicyAccountDomainInfo)->DomainSid == NULL ) { + + NtStatus = STATUS_INVALID_SID; + } + } + + IgnoreStatus = LsarClose( &PolicyHandle ); + ASSERT(NT_SUCCESS(IgnoreStatus)); + + } + +#if DBG + if ( NT_SUCCESS(NtStatus) ) { + ASSERT( (*PolicyAccountDomainInfo) != NULL ); + ASSERT( (*PolicyAccountDomainInfo)->DomainName.Buffer != NULL ); + } +#endif //DBG + + return(NtStatus); +} + + diff --git a/private/newsam2/test/main.c b/private/newsam2/test/main.c new file mode 100644 index 000000000..16451bf59 --- /dev/null +++ b/private/newsam2/test/main.c @@ -0,0 +1,82 @@ +#include <nt.h> +#include <ntrtl.h> +#include <nturtl.h> +#include <ntsam.h> +#include <ntlsa.h> +#include <windows.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#define ATTRIB_DBG_PRINTF 1 + +#if (ATTRIB_DBG_PRINTF == 1) +#define DebugPrint printf +#else +#define DebugPrint +#endif + +SAM_HANDLE SamHandle; +SAM_HANDLE DomainHandle; +SAM_HANDLE GroupHandle; +SAM_HANDLE AliasHandle; +SAM_HANDLE UserHandle; + +NTSTATUS +Connect(PWSTR Server) +{ + NTSTATUS NtStatus = STATUS_SEVERITY_ERROR; + OBJECT_ATTRIBUTES ObjectAttributes; + UNICODE_STRING ServerName; + + RtlZeroMemory(&ServerName, sizeof(UNICODE_STRING)); + RtlInitUnicodeString(&ServerName, Server); + InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL); + NtStatus = SamConnect(&ServerName, + &SamHandle, + MAXIMUM_ALLOWED, + &ObjectAttributes); + + return(NtStatus); +} + +NTSTATUS +Close() +{ + return(SamCloseHandle(SamHandle)); +} + +void +_cdecl +main( + int argc, + char *argv[] + ) +{ + NTSTATUS NtStatus = STATUS_SEVERITY_ERROR; + INT arg = 1; + WCHAR Parameter[128]; + + while(arg < argc) + { + if (0 == _stricmp(argv[arg], "-c")) + { + arg++; + DebugPrint("Server name = %s\n", argv[arg]); + mbstowcs(Parameter, argv[arg], 128); + NtStatus = Connect(Parameter); + DebugPrint("Connect status = 0x%lx\n", NtStatus); + arg++; + } + else if (0 == _stricmp(argv[arg], "close")) + { + arg++; + NtStatus = Close(); + DebugPrint("Close status = 0x%lx\n", NtStatus); + } + } + + NtStatus = Close(); + + exit(0); +} diff --git a/private/newsam2/test/makefile b/private/newsam2/test/makefile new file mode 100644 index 000000000..bdd684681 --- /dev/null +++ b/private/newsam2/test/makefile @@ -0,0 +1,29 @@ +############################################################################ +# +# Copyright (C) 1992, Microsoft Corporation. +# +# All rights reserved. +# +############################################################################ + +!if "$(NTMAKEENV)" != "" + +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def + +!else + + +default: all + +!include filelist.mk +!include $(SECURITY)\security.mk +!include $(COMMON)\src\win40.mk +!include depend.mk + + +!endif diff --git a/private/newsam2/test/sources b/private/newsam2/test/sources new file mode 100644 index 000000000..757b0b226 --- /dev/null +++ b/private/newsam2/test/sources @@ -0,0 +1,22 @@ +TARGETNAME=sambvt + +TARGETPATH=obj + +TARGETTYPE=PROGRAM + +UMTYPE=console + +INCLUDES=..\..\..\inc + +SOURCES= main.c version.rc + +TARGETLIBS= \ + $(BASEDIR)\public\sdk\lib\*\advapi32.lib \ + $(BASEDIR)\public\sdk\lib\*\samlib.lib \ + $(BASEDIR)\public\sdk\lib\*\ntdll.lib + + +C_DEFINES=$(C_DEFINES) -DRPC_NO_WINDOWS_H -DUNICODE + +USE_CRTDLL=1 + diff --git a/private/newsam2/test/version.rc b/private/newsam2/test/version.rc new file mode 100644 index 000000000..ed461d3bd --- /dev/null +++ b/private/newsam2/test/version.rc @@ -0,0 +1,10 @@ +#include <windows.h> +#include <ntverp.h> + +#define VER_FILETYPE VFT_APP +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "TestApplication" +#define VER_INTERNALNAME_STR "sambvt.exe" +#define VER_ORIGINALFILENAME_STR "sambvt.exe" + +#include "common.ver" |