1611 lines
37 KiB
C
1611 lines
37 KiB
C
/*++
|
||
|
||
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);
|
||
}
|