Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4407 lines
108 KiB

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
lsads.c
Abstract:
Implemntation of the LSA/Ds interface and support routines
Author:
Mac McLain (MacM) Jan 17, 1997
Environment:
User Mode
Revision History:
--*/
#include <lsapch2.h>
#include <dbp.h>
#include <sertlp.h>
#ifdef DS_LOOKUP
#include <dslookup.h>
#endif
#include <align.h>
#include <windns.h>
#include <alloca.h>
#if DBG
DEFINE_DEBUG2(LsaDs);
DEBUG_KEY LsaDsDebugKeys[] = {{DEB_ERROR, "Error"},
{DEB_WARN, "Warn"},
{DEB_TRACE, "Trace"},
{DEB_UPGRADE, "Upgrade"},
{DEB_POLICY, "Policy"},
{DEB_FIXUP, "Fixup"},
{DEB_NOTIFY, "Notify"},
{DEB_DSNOTIFY, "DsNotify"},
{DEB_FTRACE, "FTrace"},
{DEB_LOOKUP, "Lookup"},
{DEB_HANDLE, "Handle"},
{DEB_FTINFO, "FtInfo"},
{DEB_SIDFILTER, "SidFilter"},
{0, NULL}};
HANDLE g_hDebugWait = NULL;
HANDLE g_hDebugParamEvent = NULL;
HKEY g_hDebugParamKey = NULL;
extern DWORD LsaDsInfoLevel;
void
LsaDsGetDebugRegParams(
IN HKEY ParamKey
)
/*++
Routine Description:
Gets the debug paramaters from the registry
Sets LsaDsInfolevel for debug spew
Arguments: HKEY to HKLM/System/CCS/Control/LSA
--*/
{
DWORD cbType, tmpInfoLevel = LsaDsInfoLevel, cbSize = sizeof(DWORD);
DWORD dwErr;
dwErr = RegQueryValueExW(
ParamKey,
L"LsaDsInfoLevel",
NULL,
&cbType,
(LPBYTE)&tmpInfoLevel,
&cbSize
);
if (dwErr != ERROR_SUCCESS) {
if (dwErr == ERROR_FILE_NOT_FOUND) {
// no registry value is present, don't want info
// so reset to defaults
tmpInfoLevel = DEB_ERROR;
} else {
DebugLog((DEB_WARN, "Failed to query DebugLevel: 0x%x\n", dwErr));
tmpInfoLevel = 0;
}
} else if ( cbType != REG_DWORD ) {
DebugLog((DEB_WARN, "DebugLevel is of the wrong type, DEB_ERROR assumed"));
tmpInfoLevel = DEB_ERROR;
}
LsaDsInfoLevel = tmpInfoLevel;
dwErr = RegQueryValueExW(
ParamKey,
L"LogToFile",
NULL,
&cbType,
(LPBYTE)&tmpInfoLevel,
&cbSize
);
if (dwErr == ERROR_SUCCESS && cbType == REG_DWORD) {
LsaDsSetLoggingOption((BOOL) tmpInfoLevel);
} else {
LsaDsSetLoggingOption(FALSE);
}
return;
}
VOID
LsaDsWatchDebugParamKey(
PVOID pCtxt,
BOOLEAN fWaitStatus
)
/*++
Routine Description:
Sets RegNotifyChangeKeyValue() on param key, initializes
debug level, then utilizes thread pool to wait on
changes to this registry key. Enables dynamic debug
level changes, as this function will also be callback
if registry key modified.
Arguments: pCtxt is actually a HANDLE to an event. This event
will be triggered when key is modified.
--*/
{
NTSTATUS Status;
LONG lRes = ERROR_SUCCESS;
if (NULL == g_hDebugParamKey) { // first time we've been called.
lRes = RegOpenKeyExW(
HKEY_LOCAL_MACHINE,
L"System\\CurrentControlSet\\Control\\Lsa",
0,
KEY_READ,
&g_hDebugParamKey
);
if (ERROR_SUCCESS != lRes) {
DebugLog((DEB_WARN,"Failed to open LSA debug parameters key: 0x%x\n", lRes));
goto Reregister;
}
}
if (NULL != g_hDebugWait) {
Status = RtlDeregisterWait(g_hDebugWait);
if (!NT_SUCCESS(Status)) {
DebugLog((DEB_WARN, "Failed to Deregister wait on registry key: 0x%x\n", Status));
goto Reregister;
}
}
lRes = RegNotifyChangeKeyValue(
g_hDebugParamKey,
FALSE,
REG_NOTIFY_CHANGE_LAST_SET,
(HANDLE) pCtxt,
TRUE
);
if (ERROR_SUCCESS != lRes) {
DebugLog((DEB_ERROR,"Debug RegNotify setup failed: 0x%x\n", lRes));
// we're tanked now. No further notifications, so get this one
}
LsaDsGetDebugRegParams(g_hDebugParamKey);
Reregister:
Status = RtlRegisterWait(
&g_hDebugWait,
(HANDLE) pCtxt,
LsaDsWatchDebugParamKey,
(HANDLE) pCtxt,
INFINITE,
WT_EXECUTEINPERSISTENTIOTHREAD |
WT_EXECUTEONLYONCE
);
}
VOID
LsapDsDebugInitialize()
{
LsaDsInitDebug( LsaDsDebugKeys );
g_hDebugParamEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
if ( NULL == g_hDebugParamEvent ) {
DebugLog((DEB_WARN, "CreateEvent for g_hDebugParamEvent failed - 0x%x\n", GetLastError()));
} else {
LsaDsWatchDebugParamKey( g_hDebugParamEvent, FALSE );
}
}
#else // !DBG
VOID
LsapDsDebugInitialize()
{
}
#endif
//
// extern definitions.
//
DWORD LsapDsThreadState; // Defined in lsads.h, referenced in spinit.c
ULONG
LsapClassIdFromObjType(
IN LSAP_DB_OBJECT_TYPE_ID DsObjType
);
PVOID
LsapDsAlloc(
IN DWORD dwLen
)
/*++
Routine Description:
This function is the allocator for the LSA DS functions
Arguments:
dwLen - Number of bytes to allocate
Return Value:
Pointer to allocated memory on success or NULL on failure
--*/
{
PLSADS_PER_THREAD_INFO CurrentThreadInfo;
//
// If there's no DS thread state,
// we shouldn't be here.
//
if ( !THQuery()) {
ASSERT( THQuery() );
return NULL;
}
//
// Otherwise simply allocate from the DS thread state.
//
return( THAlloc( dwLen ) );
}
VOID
LsapDsFree(
IN PVOID pvMemory
)
/*++
Routine Description:
This function frees memory allocated by LsapDsAlloc
Arguments:
pvMemory -- memory to free
Return Value:
VOID
--*/
{
ASSERT( THQuery() );
if ( THQuery() ) {
THFree( pvMemory );
}
}
NTSTATUS
LsapDsInitializeDsStateInfo(
IN LSADS_INIT_STATE DsInitState
)
/*++
Routine Description:
This routine will initialize the global DS State information that is used
to contol the behavior of all of the lsa operations
Arguments:
DsInitState -- State the DS booted off of
Return Value:
STATUS_SUCCES -- Success
STATUS_NO_MEMORY -- A memory allocation failed
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
LSADS_INIT_STATE CalledInitState = DsInitState;
if ( LsapDsDsSetup == DsInitState ) {
//
// At the time of modification, it is difficult to tell what
// ramifications running with an init state of LsapDsDsSetup
// will have since it is untested. So, let's be safe and translate
// LsapDsDsSetup to LsapDsDs, which is a known state to
// be in.
//
DsInitState = LsapDsDs;
}
LsaDsInitState = DsInitState ;
if ( DsInitState != LsapDsDs ) {
//
// Use the dummy functions
//
LsaDsStateInfo.DsFuncTable.pOpenTransaction = LsapDsOpenTransactionDummy;
LsaDsStateInfo.DsFuncTable.pApplyTransaction = LsapDsApplyTransactionDummy;
LsaDsStateInfo.DsFuncTable.pAbortTransaction = LsapDsAbortTransactionDummy;
} else if ( !LsaDsStateInfo.DsInitializedAndRunning ) {
Status = LsaISamIndicatedDsStarted( FALSE );
}
//
// Initialize the domain and default policy object references
//
if ( NT_SUCCESS( Status ) &&
LsapDsWriteDs &&
CalledInitState != LsapDsDsSetup ) {
//
// Fixup our trusted domain objects, if necessary
//
Status = LsapDsFixupTrustedDomainObjectOnRestart();
}
#if DBG
if ( NT_SUCCESS( Status ) ) {
LsapDsDebugOut(( 0, "LsapDsInitializeDsStateInfo succeeded\n", Status ));
} else if ( LsapProductType == NtProductLanManNt ) {
LsapDsDebugOut(( DEB_ERROR, "LsapDsInitializeDsStateInfo failed: 0x%lx\n", Status ));
}
#endif
return( Status );
}
NTSTATUS
LsapDsUnitializeDsStateInfo(
VOID
)
/*++
Routine Description:
This routine will undo what the initialization did. Only valid for the setup case
Arguments:
None
Return Value:
STATUS_SUCCES -- Success
--*/
{
LsaDsStateInfo.UseDs = FALSE;
LsapDsIsRunning = FALSE;
LsaDsStateInfo.WriteLocal = TRUE;
//
// Go back to using the dummy functions
//
LsaDsStateInfo.DsFuncTable.pOpenTransaction = LsapDsOpenTransactionDummy;
LsaDsStateInfo.DsFuncTable.pApplyTransaction = LsapDsApplyTransactionDummy;
LsaDsStateInfo.DsFuncTable.pAbortTransaction = LsapDsAbortTransactionDummy;
LsaDsStateInfo.DsInitializedAndRunning = FALSE;
return( STATUS_SUCCESS );
}
NTSTATUS
LsapDsMapDsReturnToStatus (
ULONG DsStatus
)
/*++
Routine Description:
Maps a DS error to NTSTATUS
Arguments:
DsStatus - DsStatus to map
Return Values:
STATUS_SUCCESS - Ds call succeeded
STATUS_UNSUCCESSFUL - Ds call failed
--*/
{
NTSTATUS Status;
switch ( DsStatus )
{
case 0L:
Status = STATUS_SUCCESS;
break;
default:
Status = STATUS_UNSUCCESSFUL;
LsapDsDebugOut(( DEB_TRACE, "DS Error %lu mapped to NT Status 0x%lx\n",
DsStatus, Status ));
break;
}
return( Status );
}
NTSTATUS
LsapDsMapDsReturnToStatusEx (
IN COMMRES *pComRes
)
/*++
Routine Description:
Maps a DS error to NTSTATUS
Arguments:
DsStatus - DsStatus to map
Return Values:
STATUS_SUCCESS - Ds call succeeded
STATUS_UNSUCCESSFUL - Ds call failed
--*/
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
switch ( pComRes->errCode ) {
case 0:
Status = STATUS_SUCCESS;
break;
case attributeError:
switch ( pComRes->pErrInfo->AtrErr.FirstProblem.intprob.problem ) {
case PR_PROBLEM_NO_ATTRIBUTE_OR_VAL:
Status = STATUS_NOT_FOUND;
break;
case PR_PROBLEM_INVALID_ATT_SYNTAX:
case PR_PROBLEM_UNDEFINED_ATT_TYPE:
case PR_PROBLEM_CONSTRAINT_ATT_TYPE:
Status = STATUS_DATA_ERROR;
break;
case PR_PROBLEM_ATT_OR_VALUE_EXISTS:
Status = STATUS_OBJECT_NAME_COLLISION;
break;
}
break;
case nameError:
switch ( pComRes->pErrInfo->NamErr.problem ) {
case NA_PROBLEM_NO_OBJECT:
Status = STATUS_OBJECT_NAME_NOT_FOUND;
break;
case NA_PROBLEM_BAD_ATT_SYNTAX:
case NA_PROBLEM_BAD_NAME:
Status = STATUS_OBJECT_NAME_INVALID;
break;
}
break;
case updError:
switch ( pComRes->pErrInfo->UpdErr.problem ) {
case UP_PROBLEM_ENTRY_EXISTS:
Status = STATUS_OBJECT_NAME_COLLISION;
break;
case UP_PROBLEM_NAME_VIOLATION:
Status = STATUS_OBJECT_NAME_INVALID;
break;
}
break;
case securityError:
switch ( pComRes->pErrInfo->SecErr.problem ) {
case SE_PROBLEM_INSUFF_ACCESS_RIGHTS:
Status = STATUS_ACCESS_DENIED;
break;
}
break;
case serviceError:
switch ( pComRes->pErrInfo->SvcErr.problem ) {
case SV_PROBLEM_BUSY:
Status = STATUS_DEVICE_BUSY;
break;
}
}
THClearErrors();
return( Status );
}
VOID
LsapDsInitializeStdCommArg (
IN COMMARG *pCommArg,
IN ULONG Flags
)
/*++
Routine Description:
Initialized a COMMARG structue with a standard set of options used by LsapDs routines
Arguments:
pCommArg - Pointer to the COMMARG structure to be initialized
Return Values:
None
--*/
{
/* Get the default values... */
InitCommarg(pCommArg);
/* ...and override some of them */
pCommArg->Svccntl.DerefAliasFlag = DA_NEVER;
pCommArg->Svccntl.localScope = TRUE;
pCommArg->Svccntl.SecurityDescriptorFlags = 0;
pCommArg->ulSizeLimit = 0x20000;
if ( FLAG_ON( Flags, LSAPDS_USE_PERMISSIVE_WRITE ) ) {
pCommArg->Svccntl.fPermissiveModify = TRUE;
}
if ( FLAG_ON( Flags, LSAPDS_READ_DELETED ) ) {
pCommArg->Svccntl.makeDeletionsAvail = TRUE;
}
}
ULONG
LsapClassIdFromObjType(
IN LSAP_DB_OBJECT_TYPE_ID ObjType
)
/*++
Routine Description:
Maps from an LSA object type to a DS Class ID
Arguments:
DsObjType - Type of the object
Return Values:
ClassID of object type on success
0xFFFFFFFF on failure
--*/
{
ULONG ClassId = 0xFFFFFFFF;
switch ( ObjType ) {
case TrustedDomainObject:
ClassId = LsapDsClassIds[ LsapDsClassTrustedDomain ];
break;
case SecretObject:
ClassId = LsapDsClassIds[ LsapDsClassSecret ];
break;
}
return( ClassId );
}
NTSTATUS
LsapAllocAndInitializeDsNameFromUnicode(
IN PLSA_UNICODE_STRING pObjectName,
OUT PDSNAME *pDsName
)
/*++
Routine Description:
This function constructs a DSNAME structure and optional RDN for the
stated object.
Arguments:
DsObjType -- Type of the object to be created.
pObjectName -- Name of the object to be created.
pObjectPath -- Root path under which to create the object
pDsName -- Where the DS Name structure is returned. Free via LsapDsFree
Return Value:
STATUS_SUCCESS -- Success
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
DWORD dwLen;
DWORD dwNameLen = 0;
if (pObjectName == NULL || pObjectName->Length == 0) {
return( STATUS_INVALID_PARAMETER );
}
//
// Determine our length...
//
dwNameLen = LsapDsGetUnicodeStringLenNoNull( pObjectName ) / sizeof(WCHAR);
dwLen = DSNameSizeFromLen( dwNameLen );
//
// Now, allocate it...
//
*pDsName = LsapDsAlloc( dwLen );
if ( *pDsName == NULL ) {
Status = STATUS_NO_MEMORY;
} else {
(*pDsName)->structLen = dwLen;
//
// Length doesn't include trailing NULL
//
(*pDsName)->NameLen = dwNameLen;
RtlCopyMemory( (*pDsName)->StringName, pObjectName->Buffer, pObjectName->Length );
}
return(Status);
}
NTSTATUS
LsapDsCopyDsNameLsa(
OUT PDSNAME *Dest,
IN PDSNAME Source
)
/*++
Routine Description:
This function copies one
Arguments:
DsObjType -- Type of the object to be created.
pObjectName -- Name of the object to be created.
Flags -- Flags to control the various actions of the create
cItems -- Number of attributes to set
pAttrTypeList -- List of attribute types
pAttrValList -- List of attribute values
Return Value:
STATUS_SUCCESS -- Success
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
if ( Source == NULL ) {
*Dest = NULL;
} else {
*Dest = LsapAllocateLsaHeap( Source->structLen );
if ( *Dest == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
RtlCopyMemory( *Dest, Source, Source->structLen );
}
}
return( Status );
}
NTSTATUS
LsapDsCreateAndSetObject(
IN PLSA_UNICODE_STRING pObjectName,
IN ULONG Flags,
IN ULONG cItems,
IN ATTRTYP *pAttrTypeList,
IN ATTRVAL *pAttrValList
)
/*++
Routine Description:
This function creates the specified DS object and sets the given
attributes on the object
Arguments:
pObjectName -- Name of the object to be created.
Flags -- Flags to control the various actions of the create
cItems -- Number of attributes to set
pAttrTypeList -- List of attribute types
pAttrValList -- List of attribute values
Return Value:
STATUS_SUCCESS -- Success
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
PDSNAME pDSName;
ADDARG AddArg;
ADDRES *AddRes = NULL;
ATTR *pAddResAttributes;
ATTRBLOCK AddResAttrBlock;
ULONG i;
LsapEnterFunc( "LsapDsCreateAndSetObject" );
ASSERT( pObjectName != NULL );
RtlZeroMemory( &AddArg, sizeof( ADDARG ) );
//
// Build the DSName
//
Status = LsapAllocAndInitializeDsNameFromUnicode( pObjectName, &pDSName );
if ( NT_SUCCESS( Status ) ) {
//
// Initialize our memory for our structures.
//
pAddResAttributes = LsapDsAlloc( sizeof(ATTR) * cItems );
if ( pAddResAttributes == NULL ) {
Status = STATUS_NO_MEMORY;
} else {
for ( i = 0 ; i < cItems ; i++ ) {
LSAP_DS_INIT_ATTR( pAddResAttributes[i], pAttrTypeList[i], 1, &(pAttrValList[i]) );
}
AddResAttrBlock.attrCount = cItems;
AddResAttrBlock.pAttr = pAddResAttributes;
AddArg.pObject = pDSName;
AddArg.AttrBlock = AddResAttrBlock;
LsapDsInitializeStdCommArg( &(AddArg.CommArg), 0 );
}
//
// Now, do the create
//
if ( NT_SUCCESS( Status ) ) {
//
// Turn off fDSA flag. This is to force the core DS to perform
// the access ck. Only the core DS has the knowledge to consider
// the security descriptor on the logical parent in the DS. Do
// not turn of the fDSA flag if this is an upgrade ( theoritically
// for trusted clients too ). fDSA for Ds is analogous to trusted
// client in LSA.
//
if ( !FLAG_ON( Flags, LSAPDS_CREATE_TRUSTED ) ) {
LsapDsSetDsaFlags( FALSE );
}
DirAddEntry( &AddArg, &AddRes );
if ( AddRes ) {
Status = LsapDsMapDsReturnToStatusEx( &AddRes->CommRes );
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
LsapDsContinueTransaction();
if ( !FLAG_ON( Flags, LSAPDS_CREATE_TRUSTED ) ) {
LsapDsSetDsaFlags( TRUE );
}
LsapDsDebugOut(( DEB_TRACE, "DirAddEntry on %wZ returned 0x%lx\n",
pObjectName, Status ));
}
LsapDsFree(pDSName);
} else {
LsapDsDebugOut(( DEB_TRACE,
"LsapAllocAndInitializeDsNameFromUnicode on %wZ returned 0x%lx\n",
pObjectName, Status ));
}
LsapExitFunc( "LsapDsCreateAndSetObject", Status );
return( Status );
}
NTSTATUS
LsapDsCreateObjectDs(
IN PDSNAME ObjectName,
IN ULONG Flags,
IN ATTRBLOCK *AttrBlock
)
/*++
Routine Description:
This function creates the specified DS object and sets the given
attributes on the object
Arguments:
ObjectName -- Dsname of the object to be created.
Flags -- Flags to control the various actions of the create
Attrs -- Optional list of attributes to set on the object
Return Value:
STATUS_SUCCESS -- Success
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
ADDARG AddArg;
ADDRES *AddRes = NULL;
LsapEnterFunc( "LsapDsCreateObjectDs" );
RtlZeroMemory( &AddArg, sizeof (ADDARG ) );
AddArg.pObject = ObjectName;
RtlCopyMemory( &AddArg.AttrBlock, AttrBlock, sizeof( ATTRBLOCK ) );
LsapDsInitializeStdCommArg( &AddArg.CommArg, 0 );
if ( !FLAG_ON( Flags, LSAPDS_CREATE_TRUSTED ) ) {
LsapDsSetDsaFlags( FALSE );
}
//
// Do the add
//
DirAddEntry( &AddArg, &AddRes );
if ( AddRes ) {
Status = LsapDsMapDsReturnToStatusEx( &AddRes->CommRes );
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
LsapDsContinueTransaction();
if ( !FLAG_ON( Flags, LSAPDS_CREATE_TRUSTED ) ) {
LsapDsSetDsaFlags( TRUE );
}
LsapDsDebugOut(( DEB_TRACE, "DirAddEntry on %ws returned 0x%lx\n",
LsapDsNameFromDsName( ObjectName ), Status ));
LsapExitFunc( "LsapDsCreateObjectDs", Status );
return( Status );
}
NTSTATUS
LsapDsRemove (
IN PDSNAME pObject
)
{
NTSTATUS Status = STATUS_SUCCESS;
REMOVEARG Remove;
REMOVERES *RemoveRes = NULL;
RtlZeroMemory( &Remove, sizeof( REMOVEARG ) );
//
// Initialize the commarg struct
//
LsapDsInitializeStdCommArg( &Remove.CommArg, 0 );
Remove.pObject = pObject;
//
// Do the call
//
DirRemoveEntry( &Remove, &RemoveRes );
if ( RemoveRes ) {
Status = LsapDsMapDsReturnToStatusEx( &RemoveRes->CommRes );
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
LsapDsContinueTransaction();
return( Status );
}
NTSTATUS
LsapDsRead (
IN PUNICODE_STRING pObject,
IN ULONG fFlags,
IN ATTRBLOCK *pAttributesToRead,
OUT ATTRBLOCK *pAttributeValues
)
/*++
Routine Description:
This function reads the specified attributes from the given object of the
specified type. It servers as the primary interface between the LSA and the
DS for reading a property/object
Arguments:
pObject - DSNAME of the object
fFlags - Read flags
ObjType - Type of LSA object to be read
pReadAttributes - Attributes to be read
pAttributeValues - Value of the attributes that were read.
Return Value:
STATUS_SUCCESS - Success
--*/
{
NTSTATUS Status;
PDSNAME DsName = NULL;
Status = STATUS_SUCCESS;
//
// By the time we get here, everything should be valid...
//
ASSERT( pObject != NULL );
ASSERT( pAttributesToRead != NULL && pAttributesToRead->attrCount > 0 );
ASSERT( pAttributeValues != NULL );
//
// Build the DSName
//
Status = LsapAllocAndInitializeDsNameFromUnicode(
pObject,
&DsName
);
if ( NT_SUCCESS( Status ) ) {
Status = LsapDsReadByDsName( DsName,
fFlags,
pAttributesToRead,
pAttributeValues );
LsapDsFree( DsName );
}
return( Status );
}
NTSTATUS
LsapDsReadByDsName(
IN PDSNAME DsName,
IN ULONG Flags,
IN ATTRBLOCK *pAttributesToRead,
OUT ATTRBLOCK *pAttributeValues
)
/*++
Routine Description:
This function reads the specified attributes from the given object of the
specified type. It servers as the primary interface between the LSA and the
DS for reading a property/object
Arguments:
DsName - DSNAME of the object
Flags - Read flags
ObjType - Type of LSA object to be read
pReadAttributes - Attributes to be read
pAttributeValues - Value of the attributes that were read.
Return Value:
STATUS_SUCCESS - Success
--*/
{
NTSTATUS Status = STATUS_SUCCESS, Status2;
ENTINFSEL EntryInf;
READARG ReadArg;
READRES *ReadRes = NULL;
ULONG i;
//
// By the time we get here, everything should be valid...
//
ASSERT( DsName != NULL );
ASSERT( pAttributesToRead != NULL && pAttributesToRead->attrCount > 0 );
ASSERT( pAttributeValues != NULL );
ASSERT( THQuery() );
if ( !THQuery() ) {
return( STATUS_RXACT_INVALID_STATE );
}
//
// Initialize the ENTINFSEL structure
//
EntryInf.attSel = EN_ATTSET_LIST;
EntryInf.infoTypes = EN_INFOTYPES_TYPES_VALS;
//
// Initialize the READARG structure
//
RtlZeroMemory(&ReadArg, sizeof(READARG));
ReadArg.pObject = DsName;
ReadArg.pSel = &EntryInf;
//
// Initialize the commarg struct
//
LsapDsInitializeStdCommArg( &ReadArg.CommArg, Flags );
EntryInf.AttrTypBlock.pAttr = LsapDsAlloc( pAttributesToRead->attrCount * sizeof(ATTR ) );
if ( EntryInf.AttrTypBlock.pAttr == NULL ) {
Status = STATUS_NO_MEMORY;
} else {
EntryInf.AttrTypBlock.attrCount = pAttributesToRead->attrCount;
for ( i = 0 ; i < pAttributesToRead->attrCount ; i++ ) {
EntryInf.AttrTypBlock.pAttr[i].attrTyp = pAttributesToRead->pAttr[i].attrTyp;
EntryInf.AttrTypBlock.pAttr[i].AttrVal.valCount =
pAttributesToRead->pAttr[i].AttrVal.valCount;
EntryInf.AttrTypBlock.pAttr[i].AttrVal.pAVal =
pAttributesToRead->pAttr[i].AttrVal.pAVal;
EntryInf.attSel = EN_ATTSET_LIST;
EntryInf.infoTypes = EN_INFOTYPES_TYPES_VALS;
}
}
//
// Do the call
//
if ( NT_SUCCESS( Status ) ) {
DirRead( &ReadArg, &ReadRes );
if ( ReadRes ) {
Status = LsapDsMapDsReturnToStatusEx( &ReadRes->CommRes );
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
LsapDsContinueTransaction();
}
//
// Now, build the attr block going back
//
if ( NT_SUCCESS( Status ) ) {
pAttributeValues->attrCount = ReadRes->entry.AttrBlock.attrCount;
pAttributeValues->pAttr = ReadRes->entry.AttrBlock.pAttr;
}
return( Status );
}
NTSTATUS
LsapDsWrite(
IN PUNICODE_STRING pObject,
IN ULONG Flags,
IN ATTRBLOCK *Attributes
)
{
NTSTATUS Status = STATUS_SUCCESS;
MODIFYARG Modify;
MODIFYRES *ModifyRes;
ATTRMODLIST *AttrMod = NULL;
INT i;
PDSNAME DsName;
ASSERT( pObject );
ASSERT( Attributes->pAttr );
ASSERT( Flags != 0 );
RtlZeroMemory( &Modify, sizeof( MODIFYARG ) );
//
// Build the DSName
//
Status = LsapAllocAndInitializeDsNameFromUnicode(
pObject,
&DsName
);
if ( NT_SUCCESS( Status ) ) {
Status = LsapDsWriteByDsName( DsName,
Flags,
Attributes
);
LsapDsFree( DsName );
}
return( Status );
}
NTSTATUS
LsapDsWriteByDsName(
IN PDSNAME DsName,
IN ULONG Flags,
IN ATTRBLOCK *Attributes
)
{
NTSTATUS Status = STATUS_SUCCESS;
MODIFYARG Modify;
MODIFYRES *ModifyRes = NULL;
ATTRMODLIST *AttrMod = NULL;
INT i, AttrModIndex = 0;
ASSERT( DsName );
ASSERT( Attributes );
ASSERT( Attributes->pAttr );
RtlZeroMemory( &Modify, sizeof( MODIFYARG ) );
//
// If there are no attributes, simply return success. Otherwise, DirModifyEntry will
// av.
//
if ( Attributes->attrCount == 0 ) {
return( Status );
}
//
// Initialize the AttrMod structure
//
if ( Attributes->attrCount > 1 ) {
AttrMod = LsapDsAlloc( sizeof(ATTRMODLIST) * ( Attributes->attrCount - 1 ) );
if ( AttrMod == NULL ) {
Status = STATUS_NO_MEMORY;
} else {
//
// Copy the attrs into the ATTRMODLIST
//
for ( i = 0; i < (INT)Attributes->attrCount - 1 ; i++) {
AttrMod[i].pNextMod = &AttrMod[i + 1];
AttrMod[i].choice = (USHORT)( Flags & LSAPDS_WRITE_TYPES );
RtlCopyMemory( &AttrMod[i].AttrInf,
&Attributes->pAttr[i + 1],
sizeof( ATTR ) );
}
AttrMod[i - 1].pNextMod = NULL;
}
}
if ( NT_SUCCESS( Status ) ) {
//
// Set the root node...
//
Modify.FirstMod.pNextMod = AttrMod;
Modify.FirstMod.choice = (USHORT)( Flags & LSAPDS_WRITE_TYPES );
RtlCopyMemory( &Modify.FirstMod.AttrInf,
&Attributes->pAttr[0],
sizeof( ATTR ) );
//
// Setup the MODIFYARG structure
//
Modify.pObject = DsName;
Modify.count = (USHORT)Attributes->attrCount;
LsapDsInitializeStdCommArg( &Modify.CommArg, Flags );
if ( FlagOn( Flags, LSAPDS_REPL_CHANGE_URGENTLY ) ) {
Modify.CommArg.Svccntl.fUrgentReplication = TRUE;
}
//
// Make the call
//
DirModifyEntry( &Modify, &ModifyRes );
if ( ModifyRes ) {
Status = LsapDsMapDsReturnToStatusEx( &ModifyRes->CommRes );
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
LsapDsContinueTransaction();
LsapDsFree( AttrMod );
}
return( Status );
}
NTSTATUS
LsapDsLsaAttributeToDsAttribute(
IN PLSAP_DB_ATTRIBUTE LsaAttribute,
OUT PATTR Attr
)
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG Length = 0, Value;
PDSNAME DsName;
PLARGE_INTEGER LargeInt;
if ( LsaAttribute->AttribType == LsapDbAttribDsNameAsUnicode ) {
if ( LsaAttribute->AttributeValue != NULL &&
( ( PUNICODE_STRING )LsaAttribute->AttributeValue )->Length != 0 ) {
Length = DSNameSizeFromLen( LsapDsGetUnicodeStringLenNoNull(
(PUNICODE_STRING)LsaAttribute->AttributeValue) / sizeof( WCHAR ) );
} else {
return( STATUS_INVALID_PARAMETER );
}
} else if ( LsaAttribute->AttribType == LsapDbAttribIntervalAsULong ) {
Length = sizeof( LARGE_INTEGER );
} else if ( LsaAttribute->AttribType == LsapDbAttribUShortAsULong ) {
Length = sizeof( ULONG );
}
Attr->attrTyp = LsaAttribute->DsAttId;
Attr->AttrVal.valCount = 1;
Attr->AttrVal.pAVal = LsapDsAlloc( Length + sizeof( ATTRVAL ) );
if ( Attr->AttrVal.pAVal == NULL ) {
Status = STATUS_NO_MEMORY;
} else {
switch ( LsaAttribute->AttribType ) {
case LsapDbAttribUnicode:
//
// These unicode strings are self relative. Note that we have to write them out
// without a trailing NULL!
//
Attr->AttrVal.pAVal->valLen =
LsapDsGetSelfRelativeUnicodeStringLenNoNull(
(PUNICODE_STRING_SR)LsaAttribute->AttributeValue);
Attr->AttrVal.pAVal->pVal = LsaAttribute->AttributeValue;
Attr->AttrVal.pAVal->pVal += sizeof(UNICODE_STRING_SR);
break;
case LsapDbAttribMultiUnicode:
Status = STATUS_NOT_IMPLEMENTED;
break;
case LsapDbAttribGuid: // Fall through
case LsapDbAttribTime: // Fall through
case LsapDbAttribSid: // Fall through
case LsapDbAttribDsName:// Fall through
case LsapDbAttribPByte: // Fall through
case LsapDbAttribSecDesc: // Fall through
Attr->AttrVal.pAVal->valLen = LsaAttribute->AttributeValueLength;
Attr->AttrVal.pAVal->pVal = LsaAttribute->AttributeValue;
break;
case LsapDbAttribULong:
Attr->AttrVal.pAVal->valLen = sizeof(ULONG);
Attr->AttrVal.pAVal->pVal = LsaAttribute->AttributeValue;
break;
case LsapDbAttribUShortAsULong:
Attr->AttrVal.pAVal->valLen = sizeof(ULONG);
Attr->AttrVal.pAVal->pVal = ( ( PBYTE ) Attr->AttrVal.pAVal ) + sizeof( ATTRVAL );
Value = *( PULONG )LsaAttribute->AttributeValue;
Value &= 0xFFFF;
RtlCopyMemory( Attr->AttrVal.pAVal->pVal,
&Value,
sizeof( ULONG ) );
break;
case LsapDbAttribDsNameAsUnicode:
DsName = (PDSNAME)( ( ( PBYTE ) Attr->AttrVal.pAVal ) + sizeof( ATTRVAL ) );
DsName->structLen = Length;
DsName->NameLen = LsapDsGetUnicodeStringLenNoNull(
(PUNICODE_STRING)LsaAttribute->AttributeValue) / sizeof( WCHAR );
RtlCopyMemory( DsName->StringName,
((PUNICODE_STRING)LsaAttribute->AttributeValue)->Buffer,
(DsName->NameLen + 1 ) * sizeof ( WCHAR ) );
Attr->AttrVal.pAVal->pVal = (PUCHAR)DsName;
Attr->AttrVal.pAVal->valLen = DsName->structLen;
break;
case LsapDbAttribIntervalAsULong:
LargeInt = ( PLARGE_INTEGER )( ( ( PBYTE ) Attr->AttrVal.pAVal ) + sizeof( ATTRVAL ) );
*LargeInt = RtlConvertUlongToLargeInteger( *( PULONG )LsaAttribute->AttributeValue );
Attr->AttrVal.pAVal->pVal = (PUCHAR)LargeInt;
Attr->AttrVal.pAVal->valLen = sizeof( LARGE_INTEGER );
break;
default:
ASSERT(FALSE);
break;
}
}
return( Status );
}
NTSTATUS
LsapDsDsAttributeToLsaAttribute(
IN ATTRVAL *AttVal,
OUT PLSAP_DB_ATTRIBUTE LsaAttribute
)
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG Len, CopyLen;
PUNICODE_STRING_SR UnicodeStringSr;
PBYTE Buff, DsBuff;
//
// If we were supplied a buffer in the LSA attribute, copy it over
//
if ( LsaAttribute->AttributeValue != NULL && LsaAttribute->AttributeValueLength != 0 ) {
if ( AttVal->valLen > LsaAttribute->AttributeValueLength ) {
Status = STATUS_BUFFER_OVERFLOW;
} else {
RtlCopyMemory( LsaAttribute->AttributeValue, AttVal->pVal,
AttVal->valLen );
LsaAttribute->AttributeValueLength = AttVal->valLen;
}
LsaAttribute->MemoryAllocated = FALSE;
return( Status ) ;
}
//
// Allocate a new buffer using LSA heap, and then copy it over...
//
Len = AttVal->valLen;
CopyLen = AttVal->valLen;
DsBuff = AttVal->pVal;
if ( LsaAttribute->AttribType == LsapDbAttribUnicode ) {
Len += sizeof( UNICODE_STRING_SR ) + sizeof( WCHAR );
} else if ( LsaAttribute->AttribType == LsapDbAttribDsNameAsUnicode ) {
Len = ( LsapDsNameLenFromDsName( (PDSNAME)(AttVal->pVal) ) + 1 ) * sizeof( WCHAR ) +
sizeof( UNICODE_STRING );
CopyLen = 0;
DsBuff = (PBYTE)((PDSNAME)AttVal->pVal)->StringName;
} else if ( LsaAttribute->AttribType == LsapDbAttribIntervalAsULong ) {
Len = sizeof( ULONG );
CopyLen = sizeof( ULONG );
DsBuff = DsBuff;
} else if ( Len == 0 ) {
Buff = NULL;
Status = STATUS_INVALID_PARAMETER;
goto Exit;
}
Buff = MIDL_user_allocate( Len );
if ( Buff == NULL ) {
Status = STATUS_NO_MEMORY;
goto Exit;
} else if (LsaAttribute->AttribType != LsapDbAttribUnicode &&
LsaAttribute->AttribType != LsapDbAttribDsNameAsUnicode) {
RtlCopyMemory( Buff, DsBuff, CopyLen );
}
switch ( LsaAttribute->AttribType ) {
case LsapDbAttribUnicode:
//
// Make the string self relative
//
UnicodeStringSr = (PUNICODE_STRING_SR)Buff;
RtlCopyMemory( Buff + sizeof(UNICODE_STRING_SR), DsBuff, AttVal->valLen );
UnicodeStringSr->Length = (USHORT)AttVal->valLen;
UnicodeStringSr->MaximumLength = UnicodeStringSr->Length + sizeof( WCHAR );
UnicodeStringSr->Offset = sizeof(UNICODE_STRING_SR);
((PWSTR)(Buff+sizeof(UNICODE_STRING_SR)))[UnicodeStringSr->Length / sizeof(WCHAR)] = UNICODE_NULL;
LsaAttribute->AttributeValue = Buff;
LsaAttribute->AttributeValueLength = AttVal->valLen + sizeof( UNICODE_STRING_SR );
break;
case LsapDbAttribDsNameAsUnicode:
//
// Make the string self relative
//
UnicodeStringSr = (PUNICODE_STRING_SR)Buff;
RtlCopyMemory( Buff + sizeof(UNICODE_STRING_SR), DsBuff,
LsapDsNameLenFromDsName( (PDSNAME)(AttVal->pVal) ) * sizeof( WCHAR ) );
UnicodeStringSr->Length =
(USHORT)LsapDsNameLenFromDsName( (PDSNAME)(AttVal->pVal) ) * sizeof( WCHAR );
UnicodeStringSr->MaximumLength = UnicodeStringSr->Length + sizeof( WCHAR );
UnicodeStringSr->Offset = sizeof(UNICODE_STRING_SR);
((PWSTR)(Buff+sizeof(UNICODE_STRING_SR)))[UnicodeStringSr->Length / sizeof(WCHAR)] = UNICODE_NULL;
LsaAttribute->AttributeValue = Buff;
LsaAttribute->AttributeValueLength =
UnicodeStringSr->MaximumLength + sizeof( UNICODE_STRING_SR );
break;
case LsapDbAttribMultiUnicode:
Status = STATUS_NOT_IMPLEMENTED;
break;
case LsapDbAttribSecDesc:
LsaAttribute->AttributeValue = Buff;
LsaAttribute->AttributeValueLength = AttVal->valLen;
break;
case LsapDbAttribGuid:
Status = LsapDbMakeGuidAttribute( (GUID *)Buff,
LsaAttribute->AttributeName,
LsaAttribute );
break;
case LsapDbAttribSid:
Status = LsapDbMakeSidAttribute( (PSID)Buff,
LsaAttribute->AttributeName,
LsaAttribute );
break;
case LsapDbAttribPByte: // FALL THROUGH
case LsapDbAttribULong: // FALL THROUGH
case LsapDbAttribUShortAsULong: // FALL THROUGH
case LsapDbAttribTime:
Status = LsapDbMakePByteAttributeDs( Buff,
AttVal->valLen,
LsaAttribute->AttribType,
LsaAttribute->AttributeName,
LsaAttribute );
break;
case LsapDbAttribIntervalAsULong:
LsaAttribute->AttributeValue = Buff;
LsaAttribute->AttributeValueLength = sizeof( ULONG );
break;
default:
LsapDsDebugOut(( DEB_ERROR,
"Unexpected attribute type: %lu\n",
LsaAttribute->AttribType ));
Status = STATUS_INVALID_PARAMETER;
break;
}
Exit:
if ( NT_SUCCESS(Status) ) {
LsaAttribute->MemoryAllocated = TRUE;
} else {
MIDL_user_free( Buff );
LsaAttribute->AttributeValue = NULL;
LsaAttribute->AttributeValueLength = 0;
LsaAttribute->MemoryAllocated = FALSE;
}
return( Status );
}
NTSTATUS
LsapDsSearchMultiple(
IN ULONG Flags,
IN PDSNAME pContainer,
IN PATTR pAttrsToMatch,
IN ULONG cAttrs,
OUT SEARCHRES **SearchRes
)
{
NTSTATUS Status = STATUS_SUCCESS;
SEARCHARG SearchArg;
FILTER *Filters, RootFilter;
ENTINFSEL EntInfSel;
ULONG i;
RtlZeroMemory( &SearchArg, sizeof( SEARCHARG ) );
//
// Make sure we already have a transaction going
//
ASSERT( THQuery() );
//
// Build the filters
//
Filters = LsapDsAlloc(cAttrs * sizeof(FILTER) );
if ( Filters == NULL ) {
Status = STATUS_NO_MEMORY;
} else {
for ( i = 0; i < cAttrs; i++ ) {
Filters[i].pNextFilter = &Filters[i + 1];
Filters[i].choice = FILTER_CHOICE_ITEM;
Filters[i].FilterTypes.Item.choice = FI_CHOICE_EQUALITY;
Filters[i].FilterTypes.Item.FilTypes.ava.type = pAttrsToMatch[i].attrTyp;
Filters[i].FilterTypes.Item.FilTypes.ava.Value.valLen =
pAttrsToMatch[i].AttrVal.pAVal->valLen;
Filters[i].FilterTypes.Item.FilTypes.ava.Value.pVal =
pAttrsToMatch[i].AttrVal.pAVal->pVal;
}
if ( NT_SUCCESS( Status ) ) {
//
// Fill in the filter
//
Filters[cAttrs - 1].pNextFilter = NULL;
if ( cAttrs > 1 ) {
RtlZeroMemory( &RootFilter, sizeof (RootFilter));
RootFilter.pNextFilter = NULL;
if ( FLAG_ON( Flags, LSAPDS_SEARCH_OR ) ) {
RootFilter.choice = FILTER_CHOICE_OR;
RootFilter.FilterTypes.Or.count = (USHORT)cAttrs;
RootFilter.FilterTypes.Or.pFirstFilter = Filters;
} else {
RootFilter.choice = FILTER_CHOICE_AND;
RootFilter.FilterTypes.And.count = (USHORT)cAttrs;
RootFilter.FilterTypes.And.pFirstFilter = Filters;
}
SearchArg.pFilter = &RootFilter;
} else {
SearchArg.pFilter = Filters;
}
//
// Fill in the search argument
//
SearchArg.pObject = pContainer;
if ( ( Flags & LSAPDS_SEARCH_FLAGS ) == 0 || FLAG_ON( Flags, LSAPDS_SEARCH_TREE ) ) {
SearchArg.choice = SE_CHOICE_WHOLE_SUBTREE;
} else if ( FLAG_ON( Flags, LSAPDS_SEARCH_LEVEL ) ) {
SearchArg.choice = SE_CHOICE_IMMED_CHLDRN;
} else if ( FLAG_ON( Flags, LSAPDS_SEARCH_ROOT ) ) {
SearchArg.choice = SE_CHOICE_BASE_ONLY;
} else {
Status = STATUS_INVALID_PARAMETER;
}
if ( NT_SUCCESS( Status ) ) {
SearchArg.searchAliases = FALSE;
SearchArg.pSelection = &EntInfSel;
SearchArg.bOneNC = FLAG_ON(Flags, LSAPDS_SEARCH_ALL_NCS) ?
FALSE : TRUE;
EntInfSel.attSel = EN_ATTSET_LIST;
EntInfSel.AttrTypBlock.attrCount = 0;
EntInfSel.AttrTypBlock.pAttr = NULL;
EntInfSel.infoTypes = EN_INFOTYPES_TYPES_ONLY;
//
// Build the Commarg structure
//
LsapDsInitializeStdCommArg( &( SearchArg.CommArg ), 0 );
//
// Make the call
//
*SearchRes = NULL;
DirSearch( &SearchArg, SearchRes );
if ( *SearchRes ) {
Status = LsapDsMapDsReturnToStatusEx( &(*SearchRes)->CommRes );
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
LsapDsContinueTransaction();
}
}
}
return( Status ) ;
}
NTSTATUS
LsapDsSearchUnique(
IN ULONG Flags,
IN PDSNAME pContainer,
IN PATTR pAttrsToMatch,
IN ULONG cAttrs,
OUT PDSNAME *ppFoundName
)
{
NTSTATUS Status = STATUS_SUCCESS;
BOOLEAN CloseTransaction;
SEARCHRES *SearchRes;
ULONG i;
//
// Check the parameters for validity
//
ASSERT( pAttrsToMatch );
ASSERT( pAttrsToMatch->AttrVal.pAVal );
ASSERT( pContainer );
ASSERT( ppFoundName );
//
// See if we already have a transaction going
//
Status = LsapDsInitAllocAsNeededEx( LSAP_DB_READ_ONLY_TRANSACTION |
LSAP_DB_NO_LOCK |
LSAP_DB_DS_OP_TRANSACTION,
NullObject,
&CloseTransaction );
if ( !NT_SUCCESS( Status ) ) {
return( Status );
}
//
// Do the search
//
Status = LsapDsSearchMultiple( Flags,
pContainer,
pAttrsToMatch,
cAttrs,
&SearchRes );
if ( NT_SUCCESS( Status ) ) {
//
// See if we found the object
//
if ( SearchRes->count == 0 ) {
Status = STATUS_OBJECT_NAME_NOT_FOUND;
} else if ( SearchRes->count == 1 ) {
//
// Copy the name
//
*ppFoundName = LsapAllocateLsaHeap(
SearchRes->FirstEntInf.Entinf.pName->structLen );
if ( *ppFoundName == NULL ) {
Status = STATUS_NO_MEMORY;
} else {
RtlCopyMemory( *ppFoundName,
SearchRes->FirstEntInf.Entinf.pName,
SearchRes->FirstEntInf.Entinf.pName->structLen );
}
} else {
//
// More than one object was found!
//
Status = STATUS_OBJECT_NAME_COLLISION;
}
}
//
// Destruction of the thread state will delete any memory allocated via the DS
//
LsapDsDeleteAllocAsNeededEx( LSAP_DB_READ_ONLY_TRANSACTION |
LSAP_DB_NO_LOCK |
LSAP_DB_DS_OP_TRANSACTION,
NullObject,
CloseTransaction );
return( Status );
}
NTSTATUS
LsapDsFindUnique(
IN ULONG Flags,
IN PDSNAME NCName OPTIONAL,
IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId,
IN ATTRVAL *Attribute,
IN ATTRTYP AttId,
OUT PDSNAME *FoundObject
)
/*++
Routine Description:
This function will find the object with the given attribute. The attribute must be indexed.
Arguments:
Flags - Flags to control the operation of the find
NCName - DSNAME of the naming context under which to look
If not specified, the default NCNAME is used.
ObjectTypeId - ObjectType represented by DSNAME.
The corresponding lock will be locked.
If the ObjectType isn't known, pass AllObject to grab all locks.
Attribute - Attribute to be matched
AttrTyp - Attribute id of the attribute
FoundObject - Where the object's dsname is returned, if it is found
Return Value:
STATUS_SUCCESS - Success
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
FINDARG FindArg;
FINDRES *FindRes;
BOOLEAN CloseTransaction = FALSE;
LsapEnterFunc( "LsapDsFindUnique ");
//
// See if we already have a transaction going
//
Status = LsapDsInitAllocAsNeededEx( LSAP_DB_READ_ONLY_TRANSACTION |
LSAP_DB_DS_OP_TRANSACTION,
ObjectTypeId,
&CloseTransaction );
if ( !NT_SUCCESS( Status ) ) {
LsapExitFunc( "LsapDsFindUnique", Status );
return( Status );
}
//
// Do the initialization
//
RtlZeroMemory(&FindArg,sizeof(FINDARG));
if ( NCName == NULL ) {
FindArg.hDomain = LsaDsStateInfo.DsDomainHandle;
} else {
FindArg.hDomain = DirGetDomainHandle( NCName );
if (0==FindArg.hDomain)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
LsapDsContinueTransaction();
}
FindArg.AttId = AttId;
RtlCopyMemory( &FindArg.AttrVal,
Attribute,
sizeof( ATTRVAL ) );
LsapDsInitializeStdCommArg( &( FindArg.CommArg ), 0 );
DirFindEntry( &FindArg, &FindRes );
if ( FindRes ) {
Status = LsapDsMapDsReturnToStatusEx( &(FindRes->CommRes ) );
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
if ( NT_SUCCESS( Status ) ) {
//
// Copy the return value using the lsa allocator
//
Status = LsapDsCopyDsNameLsa( FoundObject,
FindRes->pObject );
}
LsapDsContinueTransaction();
Error:
//
// Destruction of the thread state will delete any memory allocated via the DS
//
LsapDsDeleteAllocAsNeededEx( LSAP_DB_READ_ONLY_TRANSACTION |
LSAP_DB_DS_OP_TRANSACTION,
ObjectTypeId,
CloseTransaction );
LsapExitFunc( "LsapDsFindUnqiue", Status );
return( Status );
}
NTSTATUS
LsapDsIsSecretDsTrustedDomain(
IN PUNICODE_STRING SecretName,
IN PLSAP_DB_OBJECT_INFORMATION ObjectInformation OPTIONAL,
IN ULONG Options,
IN ACCESS_MASK DesiredAccess,
OUT PLSAPR_HANDLE TDObjHandle OPTIONAL,
OUT BOOLEAN *IsTrustedDomainSecret
)
/*++
Routine Description:
This function will determine if the indicated secret is the global secret for a trust object.
Arguments:
SecretName - Name of secret to check
ObjectInformation - LsaDb information on the object
Need not be specified if no TDObjHandle is to be returned.
Options - Options to use for the access
Need not be specified if no TDObjHandle is to be returned.
DesiredAccess - Access to open the object with
Need not be specified if no TDObjHandle is to be returned.
TDObjHandle - Where the object handle is returned
If not specified, no handle is returned.
IsTrustedDomainSecret - A TRUE is returned here if this secret is indeed a trusted domain
secret.
Return Value:
STATUS_SUCCESS - Success
STATUS_INSUFFICIENT_RESOURCES - A memory allocation failed
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
PWSTR pwszSecretName;
UNICODE_STRING TdoName;
PLSAP_DB_TRUSTED_DOMAIN_LIST_ENTRY TrustedDomainListEntry;
BOOLEAN AcquiredTrustedDomainListReadLock = FALSE;
LsapEnterFunc( "LsapDsIsSecretDsTrustedDomain" );
*IsTrustedDomainSecret = FALSE;
LsapDsReturnSuccessIfNoDs
if ( LsaDsStateInfo.DsInitializedAndRunning == FALSE ) {
LsapDsDebugOut((DEB_ERROR,
"LsapDsIsSecretDsTrustedDomain: Object %wZ, Ds is not started\n ",
SecretName ));
goto Cleanup;
}
//
// Convert the secret name to a TDO name.
//
if ( SecretName->Length <= (LSAP_DS_TRUSTED_DOMAIN_SECRET_PREFIX_LENGTH * sizeof(WCHAR)) ) {
goto Cleanup;
}
pwszSecretName = SecretName->Buffer + LSAP_DS_TRUSTED_DOMAIN_SECRET_PREFIX_LENGTH;
LsapDsDebugOut((DEB_TRACE, "Matching secret %ws to trusted domain\n ", pwszSecretName ));
RtlInitUnicodeString( &TdoName, pwszSecretName );
//
// Acquire the Read Lock for the Trusted Domain List
//
Status = LsapDbAcquireReadLockTrustedDomainList();
if (!NT_SUCCESS(Status)) {
goto Cleanup;
}
AcquiredTrustedDomainListReadLock = TRUE;
//
// Verify that the Trusted Domain List is marked as valid.
//
if (!LsapDbIsValidTrustedDomainList()) {
Status = STATUS_INVALID_SERVER_STATE;
goto Cleanup;
}
//
// Lookup the name in the TDL
//
Status = LsapDbLookupNameTrustedDomainListEx(
(PLSAPR_UNICODE_STRING)&TdoName,
&TrustedDomainListEntry );
if ( !NT_SUCCESS(Status)) {
//
// Not a trusted domain.
//
if ( Status == STATUS_NO_SUCH_DOMAIN ) {
Status = STATUS_SUCCESS;
}
goto Cleanup;
}
//
// See if this TDO is also a secret.
//
if ( FLAG_ON( TrustedDomainListEntry->TrustInfoEx.TrustDirection, TRUST_DIRECTION_OUTBOUND ) ) {
*IsTrustedDomainSecret = TRUE;
}
//
// If the caller wants a handle,
// return one.
//
if ( TDObjHandle ) {
LSAP_DB_OBJECT_INFORMATION NewObjInfo;
RtlCopyMemory( &NewObjInfo, ObjectInformation, sizeof( LSAP_DB_OBJECT_INFORMATION ) );
NewObjInfo.ObjectTypeId = TrustedDomainObject;
NewObjInfo.ObjectAttributes.ObjectName = &TdoName;
Status = LsapDbOpenObject( &NewObjInfo,
DesiredAccess,
Options | LSAP_DB_DS_TRUSTED_DOMAIN_AS_SECRET,
TDObjHandle );
}
Cleanup:
if ( AcquiredTrustedDomainListReadLock ) {
LsapDbReleaseLockTrustedDomainList();
}
LsapExitFunc( "LsapDsIsSecretDsTrustedDomain", Status );
return( Status );
}
NTSTATUS
LsapDsIsHandleDsObjectTypeHandle(
IN LSAP_DB_HANDLE Handle,
IN LSAP_DB_OBJECT_TYPE_ID ObjectType,
OUT BOOLEAN *IsObjectHandle
)
/*++
Routine Description:
Determines if the object handle refers to an object of the specified type or not
Arguments:
Handle - Handle to verify
ObjectType - Type of object to verify
IsObjectHandle - If TRUE, the handle refers to an object of that type
Return Value:
NTSTATUS - Standard Nt Result Code
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
ATTR NameAttr;
ATTRVAL NameVal;
ATTRBLOCK NameBlock, ReturnBlock;
PDSNAME SearchName = NULL;
BOOLEAN CloseTransaction = FALSE;
ULONG ReadVal, Class;
BOOLEAN TsAllocated = FALSE;
*IsObjectHandle = FALSE;
LsapDsReturnSuccessIfNoDs;
if ( !LsapDsIsHandleDsHandle( Handle ) ) {
return( STATUS_SUCCESS );
}
if ( LsaDsStateInfo.DsInitializedAndRunning == FALSE ) {
LsapDsDebugOut((DEB_TRACE,
"LsapDsIsHandleDsObjectTypeHandle: Object %wZ, Ds is not started\n ",
&Handle->LogicalNameU ));
return( Status );
}
Class = LsapClassIdFromObjType( ObjectType );
if ( Class == 0xFFFFFFFF ) {
Status = STATUS_INVALID_PARAMETER;
}
//
// Special case the situation where we're looking for a secret object but we were given a
// trusted domain object handle
//
if ( ObjectType == TrustedDomainObject &&
((LSAP_DB_HANDLE)Handle)->ObjectTypeId == TrustedDomainObject &&
FLAG_ON( ((LSAP_DB_HANDLE)Handle)->Options, LSAP_DB_DS_TRUSTED_DOMAIN_AS_SECRET ) ) {
*IsObjectHandle = TRUE;
return( STATUS_SUCCESS );
}
if ( ObjectType == TrustedDomainObject &&
((LSAP_DB_HANDLE)Handle)->ObjectTypeId == SecretObject &&
FLAG_ON( ((LSAP_DB_HANDLE)Handle)->Options, LSAP_DB_DS_TRUSTED_DOMAIN_AS_SECRET ) ) {
*IsObjectHandle = FALSE;
return( STATUS_SUCCESS );
}
if ( NT_SUCCESS( Status ) ) {
//
// We can't lock any locks because the caller already has some locks locked.
// So locking an object type specific lock might violate the locking order.
//
// Locking any lock doesn't help anyway. An object can disappear out
// from under us due to replication or immediately after we drop the lock.
//
Status = LsapDsInitAllocAsNeededEx( LSAP_DB_READ_ONLY_TRANSACTION |
LSAP_DB_NO_LOCK |
LSAP_DB_DS_OP_TRANSACTION,
ObjectType,
&CloseTransaction );
if ( NT_SUCCESS( Status ) ) {
TsAllocated = TRUE;
Status = LsapAllocAndInitializeDsNameFromUnicode(
(PLSA_UNICODE_STRING)&Handle->PhysicalNameDs,
&SearchName );
}
}
if ( NT_SUCCESS( Status ) ) {
//
// Check for the existence of the object
//
NameAttr.attrTyp = ATT_OBJECT_CLASS;
NameAttr.AttrVal.valCount = 1;
NameAttr.AttrVal.pAVal = &NameVal;
NameVal.valLen = SearchName->structLen;
NameVal.pVal = (PBYTE)SearchName;
NameBlock.attrCount = 1;
NameBlock.pAttr = &NameAttr;
Status = LsapDsRead( &Handle->PhysicalNameDs,
LSAPDS_READ_NO_LOCK,
&NameBlock,
&ReturnBlock);
if ( NT_SUCCESS( Status ) ) {
ReadVal = LSAP_DS_GET_DS_ATTRIBUTE_AS_ULONG( ReturnBlock.pAttr );
if ( ReadVal == Class ) {
*IsObjectHandle = TRUE;
}
}
LsapDsFree( SearchName );
}
//
// If the object exists and we're looking for a global secret of the same name, then
//
if ( TsAllocated ) {
LsapDsDeleteAllocAsNeededEx( LSAP_DB_READ_ONLY_TRANSACTION |
LSAP_DB_NO_LOCK |
LSAP_DB_DS_OP_TRANSACTION,
ObjectType,
CloseTransaction );
}
return( Status );
}
NTSTATUS
LsapDsCauseTransactionToCommitOrAbort (
IN BOOLEAN Commit
)
/*++
Routine Description:
Arguments:
Return Value:
STATUS_SUCCESS - Success
--*/
{
NTSTATUS Status;
ATTR Attr;
ENTINFSEL EntryInf;
READARG ReadArg;
READRES *ReadRes = NULL;
//
// Initialize the structures
//
RtlZeroMemory(&Attr, sizeof(ATTR));
Attr.attrTyp = ATT_OBJECT_CLASS;
RtlZeroMemory(&EntryInf, sizeof(ENTINFSEL));
EntryInf.attSel = EN_ATTSET_LIST;
EntryInf.AttrTypBlock.attrCount = 1;
EntryInf.AttrTypBlock.pAttr = &Attr;
EntryInf.infoTypes = EN_INFOTYPES_TYPES_VALS;
RtlZeroMemory(&ReadArg, sizeof(READARG));
ReadArg.pSel = &EntryInf;
//
// Initialize the commarg struct
//
LsapDsInitializeStdCommArg( &ReadArg.CommArg, 0 );
//
// If there is no transaction, just exit
//
if ( !THQuery() ) {
return( STATUS_SUCCESS );
}
if ( !SampExistsDsTransaction() ) {
LsapDsDebugOut(( DEB_ERROR,
"LsapDsCauseTransactionToCommitOrAbort invoked with no active DS "
"transaction\n" ));
return( STATUS_SUCCESS );
}
//
// Clear any errors in the thread state
//
THClearErrors();
DirTransactControl( TRANSACT_DONT_BEGIN_END );
ReadArg.pObject = LsaDsStateInfo.DsRoot;
if ( Commit == FALSE ) {
Attr.attrTyp = 0xFFFFFFFF;
} else {
Attr.attrTyp = ATT_OBJECT_CLASS;
}
DirRead( &ReadArg, &ReadRes );
if ( ReadRes ) {
Status = LsapDsMapDsReturnToStatusEx( &ReadRes->CommRes );
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
DirTransactControl( TRANSACT_BEGIN_END );
return( Status );
}
NTSTATUS
LsapDsSearchNonUnique(
IN ULONG Flags,
IN PDSNAME pContainer,
IN PATTR pAttrsToMatch,
IN ULONG Attrs,
OUT PDSNAME **pppFoundNames,
OUT PULONG pcNames
)
/*++
Routine Description:
This routine will search the specified container(s) in the DS for the object
whose attributes are matched with pAttrToMatch. The returned DSNAME structures are allocated
via LSA memory allocators. The returned list should be freed via a single call to
LsapFreeLsaHeap
Arguments:
DsInitState -- State the DS booted off of
Return Value:
STATUS_SUCCES -- Success
STATUS_NO_MEMORY -- A memory allocation failed
STATUS_OBJECT_NAME_NOT_FOUND -- The object did not exist
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
BOOLEAN CloseTransaction;
ULONG OutputLen, i;
PBYTE Buff;
SEARCHRES *SearchRes;
ENTINFLIST *EntInfList;
//
// Check the parameters for validity
//
ASSERT( pAttrsToMatch );
ASSERT( pContainer );
ASSERT( pppFoundNames );
ASSERT( pcNames );
//
// See if we already have a transaction going
//
Status = LsapDsInitAllocAsNeededEx( LSAP_DB_READ_ONLY_TRANSACTION |
LSAP_DB_DS_OP_TRANSACTION |
LSAP_DB_NO_LOCK,
PolicyObject,
&CloseTransaction );
if ( !NT_SUCCESS( Status ) ) {
return( Status );
}
Status = LsapDsSearchMultiple( Flags,
pContainer,
pAttrsToMatch,
Attrs,
&SearchRes );
if ( NT_SUCCESS( Status ) ) {
//
// See if we found the object
//
if ( SearchRes->count == 0 ) {
Status = STATUS_OBJECT_NAME_NOT_FOUND;
} else if ( SearchRes->count >= 1 ) {
//
// See how big a buffer we need to allocate
//
OutputLen = sizeof( PDSNAME ) * SearchRes->count;
EntInfList = &(SearchRes->FirstEntInf);
for ( i = 0; i < SearchRes->count ; i++) {
OutputLen += ROUND_UP_COUNT( EntInfList->Entinf.pName->structLen, ALIGN_WORST );
EntInfList = EntInfList->pNextEntInf;
}
//
// Allocate it
//
*pppFoundNames = LsapAllocateLsaHeap( OutputLen );
//
// Copy the names
//
if ( *pppFoundNames == NULL ) {
Status = STATUS_NO_MEMORY;
} else {
Buff = ((PBYTE)*pppFoundNames) + (sizeof( PDSNAME ) * SearchRes->count);
EntInfList = &SearchRes->FirstEntInf;
for (i = 0; i < SearchRes->count ; i++ ) {
(*pppFoundNames)[i] = (PDSNAME)Buff;
RtlCopyMemory( Buff,
EntInfList->Entinf.pName,
EntInfList->Entinf.pName->structLen );
Buff += ROUND_UP_COUNT( EntInfList->Entinf.pName->structLen, ALIGN_WORST );
EntInfList = EntInfList->pNextEntInf;
}
*pcNames = SearchRes->count;
}
}
}
//
// Destruction of the thread state will delete the memory for pVal
//
LsapDsDeleteAllocAsNeededEx( LSAP_DB_READ_ONLY_TRANSACTION |
LSAP_DB_DS_OP_TRANSACTION |
LSAP_DB_NO_LOCK,
PolicyObject,
CloseTransaction );
return( Status );
}
NTSTATUS
LsapDsFixupTrustForXrefChange(
IN PDSNAME ObjectPath,
IN BOOLEAN TransactionActive
);
NTSTATUS
LsapDsMorphTrustsToUplevel(
VOID
)
/*++
Routine Description
This function will first enumerate all the cross ref's in the partitions container and for
each Xref will try patching up the corresponding TDO
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG ClassId = CLASS_CROSS_REF;
SEARCHRES *pSearchRes;
ENTINFLIST *CurrentEntInf = NULL;
BOOLEAN CloseTransaction = FALSE;
BOOLEAN ActiveThreadState = FALSE;
ATTRVAL XRefAttVals[] = {
{ sizeof(ULONG), (PUCHAR)&ClassId} };
ATTR XRefAttrs[] = {
{ ATT_OBJECT_CLASS, {1, &XRefAttVals[0] } },
};
ULONG CountOfDomains=0;
PDSNAME *ListOfDomains = NULL;
ULONG i;
//
// Acquire the Trusted domain lock
//
LsapDbAcquireLockEx( TrustedDomainObject,
0);
//
// Begin a Transaction
//
Status = LsapDsInitAllocAsNeededEx(
LSAP_DB_NO_LOCK,
TrustedDomainObject,
&CloseTransaction
);
if (!NT_SUCCESS(Status))
goto Error;
ActiveThreadState = TRUE;
Status = LsapDsSearchMultiple(
0,
LsaDsStateInfo.DsPartitionsContainer,
XRefAttrs,
1,
&pSearchRes
);
if (!NT_SUCCESS(Status))
{
//
// Bail, most likely resource failure
//
goto Error;
}
ASSERT(NULL!=pSearchRes);
//
// At least 1 Xref should be present otherwise there is something odd
// going on in here
//
ASSERT((pSearchRes->count>=1) && "No Xrefs In Partitions Container !!!");
CountOfDomains = pSearchRes->count;
ListOfDomains = LsapAllocateLsaHeap(CountOfDomains * sizeof(PDSNAME));
if (NULL==ListOfDomains)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
RtlZeroMemory(ListOfDomains,CountOfDomains * sizeof(PDSNAME));
//
// Walk through the linked list of entries returned by the DS
// and perform and copy the dsnames for each of the domains that were returned. Copying is
// required as once the DS thread state is deleted all the memory allocated by it is lost
//
for (CurrentEntInf = &(pSearchRes->FirstEntInf),i=0;
CurrentEntInf!=NULL;
CurrentEntInf=CurrentEntInf->pNextEntInf,i++)
{
ListOfDomains[i] = LsapAllocateLsaHeap(CurrentEntInf->Entinf.pName->structLen);
if (NULL==ListOfDomains[i])
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
RtlCopyMemory(
ListOfDomains[i],
CurrentEntInf->Entinf.pName,
CurrentEntInf->Entinf.pName->structLen
);
}
//
// Close the transaction now so that we may free up resources
// Closing the transaction and thread state now and opening up a new
// transaction/thread state per object keeps the memory consumption much
// lower and also curtails the transaction length. Remember the DS does
// not free any memory till the thread state is destroyed. If we have to
// scale to 2000 trust objects, doing it this way provides for better
// performance
//
LsapDsDeleteAllocAsNeededEx2(
LSAP_DB_NO_LOCK,
TrustedDomainObject,
CloseTransaction,
FALSE // Rollback Transaction
);
ASSERT(!SampExistsDsTransaction());
ASSERT(THVerifyCount(0));
ActiveThreadState = FALSE;
//
// For each DS NAME in the list check cross ref and update
// TDO if necessary
//
for (i=0;i<CountOfDomains;i++)
{
Status = LsapDsFixupTrustForXrefChange(
ListOfDomains[i],
FALSE
);
if (!NT_SUCCESS(Status))
{
//
// Continue on all errors excepting resource errors
// The caller logs any errors to the event log
//
if (!LsapDsIsNtStatusResourceError(Status))
{
Status = STATUS_SUCCESS;
}
else
{
//
// Break out of the loop and terminate the upgrade
//
goto Error;
}
}
}
Error:
//
// Free up resources
//
if (ListOfDomains)
{
for (i=0;i<CountOfDomains;i++)
{
if (ListOfDomains[i])
{
LsapFreeLsaHeap(ListOfDomains[i]);
}
}
LsapFreeLsaHeap(ListOfDomains);
}
if (ActiveThreadState)
{
BOOLEAN RollbackTransaction = (NT_SUCCESS(Status))?FALSE:TRUE;
LsapDsDeleteAllocAsNeededEx2(
LSAP_DB_NO_LOCK,
TrustedDomainObject,
CloseTransaction,
RollbackTransaction // Rollback Transaction
);
}
ASSERT(!SampExistsDsTransaction());
ASSERT(THVerifyCount(0));
LsapDbReleaseLockEx( TrustedDomainObject,
0);
return Status;
}
NTSTATUS
LsaIUpgradeRegistryToDs(
BOOLEAN DeleteOnly
)
/*++
Routine Description:
This function will upgrade the policy/trusted domain/secret objects in the registry
and move them to the Ds.
Arguments:
VOID
Return Value:
STATUS_SUCCESS -- Success
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
BOOLEAN ReplicatorState = LsapDbState.ReplicatorNotificationEnabled;
//
// Lock the database
//
LsapDbAcquireLockEx( AllObject,
0 );
//
// Set the upgrade flag
//
LsaDsStateInfo.Nt4UpgradeInProgress = TRUE;
//
// Ok, move the accounts...
//
LsapDbDisableReplicatorNotification();
//
// Upgrade TDO's in registry to DS.
//
Status = LsapDsDomainUpgradeRegistryToDs( DeleteOnly );
if ( !NT_SUCCESS( Status ) ) {
LsapDsDebugOut(( DEB_ERROR,
"Trusted Domain upgrade failed: 0x%lx\n", Status ));
}
//
// Upgrade secrets in registry to DS
//
if ( NT_SUCCESS( Status ) ) {
Status = LsapDsSecretUpgradeRegistryToDs( DeleteOnly );
if ( !NT_SUCCESS( Status ) ) {
LsapDsDebugOut(( DEB_ERROR,
"Secret upgrade failed: 0x%lx\n", Status ));
}
}
//
// Upgrade interdomain trust accounts in SAM to DS.
//
if ( !DeleteOnly ) {
if (NT_SUCCESS(Status)) {
Status = LsapDsDomainUpgradeInterdomainTrustAccountsToDs( );
if ( !NT_SUCCESS( Status ) ) {
LsapDsDebugOut(( DEB_ERROR,
"InterdomainTrustAccount upgrade failed with 0x%lx\n",
Status ));
}
}
if (NT_SUCCESS(Status))
{
Status = LsapDsMorphTrustsToUplevel();
if (!NT_SUCCESS(Status)) {
LsapDsDebugOut(( DEB_ERROR,
"Morphing Trusts to NT5 failed 0x%lx\n", Status ));
}
}
}
if ( ReplicatorState ) {
LsapDbEnableReplicatorNotification();
}
LsapDbReleaseLockEx( AllObject,
0 );
LsaDsStateInfo.Nt4UpgradeInProgress = FALSE;
ASSERT(!SampExistsDsTransaction());
ASSERT(THVerifyCount(0));
return( Status );
}
NTSTATUS
LsapDsRenameObject(
IN PDSNAME OldObject,
IN PDSNAME NewParent,
IN ULONG AttrType,
IN PUNICODE_STRING NewObject
)
/*++
Routine Description:
This function will change the rdn of an object
Arguments:
OldObject -- Current object name
Return Value:
STATUS_SUCCESS -- Success
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
BOOLEAN CloseTransaction;
MODIFYDNARG ModifyDnArg;
MODIFYDNRES *ModifyRes = NULL;
ATTR NewDn;
ATTRVAL NewDnVal;
ASSERT( OldObject );
ASSERT( NewObject );
Status = LsapDsInitAllocAsNeededEx( LSAP_DB_NO_LOCK,
AllObject,
&CloseTransaction );
if ( !NT_SUCCESS( Status ) ) {
return( Status );
}
RtlZeroMemory( &ModifyDnArg, sizeof( ModifyDnArg ) );
NewDnVal.valLen = NewObject->Length;
NewDnVal.pVal = ( PUCHAR )NewObject->Buffer;
NewDn.attrTyp = AttrType;
NewDn.AttrVal.valCount = 1;
NewDn.AttrVal.pAVal = &NewDnVal;
ModifyDnArg.pObject = OldObject;
ModifyDnArg.pNewParent = NewParent;
ModifyDnArg.pNewRDN = &NewDn;
LsapDsInitializeStdCommArg( &(ModifyDnArg.CommArg), 0 );
DirModifyDN( &ModifyDnArg, &ModifyRes );
if ( ModifyRes ) {
Status = LsapDsMapDsReturnToStatusEx( &ModifyRes->CommRes );
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
LsapDsContinueTransaction();
LsapDsDeleteAllocAsNeededEx( LSAP_DB_NO_LOCK,
AllObject,
CloseTransaction );
return( Status );
}
NTSTATUS NetpApiStatusToNtStatus( NET_API_STATUS );
NTSTATUS
LsapDbDomainRenameHandler(
OUT BOOLEAN * Renamed
)
/*++
Routine Description:
Checks the domain name values stored in the registry against those reported
by the DS. DS values are treated as master copies and registry is updated
accordingly.
Parameters:
None
Returns:
STATUS_SUCCESS - success
STATUS_ error code on failure
--*/
{
NTSTATUS Status;
LSAP_DB_ATTRIBUTE AttributesReadReg[4]; // attrs read from registry
LSAP_DB_ATTRIBUTE AttributesReadDsDom[2]; // attrs read from domain XRef
LSAP_DB_ATTRIBUTE AttributesReadDsRoot[1]; // attrs read from root XRef
LSAP_DB_ATTRIBUTE AttributesWriteReg[4]; // attrs written to registry
PLSAP_DB_ATTRIBUTE NextAttribute;
ULONG AttributeCountReadReg = 0;
ULONG AttributeCountReadDsDom = 0;
ULONG AttributeCountReadDsRoot = 0;
ULONG AttributeCountWriteReg = 0;
ULONG AttributeNumber;
ULONG DomainLen = 0, RootDomainLen = 0;
PDSNAME DomainXRef = NULL;
PDSNAME RootDomainXRef = NULL;
UNICODE_STRING PrimaryDomainNameReg,
AccountDomainNameReg,
DnsDomainNameReg,
RootDnsDomainNameReg,
DnsDomainNameDs,
NetbiosDomainNameDs,
RootDnsDomainNameDs;
ULONG iPolDnDDN, iPolDnTrN, iPolPrDmN, iPolAcDmN;
ULONG iXRefDnsRoot, iXRefNetbiosName, iXRefDnsRoot2;
WCHAR NameBuffer[DNS_MAX_NAME_LENGTH + 1];
DWORD NameBufferSize = DNS_MAX_NAME_LENGTH + 1;
WCHAR ErrorCode[16];
LPWSTR pErrorCode;
DWORD Reason = LSA_DOMAIN_RENAME_ERROR1;
LsarpReturnCheckSetup();
ASSERT( Renamed );
*Renamed = FALSE;
//
// Read the attributes of interest out of the registry
//
ASSERT( AttributeCountReadReg == 0 );
NextAttribute = AttributesReadReg;
//
// Request read of the DNS domain name attribute
//
LsapDbInitializeAttributeDs(
NextAttribute,
PolDnDDN,
NULL,
0,
FALSE
);
iPolDnDDN = AttributeCountReadReg;
NextAttribute++;
AttributeCountReadReg++;
//
// Request read of the Dns Tree Name attribute
//
LsapDbInitializeAttributeDs(
NextAttribute,
PolDnTrN,
NULL,
0,
FALSE
);
iPolDnTrN = AttributeCountReadReg;
NextAttribute++;
AttributeCountReadReg++;
//
// Request read of the primary domain name attribute
//
LsapDbInitializeAttributeDs(
NextAttribute,
PolPrDmN,
NULL,
0,
FALSE
);
iPolPrDmN = AttributeCountReadReg;
NextAttribute++;
AttributeCountReadReg++;
//
// Request read of the account domain name attribute
//
LsapDbInitializeAttributeDs(
NextAttribute,
PolAcDmN,
NULL,
0,
FALSE
);
iPolAcDmN = AttributeCountReadReg;
NextAttribute++;
AttributeCountReadReg++;
Status = LsapDbReadAttributesObject(
LsapPolicyHandle,
0,
AttributesReadReg,
AttributeCountReadReg
);
if ( !NT_SUCCESS( Status )) {
goto Error;
}
LsapDbCopyUnicodeAttributeNoAlloc(
&PrimaryDomainNameReg,
&AttributesReadReg[iPolPrDmN],
TRUE
);
LsapDbCopyUnicodeAttributeNoAlloc(
&AccountDomainNameReg,
&AttributesReadReg[iPolAcDmN],
TRUE
);
LsapDbCopyUnicodeAttributeNoAlloc(
&DnsDomainNameReg,
&AttributesReadReg[iPolDnDDN],
TRUE
);
LsapDbCopyUnicodeAttributeNoAlloc(
&RootDnsDomainNameReg,
&AttributesReadReg[iPolDnTrN],
TRUE
);
//
// Now read the DS to get the values to compare against
// Start by reading the names of domain and root domain cross-ref objects
//
Status = GetConfigurationName(
DSCONFIGNAME_DOMAIN_CR,
&DomainLen,
NULL
);
ASSERT( Status == STATUS_BUFFER_TOO_SMALL );
DomainXRef = ( PDSNAME )LsapAllocateLsaHeap( DomainLen );
if ( DomainXRef == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
Status = GetConfigurationName(
DSCONFIGNAME_DOMAIN_CR,
&DomainLen,
DomainXRef
);
if ( !NT_SUCCESS( Status )) {
goto Error;
}
Status = GetConfigurationName(
DSCONFIGNAME_ROOT_DOMAIN_CR,
&RootDomainLen,
NULL
);
ASSERT( Status == STATUS_BUFFER_TOO_SMALL );
RootDomainXRef = ( PDSNAME )LsapAllocateLsaHeap( RootDomainLen );
if ( RootDomainXRef == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
Status = GetConfigurationName(
DSCONFIGNAME_ROOT_DOMAIN_CR,
&RootDomainLen,
RootDomainXRef
);
if ( !NT_SUCCESS( Status )) {
goto Error;
}
//
// From the domain cross-ref object,
// read the "NetbiosName" and "DnsRoot" attributes
//
ASSERT( AttributeCountReadDsDom == 0 );
NextAttribute = AttributesReadDsDom;
LsapDbInitializeAttributeDs(
NextAttribute,
XRefDnsRoot,
NULL,
0,
FALSE
);
iXRefDnsRoot = AttributeCountReadDsDom;
NextAttribute++;
AttributeCountReadDsDom++;
LsapDbInitializeAttributeDs(
NextAttribute,
XRefNetbiosName,
NULL,
0,
FALSE
);
iXRefNetbiosName = AttributeCountReadDsDom;
NextAttribute++;
AttributeCountReadDsDom++;
Status = LsapDsReadAttributesByDsName(
DomainXRef,
0,
AttributesReadDsDom,
AttributeCountReadDsDom
);
if ( !NT_SUCCESS( Status )) {
goto Error;
}
LsapDbCopyUnicodeAttributeNoAlloc(
&DnsDomainNameDs,
&AttributesReadDsDom[iXRefDnsRoot],
TRUE
);
LsapDbCopyUnicodeAttributeNoAlloc(
&NetbiosDomainNameDs,
&AttributesReadDsDom[iXRefNetbiosName],
TRUE
);
//
// From the root domain cross-ref object,
// read the "DnsRoot" attribute
//
ASSERT( AttributeCountReadDsRoot == 0 );
NextAttribute = AttributesReadDsRoot;
LsapDbInitializeAttributeDs(
NextAttribute,
XRefDnsRoot,
NULL,
0,
FALSE
);
iXRefDnsRoot2 = AttributeCountReadDsRoot;
NextAttribute++;
AttributeCountReadDsRoot++;
Status = LsapDsReadAttributesByDsName(
RootDomainXRef,
0,
AttributesReadDsRoot,
AttributeCountReadDsRoot
);
if ( !NT_SUCCESS( Status )) {
goto Error;
}
LsapDbCopyUnicodeAttributeNoAlloc(
&RootDnsDomainNameDs,
&AttributesReadDsRoot[iXRefDnsRoot2],
TRUE
);
//
// See if the values in the registry match what's in the DS,
// and if they don't, update the registry
//
ASSERT( AttributeCountWriteReg == 0 );
NextAttribute = AttributesWriteReg;
//
// Match the netbios name in the domain object XRef against
// the primary domain name in the registry
//
if ( !RtlEqualUnicodeString(
&PrimaryDomainNameReg,
&NetbiosDomainNameDs,
TRUE )) {
Status = LsapDbMakeUnicodeAttributeDs(
&NetbiosDomainNameDs,
PolPrDmN,
NextAttribute
);
if ( !NT_SUCCESS( Status )) {
goto Error;
}
NextAttribute++;
AttributeCountWriteReg++;
}
//
// Match the netbios name in the domain object XRef against
// the account domain name in the registry
//
if ( !RtlEqualUnicodeString(
&AccountDomainNameReg,
&NetbiosDomainNameDs,
TRUE )) {
Status = LsapDbMakeUnicodeAttributeDs(
&NetbiosDomainNameDs,
PolAcDmN,
NextAttribute
);
if ( !NT_SUCCESS( Status )) {
goto Error;
}
NextAttribute++;
AttributeCountWriteReg++;
}
//
// Match the DNS name in the domain object XRef against
// the DNS domain name in the registry
//
if ( !RtlEqualUnicodeString(
&DnsDomainNameReg,
&DnsDomainNameDs,
TRUE )) {
Status = LsapDbMakeUnicodeAttributeDs(
&DnsDomainNameDs,
PolDnDDN,
NextAttribute
);
if ( !NT_SUCCESS( Status )) {
goto Error;
}
NextAttribute++;
AttributeCountWriteReg++;
}
//
// Match the DNS name in the root domain object XRef against
// the root DNS domain name in the registry
//
if ( !RtlEqualUnicodeString(
&RootDnsDomainNameReg,
&RootDnsDomainNameDs,
TRUE )) {
Status = LsapDbMakeUnicodeAttributeDs(
&RootDnsDomainNameDs,
PolDnTrN,
NextAttribute
);
if ( !NT_SUCCESS( Status )) {
goto Error;
}
NextAttribute++;
AttributeCountWriteReg++;
}
//
// See if anything in the registry has got to change
// and if so, update it
//
if ( AttributeCountWriteReg > 0 ) {
Status = LsapDbReferenceObject(
LsapPolicyHandle,
0,
PolicyObject,
PolicyObject,
LSAP_DB_LOCK |
LSAP_DB_START_TRANSACTION
);
if ( NT_SUCCESS( Status )) {
Status = LsapDbWriteAttributesObject(
LsapPolicyHandle,
AttributesWriteReg,
AttributeCountWriteReg
);
Status = LsapDbDereferenceObject(
&LsapPolicyHandle,
PolicyObject,
PolicyObject,
LSAP_DB_LOCK |
LSAP_DB_FINISH_TRANSACTION |
LSAP_DB_OMIT_REPLICATOR_NOTIFICATION,
SecurityDbChange,
Status
);
if ( NT_SUCCESS( Status )) {
*Renamed = TRUE;
}
}
if ( NT_SUCCESS( Status )) {
//
// Currently existing logon sessions must be modified with the new
// domain name
//
Status = LsapDomainRenameHandlerForLogonSessions(
&PrimaryDomainNameReg,
&DnsDomainNameReg,
&NetbiosDomainNameDs,
&DnsDomainNameDs
);
}
if ( !NT_SUCCESS( Status )) {
goto Error;
}
}
//
// Bug #380437: Since the DC can have a setting that allows it to keep
// its DNS suffix across membership changes, this check
// may cause the machine to fail to boot.
//
// Thus the following segment of code has been pulled
//
#if 0
//
// One final check: call GetComputerNameEx() and see if what it returns
// matches what we believe the DNS domain name to be
//
// Note that we do NOT ask for the physical DNS domain name,
// this is done so that the code does not have to be changed for Blackcomb,
// which is expected to be able to host multiple domains on clusters
//
if ( FALSE == GetComputerNameExW(
ComputerNameDnsDomain,
NameBuffer,
&NameBufferSize )) {
//
// Must return an NT status code, so map the error code back
//
Status = NetpApiStatusToNtStatus( GetLastError());
//
// Guard against return values that are not of type 'error'
//
if ( NT_SUCCESS( Status )) {
Status = STATUS_UNSUCCESSFUL;
}
goto Error;
} else {
WCHAR * Buffer;
BOOLEAN BufferAllocated = FALSE;
ASSERT( DnsDomainNameDs.Length <= DNS_MAX_NAME_LENGTH );
if ( DnsDomainNameDs.MaximumLength > DnsDomainNameDs.Length ) {
Buffer = DnsDomainNameDs.Buffer;
} else {
SafeAllocaAllocate( Buffer, DnsDomainNameDs.Length + sizeof( WCHAR ));
if ( Buffer == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
BufferAllocated = TRUE;
wcsncpy( Buffer, DnsDomainNameDs.Buffer, DnsDomainNameDs.Length );
}
Buffer[DnsDomainNameDs.Length / sizeof( WCHAR )] = L'\0';
if ( !DnsNameCompare_W( NameBuffer, Buffer )) {
//
// Can not proceed.
// The boot sequence must be aborted, and the computer name
// in the registry corrected manually from the recovery console
//
Status = STATUS_INTERNAL_ERROR;
Reason = LSA_DOMAIN_RENAME_ERROR2;
if ( BufferAllocated ) {
SafeAllocaFree( Buffer );
}
goto Error;
}
if ( BufferAllocated ) {
SafeAllocaFree( Buffer );
}
}
#endif // #if 0
Cleanup:
LsapDbFreeAttributes( AttributeCountReadReg, AttributesReadReg );
LsapDbFreeAttributes( AttributeCountReadDsDom, AttributesReadDsDom );
LsapDbFreeAttributes( AttributeCountReadDsRoot, AttributesReadDsRoot );
LsapDbFreeAttributes( AttributeCountWriteReg, AttributesWriteReg );
LsapFreeLsaHeap( DomainXRef );
LsapFreeLsaHeap( RootDomainXRef );
LsarpReturnPrologue();
return Status;
Error:
ASSERT( !NT_SUCCESS( Status ));
//
// log an explanatory event
//
_ltow( Status, ErrorCode, 16 );
pErrorCode = &ErrorCode[0];
SpmpReportEvent(
TRUE,
EVENTLOG_ERROR_TYPE,
Reason,
0,
0,
NULL,
1,
pErrorCode
);
goto Cleanup;
}
NTSTATUS
LsaISamIndicatedDsStarted(
IN BOOLEAN PerformDomainRenameCheck
)
/*++
Routine Description:
This function is a sort of callback from SampInitialize, which is used to tell Lsa that
the Ds has started. It is supplied so that the Lsa can initialize enough of its state
to allow access to Ds stored Lsa information so that Sam can complete initialization
This function only gets called if the Ds is running
This function must NOT call any APIs that invoke any SAM calls, since this function gets
called from SampInitialize, and it causes problems.
Arguments:
PerformDomainRenameCheck perform the domain rename check in this iteration?
Return Value:
STATUS_SUCCESS -- Success
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG Len, i, j;
BOOLEAN DbLocked = FALSE, CloseTransaction = FALSE;
ATTRBLOCK SystemContainerRead, SystemContainerResults;
ATTRVAL AttrVal;
ATTR SearchAttr;
PDSNAME SchemaPath = NULL;
SYNTAX_DISTNAME_STRING *DistnameString;
SYNTAX_ADDRESS *SyntaxAddress;
GUID KnownSystemContainerGuid = {
0xf3301dab, 0x8876, 0xd111, 0xad, 0xed, 0x00, 0xc0, 0x4f, 0xd8, 0xd5, 0xcd
};
BOOLEAN DomainRenamed = FALSE;
LsaDsStateInfo.WriteLocal = FALSE;
LsaDsStateInfo.DsRoot = NULL;
LsaDsStateInfo.FunctionTableInitialized = FALSE;
LsaDsStateInfo.UseDs = TRUE;
LsaDsStateInfo.Nt4UpgradeInProgress = FALSE;
LsapDsIsRunning = TRUE;
//
// Initialize the function table
//
LsaDsStateInfo.DsFuncTable.pOpenTransaction = LsapDsOpenTransaction;
LsaDsStateInfo.DsFuncTable.pApplyTransaction = LsapDsApplyTransaction;
LsaDsStateInfo.DsFuncTable.pAbortTransaction = LsapDsAbortTransaction;
LsaDsStateInfo.FunctionTableInitialized = TRUE;
//
// Determine our write state. At init time, the write is only allowed if
// we are a DC. The client write state will be set after we examine the
// machine object.
//
if ( LsapProductType == NtProductLanManNt ) {
LsaDsStateInfo.WriteLocal = TRUE;
}
//
// Now, build the DS name for the root of the domain. We do this using the
// Lsa memory allocations and deallocations, since this structure will outlive
// any thread state
//
Len = 0;
Status = GetConfigurationName( DSCONFIGNAME_DOMAIN, &Len, NULL );
ASSERT( Status == STATUS_BUFFER_TOO_SMALL );
LsaDsStateInfo.DsRoot = LsapAllocateLsaHeap( Len );
if ( LsaDsStateInfo.DsRoot == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
Status = GetConfigurationName( DSCONFIGNAME_DOMAIN, &Len, LsaDsStateInfo.DsRoot );
//
// Get the handle to the domain
//
if ( NT_SUCCESS( Status ) ) {
Status = LsapDsInitAllocAsNeededEx( LSAP_DB_NO_LOCK,
PolicyObject,
&CloseTransaction );
if ( NT_SUCCESS( Status ) ) {
LsaDsStateInfo.DsDomainHandle = DirGetDomainHandle( LsaDsStateInfo.DsRoot );
LsapDsDeleteAllocAsNeededEx( LSAP_DB_NO_LOCK,
PolicyObject,
CloseTransaction );
}
} else {
LsapDsDebugOut(( DEB_ERROR,
"GetConfigurationName for DOMAIN returned 0x%lx\n", Status ));
}
}
//
// Now, the Configuration container
//
if ( NT_SUCCESS( Status ) ) {
Len = 0;
Status = GetConfigurationName( DSCONFIGNAME_CONFIGURATION, &Len, NULL );
ASSERT( Status == STATUS_BUFFER_TOO_SMALL );
LsaDsStateInfo.DsConfigurationContainer = LsapAllocateLsaHeap( Len );
if ( LsaDsStateInfo.DsConfigurationContainer == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
Status = GetConfigurationName( DSCONFIGNAME_CONFIGURATION, &Len,
LsaDsStateInfo.DsConfigurationContainer );
}
}
//
// Now, the Partitions container
//
if ( NT_SUCCESS( Status ) ) {
Len = 0;
Status = GetConfigurationName( DSCONFIGNAME_PARTITIONS, &Len, NULL );
ASSERT( Status == STATUS_BUFFER_TOO_SMALL );
LsaDsStateInfo.DsPartitionsContainer = LsapAllocateLsaHeap( Len );
if ( LsaDsStateInfo.DsPartitionsContainer == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
Status = GetConfigurationName( DSCONFIGNAME_PARTITIONS, &Len,
LsaDsStateInfo.DsPartitionsContainer );
}
}
//
// Build the path to the system container. We read the wellKnownObjects from the root
// container and use that to determine which one of these objects is the system
//
if ( NT_SUCCESS( Status ) ) {
//
// Make sure we have an open transaction
//
Status = LsapDsInitAllocAsNeededEx( LSAP_DB_NO_LOCK,
PolicyObject,
&CloseTransaction );
if ( NT_SUCCESS( Status ) ) {
SystemContainerRead.attrCount = LsapDsDnsRootWellKnownObjectCount;
SystemContainerRead.pAttr = LsapDsDnsRootWellKnownObject;
Status = LsapDsReadByDsName( LsaDsStateInfo.DsRoot,
LSAPDS_READ_NO_LOCK,
&SystemContainerRead,
&SystemContainerResults );
if ( NT_SUCCESS( Status ) ) {
//
// Process all returned information until we find the one that corresponds to
// the system container
//
Status = STATUS_NOT_FOUND;
for ( i = 0; i < SystemContainerResults.attrCount; i++ ) {
for ( j = 0; j < SystemContainerResults.pAttr->AttrVal.valCount; j++ ) {
DistnameString = ( SYNTAX_DISTNAME_STRING * )
SystemContainerResults.pAttr->AttrVal.pAVal[ j ].pVal;
SyntaxAddress = DATAPTR( DistnameString );
if ( RtlCompareMemory( &KnownSystemContainerGuid,
SyntaxAddress->byteVal,
sizeof( GUID ) ) == sizeof( GUID ) ) {
Status = LsapDsCopyDsNameLsa( &LsaDsStateInfo.DsSystemContainer,
NAMEPTR( DistnameString ) );
break;
}
}
}
}
LsapDsDeleteAllocAsNeededEx( LSAP_DB_NO_LOCK,
PolicyObject,
CloseTransaction );
}
}
//
// Now, build the DS name for the schema container
//
Len = 0;
Status = GetConfigurationName( DSCONFIGNAME_DMD, &Len, NULL );
ASSERT( Status == STATUS_BUFFER_TOO_SMALL );
SchemaPath = LsapAllocateLsaHeap( Len );
if ( SchemaPath == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
Status = GetConfigurationName( DSCONFIGNAME_DMD, &Len, SchemaPath );
//
// Query for the info we need to be able to look up items in the system container
//
if ( NT_SUCCESS( Status ) ) {
AttrVal.valLen = sizeof( LSAP_DS_TRUSTED_DOMAIN ) - sizeof( WCHAR );
AttrVal.pVal = ( PUCHAR )LSAP_DS_TRUSTED_DOMAIN;
Status = LsapDsFindUnique( 0,
SchemaPath,
AllObject,
&AttrVal,
ATT_LDAP_DISPLAY_NAME,
&LsaDsStateInfo.SystemContainerItems.TrustedDomainObject );
//
// If we didn't find it via the DirFind, it could be because the indicies haven't
// been created yet. So, we're forced to try it again with an old fashioned search.
//
if ( Status == STATUS_NOT_FOUND ) {
SearchAttr.attrTyp = ATT_LDAP_DISPLAY_NAME;
SearchAttr.AttrVal.valCount = 1;
SearchAttr.AttrVal.pAVal = &AttrVal;
Status = LsapDsSearchUnique(
0,
SchemaPath,
&SearchAttr,
1,
&LsaDsStateInfo.SystemContainerItems.TrustedDomainObject );
}
}
if ( NT_SUCCESS( Status ) ) {
AttrVal.valLen = sizeof( LSAP_DS_SECRET ) - sizeof( WCHAR );
AttrVal.pVal = ( PUCHAR )LSAP_DS_SECRET;
Status = LsapDsFindUnique( 0,
SchemaPath,
AllObject,
&AttrVal,
ATT_LDAP_DISPLAY_NAME,
&LsaDsStateInfo.SystemContainerItems.SecretObject );
//
// If we didn't find it via the DirFind, it could be because the indicies haven't
// been created yet. So, we're forced to try it again with an old fashioned search.
//
if ( Status == STATUS_NOT_FOUND ) {
SearchAttr.attrTyp = ATT_LDAP_DISPLAY_NAME;
SearchAttr.AttrVal.valCount = 1;
SearchAttr.AttrVal.pAVal = &AttrVal;
Status = LsapDsSearchUnique(
0,
SchemaPath,
&SearchAttr,
1,
&LsaDsStateInfo.SystemContainerItems.SecretObject );
}
}
if ( SchemaPath ) {
LsapFreeLsaHeap( SchemaPath );
}
}
if ( NT_SUCCESS( Status )) {
LsaDsStateInfo.DsInitializedAndRunning = TRUE;
//
// Domain rename support -- synchronize domain name in the DS with what's
// in the registry
//
if ( PerformDomainRenameCheck ) {
Status = LsapDbDomainRenameHandler( &DomainRenamed );
}
}
if ( NT_SUCCESS (Status ) ) {
LsapDbAcquireLockEx( AllObject,
0 );
//
// Rebuild all the caches after setting up the ds and the registry
//
Status = LsapDbBuildObjectCaches();
LsapDbReleaseLockEx( AllObject,
0 );
}
if ( NT_SUCCESS( Status ) &&
LsapProductType == NtProductLanManNt &&
SamIIsRebootAfterPromotion()) {
//
// Bug 222800: if this a reboot after promotion, notify the parent
// of the trust relationship so netlogon.chg gets updated
//
Status = LsapNotifyNetlogonOfTrustWithParent();
}
if ( !NT_SUCCESS( Status ) ) {
LsapDsIsRunning = FALSE;
LsaDsStateInfo.UseDs = TRUE;
LsaDsStateInfo.DsInitializedAndRunning = FALSE;
} else if ( DomainRenamed ) {
LsaINotifyChangeNotification(
PolicyNotifyAccountDomainInformation
);
LsaINotifyChangeNotification(
PolicyNotifyDnsDomainInformation
);
}
return( Status ) ;
}
BOOLEAN
LsapDsIsValidSid(
IN PSID Sid,
IN BOOLEAN DsSid
)
/*++
Routine Description:
This function determines whether the SID is valid for the Ds or registry based LSA
Arguments:
Sid - Sid to validate
DsSid - If TRUE, this is a SID for a DS function
Return Value:
TRUE - Valid SID
FALSE - Invalid SID
--*/
{
BOOLEAN ValidSid;
ValidSid = RtlValidSid( Sid );
if ( ValidSid && DsSid ) {
if ( RtlLengthSid( Sid ) > sizeof( NT4SID ) ) {
ValidSid = FALSE;
}
}
return( ValidSid );
}
NTSTATUS
LsapRetrieveDnsDomainNameFromHive(
IN HKEY Hive,
IN OUT DWORD * Length,
OUT WCHAR * Buffer
)
{
DWORD Status;
DWORD Win32Err;
HKEY Hkey;
DWORD Type;
DWORD Size = DNS_MAX_NAME_LENGTH * sizeof( WCHAR ) + 8;
BYTE Value[DNS_MAX_NAME_LENGTH * sizeof( WCHAR ) + 8];
ASSERT( Hive );
ASSERT( Length );
ASSERT( *Length );
ASSERT( Buffer );
Win32Err = RegOpenKeyW(
Hive,
L"Policy\\PolDnDDN",
&Hkey
);
if ( Win32Err != ERROR_SUCCESS) {
return STATUS_NOT_FOUND;
}
Win32Err = RegQueryValueExW(
Hkey,
NULL,
NULL,
&Type,
Value,
&Size
);
RegCloseKey( Hkey );
if ( Win32Err != ERROR_SUCCESS) {
return STATUS_NOT_FOUND;
}
if ( Type != REG_BINARY && Type != REG_NONE ) {
return STATUS_DATA_ERROR; // should never happen, sanity check only
}
if ( Size <= 8 ) {
return STATUS_DATA_ERROR; // should never happen, sanity check only
}
if ( Size - 8 > *Length ) {
return STATUS_BUFFER_TOO_SMALL;
}
RtlCopyMemory( Buffer, Value + 8, Size - 8 );
*Length = Size - 8;
return STATUS_SUCCESS;
}
NTSTATUS
LsapDsReadObjectSDByDsName(
IN DSNAME* Object,
OUT PSECURITY_DESCRIPTOR *pSD
)
/*++
Routine Description:
This routine reads the security descriptor for an object in the DS.
Note that this is primary used when Object does not represent an
LSA object. For example, the domain object.
Arguments:
Object -- the object in the DS
pSD -- the security descriptor. Caller must free with LsapFreeLsaHeap
Return Values:
STATUS_SUCCESS
STATUS_NO_SECURITY_ON_OBJECT
a resource error otherwise.
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
BOOLEAN ReleaseState = FALSE;
//
// Setup the attrs to read
//
ATTR SDReadAttr = {ATT_NT_SECURITY_DESCRIPTOR, {0, NULL}};
ATTRBLOCK SDReadAttrBlock = {1,&SDReadAttr};
ATTRBLOCK SDReadResAttrBlock = {0, NULL};
PSECURITY_DESCRIPTOR LocalSD = NULL;
ULONG Size = 0;
ULONG i;
NtStatus = LsapDsInitAllocAsNeededEx( LSAP_DB_READ_ONLY_TRANSACTION |
LSAP_DB_DS_OP_TRANSACTION,
NullObject,
&ReleaseState );
if ( !NT_SUCCESS( NtStatus ) ) {
goto DsReadObjectSDError;
}
//
// Read the SD
//
NtStatus = LsapDsReadByDsName(Object,
LSAPDS_READ_NO_LOCK,
&SDReadAttrBlock,
&SDReadResAttrBlock);
if (!NT_SUCCESS(NtStatus)) {
goto DsReadObjectSDError;
}
//
// Extract the attribute
//
for (i = 0; i < SDReadResAttrBlock.attrCount; i++) {
if (SDReadResAttrBlock.pAttr[i].attrTyp == ATT_NT_SECURITY_DESCRIPTOR) {
if (SDReadResAttrBlock.pAttr[i].AttrVal.valCount > 0
&& SDReadResAttrBlock.pAttr[i].AttrVal.pAVal[0].valLen > 0) {
Size = SDReadResAttrBlock.pAttr[i].AttrVal.pAVal[0].valLen;
LocalSD = (PSECURITY_DESCRIPTOR) SDReadResAttrBlock.pAttr[i].AttrVal.pAVal[0].pVal;
ASSERT(IsValidSecurityDescriptor(LocalSD));
}
}
}
//
// Copy it to local memory
//
if (NULL == LocalSD) {
//
// No security descriptor is an error
//
NtStatus = STATUS_NO_SECURITY_ON_OBJECT;
goto DsReadObjectSDError;
}
(*pSD) = LsapAllocateLsaHeap(Size);
if (NULL == (*pSD)) {
NtStatus = STATUS_NO_MEMORY;
goto DsReadObjectSDError;
}
RtlCopyMemory((*pSD), LocalSD, Size);
DsReadObjectSDError:
if (ReleaseState) {
LsapDsDeleteAllocAsNeededEx( LSAP_DB_READ_ONLY_TRANSACTION |
LSAP_DB_DS_OP_TRANSACTION,
NullObject,
ReleaseState );
}
return NtStatus;
}
PACL LsapGetDacl(
IN PSECURITY_DESCRIPTOR Sd
)
{
BOOL Status;
PACL Dacl = NULL;
PACL DaclToReturn = NULL;
BOOL DaclPresent;
BOOL DaclDefaulted;
Status = GetSecurityDescriptorDacl(
Sd,
&DaclPresent,
&Dacl,
&DaclDefaulted
);
if ((Status)
&& DaclPresent
&& !DaclDefaulted)
{
DaclToReturn = Dacl;
}
return DaclToReturn;
}
PACL LsapGetSacl(
IN PSECURITY_DESCRIPTOR Sd
)
{
BOOL Status;
PACL Sacl = NULL;
PACL SaclToReturn = NULL;
BOOL SaclPresent;
BOOL SaclDefaulted;
Status = GetSecurityDescriptorSacl(
Sd,
&SaclPresent,
&Sacl,
&SaclDefaulted
);
if ((Status)
&& SaclPresent
&& !SaclDefaulted)
{
SaclToReturn = Sacl;
}
return SaclToReturn;
}
NTSTATUS
LsapDsGetDefaultSecurityDescriptor(
IN ULONG ClassId,
OUT PSECURITY_DESCRIPTOR *ppSD,
OUT ULONG *cbSD
)
/*++
Routine Description:
This routine obtains the default security descriptor for ClassId
and sets the Owner as the owner of the current's called token.
Arguments:
ClassId -- the class to get the security descriptor for
ppSD -- the security descriptor
cbSD -- the number of bytes pointed to by ppSD
Return Values:
STATUS_SUCCESS, a resource error otherwise
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG cbLocalSD = 0;
PSECURITY_DESCRIPTOR pLocalSD = NULL;
PTOKEN_OWNER Owner = NULL;
PTOKEN_PRIMARY_GROUP PrimaryGroup = NULL;
//
// Get the default security descriptor
//
Status = SampGetClassAttribute(ClassId,
ATT_DEFAULT_SECURITY_DESCRIPTOR,
&cbLocalSD,
pLocalSD);
if (STATUS_BUFFER_TOO_SMALL == Status) {
SafeAllocaAllocate(pLocalSD, cbLocalSD);
if (NULL == pLocalSD) {
goto Exit;
}
Status = SampGetClassAttribute(ClassId,
ATT_DEFAULT_SECURITY_DESCRIPTOR,
&cbLocalSD,
pLocalSD
);
}
if (!NT_SUCCESS(Status)) {
goto Exit;
}
//
// Get the current owner and primary group of the token
//
Status = LsapGetCurrentOwnerAndPrimaryGroup(
&Owner,
&PrimaryGroup
);
if (!NT_SUCCESS(Status)) {
goto Exit;
}
//
// Make a new security descriptor , setting the owner and the group
// to that of
//
Status = LsapMakeNewSelfRelativeSecurityDescriptor(
(Owner)?Owner->Owner:LsapAliasAdminsSid,
(PrimaryGroup)?PrimaryGroup->PrimaryGroup:LsapAliasAdminsSid,
LsapGetDacl(pLocalSD),
LsapGetSacl(pLocalSD),
cbSD,
ppSD
);
//
// Fall through to exit
//
Exit:
if (pLocalSD) {
SafeAllocaFree(pLocalSD);
}
if (Owner) {
LsapFreeLsaHeap(Owner);
}
if (PrimaryGroup) {
LsapFreeLsaHeap(PrimaryGroup);
}
return Status;
}