windows-nt/Source/XPSP1/NT/ds/security/dsrole/server/lsa.c
2020-09-26 16:20:57 +08:00

845 lines
22 KiB
C

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
setutl.c
Abstract:
Miscellaneous helper functions
Author:
Mac McLain (MacM) Feb 10, 1997
Environment:
User Mode
Revision History:
--*/
#include <setpch.h>
#include <dssetp.h>
#include <lsarpc.h>
#include <samrpc.h>
#include <samisrv.h>
#include <db.h>
#include <confname.h>
#include <loadfn.h>
#include <ntdsa.h>
#include <dsconfig.h>
#include <attids.h>
#include <dsp.h>
#include <lsaisrv.h>
#include <malloc.h>
#include <dsgetdc.h>
#include <lmcons.h>
#include <lmaccess.h>
#include <lmapibuf.h>
#include <lmerr.h>
#include <netsetp.h>
#include <winsock2.h>
#include <nspapi.h>
#include <dsgetdcp.h>
#include <lmremutl.h>
#include <spmgr.h> // For SetupPhase definition
#include <Sddl.h>
#include "secure.h"
#include "lsa.h"
DWORD
DsRolepSetLsaInformationForReplica(
IN HANDLE CallerToken,
IN LPWSTR ReplicaPartner,
IN LPWSTR Account,
IN LPWSTR Password
)
/*++
Routine Description:
This function will set the local Lsa database information to that of the replica partner
Arguments:
ReplicaPartner -- Replica partner to get the information from
Returns:
ERROR_SUCCESS - Success
--*/
{
DWORD Win32Err = ERROR_SUCCESS;
NTSTATUS Status;
UNICODE_STRING PartnerServer;
HANDLE LocalPolicy = NULL , PartnerPolicy = NULL;
OBJECT_ATTRIBUTES ObjectAttributes;
PBYTE Buffer;
ULONG i;
BOOLEAN UseAdded = FALSE;
PWSTR FullServerPath = NULL;
POLICY_INFORMATION_CLASS InfoClasses[ ] = {
PolicyDnsDomainInformation
};
if ( !ReplicaPartner ) {
return( ERROR_INVALID_PARAMETER );
}
DSROLEP_CURRENT_OP1( DSROLEEVT_SET_LSA_FROM, ReplicaPartner );
//
// Open both lsas
//
RtlInitUnicodeString( &PartnerServer, ReplicaPartner );
RtlZeroMemory( &ObjectAttributes, sizeof( ObjectAttributes ) );
Status = ImpLsaOpenPolicy( CallerToken,
&PartnerServer,
&ObjectAttributes,
MAXIMUM_ALLOWED,
&PartnerPolicy
);
if ( Status == STATUS_ACCESS_DENIED ) {
WCHAR *BufPartnerServer = NULL;
BufPartnerServer = (WCHAR*)malloc(PartnerServer.Length+sizeof(WCHAR));
if (BufPartnerServer) {
CopyMemory(BufPartnerServer,PartnerServer.Buffer,PartnerServer.Length);
BufPartnerServer[PartnerServer.Length/sizeof(WCHAR)] = L'\0';
DsRolepLogPrint(( DEB_TRACE,
"LsaOpenPolicy on %ws failed with 0x%lx. Establishing use.\n",
BufPartnerServer, Status ));
free(BufPartnerServer);
}
//
// Try establishing a session first...
//
if ( *ReplicaPartner != L'\\' ) {
FullServerPath = RtlAllocateHeap( RtlProcessHeap(), 0,
( wcslen( ReplicaPartner ) + 3 ) * sizeof( WCHAR ) );
if ( FullServerPath == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
swprintf( FullServerPath, L"\\\\%ws", ReplicaPartner );
Status = STATUS_SUCCESS;
}
} else {
FullServerPath = ReplicaPartner;
Status = STATUS_SUCCESS;
}
if ( NT_SUCCESS( Status ) ) {
Win32Err = ImpNetpManageIPCConnect( CallerToken,
FullServerPath,
Account,
Password,
NETSETUPP_CONNECT_IPC );
if ( Win32Err == ERROR_SUCCESS ) {
UseAdded = TRUE;
Status = ImpLsaOpenPolicy( CallerToken,
&PartnerServer,
&ObjectAttributes,
MAXIMUM_ALLOWED,
&PartnerPolicy );
} else {
DsRolepLogPrint(( DEB_TRACE,
"NetUseAdd to %ws failed with %lu\n",
FullServerPath, Win32Err ));
//
// Temp status code so we know a failure occurred.
//
Status = STATUS_UNSUCCESSFUL;
}
}
} else if ( !NT_SUCCESS( Status ) ) {
WCHAR *BufPartnerServer = NULL;
BufPartnerServer = (WCHAR*)malloc(PartnerServer.Length+sizeof(WCHAR));
if (BufPartnerServer) {
CopyMemory(BufPartnerServer,PartnerServer.Buffer,PartnerServer.Length);
BufPartnerServer[PartnerServer.Length/sizeof(WCHAR)] = L'\0';
DsRolepLogPrint(( DEB_TRACE,
"LsaOpenPolicy on %ws failed with 0x%lx.\n",
BufPartnerServer, Status ));
free(BufPartnerServer);
}
}
if ( NT_SUCCESS( Status ) ) {
RtlZeroMemory( &ObjectAttributes, sizeof( ObjectAttributes ) );
Status = LsaOpenPolicy( NULL,
&ObjectAttributes,
MAXIMUM_ALLOWED,
&LocalPolicy );
if ( !NT_SUCCESS( Status ) ) {
DsRolepLogPrint(( DEB_TRACE,
"Local LsaOpenPoolicy returned 0x%lx\n",
Status ));
}
}
for ( i = 0;
i < sizeof( InfoClasses ) / sizeof( POLICY_INFORMATION_CLASS ) && NT_SUCCESS( Status );
i++ ) {
Status = ImpLsaQueryInformationPolicy( CallerToken,
PartnerPolicy,
InfoClasses[ i ],
&Buffer );
if ( NT_SUCCESS( Status ) ) {
Status = LsaSetInformationPolicy( LocalPolicy,
InfoClasses[ i ],
Buffer );
LsaFreeMemory( Buffer );
}
DsRolepLogPrint(( DEB_TRACE,
"Setting Lsa policy %lu returned 0x%lx\n",
InfoClasses[ i ], Status ));
}
//
// Now, the same for the Efs policy
//
if ( NT_SUCCESS( Status ) ) {
Status = ImpLsaQueryDomainInformationPolicy( CallerToken,
PartnerPolicy,
PolicyDomainEfsInformation,
&Buffer );
if ( NT_SUCCESS( Status ) ) {
Status = LsaSetDomainInformationPolicy( LocalPolicy,
PolicyDomainEfsInformation,
Buffer );
DsRolepLogPrint(( DEB_TRACE,
"Setting Efs policy from %ws returned 0x%lx\n",
ReplicaPartner, Status ));
LsaFreeMemory( Buffer );
} else {
DsRolepLogPrint(( DEB_TRACE,
"Reading Efs policy from %ws returned 0x%lx\n",
ReplicaPartner, Status ));
if ( Status == STATUS_OBJECT_NAME_NOT_FOUND ) {
Status = STATUS_SUCCESS;
}
}
}
if ( LocalPolicy ) {
LsaClose( LocalPolicy );
}
if ( PartnerPolicy ) {
ImpLsaClose( CallerToken, PartnerPolicy );
}
if ( UseAdded ) {
Win32Err = ImpNetpManageIPCConnect( CallerToken,
FullServerPath,
Account,
Password,
(NETSETUPP_DISCONNECT_IPC|NETSETUPP_USE_LOTS_FORCE) );
}
if ( FullServerPath && FullServerPath != ReplicaPartner ) {
RtlFreeHeap( RtlProcessHeap(), 0, FullServerPath );
}
//
// We won't bother cleaning up any of the information we set on the local machine in
// the failure case, since it won't hurt anything to have it here.
//
if ( Win32Err == ERROR_SUCCESS ) {
Win32Err = RtlNtStatusToDosError( Status );
}
DsRoleDebugOut(( DEB_TRACE_DS, "DsRolepSetLsaInformationForReplica %lu\n", Win32Err ));
DsRolepLogOnFailure( Win32Err,
DsRolepLogPrint(( DEB_TRACE,
"DsRolepSetLsaInformationForReplica failed with %lu\n",
Win32Err )) );
return( Win32Err );
}
DWORD
DsRolepSetLsaDomainPolicyInfo(
IN LPWSTR DnsDomainName,
IN LPWSTR FlatDomainName,
IN LPWSTR EnterpriseDnsName,
IN GUID *DomainGuid,
IN PSID DomainSid,
DWORD InstallOptions,
OUT PDSROLEP_DOMAIN_POLICY_INFO BackupDomainInfo
)
/*++
Routine Description:
This routine sets the PolicyAccountDomainInformation and
PolicyDnsDomainInformation in the lsa to reflect the
recent role changes.
Arguments:
DnsDomainName - The Dns domain name of the newly installed Domain/Dc
FlatDomainName - The NetBIOS domain name of the newly installed Domain/Dc
EnterpriseDnsName - The Dns domain name of the root of the enterprise
DomainGuid - The new domain guid
DomainSid - The new domain sid
InstallOptions : this describes the kind of install (new domain, enterprise,
or replica)
DomainGuid - The guid of the new domain is returned here
Returns:
ERROR_SUCCESS - Success; win error otherwise
--*/
{
NTSTATUS Status;
OBJECT_ATTRIBUTES ObjectAttributes;
POLICY_ACCOUNT_DOMAIN_INFO AccountDomainInfo;
POLICY_LSA_SERVER_ROLE_INFO ServerRoleInfo;
POLICY_DNS_DOMAIN_INFO DnsDomainInfo;
LSA_HANDLE PolicyHandle = NULL;
//
// If we are setting up the replica, we don't have things like the flat domain name and
// the domain sid, so we'll use the information we have backed up.
//
if ( FlatDomainName == NULL || DomainSid == NULL ) {
RtlCopyMemory( &AccountDomainInfo.DomainName,
&BackupDomainInfo->DnsDomainInfo->Name,
sizeof( UNICODE_STRING ) );
AccountDomainInfo.DomainSid = BackupDomainInfo->DnsDomainInfo->Sid ;
} else {
RtlInitUnicodeString( &AccountDomainInfo.DomainName,
FlatDomainName);
AccountDomainInfo.DomainSid = DomainSid;
}
//
// Open the Lsa
//
RtlZeroMemory( &ObjectAttributes, sizeof( ObjectAttributes ) );
Status = LsaOpenPolicy( NULL,
&ObjectAttributes,
POLICY_WRITE,
&PolicyHandle );
//
// Set the AccountDomain information first
//
if ( NT_SUCCESS( Status ) ) {
//
// Set the values in the Account Domain Policy structure.
//
WCHAR *BufDomainName = NULL;
DsRolepLogPrint(( DEB_TRACE, "Setting AccountDomainInfo to:\n" ));
BufDomainName = (WCHAR*)malloc(AccountDomainInfo.DomainName.Length+sizeof(WCHAR));
if (BufDomainName) {
CopyMemory(BufDomainName,AccountDomainInfo.DomainName.Buffer,AccountDomainInfo.DomainName.Length);
BufDomainName[AccountDomainInfo.DomainName.Length/sizeof(WCHAR)] = L'\0';
DsRolepLogPrint(( DEB_TRACE,
"\tDomain: %ws\n",
BufDomainName, Status ));
free(BufDomainName);
}
DsRolepLogSid( DEB_TRACE, "\tSid: ", AccountDomainInfo.DomainSid );
Status = LsaSetInformationPolicy( PolicyHandle,
PolicyAccountDomainInformation,
( PVOID )&AccountDomainInfo );
DsRolepLogOnFailure( RtlNtStatusToDosError( Status ),
DsRolepLogPrint(( DEB_TRACE,
"Setting AccountDomainInformation failed with 0x%lx\n",
RtlNtStatusToDosError( Status ) )) );
}
//
// Set the Dns domain information
//
if ( NT_SUCCESS( Status ) && !FLAG_ON( InstallOptions, NTDS_INSTALL_REPLICA ) ) {
RtlInitUnicodeString( &DnsDomainInfo.Name, FlatDomainName );
RtlInitUnicodeString( &DnsDomainInfo.DnsDomainName, DnsDomainName );
RtlInitUnicodeString( &DnsDomainInfo.DnsForestName, EnterpriseDnsName );
RtlCopyMemory( &DnsDomainInfo.DomainGuid, DomainGuid, sizeof( GUID ) );
DnsDomainInfo.Sid = DomainSid;
Status = LsaSetInformationPolicy( PolicyHandle,
PolicyDnsDomainInformation,
( PVOID )&DnsDomainInfo );
DsRolepLogOnFailure( RtlNtStatusToDosError( Status ),
DsRolepLogPrint(( DEB_TRACE,
"Setting DnsDomainInformation failed with 0x%lx\n",
RtlNtStatusToDosError( Status ) )) );
}
//
// If it isn't a replica, wipe the efs policy
//
if ( NT_SUCCESS( Status ) && !FLAG_ON( InstallOptions, NTDS_INSTALL_REPLICA ) ) {
Status = LsaSetDomainInformationPolicy( PolicyHandle,
PolicyDomainEfsInformation,
NULL );
if ( Status == STATUS_OBJECT_NAME_NOT_FOUND ) {
Status = STATUS_SUCCESS;
}
DsRolepLogOnFailure( RtlNtStatusToDosError( Status ),
DsRolepLogPrint(( DEB_TRACE,
"Erasing EfsPolicy failed with 0x%lx\n",
Status )) );
}
//
// Now, cleanup and exit
//
if ( PolicyHandle ) {
LsaClose( PolicyHandle );
}
return( RtlNtStatusToDosError( Status ) );
}
DWORD
DsRolepBackupDomainPolicyInfo(
IN PLSA_HANDLE LsaHandle, OPTIONAL
OUT PDSROLEP_DOMAIN_POLICY_INFO DomainInfo
)
/*++
Routine Description
This routine reads and saves in a global the state of the
account domain policy and primary domain policy so if an error
occurs then the original state can be preserved.
Parameters
DomainInfo : pointer, to be filled in by this routine
Return Values
ERROR_SUCCESS if no errors; a winerror otherwise
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
LSA_HANDLE PolicyHandle = NULL;
OBJECT_ATTRIBUTES ObjectAttributes;
ASSERT(DomainInfo);
if ( DomainInfo->PolicyBackedUp ) {
return( STATUS_SUCCESS );
}
if ( LsaHandle == NULL ) {
RtlZeroMemory( &ObjectAttributes, sizeof( ObjectAttributes ) );
Status = LsaOpenPolicy( NULL,
&ObjectAttributes,
POLICY_VIEW_LOCAL_INFORMATION,
&PolicyHandle );
} else {
PolicyHandle = LsaHandle;
}
if ( NT_SUCCESS( Status ) ) {
Status = LsaQueryInformationPolicy(
PolicyHandle,
PolicyDnsDomainInformation,
( PVOID * )&DomainInfo->DnsDomainInfo);
}
if ( NT_SUCCESS( Status ) ) {
Status = LsaQueryInformationPolicy(
PolicyHandle,
PolicyAccountDomainInformation,
( PVOID * )&DomainInfo->AccountDomainInfo);
}
if ( NT_SUCCESS( Status ) ) {
Status = LsaQueryInformationPolicy(
PolicyHandle,
PolicyLsaServerRoleInformation,
( PVOID * )&DomainInfo->ServerRoleInfo);
}
if ( NT_SUCCESS( Status ) ) {
Status = LsaQueryDomainInformationPolicy(
PolicyHandle,
PolicyDomainEfsInformation,
( PVOID * )&DomainInfo->EfsPolicy );
if ( NT_SUCCESS( Status ) ) {
DomainInfo->EfsPolicyPresent = TRUE;
} else {
//
// It's ok for the Efs policy not to have existed
//
if ( Status == STATUS_OBJECT_NAME_NOT_FOUND ) {
DomainInfo->EfsPolicyPresent = TRUE;
Status = STATUS_SUCCESS;
} else {
DomainInfo->EfsPolicyPresent = FALSE;
}
}
}
if ( PolicyHandle && PolicyHandle != LsaHandle ) {
LsaClose( PolicyHandle );
}
if ( NT_SUCCESS( Status ) ) {
DomainInfo->PolicyBackedUp = TRUE;
}
return( RtlNtStatusToDosError( Status ) );
}
DWORD
DsRolepRestoreDomainPolicyInfo(
IN PDSROLEP_DOMAIN_POLICY_INFO DomainInfo
)
/*++
Routine Description
This routine sets the account and primary domain information to be
the values that were stored of by DsRolepBackupDomainPolicyInformation.
Parameters
DomainInfo : pointer, expected to be filled
Return Values
ERROR_SUCCESS if no errors; a winerror otherwise
ERROR_INVALID_DATA - The data was never successfully backed up
--*/
{
NTSTATUS Status, Status2;
HANDLE PolicyHandle;
OBJECT_ATTRIBUTES ObjectAttributes;
ASSERT(DomainInfo);
if ( !DomainInfo->PolicyBackedUp ) {
return( ERROR_INVALID_DATA );
}
RtlZeroMemory( &ObjectAttributes, sizeof( ObjectAttributes ) );
Status = LsaOpenPolicy( NULL,
&ObjectAttributes,
POLICY_WRITE,
&PolicyHandle );
if ( NT_SUCCESS( Status ) ) {
Status = LsaSetInformationPolicy( PolicyHandle,
PolicyDnsDomainInformation,
( PVOID )DomainInfo->DnsDomainInfo );
Status2 = LsaSetInformationPolicy( PolicyHandle,
PolicyAccountDomainInformation,
( PVOID )DomainInfo->AccountDomainInfo );
if ( NT_SUCCESS( Status ) && !NT_SUCCESS( Status2 ) ) {
Status = Status2;
}
//
// Restore the Efs policy, if it exists
//
if ( NT_SUCCESS( Status ) && DomainInfo->EfsPolicyPresent ) {
Status = LsaSetDomainInformationPolicy( PolicyHandle,
PolicyDomainEfsInformation,
( PVOID )DomainInfo->EfsPolicy );
}
Status2 = LsaClose( PolicyHandle );
if ( NT_SUCCESS( Status ) ) {
Status = Status2;
}
}
DsRolepLogOnFailure( Status,
DsRolepLogPrint(( DEB_TRACE,
"RestoreDomainPolicyInfo failed with 0x%lx\n",
Status )) );
return( RtlNtStatusToDosError( Status ) );
}
DWORD
DsRolepFreeDomainPolicyInfo(
IN PDSROLEP_DOMAIN_POLICY_INFO DomainInfo
)
/*++
Routine Description
This routine free the structures that were allocated during
DsRolepBackupDomainPolicyInformation.
Parameters
DomainInfo : pointer, expected to be filled so the fields can be freed
Return Values
ERROR_SUCCESS if no errors; a winerror otherwise
--*/
{
if ( DomainInfo->AccountDomainInfo ) {
LsaFreeMemory( DomainInfo->AccountDomainInfo );
}
if ( DomainInfo->DnsDomainInfo ) {
LsaFreeMemory( DomainInfo->DnsDomainInfo );
}
if ( DomainInfo->ServerRoleInfo ) {
LsaFreeMemory( DomainInfo->ServerRoleInfo );
}
if ( DomainInfo->EfsPolicyPresent ) {
LsaFreeMemory( DomainInfo->EfsPolicy );
}
return ERROR_SUCCESS;
}
DWORD
DsRolepUpgradeLsaToDs(
BOOLEAN InitializeLsa
)
/*++
Routine Description:
Prompts Lsa to upgrade all the information it stores in the registry into the Ds
Arguments:
None
Returns:
ERROR_SUCCESS - Success
ERROR_NOT_ENOUGH_MEMORY - A memory allocation failed
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
DWORD WinError = ERROR_SUCCESS;
if ( InitializeLsa ) {
//
// Make the Lsa think that we're initialized
//
DSROLEP_CURRENT_OP0( DSROLEEVT_SET_LSA );
//
// Make the Lsa think that we're initialized
//
Status = LsapDsInitializeDsStateInfo( LsapDsDsSetup );
if ( !NT_SUCCESS( Status ) ) {
DsRolepLogPrint(( DEB_TRACE,
"Failed to convince Lsa to reinitialize: 0x%lx\n",
Status ));
} else {
Status = LsaIUpgradeRegistryToDs( FALSE );
}
}
return( WinError == ERROR_SUCCESS ? RtlNtStatusToDosError( Status ) : WinError );
}
VOID
DsRolepFindSelfAndParentInForest(
IN PLSAPR_FOREST_TRUST_INFO ForestTrustInfo,
OUT PLSAPR_TREE_TRUST_INFO CurrentEntry,
IN PUNICODE_STRING LocalDomain,
OUT PLSAPR_TREE_TRUST_INFO *ParentEntry,
OUT PLSAPR_TREE_TRUST_INFO *OwnEntry
)
{
DWORD WinError = ERROR_SUCCESS;
ULONG i;
BOOLEAN ParentKnown = FALSE;
if ( *ParentEntry && *OwnEntry ) {
return;
}
if ( ForestTrustInfo->ParentDomainReference ) {
CurrentEntry = ForestTrustInfo->ParentDomainReference;
ParentKnown = TRUE;
}
for ( i = 0; i < CurrentEntry->Children && *OwnEntry == NULL; i++ ) {
if ( RtlCompareUnicodeString(
( PUNICODE_STRING )&CurrentEntry->ChildDomains[ i ].DnsDomainName,
LocalDomain,
TRUE ) == 0 ) {
*OwnEntry = &CurrentEntry->ChildDomains[ i ];
*ParentEntry = CurrentEntry;
break;
}
if ( !ParentKnown ) {
DsRolepFindSelfAndParentInForest( ForestTrustInfo,
&CurrentEntry->ChildDomains[ i ],
LocalDomain,
ParentEntry,
OwnEntry );
}
}
return;
}